Line data Source code
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 : #include "nsContainerFrame.h"
12 :
13 :
14 : class nsFrameIterator : public nsIFrameEnumerator
15 : {
16 : public:
17 : typedef nsIFrame::ChildListID ChildListID;
18 :
19 : NS_DECL_ISUPPORTS
20 :
21 : virtual void First() override;
22 : virtual void Next() override;
23 : virtual nsIFrame* CurrentItem() override;
24 : virtual bool IsDone() override;
25 :
26 : virtual void Last() override;
27 : virtual void Prev() override;
28 :
29 : nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
30 : nsIteratorType aType, bool aLockScroll, bool aFollowOOFs,
31 : bool aSkipPopupChecks);
32 :
33 : protected:
34 0 : virtual ~nsFrameIterator() {}
35 :
36 0 : void setCurrent(nsIFrame *aFrame){mCurrent = aFrame;}
37 0 : nsIFrame *getCurrent(){return mCurrent;}
38 : nsIFrame *getStart(){return mStart;}
39 0 : nsIFrame *getLast(){return mLast;}
40 0 : void setLast(nsIFrame *aFrame){mLast = aFrame;}
41 : int8_t getOffEdge(){return mOffEdge;}
42 0 : void setOffEdge(int8_t aOffEdge){mOffEdge = aOffEdge;}
43 :
44 : /*
45 : Our own versions of the standard frame tree navigation
46 : methods, which, if the iterator is following out-of-flows,
47 : apply the following rules for placeholder frames:
48 :
49 : - If a frame HAS a placeholder frame, getting its parent
50 : gets the placeholder's parent.
51 :
52 : - If a frame's first child or next/prev sibling IS a
53 : placeholder frame, then we instead return the real frame.
54 :
55 : - If a frame HAS a placeholder frame, getting its next/prev
56 : sibling gets the placeholder frame's next/prev sibling.
57 :
58 : These are all applied recursively to support multiple levels of
59 : placeholders.
60 : */
61 :
62 : nsIFrame* GetParentFrame(nsIFrame* aFrame);
63 : // like GetParentFrame but returns null once a popup frame is reached
64 : nsIFrame* GetParentFrameNotPopup(nsIFrame* aFrame);
65 :
66 : nsIFrame* GetFirstChild(nsIFrame* aFrame);
67 : nsIFrame* GetLastChild(nsIFrame* aFrame);
68 :
69 : nsIFrame* GetNextSibling(nsIFrame* aFrame);
70 : nsIFrame* GetPrevSibling(nsIFrame* aFrame);
71 :
72 : /*
73 : These methods are overridden by the bidi visual iterator to have the
74 : semantics of "get first child in visual order", "get last child in visual
75 : order", "get next sibling in visual order" and "get previous sibling in visual
76 : order".
77 : */
78 :
79 : virtual nsIFrame* GetFirstChildInner(nsIFrame* aFrame);
80 : virtual nsIFrame* GetLastChildInner(nsIFrame* aFrame);
81 :
82 : virtual nsIFrame* GetNextSiblingInner(nsIFrame* aFrame);
83 : virtual nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame);
84 :
85 : /**
86 : * Return the placeholder frame for aFrame if it has one, otherwise return
87 : * aFrame itself.
88 : */
89 : nsIFrame* GetPlaceholderFrame(nsIFrame* aFrame);
90 : bool IsPopupFrame(nsIFrame* aFrame);
91 :
92 : nsPresContext* const mPresContext;
93 : const bool mLockScroll;
94 : const bool mFollowOOFs;
95 : const bool mSkipPopupChecks;
96 : const nsIteratorType mType;
97 :
98 : private:
99 : nsIFrame* const mStart;
100 : nsIFrame* mCurrent;
101 : nsIFrame* mLast; //the last one that was in current;
102 : int8_t mOffEdge; //0= no -1 to far prev, 1 to far next;
103 : };
104 :
105 :
106 :
107 : // Bidi visual iterator
108 0 : class nsVisualIterator: public nsFrameIterator
109 : {
110 : public:
111 0 : nsVisualIterator(nsPresContext* aPresContext, nsIFrame *aStart,
112 : nsIteratorType aType, bool aLockScroll,
113 0 : bool aFollowOOFs, bool aSkipPopupChecks) :
114 0 : nsFrameIterator(aPresContext, aStart, aType, aLockScroll, aFollowOOFs, aSkipPopupChecks) {}
115 :
116 : protected:
117 : nsIFrame* GetFirstChildInner(nsIFrame* aFrame) override;
118 : nsIFrame* GetLastChildInner(nsIFrame* aFrame) override;
119 :
120 : nsIFrame* GetNextSiblingInner(nsIFrame* aFrame) override;
121 : nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame) override;
122 : };
123 :
124 : /************IMPLEMENTATIONS**************/
125 :
126 : nsresult
127 0 : NS_CreateFrameTraversal(nsIFrameTraversal** aResult)
128 : {
129 0 : NS_ENSURE_ARG_POINTER(aResult);
130 :
131 0 : nsCOMPtr<nsIFrameTraversal> t = new nsFrameTraversal();
132 0 : t.forget(aResult);
133 :
134 0 : return NS_OK;
135 : }
136 :
137 : nsresult
138 0 : NS_NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
139 : nsPresContext* aPresContext,
140 : nsIFrame *aStart,
141 : nsIteratorType aType,
142 : bool aVisual,
143 : bool aLockInScrollView,
144 : bool aFollowOOFs,
145 : bool aSkipPopupChecks)
146 : {
147 0 : if (!aEnumerator || !aStart)
148 0 : return NS_ERROR_NULL_POINTER;
149 :
150 0 : if (aFollowOOFs) {
151 0 : aStart = nsPlaceholderFrame::GetRealFrameFor(aStart);
152 : }
153 :
154 0 : nsCOMPtr<nsIFrameEnumerator> trav;
155 0 : if (aVisual) {
156 : trav = new nsVisualIterator(aPresContext, aStart, aType,
157 0 : aLockInScrollView, aFollowOOFs, aSkipPopupChecks);
158 : } else {
159 : trav = new nsFrameIterator(aPresContext, aStart, aType,
160 0 : aLockInScrollView, aFollowOOFs, aSkipPopupChecks);
161 : }
162 0 : trav.forget(aEnumerator);
163 0 : return NS_OK;
164 : }
165 :
166 :
167 0 : nsFrameTraversal::nsFrameTraversal()
168 : {
169 0 : }
170 :
171 0 : nsFrameTraversal::~nsFrameTraversal()
172 : {
173 0 : }
174 :
175 0 : NS_IMPL_ISUPPORTS(nsFrameTraversal,nsIFrameTraversal)
176 :
177 : NS_IMETHODIMP
178 0 : nsFrameTraversal::NewFrameTraversal(nsIFrameEnumerator **aEnumerator,
179 : nsPresContext* aPresContext,
180 : nsIFrame *aStart,
181 : int32_t aType,
182 : bool aVisual,
183 : bool aLockInScrollView,
184 : bool aFollowOOFs,
185 : bool aSkipPopupChecks)
186 : {
187 0 : return NS_NewFrameTraversal(aEnumerator, aPresContext, aStart,
188 : static_cast<nsIteratorType>(aType),
189 0 : aVisual, aLockInScrollView, aFollowOOFs, aSkipPopupChecks);
190 : }
191 :
192 : // nsFrameIterator implementation
193 :
194 0 : NS_IMPL_ISUPPORTS(nsFrameIterator, nsIFrameEnumerator)
195 :
196 0 : nsFrameIterator::nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart,
197 : nsIteratorType aType, bool aLockInScrollView,
198 0 : bool aFollowOOFs, bool aSkipPopupChecks)
199 : : mPresContext(aPresContext),
200 : mLockScroll(aLockInScrollView),
201 : mFollowOOFs(aFollowOOFs),
202 : mSkipPopupChecks(aSkipPopupChecks),
203 : mType(aType),
204 : mStart(aStart),
205 : mCurrent(aStart),
206 : mLast(aStart),
207 0 : mOffEdge(0)
208 : {
209 0 : MOZ_ASSERT(!aFollowOOFs || !aStart->IsPlaceholderFrame(),
210 : "Caller should have resolved placeholder frame");
211 0 : }
212 :
213 :
214 :
215 : nsIFrame*
216 0 : nsFrameIterator::CurrentItem()
217 : {
218 0 : if (mOffEdge)
219 0 : return nullptr;
220 :
221 0 : return mCurrent;
222 : }
223 :
224 :
225 :
226 : bool
227 0 : nsFrameIterator::IsDone()
228 : {
229 0 : return mOffEdge != 0;
230 : }
231 :
232 : void
233 0 : nsFrameIterator::First()
234 : {
235 0 : mCurrent = mStart;
236 0 : }
237 :
238 : static bool
239 0 : IsRootFrame(nsIFrame* aFrame)
240 : {
241 0 : return aFrame->IsCanvasFrame() || aFrame->IsRootFrame();
242 : }
243 :
244 : void
245 0 : nsFrameIterator::Last()
246 : {
247 : nsIFrame* result;
248 0 : nsIFrame* parent = getCurrent();
249 : // If the current frame is a popup, don't move farther up the tree.
250 : // Otherwise, get the nearest root frame or popup.
251 0 : if (mSkipPopupChecks || !parent->IsMenuPopupFrame()) {
252 0 : while (!IsRootFrame(parent) && (result = GetParentFrameNotPopup(parent)))
253 0 : parent = result;
254 : }
255 :
256 0 : while ((result = GetLastChild(parent))) {
257 0 : parent = result;
258 : }
259 :
260 0 : setCurrent(parent);
261 0 : if (!parent)
262 0 : setOffEdge(1);
263 0 : }
264 :
265 : void
266 0 : nsFrameIterator::Next()
267 : {
268 : // recursive-oid method to get next frame
269 0 : nsIFrame *result = nullptr;
270 0 : nsIFrame *parent = getCurrent();
271 0 : if (!parent)
272 0 : parent = getLast();
273 :
274 0 : if (mType == eLeaf) {
275 : // Drill down to first leaf
276 0 : while ((result = GetFirstChild(parent))) {
277 0 : parent = result;
278 : }
279 0 : } else if (mType == ePreOrder) {
280 0 : result = GetFirstChild(parent);
281 0 : if (result)
282 0 : parent = result;
283 : }
284 :
285 0 : if (parent != getCurrent()) {
286 0 : result = parent;
287 : } else {
288 0 : while (parent) {
289 0 : result = GetNextSibling(parent);
290 0 : if (result) {
291 0 : if (mType != ePreOrder) {
292 0 : parent = result;
293 0 : while ((result = GetFirstChild(parent))) {
294 0 : parent = result;
295 : }
296 0 : result = parent;
297 : }
298 0 : break;
299 : }
300 : else {
301 0 : result = GetParentFrameNotPopup(parent);
302 0 : if (!result || IsRootFrame(result) ||
303 0 : (mLockScroll && result->IsScrollFrame())) {
304 0 : result = nullptr;
305 0 : break;
306 : }
307 0 : if (mType == ePostOrder)
308 0 : break;
309 0 : parent = result;
310 : }
311 : }
312 : }
313 :
314 0 : setCurrent(result);
315 0 : if (!result) {
316 0 : setOffEdge(1);
317 0 : setLast(parent);
318 : }
319 0 : }
320 :
321 : void
322 0 : nsFrameIterator::Prev()
323 : {
324 : // recursive-oid method to get prev frame
325 0 : nsIFrame *result = nullptr;
326 0 : nsIFrame *parent = getCurrent();
327 0 : if (!parent)
328 0 : parent = getLast();
329 :
330 0 : if (mType == eLeaf) {
331 : // Drill down to last leaf
332 0 : while ((result = GetLastChild(parent))) {
333 0 : parent = result;
334 : }
335 0 : } else if (mType == ePostOrder) {
336 0 : result = GetLastChild(parent);
337 0 : if (result)
338 0 : parent = result;
339 : }
340 :
341 0 : if (parent != getCurrent()) {
342 0 : result = parent;
343 : } else {
344 0 : while (parent) {
345 0 : result = GetPrevSibling(parent);
346 0 : if (result) {
347 0 : if (mType != ePostOrder) {
348 0 : parent = result;
349 0 : while ((result = GetLastChild(parent))) {
350 0 : parent = result;
351 : }
352 0 : result = parent;
353 : }
354 0 : break;
355 : } else {
356 0 : result = GetParentFrameNotPopup(parent);
357 0 : if (!result || IsRootFrame(result) ||
358 0 : (mLockScroll && result->IsScrollFrame())) {
359 0 : result = nullptr;
360 0 : break;
361 : }
362 0 : if (mType == ePreOrder)
363 0 : break;
364 0 : parent = result;
365 : }
366 : }
367 : }
368 :
369 0 : setCurrent(result);
370 0 : if (!result) {
371 0 : setOffEdge(-1);
372 0 : setLast(parent);
373 : }
374 0 : }
375 :
376 : nsIFrame*
377 0 : nsFrameIterator::GetParentFrame(nsIFrame* aFrame)
378 : {
379 0 : if (mFollowOOFs)
380 0 : aFrame = GetPlaceholderFrame(aFrame);
381 0 : if (aFrame)
382 0 : return aFrame->GetParent();
383 :
384 0 : return nullptr;
385 : }
386 :
387 : nsIFrame*
388 0 : nsFrameIterator::GetParentFrameNotPopup(nsIFrame* aFrame)
389 : {
390 0 : if (mFollowOOFs)
391 0 : aFrame = GetPlaceholderFrame(aFrame);
392 0 : if (aFrame) {
393 0 : nsIFrame* parent = aFrame->GetParent();
394 0 : if (!IsPopupFrame(parent))
395 0 : return parent;
396 : }
397 :
398 0 : return nullptr;
399 : }
400 :
401 : nsIFrame*
402 0 : nsFrameIterator::GetFirstChild(nsIFrame* aFrame)
403 : {
404 0 : nsIFrame* result = GetFirstChildInner(aFrame);
405 0 : if (mLockScroll && result && result->IsScrollFrame())
406 0 : return nullptr;
407 0 : if (result && mFollowOOFs) {
408 0 : result = nsPlaceholderFrame::GetRealFrameFor(result);
409 :
410 0 : if (IsPopupFrame(result))
411 0 : result = GetNextSibling(result);
412 : }
413 0 : return result;
414 : }
415 :
416 : nsIFrame*
417 0 : nsFrameIterator::GetLastChild(nsIFrame* aFrame)
418 : {
419 0 : nsIFrame* result = GetLastChildInner(aFrame);
420 0 : if (mLockScroll && result && result->IsScrollFrame())
421 0 : return nullptr;
422 0 : if (result && mFollowOOFs) {
423 0 : result = nsPlaceholderFrame::GetRealFrameFor(result);
424 :
425 0 : if (IsPopupFrame(result))
426 0 : result = GetPrevSibling(result);
427 : }
428 0 : return result;
429 : }
430 :
431 : nsIFrame*
432 0 : nsFrameIterator::GetNextSibling(nsIFrame* aFrame)
433 : {
434 0 : nsIFrame* result = nullptr;
435 0 : if (mFollowOOFs)
436 0 : aFrame = GetPlaceholderFrame(aFrame);
437 0 : if (aFrame) {
438 0 : result = GetNextSiblingInner(aFrame);
439 0 : if (result && mFollowOOFs)
440 0 : result = nsPlaceholderFrame::GetRealFrameFor(result);
441 : }
442 :
443 0 : if (mFollowOOFs && IsPopupFrame(result))
444 0 : result = GetNextSibling(result);
445 :
446 0 : return result;
447 : }
448 :
449 : nsIFrame*
450 0 : nsFrameIterator::GetPrevSibling(nsIFrame* aFrame)
451 : {
452 0 : nsIFrame* result = nullptr;
453 0 : if (mFollowOOFs)
454 0 : aFrame = GetPlaceholderFrame(aFrame);
455 0 : if (aFrame) {
456 0 : result = GetPrevSiblingInner(aFrame);
457 0 : if (result && mFollowOOFs)
458 0 : result = nsPlaceholderFrame::GetRealFrameFor(result);
459 : }
460 :
461 0 : if (mFollowOOFs && IsPopupFrame(result))
462 0 : result = GetPrevSibling(result);
463 :
464 0 : return result;
465 : }
466 :
467 : nsIFrame*
468 0 : nsFrameIterator::GetFirstChildInner(nsIFrame* aFrame) {
469 0 : return aFrame->PrincipalChildList().FirstChild();
470 : }
471 :
472 : nsIFrame*
473 0 : nsFrameIterator::GetLastChildInner(nsIFrame* aFrame) {
474 0 : return aFrame->PrincipalChildList().LastChild();
475 : }
476 :
477 : nsIFrame*
478 0 : nsFrameIterator::GetNextSiblingInner(nsIFrame* aFrame) {
479 0 : return aFrame->GetNextSibling();
480 : }
481 :
482 : nsIFrame*
483 0 : nsFrameIterator::GetPrevSiblingInner(nsIFrame* aFrame) {
484 0 : return aFrame->GetPrevSibling();
485 : }
486 :
487 :
488 : nsIFrame*
489 0 : nsFrameIterator::GetPlaceholderFrame(nsIFrame* aFrame)
490 : {
491 0 : if (MOZ_LIKELY(!aFrame || !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) {
492 0 : return aFrame;
493 : }
494 0 : nsIFrame* placeholder = aFrame->GetPlaceholderFrame();
495 0 : return placeholder ? placeholder : aFrame;
496 : }
497 :
498 : bool
499 0 : nsFrameIterator::IsPopupFrame(nsIFrame* aFrame)
500 : {
501 : // If skipping popup checks, pretend this isn't one.
502 0 : if (mSkipPopupChecks) {
503 0 : return false;
504 : }
505 :
506 0 : return (aFrame &&
507 0 : aFrame->StyleDisplay()->mDisplay == StyleDisplay::MozPopup);
508 : }
509 :
510 : // nsVisualIterator implementation
511 :
512 : nsIFrame*
513 0 : nsVisualIterator::GetFirstChildInner(nsIFrame* aFrame) {
514 0 : return aFrame->PrincipalChildList().GetNextVisualFor(nullptr);
515 : }
516 :
517 : nsIFrame*
518 0 : nsVisualIterator::GetLastChildInner(nsIFrame* aFrame) {
519 0 : return aFrame->PrincipalChildList().GetPrevVisualFor(nullptr);
520 : }
521 :
522 : nsIFrame*
523 0 : nsVisualIterator::GetNextSiblingInner(nsIFrame* aFrame) {
524 0 : nsIFrame* parent = GetParentFrame(aFrame);
525 0 : if (!parent)
526 0 : return nullptr;
527 0 : return parent->PrincipalChildList().GetNextVisualFor(aFrame);
528 : }
529 :
530 : nsIFrame*
531 0 : nsVisualIterator::GetPrevSiblingInner(nsIFrame* aFrame) {
532 0 : nsIFrame* parent = GetParentFrame(aFrame);
533 0 : if (!parent)
534 0 : return nullptr;
535 0 : return parent->PrincipalChildList().GetPrevVisualFor(aFrame);
536 : }
|