Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsCOMPtr.h"
6 #include "nsGkAtoms.h"
8 #include "nsFrameTraversal.h"
9 #include "nsFrameList.h"
10 #include "nsPlaceholderFrame.h"
13 class nsFrameIterator : public nsIFrameEnumerator
14 {
15 public:
16 typedef nsIFrame::ChildListID ChildListID;
18 NS_DECL_ISUPPORTS
20 virtual ~nsFrameIterator() {}
22 virtual void First() MOZ_OVERRIDE;
23 virtual void Next() MOZ_OVERRIDE;
24 virtual nsIFrame* CurrentItem() MOZ_OVERRIDE;
25 virtual bool IsDone() MOZ_OVERRIDE;
27 virtual void Last() MOZ_OVERRIDE;
28 virtual void Prev() MOZ_OVERRIDE;
30 nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
31 nsIteratorType aType, bool aLockScroll, bool aFollowOOFs);
33 protected:
34 void setCurrent(nsIFrame *aFrame){mCurrent = aFrame;}
35 nsIFrame *getCurrent(){return mCurrent;}
36 nsIFrame *getStart(){return mStart;}
37 nsIFrame *getLast(){return mLast;}
38 void setLast(nsIFrame *aFrame){mLast = aFrame;}
39 int8_t getOffEdge(){return mOffEdge;}
40 void setOffEdge(int8_t aOffEdge){mOffEdge = aOffEdge;}
42 /*
43 Our own versions of the standard frame tree navigation
44 methods, which, if the iterator is following out-of-flows,
45 apply the following rules for placeholder frames:
47 - If a frame HAS a placeholder frame, getting its parent
48 gets the placeholder's parent.
50 - If a frame's first child or next/prev sibling IS a
51 placeholder frame, then we instead return the real frame.
53 - If a frame HAS a placeholder frame, getting its next/prev
54 sibling gets the placeholder frame's next/prev sibling.
56 These are all applied recursively to support multiple levels of
57 placeholders.
58 */
60 nsIFrame* GetParentFrame(nsIFrame* aFrame);
61 // like GetParentFrame but returns null once a popup frame is reached
62 nsIFrame* GetParentFrameNotPopup(nsIFrame* aFrame);
64 nsIFrame* GetFirstChild(nsIFrame* aFrame);
65 nsIFrame* GetLastChild(nsIFrame* aFrame);
67 nsIFrame* GetNextSibling(nsIFrame* aFrame);
68 nsIFrame* GetPrevSibling(nsIFrame* aFrame);
70 /*
71 These methods are overridden by the bidi visual iterator to have the
72 semantics of "get first child in visual order", "get last child in visual
73 order", "get next sibling in visual order" and "get previous sibling in visual
74 order".
75 */
77 virtual nsIFrame* GetFirstChildInner(nsIFrame* aFrame);
78 virtual nsIFrame* GetLastChildInner(nsIFrame* aFrame);
80 virtual nsIFrame* GetNextSiblingInner(nsIFrame* aFrame);
81 virtual nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame);
83 nsIFrame* GetPlaceholderFrame(nsIFrame* aFrame);
84 bool IsPopupFrame(nsIFrame* aFrame);
86 nsPresContext* const mPresContext;
87 const bool mLockScroll;
88 const bool mFollowOOFs;
89 const nsIteratorType mType;
91 private:
92 nsIFrame* const mStart;
93 nsIFrame* mCurrent;
94 nsIFrame* mLast; //the last one that was in current;
95 int8_t mOffEdge; //0= no -1 to far prev, 1 to far next;
96 };
100 // Bidi visual iterator
101 class nsVisualIterator: public nsFrameIterator
102 {
103 public:
104 nsVisualIterator(nsPresContext* aPresContext, nsIFrame *aStart,
105 nsIteratorType aType, bool aLockScroll, bool aFollowOOFs) :
106 nsFrameIterator(aPresContext, aStart, aType, aLockScroll, aFollowOOFs) {}
108 protected:
109 nsIFrame* GetFirstChildInner(nsIFrame* aFrame) MOZ_OVERRIDE;
110 nsIFrame* GetLastChildInner(nsIFrame* aFrame) MOZ_OVERRIDE;
112 nsIFrame* GetNextSiblingInner(nsIFrame* aFrame) MOZ_OVERRIDE;
113 nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame) MOZ_OVERRIDE;
114 };
116 /************IMPLEMENTATIONS**************/
118 nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult)
119 {
120 NS_ENSURE_ARG_POINTER(aResult);
121 *aResult = nullptr;
123 nsCOMPtr<nsIFrameTraversal> t(new nsFrameTraversal());
125 *aResult = t;
126 NS_ADDREF(*aResult);
128 return NS_OK;
129 }
131 nsresult
132 NS_NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
133 nsPresContext* aPresContext,
134 nsIFrame *aStart,
135 nsIteratorType aType,
136 bool aVisual,
137 bool aLockInScrollView,
138 bool aFollowOOFs)
139 {
140 if (!aEnumerator || !aStart)
141 return NS_ERROR_NULL_POINTER;
143 if (aFollowOOFs) {
144 aStart = nsPlaceholderFrame::GetRealFrameFor(aStart);
145 }
147 nsCOMPtr<nsIFrameEnumerator> trav;
148 if (aVisual) {
149 trav = new nsVisualIterator(aPresContext, aStart, aType,
150 aLockInScrollView, aFollowOOFs);
151 } else {
152 trav = new nsFrameIterator(aPresContext, aStart, aType,
153 aLockInScrollView, aFollowOOFs);
154 }
155 trav.forget(aEnumerator);
156 return NS_OK;
157 }
160 nsFrameTraversal::nsFrameTraversal()
161 {
162 }
164 nsFrameTraversal::~nsFrameTraversal()
165 {
166 }
168 NS_IMPL_ISUPPORTS(nsFrameTraversal,nsIFrameTraversal)
170 NS_IMETHODIMP
171 nsFrameTraversal::NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
172 nsPresContext* aPresContext,
173 nsIFrame *aStart,
174 int32_t aType,
175 bool aVisual,
176 bool aLockInScrollView,
177 bool aFollowOOFs)
178 {
179 return NS_NewFrameTraversal(aEnumerator, aPresContext, aStart,
180 static_cast<nsIteratorType>(aType),
181 aVisual, aLockInScrollView, aFollowOOFs);
182 }
184 // nsFrameIterator implementation
186 NS_IMPL_ISUPPORTS(nsFrameIterator, nsIFrameEnumerator)
188 nsFrameIterator::nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
189 nsIteratorType aType, bool aLockInScrollView,
190 bool aFollowOOFs)
191 : mPresContext(aPresContext),
192 mLockScroll(aLockInScrollView),
193 mFollowOOFs(aFollowOOFs),
194 mType(aType),
195 mStart(aStart),
196 mCurrent(aStart),
197 mLast(aStart),
198 mOffEdge(0)
199 {
200 MOZ_ASSERT(!aFollowOOFs || aStart->GetType() != nsGkAtoms::placeholderFrame,
201 "Caller should have resolved placeholder frame");
202 }
206 nsIFrame*
207 nsFrameIterator::CurrentItem()
208 {
209 if (mOffEdge)
210 return nullptr;
212 return mCurrent;
213 }
217 bool
218 nsFrameIterator::IsDone()
219 {
220 return mOffEdge != 0;
221 }
223 void
224 nsFrameIterator::First()
225 {
226 mCurrent = mStart;
227 }
229 static bool
230 IsRootFrame(nsIFrame* aFrame)
231 {
232 nsIAtom* atom = aFrame->GetType();
233 return (atom == nsGkAtoms::canvasFrame) ||
234 (atom == nsGkAtoms::rootFrame);
235 }
237 void
238 nsFrameIterator::Last()
239 {
240 nsIFrame* result;
241 nsIFrame* parent = getCurrent();
242 // If the current frame is a popup, don't move farther up the tree.
243 // Otherwise, get the nearest root frame or popup.
244 if (parent->GetType() != nsGkAtoms::menuPopupFrame) {
245 while (!IsRootFrame(parent) && (result = GetParentFrameNotPopup(parent)))
246 parent = result;
247 }
249 while ((result = GetLastChild(parent))) {
250 parent = result;
251 }
253 setCurrent(parent);
254 if (!parent)
255 setOffEdge(1);
256 }
258 void
259 nsFrameIterator::Next()
260 {
261 // recursive-oid method to get next frame
262 nsIFrame *result = nullptr;
263 nsIFrame *parent = getCurrent();
264 if (!parent)
265 parent = getLast();
267 if (mType == eLeaf) {
268 // Drill down to first leaf
269 while ((result = GetFirstChild(parent))) {
270 parent = result;
271 }
272 } else if (mType == ePreOrder) {
273 result = GetFirstChild(parent);
274 if (result)
275 parent = result;
276 }
278 if (parent != getCurrent()) {
279 result = parent;
280 } else {
281 while (parent) {
282 result = GetNextSibling(parent);
283 if (result) {
284 if (mType != ePreOrder) {
285 parent = result;
286 while ((result = GetFirstChild(parent))) {
287 parent = result;
288 }
289 result = parent;
290 }
291 break;
292 }
293 else {
294 result = GetParentFrameNotPopup(parent);
295 if (!result || IsRootFrame(result) ||
296 (mLockScroll && result->GetType() == nsGkAtoms::scrollFrame)) {
297 result = nullptr;
298 break;
299 }
300 if (mType == ePostOrder)
301 break;
302 parent = result;
303 }
304 }
305 }
307 setCurrent(result);
308 if (!result) {
309 setOffEdge(1);
310 setLast(parent);
311 }
312 }
314 void
315 nsFrameIterator::Prev()
316 {
317 // recursive-oid method to get prev frame
318 nsIFrame *result = nullptr;
319 nsIFrame *parent = getCurrent();
320 if (!parent)
321 parent = getLast();
323 if (mType == eLeaf) {
324 // Drill down to last leaf
325 while ((result = GetLastChild(parent))) {
326 parent = result;
327 }
328 } else if (mType == ePostOrder) {
329 result = GetLastChild(parent);
330 if (result)
331 parent = result;
332 }
334 if (parent != getCurrent()) {
335 result = parent;
336 } else {
337 while (parent) {
338 result = GetPrevSibling(parent);
339 if (result) {
340 if (mType != ePostOrder) {
341 parent = result;
342 while ((result = GetLastChild(parent))) {
343 parent = result;
344 }
345 result = parent;
346 }
347 break;
348 } else {
349 result = GetParentFrameNotPopup(parent);
350 if (!result || IsRootFrame(result) ||
351 (mLockScroll && result->GetType() == nsGkAtoms::scrollFrame)) {
352 result = nullptr;
353 break;
354 }
355 if (mType == ePreOrder)
356 break;
357 parent = result;
358 }
359 }
360 }
362 setCurrent(result);
363 if (!result) {
364 setOffEdge(-1);
365 setLast(parent);
366 }
367 }
369 nsIFrame*
370 nsFrameIterator::GetParentFrame(nsIFrame* aFrame)
371 {
372 if (mFollowOOFs)
373 aFrame = GetPlaceholderFrame(aFrame);
374 if (aFrame)
375 return aFrame->GetParent();
377 return nullptr;
378 }
380 nsIFrame*
381 nsFrameIterator::GetParentFrameNotPopup(nsIFrame* aFrame)
382 {
383 if (mFollowOOFs)
384 aFrame = GetPlaceholderFrame(aFrame);
385 if (aFrame) {
386 nsIFrame* parent = aFrame->GetParent();
387 if (!IsPopupFrame(parent))
388 return parent;
389 }
391 return nullptr;
392 }
394 nsIFrame*
395 nsFrameIterator::GetFirstChild(nsIFrame* aFrame)
396 {
397 nsIFrame* result = GetFirstChildInner(aFrame);
398 if (mLockScroll && result && result->GetType() == nsGkAtoms::scrollFrame)
399 return nullptr;
400 if (result && mFollowOOFs) {
401 result = nsPlaceholderFrame::GetRealFrameFor(result);
403 if (IsPopupFrame(result))
404 result = GetNextSibling(result);
405 }
406 return result;
407 }
409 nsIFrame*
410 nsFrameIterator::GetLastChild(nsIFrame* aFrame)
411 {
412 nsIFrame* result = GetLastChildInner(aFrame);
413 if (mLockScroll && result && result->GetType() == nsGkAtoms::scrollFrame)
414 return nullptr;
415 if (result && mFollowOOFs) {
416 result = nsPlaceholderFrame::GetRealFrameFor(result);
418 if (IsPopupFrame(result))
419 result = GetPrevSibling(result);
420 }
421 return result;
422 }
424 nsIFrame*
425 nsFrameIterator::GetNextSibling(nsIFrame* aFrame)
426 {
427 nsIFrame* result = nullptr;
428 if (mFollowOOFs)
429 aFrame = GetPlaceholderFrame(aFrame);
430 if (aFrame) {
431 result = GetNextSiblingInner(aFrame);
432 if (result && mFollowOOFs)
433 result = nsPlaceholderFrame::GetRealFrameFor(result);
434 }
436 if (mFollowOOFs && IsPopupFrame(result))
437 result = GetNextSibling(result);
439 return result;
440 }
442 nsIFrame*
443 nsFrameIterator::GetPrevSibling(nsIFrame* aFrame)
444 {
445 nsIFrame* result = nullptr;
446 if (mFollowOOFs)
447 aFrame = GetPlaceholderFrame(aFrame);
448 if (aFrame) {
449 result = GetPrevSiblingInner(aFrame);
450 if (result && mFollowOOFs)
451 result = nsPlaceholderFrame::GetRealFrameFor(result);
452 }
454 if (mFollowOOFs && IsPopupFrame(result))
455 result = GetPrevSibling(result);
457 return result;
458 }
460 nsIFrame*
461 nsFrameIterator::GetFirstChildInner(nsIFrame* aFrame) {
462 return aFrame->GetFirstPrincipalChild();
463 }
465 nsIFrame*
466 nsFrameIterator::GetLastChildInner(nsIFrame* aFrame) {
467 return aFrame->PrincipalChildList().LastChild();
468 }
470 nsIFrame*
471 nsFrameIterator::GetNextSiblingInner(nsIFrame* aFrame) {
472 return aFrame->GetNextSibling();
473 }
475 nsIFrame*
476 nsFrameIterator::GetPrevSiblingInner(nsIFrame* aFrame) {
477 return aFrame->GetPrevSibling();
478 }
481 nsIFrame*
482 nsFrameIterator::GetPlaceholderFrame(nsIFrame* aFrame)
483 {
484 nsIFrame* result = aFrame;
485 nsIPresShell *presShell = mPresContext->GetPresShell();
486 if (presShell) {
487 nsIFrame* placeholder = presShell->GetPlaceholderFrameFor(aFrame);
488 if (placeholder)
489 result = placeholder;
490 }
492 if (result != aFrame)
493 result = GetPlaceholderFrame(result);
495 return result;
496 }
498 bool
499 nsFrameIterator::IsPopupFrame(nsIFrame* aFrame)
500 {
501 return (aFrame &&
502 aFrame->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_POPUP);
503 }
505 // nsVisualIterator implementation
507 nsIFrame*
508 nsVisualIterator::GetFirstChildInner(nsIFrame* aFrame) {
509 return aFrame->PrincipalChildList().GetNextVisualFor(nullptr);
510 }
512 nsIFrame*
513 nsVisualIterator::GetLastChildInner(nsIFrame* aFrame) {
514 return aFrame->PrincipalChildList().GetPrevVisualFor(nullptr);
515 }
517 nsIFrame*
518 nsVisualIterator::GetNextSiblingInner(nsIFrame* aFrame) {
519 nsIFrame* parent = GetParentFrame(aFrame);
520 if (!parent)
521 return nullptr;
522 return parent->PrincipalChildList().GetNextVisualFor(aFrame);
523 }
525 nsIFrame*
526 nsVisualIterator::GetPrevSiblingInner(nsIFrame* aFrame) {
527 nsIFrame* parent = GetParentFrame(aFrame);
528 if (!parent)
529 return nullptr;
530 return parent->PrincipalChildList().GetPrevVisualFor(aFrame);
531 }