|
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 |
|
6 /* base class #1 for rendering objects that have child lists */ |
|
7 |
|
8 #include "nsContainerFrame.h" |
|
9 |
|
10 #include "nsAbsoluteContainingBlock.h" |
|
11 #include "nsIDocument.h" |
|
12 #include "nsPresContext.h" |
|
13 #include "nsStyleContext.h" |
|
14 #include "nsRect.h" |
|
15 #include "nsPoint.h" |
|
16 #include "nsStyleConsts.h" |
|
17 #include "nsView.h" |
|
18 #include "nsIPresShell.h" |
|
19 #include "nsCOMPtr.h" |
|
20 #include "nsGkAtoms.h" |
|
21 #include "nsViewManager.h" |
|
22 #include "nsIWidget.h" |
|
23 #include "nsCSSRendering.h" |
|
24 #include "nsError.h" |
|
25 #include "nsDisplayList.h" |
|
26 #include "nsIBaseWindow.h" |
|
27 #include "nsBoxLayoutState.h" |
|
28 #include "nsCSSFrameConstructor.h" |
|
29 #include "nsBlockFrame.h" |
|
30 #include "mozilla/AutoRestore.h" |
|
31 #include "nsIFrameInlines.h" |
|
32 #include "nsPrintfCString.h" |
|
33 #include <algorithm> |
|
34 |
|
35 #ifdef DEBUG |
|
36 #undef NOISY |
|
37 #else |
|
38 #undef NOISY |
|
39 #endif |
|
40 |
|
41 using namespace mozilla; |
|
42 using namespace mozilla::dom; |
|
43 using namespace mozilla::layout; |
|
44 |
|
45 NS_IMPL_FRAMEARENA_HELPERS(nsContainerFrame) |
|
46 |
|
47 nsContainerFrame::~nsContainerFrame() |
|
48 { |
|
49 } |
|
50 |
|
51 NS_QUERYFRAME_HEAD(nsContainerFrame) |
|
52 NS_QUERYFRAME_ENTRY(nsContainerFrame) |
|
53 NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame) |
|
54 |
|
55 void |
|
56 nsContainerFrame::Init(nsIContent* aContent, |
|
57 nsIFrame* aParent, |
|
58 nsIFrame* aPrevInFlow) |
|
59 { |
|
60 nsSplittableFrame::Init(aContent, aParent, aPrevInFlow); |
|
61 if (aPrevInFlow) { |
|
62 // Make sure we copy bits from our prev-in-flow that will affect |
|
63 // us. A continuation for a container frame needs to know if it |
|
64 // has a child with a view so that we'll properly reposition it. |
|
65 if (aPrevInFlow->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) |
|
66 AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW); |
|
67 } |
|
68 } |
|
69 |
|
70 nsresult |
|
71 nsContainerFrame::SetInitialChildList(ChildListID aListID, |
|
72 nsFrameList& aChildList) |
|
73 { |
|
74 nsresult result; |
|
75 if (mFrames.NotEmpty()) { |
|
76 // We already have child frames which means we've already been |
|
77 // initialized |
|
78 NS_NOTREACHED("unexpected second call to SetInitialChildList"); |
|
79 result = NS_ERROR_UNEXPECTED; |
|
80 } else if (aListID != kPrincipalList) { |
|
81 // All we know about is the principal child list. |
|
82 NS_NOTREACHED("unknown frame list"); |
|
83 result = NS_ERROR_INVALID_ARG; |
|
84 } else { |
|
85 #ifdef DEBUG |
|
86 nsFrame::VerifyDirtyBitSet(aChildList); |
|
87 #endif |
|
88 mFrames.SetFrames(aChildList); |
|
89 result = NS_OK; |
|
90 } |
|
91 return result; |
|
92 } |
|
93 |
|
94 nsresult |
|
95 nsContainerFrame::AppendFrames(ChildListID aListID, |
|
96 nsFrameList& aFrameList) |
|
97 { |
|
98 if (aListID != kPrincipalList) { |
|
99 if (aListID != kNoReflowPrincipalList) |
|
100 { |
|
101 NS_ERROR("unexpected child list"); |
|
102 return NS_ERROR_INVALID_ARG; |
|
103 } |
|
104 } |
|
105 if (aFrameList.NotEmpty()) { |
|
106 mFrames.AppendFrames(this, aFrameList); |
|
107 |
|
108 // Ask the parent frame to reflow me. |
|
109 if (aListID == kPrincipalList) |
|
110 { |
|
111 PresContext()->PresShell()-> |
|
112 FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
|
113 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
114 } |
|
115 } |
|
116 return NS_OK; |
|
117 } |
|
118 |
|
119 nsresult |
|
120 nsContainerFrame::InsertFrames(ChildListID aListID, |
|
121 nsIFrame* aPrevFrame, |
|
122 nsFrameList& aFrameList) |
|
123 { |
|
124 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, |
|
125 "inserting after sibling frame with different parent"); |
|
126 |
|
127 if (aListID != kPrincipalList) { |
|
128 if (aListID != kNoReflowPrincipalList) |
|
129 { |
|
130 NS_ERROR("unexpected child list"); |
|
131 return NS_ERROR_INVALID_ARG; |
|
132 } |
|
133 } |
|
134 if (aFrameList.NotEmpty()) { |
|
135 // Insert frames after aPrevFrame |
|
136 mFrames.InsertFrames(this, aPrevFrame, aFrameList); |
|
137 |
|
138 if (aListID == kPrincipalList) |
|
139 { |
|
140 PresContext()->PresShell()-> |
|
141 FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
|
142 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
143 } |
|
144 } |
|
145 return NS_OK; |
|
146 } |
|
147 |
|
148 nsresult |
|
149 nsContainerFrame::RemoveFrame(ChildListID aListID, |
|
150 nsIFrame* aOldFrame) |
|
151 { |
|
152 if (aListID != kPrincipalList) { |
|
153 if (kNoReflowPrincipalList != aListID) |
|
154 { |
|
155 NS_ERROR("unexpected child list"); |
|
156 return NS_ERROR_INVALID_ARG; |
|
157 } |
|
158 } |
|
159 |
|
160 // Loop and destroy aOldFrame and all of its continuations. |
|
161 // Request a reflow on the parent frames involved unless we were explicitly |
|
162 // told not to (kNoReflowPrincipalList). |
|
163 bool generateReflowCommand = true; |
|
164 if (kNoReflowPrincipalList == aListID) { |
|
165 generateReflowCommand = false; |
|
166 } |
|
167 nsIPresShell* shell = PresContext()->PresShell(); |
|
168 nsContainerFrame* lastParent = nullptr; |
|
169 while (aOldFrame) { |
|
170 //XXXfr probably should use StealFrame here. I'm not sure if we need to |
|
171 // check the overflow lists atm, but we'll need a prescontext lookup |
|
172 // for overflow containers once we can split abspos elements with |
|
173 // inline containing blocks. |
|
174 nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation(); |
|
175 nsContainerFrame* parent = |
|
176 static_cast<nsContainerFrame*>(aOldFrame->GetParent()); |
|
177 parent->StealFrame(aOldFrame, true); |
|
178 aOldFrame->Destroy(); |
|
179 aOldFrame = oldFrameNextContinuation; |
|
180 if (parent != lastParent && generateReflowCommand) { |
|
181 shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange, |
|
182 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
183 lastParent = parent; |
|
184 } |
|
185 } |
|
186 return NS_OK; |
|
187 } |
|
188 |
|
189 void |
|
190 nsContainerFrame::DestroyAbsoluteFrames(nsIFrame* aDestructRoot) |
|
191 { |
|
192 if (IsAbsoluteContainer()) { |
|
193 GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot); |
|
194 MarkAsNotAbsoluteContainingBlock(); |
|
195 } |
|
196 } |
|
197 |
|
198 void |
|
199 nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame* aDestructRoot, |
|
200 nsIPresShell* aPresShell, |
|
201 FramePropertyTable* aPropTable, |
|
202 const FramePropertyDescriptor* aProp) |
|
203 { |
|
204 // Note that the last frame can be removed through another route and thus |
|
205 // delete the property -- that's why we fetch the property again before |
|
206 // removing each frame rather than fetching it once and iterating the list. |
|
207 while (nsFrameList* frameList = |
|
208 static_cast<nsFrameList*>(aPropTable->Get(this, aProp))) { |
|
209 nsIFrame* frame = frameList->RemoveFirstChild(); |
|
210 if (MOZ_LIKELY(frame)) { |
|
211 frame->DestroyFrom(aDestructRoot); |
|
212 } else { |
|
213 aPropTable->Remove(this, aProp); |
|
214 frameList->Delete(aPresShell); |
|
215 return; |
|
216 } |
|
217 } |
|
218 } |
|
219 |
|
220 void |
|
221 nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
222 { |
|
223 // Prevent event dispatch during destruction. |
|
224 if (HasView()) { |
|
225 GetView()->SetFrame(nullptr); |
|
226 } |
|
227 |
|
228 DestroyAbsoluteFrames(aDestructRoot); |
|
229 |
|
230 // Destroy frames on the principal child list. |
|
231 mFrames.DestroyFramesFrom(aDestructRoot); |
|
232 |
|
233 // Destroy frames on the auxiliary frame lists and delete the lists. |
|
234 nsPresContext* pc = PresContext(); |
|
235 nsIPresShell* shell = pc->PresShell(); |
|
236 FramePropertyTable* props = pc->PropertyTable(); |
|
237 SafelyDestroyFrameListProp(aDestructRoot, shell, props, OverflowProperty()); |
|
238 |
|
239 MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers) || |
|
240 !(props->Get(this, nsContainerFrame::OverflowContainersProperty()) || |
|
241 props->Get(this, nsContainerFrame::ExcessOverflowContainersProperty())), |
|
242 "this type of frame should't have overflow containers"); |
|
243 |
|
244 SafelyDestroyFrameListProp(aDestructRoot, shell, props, |
|
245 OverflowContainersProperty()); |
|
246 SafelyDestroyFrameListProp(aDestructRoot, shell, props, |
|
247 ExcessOverflowContainersProperty()); |
|
248 |
|
249 nsSplittableFrame::DestroyFrom(aDestructRoot); |
|
250 } |
|
251 |
|
252 ///////////////////////////////////////////////////////////////////////////// |
|
253 // Child frame enumeration |
|
254 |
|
255 const nsFrameList& |
|
256 nsContainerFrame::GetChildList(ChildListID aListID) const |
|
257 { |
|
258 // We only know about the principal child list and the overflow lists. |
|
259 switch (aListID) { |
|
260 case kPrincipalList: |
|
261 return mFrames; |
|
262 case kOverflowList: { |
|
263 nsFrameList* list = GetOverflowFrames(); |
|
264 return list ? *list : nsFrameList::EmptyList(); |
|
265 } |
|
266 case kOverflowContainersList: { |
|
267 nsFrameList* list = GetPropTableFrames(OverflowContainersProperty()); |
|
268 return list ? *list : nsFrameList::EmptyList(); |
|
269 } |
|
270 case kExcessOverflowContainersList: { |
|
271 nsFrameList* list = |
|
272 GetPropTableFrames(ExcessOverflowContainersProperty()); |
|
273 return list ? *list : nsFrameList::EmptyList(); |
|
274 } |
|
275 default: |
|
276 return nsSplittableFrame::GetChildList(aListID); |
|
277 } |
|
278 } |
|
279 |
|
280 static void AppendIfNonempty(const nsIFrame* aFrame, |
|
281 FramePropertyTable* aPropTable, |
|
282 const FramePropertyDescriptor* aProperty, |
|
283 nsTArray<nsIFrame::ChildList>* aLists, |
|
284 nsIFrame::ChildListID aListID) |
|
285 { |
|
286 nsFrameList* list = static_cast<nsFrameList*>( |
|
287 aPropTable->Get(aFrame, aProperty)); |
|
288 if (list) { |
|
289 list->AppendIfNonempty(aLists, aListID); |
|
290 } |
|
291 } |
|
292 |
|
293 void |
|
294 nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const |
|
295 { |
|
296 mFrames.AppendIfNonempty(aLists, kPrincipalList); |
|
297 FramePropertyTable* propTable = PresContext()->PropertyTable(); |
|
298 ::AppendIfNonempty(this, propTable, OverflowProperty(), |
|
299 aLists, kOverflowList); |
|
300 if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) { |
|
301 ::AppendIfNonempty(this, propTable, OverflowContainersProperty(), |
|
302 aLists, kOverflowContainersList); |
|
303 ::AppendIfNonempty(this, propTable, ExcessOverflowContainersProperty(), |
|
304 aLists, kExcessOverflowContainersList); |
|
305 } |
|
306 nsSplittableFrame::GetChildLists(aLists); |
|
307 } |
|
308 |
|
309 ///////////////////////////////////////////////////////////////////////////// |
|
310 // Painting/Events |
|
311 |
|
312 void |
|
313 nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
314 const nsRect& aDirtyRect, |
|
315 const nsDisplayListSet& aLists) |
|
316 { |
|
317 DisplayBorderBackgroundOutline(aBuilder, aLists); |
|
318 |
|
319 BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists); |
|
320 } |
|
321 |
|
322 void |
|
323 nsContainerFrame::BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder, |
|
324 const nsRect& aDirtyRect, |
|
325 const nsDisplayListSet& aLists, |
|
326 uint32_t aFlags) |
|
327 { |
|
328 nsIFrame* kid = mFrames.FirstChild(); |
|
329 // Put each child's background directly onto the content list |
|
330 nsDisplayListSet set(aLists, aLists.Content()); |
|
331 // The children should be in content order |
|
332 while (kid) { |
|
333 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, aFlags); |
|
334 kid = kid->GetNextSibling(); |
|
335 } |
|
336 } |
|
337 |
|
338 /* virtual */ void |
|
339 nsContainerFrame::ChildIsDirty(nsIFrame* aChild) |
|
340 { |
|
341 NS_ASSERTION(NS_SUBTREE_DIRTY(aChild), "child isn't actually dirty"); |
|
342 |
|
343 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); |
|
344 } |
|
345 |
|
346 bool |
|
347 nsContainerFrame::IsLeaf() const |
|
348 { |
|
349 return false; |
|
350 } |
|
351 |
|
352 nsIFrame::FrameSearchResult |
|
353 nsContainerFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset) |
|
354 { |
|
355 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); |
|
356 // Don't allow the caret to stay in an empty (leaf) container frame. |
|
357 return CONTINUE_EMPTY; |
|
358 } |
|
359 |
|
360 nsIFrame::FrameSearchResult |
|
361 nsContainerFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset, |
|
362 bool aRespectClusters) |
|
363 { |
|
364 NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); |
|
365 // Don't allow the caret to stay in an empty (leaf) container frame. |
|
366 return CONTINUE_EMPTY; |
|
367 } |
|
368 |
|
369 ///////////////////////////////////////////////////////////////////////////// |
|
370 // Helper member functions |
|
371 |
|
372 static nsresult |
|
373 ReparentFrameViewTo(nsIFrame* aFrame, |
|
374 nsViewManager* aViewManager, |
|
375 nsView* aNewParentView, |
|
376 nsView* aOldParentView) |
|
377 { |
|
378 |
|
379 // XXX What to do about placeholder views for "position: fixed" elements? |
|
380 // They should be reparented too. |
|
381 |
|
382 // Does aFrame have a view? |
|
383 if (aFrame->HasView()) { |
|
384 #ifdef MOZ_XUL |
|
385 if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) { |
|
386 // This view must be parented by the root view, don't reparent it. |
|
387 return NS_OK; |
|
388 } |
|
389 #endif |
|
390 nsView* view = aFrame->GetView(); |
|
391 // Verify that the current parent view is what we think it is |
|
392 //nsView* parentView; |
|
393 //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view"); |
|
394 |
|
395 aViewManager->RemoveChild(view); |
|
396 |
|
397 // The view will remember the Z-order and other attributes that have been set on it. |
|
398 nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame); |
|
399 aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr); |
|
400 } else { |
|
401 nsIFrame::ChildListIterator lists(aFrame); |
|
402 for (; !lists.IsDone(); lists.Next()) { |
|
403 // Iterate the child frames, and check each child frame to see if it has |
|
404 // a view |
|
405 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
406 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
407 ReparentFrameViewTo(childFrames.get(), aViewManager, |
|
408 aNewParentView, aOldParentView); |
|
409 } |
|
410 } |
|
411 } |
|
412 |
|
413 return NS_OK; |
|
414 } |
|
415 |
|
416 void |
|
417 nsContainerFrame::CreateViewForFrame(nsIFrame* aFrame, |
|
418 bool aForce) |
|
419 { |
|
420 if (aFrame->HasView()) { |
|
421 return; |
|
422 } |
|
423 |
|
424 // If we don't yet have a view, see if we need a view |
|
425 if (!aForce && !aFrame->NeedsView()) { |
|
426 // don't need a view |
|
427 return; |
|
428 } |
|
429 |
|
430 nsView* parentView = aFrame->GetParent()->GetClosestView(); |
|
431 NS_ASSERTION(parentView, "no parent with view"); |
|
432 |
|
433 nsViewManager* viewManager = parentView->GetViewManager(); |
|
434 NS_ASSERTION(viewManager, "null view manager"); |
|
435 |
|
436 // Create a view |
|
437 nsView* view = viewManager->CreateView(aFrame->GetRect(), parentView); |
|
438 |
|
439 SyncFrameViewProperties(aFrame->PresContext(), aFrame, nullptr, view); |
|
440 |
|
441 nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame); |
|
442 // we insert this view 'above' the insertBefore view, unless insertBefore is null, |
|
443 // in which case we want to call with aAbove == false to insert at the beginning |
|
444 // in document order |
|
445 viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr); |
|
446 |
|
447 // REVIEW: Don't create a widget for fixed-pos elements anymore. |
|
448 // ComputeRepaintRegionForCopy will calculate the right area to repaint |
|
449 // when we scroll. |
|
450 // Reparent views on any child frames (or their descendants) to this |
|
451 // view. We can just call ReparentFrameViewTo on this frame because |
|
452 // we know this frame has no view, so it will crawl the children. Also, |
|
453 // we know that any descendants with views must have 'parentView' as their |
|
454 // parent view. |
|
455 ReparentFrameViewTo(aFrame, viewManager, view, parentView); |
|
456 |
|
457 // Remember our view |
|
458 aFrame->SetView(view); |
|
459 |
|
460 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, |
|
461 ("nsContainerFrame::CreateViewForFrame: frame=%p view=%p", |
|
462 aFrame)); |
|
463 } |
|
464 |
|
465 /** |
|
466 * Position the view associated with |aKidFrame|, if there is one. A |
|
467 * container frame should call this method after positioning a frame, |
|
468 * but before |Reflow|. |
|
469 */ |
|
470 void |
|
471 nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame) |
|
472 { |
|
473 nsIFrame* parentFrame = aKidFrame->GetParent(); |
|
474 if (!aKidFrame->HasView() || !parentFrame) |
|
475 return; |
|
476 |
|
477 nsView* view = aKidFrame->GetView(); |
|
478 nsViewManager* vm = view->GetViewManager(); |
|
479 nsPoint pt; |
|
480 nsView* ancestorView = parentFrame->GetClosestView(&pt); |
|
481 |
|
482 if (ancestorView != view->GetParent()) { |
|
483 NS_ASSERTION(ancestorView == view->GetParent()->GetParent(), |
|
484 "Allowed only one anonymous view between frames"); |
|
485 // parentFrame is responsible for positioning aKidFrame's view |
|
486 // explicitly |
|
487 return; |
|
488 } |
|
489 |
|
490 pt += aKidFrame->GetPosition(); |
|
491 vm->MoveViewTo(view, pt.x, pt.y); |
|
492 } |
|
493 |
|
494 nsresult |
|
495 nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame, |
|
496 nsIFrame* aOldParentFrame, |
|
497 nsIFrame* aNewParentFrame) |
|
498 { |
|
499 NS_PRECONDITION(aChildFrame, "null child frame pointer"); |
|
500 NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer"); |
|
501 NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer"); |
|
502 NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame"); |
|
503 |
|
504 // See if either the old parent frame or the new parent frame have a view |
|
505 while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) { |
|
506 // Walk up both the old parent frame and the new parent frame nodes |
|
507 // stopping when we either find a common parent or views for one |
|
508 // or both of the frames. |
|
509 // |
|
510 // This works well in the common case where we push/pull and the old parent |
|
511 // frame and the new parent frame are part of the same flow. They will |
|
512 // typically be the same distance (height wise) from the |
|
513 aOldParentFrame = aOldParentFrame->GetParent(); |
|
514 aNewParentFrame = aNewParentFrame->GetParent(); |
|
515 |
|
516 // We should never walk all the way to the root frame without finding |
|
517 // a view |
|
518 NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view"); |
|
519 |
|
520 // See if we reached a common ancestor |
|
521 if (aOldParentFrame == aNewParentFrame) { |
|
522 break; |
|
523 } |
|
524 } |
|
525 |
|
526 // See if we found a common parent frame |
|
527 if (aOldParentFrame == aNewParentFrame) { |
|
528 // We found a common parent and there are no views between the old parent |
|
529 // and the common parent or the new parent frame and the common parent. |
|
530 // Because neither the old parent frame nor the new parent frame have views, |
|
531 // then any child views don't need reparenting |
|
532 return NS_OK; |
|
533 } |
|
534 |
|
535 // We found views for one or both of the ancestor frames before we |
|
536 // found a common ancestor. |
|
537 nsView* oldParentView = aOldParentFrame->GetClosestView(); |
|
538 nsView* newParentView = aNewParentFrame->GetClosestView(); |
|
539 |
|
540 // See if the old parent frame and the new parent frame are in the |
|
541 // same view sub-hierarchy. If they are then we don't have to do |
|
542 // anything |
|
543 if (oldParentView != newParentView) { |
|
544 // They're not so we need to reparent any child views |
|
545 return ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView, |
|
546 oldParentView); |
|
547 } |
|
548 |
|
549 return NS_OK; |
|
550 } |
|
551 |
|
552 nsresult |
|
553 nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList, |
|
554 nsIFrame* aOldParentFrame, |
|
555 nsIFrame* aNewParentFrame) |
|
556 { |
|
557 NS_PRECONDITION(aChildFrameList.NotEmpty(), "empty child frame list"); |
|
558 NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer"); |
|
559 NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer"); |
|
560 NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame"); |
|
561 |
|
562 // See if either the old parent frame or the new parent frame have a view |
|
563 while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) { |
|
564 // Walk up both the old parent frame and the new parent frame nodes |
|
565 // stopping when we either find a common parent or views for one |
|
566 // or both of the frames. |
|
567 // |
|
568 // This works well in the common case where we push/pull and the old parent |
|
569 // frame and the new parent frame are part of the same flow. They will |
|
570 // typically be the same distance (height wise) from the |
|
571 aOldParentFrame = aOldParentFrame->GetParent(); |
|
572 aNewParentFrame = aNewParentFrame->GetParent(); |
|
573 |
|
574 // We should never walk all the way to the root frame without finding |
|
575 // a view |
|
576 NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view"); |
|
577 |
|
578 // See if we reached a common ancestor |
|
579 if (aOldParentFrame == aNewParentFrame) { |
|
580 break; |
|
581 } |
|
582 } |
|
583 |
|
584 |
|
585 // See if we found a common parent frame |
|
586 if (aOldParentFrame == aNewParentFrame) { |
|
587 // We found a common parent and there are no views between the old parent |
|
588 // and the common parent or the new parent frame and the common parent. |
|
589 // Because neither the old parent frame nor the new parent frame have views, |
|
590 // then any child views don't need reparenting |
|
591 return NS_OK; |
|
592 } |
|
593 |
|
594 // We found views for one or both of the ancestor frames before we |
|
595 // found a common ancestor. |
|
596 nsView* oldParentView = aOldParentFrame->GetClosestView(); |
|
597 nsView* newParentView = aNewParentFrame->GetClosestView(); |
|
598 |
|
599 // See if the old parent frame and the new parent frame are in the |
|
600 // same view sub-hierarchy. If they are then we don't have to do |
|
601 // anything |
|
602 if (oldParentView != newParentView) { |
|
603 nsViewManager* viewManager = oldParentView->GetViewManager(); |
|
604 |
|
605 // They're not so we need to reparent any child views |
|
606 for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) { |
|
607 ReparentFrameViewTo(e.get(), viewManager, newParentView, oldParentView); |
|
608 } |
|
609 } |
|
610 |
|
611 return NS_OK; |
|
612 } |
|
613 |
|
614 static nsIWidget* |
|
615 GetPresContextContainerWidget(nsPresContext* aPresContext) |
|
616 { |
|
617 nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer(); |
|
618 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container); |
|
619 if (!baseWindow) |
|
620 return nullptr; |
|
621 |
|
622 nsCOMPtr<nsIWidget> mainWidget; |
|
623 baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); |
|
624 return mainWidget; |
|
625 } |
|
626 |
|
627 static bool |
|
628 IsTopLevelWidget(nsIWidget* aWidget) |
|
629 { |
|
630 nsWindowType windowType = aWidget->WindowType(); |
|
631 return windowType == eWindowType_toplevel || |
|
632 windowType == eWindowType_dialog || |
|
633 windowType == eWindowType_sheet; |
|
634 // popups aren't toplevel so they're not handled here |
|
635 } |
|
636 |
|
637 void |
|
638 nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, |
|
639 nsIFrame* aFrame, |
|
640 nsView* aView, |
|
641 nsRenderingContext* aRC) |
|
642 { |
|
643 #ifdef MOZ_XUL |
|
644 if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget()) |
|
645 return; |
|
646 |
|
647 nsIWidget* windowWidget = GetPresContextContainerWidget(aPresContext); |
|
648 if (!windowWidget || !IsTopLevelWidget(windowWidget)) |
|
649 return; |
|
650 |
|
651 nsViewManager* vm = aView->GetViewManager(); |
|
652 nsView* rootView = vm->GetRootView(); |
|
653 |
|
654 if (aView != rootView) |
|
655 return; |
|
656 |
|
657 Element* rootElement = aPresContext->Document()->GetRootElement(); |
|
658 if (!rootElement || !rootElement->IsXUL()) { |
|
659 // Scrollframes use native widgets which don't work well with |
|
660 // translucent windows, at least in Windows XP. So if the document |
|
661 // has a root scrollrame it's useless to try to make it transparent, |
|
662 // we'll just get something broken. |
|
663 // nsCSSFrameConstructor::ConstructRootFrame constructs root |
|
664 // scrollframes whenever the root element is not a XUL element, so |
|
665 // we test for that here. We can't just call |
|
666 // presShell->GetRootScrollFrame() since that might not have |
|
667 // been constructed yet. |
|
668 // We can change this to allow translucent toplevel HTML documents |
|
669 // (e.g. to do something like Dashboard widgets), once we |
|
670 // have broad support for translucent scrolled documents, but be |
|
671 // careful because apparently some Firefox extensions expect |
|
672 // openDialog("something.html") to produce an opaque window |
|
673 // even if the HTML doesn't have a background-color set. |
|
674 return; |
|
675 } |
|
676 |
|
677 nsIFrame *rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame(); |
|
678 if (!rootFrame) |
|
679 return; |
|
680 |
|
681 nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame); |
|
682 nsIWidget* viewWidget = aView->GetWidget(); |
|
683 viewWidget->SetTransparencyMode(mode); |
|
684 windowWidget->SetWindowShadowStyle(rootFrame->StyleUIReset()->mWindowShadow); |
|
685 |
|
686 if (!aRC) |
|
687 return; |
|
688 |
|
689 nsBoxLayoutState aState(aPresContext, aRC); |
|
690 nsSize minSize = rootFrame->GetMinSize(aState); |
|
691 nsSize maxSize = rootFrame->GetMaxSize(aState); |
|
692 |
|
693 SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize); |
|
694 #endif |
|
695 } |
|
696 |
|
697 void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext, |
|
698 nsIWidget* aWidget, |
|
699 const nsSize& aMinSize, |
|
700 const nsSize& aMaxSize) |
|
701 { |
|
702 nsIntSize devMinSize(aPresContext->AppUnitsToDevPixels(aMinSize.width), |
|
703 aPresContext->AppUnitsToDevPixels(aMinSize.height)); |
|
704 nsIntSize devMaxSize(aMaxSize.width == NS_INTRINSICSIZE ? NS_MAXSIZE : |
|
705 aPresContext->AppUnitsToDevPixels(aMaxSize.width), |
|
706 aMaxSize.height == NS_INTRINSICSIZE ? NS_MAXSIZE : |
|
707 aPresContext->AppUnitsToDevPixels(aMaxSize.height)); |
|
708 widget::SizeConstraints constraints(devMinSize, devMaxSize); |
|
709 |
|
710 // The sizes are in inner window sizes, so convert them into outer window sizes. |
|
711 // Use a size of (200, 200) as only the difference between the inner and outer |
|
712 // size is needed. |
|
713 nsIntSize windowSize = aWidget->ClientToWindowSize(nsIntSize(200, 200)); |
|
714 if (constraints.mMinSize.width) |
|
715 constraints.mMinSize.width += windowSize.width - 200; |
|
716 if (constraints.mMinSize.height) |
|
717 constraints.mMinSize.height += windowSize.height - 200; |
|
718 if (constraints.mMaxSize.width != NS_MAXSIZE) |
|
719 constraints.mMaxSize.width += windowSize.width - 200; |
|
720 if (constraints.mMaxSize.height != NS_MAXSIZE) |
|
721 constraints.mMaxSize.height += windowSize.height - 200; |
|
722 |
|
723 aWidget->SetSizeConstraints(constraints); |
|
724 } |
|
725 |
|
726 void |
|
727 nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext, |
|
728 nsIFrame* aFrame, |
|
729 nsView* aView, |
|
730 const nsRect& aVisualOverflowArea, |
|
731 uint32_t aFlags) |
|
732 { |
|
733 if (!aView) { |
|
734 return; |
|
735 } |
|
736 |
|
737 // Make sure the view is sized and positioned correctly |
|
738 if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { |
|
739 PositionFrameView(aFrame); |
|
740 } |
|
741 |
|
742 if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) { |
|
743 nsViewManager* vm = aView->GetViewManager(); |
|
744 |
|
745 vm->ResizeView(aView, aVisualOverflowArea, true); |
|
746 } |
|
747 } |
|
748 |
|
749 void |
|
750 nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext, |
|
751 nsIFrame* aFrame, |
|
752 nsStyleContext* aStyleContext, |
|
753 nsView* aView, |
|
754 uint32_t aFlags) |
|
755 { |
|
756 NS_ASSERTION(!aStyleContext || aFrame->StyleContext() == aStyleContext, |
|
757 "Wrong style context for frame?"); |
|
758 |
|
759 if (!aView) { |
|
760 return; |
|
761 } |
|
762 |
|
763 nsViewManager* vm = aView->GetViewManager(); |
|
764 |
|
765 if (nullptr == aStyleContext) { |
|
766 aStyleContext = aFrame->StyleContext(); |
|
767 } |
|
768 |
|
769 // Make sure visibility is correct. This only affects nsSubdocumentFrame. |
|
770 if (0 == (aFlags & NS_FRAME_NO_VISIBILITY) && |
|
771 !aFrame->SupportsVisibilityHidden()) { |
|
772 // See if the view should be hidden or visible |
|
773 vm->SetViewVisibility(aView, |
|
774 aStyleContext->StyleVisibility()->IsVisible() |
|
775 ? nsViewVisibility_kShow : nsViewVisibility_kHide); |
|
776 } |
|
777 |
|
778 // See if the frame is being relatively positioned or absolutely |
|
779 // positioned |
|
780 bool isPositioned = aFrame->IsPositioned(); |
|
781 |
|
782 int32_t zIndex = 0; |
|
783 bool autoZIndex = false; |
|
784 |
|
785 if (!isPositioned) { |
|
786 autoZIndex = true; |
|
787 } else { |
|
788 // Make sure z-index is correct |
|
789 const nsStylePosition* position = aStyleContext->StylePosition(); |
|
790 |
|
791 if (position->mZIndex.GetUnit() == eStyleUnit_Integer) { |
|
792 zIndex = position->mZIndex.GetIntValue(); |
|
793 } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) { |
|
794 autoZIndex = true; |
|
795 } |
|
796 } |
|
797 |
|
798 vm->SetViewZIndex(aView, autoZIndex, zIndex); |
|
799 } |
|
800 |
|
801 static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord) |
|
802 { |
|
803 if (aCoord.ConvertsToLength()) { |
|
804 return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0); |
|
805 } |
|
806 return aIfNotCoord; |
|
807 } |
|
808 |
|
809 void |
|
810 nsContainerFrame::DoInlineIntrinsicWidth(nsRenderingContext *aRenderingContext, |
|
811 InlineIntrinsicWidthData *aData, |
|
812 nsLayoutUtils::IntrinsicWidthType aType) |
|
813 { |
|
814 if (GetPrevInFlow()) |
|
815 return; // Already added. |
|
816 |
|
817 NS_PRECONDITION(aType == nsLayoutUtils::MIN_WIDTH || |
|
818 aType == nsLayoutUtils::PREF_WIDTH, "bad type"); |
|
819 |
|
820 mozilla::css::Side startSide, endSide; |
|
821 if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR) { |
|
822 startSide = NS_SIDE_LEFT; |
|
823 endSide = NS_SIDE_RIGHT; |
|
824 } else { |
|
825 startSide = NS_SIDE_RIGHT; |
|
826 endSide = NS_SIDE_LEFT; |
|
827 } |
|
828 |
|
829 const nsStylePadding *stylePadding = StylePadding(); |
|
830 const nsStyleBorder *styleBorder = StyleBorder(); |
|
831 const nsStyleMargin *styleMargin = StyleMargin(); |
|
832 |
|
833 // This goes at the beginning no matter how things are broken and how |
|
834 // messy the bidi situations are, since per CSS2.1 section 8.6 |
|
835 // (implemented in bug 328168), the startSide border is always on the |
|
836 // first line. |
|
837 // This frame is a first-in-flow, but it might have a previous bidi |
|
838 // continuation, in which case that continuation should handle the startSide |
|
839 // border. |
|
840 if (!GetPrevContinuation()) { |
|
841 aData->currentLine += |
|
842 // clamp negative calc() to 0 |
|
843 std::max(GetCoord(stylePadding->mPadding.Get(startSide), 0), 0) + |
|
844 styleBorder->GetComputedBorderWidth(startSide) + |
|
845 GetCoord(styleMargin->mMargin.Get(startSide), 0); |
|
846 } |
|
847 |
|
848 const nsLineList_iterator* savedLine = aData->line; |
|
849 nsIFrame* const savedLineContainer = aData->lineContainer; |
|
850 |
|
851 nsContainerFrame *lastInFlow; |
|
852 for (nsContainerFrame *nif = this; nif; |
|
853 nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow())) { |
|
854 for (nsIFrame *kid = nif->mFrames.FirstChild(); kid; |
|
855 kid = kid->GetNextSibling()) { |
|
856 if (aType == nsLayoutUtils::MIN_WIDTH) |
|
857 kid->AddInlineMinWidth(aRenderingContext, |
|
858 static_cast<InlineMinWidthData*>(aData)); |
|
859 else |
|
860 kid->AddInlinePrefWidth(aRenderingContext, |
|
861 static_cast<InlinePrefWidthData*>(aData)); |
|
862 } |
|
863 |
|
864 // After we advance to our next-in-flow, the stored line and line container |
|
865 // may no longer be correct. Just forget them. |
|
866 aData->line = nullptr; |
|
867 aData->lineContainer = nullptr; |
|
868 |
|
869 lastInFlow = nif; |
|
870 } |
|
871 |
|
872 aData->line = savedLine; |
|
873 aData->lineContainer = savedLineContainer; |
|
874 |
|
875 // This goes at the end no matter how things are broken and how |
|
876 // messy the bidi situations are, since per CSS2.1 section 8.6 |
|
877 // (implemented in bug 328168), the endSide border is always on the |
|
878 // last line. |
|
879 // We reached the last-in-flow, but it might have a next bidi |
|
880 // continuation, in which case that continuation should handle |
|
881 // the endSide border. |
|
882 if (!lastInFlow->GetNextContinuation()) { |
|
883 aData->currentLine += |
|
884 // clamp negative calc() to 0 |
|
885 std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) + |
|
886 styleBorder->GetComputedBorderWidth(endSide) + |
|
887 GetCoord(styleMargin->mMargin.Get(endSide), 0); |
|
888 } |
|
889 } |
|
890 |
|
891 /* virtual */ nsSize |
|
892 nsContainerFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, |
|
893 nsSize aCBSize, nscoord aAvailableWidth, |
|
894 nsSize aMargin, nsSize aBorder, |
|
895 nsSize aPadding, bool aShrinkWrap) |
|
896 { |
|
897 nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE); |
|
898 nscoord availBased = aAvailableWidth - aMargin.width - aBorder.width - |
|
899 aPadding.width; |
|
900 // replaced elements always shrink-wrap |
|
901 if (aShrinkWrap || IsFrameOfType(eReplaced)) { |
|
902 // don't bother setting it if the result won't be used |
|
903 if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { |
|
904 result.width = ShrinkWidthToFit(aRenderingContext, availBased); |
|
905 } |
|
906 } else { |
|
907 result.width = availBased; |
|
908 } |
|
909 return result; |
|
910 } |
|
911 |
|
912 /** |
|
913 * Invokes the WillReflow() function, positions the frame and its view (if |
|
914 * requested), and then calls Reflow(). If the reflow succeeds and the child |
|
915 * frame is complete, deletes any next-in-flows using DeleteNextInFlowChild() |
|
916 */ |
|
917 nsresult |
|
918 nsContainerFrame::ReflowChild(nsIFrame* aKidFrame, |
|
919 nsPresContext* aPresContext, |
|
920 nsHTMLReflowMetrics& aDesiredSize, |
|
921 const nsHTMLReflowState& aReflowState, |
|
922 nscoord aX, |
|
923 nscoord aY, |
|
924 uint32_t aFlags, |
|
925 nsReflowStatus& aStatus, |
|
926 nsOverflowContinuationTracker* aTracker) |
|
927 { |
|
928 NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state"); |
|
929 |
|
930 nsresult result; |
|
931 |
|
932 // Send the WillReflow() notification, and position the child frame |
|
933 // and its view if requested |
|
934 aKidFrame->WillReflow(aPresContext); |
|
935 |
|
936 if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { |
|
937 aKidFrame->SetPosition(nsPoint(aX, aY)); |
|
938 } |
|
939 |
|
940 if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { |
|
941 PositionFrameView(aKidFrame); |
|
942 } |
|
943 |
|
944 // Reflow the child frame |
|
945 result = aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState, |
|
946 aStatus); |
|
947 |
|
948 // If the reflow was successful and the child frame is complete, delete any |
|
949 // next-in-flows, but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set. |
|
950 if (NS_SUCCEEDED(result) && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && |
|
951 !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) { |
|
952 nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow(); |
|
953 if (kidNextInFlow) { |
|
954 // Remove all of the childs next-in-flows. Make sure that we ask |
|
955 // the right parent to do the removal (it's possible that the |
|
956 // parent is not this because we are executing pullup code) |
|
957 nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame); |
|
958 static_cast<nsContainerFrame*>(kidNextInFlow->GetParent()) |
|
959 ->DeleteNextInFlowChild(kidNextInFlow, true); |
|
960 } |
|
961 } |
|
962 return result; |
|
963 } |
|
964 |
|
965 |
|
966 /** |
|
967 * Position the views of |aFrame|'s descendants. A container frame |
|
968 * should call this method if it moves a frame after |Reflow|. |
|
969 */ |
|
970 void |
|
971 nsContainerFrame::PositionChildViews(nsIFrame* aFrame) |
|
972 { |
|
973 if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) { |
|
974 return; |
|
975 } |
|
976 |
|
977 // Recursively walk aFrame's child frames. |
|
978 // Process the additional child lists, but skip the popup list as the |
|
979 // view for popups is managed by the parent. Currently only nsMenuFrame |
|
980 // and nsPopupSetFrame have a popupList and during layout will adjust the |
|
981 // view manually to position the popup. |
|
982 ChildListIterator lists(aFrame); |
|
983 for (; !lists.IsDone(); lists.Next()) { |
|
984 if (lists.CurrentID() == kPopupList) { |
|
985 continue; |
|
986 } |
|
987 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
988 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
989 // Position the frame's view (if it has one) otherwise recursively |
|
990 // process its children |
|
991 nsIFrame* childFrame = childFrames.get(); |
|
992 if (childFrame->HasView()) { |
|
993 PositionFrameView(childFrame); |
|
994 } else { |
|
995 PositionChildViews(childFrame); |
|
996 } |
|
997 } |
|
998 } |
|
999 } |
|
1000 |
|
1001 /** |
|
1002 * The second half of frame reflow. Does the following: |
|
1003 * - sets the frame's bounds |
|
1004 * - sizes and positions (if requested) the frame's view. If the frame's final |
|
1005 * position differs from the current position and the frame itself does not |
|
1006 * have a view, then any child frames with views are positioned so they stay |
|
1007 * in sync |
|
1008 * - sets the view's visibility, opacity, content transparency, and clip |
|
1009 * - invoked the DidReflow() function |
|
1010 * |
|
1011 * Flags: |
|
1012 * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this |
|
1013 * case. Also implies NS_FRAME_NO_MOVE_VIEW |
|
1014 * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you |
|
1015 * don't want to automatically sync the frame and view |
|
1016 * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view |
|
1017 */ |
|
1018 nsresult |
|
1019 nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame, |
|
1020 nsPresContext* aPresContext, |
|
1021 const nsHTMLReflowMetrics& aDesiredSize, |
|
1022 const nsHTMLReflowState* aReflowState, |
|
1023 nscoord aX, |
|
1024 nscoord aY, |
|
1025 uint32_t aFlags) |
|
1026 { |
|
1027 nsPoint curOrigin = aKidFrame->GetPosition(); |
|
1028 |
|
1029 if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { |
|
1030 aKidFrame->SetRect(nsRect(aX, aY, aDesiredSize.Width(), aDesiredSize.Height())); |
|
1031 } else { |
|
1032 aKidFrame->SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height())); |
|
1033 } |
|
1034 |
|
1035 if (aKidFrame->HasView()) { |
|
1036 nsView* view = aKidFrame->GetView(); |
|
1037 // Make sure the frame's view is properly sized and positioned and has |
|
1038 // things like opacity correct |
|
1039 SyncFrameViewAfterReflow(aPresContext, aKidFrame, view, |
|
1040 aDesiredSize.VisualOverflow(), aFlags); |
|
1041 } |
|
1042 |
|
1043 if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) && |
|
1044 (curOrigin.x != aX || curOrigin.y != aY)) { |
|
1045 if (!aKidFrame->HasView()) { |
|
1046 // If the frame has moved, then we need to make sure any child views are |
|
1047 // correctly positioned |
|
1048 PositionChildViews(aKidFrame); |
|
1049 } |
|
1050 } |
|
1051 |
|
1052 return aKidFrame->DidReflow(aPresContext, aReflowState, nsDidReflowStatus::FINISHED); |
|
1053 } |
|
1054 |
|
1055 nsresult |
|
1056 nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext* aPresContext, |
|
1057 const nsHTMLReflowState& aReflowState, |
|
1058 nsOverflowAreas& aOverflowRects, |
|
1059 uint32_t aFlags, |
|
1060 nsReflowStatus& aStatus) |
|
1061 { |
|
1062 NS_PRECONDITION(aPresContext, "null pointer"); |
|
1063 nsresult rv = NS_OK; |
|
1064 |
|
1065 nsFrameList* overflowContainers = |
|
1066 GetPropTableFrames(OverflowContainersProperty()); |
|
1067 |
|
1068 NS_ASSERTION(!(overflowContainers && GetPrevInFlow() |
|
1069 && static_cast<nsContainerFrame*>(GetPrevInFlow()) |
|
1070 ->GetPropTableFrames(ExcessOverflowContainersProperty())), |
|
1071 "conflicting overflow containers lists"); |
|
1072 |
|
1073 if (!overflowContainers) { |
|
1074 // Drain excess from previnflow |
|
1075 nsContainerFrame* prev = (nsContainerFrame*) GetPrevInFlow(); |
|
1076 if (prev) { |
|
1077 nsFrameList* excessFrames = |
|
1078 prev->RemovePropTableFrames(ExcessOverflowContainersProperty()); |
|
1079 if (excessFrames) { |
|
1080 excessFrames->ApplySetParent(this); |
|
1081 nsContainerFrame::ReparentFrameViewList(*excessFrames, prev, this); |
|
1082 overflowContainers = excessFrames; |
|
1083 SetPropTableFrames(overflowContainers, OverflowContainersProperty()); |
|
1084 } |
|
1085 } |
|
1086 } |
|
1087 |
|
1088 // Our own excess overflow containers from a previous reflow can still be |
|
1089 // present if our next-in-flow hasn't been reflown yet. |
|
1090 nsFrameList* selfExcessOCFrames = |
|
1091 RemovePropTableFrames(ExcessOverflowContainersProperty()); |
|
1092 if (selfExcessOCFrames) { |
|
1093 if (overflowContainers) { |
|
1094 overflowContainers->AppendFrames(nullptr, *selfExcessOCFrames); |
|
1095 selfExcessOCFrames->Delete(aPresContext->PresShell()); |
|
1096 } else { |
|
1097 overflowContainers = selfExcessOCFrames; |
|
1098 SetPropTableFrames(overflowContainers, OverflowContainersProperty()); |
|
1099 } |
|
1100 } |
|
1101 if (!overflowContainers) { |
|
1102 return NS_OK; // nothing to reflow |
|
1103 } |
|
1104 |
|
1105 nsOverflowContinuationTracker tracker(this, false, false); |
|
1106 bool shouldReflowAllKids = aReflowState.ShouldReflowAllKids(); |
|
1107 |
|
1108 for (nsIFrame* frame = overflowContainers->FirstChild(); frame; |
|
1109 frame = frame->GetNextSibling()) { |
|
1110 if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) { |
|
1111 // frame's prevInFlow has moved, skip reflowing this frame; |
|
1112 // it will get reflowed once it's been placed |
|
1113 continue; |
|
1114 } |
|
1115 // If the available vertical height has changed, we need to reflow |
|
1116 // even if the frame isn't dirty. |
|
1117 if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) { |
|
1118 // Get prev-in-flow |
|
1119 nsIFrame* prevInFlow = frame->GetPrevInFlow(); |
|
1120 NS_ASSERTION(prevInFlow, |
|
1121 "overflow container frame must have a prev-in-flow"); |
|
1122 NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER, |
|
1123 "overflow container frame must have overflow container bit set"); |
|
1124 nsRect prevRect = prevInFlow->GetRect(); |
|
1125 |
|
1126 // Initialize reflow params |
|
1127 nsSize availSpace(prevRect.width, aReflowState.AvailableHeight()); |
|
1128 nsHTMLReflowMetrics desiredSize(aReflowState); |
|
1129 nsHTMLReflowState frameState(aPresContext, aReflowState, |
|
1130 frame, availSpace); |
|
1131 nsReflowStatus frameStatus; |
|
1132 |
|
1133 // Reflow |
|
1134 rv = ReflowChild(frame, aPresContext, desiredSize, frameState, |
|
1135 prevRect.x, 0, aFlags, frameStatus, &tracker); |
|
1136 NS_ENSURE_SUCCESS(rv, rv); |
|
1137 //XXXfr Do we need to override any shrinkwrap effects here? |
|
1138 // e.g. desiredSize.Width() = prevRect.width; |
|
1139 rv = FinishReflowChild(frame, aPresContext, desiredSize, &frameState, |
|
1140 prevRect.x, 0, aFlags); |
|
1141 NS_ENSURE_SUCCESS(rv, rv); |
|
1142 |
|
1143 // Handle continuations |
|
1144 if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) { |
|
1145 if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { |
|
1146 // Abspos frames can't cause their parent to be incomplete, |
|
1147 // only overflow incomplete. |
|
1148 NS_FRAME_SET_OVERFLOW_INCOMPLETE(frameStatus); |
|
1149 } |
|
1150 else { |
|
1151 NS_ASSERTION(NS_FRAME_IS_COMPLETE(frameStatus), |
|
1152 "overflow container frames can't be incomplete, only overflow-incomplete"); |
|
1153 } |
|
1154 |
|
1155 // Acquire a next-in-flow, creating it if necessary |
|
1156 nsIFrame* nif = frame->GetNextInFlow(); |
|
1157 if (!nif) { |
|
1158 NS_ASSERTION(frameStatus & NS_FRAME_REFLOW_NEXTINFLOW, |
|
1159 "Someone forgot a REFLOW_NEXTINFLOW flag"); |
|
1160 nif = aPresContext->PresShell()->FrameConstructor()-> |
|
1161 CreateContinuingFrame(aPresContext, frame, this); |
|
1162 } |
|
1163 else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { |
|
1164 // used to be a normal next-in-flow; steal it from the child list |
|
1165 rv = static_cast<nsContainerFrame*>(nif->GetParent()) |
|
1166 ->StealFrame(nif); |
|
1167 NS_ENSURE_SUCCESS(rv, rv); |
|
1168 } |
|
1169 |
|
1170 tracker.Insert(nif, frameStatus); |
|
1171 } |
|
1172 NS_MergeReflowStatusInto(&aStatus, frameStatus); |
|
1173 // At this point it would be nice to assert !frame->GetOverflowRect().IsEmpty(), |
|
1174 // but we have some unsplittable frames that, when taller than |
|
1175 // availableHeight will push zero-height content into a next-in-flow. |
|
1176 } |
|
1177 else { |
|
1178 tracker.Skip(frame, aStatus); |
|
1179 if (aReflowState.mFloatManager) |
|
1180 nsBlockFrame::RecoverFloatsFor(frame, *aReflowState.mFloatManager); |
|
1181 } |
|
1182 ConsiderChildOverflow(aOverflowRects, frame); |
|
1183 } |
|
1184 |
|
1185 return NS_OK; |
|
1186 } |
|
1187 |
|
1188 void |
|
1189 nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder* aBuilder, |
|
1190 const nsRect& aDirtyRect, |
|
1191 const nsDisplayListSet& aLists) |
|
1192 { |
|
1193 nsFrameList* overflowconts = GetPropTableFrames(OverflowContainersProperty()); |
|
1194 if (overflowconts) { |
|
1195 for (nsIFrame* frame = overflowconts->FirstChild(); frame; |
|
1196 frame = frame->GetNextSibling()) { |
|
1197 BuildDisplayListForChild(aBuilder, frame, aDirtyRect, aLists); |
|
1198 } |
|
1199 } |
|
1200 } |
|
1201 |
|
1202 static bool |
|
1203 TryRemoveFrame(nsIFrame* aFrame, FramePropertyTable* aPropTable, |
|
1204 const FramePropertyDescriptor* aProp, nsIFrame* aChildToRemove) |
|
1205 { |
|
1206 nsFrameList* list = static_cast<nsFrameList*>(aPropTable->Get(aFrame, aProp)); |
|
1207 if (list && list->StartRemoveFrame(aChildToRemove)) { |
|
1208 // aChildToRemove *may* have been removed from this list. |
|
1209 if (list->IsEmpty()) { |
|
1210 aPropTable->Remove(aFrame, aProp); |
|
1211 list->Delete(aFrame->PresContext()->PresShell()); |
|
1212 } |
|
1213 return true; |
|
1214 } |
|
1215 return false; |
|
1216 } |
|
1217 |
|
1218 nsresult |
|
1219 nsContainerFrame::StealFrame(nsIFrame* aChild, |
|
1220 bool aForceNormal) |
|
1221 { |
|
1222 #ifdef DEBUG |
|
1223 if (!mFrames.ContainsFrame(aChild)) { |
|
1224 nsFrameList* list = GetOverflowFrames(); |
|
1225 if (!list || !list->ContainsFrame(aChild)) { |
|
1226 FramePropertyTable* propTable = PresContext()->PropertyTable(); |
|
1227 list = static_cast<nsFrameList*>( |
|
1228 propTable->Get(this, OverflowContainersProperty())); |
|
1229 if (!list || !list->ContainsFrame(aChild)) { |
|
1230 list = static_cast<nsFrameList*>( |
|
1231 propTable->Get(this, ExcessOverflowContainersProperty())); |
|
1232 MOZ_ASSERT(list && list->ContainsFrame(aChild), "aChild isn't our child" |
|
1233 " or on a frame list not supported by StealFrame"); |
|
1234 } |
|
1235 } |
|
1236 } |
|
1237 #endif |
|
1238 |
|
1239 bool removed; |
|
1240 if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) |
|
1241 && !aForceNormal) { |
|
1242 FramePropertyTable* propTable = PresContext()->PropertyTable(); |
|
1243 // Try removing from the overflow container list. |
|
1244 removed = ::TryRemoveFrame(this, propTable, OverflowContainersProperty(), |
|
1245 aChild); |
|
1246 if (!removed) { |
|
1247 // It must be in the excess overflow container list. |
|
1248 removed = ::TryRemoveFrame(this, propTable, |
|
1249 ExcessOverflowContainersProperty(), |
|
1250 aChild); |
|
1251 } |
|
1252 } else { |
|
1253 removed = mFrames.StartRemoveFrame(aChild); |
|
1254 if (!removed) { |
|
1255 // We didn't find the child in our principal child list. |
|
1256 // Maybe it's on the overflow list? |
|
1257 nsFrameList* frameList = GetOverflowFrames(); |
|
1258 if (frameList) { |
|
1259 removed = frameList->ContinueRemoveFrame(aChild); |
|
1260 if (frameList->IsEmpty()) { |
|
1261 DestroyOverflowList(); |
|
1262 } |
|
1263 } |
|
1264 } |
|
1265 } |
|
1266 |
|
1267 NS_POSTCONDITION(removed, "StealFrame: can't find aChild"); |
|
1268 return removed ? NS_OK : NS_ERROR_UNEXPECTED; |
|
1269 } |
|
1270 |
|
1271 nsFrameList |
|
1272 nsContainerFrame::StealFramesAfter(nsIFrame* aChild) |
|
1273 { |
|
1274 NS_ASSERTION(!aChild || |
|
1275 !(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER), |
|
1276 "StealFramesAfter doesn't handle overflow containers"); |
|
1277 NS_ASSERTION(GetType() != nsGkAtoms::blockFrame, "unexpected call"); |
|
1278 |
|
1279 if (!aChild) { |
|
1280 nsFrameList copy(mFrames); |
|
1281 mFrames.Clear(); |
|
1282 return copy; |
|
1283 } |
|
1284 |
|
1285 for (nsFrameList::FrameLinkEnumerator iter(mFrames); !iter.AtEnd(); |
|
1286 iter.Next()) { |
|
1287 if (iter.PrevFrame() == aChild) { |
|
1288 return mFrames.ExtractTail(iter); |
|
1289 } |
|
1290 } |
|
1291 |
|
1292 // We didn't find the child in the principal child list. |
|
1293 // Maybe it's on the overflow list? |
|
1294 nsFrameList* overflowFrames = GetOverflowFrames(); |
|
1295 if (overflowFrames) { |
|
1296 for (nsFrameList::FrameLinkEnumerator iter(*overflowFrames); !iter.AtEnd(); |
|
1297 iter.Next()) { |
|
1298 if (iter.PrevFrame() == aChild) { |
|
1299 return overflowFrames->ExtractTail(iter); |
|
1300 } |
|
1301 } |
|
1302 } |
|
1303 |
|
1304 NS_ERROR("StealFramesAfter: can't find aChild"); |
|
1305 return nsFrameList::EmptyList(); |
|
1306 } |
|
1307 |
|
1308 /* |
|
1309 * Create a next-in-flow for aFrame. Will return the newly created |
|
1310 * frame in aNextInFlowResult <b>if and only if</b> a new frame is |
|
1311 * created; otherwise nullptr is returned in aNextInFlowResult. |
|
1312 */ |
|
1313 nsresult |
|
1314 nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame, |
|
1315 nsIFrame*& aNextInFlowResult) |
|
1316 { |
|
1317 NS_PRECONDITION(GetType() != nsGkAtoms::blockFrame, |
|
1318 "you should have called nsBlockFrame::CreateContinuationFor instead"); |
|
1319 NS_PRECONDITION(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame"); |
|
1320 |
|
1321 nsPresContext* pc = PresContext(); |
|
1322 aNextInFlowResult = nullptr; |
|
1323 |
|
1324 nsIFrame* nextInFlow = aFrame->GetNextInFlow(); |
|
1325 if (nullptr == nextInFlow) { |
|
1326 // Create a continuation frame for the child frame and insert it |
|
1327 // into our child list. |
|
1328 nextInFlow = pc->PresShell()->FrameConstructor()-> |
|
1329 CreateContinuingFrame(pc, aFrame, this); |
|
1330 mFrames.InsertFrame(nullptr, aFrame, nextInFlow); |
|
1331 |
|
1332 NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES, |
|
1333 ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p", |
|
1334 aFrame, nextInFlow)); |
|
1335 |
|
1336 aNextInFlowResult = nextInFlow; |
|
1337 } |
|
1338 return NS_OK; |
|
1339 } |
|
1340 |
|
1341 /** |
|
1342 * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow |
|
1343 * pointers |
|
1344 */ |
|
1345 void |
|
1346 nsContainerFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow, |
|
1347 bool aDeletingEmptyFrames) |
|
1348 { |
|
1349 #ifdef DEBUG |
|
1350 nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow(); |
|
1351 #endif |
|
1352 NS_PRECONDITION(prevInFlow, "bad prev-in-flow"); |
|
1353 |
|
1354 // If the next-in-flow has a next-in-flow then delete it, too (and |
|
1355 // delete it first). |
|
1356 // Do this in a loop so we don't overflow the stack for frames |
|
1357 // with very many next-in-flows |
|
1358 nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow(); |
|
1359 if (nextNextInFlow) { |
|
1360 nsAutoTArray<nsIFrame*, 8> frames; |
|
1361 for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) { |
|
1362 frames.AppendElement(f); |
|
1363 } |
|
1364 for (int32_t i = frames.Length() - 1; i >= 0; --i) { |
|
1365 nsIFrame* delFrame = frames.ElementAt(i); |
|
1366 static_cast<nsContainerFrame*>(delFrame->GetParent()) |
|
1367 ->DeleteNextInFlowChild(delFrame, aDeletingEmptyFrames); |
|
1368 } |
|
1369 } |
|
1370 |
|
1371 // Take the next-in-flow out of the parent's child list |
|
1372 DebugOnly<nsresult> rv = StealFrame(aNextInFlow); |
|
1373 NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure"); |
|
1374 |
|
1375 #ifdef DEBUG |
|
1376 if (aDeletingEmptyFrames) { |
|
1377 nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow); |
|
1378 } |
|
1379 #endif |
|
1380 |
|
1381 // Delete the next-in-flow frame and its descendants. This will also |
|
1382 // remove it from its next-in-flow/prev-in-flow chain. |
|
1383 aNextInFlow->Destroy(); |
|
1384 |
|
1385 NS_POSTCONDITION(!prevInFlow->GetNextInFlow(), "non null next-in-flow"); |
|
1386 } |
|
1387 |
|
1388 /** |
|
1389 * Set the frames on the overflow list |
|
1390 */ |
|
1391 void |
|
1392 nsContainerFrame::SetOverflowFrames(const nsFrameList& aOverflowFrames) |
|
1393 { |
|
1394 NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called"); |
|
1395 |
|
1396 nsPresContext* pc = PresContext(); |
|
1397 nsFrameList* newList = new (pc->PresShell()) nsFrameList(aOverflowFrames); |
|
1398 |
|
1399 pc->PropertyTable()->Set(this, OverflowProperty(), newList); |
|
1400 } |
|
1401 |
|
1402 nsFrameList* |
|
1403 nsContainerFrame::GetPropTableFrames(const FramePropertyDescriptor* aProperty) const |
|
1404 { |
|
1405 FramePropertyTable* propTable = PresContext()->PropertyTable(); |
|
1406 return static_cast<nsFrameList*>(propTable->Get(this, aProperty)); |
|
1407 } |
|
1408 |
|
1409 nsFrameList* |
|
1410 nsContainerFrame::RemovePropTableFrames(const FramePropertyDescriptor* aProperty) |
|
1411 { |
|
1412 FramePropertyTable* propTable = PresContext()->PropertyTable(); |
|
1413 return static_cast<nsFrameList*>(propTable->Remove(this, aProperty)); |
|
1414 } |
|
1415 |
|
1416 void |
|
1417 nsContainerFrame::SetPropTableFrames(nsFrameList* aFrameList, |
|
1418 const FramePropertyDescriptor* aProperty) |
|
1419 { |
|
1420 NS_PRECONDITION(aProperty && aFrameList, "null ptr"); |
|
1421 NS_PRECONDITION( |
|
1422 (aProperty != nsContainerFrame::OverflowContainersProperty() && |
|
1423 aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) || |
|
1424 IsFrameOfType(nsIFrame::eCanContainOverflowContainers), |
|
1425 "this type of frame can't have overflow containers"); |
|
1426 MOZ_ASSERT(!GetPropTableFrames(aProperty)); |
|
1427 PresContext()->PropertyTable()->Set(this, aProperty, aFrameList); |
|
1428 } |
|
1429 |
|
1430 /** |
|
1431 * Push aFromChild and its next siblings to the next-in-flow. Change the |
|
1432 * geometric parent of each frame that's pushed. If there is no next-in-flow |
|
1433 * the frames are placed on the overflow list (and the geometric parent is |
|
1434 * left unchanged). |
|
1435 * |
|
1436 * Updates the next-in-flow's child count. Does <b>not</b> update the |
|
1437 * pusher's child count. |
|
1438 * |
|
1439 * @param aFromChild the first child frame to push. It is disconnected from |
|
1440 * aPrevSibling |
|
1441 * @param aPrevSibling aFromChild's previous sibling. Must not be null. It's |
|
1442 * an error to push a parent's first child frame |
|
1443 */ |
|
1444 void |
|
1445 nsContainerFrame::PushChildren(nsIFrame* aFromChild, |
|
1446 nsIFrame* aPrevSibling) |
|
1447 { |
|
1448 NS_PRECONDITION(aFromChild, "null pointer"); |
|
1449 NS_PRECONDITION(aPrevSibling, "pushing first child"); |
|
1450 NS_PRECONDITION(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling"); |
|
1451 |
|
1452 // Disconnect aFromChild from its previous sibling |
|
1453 nsFrameList tail = mFrames.RemoveFramesAfter(aPrevSibling); |
|
1454 |
|
1455 nsContainerFrame* nextInFlow = |
|
1456 static_cast<nsContainerFrame*>(GetNextInFlow()); |
|
1457 if (nextInFlow) { |
|
1458 // XXX This is not a very good thing to do. If it gets removed |
|
1459 // then remove the copy of this routine that doesn't do this from |
|
1460 // nsInlineFrame. |
|
1461 // When pushing and pulling frames we need to check for whether any |
|
1462 // views need to be reparented. |
|
1463 for (nsIFrame* f = aFromChild; f; f = f->GetNextSibling()) { |
|
1464 nsContainerFrame::ReparentFrameView(f, this, nextInFlow); |
|
1465 } |
|
1466 nextInFlow->mFrames.InsertFrames(nextInFlow, nullptr, tail); |
|
1467 } |
|
1468 else { |
|
1469 // Add the frames to our overflow list |
|
1470 SetOverflowFrames(tail); |
|
1471 } |
|
1472 } |
|
1473 |
|
1474 /** |
|
1475 * Moves any frames on the overflow lists (the prev-in-flow's overflow list and |
|
1476 * the receiver's overflow list) to the child list. |
|
1477 * |
|
1478 * Updates this frame's child count and content mapping. |
|
1479 * |
|
1480 * @return true if any frames were moved and false otherwise |
|
1481 */ |
|
1482 bool |
|
1483 nsContainerFrame::MoveOverflowToChildList() |
|
1484 { |
|
1485 bool result = false; |
|
1486 |
|
1487 // Check for an overflow list with our prev-in-flow |
|
1488 nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow(); |
|
1489 if (nullptr != prevInFlow) { |
|
1490 AutoFrameListPtr prevOverflowFrames(PresContext(), |
|
1491 prevInFlow->StealOverflowFrames()); |
|
1492 if (prevOverflowFrames) { |
|
1493 // Tables are special; they can have repeated header/footer |
|
1494 // frames on mFrames at this point. |
|
1495 NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame, |
|
1496 "bad overflow list"); |
|
1497 // When pushing and pulling frames we need to check for whether any |
|
1498 // views need to be reparented. |
|
1499 nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, |
|
1500 prevInFlow, this); |
|
1501 mFrames.AppendFrames(this, *prevOverflowFrames); |
|
1502 result = true; |
|
1503 } |
|
1504 } |
|
1505 |
|
1506 // It's also possible that we have an overflow list for ourselves. |
|
1507 return DrainSelfOverflowList() || result; |
|
1508 } |
|
1509 |
|
1510 bool |
|
1511 nsContainerFrame::DrainSelfOverflowList() |
|
1512 { |
|
1513 AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames()); |
|
1514 if (overflowFrames) { |
|
1515 NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames"); |
|
1516 mFrames.AppendFrames(nullptr, *overflowFrames); |
|
1517 return true; |
|
1518 } |
|
1519 return false; |
|
1520 } |
|
1521 |
|
1522 nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsContainerFrame* aFrame, |
|
1523 bool aWalkOOFFrames, |
|
1524 bool aSkipOverflowContainerChildren) |
|
1525 : mOverflowContList(nullptr), |
|
1526 mPrevOverflowCont(nullptr), |
|
1527 mSentry(nullptr), |
|
1528 mParent(aFrame), |
|
1529 mSkipOverflowContainerChildren(aSkipOverflowContainerChildren), |
|
1530 mWalkOOFFrames(aWalkOOFFrames) |
|
1531 { |
|
1532 NS_PRECONDITION(aFrame, "null frame pointer"); |
|
1533 SetupOverflowContList(); |
|
1534 } |
|
1535 |
|
1536 void |
|
1537 nsOverflowContinuationTracker::SetupOverflowContList() |
|
1538 { |
|
1539 NS_PRECONDITION(mParent, "null frame pointer"); |
|
1540 NS_PRECONDITION(!mOverflowContList, "already have list"); |
|
1541 nsContainerFrame* nif = |
|
1542 static_cast<nsContainerFrame*>(mParent->GetNextInFlow()); |
|
1543 if (nif) { |
|
1544 mOverflowContList = nif->GetPropTableFrames( |
|
1545 nsContainerFrame::OverflowContainersProperty()); |
|
1546 if (mOverflowContList) { |
|
1547 mParent = nif; |
|
1548 SetUpListWalker(); |
|
1549 } |
|
1550 } |
|
1551 if (!mOverflowContList) { |
|
1552 mOverflowContList = mParent->GetPropTableFrames( |
|
1553 nsContainerFrame::ExcessOverflowContainersProperty()); |
|
1554 if (mOverflowContList) { |
|
1555 SetUpListWalker(); |
|
1556 } |
|
1557 } |
|
1558 } |
|
1559 |
|
1560 /** |
|
1561 * Helper function to walk past overflow continuations whose prev-in-flow |
|
1562 * isn't a normal child and to set mSentry and mPrevOverflowCont correctly. |
|
1563 */ |
|
1564 void |
|
1565 nsOverflowContinuationTracker::SetUpListWalker() |
|
1566 { |
|
1567 NS_ASSERTION(!mSentry && !mPrevOverflowCont, |
|
1568 "forgot to reset mSentry or mPrevOverflowCont"); |
|
1569 if (mOverflowContList) { |
|
1570 nsIFrame* cur = mOverflowContList->FirstChild(); |
|
1571 if (mSkipOverflowContainerChildren) { |
|
1572 while (cur && (cur->GetPrevInFlow()->GetStateBits() |
|
1573 & NS_FRAME_IS_OVERFLOW_CONTAINER)) { |
|
1574 mPrevOverflowCont = cur; |
|
1575 cur = cur->GetNextSibling(); |
|
1576 } |
|
1577 while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW) |
|
1578 == mWalkOOFFrames)) { |
|
1579 mPrevOverflowCont = cur; |
|
1580 cur = cur->GetNextSibling(); |
|
1581 } |
|
1582 } |
|
1583 if (cur) { |
|
1584 mSentry = cur->GetPrevInFlow(); |
|
1585 } |
|
1586 } |
|
1587 } |
|
1588 |
|
1589 /** |
|
1590 * Helper function to step forward through the overflow continuations list. |
|
1591 * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames |
|
1592 * as appropriate. May only be called when we have already set up an |
|
1593 * mOverflowContList; mOverflowContList cannot be null. |
|
1594 */ |
|
1595 void |
|
1596 nsOverflowContinuationTracker::StepForward() |
|
1597 { |
|
1598 NS_PRECONDITION(mOverflowContList, "null list"); |
|
1599 |
|
1600 // Step forward |
|
1601 if (mPrevOverflowCont) { |
|
1602 mPrevOverflowCont = mPrevOverflowCont->GetNextSibling(); |
|
1603 } |
|
1604 else { |
|
1605 mPrevOverflowCont = mOverflowContList->FirstChild(); |
|
1606 } |
|
1607 |
|
1608 // Skip over oof or non-oof frames as appropriate |
|
1609 if (mSkipOverflowContainerChildren) { |
|
1610 nsIFrame* cur = mPrevOverflowCont->GetNextSibling(); |
|
1611 while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW) |
|
1612 == mWalkOOFFrames)) { |
|
1613 mPrevOverflowCont = cur; |
|
1614 cur = cur->GetNextSibling(); |
|
1615 } |
|
1616 } |
|
1617 |
|
1618 // Set up the sentry |
|
1619 mSentry = (mPrevOverflowCont->GetNextSibling()) |
|
1620 ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow() |
|
1621 : nullptr; |
|
1622 } |
|
1623 |
|
1624 nsresult |
|
1625 nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont, |
|
1626 nsReflowStatus& aReflowStatus) |
|
1627 { |
|
1628 NS_PRECONDITION(aOverflowCont, "null frame pointer"); |
|
1629 NS_PRECONDITION(!mSkipOverflowContainerChildren || mWalkOOFFrames == |
|
1630 !!(aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW), |
|
1631 "shouldn't insert frame that doesn't match walker type"); |
|
1632 NS_PRECONDITION(aOverflowCont->GetPrevInFlow(), |
|
1633 "overflow containers must have a prev-in-flow"); |
|
1634 nsresult rv = NS_OK; |
|
1635 bool reparented = false; |
|
1636 nsPresContext* presContext = aOverflowCont->PresContext(); |
|
1637 bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow(); |
|
1638 |
|
1639 // If we have a list and aOverflowCont is already in it then don't try to |
|
1640 // add it again. |
|
1641 if (addToList && aOverflowCont->GetParent() == mParent && |
|
1642 (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) && |
|
1643 mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) { |
|
1644 addToList = false; |
|
1645 mPrevOverflowCont = aOverflowCont->GetPrevSibling(); |
|
1646 } |
|
1647 |
|
1648 if (addToList) { |
|
1649 if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { |
|
1650 // aOverflowCont is in some other overflow container list, |
|
1651 // steal it first |
|
1652 NS_ASSERTION(!(mOverflowContList && |
|
1653 mOverflowContList->ContainsFrame(aOverflowCont)), |
|
1654 "overflow containers out of order"); |
|
1655 rv = static_cast<nsContainerFrame*>(aOverflowCont->GetParent()) |
|
1656 ->StealFrame(aOverflowCont); |
|
1657 NS_ENSURE_SUCCESS(rv, rv); |
|
1658 } |
|
1659 else { |
|
1660 aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); |
|
1661 } |
|
1662 if (!mOverflowContList) { |
|
1663 mOverflowContList = new (presContext->PresShell()) nsFrameList(); |
|
1664 mParent->SetPropTableFrames(mOverflowContList, |
|
1665 nsContainerFrame::ExcessOverflowContainersProperty()); |
|
1666 SetUpListWalker(); |
|
1667 } |
|
1668 if (aOverflowCont->GetParent() != mParent) { |
|
1669 nsContainerFrame::ReparentFrameView(aOverflowCont, |
|
1670 aOverflowCont->GetParent(), |
|
1671 mParent); |
|
1672 reparented = true; |
|
1673 } |
|
1674 |
|
1675 // If aOverflowCont has a prev/next-in-flow that might be in |
|
1676 // mOverflowContList we need to find it and insert after/before it to |
|
1677 // maintain the order amongst next-in-flows in this list. |
|
1678 nsIFrame* pif = aOverflowCont->GetPrevInFlow(); |
|
1679 nsIFrame* nif = aOverflowCont->GetNextInFlow(); |
|
1680 if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) || |
|
1681 (nif && nif->GetParent() == mParent && mPrevOverflowCont)) { |
|
1682 for (nsFrameList::Enumerator e(*mOverflowContList); !e.AtEnd(); e.Next()) { |
|
1683 nsIFrame* f = e.get(); |
|
1684 if (f == pif) { |
|
1685 mPrevOverflowCont = pif; |
|
1686 break; |
|
1687 } |
|
1688 if (f == nif) { |
|
1689 mPrevOverflowCont = f->GetPrevSibling(); |
|
1690 break; |
|
1691 } |
|
1692 } |
|
1693 } |
|
1694 |
|
1695 mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont); |
|
1696 aReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; |
|
1697 } |
|
1698 |
|
1699 // If we need to reflow it, mark it dirty |
|
1700 if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) |
|
1701 aOverflowCont->AddStateBits(NS_FRAME_IS_DIRTY); |
|
1702 |
|
1703 // It's in our list, just step forward |
|
1704 StepForward(); |
|
1705 NS_ASSERTION(mPrevOverflowCont == aOverflowCont || |
|
1706 (mSkipOverflowContainerChildren && |
|
1707 (mPrevOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW) != |
|
1708 (aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW)), |
|
1709 "OverflowContTracker in unexpected state"); |
|
1710 |
|
1711 if (addToList) { |
|
1712 // Convert all non-overflow-container continuations of aOverflowCont |
|
1713 // into overflow containers and move them to our overflow |
|
1714 // tracker. This preserves the invariant that the next-continuations |
|
1715 // of an overflow container are also overflow containers. |
|
1716 nsIFrame* f = aOverflowCont->GetNextContinuation(); |
|
1717 if (f && (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) || |
|
1718 (!reparented && f->GetParent() == mParent) || |
|
1719 (reparented && f->GetParent() != mParent))) { |
|
1720 if (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { |
|
1721 nsContainerFrame* parent = static_cast<nsContainerFrame*>(f->GetParent()); |
|
1722 rv = parent->StealFrame(f); |
|
1723 NS_ENSURE_SUCCESS(rv, rv); |
|
1724 } |
|
1725 Insert(f, aReflowStatus); |
|
1726 } |
|
1727 } |
|
1728 return rv; |
|
1729 } |
|
1730 |
|
1731 void |
|
1732 nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild) |
|
1733 { |
|
1734 NS_PRECONDITION(aChild, "null ptr"); |
|
1735 NS_PRECONDITION(aChild->GetNextInFlow(), |
|
1736 "supposed to call Finish *before* deleting next-in-flow!"); |
|
1737 for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) { |
|
1738 // We'll update these in EndFinish after the next-in-flows are gone. |
|
1739 if (f == mPrevOverflowCont) { |
|
1740 mSentry = nullptr; |
|
1741 mPrevOverflowCont = nullptr; |
|
1742 break; |
|
1743 } |
|
1744 if (f == mSentry) { |
|
1745 mSentry = nullptr; |
|
1746 break; |
|
1747 } |
|
1748 } |
|
1749 } |
|
1750 |
|
1751 void |
|
1752 nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild) |
|
1753 { |
|
1754 if (!mOverflowContList) { |
|
1755 return; |
|
1756 } |
|
1757 // Forget mOverflowContList if it was deleted. |
|
1758 nsPresContext* pc = aChild->PresContext(); |
|
1759 FramePropertyTable* propTable = pc->PropertyTable(); |
|
1760 nsFrameList* eoc = static_cast<nsFrameList*>(propTable->Get(mParent, |
|
1761 nsContainerFrame::ExcessOverflowContainersProperty())); |
|
1762 if (eoc != mOverflowContList) { |
|
1763 nsFrameList* oc = static_cast<nsFrameList*>(propTable->Get(mParent, |
|
1764 nsContainerFrame::OverflowContainersProperty())); |
|
1765 if (oc != mOverflowContList) { |
|
1766 // mOverflowContList was deleted |
|
1767 mPrevOverflowCont = nullptr; |
|
1768 mSentry = nullptr; |
|
1769 mParent = static_cast<nsContainerFrame*>(aChild->GetParent()); |
|
1770 mOverflowContList = nullptr; |
|
1771 SetupOverflowContList(); |
|
1772 return; |
|
1773 } |
|
1774 } |
|
1775 // The list survived, update mSentry if needed. |
|
1776 if (!mSentry) { |
|
1777 if (!mPrevOverflowCont) { |
|
1778 SetUpListWalker(); |
|
1779 } else { |
|
1780 mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont); |
|
1781 // step backward to make StepForward() use our current mPrevOverflowCont |
|
1782 mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling(); |
|
1783 StepForward(); |
|
1784 } |
|
1785 } |
|
1786 } |
|
1787 |
|
1788 ///////////////////////////////////////////////////////////////////////////// |
|
1789 // Debugging |
|
1790 |
|
1791 #ifdef DEBUG_FRAME_DUMP |
|
1792 void |
|
1793 nsContainerFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const |
|
1794 { |
|
1795 nsCString str; |
|
1796 ListGeneric(str, aPrefix, aFlags); |
|
1797 |
|
1798 // Output the children |
|
1799 bool outputOneList = false; |
|
1800 ChildListIterator lists(this); |
|
1801 for (; !lists.IsDone(); lists.Next()) { |
|
1802 if (outputOneList) { |
|
1803 str += aPrefix; |
|
1804 } |
|
1805 if (lists.CurrentID() != kPrincipalList) { |
|
1806 if (!outputOneList) { |
|
1807 str += "\n"; |
|
1808 str += aPrefix; |
|
1809 } |
|
1810 str += nsPrintfCString("%s %p ", mozilla::layout::ChildListName(lists.CurrentID()), |
|
1811 &GetChildList(lists.CurrentID())); |
|
1812 } |
|
1813 fprintf_stderr(out, "%s<\n", str.get()); |
|
1814 str = ""; |
|
1815 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
1816 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
1817 nsIFrame* kid = childFrames.get(); |
|
1818 // Verify the child frame's parent frame pointer is correct |
|
1819 NS_ASSERTION(kid->GetParent() == this, "bad parent frame pointer"); |
|
1820 |
|
1821 // Have the child frame list |
|
1822 nsCString pfx(aPrefix); |
|
1823 pfx += " "; |
|
1824 kid->List(out, pfx.get(), aFlags); |
|
1825 } |
|
1826 fprintf_stderr(out, "%s>\n", aPrefix); |
|
1827 outputOneList = true; |
|
1828 } |
|
1829 |
|
1830 if (!outputOneList) { |
|
1831 fprintf_stderr(out, "%s<>\n", str.get()); |
|
1832 } |
|
1833 } |
|
1834 #endif |