1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsContainerFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1834 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* base class #1 for rendering objects that have child lists */ 1.10 + 1.11 +#include "nsContainerFrame.h" 1.12 + 1.13 +#include "nsAbsoluteContainingBlock.h" 1.14 +#include "nsIDocument.h" 1.15 +#include "nsPresContext.h" 1.16 +#include "nsStyleContext.h" 1.17 +#include "nsRect.h" 1.18 +#include "nsPoint.h" 1.19 +#include "nsStyleConsts.h" 1.20 +#include "nsView.h" 1.21 +#include "nsIPresShell.h" 1.22 +#include "nsCOMPtr.h" 1.23 +#include "nsGkAtoms.h" 1.24 +#include "nsViewManager.h" 1.25 +#include "nsIWidget.h" 1.26 +#include "nsCSSRendering.h" 1.27 +#include "nsError.h" 1.28 +#include "nsDisplayList.h" 1.29 +#include "nsIBaseWindow.h" 1.30 +#include "nsBoxLayoutState.h" 1.31 +#include "nsCSSFrameConstructor.h" 1.32 +#include "nsBlockFrame.h" 1.33 +#include "mozilla/AutoRestore.h" 1.34 +#include "nsIFrameInlines.h" 1.35 +#include "nsPrintfCString.h" 1.36 +#include <algorithm> 1.37 + 1.38 +#ifdef DEBUG 1.39 +#undef NOISY 1.40 +#else 1.41 +#undef NOISY 1.42 +#endif 1.43 + 1.44 +using namespace mozilla; 1.45 +using namespace mozilla::dom; 1.46 +using namespace mozilla::layout; 1.47 + 1.48 +NS_IMPL_FRAMEARENA_HELPERS(nsContainerFrame) 1.49 + 1.50 +nsContainerFrame::~nsContainerFrame() 1.51 +{ 1.52 +} 1.53 + 1.54 +NS_QUERYFRAME_HEAD(nsContainerFrame) 1.55 + NS_QUERYFRAME_ENTRY(nsContainerFrame) 1.56 +NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame) 1.57 + 1.58 +void 1.59 +nsContainerFrame::Init(nsIContent* aContent, 1.60 + nsIFrame* aParent, 1.61 + nsIFrame* aPrevInFlow) 1.62 +{ 1.63 + nsSplittableFrame::Init(aContent, aParent, aPrevInFlow); 1.64 + if (aPrevInFlow) { 1.65 + // Make sure we copy bits from our prev-in-flow that will affect 1.66 + // us. A continuation for a container frame needs to know if it 1.67 + // has a child with a view so that we'll properly reposition it. 1.68 + if (aPrevInFlow->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) 1.69 + AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW); 1.70 + } 1.71 +} 1.72 + 1.73 +nsresult 1.74 +nsContainerFrame::SetInitialChildList(ChildListID aListID, 1.75 + nsFrameList& aChildList) 1.76 +{ 1.77 + nsresult result; 1.78 + if (mFrames.NotEmpty()) { 1.79 + // We already have child frames which means we've already been 1.80 + // initialized 1.81 + NS_NOTREACHED("unexpected second call to SetInitialChildList"); 1.82 + result = NS_ERROR_UNEXPECTED; 1.83 + } else if (aListID != kPrincipalList) { 1.84 + // All we know about is the principal child list. 1.85 + NS_NOTREACHED("unknown frame list"); 1.86 + result = NS_ERROR_INVALID_ARG; 1.87 + } else { 1.88 +#ifdef DEBUG 1.89 + nsFrame::VerifyDirtyBitSet(aChildList); 1.90 +#endif 1.91 + mFrames.SetFrames(aChildList); 1.92 + result = NS_OK; 1.93 + } 1.94 + return result; 1.95 +} 1.96 + 1.97 +nsresult 1.98 +nsContainerFrame::AppendFrames(ChildListID aListID, 1.99 + nsFrameList& aFrameList) 1.100 +{ 1.101 + if (aListID != kPrincipalList) { 1.102 + if (aListID != kNoReflowPrincipalList) 1.103 + { 1.104 + NS_ERROR("unexpected child list"); 1.105 + return NS_ERROR_INVALID_ARG; 1.106 + } 1.107 + } 1.108 + if (aFrameList.NotEmpty()) { 1.109 + mFrames.AppendFrames(this, aFrameList); 1.110 + 1.111 + // Ask the parent frame to reflow me. 1.112 + if (aListID == kPrincipalList) 1.113 + { 1.114 + PresContext()->PresShell()-> 1.115 + FrameNeedsReflow(this, nsIPresShell::eTreeChange, 1.116 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.117 + } 1.118 + } 1.119 + return NS_OK; 1.120 +} 1.121 + 1.122 +nsresult 1.123 +nsContainerFrame::InsertFrames(ChildListID aListID, 1.124 + nsIFrame* aPrevFrame, 1.125 + nsFrameList& aFrameList) 1.126 +{ 1.127 + NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, 1.128 + "inserting after sibling frame with different parent"); 1.129 + 1.130 + if (aListID != kPrincipalList) { 1.131 + if (aListID != kNoReflowPrincipalList) 1.132 + { 1.133 + NS_ERROR("unexpected child list"); 1.134 + return NS_ERROR_INVALID_ARG; 1.135 + } 1.136 + } 1.137 + if (aFrameList.NotEmpty()) { 1.138 + // Insert frames after aPrevFrame 1.139 + mFrames.InsertFrames(this, aPrevFrame, aFrameList); 1.140 + 1.141 + if (aListID == kPrincipalList) 1.142 + { 1.143 + PresContext()->PresShell()-> 1.144 + FrameNeedsReflow(this, nsIPresShell::eTreeChange, 1.145 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.146 + } 1.147 + } 1.148 + return NS_OK; 1.149 +} 1.150 + 1.151 +nsresult 1.152 +nsContainerFrame::RemoveFrame(ChildListID aListID, 1.153 + nsIFrame* aOldFrame) 1.154 +{ 1.155 + if (aListID != kPrincipalList) { 1.156 + if (kNoReflowPrincipalList != aListID) 1.157 + { 1.158 + NS_ERROR("unexpected child list"); 1.159 + return NS_ERROR_INVALID_ARG; 1.160 + } 1.161 + } 1.162 + 1.163 + // Loop and destroy aOldFrame and all of its continuations. 1.164 + // Request a reflow on the parent frames involved unless we were explicitly 1.165 + // told not to (kNoReflowPrincipalList). 1.166 + bool generateReflowCommand = true; 1.167 + if (kNoReflowPrincipalList == aListID) { 1.168 + generateReflowCommand = false; 1.169 + } 1.170 + nsIPresShell* shell = PresContext()->PresShell(); 1.171 + nsContainerFrame* lastParent = nullptr; 1.172 + while (aOldFrame) { 1.173 + //XXXfr probably should use StealFrame here. I'm not sure if we need to 1.174 + // check the overflow lists atm, but we'll need a prescontext lookup 1.175 + // for overflow containers once we can split abspos elements with 1.176 + // inline containing blocks. 1.177 + nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation(); 1.178 + nsContainerFrame* parent = 1.179 + static_cast<nsContainerFrame*>(aOldFrame->GetParent()); 1.180 + parent->StealFrame(aOldFrame, true); 1.181 + aOldFrame->Destroy(); 1.182 + aOldFrame = oldFrameNextContinuation; 1.183 + if (parent != lastParent && generateReflowCommand) { 1.184 + shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange, 1.185 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.186 + lastParent = parent; 1.187 + } 1.188 + } 1.189 + return NS_OK; 1.190 +} 1.191 + 1.192 +void 1.193 +nsContainerFrame::DestroyAbsoluteFrames(nsIFrame* aDestructRoot) 1.194 +{ 1.195 + if (IsAbsoluteContainer()) { 1.196 + GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot); 1.197 + MarkAsNotAbsoluteContainingBlock(); 1.198 + } 1.199 +} 1.200 + 1.201 +void 1.202 +nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame* aDestructRoot, 1.203 + nsIPresShell* aPresShell, 1.204 + FramePropertyTable* aPropTable, 1.205 + const FramePropertyDescriptor* aProp) 1.206 +{ 1.207 + // Note that the last frame can be removed through another route and thus 1.208 + // delete the property -- that's why we fetch the property again before 1.209 + // removing each frame rather than fetching it once and iterating the list. 1.210 + while (nsFrameList* frameList = 1.211 + static_cast<nsFrameList*>(aPropTable->Get(this, aProp))) { 1.212 + nsIFrame* frame = frameList->RemoveFirstChild(); 1.213 + if (MOZ_LIKELY(frame)) { 1.214 + frame->DestroyFrom(aDestructRoot); 1.215 + } else { 1.216 + aPropTable->Remove(this, aProp); 1.217 + frameList->Delete(aPresShell); 1.218 + return; 1.219 + } 1.220 + } 1.221 +} 1.222 + 1.223 +void 1.224 +nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.225 +{ 1.226 + // Prevent event dispatch during destruction. 1.227 + if (HasView()) { 1.228 + GetView()->SetFrame(nullptr); 1.229 + } 1.230 + 1.231 + DestroyAbsoluteFrames(aDestructRoot); 1.232 + 1.233 + // Destroy frames on the principal child list. 1.234 + mFrames.DestroyFramesFrom(aDestructRoot); 1.235 + 1.236 + // Destroy frames on the auxiliary frame lists and delete the lists. 1.237 + nsPresContext* pc = PresContext(); 1.238 + nsIPresShell* shell = pc->PresShell(); 1.239 + FramePropertyTable* props = pc->PropertyTable(); 1.240 + SafelyDestroyFrameListProp(aDestructRoot, shell, props, OverflowProperty()); 1.241 + 1.242 + MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers) || 1.243 + !(props->Get(this, nsContainerFrame::OverflowContainersProperty()) || 1.244 + props->Get(this, nsContainerFrame::ExcessOverflowContainersProperty())), 1.245 + "this type of frame should't have overflow containers"); 1.246 + 1.247 + SafelyDestroyFrameListProp(aDestructRoot, shell, props, 1.248 + OverflowContainersProperty()); 1.249 + SafelyDestroyFrameListProp(aDestructRoot, shell, props, 1.250 + ExcessOverflowContainersProperty()); 1.251 + 1.252 + nsSplittableFrame::DestroyFrom(aDestructRoot); 1.253 +} 1.254 + 1.255 +///////////////////////////////////////////////////////////////////////////// 1.256 +// Child frame enumeration 1.257 + 1.258 +const nsFrameList& 1.259 +nsContainerFrame::GetChildList(ChildListID aListID) const 1.260 +{ 1.261 + // We only know about the principal child list and the overflow lists. 1.262 + switch (aListID) { 1.263 + case kPrincipalList: 1.264 + return mFrames; 1.265 + case kOverflowList: { 1.266 + nsFrameList* list = GetOverflowFrames(); 1.267 + return list ? *list : nsFrameList::EmptyList(); 1.268 + } 1.269 + case kOverflowContainersList: { 1.270 + nsFrameList* list = GetPropTableFrames(OverflowContainersProperty()); 1.271 + return list ? *list : nsFrameList::EmptyList(); 1.272 + } 1.273 + case kExcessOverflowContainersList: { 1.274 + nsFrameList* list = 1.275 + GetPropTableFrames(ExcessOverflowContainersProperty()); 1.276 + return list ? *list : nsFrameList::EmptyList(); 1.277 + } 1.278 + default: 1.279 + return nsSplittableFrame::GetChildList(aListID); 1.280 + } 1.281 +} 1.282 + 1.283 +static void AppendIfNonempty(const nsIFrame* aFrame, 1.284 + FramePropertyTable* aPropTable, 1.285 + const FramePropertyDescriptor* aProperty, 1.286 + nsTArray<nsIFrame::ChildList>* aLists, 1.287 + nsIFrame::ChildListID aListID) 1.288 +{ 1.289 + nsFrameList* list = static_cast<nsFrameList*>( 1.290 + aPropTable->Get(aFrame, aProperty)); 1.291 + if (list) { 1.292 + list->AppendIfNonempty(aLists, aListID); 1.293 + } 1.294 +} 1.295 + 1.296 +void 1.297 +nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const 1.298 +{ 1.299 + mFrames.AppendIfNonempty(aLists, kPrincipalList); 1.300 + FramePropertyTable* propTable = PresContext()->PropertyTable(); 1.301 + ::AppendIfNonempty(this, propTable, OverflowProperty(), 1.302 + aLists, kOverflowList); 1.303 + if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) { 1.304 + ::AppendIfNonempty(this, propTable, OverflowContainersProperty(), 1.305 + aLists, kOverflowContainersList); 1.306 + ::AppendIfNonempty(this, propTable, ExcessOverflowContainersProperty(), 1.307 + aLists, kExcessOverflowContainersList); 1.308 + } 1.309 + nsSplittableFrame::GetChildLists(aLists); 1.310 +} 1.311 + 1.312 +///////////////////////////////////////////////////////////////////////////// 1.313 +// Painting/Events 1.314 + 1.315 +void 1.316 +nsContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.317 + const nsRect& aDirtyRect, 1.318 + const nsDisplayListSet& aLists) 1.319 +{ 1.320 + DisplayBorderBackgroundOutline(aBuilder, aLists); 1.321 + 1.322 + BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists); 1.323 +} 1.324 + 1.325 +void 1.326 +nsContainerFrame::BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder, 1.327 + const nsRect& aDirtyRect, 1.328 + const nsDisplayListSet& aLists, 1.329 + uint32_t aFlags) 1.330 +{ 1.331 + nsIFrame* kid = mFrames.FirstChild(); 1.332 + // Put each child's background directly onto the content list 1.333 + nsDisplayListSet set(aLists, aLists.Content()); 1.334 + // The children should be in content order 1.335 + while (kid) { 1.336 + BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, aFlags); 1.337 + kid = kid->GetNextSibling(); 1.338 + } 1.339 +} 1.340 + 1.341 +/* virtual */ void 1.342 +nsContainerFrame::ChildIsDirty(nsIFrame* aChild) 1.343 +{ 1.344 + NS_ASSERTION(NS_SUBTREE_DIRTY(aChild), "child isn't actually dirty"); 1.345 + 1.346 + AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); 1.347 +} 1.348 + 1.349 +bool 1.350 +nsContainerFrame::IsLeaf() const 1.351 +{ 1.352 + return false; 1.353 +} 1.354 + 1.355 +nsIFrame::FrameSearchResult 1.356 +nsContainerFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset) 1.357 +{ 1.358 + NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); 1.359 + // Don't allow the caret to stay in an empty (leaf) container frame. 1.360 + return CONTINUE_EMPTY; 1.361 +} 1.362 + 1.363 +nsIFrame::FrameSearchResult 1.364 +nsContainerFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset, 1.365 + bool aRespectClusters) 1.366 +{ 1.367 + NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range"); 1.368 + // Don't allow the caret to stay in an empty (leaf) container frame. 1.369 + return CONTINUE_EMPTY; 1.370 +} 1.371 + 1.372 +///////////////////////////////////////////////////////////////////////////// 1.373 +// Helper member functions 1.374 + 1.375 +static nsresult 1.376 +ReparentFrameViewTo(nsIFrame* aFrame, 1.377 + nsViewManager* aViewManager, 1.378 + nsView* aNewParentView, 1.379 + nsView* aOldParentView) 1.380 +{ 1.381 + 1.382 + // XXX What to do about placeholder views for "position: fixed" elements? 1.383 + // They should be reparented too. 1.384 + 1.385 + // Does aFrame have a view? 1.386 + if (aFrame->HasView()) { 1.387 +#ifdef MOZ_XUL 1.388 + if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) { 1.389 + // This view must be parented by the root view, don't reparent it. 1.390 + return NS_OK; 1.391 + } 1.392 +#endif 1.393 + nsView* view = aFrame->GetView(); 1.394 + // Verify that the current parent view is what we think it is 1.395 + //nsView* parentView; 1.396 + //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view"); 1.397 + 1.398 + aViewManager->RemoveChild(view); 1.399 + 1.400 + // The view will remember the Z-order and other attributes that have been set on it. 1.401 + nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame); 1.402 + aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr); 1.403 + } else { 1.404 + nsIFrame::ChildListIterator lists(aFrame); 1.405 + for (; !lists.IsDone(); lists.Next()) { 1.406 + // Iterate the child frames, and check each child frame to see if it has 1.407 + // a view 1.408 + nsFrameList::Enumerator childFrames(lists.CurrentList()); 1.409 + for (; !childFrames.AtEnd(); childFrames.Next()) { 1.410 + ReparentFrameViewTo(childFrames.get(), aViewManager, 1.411 + aNewParentView, aOldParentView); 1.412 + } 1.413 + } 1.414 + } 1.415 + 1.416 + return NS_OK; 1.417 +} 1.418 + 1.419 +void 1.420 +nsContainerFrame::CreateViewForFrame(nsIFrame* aFrame, 1.421 + bool aForce) 1.422 +{ 1.423 + if (aFrame->HasView()) { 1.424 + return; 1.425 + } 1.426 + 1.427 + // If we don't yet have a view, see if we need a view 1.428 + if (!aForce && !aFrame->NeedsView()) { 1.429 + // don't need a view 1.430 + return; 1.431 + } 1.432 + 1.433 + nsView* parentView = aFrame->GetParent()->GetClosestView(); 1.434 + NS_ASSERTION(parentView, "no parent with view"); 1.435 + 1.436 + nsViewManager* viewManager = parentView->GetViewManager(); 1.437 + NS_ASSERTION(viewManager, "null view manager"); 1.438 + 1.439 + // Create a view 1.440 + nsView* view = viewManager->CreateView(aFrame->GetRect(), parentView); 1.441 + 1.442 + SyncFrameViewProperties(aFrame->PresContext(), aFrame, nullptr, view); 1.443 + 1.444 + nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame); 1.445 + // we insert this view 'above' the insertBefore view, unless insertBefore is null, 1.446 + // in which case we want to call with aAbove == false to insert at the beginning 1.447 + // in document order 1.448 + viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr); 1.449 + 1.450 + // REVIEW: Don't create a widget for fixed-pos elements anymore. 1.451 + // ComputeRepaintRegionForCopy will calculate the right area to repaint 1.452 + // when we scroll. 1.453 + // Reparent views on any child frames (or their descendants) to this 1.454 + // view. We can just call ReparentFrameViewTo on this frame because 1.455 + // we know this frame has no view, so it will crawl the children. Also, 1.456 + // we know that any descendants with views must have 'parentView' as their 1.457 + // parent view. 1.458 + ReparentFrameViewTo(aFrame, viewManager, view, parentView); 1.459 + 1.460 + // Remember our view 1.461 + aFrame->SetView(view); 1.462 + 1.463 + NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, 1.464 + ("nsContainerFrame::CreateViewForFrame: frame=%p view=%p", 1.465 + aFrame)); 1.466 +} 1.467 + 1.468 +/** 1.469 + * Position the view associated with |aKidFrame|, if there is one. A 1.470 + * container frame should call this method after positioning a frame, 1.471 + * but before |Reflow|. 1.472 + */ 1.473 +void 1.474 +nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame) 1.475 +{ 1.476 + nsIFrame* parentFrame = aKidFrame->GetParent(); 1.477 + if (!aKidFrame->HasView() || !parentFrame) 1.478 + return; 1.479 + 1.480 + nsView* view = aKidFrame->GetView(); 1.481 + nsViewManager* vm = view->GetViewManager(); 1.482 + nsPoint pt; 1.483 + nsView* ancestorView = parentFrame->GetClosestView(&pt); 1.484 + 1.485 + if (ancestorView != view->GetParent()) { 1.486 + NS_ASSERTION(ancestorView == view->GetParent()->GetParent(), 1.487 + "Allowed only one anonymous view between frames"); 1.488 + // parentFrame is responsible for positioning aKidFrame's view 1.489 + // explicitly 1.490 + return; 1.491 + } 1.492 + 1.493 + pt += aKidFrame->GetPosition(); 1.494 + vm->MoveViewTo(view, pt.x, pt.y); 1.495 +} 1.496 + 1.497 +nsresult 1.498 +nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame, 1.499 + nsIFrame* aOldParentFrame, 1.500 + nsIFrame* aNewParentFrame) 1.501 +{ 1.502 + NS_PRECONDITION(aChildFrame, "null child frame pointer"); 1.503 + NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer"); 1.504 + NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer"); 1.505 + NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame"); 1.506 + 1.507 + // See if either the old parent frame or the new parent frame have a view 1.508 + while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) { 1.509 + // Walk up both the old parent frame and the new parent frame nodes 1.510 + // stopping when we either find a common parent or views for one 1.511 + // or both of the frames. 1.512 + // 1.513 + // This works well in the common case where we push/pull and the old parent 1.514 + // frame and the new parent frame are part of the same flow. They will 1.515 + // typically be the same distance (height wise) from the 1.516 + aOldParentFrame = aOldParentFrame->GetParent(); 1.517 + aNewParentFrame = aNewParentFrame->GetParent(); 1.518 + 1.519 + // We should never walk all the way to the root frame without finding 1.520 + // a view 1.521 + NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view"); 1.522 + 1.523 + // See if we reached a common ancestor 1.524 + if (aOldParentFrame == aNewParentFrame) { 1.525 + break; 1.526 + } 1.527 + } 1.528 + 1.529 + // See if we found a common parent frame 1.530 + if (aOldParentFrame == aNewParentFrame) { 1.531 + // We found a common parent and there are no views between the old parent 1.532 + // and the common parent or the new parent frame and the common parent. 1.533 + // Because neither the old parent frame nor the new parent frame have views, 1.534 + // then any child views don't need reparenting 1.535 + return NS_OK; 1.536 + } 1.537 + 1.538 + // We found views for one or both of the ancestor frames before we 1.539 + // found a common ancestor. 1.540 + nsView* oldParentView = aOldParentFrame->GetClosestView(); 1.541 + nsView* newParentView = aNewParentFrame->GetClosestView(); 1.542 + 1.543 + // See if the old parent frame and the new parent frame are in the 1.544 + // same view sub-hierarchy. If they are then we don't have to do 1.545 + // anything 1.546 + if (oldParentView != newParentView) { 1.547 + // They're not so we need to reparent any child views 1.548 + return ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView, 1.549 + oldParentView); 1.550 + } 1.551 + 1.552 + return NS_OK; 1.553 +} 1.554 + 1.555 +nsresult 1.556 +nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList, 1.557 + nsIFrame* aOldParentFrame, 1.558 + nsIFrame* aNewParentFrame) 1.559 +{ 1.560 + NS_PRECONDITION(aChildFrameList.NotEmpty(), "empty child frame list"); 1.561 + NS_PRECONDITION(aOldParentFrame, "null old parent frame pointer"); 1.562 + NS_PRECONDITION(aNewParentFrame, "null new parent frame pointer"); 1.563 + NS_PRECONDITION(aOldParentFrame != aNewParentFrame, "same old and new parent frame"); 1.564 + 1.565 + // See if either the old parent frame or the new parent frame have a view 1.566 + while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) { 1.567 + // Walk up both the old parent frame and the new parent frame nodes 1.568 + // stopping when we either find a common parent or views for one 1.569 + // or both of the frames. 1.570 + // 1.571 + // This works well in the common case where we push/pull and the old parent 1.572 + // frame and the new parent frame are part of the same flow. They will 1.573 + // typically be the same distance (height wise) from the 1.574 + aOldParentFrame = aOldParentFrame->GetParent(); 1.575 + aNewParentFrame = aNewParentFrame->GetParent(); 1.576 + 1.577 + // We should never walk all the way to the root frame without finding 1.578 + // a view 1.579 + NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view"); 1.580 + 1.581 + // See if we reached a common ancestor 1.582 + if (aOldParentFrame == aNewParentFrame) { 1.583 + break; 1.584 + } 1.585 + } 1.586 + 1.587 + 1.588 + // See if we found a common parent frame 1.589 + if (aOldParentFrame == aNewParentFrame) { 1.590 + // We found a common parent and there are no views between the old parent 1.591 + // and the common parent or the new parent frame and the common parent. 1.592 + // Because neither the old parent frame nor the new parent frame have views, 1.593 + // then any child views don't need reparenting 1.594 + return NS_OK; 1.595 + } 1.596 + 1.597 + // We found views for one or both of the ancestor frames before we 1.598 + // found a common ancestor. 1.599 + nsView* oldParentView = aOldParentFrame->GetClosestView(); 1.600 + nsView* newParentView = aNewParentFrame->GetClosestView(); 1.601 + 1.602 + // See if the old parent frame and the new parent frame are in the 1.603 + // same view sub-hierarchy. If they are then we don't have to do 1.604 + // anything 1.605 + if (oldParentView != newParentView) { 1.606 + nsViewManager* viewManager = oldParentView->GetViewManager(); 1.607 + 1.608 + // They're not so we need to reparent any child views 1.609 + for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) { 1.610 + ReparentFrameViewTo(e.get(), viewManager, newParentView, oldParentView); 1.611 + } 1.612 + } 1.613 + 1.614 + return NS_OK; 1.615 +} 1.616 + 1.617 +static nsIWidget* 1.618 +GetPresContextContainerWidget(nsPresContext* aPresContext) 1.619 +{ 1.620 + nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer(); 1.621 + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container); 1.622 + if (!baseWindow) 1.623 + return nullptr; 1.624 + 1.625 + nsCOMPtr<nsIWidget> mainWidget; 1.626 + baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); 1.627 + return mainWidget; 1.628 +} 1.629 + 1.630 +static bool 1.631 +IsTopLevelWidget(nsIWidget* aWidget) 1.632 +{ 1.633 + nsWindowType windowType = aWidget->WindowType(); 1.634 + return windowType == eWindowType_toplevel || 1.635 + windowType == eWindowType_dialog || 1.636 + windowType == eWindowType_sheet; 1.637 + // popups aren't toplevel so they're not handled here 1.638 +} 1.639 + 1.640 +void 1.641 +nsContainerFrame::SyncWindowProperties(nsPresContext* aPresContext, 1.642 + nsIFrame* aFrame, 1.643 + nsView* aView, 1.644 + nsRenderingContext* aRC) 1.645 +{ 1.646 +#ifdef MOZ_XUL 1.647 + if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget()) 1.648 + return; 1.649 + 1.650 + nsIWidget* windowWidget = GetPresContextContainerWidget(aPresContext); 1.651 + if (!windowWidget || !IsTopLevelWidget(windowWidget)) 1.652 + return; 1.653 + 1.654 + nsViewManager* vm = aView->GetViewManager(); 1.655 + nsView* rootView = vm->GetRootView(); 1.656 + 1.657 + if (aView != rootView) 1.658 + return; 1.659 + 1.660 + Element* rootElement = aPresContext->Document()->GetRootElement(); 1.661 + if (!rootElement || !rootElement->IsXUL()) { 1.662 + // Scrollframes use native widgets which don't work well with 1.663 + // translucent windows, at least in Windows XP. So if the document 1.664 + // has a root scrollrame it's useless to try to make it transparent, 1.665 + // we'll just get something broken. 1.666 + // nsCSSFrameConstructor::ConstructRootFrame constructs root 1.667 + // scrollframes whenever the root element is not a XUL element, so 1.668 + // we test for that here. We can't just call 1.669 + // presShell->GetRootScrollFrame() since that might not have 1.670 + // been constructed yet. 1.671 + // We can change this to allow translucent toplevel HTML documents 1.672 + // (e.g. to do something like Dashboard widgets), once we 1.673 + // have broad support for translucent scrolled documents, but be 1.674 + // careful because apparently some Firefox extensions expect 1.675 + // openDialog("something.html") to produce an opaque window 1.676 + // even if the HTML doesn't have a background-color set. 1.677 + return; 1.678 + } 1.679 + 1.680 + nsIFrame *rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame(); 1.681 + if (!rootFrame) 1.682 + return; 1.683 + 1.684 + nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame); 1.685 + nsIWidget* viewWidget = aView->GetWidget(); 1.686 + viewWidget->SetTransparencyMode(mode); 1.687 + windowWidget->SetWindowShadowStyle(rootFrame->StyleUIReset()->mWindowShadow); 1.688 + 1.689 + if (!aRC) 1.690 + return; 1.691 + 1.692 + nsBoxLayoutState aState(aPresContext, aRC); 1.693 + nsSize minSize = rootFrame->GetMinSize(aState); 1.694 + nsSize maxSize = rootFrame->GetMaxSize(aState); 1.695 + 1.696 + SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize); 1.697 +#endif 1.698 +} 1.699 + 1.700 +void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext, 1.701 + nsIWidget* aWidget, 1.702 + const nsSize& aMinSize, 1.703 + const nsSize& aMaxSize) 1.704 +{ 1.705 + nsIntSize devMinSize(aPresContext->AppUnitsToDevPixels(aMinSize.width), 1.706 + aPresContext->AppUnitsToDevPixels(aMinSize.height)); 1.707 + nsIntSize devMaxSize(aMaxSize.width == NS_INTRINSICSIZE ? NS_MAXSIZE : 1.708 + aPresContext->AppUnitsToDevPixels(aMaxSize.width), 1.709 + aMaxSize.height == NS_INTRINSICSIZE ? NS_MAXSIZE : 1.710 + aPresContext->AppUnitsToDevPixels(aMaxSize.height)); 1.711 + widget::SizeConstraints constraints(devMinSize, devMaxSize); 1.712 + 1.713 + // The sizes are in inner window sizes, so convert them into outer window sizes. 1.714 + // Use a size of (200, 200) as only the difference between the inner and outer 1.715 + // size is needed. 1.716 + nsIntSize windowSize = aWidget->ClientToWindowSize(nsIntSize(200, 200)); 1.717 + if (constraints.mMinSize.width) 1.718 + constraints.mMinSize.width += windowSize.width - 200; 1.719 + if (constraints.mMinSize.height) 1.720 + constraints.mMinSize.height += windowSize.height - 200; 1.721 + if (constraints.mMaxSize.width != NS_MAXSIZE) 1.722 + constraints.mMaxSize.width += windowSize.width - 200; 1.723 + if (constraints.mMaxSize.height != NS_MAXSIZE) 1.724 + constraints.mMaxSize.height += windowSize.height - 200; 1.725 + 1.726 + aWidget->SetSizeConstraints(constraints); 1.727 +} 1.728 + 1.729 +void 1.730 +nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext, 1.731 + nsIFrame* aFrame, 1.732 + nsView* aView, 1.733 + const nsRect& aVisualOverflowArea, 1.734 + uint32_t aFlags) 1.735 +{ 1.736 + if (!aView) { 1.737 + return; 1.738 + } 1.739 + 1.740 + // Make sure the view is sized and positioned correctly 1.741 + if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { 1.742 + PositionFrameView(aFrame); 1.743 + } 1.744 + 1.745 + if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) { 1.746 + nsViewManager* vm = aView->GetViewManager(); 1.747 + 1.748 + vm->ResizeView(aView, aVisualOverflowArea, true); 1.749 + } 1.750 +} 1.751 + 1.752 +void 1.753 +nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext, 1.754 + nsIFrame* aFrame, 1.755 + nsStyleContext* aStyleContext, 1.756 + nsView* aView, 1.757 + uint32_t aFlags) 1.758 +{ 1.759 + NS_ASSERTION(!aStyleContext || aFrame->StyleContext() == aStyleContext, 1.760 + "Wrong style context for frame?"); 1.761 + 1.762 + if (!aView) { 1.763 + return; 1.764 + } 1.765 + 1.766 + nsViewManager* vm = aView->GetViewManager(); 1.767 + 1.768 + if (nullptr == aStyleContext) { 1.769 + aStyleContext = aFrame->StyleContext(); 1.770 + } 1.771 + 1.772 + // Make sure visibility is correct. This only affects nsSubdocumentFrame. 1.773 + if (0 == (aFlags & NS_FRAME_NO_VISIBILITY) && 1.774 + !aFrame->SupportsVisibilityHidden()) { 1.775 + // See if the view should be hidden or visible 1.776 + vm->SetViewVisibility(aView, 1.777 + aStyleContext->StyleVisibility()->IsVisible() 1.778 + ? nsViewVisibility_kShow : nsViewVisibility_kHide); 1.779 + } 1.780 + 1.781 + // See if the frame is being relatively positioned or absolutely 1.782 + // positioned 1.783 + bool isPositioned = aFrame->IsPositioned(); 1.784 + 1.785 + int32_t zIndex = 0; 1.786 + bool autoZIndex = false; 1.787 + 1.788 + if (!isPositioned) { 1.789 + autoZIndex = true; 1.790 + } else { 1.791 + // Make sure z-index is correct 1.792 + const nsStylePosition* position = aStyleContext->StylePosition(); 1.793 + 1.794 + if (position->mZIndex.GetUnit() == eStyleUnit_Integer) { 1.795 + zIndex = position->mZIndex.GetIntValue(); 1.796 + } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) { 1.797 + autoZIndex = true; 1.798 + } 1.799 + } 1.800 + 1.801 + vm->SetViewZIndex(aView, autoZIndex, zIndex); 1.802 +} 1.803 + 1.804 +static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord) 1.805 +{ 1.806 + if (aCoord.ConvertsToLength()) { 1.807 + return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0); 1.808 + } 1.809 + return aIfNotCoord; 1.810 +} 1.811 + 1.812 +void 1.813 +nsContainerFrame::DoInlineIntrinsicWidth(nsRenderingContext *aRenderingContext, 1.814 + InlineIntrinsicWidthData *aData, 1.815 + nsLayoutUtils::IntrinsicWidthType aType) 1.816 +{ 1.817 + if (GetPrevInFlow()) 1.818 + return; // Already added. 1.819 + 1.820 + NS_PRECONDITION(aType == nsLayoutUtils::MIN_WIDTH || 1.821 + aType == nsLayoutUtils::PREF_WIDTH, "bad type"); 1.822 + 1.823 + mozilla::css::Side startSide, endSide; 1.824 + if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR) { 1.825 + startSide = NS_SIDE_LEFT; 1.826 + endSide = NS_SIDE_RIGHT; 1.827 + } else { 1.828 + startSide = NS_SIDE_RIGHT; 1.829 + endSide = NS_SIDE_LEFT; 1.830 + } 1.831 + 1.832 + const nsStylePadding *stylePadding = StylePadding(); 1.833 + const nsStyleBorder *styleBorder = StyleBorder(); 1.834 + const nsStyleMargin *styleMargin = StyleMargin(); 1.835 + 1.836 + // This goes at the beginning no matter how things are broken and how 1.837 + // messy the bidi situations are, since per CSS2.1 section 8.6 1.838 + // (implemented in bug 328168), the startSide border is always on the 1.839 + // first line. 1.840 + // This frame is a first-in-flow, but it might have a previous bidi 1.841 + // continuation, in which case that continuation should handle the startSide 1.842 + // border. 1.843 + if (!GetPrevContinuation()) { 1.844 + aData->currentLine += 1.845 + // clamp negative calc() to 0 1.846 + std::max(GetCoord(stylePadding->mPadding.Get(startSide), 0), 0) + 1.847 + styleBorder->GetComputedBorderWidth(startSide) + 1.848 + GetCoord(styleMargin->mMargin.Get(startSide), 0); 1.849 + } 1.850 + 1.851 + const nsLineList_iterator* savedLine = aData->line; 1.852 + nsIFrame* const savedLineContainer = aData->lineContainer; 1.853 + 1.854 + nsContainerFrame *lastInFlow; 1.855 + for (nsContainerFrame *nif = this; nif; 1.856 + nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow())) { 1.857 + for (nsIFrame *kid = nif->mFrames.FirstChild(); kid; 1.858 + kid = kid->GetNextSibling()) { 1.859 + if (aType == nsLayoutUtils::MIN_WIDTH) 1.860 + kid->AddInlineMinWidth(aRenderingContext, 1.861 + static_cast<InlineMinWidthData*>(aData)); 1.862 + else 1.863 + kid->AddInlinePrefWidth(aRenderingContext, 1.864 + static_cast<InlinePrefWidthData*>(aData)); 1.865 + } 1.866 + 1.867 + // After we advance to our next-in-flow, the stored line and line container 1.868 + // may no longer be correct. Just forget them. 1.869 + aData->line = nullptr; 1.870 + aData->lineContainer = nullptr; 1.871 + 1.872 + lastInFlow = nif; 1.873 + } 1.874 + 1.875 + aData->line = savedLine; 1.876 + aData->lineContainer = savedLineContainer; 1.877 + 1.878 + // This goes at the end no matter how things are broken and how 1.879 + // messy the bidi situations are, since per CSS2.1 section 8.6 1.880 + // (implemented in bug 328168), the endSide border is always on the 1.881 + // last line. 1.882 + // We reached the last-in-flow, but it might have a next bidi 1.883 + // continuation, in which case that continuation should handle 1.884 + // the endSide border. 1.885 + if (!lastInFlow->GetNextContinuation()) { 1.886 + aData->currentLine += 1.887 + // clamp negative calc() to 0 1.888 + std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) + 1.889 + styleBorder->GetComputedBorderWidth(endSide) + 1.890 + GetCoord(styleMargin->mMargin.Get(endSide), 0); 1.891 + } 1.892 +} 1.893 + 1.894 +/* virtual */ nsSize 1.895 +nsContainerFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, 1.896 + nsSize aCBSize, nscoord aAvailableWidth, 1.897 + nsSize aMargin, nsSize aBorder, 1.898 + nsSize aPadding, bool aShrinkWrap) 1.899 +{ 1.900 + nsSize result(0xdeadbeef, NS_UNCONSTRAINEDSIZE); 1.901 + nscoord availBased = aAvailableWidth - aMargin.width - aBorder.width - 1.902 + aPadding.width; 1.903 + // replaced elements always shrink-wrap 1.904 + if (aShrinkWrap || IsFrameOfType(eReplaced)) { 1.905 + // don't bother setting it if the result won't be used 1.906 + if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) { 1.907 + result.width = ShrinkWidthToFit(aRenderingContext, availBased); 1.908 + } 1.909 + } else { 1.910 + result.width = availBased; 1.911 + } 1.912 + return result; 1.913 +} 1.914 + 1.915 +/** 1.916 + * Invokes the WillReflow() function, positions the frame and its view (if 1.917 + * requested), and then calls Reflow(). If the reflow succeeds and the child 1.918 + * frame is complete, deletes any next-in-flows using DeleteNextInFlowChild() 1.919 + */ 1.920 +nsresult 1.921 +nsContainerFrame::ReflowChild(nsIFrame* aKidFrame, 1.922 + nsPresContext* aPresContext, 1.923 + nsHTMLReflowMetrics& aDesiredSize, 1.924 + const nsHTMLReflowState& aReflowState, 1.925 + nscoord aX, 1.926 + nscoord aY, 1.927 + uint32_t aFlags, 1.928 + nsReflowStatus& aStatus, 1.929 + nsOverflowContinuationTracker* aTracker) 1.930 +{ 1.931 + NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state"); 1.932 + 1.933 + nsresult result; 1.934 + 1.935 + // Send the WillReflow() notification, and position the child frame 1.936 + // and its view if requested 1.937 + aKidFrame->WillReflow(aPresContext); 1.938 + 1.939 + if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { 1.940 + aKidFrame->SetPosition(nsPoint(aX, aY)); 1.941 + } 1.942 + 1.943 + if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { 1.944 + PositionFrameView(aKidFrame); 1.945 + } 1.946 + 1.947 + // Reflow the child frame 1.948 + result = aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState, 1.949 + aStatus); 1.950 + 1.951 + // If the reflow was successful and the child frame is complete, delete any 1.952 + // next-in-flows, but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set. 1.953 + if (NS_SUCCEEDED(result) && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && 1.954 + !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) { 1.955 + nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow(); 1.956 + if (kidNextInFlow) { 1.957 + // Remove all of the childs next-in-flows. Make sure that we ask 1.958 + // the right parent to do the removal (it's possible that the 1.959 + // parent is not this because we are executing pullup code) 1.960 + nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame); 1.961 + static_cast<nsContainerFrame*>(kidNextInFlow->GetParent()) 1.962 + ->DeleteNextInFlowChild(kidNextInFlow, true); 1.963 + } 1.964 + } 1.965 + return result; 1.966 +} 1.967 + 1.968 + 1.969 +/** 1.970 + * Position the views of |aFrame|'s descendants. A container frame 1.971 + * should call this method if it moves a frame after |Reflow|. 1.972 + */ 1.973 +void 1.974 +nsContainerFrame::PositionChildViews(nsIFrame* aFrame) 1.975 +{ 1.976 + if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) { 1.977 + return; 1.978 + } 1.979 + 1.980 + // Recursively walk aFrame's child frames. 1.981 + // Process the additional child lists, but skip the popup list as the 1.982 + // view for popups is managed by the parent. Currently only nsMenuFrame 1.983 + // and nsPopupSetFrame have a popupList and during layout will adjust the 1.984 + // view manually to position the popup. 1.985 + ChildListIterator lists(aFrame); 1.986 + for (; !lists.IsDone(); lists.Next()) { 1.987 + if (lists.CurrentID() == kPopupList) { 1.988 + continue; 1.989 + } 1.990 + nsFrameList::Enumerator childFrames(lists.CurrentList()); 1.991 + for (; !childFrames.AtEnd(); childFrames.Next()) { 1.992 + // Position the frame's view (if it has one) otherwise recursively 1.993 + // process its children 1.994 + nsIFrame* childFrame = childFrames.get(); 1.995 + if (childFrame->HasView()) { 1.996 + PositionFrameView(childFrame); 1.997 + } else { 1.998 + PositionChildViews(childFrame); 1.999 + } 1.1000 + } 1.1001 + } 1.1002 +} 1.1003 + 1.1004 +/** 1.1005 + * The second half of frame reflow. Does the following: 1.1006 + * - sets the frame's bounds 1.1007 + * - sizes and positions (if requested) the frame's view. If the frame's final 1.1008 + * position differs from the current position and the frame itself does not 1.1009 + * have a view, then any child frames with views are positioned so they stay 1.1010 + * in sync 1.1011 + * - sets the view's visibility, opacity, content transparency, and clip 1.1012 + * - invoked the DidReflow() function 1.1013 + * 1.1014 + * Flags: 1.1015 + * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this 1.1016 + * case. Also implies NS_FRAME_NO_MOVE_VIEW 1.1017 + * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you 1.1018 + * don't want to automatically sync the frame and view 1.1019 + * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view 1.1020 + */ 1.1021 +nsresult 1.1022 +nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame, 1.1023 + nsPresContext* aPresContext, 1.1024 + const nsHTMLReflowMetrics& aDesiredSize, 1.1025 + const nsHTMLReflowState* aReflowState, 1.1026 + nscoord aX, 1.1027 + nscoord aY, 1.1028 + uint32_t aFlags) 1.1029 +{ 1.1030 + nsPoint curOrigin = aKidFrame->GetPosition(); 1.1031 + 1.1032 + if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { 1.1033 + aKidFrame->SetRect(nsRect(aX, aY, aDesiredSize.Width(), aDesiredSize.Height())); 1.1034 + } else { 1.1035 + aKidFrame->SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height())); 1.1036 + } 1.1037 + 1.1038 + if (aKidFrame->HasView()) { 1.1039 + nsView* view = aKidFrame->GetView(); 1.1040 + // Make sure the frame's view is properly sized and positioned and has 1.1041 + // things like opacity correct 1.1042 + SyncFrameViewAfterReflow(aPresContext, aKidFrame, view, 1.1043 + aDesiredSize.VisualOverflow(), aFlags); 1.1044 + } 1.1045 + 1.1046 + if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) && 1.1047 + (curOrigin.x != aX || curOrigin.y != aY)) { 1.1048 + if (!aKidFrame->HasView()) { 1.1049 + // If the frame has moved, then we need to make sure any child views are 1.1050 + // correctly positioned 1.1051 + PositionChildViews(aKidFrame); 1.1052 + } 1.1053 + } 1.1054 + 1.1055 + return aKidFrame->DidReflow(aPresContext, aReflowState, nsDidReflowStatus::FINISHED); 1.1056 +} 1.1057 + 1.1058 +nsresult 1.1059 +nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext* aPresContext, 1.1060 + const nsHTMLReflowState& aReflowState, 1.1061 + nsOverflowAreas& aOverflowRects, 1.1062 + uint32_t aFlags, 1.1063 + nsReflowStatus& aStatus) 1.1064 +{ 1.1065 + NS_PRECONDITION(aPresContext, "null pointer"); 1.1066 + nsresult rv = NS_OK; 1.1067 + 1.1068 + nsFrameList* overflowContainers = 1.1069 + GetPropTableFrames(OverflowContainersProperty()); 1.1070 + 1.1071 + NS_ASSERTION(!(overflowContainers && GetPrevInFlow() 1.1072 + && static_cast<nsContainerFrame*>(GetPrevInFlow()) 1.1073 + ->GetPropTableFrames(ExcessOverflowContainersProperty())), 1.1074 + "conflicting overflow containers lists"); 1.1075 + 1.1076 + if (!overflowContainers) { 1.1077 + // Drain excess from previnflow 1.1078 + nsContainerFrame* prev = (nsContainerFrame*) GetPrevInFlow(); 1.1079 + if (prev) { 1.1080 + nsFrameList* excessFrames = 1.1081 + prev->RemovePropTableFrames(ExcessOverflowContainersProperty()); 1.1082 + if (excessFrames) { 1.1083 + excessFrames->ApplySetParent(this); 1.1084 + nsContainerFrame::ReparentFrameViewList(*excessFrames, prev, this); 1.1085 + overflowContainers = excessFrames; 1.1086 + SetPropTableFrames(overflowContainers, OverflowContainersProperty()); 1.1087 + } 1.1088 + } 1.1089 + } 1.1090 + 1.1091 + // Our own excess overflow containers from a previous reflow can still be 1.1092 + // present if our next-in-flow hasn't been reflown yet. 1.1093 + nsFrameList* selfExcessOCFrames = 1.1094 + RemovePropTableFrames(ExcessOverflowContainersProperty()); 1.1095 + if (selfExcessOCFrames) { 1.1096 + if (overflowContainers) { 1.1097 + overflowContainers->AppendFrames(nullptr, *selfExcessOCFrames); 1.1098 + selfExcessOCFrames->Delete(aPresContext->PresShell()); 1.1099 + } else { 1.1100 + overflowContainers = selfExcessOCFrames; 1.1101 + SetPropTableFrames(overflowContainers, OverflowContainersProperty()); 1.1102 + } 1.1103 + } 1.1104 + if (!overflowContainers) { 1.1105 + return NS_OK; // nothing to reflow 1.1106 + } 1.1107 + 1.1108 + nsOverflowContinuationTracker tracker(this, false, false); 1.1109 + bool shouldReflowAllKids = aReflowState.ShouldReflowAllKids(); 1.1110 + 1.1111 + for (nsIFrame* frame = overflowContainers->FirstChild(); frame; 1.1112 + frame = frame->GetNextSibling()) { 1.1113 + if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) { 1.1114 + // frame's prevInFlow has moved, skip reflowing this frame; 1.1115 + // it will get reflowed once it's been placed 1.1116 + continue; 1.1117 + } 1.1118 + // If the available vertical height has changed, we need to reflow 1.1119 + // even if the frame isn't dirty. 1.1120 + if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) { 1.1121 + // Get prev-in-flow 1.1122 + nsIFrame* prevInFlow = frame->GetPrevInFlow(); 1.1123 + NS_ASSERTION(prevInFlow, 1.1124 + "overflow container frame must have a prev-in-flow"); 1.1125 + NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER, 1.1126 + "overflow container frame must have overflow container bit set"); 1.1127 + nsRect prevRect = prevInFlow->GetRect(); 1.1128 + 1.1129 + // Initialize reflow params 1.1130 + nsSize availSpace(prevRect.width, aReflowState.AvailableHeight()); 1.1131 + nsHTMLReflowMetrics desiredSize(aReflowState); 1.1132 + nsHTMLReflowState frameState(aPresContext, aReflowState, 1.1133 + frame, availSpace); 1.1134 + nsReflowStatus frameStatus; 1.1135 + 1.1136 + // Reflow 1.1137 + rv = ReflowChild(frame, aPresContext, desiredSize, frameState, 1.1138 + prevRect.x, 0, aFlags, frameStatus, &tracker); 1.1139 + NS_ENSURE_SUCCESS(rv, rv); 1.1140 + //XXXfr Do we need to override any shrinkwrap effects here? 1.1141 + // e.g. desiredSize.Width() = prevRect.width; 1.1142 + rv = FinishReflowChild(frame, aPresContext, desiredSize, &frameState, 1.1143 + prevRect.x, 0, aFlags); 1.1144 + NS_ENSURE_SUCCESS(rv, rv); 1.1145 + 1.1146 + // Handle continuations 1.1147 + if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) { 1.1148 + if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { 1.1149 + // Abspos frames can't cause their parent to be incomplete, 1.1150 + // only overflow incomplete. 1.1151 + NS_FRAME_SET_OVERFLOW_INCOMPLETE(frameStatus); 1.1152 + } 1.1153 + else { 1.1154 + NS_ASSERTION(NS_FRAME_IS_COMPLETE(frameStatus), 1.1155 + "overflow container frames can't be incomplete, only overflow-incomplete"); 1.1156 + } 1.1157 + 1.1158 + // Acquire a next-in-flow, creating it if necessary 1.1159 + nsIFrame* nif = frame->GetNextInFlow(); 1.1160 + if (!nif) { 1.1161 + NS_ASSERTION(frameStatus & NS_FRAME_REFLOW_NEXTINFLOW, 1.1162 + "Someone forgot a REFLOW_NEXTINFLOW flag"); 1.1163 + nif = aPresContext->PresShell()->FrameConstructor()-> 1.1164 + CreateContinuingFrame(aPresContext, frame, this); 1.1165 + } 1.1166 + else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { 1.1167 + // used to be a normal next-in-flow; steal it from the child list 1.1168 + rv = static_cast<nsContainerFrame*>(nif->GetParent()) 1.1169 + ->StealFrame(nif); 1.1170 + NS_ENSURE_SUCCESS(rv, rv); 1.1171 + } 1.1172 + 1.1173 + tracker.Insert(nif, frameStatus); 1.1174 + } 1.1175 + NS_MergeReflowStatusInto(&aStatus, frameStatus); 1.1176 + // At this point it would be nice to assert !frame->GetOverflowRect().IsEmpty(), 1.1177 + // but we have some unsplittable frames that, when taller than 1.1178 + // availableHeight will push zero-height content into a next-in-flow. 1.1179 + } 1.1180 + else { 1.1181 + tracker.Skip(frame, aStatus); 1.1182 + if (aReflowState.mFloatManager) 1.1183 + nsBlockFrame::RecoverFloatsFor(frame, *aReflowState.mFloatManager); 1.1184 + } 1.1185 + ConsiderChildOverflow(aOverflowRects, frame); 1.1186 + } 1.1187 + 1.1188 + return NS_OK; 1.1189 +} 1.1190 + 1.1191 +void 1.1192 +nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder* aBuilder, 1.1193 + const nsRect& aDirtyRect, 1.1194 + const nsDisplayListSet& aLists) 1.1195 +{ 1.1196 + nsFrameList* overflowconts = GetPropTableFrames(OverflowContainersProperty()); 1.1197 + if (overflowconts) { 1.1198 + for (nsIFrame* frame = overflowconts->FirstChild(); frame; 1.1199 + frame = frame->GetNextSibling()) { 1.1200 + BuildDisplayListForChild(aBuilder, frame, aDirtyRect, aLists); 1.1201 + } 1.1202 + } 1.1203 +} 1.1204 + 1.1205 +static bool 1.1206 +TryRemoveFrame(nsIFrame* aFrame, FramePropertyTable* aPropTable, 1.1207 + const FramePropertyDescriptor* aProp, nsIFrame* aChildToRemove) 1.1208 +{ 1.1209 + nsFrameList* list = static_cast<nsFrameList*>(aPropTable->Get(aFrame, aProp)); 1.1210 + if (list && list->StartRemoveFrame(aChildToRemove)) { 1.1211 + // aChildToRemove *may* have been removed from this list. 1.1212 + if (list->IsEmpty()) { 1.1213 + aPropTable->Remove(aFrame, aProp); 1.1214 + list->Delete(aFrame->PresContext()->PresShell()); 1.1215 + } 1.1216 + return true; 1.1217 + } 1.1218 + return false; 1.1219 +} 1.1220 + 1.1221 +nsresult 1.1222 +nsContainerFrame::StealFrame(nsIFrame* aChild, 1.1223 + bool aForceNormal) 1.1224 +{ 1.1225 +#ifdef DEBUG 1.1226 + if (!mFrames.ContainsFrame(aChild)) { 1.1227 + nsFrameList* list = GetOverflowFrames(); 1.1228 + if (!list || !list->ContainsFrame(aChild)) { 1.1229 + FramePropertyTable* propTable = PresContext()->PropertyTable(); 1.1230 + list = static_cast<nsFrameList*>( 1.1231 + propTable->Get(this, OverflowContainersProperty())); 1.1232 + if (!list || !list->ContainsFrame(aChild)) { 1.1233 + list = static_cast<nsFrameList*>( 1.1234 + propTable->Get(this, ExcessOverflowContainersProperty())); 1.1235 + MOZ_ASSERT(list && list->ContainsFrame(aChild), "aChild isn't our child" 1.1236 + " or on a frame list not supported by StealFrame"); 1.1237 + } 1.1238 + } 1.1239 + } 1.1240 +#endif 1.1241 + 1.1242 + bool removed; 1.1243 + if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) 1.1244 + && !aForceNormal) { 1.1245 + FramePropertyTable* propTable = PresContext()->PropertyTable(); 1.1246 + // Try removing from the overflow container list. 1.1247 + removed = ::TryRemoveFrame(this, propTable, OverflowContainersProperty(), 1.1248 + aChild); 1.1249 + if (!removed) { 1.1250 + // It must be in the excess overflow container list. 1.1251 + removed = ::TryRemoveFrame(this, propTable, 1.1252 + ExcessOverflowContainersProperty(), 1.1253 + aChild); 1.1254 + } 1.1255 + } else { 1.1256 + removed = mFrames.StartRemoveFrame(aChild); 1.1257 + if (!removed) { 1.1258 + // We didn't find the child in our principal child list. 1.1259 + // Maybe it's on the overflow list? 1.1260 + nsFrameList* frameList = GetOverflowFrames(); 1.1261 + if (frameList) { 1.1262 + removed = frameList->ContinueRemoveFrame(aChild); 1.1263 + if (frameList->IsEmpty()) { 1.1264 + DestroyOverflowList(); 1.1265 + } 1.1266 + } 1.1267 + } 1.1268 + } 1.1269 + 1.1270 + NS_POSTCONDITION(removed, "StealFrame: can't find aChild"); 1.1271 + return removed ? NS_OK : NS_ERROR_UNEXPECTED; 1.1272 +} 1.1273 + 1.1274 +nsFrameList 1.1275 +nsContainerFrame::StealFramesAfter(nsIFrame* aChild) 1.1276 +{ 1.1277 + NS_ASSERTION(!aChild || 1.1278 + !(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER), 1.1279 + "StealFramesAfter doesn't handle overflow containers"); 1.1280 + NS_ASSERTION(GetType() != nsGkAtoms::blockFrame, "unexpected call"); 1.1281 + 1.1282 + if (!aChild) { 1.1283 + nsFrameList copy(mFrames); 1.1284 + mFrames.Clear(); 1.1285 + return copy; 1.1286 + } 1.1287 + 1.1288 + for (nsFrameList::FrameLinkEnumerator iter(mFrames); !iter.AtEnd(); 1.1289 + iter.Next()) { 1.1290 + if (iter.PrevFrame() == aChild) { 1.1291 + return mFrames.ExtractTail(iter); 1.1292 + } 1.1293 + } 1.1294 + 1.1295 + // We didn't find the child in the principal child list. 1.1296 + // Maybe it's on the overflow list? 1.1297 + nsFrameList* overflowFrames = GetOverflowFrames(); 1.1298 + if (overflowFrames) { 1.1299 + for (nsFrameList::FrameLinkEnumerator iter(*overflowFrames); !iter.AtEnd(); 1.1300 + iter.Next()) { 1.1301 + if (iter.PrevFrame() == aChild) { 1.1302 + return overflowFrames->ExtractTail(iter); 1.1303 + } 1.1304 + } 1.1305 + } 1.1306 + 1.1307 + NS_ERROR("StealFramesAfter: can't find aChild"); 1.1308 + return nsFrameList::EmptyList(); 1.1309 +} 1.1310 + 1.1311 +/* 1.1312 + * Create a next-in-flow for aFrame. Will return the newly created 1.1313 + * frame in aNextInFlowResult <b>if and only if</b> a new frame is 1.1314 + * created; otherwise nullptr is returned in aNextInFlowResult. 1.1315 + */ 1.1316 +nsresult 1.1317 +nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame, 1.1318 + nsIFrame*& aNextInFlowResult) 1.1319 +{ 1.1320 + NS_PRECONDITION(GetType() != nsGkAtoms::blockFrame, 1.1321 + "you should have called nsBlockFrame::CreateContinuationFor instead"); 1.1322 + NS_PRECONDITION(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame"); 1.1323 + 1.1324 + nsPresContext* pc = PresContext(); 1.1325 + aNextInFlowResult = nullptr; 1.1326 + 1.1327 + nsIFrame* nextInFlow = aFrame->GetNextInFlow(); 1.1328 + if (nullptr == nextInFlow) { 1.1329 + // Create a continuation frame for the child frame and insert it 1.1330 + // into our child list. 1.1331 + nextInFlow = pc->PresShell()->FrameConstructor()-> 1.1332 + CreateContinuingFrame(pc, aFrame, this); 1.1333 + mFrames.InsertFrame(nullptr, aFrame, nextInFlow); 1.1334 + 1.1335 + NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES, 1.1336 + ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p", 1.1337 + aFrame, nextInFlow)); 1.1338 + 1.1339 + aNextInFlowResult = nextInFlow; 1.1340 + } 1.1341 + return NS_OK; 1.1342 +} 1.1343 + 1.1344 +/** 1.1345 + * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow 1.1346 + * pointers 1.1347 + */ 1.1348 +void 1.1349 +nsContainerFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow, 1.1350 + bool aDeletingEmptyFrames) 1.1351 +{ 1.1352 +#ifdef DEBUG 1.1353 + nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow(); 1.1354 +#endif 1.1355 + NS_PRECONDITION(prevInFlow, "bad prev-in-flow"); 1.1356 + 1.1357 + // If the next-in-flow has a next-in-flow then delete it, too (and 1.1358 + // delete it first). 1.1359 + // Do this in a loop so we don't overflow the stack for frames 1.1360 + // with very many next-in-flows 1.1361 + nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow(); 1.1362 + if (nextNextInFlow) { 1.1363 + nsAutoTArray<nsIFrame*, 8> frames; 1.1364 + for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) { 1.1365 + frames.AppendElement(f); 1.1366 + } 1.1367 + for (int32_t i = frames.Length() - 1; i >= 0; --i) { 1.1368 + nsIFrame* delFrame = frames.ElementAt(i); 1.1369 + static_cast<nsContainerFrame*>(delFrame->GetParent()) 1.1370 + ->DeleteNextInFlowChild(delFrame, aDeletingEmptyFrames); 1.1371 + } 1.1372 + } 1.1373 + 1.1374 + // Take the next-in-flow out of the parent's child list 1.1375 + DebugOnly<nsresult> rv = StealFrame(aNextInFlow); 1.1376 + NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure"); 1.1377 + 1.1378 +#ifdef DEBUG 1.1379 + if (aDeletingEmptyFrames) { 1.1380 + nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow); 1.1381 + } 1.1382 +#endif 1.1383 + 1.1384 + // Delete the next-in-flow frame and its descendants. This will also 1.1385 + // remove it from its next-in-flow/prev-in-flow chain. 1.1386 + aNextInFlow->Destroy(); 1.1387 + 1.1388 + NS_POSTCONDITION(!prevInFlow->GetNextInFlow(), "non null next-in-flow"); 1.1389 +} 1.1390 + 1.1391 +/** 1.1392 + * Set the frames on the overflow list 1.1393 + */ 1.1394 +void 1.1395 +nsContainerFrame::SetOverflowFrames(const nsFrameList& aOverflowFrames) 1.1396 +{ 1.1397 + NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called"); 1.1398 + 1.1399 + nsPresContext* pc = PresContext(); 1.1400 + nsFrameList* newList = new (pc->PresShell()) nsFrameList(aOverflowFrames); 1.1401 + 1.1402 + pc->PropertyTable()->Set(this, OverflowProperty(), newList); 1.1403 +} 1.1404 + 1.1405 +nsFrameList* 1.1406 +nsContainerFrame::GetPropTableFrames(const FramePropertyDescriptor* aProperty) const 1.1407 +{ 1.1408 + FramePropertyTable* propTable = PresContext()->PropertyTable(); 1.1409 + return static_cast<nsFrameList*>(propTable->Get(this, aProperty)); 1.1410 +} 1.1411 + 1.1412 +nsFrameList* 1.1413 +nsContainerFrame::RemovePropTableFrames(const FramePropertyDescriptor* aProperty) 1.1414 +{ 1.1415 + FramePropertyTable* propTable = PresContext()->PropertyTable(); 1.1416 + return static_cast<nsFrameList*>(propTable->Remove(this, aProperty)); 1.1417 +} 1.1418 + 1.1419 +void 1.1420 +nsContainerFrame::SetPropTableFrames(nsFrameList* aFrameList, 1.1421 + const FramePropertyDescriptor* aProperty) 1.1422 +{ 1.1423 + NS_PRECONDITION(aProperty && aFrameList, "null ptr"); 1.1424 + NS_PRECONDITION( 1.1425 + (aProperty != nsContainerFrame::OverflowContainersProperty() && 1.1426 + aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) || 1.1427 + IsFrameOfType(nsIFrame::eCanContainOverflowContainers), 1.1428 + "this type of frame can't have overflow containers"); 1.1429 + MOZ_ASSERT(!GetPropTableFrames(aProperty)); 1.1430 + PresContext()->PropertyTable()->Set(this, aProperty, aFrameList); 1.1431 +} 1.1432 + 1.1433 +/** 1.1434 + * Push aFromChild and its next siblings to the next-in-flow. Change the 1.1435 + * geometric parent of each frame that's pushed. If there is no next-in-flow 1.1436 + * the frames are placed on the overflow list (and the geometric parent is 1.1437 + * left unchanged). 1.1438 + * 1.1439 + * Updates the next-in-flow's child count. Does <b>not</b> update the 1.1440 + * pusher's child count. 1.1441 + * 1.1442 + * @param aFromChild the first child frame to push. It is disconnected from 1.1443 + * aPrevSibling 1.1444 + * @param aPrevSibling aFromChild's previous sibling. Must not be null. It's 1.1445 + * an error to push a parent's first child frame 1.1446 + */ 1.1447 +void 1.1448 +nsContainerFrame::PushChildren(nsIFrame* aFromChild, 1.1449 + nsIFrame* aPrevSibling) 1.1450 +{ 1.1451 + NS_PRECONDITION(aFromChild, "null pointer"); 1.1452 + NS_PRECONDITION(aPrevSibling, "pushing first child"); 1.1453 + NS_PRECONDITION(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling"); 1.1454 + 1.1455 + // Disconnect aFromChild from its previous sibling 1.1456 + nsFrameList tail = mFrames.RemoveFramesAfter(aPrevSibling); 1.1457 + 1.1458 + nsContainerFrame* nextInFlow = 1.1459 + static_cast<nsContainerFrame*>(GetNextInFlow()); 1.1460 + if (nextInFlow) { 1.1461 + // XXX This is not a very good thing to do. If it gets removed 1.1462 + // then remove the copy of this routine that doesn't do this from 1.1463 + // nsInlineFrame. 1.1464 + // When pushing and pulling frames we need to check for whether any 1.1465 + // views need to be reparented. 1.1466 + for (nsIFrame* f = aFromChild; f; f = f->GetNextSibling()) { 1.1467 + nsContainerFrame::ReparentFrameView(f, this, nextInFlow); 1.1468 + } 1.1469 + nextInFlow->mFrames.InsertFrames(nextInFlow, nullptr, tail); 1.1470 + } 1.1471 + else { 1.1472 + // Add the frames to our overflow list 1.1473 + SetOverflowFrames(tail); 1.1474 + } 1.1475 +} 1.1476 + 1.1477 +/** 1.1478 + * Moves any frames on the overflow lists (the prev-in-flow's overflow list and 1.1479 + * the receiver's overflow list) to the child list. 1.1480 + * 1.1481 + * Updates this frame's child count and content mapping. 1.1482 + * 1.1483 + * @return true if any frames were moved and false otherwise 1.1484 + */ 1.1485 +bool 1.1486 +nsContainerFrame::MoveOverflowToChildList() 1.1487 +{ 1.1488 + bool result = false; 1.1489 + 1.1490 + // Check for an overflow list with our prev-in-flow 1.1491 + nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow(); 1.1492 + if (nullptr != prevInFlow) { 1.1493 + AutoFrameListPtr prevOverflowFrames(PresContext(), 1.1494 + prevInFlow->StealOverflowFrames()); 1.1495 + if (prevOverflowFrames) { 1.1496 + // Tables are special; they can have repeated header/footer 1.1497 + // frames on mFrames at this point. 1.1498 + NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame, 1.1499 + "bad overflow list"); 1.1500 + // When pushing and pulling frames we need to check for whether any 1.1501 + // views need to be reparented. 1.1502 + nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, 1.1503 + prevInFlow, this); 1.1504 + mFrames.AppendFrames(this, *prevOverflowFrames); 1.1505 + result = true; 1.1506 + } 1.1507 + } 1.1508 + 1.1509 + // It's also possible that we have an overflow list for ourselves. 1.1510 + return DrainSelfOverflowList() || result; 1.1511 +} 1.1512 + 1.1513 +bool 1.1514 +nsContainerFrame::DrainSelfOverflowList() 1.1515 +{ 1.1516 + AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames()); 1.1517 + if (overflowFrames) { 1.1518 + NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames"); 1.1519 + mFrames.AppendFrames(nullptr, *overflowFrames); 1.1520 + return true; 1.1521 + } 1.1522 + return false; 1.1523 +} 1.1524 + 1.1525 +nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsContainerFrame* aFrame, 1.1526 + bool aWalkOOFFrames, 1.1527 + bool aSkipOverflowContainerChildren) 1.1528 + : mOverflowContList(nullptr), 1.1529 + mPrevOverflowCont(nullptr), 1.1530 + mSentry(nullptr), 1.1531 + mParent(aFrame), 1.1532 + mSkipOverflowContainerChildren(aSkipOverflowContainerChildren), 1.1533 + mWalkOOFFrames(aWalkOOFFrames) 1.1534 +{ 1.1535 + NS_PRECONDITION(aFrame, "null frame pointer"); 1.1536 + SetupOverflowContList(); 1.1537 +} 1.1538 + 1.1539 +void 1.1540 +nsOverflowContinuationTracker::SetupOverflowContList() 1.1541 +{ 1.1542 + NS_PRECONDITION(mParent, "null frame pointer"); 1.1543 + NS_PRECONDITION(!mOverflowContList, "already have list"); 1.1544 + nsContainerFrame* nif = 1.1545 + static_cast<nsContainerFrame*>(mParent->GetNextInFlow()); 1.1546 + if (nif) { 1.1547 + mOverflowContList = nif->GetPropTableFrames( 1.1548 + nsContainerFrame::OverflowContainersProperty()); 1.1549 + if (mOverflowContList) { 1.1550 + mParent = nif; 1.1551 + SetUpListWalker(); 1.1552 + } 1.1553 + } 1.1554 + if (!mOverflowContList) { 1.1555 + mOverflowContList = mParent->GetPropTableFrames( 1.1556 + nsContainerFrame::ExcessOverflowContainersProperty()); 1.1557 + if (mOverflowContList) { 1.1558 + SetUpListWalker(); 1.1559 + } 1.1560 + } 1.1561 +} 1.1562 + 1.1563 +/** 1.1564 + * Helper function to walk past overflow continuations whose prev-in-flow 1.1565 + * isn't a normal child and to set mSentry and mPrevOverflowCont correctly. 1.1566 + */ 1.1567 +void 1.1568 +nsOverflowContinuationTracker::SetUpListWalker() 1.1569 +{ 1.1570 + NS_ASSERTION(!mSentry && !mPrevOverflowCont, 1.1571 + "forgot to reset mSentry or mPrevOverflowCont"); 1.1572 + if (mOverflowContList) { 1.1573 + nsIFrame* cur = mOverflowContList->FirstChild(); 1.1574 + if (mSkipOverflowContainerChildren) { 1.1575 + while (cur && (cur->GetPrevInFlow()->GetStateBits() 1.1576 + & NS_FRAME_IS_OVERFLOW_CONTAINER)) { 1.1577 + mPrevOverflowCont = cur; 1.1578 + cur = cur->GetNextSibling(); 1.1579 + } 1.1580 + while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW) 1.1581 + == mWalkOOFFrames)) { 1.1582 + mPrevOverflowCont = cur; 1.1583 + cur = cur->GetNextSibling(); 1.1584 + } 1.1585 + } 1.1586 + if (cur) { 1.1587 + mSentry = cur->GetPrevInFlow(); 1.1588 + } 1.1589 + } 1.1590 +} 1.1591 + 1.1592 +/** 1.1593 + * Helper function to step forward through the overflow continuations list. 1.1594 + * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames 1.1595 + * as appropriate. May only be called when we have already set up an 1.1596 + * mOverflowContList; mOverflowContList cannot be null. 1.1597 + */ 1.1598 +void 1.1599 +nsOverflowContinuationTracker::StepForward() 1.1600 +{ 1.1601 + NS_PRECONDITION(mOverflowContList, "null list"); 1.1602 + 1.1603 + // Step forward 1.1604 + if (mPrevOverflowCont) { 1.1605 + mPrevOverflowCont = mPrevOverflowCont->GetNextSibling(); 1.1606 + } 1.1607 + else { 1.1608 + mPrevOverflowCont = mOverflowContList->FirstChild(); 1.1609 + } 1.1610 + 1.1611 + // Skip over oof or non-oof frames as appropriate 1.1612 + if (mSkipOverflowContainerChildren) { 1.1613 + nsIFrame* cur = mPrevOverflowCont->GetNextSibling(); 1.1614 + while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW) 1.1615 + == mWalkOOFFrames)) { 1.1616 + mPrevOverflowCont = cur; 1.1617 + cur = cur->GetNextSibling(); 1.1618 + } 1.1619 + } 1.1620 + 1.1621 + // Set up the sentry 1.1622 + mSentry = (mPrevOverflowCont->GetNextSibling()) 1.1623 + ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow() 1.1624 + : nullptr; 1.1625 +} 1.1626 + 1.1627 +nsresult 1.1628 +nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont, 1.1629 + nsReflowStatus& aReflowStatus) 1.1630 +{ 1.1631 + NS_PRECONDITION(aOverflowCont, "null frame pointer"); 1.1632 + NS_PRECONDITION(!mSkipOverflowContainerChildren || mWalkOOFFrames == 1.1633 + !!(aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW), 1.1634 + "shouldn't insert frame that doesn't match walker type"); 1.1635 + NS_PRECONDITION(aOverflowCont->GetPrevInFlow(), 1.1636 + "overflow containers must have a prev-in-flow"); 1.1637 + nsresult rv = NS_OK; 1.1638 + bool reparented = false; 1.1639 + nsPresContext* presContext = aOverflowCont->PresContext(); 1.1640 + bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow(); 1.1641 + 1.1642 + // If we have a list and aOverflowCont is already in it then don't try to 1.1643 + // add it again. 1.1644 + if (addToList && aOverflowCont->GetParent() == mParent && 1.1645 + (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) && 1.1646 + mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) { 1.1647 + addToList = false; 1.1648 + mPrevOverflowCont = aOverflowCont->GetPrevSibling(); 1.1649 + } 1.1650 + 1.1651 + if (addToList) { 1.1652 + if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { 1.1653 + // aOverflowCont is in some other overflow container list, 1.1654 + // steal it first 1.1655 + NS_ASSERTION(!(mOverflowContList && 1.1656 + mOverflowContList->ContainsFrame(aOverflowCont)), 1.1657 + "overflow containers out of order"); 1.1658 + rv = static_cast<nsContainerFrame*>(aOverflowCont->GetParent()) 1.1659 + ->StealFrame(aOverflowCont); 1.1660 + NS_ENSURE_SUCCESS(rv, rv); 1.1661 + } 1.1662 + else { 1.1663 + aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); 1.1664 + } 1.1665 + if (!mOverflowContList) { 1.1666 + mOverflowContList = new (presContext->PresShell()) nsFrameList(); 1.1667 + mParent->SetPropTableFrames(mOverflowContList, 1.1668 + nsContainerFrame::ExcessOverflowContainersProperty()); 1.1669 + SetUpListWalker(); 1.1670 + } 1.1671 + if (aOverflowCont->GetParent() != mParent) { 1.1672 + nsContainerFrame::ReparentFrameView(aOverflowCont, 1.1673 + aOverflowCont->GetParent(), 1.1674 + mParent); 1.1675 + reparented = true; 1.1676 + } 1.1677 + 1.1678 + // If aOverflowCont has a prev/next-in-flow that might be in 1.1679 + // mOverflowContList we need to find it and insert after/before it to 1.1680 + // maintain the order amongst next-in-flows in this list. 1.1681 + nsIFrame* pif = aOverflowCont->GetPrevInFlow(); 1.1682 + nsIFrame* nif = aOverflowCont->GetNextInFlow(); 1.1683 + if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) || 1.1684 + (nif && nif->GetParent() == mParent && mPrevOverflowCont)) { 1.1685 + for (nsFrameList::Enumerator e(*mOverflowContList); !e.AtEnd(); e.Next()) { 1.1686 + nsIFrame* f = e.get(); 1.1687 + if (f == pif) { 1.1688 + mPrevOverflowCont = pif; 1.1689 + break; 1.1690 + } 1.1691 + if (f == nif) { 1.1692 + mPrevOverflowCont = f->GetPrevSibling(); 1.1693 + break; 1.1694 + } 1.1695 + } 1.1696 + } 1.1697 + 1.1698 + mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont); 1.1699 + aReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.1700 + } 1.1701 + 1.1702 + // If we need to reflow it, mark it dirty 1.1703 + if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) 1.1704 + aOverflowCont->AddStateBits(NS_FRAME_IS_DIRTY); 1.1705 + 1.1706 + // It's in our list, just step forward 1.1707 + StepForward(); 1.1708 + NS_ASSERTION(mPrevOverflowCont == aOverflowCont || 1.1709 + (mSkipOverflowContainerChildren && 1.1710 + (mPrevOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW) != 1.1711 + (aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW)), 1.1712 + "OverflowContTracker in unexpected state"); 1.1713 + 1.1714 + if (addToList) { 1.1715 + // Convert all non-overflow-container continuations of aOverflowCont 1.1716 + // into overflow containers and move them to our overflow 1.1717 + // tracker. This preserves the invariant that the next-continuations 1.1718 + // of an overflow container are also overflow containers. 1.1719 + nsIFrame* f = aOverflowCont->GetNextContinuation(); 1.1720 + if (f && (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) || 1.1721 + (!reparented && f->GetParent() == mParent) || 1.1722 + (reparented && f->GetParent() != mParent))) { 1.1723 + if (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { 1.1724 + nsContainerFrame* parent = static_cast<nsContainerFrame*>(f->GetParent()); 1.1725 + rv = parent->StealFrame(f); 1.1726 + NS_ENSURE_SUCCESS(rv, rv); 1.1727 + } 1.1728 + Insert(f, aReflowStatus); 1.1729 + } 1.1730 + } 1.1731 + return rv; 1.1732 +} 1.1733 + 1.1734 +void 1.1735 +nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild) 1.1736 +{ 1.1737 + NS_PRECONDITION(aChild, "null ptr"); 1.1738 + NS_PRECONDITION(aChild->GetNextInFlow(), 1.1739 + "supposed to call Finish *before* deleting next-in-flow!"); 1.1740 + for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) { 1.1741 + // We'll update these in EndFinish after the next-in-flows are gone. 1.1742 + if (f == mPrevOverflowCont) { 1.1743 + mSentry = nullptr; 1.1744 + mPrevOverflowCont = nullptr; 1.1745 + break; 1.1746 + } 1.1747 + if (f == mSentry) { 1.1748 + mSentry = nullptr; 1.1749 + break; 1.1750 + } 1.1751 + } 1.1752 +} 1.1753 + 1.1754 +void 1.1755 +nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild) 1.1756 +{ 1.1757 + if (!mOverflowContList) { 1.1758 + return; 1.1759 + } 1.1760 + // Forget mOverflowContList if it was deleted. 1.1761 + nsPresContext* pc = aChild->PresContext(); 1.1762 + FramePropertyTable* propTable = pc->PropertyTable(); 1.1763 + nsFrameList* eoc = static_cast<nsFrameList*>(propTable->Get(mParent, 1.1764 + nsContainerFrame::ExcessOverflowContainersProperty())); 1.1765 + if (eoc != mOverflowContList) { 1.1766 + nsFrameList* oc = static_cast<nsFrameList*>(propTable->Get(mParent, 1.1767 + nsContainerFrame::OverflowContainersProperty())); 1.1768 + if (oc != mOverflowContList) { 1.1769 + // mOverflowContList was deleted 1.1770 + mPrevOverflowCont = nullptr; 1.1771 + mSentry = nullptr; 1.1772 + mParent = static_cast<nsContainerFrame*>(aChild->GetParent()); 1.1773 + mOverflowContList = nullptr; 1.1774 + SetupOverflowContList(); 1.1775 + return; 1.1776 + } 1.1777 + } 1.1778 + // The list survived, update mSentry if needed. 1.1779 + if (!mSentry) { 1.1780 + if (!mPrevOverflowCont) { 1.1781 + SetUpListWalker(); 1.1782 + } else { 1.1783 + mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont); 1.1784 + // step backward to make StepForward() use our current mPrevOverflowCont 1.1785 + mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling(); 1.1786 + StepForward(); 1.1787 + } 1.1788 + } 1.1789 +} 1.1790 + 1.1791 +///////////////////////////////////////////////////////////////////////////// 1.1792 +// Debugging 1.1793 + 1.1794 +#ifdef DEBUG_FRAME_DUMP 1.1795 +void 1.1796 +nsContainerFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const 1.1797 +{ 1.1798 + nsCString str; 1.1799 + ListGeneric(str, aPrefix, aFlags); 1.1800 + 1.1801 + // Output the children 1.1802 + bool outputOneList = false; 1.1803 + ChildListIterator lists(this); 1.1804 + for (; !lists.IsDone(); lists.Next()) { 1.1805 + if (outputOneList) { 1.1806 + str += aPrefix; 1.1807 + } 1.1808 + if (lists.CurrentID() != kPrincipalList) { 1.1809 + if (!outputOneList) { 1.1810 + str += "\n"; 1.1811 + str += aPrefix; 1.1812 + } 1.1813 + str += nsPrintfCString("%s %p ", mozilla::layout::ChildListName(lists.CurrentID()), 1.1814 + &GetChildList(lists.CurrentID())); 1.1815 + } 1.1816 + fprintf_stderr(out, "%s<\n", str.get()); 1.1817 + str = ""; 1.1818 + nsFrameList::Enumerator childFrames(lists.CurrentList()); 1.1819 + for (; !childFrames.AtEnd(); childFrames.Next()) { 1.1820 + nsIFrame* kid = childFrames.get(); 1.1821 + // Verify the child frame's parent frame pointer is correct 1.1822 + NS_ASSERTION(kid->GetParent() == this, "bad parent frame pointer"); 1.1823 + 1.1824 + // Have the child frame list 1.1825 + nsCString pfx(aPrefix); 1.1826 + pfx += " "; 1.1827 + kid->List(out, pfx.get(), aFlags); 1.1828 + } 1.1829 + fprintf_stderr(out, "%s>\n", aPrefix); 1.1830 + outputOneList = true; 1.1831 + } 1.1832 + 1.1833 + if (!outputOneList) { 1.1834 + fprintf_stderr(out, "%s<>\n", str.get()); 1.1835 + } 1.1836 +} 1.1837 +#endif