layout/base/nsFrameTraversal.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:4f7e8565ea26
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"
7
8 #include "nsFrameTraversal.h"
9 #include "nsFrameList.h"
10 #include "nsPlaceholderFrame.h"
11
12
13 class nsFrameIterator : public nsIFrameEnumerator
14 {
15 public:
16 typedef nsIFrame::ChildListID ChildListID;
17
18 NS_DECL_ISUPPORTS
19
20 virtual ~nsFrameIterator() {}
21
22 virtual void First() MOZ_OVERRIDE;
23 virtual void Next() MOZ_OVERRIDE;
24 virtual nsIFrame* CurrentItem() MOZ_OVERRIDE;
25 virtual bool IsDone() MOZ_OVERRIDE;
26
27 virtual void Last() MOZ_OVERRIDE;
28 virtual void Prev() MOZ_OVERRIDE;
29
30 nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
31 nsIteratorType aType, bool aLockScroll, bool aFollowOOFs);
32
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;}
41
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:
46
47 - If a frame HAS a placeholder frame, getting its parent
48 gets the placeholder's parent.
49
50 - If a frame's first child or next/prev sibling IS a
51 placeholder frame, then we instead return the real frame.
52
53 - If a frame HAS a placeholder frame, getting its next/prev
54 sibling gets the placeholder frame's next/prev sibling.
55
56 These are all applied recursively to support multiple levels of
57 placeholders.
58 */
59
60 nsIFrame* GetParentFrame(nsIFrame* aFrame);
61 // like GetParentFrame but returns null once a popup frame is reached
62 nsIFrame* GetParentFrameNotPopup(nsIFrame* aFrame);
63
64 nsIFrame* GetFirstChild(nsIFrame* aFrame);
65 nsIFrame* GetLastChild(nsIFrame* aFrame);
66
67 nsIFrame* GetNextSibling(nsIFrame* aFrame);
68 nsIFrame* GetPrevSibling(nsIFrame* aFrame);
69
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 */
76
77 virtual nsIFrame* GetFirstChildInner(nsIFrame* aFrame);
78 virtual nsIFrame* GetLastChildInner(nsIFrame* aFrame);
79
80 virtual nsIFrame* GetNextSiblingInner(nsIFrame* aFrame);
81 virtual nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame);
82
83 nsIFrame* GetPlaceholderFrame(nsIFrame* aFrame);
84 bool IsPopupFrame(nsIFrame* aFrame);
85
86 nsPresContext* const mPresContext;
87 const bool mLockScroll;
88 const bool mFollowOOFs;
89 const nsIteratorType mType;
90
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 };
97
98
99
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) {}
107
108 protected:
109 nsIFrame* GetFirstChildInner(nsIFrame* aFrame) MOZ_OVERRIDE;
110 nsIFrame* GetLastChildInner(nsIFrame* aFrame) MOZ_OVERRIDE;
111
112 nsIFrame* GetNextSiblingInner(nsIFrame* aFrame) MOZ_OVERRIDE;
113 nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame) MOZ_OVERRIDE;
114 };
115
116 /************IMPLEMENTATIONS**************/
117
118 nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult)
119 {
120 NS_ENSURE_ARG_POINTER(aResult);
121 *aResult = nullptr;
122
123 nsCOMPtr<nsIFrameTraversal> t(new nsFrameTraversal());
124
125 *aResult = t;
126 NS_ADDREF(*aResult);
127
128 return NS_OK;
129 }
130
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;
142
143 if (aFollowOOFs) {
144 aStart = nsPlaceholderFrame::GetRealFrameFor(aStart);
145 }
146
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 }
158
159
160 nsFrameTraversal::nsFrameTraversal()
161 {
162 }
163
164 nsFrameTraversal::~nsFrameTraversal()
165 {
166 }
167
168 NS_IMPL_ISUPPORTS(nsFrameTraversal,nsIFrameTraversal)
169
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 }
183
184 // nsFrameIterator implementation
185
186 NS_IMPL_ISUPPORTS(nsFrameIterator, nsIFrameEnumerator)
187
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 }
203
204
205
206 nsIFrame*
207 nsFrameIterator::CurrentItem()
208 {
209 if (mOffEdge)
210 return nullptr;
211
212 return mCurrent;
213 }
214
215
216
217 bool
218 nsFrameIterator::IsDone()
219 {
220 return mOffEdge != 0;
221 }
222
223 void
224 nsFrameIterator::First()
225 {
226 mCurrent = mStart;
227 }
228
229 static bool
230 IsRootFrame(nsIFrame* aFrame)
231 {
232 nsIAtom* atom = aFrame->GetType();
233 return (atom == nsGkAtoms::canvasFrame) ||
234 (atom == nsGkAtoms::rootFrame);
235 }
236
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 }
248
249 while ((result = GetLastChild(parent))) {
250 parent = result;
251 }
252
253 setCurrent(parent);
254 if (!parent)
255 setOffEdge(1);
256 }
257
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();
266
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 }
277
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 }
306
307 setCurrent(result);
308 if (!result) {
309 setOffEdge(1);
310 setLast(parent);
311 }
312 }
313
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();
322
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 }
333
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 }
361
362 setCurrent(result);
363 if (!result) {
364 setOffEdge(-1);
365 setLast(parent);
366 }
367 }
368
369 nsIFrame*
370 nsFrameIterator::GetParentFrame(nsIFrame* aFrame)
371 {
372 if (mFollowOOFs)
373 aFrame = GetPlaceholderFrame(aFrame);
374 if (aFrame)
375 return aFrame->GetParent();
376
377 return nullptr;
378 }
379
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 }
390
391 return nullptr;
392 }
393
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);
402
403 if (IsPopupFrame(result))
404 result = GetNextSibling(result);
405 }
406 return result;
407 }
408
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);
417
418 if (IsPopupFrame(result))
419 result = GetPrevSibling(result);
420 }
421 return result;
422 }
423
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 }
435
436 if (mFollowOOFs && IsPopupFrame(result))
437 result = GetNextSibling(result);
438
439 return result;
440 }
441
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 }
453
454 if (mFollowOOFs && IsPopupFrame(result))
455 result = GetPrevSibling(result);
456
457 return result;
458 }
459
460 nsIFrame*
461 nsFrameIterator::GetFirstChildInner(nsIFrame* aFrame) {
462 return aFrame->GetFirstPrincipalChild();
463 }
464
465 nsIFrame*
466 nsFrameIterator::GetLastChildInner(nsIFrame* aFrame) {
467 return aFrame->PrincipalChildList().LastChild();
468 }
469
470 nsIFrame*
471 nsFrameIterator::GetNextSiblingInner(nsIFrame* aFrame) {
472 return aFrame->GetNextSibling();
473 }
474
475 nsIFrame*
476 nsFrameIterator::GetPrevSiblingInner(nsIFrame* aFrame) {
477 return aFrame->GetPrevSibling();
478 }
479
480
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 }
491
492 if (result != aFrame)
493 result = GetPlaceholderFrame(result);
494
495 return result;
496 }
497
498 bool
499 nsFrameIterator::IsPopupFrame(nsIFrame* aFrame)
500 {
501 return (aFrame &&
502 aFrame->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_POPUP);
503 }
504
505 // nsVisualIterator implementation
506
507 nsIFrame*
508 nsVisualIterator::GetFirstChildInner(nsIFrame* aFrame) {
509 return aFrame->PrincipalChildList().GetNextVisualFor(nullptr);
510 }
511
512 nsIFrame*
513 nsVisualIterator::GetLastChildInner(nsIFrame* aFrame) {
514 return aFrame->PrincipalChildList().GetPrevVisualFor(nullptr);
515 }
516
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 }
524
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 }

mercurial