layout/generic/nsContainerFrame.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial