nsCSSFrameConstructor.cpp (468395B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * construction of a frame tree that is nearly isomorphic to the content 9 * tree and updating of that tree in response to dynamic changes 10 */ 11 12 #include "nsCSSFrameConstructor.h" 13 14 #include "ActiveLayerTracker.h" 15 #include "ChildIterator.h" 16 #include "RetainedDisplayListBuilder.h" 17 #include "RubyUtils.h" 18 #include "StickyScrollContainer.h" 19 #include "mozilla/AbsoluteContainingBlock.h" 20 #include "mozilla/Assertions.h" 21 #include "mozilla/AutoRestore.h" 22 #include "mozilla/ComputedStyleInlines.h" 23 #include "mozilla/DebugOnly.h" 24 #include "mozilla/ErrorResult.h" 25 #include "mozilla/Likely.h" 26 #include "mozilla/ManualNAC.h" 27 #include "mozilla/PresShell.h" 28 #include "mozilla/PresShellInlines.h" 29 #include "mozilla/PrintedSheetFrame.h" 30 #include "mozilla/ProfilerLabels.h" 31 #include "mozilla/ProfilerMarkers.h" 32 #include "mozilla/RestyleManager.h" 33 #include "mozilla/SVGGradientFrame.h" 34 #include "mozilla/ScopeExit.h" 35 #include "mozilla/ScrollContainerFrame.h" 36 #include "mozilla/ServoBindings.h" 37 #include "mozilla/ServoStyleSetInlines.h" 38 #include "mozilla/StaticPrefs_browser.h" 39 #include "mozilla/StaticPrefs_layout.h" 40 #include "mozilla/StaticPrefs_mathml.h" 41 #include "mozilla/dom/BindContext.h" 42 #include "mozilla/dom/BrowsingContext.h" 43 #include "mozilla/dom/CharacterData.h" 44 #include "mozilla/dom/CharacterDataBuffer.h" 45 #include "mozilla/dom/Document.h" 46 #include "mozilla/dom/DocumentInlines.h" 47 #include "mozilla/dom/Element.h" 48 #include "mozilla/dom/ElementInlines.h" 49 #include "mozilla/dom/GeneratedImageContent.h" 50 #include "mozilla/dom/HTMLInputElement.h" 51 #include "mozilla/dom/HTMLSelectElement.h" 52 #include "mozilla/dom/HTMLSharedListElement.h" 53 #include "mozilla/dom/HTMLSummaryElement.h" 54 #include "mozilla/intl/LocaleService.h" 55 #include "nsAtom.h" 56 #include "nsAutoLayoutPhase.h" 57 #include "nsBlockFrame.h" 58 #include "nsCRT.h" 59 #include "nsCSSAnonBoxes.h" 60 #include "nsCSSPseudoElements.h" 61 #include "nsCanvasFrame.h" 62 #include "nsCheckboxRadioFrame.h" 63 #include "nsComboboxControlFrame.h" 64 #include "nsContainerFrame.h" 65 #include "nsContentUtils.h" 66 #include "nsError.h" 67 #include "nsFieldSetFrame.h" 68 #include "nsFirstLetterFrame.h" 69 #include "nsFlexContainerFrame.h" 70 #include "nsGkAtoms.h" 71 #include "nsGridContainerFrame.h" 72 #include "nsHTMLParts.h" 73 #include "nsIAnonymousContentCreator.h" 74 #include "nsIFormControl.h" 75 #include "nsIFrameInlines.h" 76 #include "nsIObjectLoadingContent.h" 77 #include "nsIPopupContainer.h" 78 #include "nsIScriptError.h" 79 #include "nsImageFrame.h" 80 #include "nsInlineFrame.h" 81 #include "nsLayoutUtils.h" 82 #include "nsListControlFrame.h" 83 #include "nsMathMLParts.h" 84 #include "nsNameSpaceManager.h" 85 #include "nsPageContentFrame.h" 86 #include "nsPageFrame.h" 87 #include "nsPageSequenceFrame.h" 88 #include "nsPlaceholderFrame.h" 89 #include "nsPresContext.h" 90 #include "nsRefreshDriver.h" 91 #include "nsRubyBaseContainerFrame.h" 92 #include "nsRubyBaseFrame.h" 93 #include "nsRubyFrame.h" 94 #include "nsRubyTextContainerFrame.h" 95 #include "nsRubyTextFrame.h" 96 #include "nsStyleConsts.h" 97 #include "nsStyleStructInlines.h" 98 #include "nsTArray.h" 99 #include "nsTableCellFrame.h" 100 #include "nsTableColFrame.h" 101 #include "nsTableFrame.h" 102 #include "nsTableRowFrame.h" 103 #include "nsTableRowGroupFrame.h" 104 #include "nsTableWrapperFrame.h" 105 #include "nsTextNode.h" 106 #include "nsTransitionManager.h" 107 #include "nsUnicharUtils.h" 108 #include "nsXULElement.h" 109 110 #ifdef XP_MACOSX 111 # include "nsIDocShell.h" 112 #endif 113 114 #ifdef ACCESSIBILITY 115 # include "nsAccessibilityService.h" 116 #endif 117 118 #undef NOISY_FIRST_LETTER 119 120 using namespace mozilla; 121 using namespace mozilla::dom; 122 123 nsIFrame* NS_NewHTMLCanvasFrame(PresShell* aPresShell, ComputedStyle* aStyle); 124 125 nsIFrame* NS_NewHTMLVideoFrame(PresShell* aPresShell, ComputedStyle* aStyle); 126 nsIFrame* NS_NewHTMLAudioFrame(PresShell* aPresShell, ComputedStyle* aStyle); 127 128 nsContainerFrame* NS_NewSVGOuterSVGFrame(PresShell* aPresShell, 129 ComputedStyle* aStyle); 130 nsContainerFrame* NS_NewSVGOuterSVGAnonChildFrame(PresShell* aPresShell, 131 ComputedStyle* aStyle); 132 nsIFrame* NS_NewSVGInnerSVGFrame(PresShell* aPresShell, ComputedStyle* aStyle); 133 nsIFrame* NS_NewSVGGeometryFrame(PresShell* aPresShell, ComputedStyle* aStyle); 134 nsIFrame* NS_NewSVGGFrame(PresShell* aPresShell, ComputedStyle* aStyle); 135 nsContainerFrame* NS_NewSVGForeignObjectFrame(PresShell* aPresShell, 136 ComputedStyle* aStyle); 137 nsIFrame* NS_NewSVGAFrame(PresShell* aPresShell, ComputedStyle* aStyle); 138 nsIFrame* NS_NewSVGSwitchFrame(PresShell* aPresShell, ComputedStyle* aStyle); 139 nsIFrame* NS_NewSVGSymbolFrame(PresShell* aPresShell, ComputedStyle* aStyle); 140 nsIFrame* NS_NewSVGTextFrame(PresShell* aPresShell, ComputedStyle* aStyle); 141 nsIFrame* NS_NewSVGContainerFrame(PresShell* aPresShell, ComputedStyle* aStyle); 142 nsIFrame* NS_NewSVGUseFrame(PresShell* aPresShell, ComputedStyle* aStyle); 143 nsIFrame* NS_NewSVGViewFrame(PresShell* aPresShell, ComputedStyle* aStyle); 144 extern nsIFrame* NS_NewSVGLinearGradientFrame(PresShell* aPresShell, 145 ComputedStyle* aStyle); 146 extern nsIFrame* NS_NewSVGRadialGradientFrame(PresShell* aPresShell, 147 ComputedStyle* aStyle); 148 extern nsIFrame* NS_NewSVGStopFrame(PresShell* aPresShell, 149 ComputedStyle* aStyle); 150 nsContainerFrame* NS_NewSVGMarkerFrame(PresShell* aPresShell, 151 ComputedStyle* aStyle); 152 nsContainerFrame* NS_NewSVGMarkerAnonChildFrame(PresShell* aPresShell, 153 ComputedStyle* aStyle); 154 extern nsIFrame* NS_NewSVGImageFrame(PresShell* aPresShell, 155 ComputedStyle* aStyle); 156 nsIFrame* NS_NewSVGClipPathFrame(PresShell* aPresShell, ComputedStyle* aStyle); 157 nsIFrame* NS_NewSVGFilterFrame(PresShell* aPresShell, ComputedStyle* aStyle); 158 nsIFrame* NS_NewSVGPatternFrame(PresShell* aPresShell, ComputedStyle* aStyle); 159 nsIFrame* NS_NewSVGMaskFrame(PresShell* aPresShell, ComputedStyle* aStyle); 160 nsIFrame* NS_NewSVGFEContainerFrame(PresShell* aPresShell, 161 ComputedStyle* aStyle); 162 nsIFrame* NS_NewSVGFELeafFrame(PresShell* aPresShell, ComputedStyle* aStyle); 163 nsIFrame* NS_NewSVGFEImageFrame(PresShell* aPresShell, ComputedStyle* aStyle); 164 nsIFrame* NS_NewSVGFEUnstyledLeafFrame(PresShell* aPresShell, 165 ComputedStyle* aStyle); 166 nsIFrame* NS_NewFileControlLabelFrame(PresShell*, ComputedStyle*); 167 nsIFrame* NS_NewComboboxLabelFrame(PresShell*, ComputedStyle*); 168 nsIFrame* NS_NewMiddleCroppingLabelFrame(PresShell*, ComputedStyle*); 169 nsIFrame* NS_NewInputButtonControlFrame(PresShell*, ComputedStyle*); 170 171 #include "mozilla/dom/NodeInfo.h" 172 #include "nsContentCreatorFunctions.h" 173 #include "nsNodeInfoManager.h" 174 #include "prenv.h" 175 176 #ifdef DEBUG 177 // Set the environment variable GECKO_FRAMECTOR_DEBUG_FLAGS to one or 178 // more of the following flags (comma separated) for handy debug 179 // output. 180 static bool gNoisyContentUpdates = false; 181 static bool gReallyNoisyContentUpdates = false; 182 static bool gNoisyInlineConstruction = false; 183 184 struct FrameCtorDebugFlags { 185 const char* name; 186 bool* on; 187 }; 188 189 static FrameCtorDebugFlags gFrameCtorDebugFlags[] = { 190 {"content-updates", &gNoisyContentUpdates}, 191 {"really-noisy-content-updates", &gReallyNoisyContentUpdates}, 192 {"noisy-inline", &gNoisyInlineConstruction}}; 193 194 # define NUM_DEBUG_FLAGS (std::size(gFrameCtorDebugFlags)) 195 #endif 196 197 //------------------------------------------------------------------ 198 199 nsIFrame* NS_NewLeafBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle); 200 201 nsIFrame* NS_NewRangeFrame(PresShell* aPresShell, ComputedStyle* aStyle); 202 203 nsIFrame* NS_NewTextBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle); 204 205 nsIFrame* NS_NewSplitterFrame(PresShell* aPresShell, ComputedStyle* aStyle); 206 207 nsIFrame* NS_NewMenuPopupFrame(PresShell* aPresShell, ComputedStyle* aStyle); 208 209 nsIFrame* NS_NewTreeBodyFrame(PresShell* aPresShell, ComputedStyle* aStyle); 210 211 nsIFrame* NS_NewSliderFrame(PresShell* aPresShell, ComputedStyle* aStyle); 212 213 nsIFrame* NS_NewScrollbarFrame(PresShell* aPresShell, ComputedStyle* aStyle); 214 215 nsIFrame* NS_NewScrollbarButtonFrame(PresShell*, ComputedStyle*); 216 nsIFrame* NS_NewSimpleXULLeafFrame(PresShell*, ComputedStyle*); 217 218 nsIFrame* NS_NewXULImageFrame(PresShell*, ComputedStyle*); 219 nsIFrame* NS_NewImageFrameForContentProperty(PresShell*, ComputedStyle*); 220 nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell*, ComputedStyle*); 221 nsIFrame* NS_NewImageFrameForListStyleImage(PresShell*, ComputedStyle*); 222 nsIFrame* NS_NewImageFrameForViewTransition(PresShell*, ComputedStyle*); 223 224 // Returns true if aFrame is an anonymous flex/grid item. 225 static inline bool IsAnonymousItem(const nsIFrame* aFrame) { 226 return aFrame->Style()->GetPseudoType() == PseudoStyleType::anonymousItem; 227 } 228 229 // Returns true IFF the given nsIFrame is a nsFlexContainerFrame and represents 230 // a -webkit-{inline-}box container. 231 static inline bool IsFlexContainerForLegacyWebKitBox(const nsIFrame* aFrame) { 232 return aFrame->IsFlexContainerFrame() && aFrame->IsLegacyWebkitBox(); 233 } 234 235 #if DEBUG 236 static void AssertAnonymousFlexOrGridItemParent(const nsIFrame* aChild, 237 const nsIFrame* aParent) { 238 MOZ_ASSERT(IsAnonymousItem(aChild), "expected an anonymous item child frame"); 239 MOZ_ASSERT(aParent, "expected a parent frame"); 240 MOZ_ASSERT(aParent->IsFlexOrGridContainer(), 241 "anonymous items should only exist as children of flex/grid " 242 "container frames"); 243 } 244 #else 245 # define AssertAnonymousFlexOrGridItemParent(x, y) PR_BEGIN_MACRO PR_END_MACRO 246 #endif 247 248 #define ToCreationFunc(_func) \ 249 [](PresShell* aPs, ComputedStyle* aStyle) -> nsIFrame* { \ 250 return _func(aPs, aStyle); \ 251 } 252 253 /** 254 * True if aFrame is an actual inline frame in the sense of non-replaced 255 * display:inline CSS boxes. In other words, it can be affected by {ib} 256 * splitting and can contain first-letter frames. Basically, this is either an 257 * inline frame (positioned or otherwise) or an line frame (this last because 258 * it can contain first-letter and because inserting blocks in the middle of it 259 * needs to terminate it). 260 */ 261 static bool IsInlineFrame(const nsIFrame* aFrame) { 262 return aFrame->IsLineParticipant(); 263 } 264 265 /** 266 * True for display: contents elements. 267 */ 268 static inline bool IsDisplayContents(const Element* aElement) { 269 return aElement->IsDisplayContents(); 270 } 271 272 static inline bool IsDisplayContents(const nsIContent* aContent) { 273 return aContent->IsElement() && IsDisplayContents(aContent->AsElement()); 274 } 275 276 /** 277 * True if aFrame is an instance of an SVG frame class or is an inline/block 278 * frame being used for SVG text. 279 */ 280 static bool IsFrameForSVG(const nsIFrame* aFrame) { 281 return aFrame->IsSVGFrame() || aFrame->IsInSVGTextSubtree(); 282 } 283 284 static bool IsLastContinuationForColumnContent(const nsIFrame* aFrame) { 285 MOZ_ASSERT(aFrame); 286 return aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent && 287 !aFrame->GetNextContinuation(); 288 } 289 290 /** 291 * Returns true iff aFrame explicitly prevents its descendants from floating 292 * (at least, down to the level of descendants which themselves are 293 * float-containing blocks -- those will manage the floating status of any 294 * lower-level descendents inside them, of course). 295 */ 296 static bool ShouldSuppressFloatingOfDescendants(nsIFrame* aFrame) { 297 return aFrame->IsFlexOrGridContainer() || aFrame->IsMathMLFrame(); 298 } 299 300 // Return true if column-span descendants should be suppressed under aFrame's 301 // subtree (until a multi-column container re-establishing a block formatting 302 // context). Basically, this is testing whether aFrame establishes a new block 303 // formatting context or not. 304 static bool ShouldSuppressColumnSpanDescendants(nsIFrame* aFrame) { 305 if (aFrame->Style()->GetPseudoType() == PseudoStyleType::columnContent) { 306 // Never suppress column-span under ::-moz-column-content frames. 307 return false; 308 } 309 310 if (aFrame->IsInlineFrame()) { 311 // Allow inline frames to have column-span block children. 312 return false; 313 } 314 315 if (!aFrame->IsBlockFrameOrSubclass() || 316 aFrame->HasAnyStateBits(NS_BLOCK_BFC | NS_FRAME_OUT_OF_FLOW) || 317 aFrame->IsFixedPosContainingBlock()) { 318 // Need to suppress column-span if we: 319 // - Are a different block formatting context, 320 // - Are an out-of-flow frame, OR 321 // - Establish a containing block for fixed-position descendants 322 // 323 // For example, the children of a column-span never need to be further 324 // processed even if there is a nested column-span child. Because a 325 // column-span always creates its own block formatting context, a nested 326 // column-span child won't be in the same block formatting context with the 327 // nearest multi-column ancestor. This is the same case as if the 328 // column-span is outside of a multi-column hierarchy. 329 return true; 330 } 331 332 return false; 333 } 334 335 // Reparent a frame into a wrapper frame that is a child of its old parent. 336 static void ReparentFrame(RestyleManager* aRestyleManager, 337 nsContainerFrame* aNewParentFrame, nsIFrame* aFrame, 338 bool aForceStyleReparent) { 339 aFrame->SetParent(aNewParentFrame); 340 // We reparent frames for two reasons: to put them inside ::first-line, and to 341 // put them inside some wrapper anonymous boxes. 342 if (aForceStyleReparent) { 343 aRestyleManager->ReparentComputedStyleForFirstLine(aFrame); 344 } 345 } 346 347 static void ReparentFrames(nsCSSFrameConstructor* aFrameConstructor, 348 nsContainerFrame* aNewParentFrame, 349 const nsFrameList& aFrameList, 350 bool aForceStyleReparent) { 351 RestyleManager* restyleManager = aFrameConstructor->RestyleManager(); 352 for (nsIFrame* f : aFrameList) { 353 ReparentFrame(restyleManager, aNewParentFrame, f, aForceStyleReparent); 354 } 355 } 356 357 //---------------------------------------------------------------------- 358 // 359 // When inline frames get weird and have block frames in them, we 360 // annotate them to help us respond to incremental content changes 361 // more easily. 362 363 static inline bool IsFramePartOfIBSplit(nsIFrame* aFrame) { 364 bool result = aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT); 365 MOZ_ASSERT(!result || static_cast<nsBlockFrame*>(do_QueryFrame(aFrame)) || 366 static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)), 367 "only block/inline frames can have NS_FRAME_PART_OF_IBSPLIT"); 368 return result; 369 } 370 371 static nsContainerFrame* GetIBSplitSibling(nsIFrame* aFrame) { 372 MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this"); 373 374 // We only store the "ib-split sibling" annotation with the first 375 // frame in the continuation chain. Walk back to find that frame now. 376 return aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling()); 377 } 378 379 static nsContainerFrame* GetIBSplitPrevSibling(nsIFrame* aFrame) { 380 MOZ_ASSERT(IsFramePartOfIBSplit(aFrame), "Shouldn't call this"); 381 382 // We only store the ib-split sibling annotation with the first 383 // frame in the continuation chain. Walk back to find that frame now. 384 return aFrame->FirstContinuation()->GetProperty( 385 nsIFrame::IBSplitPrevSibling()); 386 } 387 388 static nsContainerFrame* GetLastIBSplitSibling(nsIFrame* aFrame) { 389 for (nsIFrame *frame = aFrame, *next;; frame = next) { 390 next = GetIBSplitSibling(frame); 391 if (!next) { 392 return static_cast<nsContainerFrame*>(frame); 393 } 394 } 395 MOZ_ASSERT_UNREACHABLE("unreachable code"); 396 return nullptr; 397 } 398 399 static void SetFrameIsIBSplit(nsContainerFrame* aFrame, 400 nsContainerFrame* aIBSplitSibling) { 401 MOZ_ASSERT(aFrame, "bad args!"); 402 403 // We should be the only continuation 404 NS_ASSERTION(!aFrame->GetPrevContinuation(), 405 "assigning ib-split sibling to other than first continuation!"); 406 NS_ASSERTION(!aFrame->GetNextContinuation() || 407 IsFramePartOfIBSplit(aFrame->GetNextContinuation()), 408 "should have no non-ib-split continuations here"); 409 410 // Mark the frame as ib-split. 411 aFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT); 412 413 if (aIBSplitSibling) { 414 NS_ASSERTION(!aIBSplitSibling->GetPrevContinuation(), 415 "assigning something other than the first continuation as the " 416 "ib-split sibling"); 417 418 // Store the ib-split sibling (if we were given one) with the 419 // first frame in the flow. 420 aFrame->SetProperty(nsIFrame::IBSplitSibling(), aIBSplitSibling); 421 aIBSplitSibling->SetProperty(nsIFrame::IBSplitPrevSibling(), aFrame); 422 } 423 } 424 425 static nsIFrame* GetIBContainingBlockFor(nsIFrame* aFrame) { 426 MOZ_ASSERT( 427 IsFramePartOfIBSplit(aFrame), 428 "GetIBContainingBlockFor() should only be called on known IB frames"); 429 430 // Get the first "normal" ancestor of the target frame. 431 nsIFrame* parentFrame; 432 do { 433 parentFrame = aFrame->GetParent(); 434 435 if (!parentFrame) { 436 NS_ERROR("no unsplit block frame in IB hierarchy"); 437 return aFrame; 438 } 439 440 // Note that we ignore non-ib-split frames which have a pseudo on their 441 // ComputedStyle -- they're not the frames we're looking for! In 442 // particular, they may be hiding a real parent that _is_ in an ib-split. 443 if (!IsFramePartOfIBSplit(parentFrame) && 444 !parentFrame->Style()->IsPseudoOrAnonBox()) { 445 break; 446 } 447 448 aFrame = parentFrame; 449 } while (true); 450 451 // post-conditions 452 NS_ASSERTION(parentFrame, 453 "no normal ancestor found for ib-split frame " 454 "in GetIBContainingBlockFor"); 455 NS_ASSERTION(parentFrame != aFrame, 456 "parentFrame is actually the child frame - bogus reslt"); 457 458 return parentFrame; 459 } 460 461 // Find the multicol containing block suitable for reframing. 462 // 463 // Note: this function may not return a ColumnSetWrapperFrame. For example, if 464 // the multicol containing block has "overflow:scroll" style, 465 // ScrollContainerFrame is returned because ColumnSetWrapperFrame is the 466 // scrolled frame which has the -moz-scrolled-content pseudo style. We may walk 467 // up "too far", but in terms of correctness of reframing, it's OK. 468 static nsContainerFrame* GetMultiColumnContainingBlockFor(nsIFrame* aFrame) { 469 MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR), 470 "Should only be called if the frame has a multi-column ancestor!"); 471 472 nsContainerFrame* current = aFrame->GetParent(); 473 while (current && 474 (current->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) || 475 current->Style()->IsPseudoOrAnonBox())) { 476 current = current->GetParent(); 477 } 478 479 MOZ_ASSERT(current, 480 "No multicol containing block in a valid column hierarchy?"); 481 482 return current; 483 } 484 485 static bool InsertSeparatorBeforeAccessKey() { 486 static bool sInitialized = false; 487 static bool sValue = false; 488 if (!sInitialized) { 489 sInitialized = true; 490 sValue = 491 intl::LocaleService::GetInstance()->InsertSeparatorBeforeAccesskeys(); 492 } 493 return sValue; 494 } 495 496 static bool AlwaysAppendAccessKey() { 497 static bool sInitialized = false; 498 static bool sValue = false; 499 if (!sInitialized) { 500 sInitialized = true; 501 sValue = intl::LocaleService::GetInstance()->AlwaysAppendAccesskeys(); 502 } 503 return sValue; 504 } 505 506 //---------------------------------------------------------------------- 507 508 // Block/inline frame construction logic. We maintain a few invariants here: 509 // 510 // 1. Block frames contain block and inline frames. 511 // 512 // 2. Inline frames only contain inline frames. If an inline parent has a block 513 // child then the block child is migrated upward until it lands in a block 514 // parent (the inline frames containing block is where it will end up). 515 516 inline void SetInitialSingleChild(nsContainerFrame* aParent, nsIFrame* aFrame) { 517 MOZ_ASSERT(!aFrame->GetNextSibling(), "Should be using a frame list"); 518 aParent->SetInitialChildList(FrameChildListID::Principal, 519 nsFrameList(aFrame, aFrame)); 520 } 521 522 // ----------------------------------------------------------- 523 524 // Structure used when constructing formatting object trees. Contains 525 // state information needed for absolutely positioned elements 526 namespace mozilla { 527 struct AbsoluteFrameList final : public nsFrameList { 528 // Containing block for absolutely positioned elements. 529 nsContainerFrame* mContainingBlock; 530 531 explicit AbsoluteFrameList(nsContainerFrame* aContainingBlock = nullptr) 532 : mContainingBlock(aContainingBlock) {} 533 534 // Transfer frames in aOther to this list. aOther becomes empty after this 535 // operation. 536 AbsoluteFrameList(AbsoluteFrameList&& aOther) = default; 537 AbsoluteFrameList& operator=(AbsoluteFrameList&& aOther) = default; 538 539 #ifdef DEBUG 540 // XXXbz Does this need a debug-only assignment operator that nulls out the 541 // childList in the AbsoluteFrameList we're copying? Introducing a difference 542 // between debug and non-debug behavior seems bad, so I guess not... 543 ~AbsoluteFrameList() { 544 NS_ASSERTION(!FirstChild(), 545 "Dangling child list. Someone forgot to insert it?"); 546 } 547 #endif 548 }; 549 } // namespace mozilla 550 551 // ----------------------------------------------------------- 552 553 // Structure for saving the existing state when pushing/poping containing 554 // blocks. The destructor restores the state to its previous state 555 class MOZ_STACK_CLASS nsFrameConstructorSaveState { 556 public: 557 ~nsFrameConstructorSaveState(); 558 559 private: 560 // Pointer to struct whose data we save/restore. 561 AbsoluteFrameList* mList = nullptr; 562 563 // The saved pointer to the fixed list. 564 AbsoluteFrameList* mSavedFixedList = nullptr; 565 566 // Copy of original frame list. This can be the original absolute list or a 567 // float list. 568 AbsoluteFrameList mSavedList; 569 570 // The name of the child list in which our frames would belong. 571 mozilla::FrameChildListID mChildListID = FrameChildListID::Principal; 572 nsFrameConstructorState* mState = nullptr; 573 574 friend class nsFrameConstructorState; 575 }; 576 577 // Structure used for maintaining state information during the 578 // frame construction process 579 class MOZ_STACK_CLASS nsFrameConstructorState { 580 public: 581 nsPresContext* mPresContext; 582 PresShell* mPresShell; 583 nsCSSFrameConstructor* mFrameConstructor; 584 585 // Containing block information for out-of-flow frames. 586 // 587 // Floats are easy. Whatever is our float CB. 588 // 589 // Regular abspos elements are easy too. Its containing block can be the 590 // nearest abspos element, or the ICB (the canvas frame). 591 // 592 // Top layer abspos elements are always children of the ICB, but we can get 593 // away with having two different lists (mAbsoluteList and 594 // mTopLayerAbsoluteList), because because top layer frames cause 595 // non-top-layer frames to be contained inside (so any descendants of a top 596 // layer abspos can never share containing block with it, unless they're also 597 // in the top layer). 598 // 599 // Regular fixed elements however are trickier. Fixed elements can be 600 // contained in one of three lists: 601 // 602 // * mAbsoluteList, if our abspos cb is also a fixpos cb (e.g., is 603 // transformed or has a filter). 604 // 605 // * mAncestorFixedList, if the fixpos cb is an ancestor element other than 606 // the viewport frame, (so, a transformed / filtered 607 // ancestor). 608 // 609 // * mRealFixedList, which is also the fixed list used for the top layer 610 // fixed items, which is the fixed list of the viewport 611 // frame. 612 // 613 // It is important that mRealFixedList is shared between regular and top layer 614 // fixpos elements, since no-top-layer descendants of top layer fixed elements 615 // could share ICB and vice versa, so without that there would be no guarantee 616 // of layout ordering between them. 617 AbsoluteFrameList mFloatedList; 618 AbsoluteFrameList mAbsoluteList; 619 AbsoluteFrameList mTopLayerAbsoluteList; 620 AbsoluteFrameList mAncestorFixedList; 621 AbsoluteFrameList mRealFixedList; 622 623 // Never null, always pointing to one of the lists documented above. 624 AbsoluteFrameList* mFixedList; 625 626 // What `page: auto` resolves to. This is the used page-name of the parent 627 // frame. Updated by AutoFrameConstructionPageName. 628 const nsAtom* mAutoPageNameValue = nullptr; 629 630 nsCOMPtr<nsILayoutHistoryState> mFrameState; 631 // These bits will be added to the state bits of any frame we construct 632 // using this state. 633 nsFrameState mAdditionalStateBits{0}; 634 635 // If false (which is the default) then call SetPrimaryFrame() as needed 636 // during frame construction. If true, don't make any SetPrimaryFrame() 637 // calls, except for generated content which doesn't have a primary frame 638 // yet. The mCreatingExtraFrames == true mode is meant to be used for 639 // construction of random "extra" frames for elements via normal frame 640 // construction APIs (e.g. replication of things across pages in paginated 641 // mode). 642 bool mCreatingExtraFrames; 643 644 // This keeps track of whether we have found a "rendered legend" for 645 // the current FieldSetFrame. 646 bool mHasRenderedLegend; 647 648 nsTArray<RefPtr<nsIContent>> mGeneratedContentWithInitializer; 649 650 #ifdef DEBUG 651 // Record the float containing block candidate passed into 652 // MaybePushFloatContainingBlock() to keep track that we've call the method to 653 // handle the float CB scope before processing the CB's children. It is reset 654 // in ConstructFramesFromItemList(). 655 nsContainerFrame* mFloatCBCandidate = nullptr; 656 #endif 657 658 // Constructor 659 // Use the passed-in history state. 660 nsFrameConstructorState( 661 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock, 662 nsContainerFrame* aAbsoluteContainingBlock, 663 nsContainerFrame* aFloatContainingBlock, 664 already_AddRefed<nsILayoutHistoryState> aHistoryState); 665 // Get the history state from the pres context's pres shell. 666 nsFrameConstructorState(PresShell* aPresShell, 667 nsContainerFrame* aFixedContainingBlock, 668 nsContainerFrame* aAbsoluteContainingBlock, 669 nsContainerFrame* aFloatContainingBlock); 670 671 ~nsFrameConstructorState(); 672 673 // Process the frame insertions for all the out-of-flow nsAbsoluteItems. 674 void ProcessFrameInsertionsForAllLists(); 675 676 // Function to push the existing absolute containing block state and 677 // create a new scope. Code that uses this function should get matching 678 // logic in GetAbsoluteContainingBlock. 679 // Also makes aNewAbsoluteContainingBlock the containing block for 680 // fixed-pos elements if necessary. 681 // aPositionedFrame is the frame whose style actually makes 682 // aNewAbsoluteContainingBlock a containing block. E.g. for a scrollable 683 // element aPositionedFrame is the element's primary frame and 684 // aNewAbsoluteContainingBlock is the scrolled frame. 685 void PushAbsoluteContainingBlock( 686 nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame, 687 nsFrameConstructorSaveState& aSaveState); 688 689 // Function to forbid floats descendants under aFloatCBCandidate, or open a 690 // new float containing block scope for aFloatCBCandidate. The current 691 // state is saved in aSaveState if a new scope is pushed. 692 void MaybePushFloatContainingBlock(nsContainerFrame* aFloatCBCandidate, 693 nsFrameConstructorSaveState& aSaveState); 694 695 // Helper function for MaybePushFloatContainingBlock(). 696 void PushFloatContainingBlock(nsContainerFrame* aNewFloatContainingBlock, 697 nsFrameConstructorSaveState& aSaveState); 698 699 // Function to return the proper geometric parent for a frame with display 700 // struct given by aStyleDisplay and parent's frame given by 701 // aContentParentFrame. 702 nsContainerFrame* GetGeometricParent( 703 const nsStyleDisplay& aStyleDisplay, 704 nsContainerFrame* aContentParentFrame) const; 705 706 // Collect absolute frames in mAbsoluteList which are proper descendants 707 // of aNewParent, and reparent them to aNewParent. 708 // 709 // Note: This function does something unusual that moves absolute items 710 // after their frames are constructed under a column hierarchy which has 711 // column-span elements. Do not use this if you're not dealing with 712 // columns. 713 void ReparentAbsoluteItems(nsContainerFrame* aNewParent); 714 715 // Collect floats in mFloatedList which are proper descendants of aNewParent, 716 // and reparent them to aNewParent. 717 // 718 // Note: This function does something unusual that moves floats after their 719 // frames are constructed under a column hierarchy which has column-span 720 // elements. Do not use this if you're not dealing with columns. 721 void ReparentFloats(nsContainerFrame* aNewParent); 722 723 /** 724 * Function to add a new frame to the right frame list. This MUST be called 725 * on frames before their children have been processed if the frames might 726 * conceivably be out-of-flow; otherwise cleanup in error cases won't work 727 * right. Also, this MUST be called on frames after they have been 728 * initialized. 729 * @param aNewFrame the frame to add 730 * @param aFrameList the list to add in-flow frames to 731 * @param aContent the content pointer for aNewFrame 732 * @param aParentFrame the parent frame for the content if it were in-flow 733 * @param aCanBePositioned pass false if the frame isn't allowed to be 734 * positioned 735 * @param aCanBeFloated pass false if the frame isn't allowed to be 736 * floated 737 */ 738 void AddChild(nsIFrame* aNewFrame, nsFrameList& aFrameList, 739 nsIContent* aContent, nsContainerFrame* aParentFrame, 740 bool aCanBePositioned = true, bool aCanBeFloated = true, 741 bool aInsertAfter = false, 742 nsIFrame* aInsertAfterFrame = nullptr); 743 744 /** 745 * Function to return the fixed-pos element list. Normally this will just 746 * hand back the fixed-pos element list, but in case we're dealing with a 747 * transformed element that's acting as an abs-pos and fixed-pos container, 748 * we'll hand back the abs-pos list. Callers should use this function if they 749 * want to get the list acting as the fixed-pos item parent. 750 */ 751 AbsoluteFrameList& GetFixedList() { return *mFixedList; } 752 const AbsoluteFrameList& GetFixedList() const { return *mFixedList; } 753 754 protected: 755 friend class nsFrameConstructorSaveState; 756 757 /** 758 * ProcessFrameInsertions takes the frames in aFrameList and adds them as 759 * kids to the aChildListID child list of |aFrameList.containingBlock|. 760 */ 761 void ProcessFrameInsertions(AbsoluteFrameList& aFrameList, 762 mozilla::FrameChildListID aChildListID); 763 764 /** 765 * GetOutOfFlowFrameList selects the out-of-flow frame list the new 766 * frame should be added to. If the frame shouldn't be added to any 767 * out-of-flow list, it returns nullptr. The corresponding type of 768 * placeholder is also returned via the aPlaceholderType parameter 769 * if this method doesn't return nullptr. The caller should check 770 * whether the returned list really has a containing block. 771 */ 772 AbsoluteFrameList* GetOutOfFlowFrameList(nsIFrame* aNewFrame, 773 bool aCanBePositioned, 774 bool aCanBeFloated, 775 nsFrameState* aPlaceholderType); 776 }; 777 778 nsFrameConstructorState::nsFrameConstructorState( 779 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock, 780 nsContainerFrame* aAbsoluteContainingBlock, 781 nsContainerFrame* aFloatContainingBlock, 782 already_AddRefed<nsILayoutHistoryState> aHistoryState) 783 : mPresContext(aPresShell->GetPresContext()), 784 mPresShell(aPresShell), 785 mFrameConstructor(aPresShell->FrameConstructor()), 786 mFloatedList(aFloatContainingBlock), 787 mAbsoluteList(aAbsoluteContainingBlock), 788 mTopLayerAbsoluteList(mFrameConstructor->GetCanvasFrame()), 789 mAncestorFixedList(aFixedContainingBlock), 790 mRealFixedList( 791 static_cast<nsContainerFrame*>(mFrameConstructor->GetRootFrame())), 792 // See PushAbsoluteContaningBlock below 793 mFrameState(aHistoryState), 794 mCreatingExtraFrames(false), 795 mHasRenderedLegend(false) { 796 MOZ_COUNT_CTOR(nsFrameConstructorState); 797 mFixedList = [&] { 798 if (aFixedContainingBlock == aAbsoluteContainingBlock) { 799 return &mAbsoluteList; 800 } 801 if (aAbsoluteContainingBlock == mRealFixedList.mContainingBlock) { 802 return &mRealFixedList; 803 } 804 return &mAncestorFixedList; 805 }(); 806 } 807 808 nsFrameConstructorState::nsFrameConstructorState( 809 PresShell* aPresShell, nsContainerFrame* aFixedContainingBlock, 810 nsContainerFrame* aAbsoluteContainingBlock, 811 nsContainerFrame* aFloatContainingBlock) 812 : nsFrameConstructorState( 813 aPresShell, aFixedContainingBlock, aAbsoluteContainingBlock, 814 aFloatContainingBlock, 815 aPresShell->GetDocument()->GetLayoutHistoryState()) {} 816 817 nsFrameConstructorState::~nsFrameConstructorState() { 818 MOZ_COUNT_DTOR(nsFrameConstructorState); 819 ProcessFrameInsertionsForAllLists(); 820 for (auto& content : Reversed(mGeneratedContentWithInitializer)) { 821 content->RemoveProperty(nsGkAtoms::genConInitializerProperty); 822 } 823 } 824 825 void nsFrameConstructorState::ProcessFrameInsertionsForAllLists() { 826 ProcessFrameInsertions(mFloatedList, FrameChildListID::Float); 827 ProcessFrameInsertions(mAbsoluteList, FrameChildListID::Absolute); 828 ProcessFrameInsertions(mTopLayerAbsoluteList, FrameChildListID::Absolute); 829 ProcessFrameInsertions(*mFixedList, FrameChildListID::Fixed); 830 ProcessFrameInsertions(mRealFixedList, FrameChildListID::Fixed); 831 } 832 833 void nsFrameConstructorState::PushAbsoluteContainingBlock( 834 nsContainerFrame* aNewAbsoluteContainingBlock, nsIFrame* aPositionedFrame, 835 nsFrameConstructorSaveState& aSaveState) { 836 MOZ_ASSERT(!!aNewAbsoluteContainingBlock == !!aPositionedFrame, 837 "We should have both or none"); 838 aSaveState.mList = &mAbsoluteList; 839 aSaveState.mChildListID = FrameChildListID::Absolute; 840 aSaveState.mState = this; 841 aSaveState.mSavedList = std::move(mAbsoluteList); 842 aSaveState.mSavedFixedList = mFixedList; 843 mAbsoluteList = AbsoluteFrameList(aNewAbsoluteContainingBlock); 844 mFixedList = [&] { 845 if (!aPositionedFrame || aPositionedFrame->IsFixedPosContainingBlock()) { 846 // See if we need to treat abspos and fixedpos the same. This happens if 847 // we're a transformed/filtered/etc element, or if we force a null abspos 848 // containing block (for mathml for example). 849 return &mAbsoluteList; 850 } 851 if (aPositionedFrame->StyleDisplay()->mTopLayer == StyleTopLayer::Auto) { 852 // If our new CB is in the top layer, and isn't a fixed CB itself, we also 853 // escape the usual containment. 854 return &mRealFixedList; 855 } 856 if (mFixedList == &mAbsoluteList) { 857 // If we were pointing to our old absolute list, keep pointing to it. 858 return &aSaveState.mSavedList; 859 } 860 // Otherwise keep pointing to the current thing (another ancestor's 861 // absolute list, or the real fixed list, doesn't matter). 862 return mFixedList; 863 }(); 864 865 if (aNewAbsoluteContainingBlock && 866 !aNewAbsoluteContainingBlock->IsAbsoluteContainer()) { 867 aNewAbsoluteContainingBlock->MarkAsAbsoluteContainingBlock(); 868 } 869 } 870 871 void nsFrameConstructorState::MaybePushFloatContainingBlock( 872 nsContainerFrame* aFloatCBCandidate, 873 nsFrameConstructorSaveState& aSaveState) { 874 // The logic here needs to match the logic in GetFloatContainingBlock(). 875 if (ShouldSuppressFloatingOfDescendants(aFloatCBCandidate)) { 876 // Pushing a null float containing block forbids any frames from being 877 // floated until a new float containing block is pushed. See implementation 878 // of nsFrameConstructorState::AddChild(). 879 // 880 // XXX we should get rid of null float containing blocks and teach the 881 // various frame classes to deal with floats instead. 882 PushFloatContainingBlock(nullptr, aSaveState); 883 } else if (aFloatCBCandidate->IsFloatContainingBlock()) { 884 PushFloatContainingBlock(aFloatCBCandidate, aSaveState); 885 } 886 887 #ifdef DEBUG 888 mFloatCBCandidate = aFloatCBCandidate; 889 #endif 890 } 891 892 void nsFrameConstructorState::PushFloatContainingBlock( 893 nsContainerFrame* aNewFloatContainingBlock, 894 nsFrameConstructorSaveState& aSaveState) { 895 MOZ_ASSERT(!aNewFloatContainingBlock || 896 aNewFloatContainingBlock->IsFloatContainingBlock(), 897 "Please push a real float containing block!"); 898 NS_ASSERTION( 899 !aNewFloatContainingBlock || 900 !ShouldSuppressFloatingOfDescendants(aNewFloatContainingBlock), 901 "We should not push a frame that is supposed to _suppress_ " 902 "floats as a float containing block!"); 903 aSaveState.mList = &mFloatedList; 904 aSaveState.mSavedList = std::move(mFloatedList); 905 aSaveState.mChildListID = FrameChildListID::Float; 906 aSaveState.mState = this; 907 mFloatedList = AbsoluteFrameList(aNewFloatContainingBlock); 908 } 909 910 nsContainerFrame* nsFrameConstructorState::GetGeometricParent( 911 const nsStyleDisplay& aStyleDisplay, 912 nsContainerFrame* aContentParentFrame) const { 913 // If there is no container for a fixed, absolute, or floating root 914 // frame, we will ignore the positioning. This hack is originally 915 // brought to you by the letter T: tables, since other roots don't 916 // even call into this code. See bug 178855. 917 // 918 // XXX Disabling positioning in this case is a hack. If one was so inclined, 919 // one could support this either by (1) inserting a dummy block between the 920 // table and the canvas or (2) teaching the canvas how to reflow positioned 921 // elements. (1) has the usual problems when multiple frames share the same 922 // content (notice all the special cases in this file dealing with inner 923 // tables and table wrappers which share the same content). (2) requires some 924 // work and possible factoring. 925 // 926 // XXXbz couldn't we just force position to "static" on roots and 927 // float to "none"? That's OK per CSS 2.1, as far as I can tell. 928 929 if (aContentParentFrame && aContentParentFrame->IsInSVGTextSubtree()) { 930 return aContentParentFrame; 931 } 932 933 if (aStyleDisplay.IsFloatingStyle() && mFloatedList.mContainingBlock) { 934 NS_ASSERTION(!aStyleDisplay.IsAbsolutelyPositionedStyle(), 935 "Absolutely positioned _and_ floating?"); 936 return mFloatedList.mContainingBlock; 937 } 938 939 if (aStyleDisplay.mTopLayer != StyleTopLayer::None) { 940 MOZ_ASSERT(aStyleDisplay.mTopLayer == StyleTopLayer::Auto, 941 "-moz-top-layer should be either none or auto"); 942 MOZ_ASSERT(aStyleDisplay.IsAbsolutelyPositionedStyle(), 943 "Top layer items should always be absolutely positioned"); 944 if (aStyleDisplay.mPosition == StylePositionProperty::Fixed) { 945 MOZ_ASSERT(mRealFixedList.mContainingBlock, "No root frame?"); 946 return mRealFixedList.mContainingBlock; 947 } 948 MOZ_ASSERT(aStyleDisplay.mPosition == StylePositionProperty::Absolute); 949 MOZ_ASSERT(mTopLayerAbsoluteList.mContainingBlock); 950 return mTopLayerAbsoluteList.mContainingBlock; 951 } 952 953 if (aStyleDisplay.mPosition == StylePositionProperty::Absolute && 954 mAbsoluteList.mContainingBlock) { 955 return mAbsoluteList.mContainingBlock; 956 } 957 958 if (aStyleDisplay.mPosition == StylePositionProperty::Fixed && 959 mFixedList->mContainingBlock) { 960 return mFixedList->mContainingBlock; 961 } 962 963 return aContentParentFrame; 964 } 965 966 void nsFrameConstructorState::ReparentAbsoluteItems( 967 nsContainerFrame* aNewParent) { 968 // Bug 1491727: This function might not conform to the spec. See 969 // https://github.com/w3c/csswg-drafts/issues/1894. 970 971 MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR), 972 "Restrict the usage under column hierarchy."); 973 974 AbsoluteFrameList newAbsoluteItems(aNewParent); 975 976 nsIFrame* current = mAbsoluteList.FirstChild(); 977 while (current) { 978 nsIFrame* placeholder = current->GetPlaceholderFrame(); 979 980 if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) { 981 nsIFrame* next = current->GetNextSibling(); 982 mAbsoluteList.RemoveFrame(current); 983 newAbsoluteItems.AppendFrame(aNewParent, current); 984 current = next; 985 } else { 986 current = current->GetNextSibling(); 987 } 988 } 989 990 if (newAbsoluteItems.NotEmpty()) { 991 // ~nsFrameConstructorSaveState() will move newAbsoluteItems to 992 // aNewParent's absolute child list. 993 nsFrameConstructorSaveState absoluteSaveState; 994 995 // It doesn't matter whether aNewParent has position style or not. Caller 996 // won't call us if we can't have absolute children. 997 PushAbsoluteContainingBlock(aNewParent, aNewParent, absoluteSaveState); 998 mAbsoluteList = std::move(newAbsoluteItems); 999 } 1000 } 1001 1002 void nsFrameConstructorState::ReparentFloats(nsContainerFrame* aNewParent) { 1003 MOZ_ASSERT(aNewParent->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR), 1004 "Restrict the usage under column hierarchy."); 1005 MOZ_ASSERT( 1006 aNewParent->IsFloatContainingBlock(), 1007 "Why calling this method if aNewParent is not a float containing block?"); 1008 1009 // Gather floats that should reparent under aNewParent. 1010 AbsoluteFrameList floats(aNewParent); 1011 nsIFrame* current = mFloatedList.FirstChild(); 1012 while (current) { 1013 nsIFrame* placeholder = current->GetPlaceholderFrame(); 1014 nsIFrame* next = current->GetNextSibling(); 1015 if (nsLayoutUtils::IsProperAncestorFrame(aNewParent, placeholder)) { 1016 mFloatedList.RemoveFrame(current); 1017 floats.AppendFrame(aNewParent, current); 1018 } 1019 current = next; 1020 } 1021 1022 if (floats.NotEmpty()) { 1023 // Make floats move into aNewParent's float child list in 1024 // ~nsFrameConstructorSaveState() when destructing floatSaveState. 1025 nsFrameConstructorSaveState floatSaveState; 1026 PushFloatContainingBlock(aNewParent, floatSaveState); 1027 mFloatedList = std::move(floats); 1028 } 1029 } 1030 1031 AbsoluteFrameList* nsFrameConstructorState::GetOutOfFlowFrameList( 1032 nsIFrame* aNewFrame, bool aCanBePositioned, bool aCanBeFloated, 1033 nsFrameState* aPlaceholderType) { 1034 const nsStyleDisplay* disp = aNewFrame->StyleDisplay(); 1035 if (aCanBeFloated && disp->IsFloatingStyle()) { 1036 *aPlaceholderType = PLACEHOLDER_FOR_FLOAT; 1037 return &mFloatedList; 1038 } 1039 1040 if (aCanBePositioned) { 1041 if (disp->mTopLayer != StyleTopLayer::None) { 1042 *aPlaceholderType = PLACEHOLDER_FOR_TOPLAYER; 1043 if (disp->mPosition == StylePositionProperty::Fixed) { 1044 *aPlaceholderType |= PLACEHOLDER_FOR_FIXEDPOS; 1045 return &mRealFixedList; 1046 } 1047 *aPlaceholderType |= PLACEHOLDER_FOR_ABSPOS; 1048 return &mTopLayerAbsoluteList; 1049 } 1050 if (disp->mPosition == StylePositionProperty::Absolute) { 1051 *aPlaceholderType = PLACEHOLDER_FOR_ABSPOS; 1052 return &mAbsoluteList; 1053 } 1054 if (disp->mPosition == StylePositionProperty::Fixed) { 1055 *aPlaceholderType = PLACEHOLDER_FOR_FIXEDPOS; 1056 return mFixedList; 1057 } 1058 } 1059 return nullptr; 1060 } 1061 1062 void nsFrameConstructorState::AddChild( 1063 nsIFrame* aNewFrame, nsFrameList& aFrameList, nsIContent* aContent, 1064 nsContainerFrame* aParentFrame, bool aCanBePositioned, bool aCanBeFloated, 1065 bool aInsertAfter, nsIFrame* aInsertAfterFrame) { 1066 MOZ_ASSERT(!aNewFrame->GetNextSibling(), "Shouldn't happen"); 1067 1068 nsFrameState placeholderType; 1069 AbsoluteFrameList* outOfFlowFrameList = GetOutOfFlowFrameList( 1070 aNewFrame, aCanBePositioned, aCanBeFloated, &placeholderType); 1071 1072 // The comments in GetGeometricParent regarding root table frames 1073 // all apply here, unfortunately. Thus, we need to check whether 1074 // the returned frame items really has containing block. 1075 nsFrameList* frameList; 1076 if (outOfFlowFrameList && outOfFlowFrameList->mContainingBlock) { 1077 MOZ_ASSERT(aNewFrame->GetParent() == outOfFlowFrameList->mContainingBlock, 1078 "Parent of the frame is not the containing block?"); 1079 frameList = outOfFlowFrameList; 1080 } else { 1081 frameList = &aFrameList; 1082 placeholderType = nsFrameState(0); 1083 } 1084 1085 if (placeholderType) { 1086 NS_ASSERTION(frameList != &aFrameList, 1087 "Putting frame in-flow _and_ want a placeholder?"); 1088 nsIFrame* placeholderFrame = 1089 nsCSSFrameConstructor::CreatePlaceholderFrameFor( 1090 mPresShell, aContent, aNewFrame, aParentFrame, nullptr, 1091 placeholderType); 1092 1093 placeholderFrame->AddStateBits(mAdditionalStateBits); 1094 // Add the placeholder frame to the flow 1095 aFrameList.AppendFrame(nullptr, placeholderFrame); 1096 } 1097 #ifdef DEBUG 1098 else { 1099 NS_ASSERTION(aNewFrame->GetParent() == aParentFrame, 1100 "In-flow frame has wrong parent"); 1101 } 1102 #endif 1103 1104 if (aInsertAfter) { 1105 frameList->InsertFrame(nullptr, aInsertAfterFrame, aNewFrame); 1106 } else { 1107 frameList->AppendFrame(nullptr, aNewFrame); 1108 } 1109 } 1110 1111 // Some of this function's callers recurse 1000 levels deep in crashtests. On 1112 // platforms where stack limits are low, we can't afford to incorporate this 1113 // function's `AutoTArray`s into its callers' stack frames, so disable inlining. 1114 MOZ_NEVER_INLINE void nsFrameConstructorState::ProcessFrameInsertions( 1115 AbsoluteFrameList& aFrameList, FrameChildListID aChildListID) { 1116 MOZ_ASSERT(&aFrameList == &mFloatedList || &aFrameList == &mAbsoluteList || 1117 &aFrameList == &mTopLayerAbsoluteList || 1118 &aFrameList == &mAncestorFixedList || &aFrameList == mFixedList || 1119 &aFrameList == &mRealFixedList); 1120 MOZ_ASSERT_IF(&aFrameList == &mFloatedList, 1121 aChildListID == FrameChildListID::Float); 1122 MOZ_ASSERT_IF(&aFrameList == &mAbsoluteList || &aFrameList == mFixedList, 1123 aChildListID == FrameChildListID::Absolute || 1124 aChildListID == FrameChildListID::Fixed); 1125 MOZ_ASSERT_IF(&aFrameList == &mTopLayerAbsoluteList, 1126 aChildListID == FrameChildListID::Absolute); 1127 MOZ_ASSERT_IF(&aFrameList == mFixedList && &aFrameList != &mAbsoluteList, 1128 aChildListID == FrameChildListID::Fixed); 1129 MOZ_ASSERT_IF(&aFrameList == &mAncestorFixedList, 1130 aChildListID == FrameChildListID::Fixed); 1131 MOZ_ASSERT_IF(&aFrameList == &mRealFixedList, 1132 aChildListID == FrameChildListID::Fixed); 1133 1134 if (aFrameList.IsEmpty()) { 1135 return; 1136 } 1137 1138 nsContainerFrame* containingBlock = aFrameList.mContainingBlock; 1139 1140 NS_ASSERTION(containingBlock, "Child list without containing block?"); 1141 1142 if (aChildListID == FrameChildListID::Fixed) { 1143 // Put this frame on the transformed-frame's abs-pos list instead, if 1144 // it has abs-pos children instead of fixed-pos children. 1145 aChildListID = containingBlock->GetAbsoluteListID(); 1146 } 1147 1148 // Insert the frames hanging out in aItems. We can use SetInitialChildList() 1149 // if the containing block hasn't been reflowed yet (so NS_FRAME_FIRST_REFLOW 1150 // is set) and doesn't have any frames in the aChildListID child list yet. 1151 const nsFrameList& childList = containingBlock->GetChildList(aChildListID); 1152 if (childList.IsEmpty() && 1153 containingBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { 1154 // If we're injecting absolutely positioned frames, inject them on the 1155 // absolute containing block 1156 if (aChildListID == containingBlock->GetAbsoluteListID()) { 1157 containingBlock->GetAbsoluteContainingBlock()->SetInitialChildList( 1158 containingBlock, aChildListID, std::move(aFrameList)); 1159 } else { 1160 containingBlock->SetInitialChildList(aChildListID, std::move(aFrameList)); 1161 } 1162 } else if (childList.IsEmpty() || aChildListID == FrameChildListID::Fixed || 1163 aChildListID == FrameChildListID::Absolute) { 1164 // The order is not important for abs-pos/fixed-pos frame list, just 1165 // append the frame items to the list directly. 1166 mFrameConstructor->AppendFrames(containingBlock, aChildListID, 1167 std::move(aFrameList)); 1168 } else { 1169 MOZ_ASSERT(aChildListID == FrameChildListID::Float); 1170 // Note that whether the frame construction context is doing an append or 1171 // not is not helpful here, since it could be appending to some frame in 1172 // the middle of the document, which means we're not necessarily 1173 // appending to the children of the containing block. 1174 // 1175 // We need to make sure the 'append to the end of document' case is fast. 1176 // So first test the last child of the containing block 1177 nsIFrame* lastChild = childList.LastChild(); 1178 lastChild = lastChild->FirstContinuation()->GetPlaceholderFrame(); 1179 1180 // CompareTreePosition uses placeholder hierarchy for out of flow frames, 1181 // so this will make out-of-flows respect the ordering of placeholders, 1182 // which is great because it takes care of anonymous content. 1183 nsIFrame* firstNewFrame = aFrameList.FirstChild(); 1184 firstNewFrame = firstNewFrame->GetPlaceholderFrame(); 1185 1186 // Cache the ancestor chain so that we can reuse it if needed. 1187 AutoTArray<const nsIFrame*, 20> firstNewFrameAncestors; 1188 const nsIFrame* notCommonAncestor = nsLayoutUtils::FillAncestors( 1189 firstNewFrame, containingBlock, &firstNewFrameAncestors); 1190 1191 if (nsLayoutUtils::CompareTreePosition( 1192 lastChild, firstNewFrame, firstNewFrameAncestors, 1193 notCommonAncestor ? containingBlock : nullptr) < 0) { 1194 // lastChild comes before the new children, so just append 1195 mFrameConstructor->AppendFrames(containingBlock, aChildListID, 1196 std::move(aFrameList)); 1197 } else { 1198 // Try the other children. First collect them to an array so that a 1199 // reasonable fast binary search can be used to find the insertion point. 1200 AutoTArray<std::pair<nsIFrame*, nsPlaceholderFrame*>, 128> children; 1201 for (nsIFrame* f : childList) { 1202 children.AppendElement( 1203 std::make_pair(f, f->FirstContinuation()->GetPlaceholderFrame())); 1204 } 1205 1206 nsIFrame* insertionPoint = nullptr; 1207 int32_t imin = 0; 1208 int32_t max = children.Length(); 1209 while (max > imin) { 1210 int32_t imid = imin + ((max - imin) / 2); 1211 const auto& pair = children[imid]; 1212 int32_t compare = nsLayoutUtils::CompareTreePosition( 1213 pair.second, firstNewFrame, firstNewFrameAncestors, 1214 notCommonAncestor ? containingBlock : nullptr); 1215 if (compare > 0) { 1216 // f is after the new frame. 1217 max = imid; 1218 insertionPoint = imid > 0 ? children[imid - 1].first : nullptr; 1219 } else if (compare < 0) { 1220 // f is before the new frame. 1221 imin = imid + 1; 1222 insertionPoint = pair.first; 1223 } else { 1224 // This is for the old behavior. Should be removed once it is 1225 // guaranteed that CompareTreePosition can't return 0! 1226 // See bug 928645. 1227 NS_WARNING("Something odd happening???"); 1228 insertionPoint = nullptr; 1229 for (auto [frame, placeholder] : children) { 1230 if (nsLayoutUtils::CompareTreePosition( 1231 placeholder, firstNewFrame, firstNewFrameAncestors, 1232 notCommonAncestor ? containingBlock : nullptr) > 0) { 1233 break; 1234 } 1235 insertionPoint = frame; 1236 } 1237 break; 1238 } 1239 } 1240 mFrameConstructor->InsertFrames(containingBlock, aChildListID, 1241 insertionPoint, std::move(aFrameList)); 1242 } 1243 } 1244 1245 MOZ_ASSERT(aFrameList.IsEmpty(), "How did that happen?"); 1246 } 1247 1248 nsFrameConstructorSaveState::~nsFrameConstructorSaveState() { 1249 // Restore the state 1250 if (mList) { 1251 MOZ_ASSERT(mState, "Can't have mList set without having a state!"); 1252 mState->ProcessFrameInsertions(*mList, mChildListID); 1253 1254 if (mList == &mState->mAbsoluteList) { 1255 mState->mAbsoluteList = std::move(mSavedList); 1256 mState->mFixedList = mSavedFixedList; 1257 } else { 1258 mState->mFloatedList = std::move(mSavedList); 1259 } 1260 1261 MOZ_ASSERT(mSavedList.IsEmpty(), 1262 "Frames in mSavedList should've moved back into mState!"); 1263 MOZ_ASSERT(!mList->LastChild() || !mList->LastChild()->GetNextSibling(), 1264 "Something corrupted our list!"); 1265 } 1266 } 1267 1268 /** 1269 * Moves aFrameList from aOldParent to aNewParent. This updates the parent 1270 * pointer of the frames in the list, and reparents their views as needed. 1271 * nsIFrame::SetParent sets the NS_FRAME_HAS_VIEW bit on aNewParent and its 1272 * ancestors as needed. Then it sets the list as the initial child list 1273 * on aNewParent, unless aNewParent either already has kids or has been 1274 * reflowed; in that case it appends the new frames. Note that this 1275 * method differs from ReparentFrames in that it doesn't change the kids' 1276 * style. 1277 */ 1278 // XXXbz Since this is only used for {ib} splits, could we just copy the view 1279 // bits from aOldParent to aNewParent and then use the 1280 // nsFrameList::ApplySetParent? That would still leave us doing two passes 1281 // over the list, of course; if we really wanted to we could factor out the 1282 // relevant part of ReparentFrameViewList, I suppose... Or just get rid of 1283 // views, which would make most of this function go away. 1284 static void MoveChildrenTo(nsIFrame* aOldParent, nsContainerFrame* aNewParent, 1285 nsFrameList& aFrameList) { 1286 aFrameList.ApplySetParent(aNewParent); 1287 1288 if (aNewParent->PrincipalChildList().IsEmpty() && 1289 aNewParent->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { 1290 aNewParent->SetInitialChildList(FrameChildListID::Principal, 1291 std::move(aFrameList)); 1292 } else { 1293 aNewParent->AppendFrames(FrameChildListID::Principal, 1294 std::move(aFrameList)); 1295 } 1296 } 1297 1298 static void EnsureAutoPageName(nsFrameConstructorState& aState, 1299 const nsContainerFrame* const aFrame) { 1300 // Check if we need to figure out our used page name. 1301 // When building the entire document, this should only happen for the 1302 // root, which will mean the loop will immediately end. Either way, this will 1303 // only happen once for each time the frame constructor is run. 1304 if (aState.mAutoPageNameValue) { 1305 return; 1306 } 1307 1308 for (const nsContainerFrame* frame = aFrame; frame; 1309 frame = frame->GetParent()) { 1310 if (const nsAtom* maybePageName = frame->GetStylePageName()) { 1311 aState.mAutoPageNameValue = maybePageName; 1312 return; 1313 } 1314 } 1315 // Ensure that a root with `page: auto` gets an empty page name 1316 // https://drafts.csswg.org/css-page-3/#using-named-pages 1317 aState.mAutoPageNameValue = nsGkAtoms::_empty; 1318 } 1319 1320 nsCSSFrameConstructor::AutoFrameConstructionPageName:: 1321 AutoFrameConstructionPageName(nsFrameConstructorState& aState, 1322 nsIFrame* const aFrame) 1323 : mState(aState), mNameToRestore(nullptr) { 1324 if (!aState.mPresContext->IsPaginated()) { 1325 MOZ_ASSERT(!aState.mAutoPageNameValue, 1326 "Page name should not have been set"); 1327 return; 1328 } 1329 #ifdef DEBUG 1330 MOZ_ASSERT(!aFrame->mWasVisitedByAutoFrameConstructionPageName, 1331 "Frame should only have been visited once"); 1332 aFrame->mWasVisitedByAutoFrameConstructionPageName = true; 1333 #endif 1334 1335 EnsureAutoPageName(aState, aFrame->GetParent()); 1336 mNameToRestore = aState.mAutoPageNameValue; 1337 1338 MOZ_ASSERT(mNameToRestore, 1339 "Page name should have been found by EnsureAutoPageName"); 1340 if (const nsAtom* maybePageName = aFrame->GetStylePageName()) { 1341 aState.mAutoPageNameValue = maybePageName; 1342 } 1343 aFrame->SetAutoPageValue(aState.mAutoPageNameValue); 1344 } 1345 1346 nsCSSFrameConstructor::AutoFrameConstructionPageName:: 1347 ~AutoFrameConstructionPageName() { 1348 // This isn't actually useful when not in paginated layout, but it's very 1349 // likely cheaper to unconditionally write this pointer than to test for 1350 // paginated layout and then branch on the result. 1351 mState.mAutoPageNameValue = mNameToRestore; 1352 } 1353 1354 //---------------------------------------------------------------------- 1355 1356 nsCSSFrameConstructor::nsCSSFrameConstructor(Document* aDocument, 1357 PresShell* aPresShell) 1358 : nsFrameManager(aPresShell), 1359 mDocument(aDocument), 1360 mFirstFreeFCItem(nullptr), 1361 mFCItemsInUse(0), 1362 mCurrentDepth(0), 1363 mQuotesDirty(false), 1364 mCountersDirty(false), 1365 mAlwaysCreateFramesForIgnorableWhitespace(false), 1366 mRemovingContent(false) { 1367 #ifdef DEBUG 1368 static bool gFirstTime = true; 1369 if (gFirstTime) { 1370 gFirstTime = false; 1371 char* flags = PR_GetEnv("GECKO_FRAMECTOR_DEBUG_FLAGS"); 1372 if (flags) { 1373 bool error = false; 1374 for (;;) { 1375 char* comma = strchr(flags, ','); 1376 if (comma) *comma = '\0'; 1377 1378 bool found = false; 1379 FrameCtorDebugFlags* flag = gFrameCtorDebugFlags; 1380 FrameCtorDebugFlags* limit = gFrameCtorDebugFlags + NUM_DEBUG_FLAGS; 1381 while (flag < limit) { 1382 if (nsCRT::strcasecmp(flag->name, flags) == 0) { 1383 *(flag->on) = true; 1384 printf("nsCSSFrameConstructor: setting %s debug flag on\n", 1385 flag->name); 1386 found = true; 1387 break; 1388 } 1389 ++flag; 1390 } 1391 1392 if (!found) error = true; 1393 1394 if (!comma) break; 1395 1396 *comma = ','; 1397 flags = comma + 1; 1398 } 1399 1400 if (error) { 1401 printf("Here are the available GECKO_FRAMECTOR_DEBUG_FLAGS:\n"); 1402 FrameCtorDebugFlags* flag = gFrameCtorDebugFlags; 1403 FrameCtorDebugFlags* limit = gFrameCtorDebugFlags + NUM_DEBUG_FLAGS; 1404 while (flag < limit) { 1405 printf(" %s\n", flag->name); 1406 ++flag; 1407 } 1408 printf( 1409 "Note: GECKO_FRAMECTOR_DEBUG_FLAGS is a comma separated list of " 1410 "flag\n"); 1411 printf("names (no whitespace)\n"); 1412 } 1413 } 1414 } 1415 #endif 1416 } 1417 1418 void nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame) { 1419 if (aFrame->StyleDisplay()->IsContainStyle()) { 1420 mContainStyleScopeManager.DestroyScopesFor(aFrame); 1421 } 1422 1423 if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) && 1424 mContainStyleScopeManager.DestroyQuoteNodesFor(aFrame)) { 1425 QuotesDirty(); 1426 } 1427 1428 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE) && 1429 mContainStyleScopeManager.DestroyCounterNodesFor(aFrame)) { 1430 // Technically we don't need to update anything if we destroyed only 1431 // USE nodes. However, this is unlikely to happen in the real world 1432 // since USE nodes generally go along with INCREMENT nodes. 1433 CountersDirty(); 1434 } 1435 1436 RestyleManager()->NotifyDestroyingFrame(aFrame); 1437 } 1438 1439 struct nsGenConInitializer { 1440 UniquePtr<nsGenConNode> mNode; 1441 nsGenConList* mList; 1442 void (nsCSSFrameConstructor::*mDirtyAll)(); 1443 1444 nsGenConInitializer(UniquePtr<nsGenConNode> aNode, nsGenConList* aList, 1445 void (nsCSSFrameConstructor::*aDirtyAll)()) 1446 : mNode(std::move(aNode)), mList(aList), mDirtyAll(aDirtyAll) {} 1447 }; 1448 1449 already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGenConTextNode( 1450 nsFrameConstructorState& aState, const nsAString& aString, 1451 UniquePtr<nsGenConInitializer> aInitializer) { 1452 RefPtr<nsTextNode> content = new (mDocument->NodeInfoManager()) 1453 nsTextNode(mDocument->NodeInfoManager()); 1454 content->SetText(aString, false); 1455 if (aInitializer) { 1456 aInitializer->mNode->mText = content; 1457 content->SetProperty(nsGkAtoms::genConInitializerProperty, 1458 aInitializer.release(), 1459 nsINode::DeleteProperty<nsGenConInitializer>); 1460 aState.mGeneratedContentWithInitializer.AppendElement(content); 1461 } 1462 return content.forget(); 1463 } 1464 1465 void nsCSSFrameConstructor::CreateGeneratedContent( 1466 nsFrameConstructorState& aState, Element& aOriginatingElement, 1467 ComputedStyle& aPseudoStyle, const StyleContentItem& aItem, 1468 size_t aContentIndex, const FunctionRef<void(nsIContent*)> aAddChild) { 1469 using Type = StyleContentItem::Tag; 1470 // Get the content value 1471 const Type type = aItem.tag; 1472 1473 switch (type) { 1474 case Type::Image: { 1475 RefPtr c = GeneratedImageContent::Create(*mDocument, aContentIndex); 1476 aAddChild(c); 1477 return; 1478 } 1479 1480 case Type::String: { 1481 const auto string = aItem.AsString().AsString(); 1482 if (string.IsEmpty()) { 1483 return; 1484 } 1485 RefPtr text = 1486 CreateGenConTextNode(aState, NS_ConvertUTF8toUTF16(string), nullptr); 1487 aAddChild(text); 1488 return; 1489 } 1490 1491 case Type::Attr: { 1492 const auto& attr = aItem.AsAttr(); 1493 RefPtr<nsAtom> attrName = attr.attribute.AsAtom(); 1494 int32_t attrNameSpace = kNameSpaceID_None; 1495 RefPtr<nsAtom> ns = attr.namespace_url.AsAtom(); 1496 if (!ns->IsEmpty()) { 1497 nsresult rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace( 1498 ns.forget(), attrNameSpace); 1499 NS_ENSURE_SUCCESS_VOID(rv); 1500 } 1501 1502 if (mDocument->IsHTMLDocument() && aOriginatingElement.IsHTMLElement()) { 1503 ToLowerCaseASCII(attrName); 1504 } 1505 1506 RefPtr<nsAtom> fallback = attr.fallback.AsAtom(); 1507 1508 nsCOMPtr<nsIContent> content; 1509 NS_NewAttributeContent(mDocument->NodeInfoManager(), attrNameSpace, 1510 attrName, fallback, getter_AddRefs(content)); 1511 aAddChild(content); 1512 return; 1513 } 1514 1515 case Type::Counter: 1516 case Type::Counters: { 1517 RefPtr<nsAtom> name; 1518 const StyleCounterStyle* style; 1519 nsString separator; 1520 if (type == Type::Counter) { 1521 const auto& counter = aItem.AsCounter(); 1522 name = counter._0.AsAtom(); 1523 style = &counter._1; 1524 } else { 1525 const auto& counters = aItem.AsCounters(); 1526 name = counters._0.AsAtom(); 1527 CopyUTF8toUTF16(counters._1.AsString(), separator); 1528 style = &counters._2; 1529 } 1530 1531 auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList( 1532 aOriginatingElement, name); 1533 auto node = MakeUnique<nsCounterUseNode>( 1534 *style, std::move(separator), aContentIndex, 1535 /* aAllCounters = */ type == Type::Counters); 1536 1537 auto initializer = MakeUnique<nsGenConInitializer>( 1538 std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty); 1539 RefPtr c = CreateGenConTextNode(aState, u""_ns, std::move(initializer)); 1540 aAddChild(c); 1541 return; 1542 } 1543 case Type::OpenQuote: 1544 case Type::CloseQuote: 1545 case Type::NoOpenQuote: 1546 case Type::NoCloseQuote: { 1547 auto node = MakeUnique<nsQuoteNode>(type, aContentIndex); 1548 auto* quoteList = 1549 mContainStyleScopeManager.QuoteListFor(aOriginatingElement); 1550 auto initializer = MakeUnique<nsGenConInitializer>( 1551 std::move(node), quoteList, &nsCSSFrameConstructor::QuotesDirty); 1552 RefPtr c = CreateGenConTextNode(aState, u""_ns, std::move(initializer)); 1553 aAddChild(c); 1554 return; 1555 } 1556 1557 case Type::MozLabelContent: { 1558 nsAutoString accesskey; 1559 if (!aOriginatingElement.GetAttr(nsGkAtoms::accesskey, accesskey) || 1560 accesskey.IsEmpty() || !LookAndFeel::GetMenuAccessKey()) { 1561 // Easy path: just return a regular value attribute content. 1562 nsCOMPtr<nsIContent> content; 1563 NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None, 1564 nsGkAtoms::value, nsGkAtoms::_empty, 1565 getter_AddRefs(content)); 1566 aAddChild(content); 1567 return; 1568 } 1569 1570 nsAutoString value; 1571 aOriginatingElement.GetAttr(nsGkAtoms::value, value); 1572 1573 auto AppendAccessKeyLabel = [&] { 1574 // Always append accesskey text in uppercase, see bug 1806167. 1575 ToUpperCase(accesskey); 1576 nsAutoString accessKeyLabel = u"("_ns + accesskey + u")"_ns; 1577 if (!StringEndsWith(value, accessKeyLabel)) { 1578 if (InsertSeparatorBeforeAccessKey() && !value.IsEmpty() && 1579 !NS_IS_SPACE(value.Last())) { 1580 value.Append(' '); 1581 } 1582 value.Append(accessKeyLabel); 1583 } 1584 }; 1585 if (AlwaysAppendAccessKey()) { 1586 AppendAccessKeyLabel(); 1587 RefPtr c = CreateGenConTextNode(aState, value, nullptr); 1588 aAddChild(c); 1589 return; 1590 } 1591 1592 const auto accessKeyStart = [&]() -> Maybe<size_t> { 1593 nsAString::const_iterator start, end; 1594 value.BeginReading(start); 1595 value.EndReading(end); 1596 1597 const auto originalStart = start; 1598 // not appending access key - do case-sensitive search 1599 // first 1600 bool found = true; 1601 if (!FindInReadable(accesskey, start, end)) { 1602 start = originalStart; 1603 // didn't find it - perform a case-insensitive search 1604 found = FindInReadable(accesskey, start, end, 1605 nsCaseInsensitiveStringComparator); 1606 } 1607 if (!found) { 1608 return Nothing(); 1609 } 1610 return Some(Distance(originalStart, start)); 1611 }(); 1612 1613 if (accessKeyStart.isNothing()) { 1614 AppendAccessKeyLabel(); 1615 RefPtr c = CreateGenConTextNode(aState, value, nullptr); 1616 aAddChild(c); 1617 return; 1618 } 1619 1620 if (*accessKeyStart != 0) { 1621 RefPtr beginning = CreateGenConTextNode( 1622 aState, Substring(value, 0, *accessKeyStart), nullptr); 1623 aAddChild(beginning); 1624 } 1625 1626 { 1627 RefPtr accessKeyText = CreateGenConTextNode( 1628 aState, Substring(value, *accessKeyStart, accesskey.Length()), 1629 nullptr); 1630 RefPtr<nsIContent> underline = 1631 mDocument->CreateHTMLElement(nsGkAtoms::u); 1632 underline->AppendChildTo(accessKeyText, /* aNotify = */ false, 1633 IgnoreErrors()); 1634 aAddChild(underline); 1635 } 1636 1637 size_t accessKeyEnd = *accessKeyStart + accesskey.Length(); 1638 if (accessKeyEnd != value.Length()) { 1639 RefPtr valueEnd = CreateGenConTextNode( 1640 aState, Substring(value, *accessKeyStart + accesskey.Length()), 1641 nullptr); 1642 aAddChild(valueEnd); 1643 } 1644 break; 1645 } 1646 case Type::MozAltContent: { 1647 // Use the "alt" attribute; if that fails and the node is an HTML 1648 // <input>, try the value attribute and then fall back to some default 1649 // localized text we have. 1650 // XXX what if the 'alt' attribute is added later, how will we 1651 // detect that and do the right thing here? 1652 if (aOriginatingElement.HasAttr(nsGkAtoms::alt)) { 1653 nsCOMPtr<nsIContent> content; 1654 NS_NewAttributeContent(mDocument->NodeInfoManager(), kNameSpaceID_None, 1655 nsGkAtoms::alt, nsGkAtoms::_empty, 1656 getter_AddRefs(content)); 1657 aAddChild(content); 1658 return; 1659 } 1660 1661 if (aOriginatingElement.IsHTMLElement(nsGkAtoms::input)) { 1662 if (aOriginatingElement.HasAttr(nsGkAtoms::value)) { 1663 nsCOMPtr<nsIContent> content; 1664 NS_NewAttributeContent(mDocument->NodeInfoManager(), 1665 kNameSpaceID_None, nsGkAtoms::value, 1666 nsGkAtoms::_empty, getter_AddRefs(content)); 1667 aAddChild(content); 1668 return; 1669 } 1670 1671 nsAutoString temp; 1672 nsContentUtils::GetMaybeLocalizedString( 1673 nsContentUtils::eFORMS_PROPERTIES, "Submit", mDocument, temp); 1674 RefPtr c = CreateGenConTextNode(aState, temp, nullptr); 1675 aAddChild(c); 1676 return; 1677 } 1678 break; 1679 } 1680 } 1681 } 1682 1683 void nsCSSFrameConstructor::CreateGeneratedContentFromListStyle( 1684 nsFrameConstructorState& aState, Element& aOriginatingElement, 1685 const ComputedStyle& aPseudoStyle, 1686 const FunctionRef<void(nsIContent*)> aAddChild) { 1687 const nsStyleList* styleList = aPseudoStyle.StyleList(); 1688 if (!styleList->mListStyleImage.IsNone()) { 1689 RefPtr<nsIContent> child = 1690 GeneratedImageContent::CreateForListStyleImage(*mDocument); 1691 aAddChild(child); 1692 child = CreateGenConTextNode(aState, u" "_ns, nullptr); 1693 aAddChild(child); 1694 return; 1695 } 1696 CreateGeneratedContentFromListStyleType(aState, aOriginatingElement, 1697 aPseudoStyle, aAddChild); 1698 } 1699 1700 void nsCSSFrameConstructor::CreateGeneratedContentFromListStyleType( 1701 nsFrameConstructorState& aState, Element& aOriginatingElement, 1702 const ComputedStyle& aPseudoStyle, 1703 const FunctionRef<void(nsIContent*)> aAddChild) { 1704 using Tag = StyleCounterStyle::Tag; 1705 const auto& styleType = aPseudoStyle.StyleList()->mListStyleType; 1706 switch (styleType.tag) { 1707 case Tag::None: 1708 return; 1709 case Tag::String: { 1710 nsDependentAtomString string(styleType.AsString().AsAtom()); 1711 RefPtr<nsIContent> child = CreateGenConTextNode(aState, string, nullptr); 1712 aAddChild(child); 1713 return; 1714 } 1715 case Tag::Name: 1716 case Tag::Symbols: 1717 break; 1718 } 1719 1720 auto node = MakeUnique<nsCounterUseNode>(nsCounterUseNode::ForLegacyBullet, 1721 styleType); 1722 if (styleType.IsName()) { 1723 nsAtom* name = styleType.AsName().AsAtom(); 1724 if (name == nsGkAtoms::disc || name == nsGkAtoms::circle || 1725 name == nsGkAtoms::square || name == nsGkAtoms::disclosure_closed || 1726 name == nsGkAtoms::disclosure_open) { 1727 // We don't need a use node inserted for these. 1728 CounterStyle* counterStyle = mPresShell->GetPresContext() 1729 ->CounterStyleManager() 1730 ->ResolveCounterStyle(name); 1731 nsAutoString text; 1732 node->GetText(WritingMode(&aPseudoStyle), counterStyle, text); 1733 // Note that we're done with 'node' in this case. It's not inserted into 1734 // any list so it's deleted when we return. 1735 RefPtr<nsIContent> child = CreateGenConTextNode(aState, text, nullptr); 1736 aAddChild(child); 1737 return; 1738 } 1739 } 1740 1741 auto* counterList = mContainStyleScopeManager.GetOrCreateCounterList( 1742 aOriginatingElement, nsGkAtoms::list_item); 1743 auto initializer = MakeUnique<nsGenConInitializer>( 1744 std::move(node), counterList, &nsCSSFrameConstructor::CountersDirty); 1745 RefPtr<nsIContent> child = 1746 CreateGenConTextNode(aState, EmptyString(), std::move(initializer)); 1747 aAddChild(child); 1748 } 1749 1750 // Frames for these may not be leaves in the proper sense, but we still don't 1751 // want to expose generated content on them. For the purposes of the page they 1752 // should be leaves. 1753 static bool HasUAWidget(const Element& aOriginatingElement) { 1754 const ShadowRoot* sr = aOriginatingElement.GetShadowRoot(); 1755 return sr && sr->IsUAWidget(); 1756 } 1757 1758 /* 1759 * aParentFrame - the frame that should be the parent of the generated 1760 * content. This is the frame for the corresponding content node, 1761 * which must not be a leaf frame. 1762 * 1763 * Any items created are added to aItems. 1764 * 1765 * We create an XML element (tag _moz_generated_content_before/after/marker) 1766 * representing the pseudoelement. We create a DOM node for each 'content' 1767 * item and make those nodes the children of the XML element. Then we create 1768 * a frame subtree for the XML element as if it were a regular child of 1769 * aParentFrame/aParentContent, giving the XML element the ::before, ::after 1770 * or ::marker style. 1771 */ 1772 void nsCSSFrameConstructor::CreateGeneratedContentItem( 1773 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame, 1774 Element& aOriginatingElement, ComputedStyle& aStyle, 1775 PseudoStyleType aPseudoElement, FrameConstructionItemList& aItems, 1776 ItemFlags aExtraFlags) { 1777 MOZ_ASSERT(aPseudoElement == PseudoStyleType::before || 1778 aPseudoElement == PseudoStyleType::after || 1779 aPseudoElement == PseudoStyleType::marker || 1780 aPseudoElement == PseudoStyleType::backdrop, 1781 "unexpected aPseudoElement"); 1782 1783 if (aPseudoElement != PseudoStyleType::backdrop && 1784 HasUAWidget(aOriginatingElement) && 1785 !aOriginatingElement.IsHTMLElement(nsGkAtoms::details)) { 1786 // ::before / ::after / ::marker shouldn't work on <video> / <input>. 1787 return; 1788 } 1789 1790 ServoStyleSet* styleSet = mPresShell->StyleSet(); 1791 1792 // Probe for the existence of the pseudo-element. 1793 // |ProbePseudoElementStyle| checks the relevant properties for the pseudo. 1794 // It only returns a non-null value if the pseudo should exist. 1795 RefPtr<ComputedStyle> pseudoStyle = styleSet->ProbePseudoElementStyle( 1796 aOriginatingElement, aPseudoElement, nullptr, &aStyle); 1797 if (!pseudoStyle) { 1798 return; 1799 } 1800 1801 nsAtom* elemName = nullptr; 1802 nsAtom* property = nullptr; 1803 switch (aPseudoElement) { 1804 case PseudoStyleType::before: 1805 elemName = nsGkAtoms::mozgeneratedcontentbefore; 1806 property = nsGkAtoms::beforePseudoProperty; 1807 break; 1808 case PseudoStyleType::after: 1809 elemName = nsGkAtoms::mozgeneratedcontentafter; 1810 property = nsGkAtoms::afterPseudoProperty; 1811 break; 1812 case PseudoStyleType::marker: 1813 // We want to get a marker style even if we match no rules, but we still 1814 // want to check the result of GeneratedContentPseudoExists. 1815 elemName = nsGkAtoms::mozgeneratedcontentmarker; 1816 property = nsGkAtoms::markerPseudoProperty; 1817 break; 1818 case PseudoStyleType::backdrop: 1819 elemName = nsGkAtoms::mozgeneratedcontentbackdrop; 1820 property = nsGkAtoms::backdropPseudoProperty; 1821 break; 1822 default: 1823 MOZ_ASSERT_UNREACHABLE("unexpected aPseudoElement"); 1824 } 1825 1826 RefPtr<NodeInfo> nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo( 1827 elemName, nullptr, kNameSpaceID_None, nsINode::ELEMENT_NODE); 1828 RefPtr<Element> container; 1829 nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget()); 1830 if (NS_FAILED(rv)) { 1831 return; 1832 } 1833 1834 // Cleared when the pseudo is unbound from the tree, so no need to store a 1835 // strong reference, nor a destructor. 1836 aOriginatingElement.SetProperty(property, container.get()); 1837 1838 container->SetIsNativeAnonymousRoot(); 1839 container->SetPseudoElementType(aPseudoElement); 1840 1841 BindContext context(aOriginatingElement, BindContext::ForNativeAnonymous); 1842 rv = container->BindToTree(context, aOriginatingElement); 1843 if (NS_FAILED(rv)) { 1844 container->UnbindFromTree(); 1845 return; 1846 } 1847 1848 if (mDocument->DevToolsAnonymousAndShadowEventsEnabled()) { 1849 container->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ false); 1850 } 1851 1852 // Servo has already eagerly computed the style for the container, so we can 1853 // just stick the style on the element and avoid an additional traversal. 1854 // 1855 // We don't do this for pseudos that may trigger animations or transitions, 1856 // since those need to be kicked off by the traversal machinery. 1857 // 1858 // Note that when a pseudo-element animates, we flag the originating element, 1859 // so we check that flag, but we could also a more expensive (but exhaustive) 1860 // check using EffectSet::GetEffectSet, for example. 1861 if (!Servo_ComputedValues_SpecifiesAnimationsOrTransitions(pseudoStyle) && 1862 !aOriginatingElement.MayHaveAnimations()) { 1863 Servo_SetExplicitStyle(container, pseudoStyle); 1864 } else { 1865 // If animations are involved, we avoid the SetExplicitStyle optimization 1866 // above. We need to grab style with animations from the pseudo element and 1867 // replace old one. 1868 mPresShell->StyleSet()->StyleNewSubtree(container); 1869 pseudoStyle = ServoStyleSet::ResolveServoStyle(*container); 1870 } 1871 if (aPseudoElement != PseudoStyleType::backdrop) { 1872 auto AppendChild = [&container, this](nsIContent* aChild) { 1873 // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE 1874 // here; it would get set under AppendChildTo. But AppendChildTo might 1875 // think that we're going from not being anonymous to being anonymous and 1876 // do some extra work; setting the flag here avoids that. 1877 aChild->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); 1878 container->AppendChildTo(aChild, false, IgnoreErrors()); 1879 if (auto* childElement = Element::FromNode(aChild)) { 1880 // If we created any children elements, Servo needs to traverse them, 1881 // but the root is already set up. 1882 mPresShell->StyleSet()->StyleNewSubtree(childElement); 1883 } 1884 }; 1885 auto items = pseudoStyle->StyleContent()->NonAltContentItems(); 1886 size_t index = 0; 1887 for (const auto& item : items) { 1888 CreateGeneratedContent(aState, aOriginatingElement, *pseudoStyle, item, 1889 index++, AppendChild); 1890 } 1891 // If a ::marker has no 'content' then generate it from its 'list-style-*'. 1892 if (index == 0 && aPseudoElement == PseudoStyleType::marker) { 1893 CreateGeneratedContentFromListStyle(aState, aOriginatingElement, 1894 *pseudoStyle, AppendChild); 1895 } 1896 } 1897 auto flags = ItemFlags{ItemFlag::IsGeneratedContent} + aExtraFlags; 1898 AddFrameConstructionItemsInternal(aState, container, aParentFrame, true, 1899 pseudoStyle, flags, aItems); 1900 } 1901 1902 /**************************************************** 1903 ** BEGIN TABLE SECTION 1904 ****************************************************/ 1905 1906 // The term pseudo frame is being used instead of anonymous frame, since 1907 // anonymous frame has been used elsewhere to refer to frames that have 1908 // generated content 1909 1910 // Return whether the given frame is a table pseudo-frame. Note that 1911 // cell-content and table-outer frames have pseudo-types, but are always 1912 // created, even for non-anonymous cells and tables respectively. So for those 1913 // we have to examine the cell or table frame to see whether it's a pseudo 1914 // frame. In particular, a lone table caption will have a table wrapper as its 1915 // parent, but will also trigger construction of an empty inner table, which 1916 // will be the one we can examine to see whether the wrapper was a pseudo-frame. 1917 static bool IsTablePseudo(nsIFrame* aFrame) { 1918 auto pseudoType = aFrame->Style()->GetPseudoType(); 1919 if (pseudoType == PseudoStyleType::NotPseudo) { 1920 return false; 1921 } 1922 return pseudoType == PseudoStyleType::table || 1923 pseudoType == PseudoStyleType::inlineTable || 1924 pseudoType == PseudoStyleType::tableColGroup || 1925 pseudoType == PseudoStyleType::tableRowGroup || 1926 pseudoType == PseudoStyleType::tableRow || 1927 pseudoType == PseudoStyleType::tableCell || 1928 (pseudoType == PseudoStyleType::cellContent && 1929 aFrame->GetParent()->Style()->GetPseudoType() == 1930 PseudoStyleType::tableCell) || 1931 (pseudoType == PseudoStyleType::tableWrapper && 1932 static_cast<nsTableWrapperFrame*>(aFrame) 1933 ->InnerTableFrame() 1934 ->Style() 1935 ->IsPseudoOrAnonBox()); 1936 } 1937 1938 static bool IsRubyPseudo(nsIFrame* aFrame) { 1939 return RubyUtils::IsRubyPseudo(aFrame->Style()->GetPseudoType()); 1940 } 1941 1942 // Note that this is (subtly) different to ParentIsWrapperAnonBox, since 1943 // ParentIsWrapperAnonBox is really just about restyles, but there are wrapper 1944 // anon boxes that don't need to return true for that... 1945 // FIXME(emilio): This should be less complicated, ParentIsWrapperAnonBox should 1946 // probably be renamed to something else, and this should just use 1947 // IsWrapperAnonBox or similar... 1948 static bool IsWrapperPseudo(nsIFrame* aFrame) { 1949 auto pseudoType = aFrame->Style()->GetPseudoType(); 1950 if (!PseudoStyle::IsAnonBox(pseudoType)) { 1951 return false; 1952 } 1953 return PseudoStyle::IsWrapperAnonBox(pseudoType) || IsTablePseudo(aFrame); 1954 } 1955 1956 static bool IsInAnonymousTable(nsIFrame* aFrame) { 1957 for (nsIFrame* f = aFrame; f; f = f->GetParent()) { 1958 if (!IsWrapperPseudo(f)) { 1959 return false; 1960 } 1961 if (f->IsTableWrapperFrame()) { 1962 return true; 1963 } 1964 } 1965 MOZ_ASSERT_UNREACHABLE("Expected to be called inside tables"); 1966 return false; 1967 } 1968 1969 /* static */ 1970 nsCSSFrameConstructor::ParentType nsCSSFrameConstructor::GetParentType( 1971 LayoutFrameType aFrameType) { 1972 if (aFrameType == LayoutFrameType::Table) { 1973 return eTypeTable; 1974 } 1975 if (aFrameType == LayoutFrameType::TableRowGroup) { 1976 return eTypeRowGroup; 1977 } 1978 if (aFrameType == LayoutFrameType::TableRow) { 1979 return eTypeRow; 1980 } 1981 if (aFrameType == LayoutFrameType::TableColGroup) { 1982 return eTypeColGroup; 1983 } 1984 if (aFrameType == LayoutFrameType::RubyBaseContainer) { 1985 return eTypeRubyBaseContainer; 1986 } 1987 if (aFrameType == LayoutFrameType::RubyTextContainer) { 1988 return eTypeRubyTextContainer; 1989 } 1990 if (aFrameType == LayoutFrameType::Ruby) { 1991 return eTypeRuby; 1992 } 1993 1994 return eTypeBlock; 1995 } 1996 1997 // Pull all the captions present in aItems out into aCaptions. 1998 static void PullOutCaptionFrames(nsFrameList& aList, nsFrameList& aCaptions) { 1999 nsIFrame* child = aList.FirstChild(); 2000 while (child) { 2001 nsIFrame* nextSibling = child->GetNextSibling(); 2002 if (child->StyleDisplay()->mDisplay == StyleDisplay::TableCaption) { 2003 aList.RemoveFrame(child); 2004 aCaptions.AppendFrame(nullptr, child); 2005 } 2006 child = nextSibling; 2007 } 2008 } 2009 2010 // Construct the outer, inner table frames and the children frames for the 2011 // table. 2012 // XXX Page break frames for pseudo table frames are not constructed to avoid 2013 // the risk associated with revising the pseudo frame mechanism. The long term 2014 // solution of having frames handle page-break-before/after will solve the 2015 // problem. 2016 nsIFrame* nsCSSFrameConstructor::ConstructTable(nsFrameConstructorState& aState, 2017 FrameConstructionItem& aItem, 2018 nsContainerFrame* aParentFrame, 2019 const nsStyleDisplay* aDisplay, 2020 nsFrameList& aFrameList) { 2021 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::Table || 2022 aDisplay->mDisplay == StyleDisplay::InlineTable, 2023 "Unexpected call"); 2024 2025 nsIContent* const content = aItem.mContent; 2026 ComputedStyle* const computedStyle = aItem.mComputedStyle; 2027 const bool isMathMLContent = content->IsMathMLElement(); 2028 2029 // create the pseudo SC for the table wrapper as a child of the inner SC 2030 RefPtr<ComputedStyle> outerComputedStyle = 2031 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 2032 PseudoStyleType::tableWrapper, computedStyle); 2033 2034 // Create the table wrapper frame which holds the caption and inner table 2035 // frame 2036 nsContainerFrame* newFrame; 2037 if (isMathMLContent) { 2038 newFrame = NS_NewMathMLmtableOuterFrame(mPresShell, outerComputedStyle); 2039 } else { 2040 newFrame = NS_NewTableWrapperFrame(mPresShell, outerComputedStyle); 2041 } 2042 2043 nsContainerFrame* geometricParent = aState.GetGeometricParent( 2044 *outerComputedStyle->StyleDisplay(), aParentFrame); 2045 2046 // Init the table wrapper frame 2047 InitAndRestoreFrame(aState, content, geometricParent, newFrame); 2048 2049 // Create the inner table frame 2050 nsContainerFrame* innerFrame; 2051 if (isMathMLContent) { 2052 innerFrame = NS_NewMathMLmtableFrame(mPresShell, computedStyle); 2053 } else { 2054 innerFrame = NS_NewTableFrame(mPresShell, computedStyle); 2055 } 2056 2057 InitAndRestoreFrame(aState, content, newFrame, innerFrame); 2058 innerFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); 2059 2060 // Put the newly created frames into the right child list 2061 SetInitialSingleChild(newFrame, innerFrame); 2062 2063 aState.AddChild(newFrame, aFrameList, content, aParentFrame); 2064 2065 if (!mRootElementFrame) { 2066 mRootElementFrame = newFrame; 2067 } 2068 2069 nsFrameList childList; 2070 2071 // Process children 2072 nsFrameConstructorSaveState absoluteSaveState; 2073 2074 // Mark the table frame as an absolute container if needed 2075 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 2076 if (newFrame->IsAbsPosContainingBlock()) { 2077 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState); 2078 } 2079 2080 nsFrameConstructorSaveState floatSaveState; 2081 aState.MaybePushFloatContainingBlock(innerFrame, floatSaveState); 2082 2083 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) { 2084 ConstructFramesFromItemList( 2085 aState, aItem.mChildItems, innerFrame, 2086 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList); 2087 } else { 2088 ProcessChildren(aState, content, computedStyle, innerFrame, true, childList, 2089 false); 2090 } 2091 2092 nsFrameList captionList; 2093 PullOutCaptionFrames(childList, captionList); 2094 2095 // Set the inner table frame's principal child list. 2096 innerFrame->SetInitialChildList(FrameChildListID::Principal, 2097 std::move(childList)); 2098 2099 // Append caption frames to the table wrapper frame's principal child list. 2100 if (captionList.NotEmpty()) { 2101 captionList.ApplySetParent(newFrame); 2102 newFrame->AppendFrames(FrameChildListID::Principal, std::move(captionList)); 2103 } 2104 2105 return newFrame; 2106 } 2107 2108 static void MakeTablePartAbsoluteContainingBlock( 2109 nsFrameConstructorState& aState, nsFrameConstructorSaveState& aAbsSaveState, 2110 nsContainerFrame* aFrame) { 2111 // If we're positioned, then we need to become an absolute containing block 2112 // for any absolutely positioned children. 2113 aFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 2114 if (aFrame->IsAbsPosContainingBlock()) { 2115 aState.PushAbsoluteContainingBlock(aFrame, aFrame, aAbsSaveState); 2116 } 2117 } 2118 2119 nsIFrame* nsCSSFrameConstructor::ConstructTableRowOrRowGroup( 2120 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 2121 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay, 2122 nsFrameList& aFrameList) { 2123 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableRow || 2124 aDisplay->mDisplay == StyleDisplay::TableRowGroup || 2125 aDisplay->mDisplay == StyleDisplay::TableFooterGroup || 2126 aDisplay->mDisplay == StyleDisplay::TableHeaderGroup, 2127 "Not a row or row group"); 2128 MOZ_ASSERT(aItem.mComputedStyle->StyleDisplay() == aDisplay, 2129 "Display style doesn't match style"); 2130 nsIContent* const content = aItem.mContent; 2131 ComputedStyle* const computedStyle = aItem.mComputedStyle; 2132 2133 nsContainerFrame* newFrame; 2134 if (aDisplay->mDisplay == StyleDisplay::TableRow) { 2135 if (content->IsMathMLElement()) { 2136 newFrame = NS_NewMathMLmtrFrame(mPresShell, computedStyle); 2137 } else { 2138 newFrame = NS_NewTableRowFrame(mPresShell, computedStyle); 2139 } 2140 } else { 2141 newFrame = NS_NewTableRowGroupFrame(mPresShell, computedStyle); 2142 } 2143 2144 InitAndRestoreFrame(aState, content, aParentFrame, newFrame); 2145 2146 nsFrameConstructorSaveState absoluteSaveState; 2147 MakeTablePartAbsoluteContainingBlock(aState, absoluteSaveState, newFrame); 2148 2149 nsFrameConstructorSaveState floatSaveState; 2150 aState.MaybePushFloatContainingBlock(newFrame, floatSaveState); 2151 2152 nsFrameList childList; 2153 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) { 2154 ConstructFramesFromItemList( 2155 aState, aItem.mChildItems, newFrame, 2156 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList); 2157 } else { 2158 ProcessChildren(aState, content, computedStyle, newFrame, true, childList, 2159 false); 2160 } 2161 2162 newFrame->SetInitialChildList(FrameChildListID::Principal, 2163 std::move(childList)); 2164 aFrameList.AppendFrame(nullptr, newFrame); 2165 return newFrame; 2166 } 2167 2168 nsIFrame* nsCSSFrameConstructor::ConstructTableCol( 2169 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 2170 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay, 2171 nsFrameList& aFrameList) { 2172 nsIContent* const content = aItem.mContent; 2173 ComputedStyle* const computedStyle = aItem.mComputedStyle; 2174 2175 nsTableColFrame* colFrame = NS_NewTableColFrame(mPresShell, computedStyle); 2176 InitAndRestoreFrame(aState, content, aParentFrame, colFrame); 2177 2178 NS_ASSERTION(colFrame->Style() == computedStyle, "Unexpected style"); 2179 2180 aFrameList.AppendFrame(nullptr, colFrame); 2181 2182 // construct additional col frames if the col frame has a span > 1 2183 int32_t span = colFrame->GetSpan(); 2184 for (int32_t spanX = 1; spanX < span; spanX++) { 2185 nsTableColFrame* newCol = NS_NewTableColFrame(mPresShell, computedStyle); 2186 InitAndRestoreFrame(aState, content, aParentFrame, newCol, false); 2187 aFrameList.LastChild()->SetNextContinuation(newCol); 2188 newCol->SetPrevContinuation(aFrameList.LastChild()); 2189 aFrameList.AppendFrame(nullptr, newCol); 2190 newCol->SetColType(eColAnonymousCol); 2191 } 2192 2193 return colFrame; 2194 } 2195 2196 nsIFrame* nsCSSFrameConstructor::ConstructTableCell( 2197 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 2198 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay, 2199 nsFrameList& aFrameList) { 2200 MOZ_ASSERT(aDisplay->mDisplay == StyleDisplay::TableCell, "Unexpected call"); 2201 2202 nsIContent* const content = aItem.mContent; 2203 ComputedStyle* const computedStyle = aItem.mComputedStyle; 2204 const bool isMathMLContent = content->IsMathMLElement(); 2205 2206 nsTableFrame* tableFrame = 2207 static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame(); 2208 nsContainerFrame* cellFrame; 2209 // <mtable> is border separate in mathml.css and the MathML code doesn't 2210 // implement border collapse. For those users who style <mtable> with border 2211 // collapse, give them the default non-MathML table frames that understand 2212 // border collapse. This won't break us because MathML table frames are all 2213 // subclasses of the default table code, and so we can freely mix <mtable> 2214 // with <mtr> or <tr>, <mtd> or <td>. What will happen is just that non-MathML 2215 // frames won't understand MathML attributes and will therefore miss the 2216 // special handling that the MathML code does. 2217 if (isMathMLContent && !tableFrame->IsBorderCollapse()) { 2218 cellFrame = NS_NewMathMLmtdFrame(mPresShell, computedStyle, tableFrame); 2219 } else { 2220 // Warning: If you change this and add a wrapper frame around table cell 2221 // frames, make sure Bug 368554 doesn't regress! 2222 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp. 2223 cellFrame = NS_NewTableCellFrame(mPresShell, computedStyle, tableFrame); 2224 } 2225 2226 // Initialize the table cell frame 2227 InitAndRestoreFrame(aState, content, aParentFrame, cellFrame); 2228 cellFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); 2229 2230 // Resolve pseudo style and initialize the body cell frame 2231 RefPtr<ComputedStyle> innerPseudoStyle = 2232 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 2233 PseudoStyleType::cellContent, computedStyle); 2234 2235 nsContainerFrame* cellInnerFrame; 2236 nsContainerFrame* scrollFrame = nullptr; 2237 bool isScrollable = false; 2238 // Create a block frame that will format the cell's content 2239 if (isMathMLContent) { 2240 cellInnerFrame = NS_NewMathMLmtdInnerFrame(mPresShell, innerPseudoStyle); 2241 } else { 2242 isScrollable = innerPseudoStyle->StyleDisplay()->IsScrollableOverflow() && 2243 !aState.mPresContext->IsPaginated() && 2244 StaticPrefs::layout_tables_scrollable_cells(); 2245 if (isScrollable) { 2246 innerPseudoStyle = BeginBuildingScrollContainerFrame( 2247 aState, content, innerPseudoStyle, cellFrame, 2248 PseudoStyleType::scrolledContent, false, scrollFrame); 2249 } 2250 cellInnerFrame = NS_NewBlockFrame(mPresShell, innerPseudoStyle); 2251 } 2252 auto* parent = scrollFrame ? scrollFrame : cellFrame; 2253 InitAndRestoreFrame(aState, content, parent, cellInnerFrame); 2254 2255 nsFrameConstructorSaveState absoluteSaveState; 2256 MakeTablePartAbsoluteContainingBlock(aState, absoluteSaveState, cellFrame); 2257 2258 nsFrameConstructorSaveState floatSaveState; 2259 aState.MaybePushFloatContainingBlock(cellInnerFrame, floatSaveState); 2260 2261 nsFrameList childList; 2262 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) { 2263 AutoFrameConstructionPageName pageNameTracker(aState, cellInnerFrame); 2264 ConstructFramesFromItemList( 2265 aState, aItem.mChildItems, cellInnerFrame, 2266 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList); 2267 } else { 2268 // Process the child content 2269 ProcessChildren(aState, content, computedStyle, cellInnerFrame, true, 2270 childList, !isMathMLContent); 2271 } 2272 2273 cellInnerFrame->SetInitialChildList(FrameChildListID::Principal, 2274 std::move(childList)); 2275 2276 if (isScrollable) { 2277 FinishBuildingScrollContainerFrame(scrollFrame, cellInnerFrame); 2278 } 2279 SetInitialSingleChild(cellFrame, scrollFrame ? scrollFrame : cellInnerFrame); 2280 aFrameList.AppendFrame(nullptr, cellFrame); 2281 return cellFrame; 2282 } 2283 2284 static inline bool NeedFrameFor(const nsFrameConstructorState& aState, 2285 nsContainerFrame* aParentFrame, 2286 nsIContent* aChildContent) { 2287 // XXX the GetContent() != aChildContent check is needed due to bug 135040. 2288 // Remove it once that's fixed. 2289 MOZ_ASSERT( 2290 !aChildContent->GetPrimaryFrame() || aState.mCreatingExtraFrames || 2291 aChildContent->GetPrimaryFrame()->GetContent() != aChildContent, 2292 "Why did we get called?"); 2293 2294 // don't create a whitespace frame if aParentFrame doesn't want it. 2295 // always create frames for children in generated content. counter(), 2296 // quotes, and attr() content can easily change dynamically and we don't 2297 // want to be reconstructing frames. It's not even clear that these 2298 // should be considered ignorable just because they evaluate to 2299 // whitespace. 2300 2301 // We could handle all this in CreateNeededPseudoContainers or some other 2302 // place after we build our frame construction items, but that would involve 2303 // creating frame construction items for whitespace kids that ignores 2304 // white-space, where we know we'll be dropping them all anyway, and involve 2305 // an extra walk down the frame construction item list. 2306 auto excludesIgnorableWhitespace = [](nsIFrame* aParentFrame) { 2307 return aParentFrame->IsMathMLFrame(); 2308 }; 2309 if (!aParentFrame || !excludesIgnorableWhitespace(aParentFrame) || 2310 aParentFrame->IsGeneratedContentFrame() || !aChildContent->IsText()) { 2311 return true; 2312 } 2313 2314 aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | 2315 NS_REFRAME_IF_WHITESPACE); 2316 return !aChildContent->TextIsOnlyWhitespace(); 2317 } 2318 2319 /*********************************************** 2320 * END TABLE SECTION 2321 ***********************************************/ 2322 2323 nsIFrame* nsCSSFrameConstructor::ConstructDocElementFrame( 2324 Element* aDocElement) { 2325 MOZ_ASSERT(GetRootFrame(), 2326 "No viewport? Someone forgot to call ConstructRootFrame!"); 2327 MOZ_ASSERT(!mDocElementContainingBlock, 2328 "Shouldn't have a doc element containing block here"); 2329 2330 // Resolve a new style for the viewport since it may be affected by a new root 2331 // element style (e.g. a propagated 'direction'). 2332 // 2333 // @see ComputedStyle::ApplyStyleFixups 2334 { 2335 RefPtr<ComputedStyle> sc = 2336 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 2337 PseudoStyleType::viewport, nullptr); 2338 GetRootFrame()->SetComputedStyleWithoutNotification(sc); 2339 } 2340 2341 // Ensure the document element is styled at this point. 2342 // FIXME(emilio, bug 1852735): This is only needed because of the sync frame 2343 // construction from PresShell::Initialize. 2344 if (!aDocElement->HasServoData()) { 2345 mPresShell->StyleSet()->StyleNewSubtree(aDocElement); 2346 } 2347 aDocElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME); 2348 2349 // Make sure to call UpdateViewportScrollStylesOverride before 2350 // SetUpDocElementContainingBlock, since it sets up our scrollbar state 2351 // properly. 2352 DebugOnly<nsIContent*> propagatedScrollFrom; 2353 if (nsPresContext* presContext = mPresShell->GetPresContext()) { 2354 propagatedScrollFrom = presContext->UpdateViewportScrollStylesOverride(); 2355 } 2356 2357 SetUpDocElementContainingBlock(aDocElement); 2358 2359 // This has the side-effect of getting `mFrameTreeState` from our docshell. 2360 // 2361 // FIXME(emilio): There may be a more sensible time to do this. 2362 if (!mFrameTreeState) { 2363 mPresShell->CaptureHistoryState(getter_AddRefs(mFrameTreeState)); 2364 } 2365 2366 NS_ASSERTION(mDocElementContainingBlock, "Should have parent by now"); 2367 nsFrameConstructorState state( 2368 mPresShell, 2369 GetAbsoluteContainingBlock(mDocElementContainingBlock, FIXED_POS), 2370 nullptr, nullptr, do_AddRef(mFrameTreeState)); 2371 2372 RefPtr<ComputedStyle> computedStyle = 2373 ServoStyleSet::ResolveServoStyle(*aDocElement); 2374 2375 const nsStyleDisplay* display = computedStyle->StyleDisplay(); 2376 2377 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME -------- 2378 2379 NS_ASSERTION(!display->IsScrollableOverflow() || 2380 state.mPresContext->IsPaginated() || 2381 propagatedScrollFrom == aDocElement, 2382 "Scrollbars should have been propagated to the viewport"); 2383 2384 if (MOZ_UNLIKELY(display->mDisplay == StyleDisplay::None)) { 2385 return nullptr; 2386 } 2387 2388 // This implements "The Principal Writing Mode". 2389 // https://drafts.csswg.org/css-writing-modes-3/#principal-flow 2390 // 2391 // If there's a <body> element in an HTML document, its writing-mode, 2392 // direction, and text-orientation override the root element's used value. 2393 // 2394 // We need to copy <body>'s WritingMode to mDocElementContainingBlock before 2395 // construct mRootElementFrame so that anonymous internal frames such as 2396 // <html> with table style can copy their parent frame's mWritingMode in 2397 // nsIFrame::Init(). 2398 MOZ_ASSERT(!mRootElementFrame, 2399 "We need to copy <body>'s principal writing-mode before " 2400 "constructing mRootElementFrame."); 2401 2402 const WritingMode propagatedWM = [&] { 2403 const WritingMode rootWM(computedStyle); 2404 if (computedStyle->StyleDisplay()->IsContainAny()) { 2405 return rootWM; 2406 } 2407 Element* body = mDocument->GetBodyElement(); 2408 if (!body) { 2409 return rootWM; 2410 } 2411 RefPtr<ComputedStyle> bodyStyle = ResolveComputedStyle(body); 2412 if (bodyStyle->StyleDisplay()->IsContainAny()) { 2413 return rootWM; 2414 } 2415 const WritingMode bodyWM(bodyStyle); 2416 if (bodyWM != rootWM) { 2417 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Layout"_ns, 2418 mDocument, 2419 nsContentUtils::eLAYOUT_PROPERTIES, 2420 "PrincipalWritingModePropagationWarning"); 2421 } 2422 return bodyWM; 2423 }(); 2424 2425 mDocElementContainingBlock->PropagateWritingModeToSelfAndAncestors( 2426 propagatedWM); 2427 2428 // Push the absolute containing block now so we can absolutely position the 2429 // root element 2430 nsFrameConstructorSaveState canvasCbSaveState; 2431 mCanvasFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 2432 2433 state.PushAbsoluteContainingBlock(mCanvasFrame, mCanvasFrame, 2434 canvasCbSaveState); 2435 2436 nsFrameConstructorSaveState docElementCbSaveState; 2437 if (mCanvasFrame != mDocElementContainingBlock) { 2438 mDocElementContainingBlock->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 2439 state.PushAbsoluteContainingBlock(mDocElementContainingBlock, 2440 mDocElementContainingBlock, 2441 docElementCbSaveState); 2442 } 2443 2444 // The rules from CSS 2.1, section 9.2.4, have already been applied 2445 // by the style system, so we can assume that display->mDisplay is 2446 // either NONE, BLOCK, or TABLE. 2447 2448 // contentFrame is the primary frame for the root element. frameList contains 2449 // the children of the initial containing block. 2450 // 2451 // The first of those frames is usually `contentFrame`, but it can be 2452 // different, in particular if the root frame is positioned, in which case 2453 // contentFrame is the out-of-flow frame and frameList.FirstChild() is the 2454 // placeholder. 2455 // 2456 // The rest of the frames in frameList are the anonymous content of the canvas 2457 // frame. 2458 nsContainerFrame* contentFrame; 2459 nsFrameList frameList; 2460 bool processChildren = false; 2461 2462 nsFrameConstructorSaveState absoluteSaveState; 2463 2464 if (aDocElement->IsSVGElement()) { 2465 if (!aDocElement->IsSVGElement(nsGkAtoms::svg)) { 2466 return nullptr; 2467 } 2468 // We're going to call the right function ourselves, so no need to give a 2469 // function to this FrameConstructionData. 2470 2471 // XXXbz on the other hand, if we converted this whole function to 2472 // FrameConstructionData/Item, then we'd need the right function 2473 // here... but would probably be able to get away with less code in this 2474 // function in general. 2475 static constexpr FrameConstructionData rootSVGData; 2476 AutoFrameConstructionItem item(this, &rootSVGData, aDocElement, 2477 do_AddRef(computedStyle), true); 2478 2479 contentFrame = static_cast<nsContainerFrame*>(ConstructOuterSVG( 2480 state, item, mDocElementContainingBlock, display, frameList)); 2481 } else if (display->mDisplay == StyleDisplay::Flex || 2482 display->mDisplay == StyleDisplay::WebkitBox || 2483 display->mDisplay == StyleDisplay::Grid) { 2484 auto func = [&] { 2485 if (display->mDisplay == StyleDisplay::Grid) { 2486 return NS_NewGridContainerFrame; 2487 } 2488 return NS_NewFlexContainerFrame; 2489 }(); 2490 contentFrame = func(mPresShell, computedStyle); 2491 InitAndRestoreFrame( 2492 state, aDocElement, 2493 state.GetGeometricParent(*display, mDocElementContainingBlock), 2494 contentFrame); 2495 state.AddChild(contentFrame, frameList, aDocElement, 2496 mDocElementContainingBlock); 2497 processChildren = true; 2498 2499 contentFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 2500 if (contentFrame->IsAbsPosContainingBlock()) { 2501 state.PushAbsoluteContainingBlock(contentFrame, contentFrame, 2502 absoluteSaveState); 2503 } 2504 } else if (display->mDisplay == StyleDisplay::Table) { 2505 // We're going to call the right function ourselves, so no need to give a 2506 // function to this FrameConstructionData. 2507 2508 // XXXbz on the other hand, if we converted this whole function to 2509 // FrameConstructionData/Item, then we'd need the right function 2510 // here... but would probably be able to get away with less code in this 2511 // function in general. 2512 static constexpr FrameConstructionData rootTableData; 2513 AutoFrameConstructionItem item(this, &rootTableData, aDocElement, 2514 do_AddRef(computedStyle), true); 2515 2516 // if the document is a table then just populate it. 2517 contentFrame = static_cast<nsContainerFrame*>(ConstructTable( 2518 state, item, mDocElementContainingBlock, display, frameList)); 2519 } else if (display->DisplayInside() == StyleDisplayInside::Ruby) { 2520 static constexpr FrameConstructionData data( 2521 &nsCSSFrameConstructor::ConstructBlockRubyFrame); 2522 AutoFrameConstructionItem item(this, &data, aDocElement, 2523 do_AddRef(computedStyle), true); 2524 contentFrame = static_cast<nsContainerFrame*>(ConstructBlockRubyFrame( 2525 state, item, 2526 state.GetGeometricParent(*display, mDocElementContainingBlock), display, 2527 frameList)); 2528 } else { 2529 MOZ_ASSERT(display->mDisplay == StyleDisplay::Block || 2530 display->mDisplay == StyleDisplay::FlowRoot, 2531 "Unhandled display type for root element"); 2532 contentFrame = NS_NewBlockFrame(mPresShell, computedStyle); 2533 ConstructBlock( 2534 state, aDocElement, 2535 state.GetGeometricParent(*display, mDocElementContainingBlock), 2536 mDocElementContainingBlock, computedStyle, &contentFrame, frameList, 2537 contentFrame->IsAbsPosContainingBlock() ? contentFrame : nullptr); 2538 } 2539 2540 MOZ_ASSERT(frameList.FirstChild()); 2541 MOZ_ASSERT(frameList.FirstChild()->GetContent() == aDocElement); 2542 MOZ_ASSERT(contentFrame); 2543 2544 MOZ_ASSERT( 2545 processChildren ? !mRootElementFrame : mRootElementFrame == contentFrame, 2546 "unexpected mRootElementFrame"); 2547 if (processChildren) { 2548 mRootElementFrame = contentFrame; 2549 } 2550 2551 // Figure out which frame has the main style for the document element, 2552 // assigning it to mRootElementStyleFrame. 2553 // Backgrounds should be propagated from that frame to the viewport. 2554 contentFrame->GetParentComputedStyle(&mRootElementStyleFrame); 2555 bool isChild = mRootElementStyleFrame && 2556 mRootElementStyleFrame->GetParent() == contentFrame; 2557 if (!isChild) { 2558 mRootElementStyleFrame = mRootElementFrame; 2559 } 2560 2561 if (processChildren) { 2562 // Still need to process the child content 2563 nsFrameList childList; 2564 2565 NS_ASSERTION( 2566 !contentFrame->IsBlockFrameOrSubclass() && !contentFrame->IsSVGFrame(), 2567 "Only XUL frames should reach here"); 2568 2569 nsFrameConstructorSaveState floatSaveState; 2570 state.MaybePushFloatContainingBlock(contentFrame, floatSaveState); 2571 2572 ProcessChildren(state, aDocElement, computedStyle, contentFrame, true, 2573 childList, false); 2574 2575 // Set the initial child lists 2576 contentFrame->SetInitialChildList(FrameChildListID::Principal, 2577 std::move(childList)); 2578 } 2579 2580 nsIFrame* newFrame = frameList.FirstChild(); 2581 // set the primary frame 2582 aDocElement->SetPrimaryFrame(contentFrame); 2583 mDocElementContainingBlock->AppendFrames(FrameChildListID::Principal, 2584 std::move(frameList)); 2585 2586 // NOTE(emilio): This is in the reverse order compared to normal anonymous 2587 // children. We usually generate anonymous kids first, then non-anonymous, 2588 // but we generate the doc element frame the other way around. This is fine 2589 // either way, but generating anonymous children in a different order requires 2590 // changing nsCanvasFrame (and a whole lot of other potentially unknown code) 2591 // to look at the last child to find the root frame rather than the first 2592 // child. 2593 ConstructAnonymousContentForRoot(state, mCanvasFrame, 2594 mRootElementFrame->GetContent(), frameList); 2595 mCanvasFrame->AppendFrames(FrameChildListID::Principal, std::move(frameList)); 2596 2597 return newFrame; 2598 } 2599 2600 RestyleManager* nsCSSFrameConstructor::RestyleManager() const { 2601 return mPresShell->GetPresContext()->RestyleManager(); 2602 } 2603 2604 ViewportFrame* nsCSSFrameConstructor::ConstructRootFrame() { 2605 AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ConstructRootFrame", 2606 LAYOUT_FrameConstruction); 2607 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); 2608 2609 ServoStyleSet* styleSet = mPresShell->StyleSet(); 2610 2611 // --------- BUILD VIEWPORT ----------- 2612 RefPtr<ComputedStyle> viewportPseudoStyle = 2613 styleSet->ResolveInheritingAnonymousBoxStyle(PseudoStyleType::viewport, 2614 nullptr); 2615 ViewportFrame* viewportFrame = 2616 NS_NewViewportFrame(mPresShell, viewportPseudoStyle); 2617 2618 // XXXbz do we _have_ to pass a null content pointer to that frame? 2619 // Would it really kill us to pass in the root element or something? 2620 // What would that break? 2621 viewportFrame->Init(nullptr, nullptr, nullptr); 2622 2623 viewportFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); 2624 2625 mPresShell->SetNeedsWindowPropertiesSync(); 2626 2627 // Make it an absolute container for fixed-pos elements 2628 viewportFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 2629 viewportFrame->MarkAsAbsoluteContainingBlock(); 2630 2631 return viewportFrame; 2632 } 2633 2634 void nsCSSFrameConstructor::SetUpDocElementContainingBlock( 2635 nsIContent* aDocElement) { 2636 MOZ_ASSERT(aDocElement, "No element?"); 2637 MOZ_ASSERT(!aDocElement->GetParent(), "Not root content?"); 2638 MOZ_ASSERT(aDocElement->GetUncomposedDoc(), "Not in a document?"); 2639 MOZ_ASSERT(aDocElement->GetUncomposedDoc()->GetRootElement() == aDocElement, 2640 "Not the root of the document?"); 2641 2642 /* 2643 how the root frame hierarchy should look 2644 2645 Galley presentation, with scrolling: 2646 2647 ViewportFrame [fixed-cb] 2648 ScrollContainerFrame (if needed) 2649 nsCanvasFrame [abs-cb] 2650 root element frame (nsBlockFrame, SVGOuterSVGFrame, 2651 nsTableWrapperFrame, nsPlaceholderFrame, 2652 nsFlexContainerFrame, nsGridContainerFrame) 2653 2654 Print presentation, non-XUL 2655 2656 ViewportFrame 2657 nsCanvasFrame 2658 nsPageSequenceFrame 2659 PrintedSheetFrame 2660 nsPageFrame 2661 nsPageContentFrame [fixed-cb] 2662 nsCanvasFrame [abs-cb] 2663 root element frame (nsBlockFrame, SVGOuterSVGFrame, 2664 nsTableWrapperFrame, nsPlaceholderFrame, 2665 nsFlexContainerFrame, 2666 nsGridContainerFrame) 2667 2668 Print-preview presentation, non-XUL 2669 2670 ViewportFrame 2671 ScrollContainerFrame 2672 nsCanvasFrame 2673 nsPageSequenceFrame 2674 PrintedSheetFrame 2675 nsPageFrame 2676 nsPageContentFrame [fixed-cb] 2677 nsCanvasFrame [abs-cb] 2678 root element frame (nsBlockFrame, SVGOuterSVGFrame, 2679 nsTableWrapperFrame, 2680 nsPlaceholderFrame, 2681 nsFlexContainerFrame, 2682 nsGridContainerFrame) 2683 2684 Print/print preview of XUL is not supported. 2685 [fixed-cb]: the default containing block for fixed-pos content 2686 [abs-cb]: the default containing block for abs-pos content 2687 2688 Meaning of nsCSSFrameConstructor fields: 2689 mRootElementFrame is "root element frame". This is the primary frame for 2690 the root element. 2691 mDocElementContainingBlock is the parent of mRootElementFrame 2692 (i.e. nsCanvasFrame) 2693 mPageSequenceFrame is the nsPageSequenceFrame, or null if there isn't 2694 one 2695 */ 2696 2697 // --------- CREATE ROOT FRAME ------- 2698 2699 // Create the root frame. The document element's frame is a child of the 2700 // root frame. 2701 // 2702 // The root frame serves two purposes: 2703 // - reserves space for any margins needed for the document element's frame 2704 // - renders the document element's background. This ensures the background 2705 // covers the entire canvas as specified by the CSS2 spec 2706 2707 nsPresContext* presContext = mPresShell->GetPresContext(); 2708 const bool isPaginated = presContext->IsRootPaginatedDocument(); 2709 2710 const bool isHTML = aDocElement->IsHTMLElement(); 2711 const bool isXUL = !isHTML && aDocElement->IsXULElement(); 2712 2713 const bool isScrollable = [&] { 2714 if (isPaginated) { 2715 return presContext->HasPaginatedScrolling(); 2716 } 2717 // Never create scrollbars for XUL documents or top level XHTML documents 2718 // that disable scrolling. 2719 if (isXUL) { 2720 return false; 2721 } 2722 if (aDocElement->OwnerDoc()->ChromeRulesEnabled() && 2723 aDocElement->AsElement()->AttrValueIs( 2724 kNameSpaceID_None, nsGkAtoms::scrolling, nsGkAtoms::_false, 2725 eCaseMatters)) { 2726 return false; 2727 } 2728 return true; 2729 }(); 2730 2731 nsContainerFrame* viewportFrame = 2732 static_cast<nsContainerFrame*>(GetRootFrame()); 2733 ComputedStyle* viewportPseudoStyle = viewportFrame->Style(); 2734 2735 nsCanvasFrame* rootCanvasFrame = 2736 NS_NewCanvasFrame(mPresShell, viewportPseudoStyle); 2737 PseudoStyleType rootPseudo = PseudoStyleType::canvas; 2738 mCanvasFrame = rootCanvasFrame; 2739 mDocElementContainingBlock = rootCanvasFrame; 2740 2741 // --------- IF SCROLLABLE WRAP IN SCROLLFRAME -------- 2742 2743 // If the device supports scrolling (e.g., in galley mode on the screen and 2744 // for print-preview, but not when printing), then create a scroll frame that 2745 // will act as the scrolling mechanism for the viewport. 2746 // XXX Do we even need a viewport when printing to a printer? 2747 2748 // We no longer need to do overflow propagation here. It's taken care of 2749 // when we construct frames for the element whose overflow might be 2750 // propagated 2751 NS_ASSERTION(!isScrollable || !isXUL, 2752 "XUL documents should never be scrollable - see above"); 2753 2754 nsContainerFrame* newFrame = rootCanvasFrame; 2755 RefPtr<ComputedStyle> rootPseudoStyle; 2756 // we must create a state because if the scrollbars are GFX it needs the 2757 // state to build the scrollbar frames. 2758 nsFrameConstructorState state(mPresShell, nullptr, nullptr, nullptr); 2759 2760 // Start off with the viewport as parent; we'll adjust it as needed. 2761 nsContainerFrame* parentFrame = viewportFrame; 2762 2763 ServoStyleSet* styleSet = mPresShell->StyleSet(); 2764 // If paginated, make sure we don't put scrollbars in 2765 if (!isScrollable) { 2766 rootPseudoStyle = styleSet->ResolveInheritingAnonymousBoxStyle( 2767 rootPseudo, viewportPseudoStyle); 2768 } else { 2769 rootPseudo = PseudoStyleType::scrolledCanvas; 2770 2771 // Build the frame. We give it the content we are wrapping which is the 2772 // document element, the root frame, the parent view port frame, and we 2773 // should get back the new frame and the scrollable view if one was 2774 // created. 2775 2776 // resolve a context for the scrollframe 2777 RefPtr<ComputedStyle> computedStyle = 2778 styleSet->ResolveInheritingAnonymousBoxStyle( 2779 PseudoStyleType::viewportScroll, viewportPseudoStyle); 2780 2781 // Note that the viewport scrollframe is always built with 2782 // overflow:auto style. This forces the scroll frame to create 2783 // anonymous content for both scrollbars. This is necessary even 2784 // if the HTML or BODY elements are overriding the viewport 2785 // scroll style to 'hidden' --- dynamic style changes might put 2786 // scrollbars back on the viewport and we don't want to have to 2787 // reframe the viewport to create the scrollbar content. 2788 newFrame = nullptr; 2789 rootPseudoStyle = BeginBuildingScrollContainerFrame( 2790 state, aDocElement, computedStyle, viewportFrame, rootPseudo, true, 2791 newFrame); 2792 parentFrame = newFrame; 2793 } 2794 2795 rootCanvasFrame->SetComputedStyleWithoutNotification(rootPseudoStyle); 2796 rootCanvasFrame->Init(aDocElement, parentFrame, nullptr); 2797 2798 if (isScrollable) { 2799 FinishBuildingScrollContainerFrame(parentFrame, rootCanvasFrame); 2800 } 2801 2802 if (isPaginated) { 2803 // Create a page sequence frame 2804 { 2805 RefPtr<ComputedStyle> pageSequenceStyle = 2806 styleSet->ResolveInheritingAnonymousBoxStyle( 2807 PseudoStyleType::pageSequence, viewportPseudoStyle); 2808 mPageSequenceFrame = 2809 NS_NewPageSequenceFrame(mPresShell, pageSequenceStyle); 2810 mPageSequenceFrame->Init(aDocElement, rootCanvasFrame, nullptr); 2811 SetInitialSingleChild(rootCanvasFrame, mPageSequenceFrame); 2812 } 2813 2814 // Create the first printed sheet frame, as the sole child (for now) of our 2815 // page sequence frame (mPageSequenceFrame). 2816 auto* printedSheetFrame = 2817 ConstructPrintedSheetFrame(mPresShell, mPageSequenceFrame, nullptr); 2818 SetInitialSingleChild(mPageSequenceFrame, printedSheetFrame); 2819 2820 MOZ_ASSERT(!mNextPageContentFramePageName, 2821 "Next page name should not have been set."); 2822 2823 // Create the first page, as the sole child (for now) of the printed sheet 2824 // frame that we just created. 2825 nsCanvasFrame* canvasFrame; 2826 nsContainerFrame* pageFrame = 2827 ConstructPageFrame(mPresShell, printedSheetFrame, nullptr, canvasFrame); 2828 pageFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); 2829 SetInitialSingleChild(printedSheetFrame, pageFrame); 2830 2831 // The eventual parent of the document element frame. 2832 // XXX should this be set for every new page (in ConstructPageFrame)? 2833 mDocElementContainingBlock = canvasFrame; 2834 } 2835 2836 if (viewportFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { 2837 SetInitialSingleChild(viewportFrame, newFrame); 2838 } else { 2839 viewportFrame->AppendFrames(FrameChildListID::Principal, 2840 nsFrameList(newFrame, newFrame)); 2841 } 2842 } 2843 2844 void nsCSSFrameConstructor::ConstructAnonymousContentForRoot( 2845 nsFrameConstructorState& aState, nsContainerFrame* aCanvasFrame, 2846 nsIContent* aDocElement, nsFrameList& aFrameList) { 2847 NS_ASSERTION(aCanvasFrame->IsCanvasFrame(), "aFrame should be canvas frame!"); 2848 MOZ_ASSERT(mRootElementFrame->GetContent() == aDocElement); 2849 2850 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems; 2851 GetAnonymousContent(aDocElement, aCanvasFrame, anonymousItems); 2852 2853 // If we get here, we are rebuilding the anonymous content of the root 2854 // element. In this case, we also need to deal with the custom content 2855 // container. 2856 if (auto* container = 2857 aState.mPresContext->Document()->GetCustomContentContainer()) { 2858 // FIXME(emilio, bug 1852735): This is only needed because of the sync frame 2859 // construction from PresShell::Initialize. See the similar code-path in 2860 // ConstructDocElementFrame. 2861 if (!container->HasServoData()) { 2862 mPresShell->StyleSet()->StyleNewSubtree(container); 2863 } 2864 anonymousItems.AppendElement(container); 2865 } 2866 2867 if (anonymousItems.IsEmpty()) { 2868 return; 2869 } 2870 2871 AutoFrameConstructionItemList itemsToConstruct(this); 2872 AutoFrameConstructionPageName pageNameTracker(aState, aCanvasFrame); 2873 AddFCItemsForAnonymousContent(aState, aCanvasFrame, anonymousItems, 2874 itemsToConstruct, pageNameTracker); 2875 ConstructFramesFromItemList(aState, itemsToConstruct, aCanvasFrame, 2876 /* aParentIsWrapperAnonBox = */ false, 2877 aFrameList); 2878 } 2879 2880 PrintedSheetFrame* nsCSSFrameConstructor::ConstructPrintedSheetFrame( 2881 PresShell* aPresShell, nsContainerFrame* aParentFrame, 2882 nsIFrame* aPrevSheetFrame) { 2883 RefPtr<ComputedStyle> printedSheetPseudoStyle = 2884 aPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle( 2885 PseudoStyleType::printedSheet); 2886 2887 auto* printedSheetFrame = 2888 NS_NewPrintedSheetFrame(aPresShell, printedSheetPseudoStyle); 2889 2890 printedSheetFrame->Init(nullptr, aParentFrame, aPrevSheetFrame); 2891 2892 return printedSheetFrame; 2893 } 2894 2895 nsContainerFrame* nsCSSFrameConstructor::ConstructPageFrame( 2896 PresShell* aPresShell, nsContainerFrame* aParentFrame, 2897 nsIFrame* aPrevPageFrame, nsCanvasFrame*& aCanvasFrame) { 2898 ServoStyleSet* styleSet = aPresShell->StyleSet(); 2899 2900 RefPtr<ComputedStyle> pagePseudoStyle = 2901 styleSet->ResolveNonInheritingAnonymousBoxStyle(PseudoStyleType::page); 2902 2903 nsContainerFrame* pageFrame = NS_NewPageFrame(aPresShell, pagePseudoStyle); 2904 2905 // Initialize the page frame and force it to have a view. This makes printing 2906 // of the pages easier and faster. 2907 pageFrame->Init(nullptr, aParentFrame, aPrevPageFrame); 2908 2909 RefPtr<const nsAtom> pageName; 2910 if (mNextPageContentFramePageName) { 2911 pageName = mNextPageContentFramePageName.forget(); 2912 } else if (aPrevPageFrame) { 2913 pageName = aPrevPageFrame->ComputePageValue(); 2914 MOZ_ASSERT(pageName, 2915 "Page name from prev-in-flow should not have been null"); 2916 } 2917 RefPtr<ComputedStyle> pageContentPseudoStyle = 2918 styleSet->ResolvePageContentStyle(pageName, 2919 StylePagePseudoClassFlags::NONE); 2920 2921 nsContainerFrame* pageContentFrame = NS_NewPageContentFrame( 2922 aPresShell, pageContentPseudoStyle, pageName.forget()); 2923 2924 nsPageContentFrame* prevPageContentFrame = nullptr; 2925 if (aPrevPageFrame) { 2926 MOZ_ASSERT(aPrevPageFrame->IsPageFrame()); 2927 prevPageContentFrame = 2928 static_cast<nsPageFrame*>(aPrevPageFrame)->PageContentFrame(); 2929 } 2930 pageContentFrame->Init(nullptr, pageFrame, prevPageContentFrame); 2931 if (!prevPageContentFrame) { 2932 // The canvas is an inheriting anon box, so needs to be "owned" by the page 2933 // content. 2934 pageContentFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES | 2935 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 2936 // Make it an absolute container for fixed-pos elements 2937 pageContentFrame->MarkAsAbsoluteContainingBlock(); 2938 } else { 2939 MOZ_ASSERT( 2940 pageContentFrame->HasAllStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN), 2941 "This bit should've been carried over from the previous continuation " 2942 "in nsIFrame::Init()."); 2943 MOZ_ASSERT(pageContentFrame->GetAbsoluteContainingBlock(), 2944 "nsIFrame::Init() should've constructed AbsoluteContainingBlock " 2945 "for continuations!"); 2946 } 2947 SetInitialSingleChild(pageFrame, pageContentFrame); 2948 2949 RefPtr<ComputedStyle> canvasPseudoStyle = 2950 styleSet->ResolveInheritingAnonymousBoxStyle(PseudoStyleType::canvas, 2951 pageContentPseudoStyle); 2952 2953 aCanvasFrame = NS_NewCanvasFrame(aPresShell, canvasPseudoStyle); 2954 2955 nsIFrame* prevCanvasFrame = nullptr; 2956 if (prevPageContentFrame) { 2957 prevCanvasFrame = prevPageContentFrame->PrincipalChildList().FirstChild(); 2958 NS_ASSERTION(prevCanvasFrame, "missing canvas frame"); 2959 } 2960 aCanvasFrame->Init(nullptr, pageContentFrame, prevCanvasFrame); 2961 SetInitialSingleChild(pageContentFrame, aCanvasFrame); 2962 return pageFrame; 2963 } 2964 2965 /* static */ 2966 nsIFrame* nsCSSFrameConstructor::CreatePlaceholderFrameFor( 2967 PresShell* aPresShell, nsIContent* aContent, nsIFrame* aFrame, 2968 nsContainerFrame* aParentFrame, nsIFrame* aPrevInFlow, 2969 nsFrameState aTypeBit) { 2970 RefPtr<ComputedStyle> placeholderStyle = 2971 aPresShell->StyleSet()->ResolveStyleForPlaceholder(); 2972 2973 // The placeholder frame gets a pseudo style. 2974 nsPlaceholderFrame* placeholderFrame = 2975 NS_NewPlaceholderFrame(aPresShell, placeholderStyle, aTypeBit); 2976 2977 placeholderFrame->Init(aContent, aParentFrame, aPrevInFlow); 2978 2979 // Associate the placeholder/out-of-flow with each other. 2980 placeholderFrame->SetOutOfFlowFrame(aFrame); 2981 aFrame->SetProperty(nsIFrame::PlaceholderFrameProperty(), placeholderFrame); 2982 2983 aFrame->AddStateBits(NS_FRAME_OUT_OF_FLOW); 2984 2985 return placeholderFrame; 2986 } 2987 2988 // Clears any lazy bits set in the range [aStartContent, aEndContent). If 2989 // aEndContent is null, that means to clear bits in all siblings starting with 2990 // aStartContent. aStartContent must not be null unless aEndContent is also 2991 // null. We do this so that when new children are inserted under elements whose 2992 // frame is a leaf the new children don't cause us to try to construct frames 2993 // for the existing children again. 2994 static inline void ClearLazyBits(nsIContent* aStartContent, 2995 nsIContent* aEndContent) { 2996 MOZ_ASSERT(aStartContent || !aEndContent, 2997 "Must have start child if we have an end child"); 2998 2999 for (nsIContent* cur = aStartContent; cur != aEndContent; 3000 cur = cur->GetNextSibling()) { 3001 cur->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME); 3002 } 3003 } 3004 3005 /* static */ 3006 const nsCSSFrameConstructor::FrameConstructionData* 3007 nsCSSFrameConstructor::FindSelectData(const Element& aElement, 3008 ComputedStyle& aStyle) { 3009 // Construct a frame-based listbox or combobox 3010 const auto* sel = dom::HTMLSelectElement::FromNode(aElement); 3011 MOZ_ASSERT(sel); 3012 if (sel->IsCombobox()) { 3013 static constexpr FrameConstructionData sComboboxData{ 3014 ToCreationFunc(NS_NewComboboxControlFrame)}; 3015 return &sComboboxData; 3016 } 3017 // FIXME: Can we simplify this to avoid needing ConstructListboxSelectFrame, 3018 // and reuse ConstructScrollableBlock or so? 3019 static constexpr FrameConstructionData sListBoxData{ 3020 &nsCSSFrameConstructor::ConstructListBoxSelectFrame}; 3021 return &sListBoxData; 3022 } 3023 3024 nsIFrame* nsCSSFrameConstructor::ConstructListBoxSelectFrame( 3025 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 3026 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay, 3027 nsFrameList& aFrameList) { 3028 nsIContent* const content = aItem.mContent; 3029 ComputedStyle* const computedStyle = aItem.mComputedStyle; 3030 3031 // Listbox, not combobox 3032 nsContainerFrame* listFrame = 3033 NS_NewListControlFrame(mPresShell, computedStyle); 3034 3035 nsContainerFrame* scrolledFrame = NS_NewBlockFrame(mPresShell, computedStyle); 3036 3037 // ******* this code stolen from Initialze ScrollFrame ******** 3038 // please adjust this code to use BuildScrollFrame. 3039 3040 InitializeListboxSelect(aState, listFrame, scrolledFrame, content, 3041 aParentFrame, computedStyle, aFrameList); 3042 3043 return listFrame; 3044 } 3045 3046 void nsCSSFrameConstructor::InitializeListboxSelect( 3047 nsFrameConstructorState& aState, nsContainerFrame* scrollFrame, 3048 nsContainerFrame* scrolledFrame, nsIContent* aContent, 3049 nsContainerFrame* aParentFrame, ComputedStyle* aComputedStyle, 3050 nsFrameList& aFrameList) { 3051 // Initialize it 3052 nsContainerFrame* geometricParent = 3053 aState.GetGeometricParent(*aComputedStyle->StyleDisplay(), aParentFrame); 3054 3055 // We don't call InitAndRestoreFrame for scrollFrame because we can only 3056 // restore the frame state after its parts have been created (in particular, 3057 // the scrollable view). So we have to split Init and Restore. 3058 3059 scrollFrame->Init(aContent, geometricParent, nullptr); 3060 aState.AddChild(scrollFrame, aFrameList, aContent, aParentFrame); 3061 BuildScrollContainerFrame(aState, aContent, aComputedStyle, scrolledFrame, 3062 geometricParent, scrollFrame); 3063 if (aState.mFrameState) { 3064 // Restore frame state for the scroll frame 3065 RestoreFrameStateFor(scrollFrame, aState.mFrameState); 3066 } 3067 3068 nsFrameConstructorSaveState floatSaveState; 3069 aState.MaybePushFloatContainingBlock(scrolledFrame, floatSaveState); 3070 3071 // Process children 3072 nsFrameList childList; 3073 3074 ProcessChildren(aState, aContent, aComputedStyle, scrolledFrame, false, 3075 childList, false); 3076 3077 // Set the scrolled frame's initial child lists 3078 scrolledFrame->SetInitialChildList(FrameChildListID::Principal, 3079 std::move(childList)); 3080 } 3081 3082 nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame( 3083 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 3084 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay, 3085 nsFrameList& aFrameList) { 3086 AutoRestore<bool> savedHasRenderedLegend(aState.mHasRenderedLegend); 3087 aState.mHasRenderedLegend = false; 3088 nsIContent* const content = aItem.mContent; 3089 ComputedStyle* const computedStyle = aItem.mComputedStyle; 3090 3091 nsContainerFrame* fieldsetFrame = 3092 NS_NewFieldSetFrame(mPresShell, computedStyle); 3093 3094 // Initialize it 3095 InitAndRestoreFrame(aState, content, 3096 aState.GetGeometricParent(*aStyleDisplay, aParentFrame), 3097 fieldsetFrame); 3098 3099 fieldsetFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); 3100 3101 // Resolve style and initialize the frame 3102 RefPtr<ComputedStyle> fieldsetContentStyle = 3103 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 3104 PseudoStyleType::fieldsetContent, computedStyle); 3105 3106 const nsStyleDisplay* fieldsetContentDisplay = 3107 fieldsetContentStyle->StyleDisplay(); 3108 const bool isScrollable = fieldsetContentDisplay->IsScrollableOverflow(); 3109 nsContainerFrame* scrollFrame = nullptr; 3110 if (isScrollable) { 3111 fieldsetContentStyle = BeginBuildingScrollContainerFrame( 3112 aState, content, fieldsetContentStyle, fieldsetFrame, 3113 PseudoStyleType::scrolledContent, false, scrollFrame); 3114 } 3115 3116 // Create the inner ::-moz-fieldset-content frame. 3117 nsContainerFrame* contentFrameTop; 3118 nsContainerFrame* contentFrame; 3119 auto* parent = scrollFrame ? scrollFrame : fieldsetFrame; 3120 MOZ_ASSERT(fieldsetContentDisplay->DisplayOutside() == 3121 StyleDisplayOutside::Block); 3122 switch (fieldsetContentDisplay->DisplayInside()) { 3123 case StyleDisplayInside::Flex: 3124 contentFrame = NS_NewFlexContainerFrame(mPresShell, fieldsetContentStyle); 3125 InitAndRestoreFrame(aState, content, parent, contentFrame); 3126 contentFrameTop = contentFrame; 3127 break; 3128 case StyleDisplayInside::Grid: 3129 contentFrame = NS_NewGridContainerFrame(mPresShell, fieldsetContentStyle); 3130 InitAndRestoreFrame(aState, content, parent, contentFrame); 3131 contentFrameTop = contentFrame; 3132 break; 3133 default: { 3134 MOZ_ASSERT(fieldsetContentDisplay->mDisplay == StyleDisplay::Block, 3135 "bug in StyleAdjuster::adjust_for_fieldset_content?"); 3136 3137 contentFrame = NS_NewBlockFrame(mPresShell, fieldsetContentStyle); 3138 if (fieldsetContentStyle->StyleColumn()->IsColumnContainerStyle()) { 3139 contentFrameTop = BeginBuildingColumns( 3140 aState, content, parent, contentFrame, fieldsetContentStyle); 3141 } else { 3142 // No need to create column container. Initialize content frame. 3143 InitAndRestoreFrame(aState, content, parent, contentFrame); 3144 contentFrameTop = contentFrame; 3145 } 3146 3147 break; 3148 } 3149 } 3150 3151 aState.AddChild(fieldsetFrame, aFrameList, content, aParentFrame); 3152 3153 // Process children 3154 nsFrameConstructorSaveState absoluteSaveState; 3155 nsFrameList childList; 3156 3157 contentFrameTop->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 3158 if (fieldsetFrame->IsAbsPosContainingBlock()) { 3159 aState.PushAbsoluteContainingBlock(contentFrameTop, fieldsetFrame, 3160 absoluteSaveState); 3161 } 3162 3163 nsFrameConstructorSaveState floatSaveState; 3164 aState.MaybePushFloatContainingBlock(contentFrame, floatSaveState); 3165 3166 ProcessChildren(aState, content, computedStyle, contentFrame, true, childList, 3167 true); 3168 nsFrameList fieldsetKids; 3169 fieldsetKids.AppendFrame(nullptr, 3170 scrollFrame ? scrollFrame : contentFrameTop); 3171 3172 if (!MayNeedToCreateColumnSpanSiblings(contentFrame, childList)) { 3173 // Set the inner frame's initial child lists. 3174 contentFrame->SetInitialChildList(FrameChildListID::Principal, 3175 std::move(childList)); 3176 } else { 3177 // Extract any initial non-column-span kids, and put them in inner frame's 3178 // child list. 3179 nsFrameList initialNonColumnSpanKids = 3180 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); }); 3181 contentFrame->SetInitialChildList(FrameChildListID::Principal, 3182 std::move(initialNonColumnSpanKids)); 3183 3184 if (childList.NotEmpty()) { 3185 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings( 3186 aState, contentFrame, childList, 3187 // Column content should never be a absolute/fixed positioned 3188 // containing block. Pass nullptr as aPositionedFrame. 3189 nullptr); 3190 FinishBuildingColumns(aState, contentFrameTop, contentFrame, 3191 columnSpanSiblings); 3192 } 3193 } 3194 3195 if (isScrollable) { 3196 FinishBuildingScrollContainerFrame(scrollFrame, contentFrameTop); 3197 } 3198 3199 // We use AppendFrames here because the rendered legend will already 3200 // be present in the principal child list if it exists. 3201 fieldsetFrame->AppendFrames(FrameChildListID::NoReflowPrincipal, 3202 std::move(fieldsetKids)); 3203 3204 return fieldsetFrame; 3205 } 3206 3207 const nsCSSFrameConstructor::FrameConstructionData* 3208 nsCSSFrameConstructor::FindDetailsData(const Element& aElement, 3209 ComputedStyle& aStyle) { 3210 if (!StaticPrefs::layout_details_force_block_layout()) { 3211 return nullptr; 3212 } 3213 static constexpr FrameConstructionData sBlockData[2] = { 3214 {&nsCSSFrameConstructor::ConstructNonScrollableBlock}, 3215 {&nsCSSFrameConstructor::ConstructScrollableBlock}, 3216 }; 3217 return &sBlockData[aStyle.StyleDisplay()->IsScrollableOverflow()]; 3218 } 3219 3220 nsIFrame* nsCSSFrameConstructor::ConstructBlockRubyFrame( 3221 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 3222 nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay, 3223 nsFrameList& aFrameList) { 3224 nsIContent* const content = aItem.mContent; 3225 ComputedStyle* const computedStyle = aItem.mComputedStyle; 3226 3227 nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, computedStyle); 3228 nsContainerFrame* newFrame = blockFrame; 3229 nsContainerFrame* geometricParent = 3230 aState.GetGeometricParent(*aStyleDisplay, aParentFrame); 3231 AutoFrameConstructionPageName pageNameTracker(aState, blockFrame); 3232 if ((aItem.mFCData->mBits & FCDATA_MAY_NEED_SCROLLFRAME) && 3233 aStyleDisplay->IsScrollableOverflow()) { 3234 nsContainerFrame* scrollframe = nullptr; 3235 BuildScrollContainerFrame(aState, content, computedStyle, blockFrame, 3236 geometricParent, scrollframe); 3237 newFrame = scrollframe; 3238 } else { 3239 InitAndRestoreFrame(aState, content, geometricParent, blockFrame); 3240 } 3241 3242 RefPtr<ComputedStyle> rubyStyle = 3243 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 3244 PseudoStyleType::blockRubyContent, computedStyle); 3245 nsContainerFrame* rubyFrame = NS_NewRubyFrame(mPresShell, rubyStyle); 3246 InitAndRestoreFrame(aState, content, blockFrame, rubyFrame); 3247 SetInitialSingleChild(blockFrame, rubyFrame); 3248 blockFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); 3249 3250 aState.AddChild(newFrame, aFrameList, content, aParentFrame); 3251 3252 if (!mRootElementFrame) { 3253 mRootElementFrame = newFrame; 3254 } 3255 3256 nsFrameConstructorSaveState absoluteSaveState; 3257 blockFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 3258 if (newFrame->IsAbsPosContainingBlock()) { 3259 aState.PushAbsoluteContainingBlock(blockFrame, blockFrame, 3260 absoluteSaveState); 3261 } 3262 nsFrameConstructorSaveState floatSaveState; 3263 aState.MaybePushFloatContainingBlock(blockFrame, floatSaveState); 3264 3265 nsFrameList childList; 3266 ProcessChildren(aState, content, rubyStyle, rubyFrame, true, childList, false, 3267 nullptr); 3268 rubyFrame->SetInitialChildList(FrameChildListID::Principal, 3269 std::move(childList)); 3270 3271 return newFrame; 3272 } 3273 3274 static nsIFrame* FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame) { 3275 for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) { 3276 NS_ASSERTION(f->IsGeneratedContentFrame(), 3277 "should not have exited generated content"); 3278 auto pseudo = f->Style()->GetPseudoType(); 3279 if (pseudo == PseudoStyleType::before || pseudo == PseudoStyleType::after || 3280 pseudo == PseudoStyleType::marker || 3281 pseudo == PseudoStyleType::backdrop) { 3282 return f; 3283 } 3284 } 3285 return nullptr; 3286 } 3287 3288 /* static */ 3289 const nsCSSFrameConstructor::FrameConstructionData* 3290 nsCSSFrameConstructor::FindTextData(const Text& aTextContent, 3291 nsIFrame* aParentFrame) { 3292 if (aParentFrame && IsFrameForSVG(aParentFrame)) { 3293 if (!aParentFrame->IsInSVGTextSubtree()) { 3294 return nullptr; 3295 } 3296 3297 // FIXME(bug 1588477) Don't render stuff in display: contents / Shadow DOM 3298 // subtrees, because TextCorrespondenceRecorder in the SVG text code doesn't 3299 // really know how to deal with it. This kinda sucks. :( 3300 if (aParentFrame->GetContent() != aTextContent.GetParent()) { 3301 return nullptr; 3302 } 3303 3304 static constexpr FrameConstructionData sSVGTextData( 3305 NS_NewTextFrame, FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_SVG_TEXT); 3306 return &sSVGTextData; 3307 } 3308 3309 static constexpr FrameConstructionData sTextData(NS_NewTextFrame, 3310 FCDATA_IS_LINE_PARTICIPANT); 3311 return &sTextData; 3312 } 3313 3314 void nsCSSFrameConstructor::ConstructTextFrame( 3315 const FrameConstructionData* aData, nsFrameConstructorState& aState, 3316 nsIContent* aContent, nsContainerFrame* aParentFrame, 3317 ComputedStyle* aComputedStyle, nsFrameList& aFrameList) { 3318 MOZ_ASSERT(aData, "Must have frame construction data"); 3319 3320 nsIFrame* newFrame = 3321 (*aData->mFunc.mCreationFunc)(mPresShell, aComputedStyle); 3322 3323 InitAndRestoreFrame(aState, aContent, aParentFrame, newFrame); 3324 3325 // We never need to create a view for a text frame. 3326 3327 if (newFrame->IsGeneratedContentFrame()) { 3328 UniquePtr<nsGenConInitializer> initializer( 3329 static_cast<nsGenConInitializer*>( 3330 aContent->TakeProperty(nsGkAtoms::genConInitializerProperty))); 3331 if (initializer) { 3332 if (initializer->mNode.release()->InitTextFrame( 3333 initializer->mList, 3334 FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) { 3335 (this->*(initializer->mDirtyAll))(); 3336 } 3337 } 3338 } 3339 3340 // Add the newly constructed frame to the flow 3341 aFrameList.AppendFrame(nullptr, newFrame); 3342 3343 if (!aState.mCreatingExtraFrames || (aContent->IsInNativeAnonymousSubtree() && 3344 !aContent->GetPrimaryFrame())) { 3345 aContent->SetPrimaryFrame(newFrame); 3346 } 3347 } 3348 3349 /* static */ 3350 const nsCSSFrameConstructor::FrameConstructionData* 3351 nsCSSFrameConstructor::FindDataByInt(int32_t aInt, const Element& aElement, 3352 ComputedStyle& aComputedStyle, 3353 const FrameConstructionDataByInt* aDataPtr, 3354 uint32_t aDataLength) { 3355 for (const FrameConstructionDataByInt *curData = aDataPtr, 3356 *endData = aDataPtr + aDataLength; 3357 curData != endData; ++curData) { 3358 if (curData->mInt == aInt) { 3359 const FrameConstructionData* data = &curData->mData; 3360 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) { 3361 return data->mFunc.mDataGetter(aElement, aComputedStyle); 3362 } 3363 3364 return data; 3365 } 3366 } 3367 3368 return nullptr; 3369 } 3370 3371 /* static */ 3372 const nsCSSFrameConstructor::FrameConstructionData* 3373 nsCSSFrameConstructor::FindDataByTag(const Element& aElement, 3374 ComputedStyle& aStyle, 3375 const FrameConstructionDataByTag* aDataPtr, 3376 uint32_t aDataLength) { 3377 const nsAtom* tag = aElement.NodeInfo()->NameAtom(); 3378 for (const FrameConstructionDataByTag *curData = aDataPtr, 3379 *endData = aDataPtr + aDataLength; 3380 curData != endData; ++curData) { 3381 if (curData->mTag == tag) { 3382 const FrameConstructionData* data = &curData->mData; 3383 if (data->mBits & FCDATA_FUNC_IS_DATA_GETTER) { 3384 return data->mFunc.mDataGetter(aElement, aStyle); 3385 } 3386 3387 return data; 3388 } 3389 } 3390 3391 return nullptr; 3392 } 3393 3394 #define SUPPRESS_FCDATA() FrameConstructionData(nullptr, FCDATA_SUPPRESS_FRAME) 3395 #define SIMPLE_INT_CREATE(_int, _func) \ 3396 {int32_t(_int), FrameConstructionData(_func)} 3397 #define SIMPLE_INT_CHAIN(_int, _func) \ 3398 {int32_t(_int), FrameConstructionData(_func)} 3399 #define COMPLEX_INT_CREATE(_int, _func) \ 3400 {int32_t(_int), FrameConstructionData(_func)} 3401 3402 #define SIMPLE_TAG_CREATE(_tag, _func) \ 3403 {nsGkAtoms::_tag, FrameConstructionData(_func)} 3404 #define SIMPLE_TAG_CHAIN(_tag, _func) \ 3405 {nsGkAtoms::_tag, FrameConstructionData(_func)} 3406 #define COMPLEX_TAG_CREATE(_tag, _func) \ 3407 {nsGkAtoms::_tag, FrameConstructionData(_func)} 3408 3409 static nsFieldSetFrame* GetFieldSetFrameFor(nsIFrame* aFrame) { 3410 auto pseudo = aFrame->Style()->GetPseudoType(); 3411 if (pseudo == PseudoStyleType::fieldsetContent || 3412 pseudo == PseudoStyleType::scrolledContent || 3413 pseudo == PseudoStyleType::columnSet || 3414 pseudo == PseudoStyleType::columnContent) { 3415 return GetFieldSetFrameFor(aFrame->GetParent()); 3416 } 3417 return do_QueryFrame(aFrame); 3418 } 3419 3420 /* static */ 3421 const nsCSSFrameConstructor::FrameConstructionData* 3422 nsCSSFrameConstructor::FindHTMLData(const Element& aElement, 3423 nsIFrame* aParentFrame, 3424 ComputedStyle& aStyle) { 3425 MOZ_ASSERT(aElement.IsHTMLElement()); 3426 NS_ASSERTION(!aParentFrame || 3427 aParentFrame->Style()->GetPseudoType() != 3428 PseudoStyleType::fieldsetContent || 3429 aParentFrame->GetParent()->IsFieldSetFrame(), 3430 "Unexpected parent for fieldset content anon box"); 3431 3432 if (aElement.IsInNativeAnonymousSubtree()) { 3433 if (aElement.NodeInfo()->NameAtom() == nsGkAtoms::label && aParentFrame) { 3434 if (aParentFrame->IsFileControlFrame()) { 3435 static constexpr FrameConstructionData sFileLabelData( 3436 NS_NewFileControlLabelFrame); 3437 return &sFileLabelData; 3438 } 3439 if (aParentFrame->IsComboboxControlFrame()) { 3440 static constexpr FrameConstructionData sComboboxLabelData( 3441 NS_NewComboboxLabelFrame); 3442 return &sComboboxLabelData; 3443 } 3444 } 3445 if (aStyle.GetPseudoType() == PseudoStyleType::viewTransitionOld || 3446 aStyle.GetPseudoType() == PseudoStyleType::viewTransitionNew) { 3447 static constexpr FrameConstructionData sViewTransitionData( 3448 NS_NewImageFrameForViewTransition); 3449 return &sViewTransitionData; 3450 } 3451 } 3452 3453 static constexpr FrameConstructionDataByTag sHTMLData[] = { 3454 SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData), 3455 SIMPLE_TAG_CHAIN(mozgeneratedcontentimage, 3456 nsCSSFrameConstructor::FindGeneratedImageData), 3457 {nsGkAtoms::br, 3458 {NS_NewBRFrame, FCDATA_IS_LINE_PARTICIPANT | FCDATA_IS_LINE_BREAK}}, 3459 SIMPLE_TAG_CREATE(wbr, NS_NewWBRFrame), 3460 SIMPLE_TAG_CHAIN(button, nsCSSFrameConstructor::FindHTMLButtonData), 3461 SIMPLE_TAG_CHAIN(input, nsCSSFrameConstructor::FindInputData), 3462 SIMPLE_TAG_CREATE(textarea, NS_NewTextControlFrame), 3463 SIMPLE_TAG_CHAIN(select, nsCSSFrameConstructor::FindSelectData), 3464 SIMPLE_TAG_CHAIN(object, nsCSSFrameConstructor::FindObjectData), 3465 SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData), 3466 COMPLEX_TAG_CREATE(fieldset, 3467 &nsCSSFrameConstructor::ConstructFieldSetFrame), 3468 SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame), 3469 SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame), 3470 SIMPLE_TAG_CHAIN(canvas, nsCSSFrameConstructor::FindCanvasData), 3471 SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame), 3472 SIMPLE_TAG_CREATE(audio, NS_NewHTMLAudioFrame), 3473 SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame), 3474 SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame), 3475 SIMPLE_TAG_CHAIN(details, nsCSSFrameConstructor::FindDetailsData), 3476 }; 3477 3478 return FindDataByTag(aElement, aStyle, sHTMLData, std::size(sHTMLData)); 3479 } 3480 3481 /* static */ 3482 const nsCSSFrameConstructor::FrameConstructionData* 3483 nsCSSFrameConstructor::FindGeneratedImageData(const Element& aElement, 3484 ComputedStyle&) { 3485 if (!aElement.IsInNativeAnonymousSubtree()) { 3486 return nullptr; 3487 } 3488 3489 auto& generatedContent = static_cast<const GeneratedImageContent&>(aElement); 3490 if (generatedContent.IsForListStyleImageMarker()) { 3491 static constexpr FrameConstructionData sImgData( 3492 NS_NewImageFrameForListStyleImage); 3493 return &sImgData; 3494 } 3495 3496 static constexpr FrameConstructionData sImgData( 3497 NS_NewImageFrameForGeneratedContentIndex); 3498 return &sImgData; 3499 } 3500 3501 const nsCSSFrameConstructor::FrameConstructionData* 3502 nsCSSFrameConstructor::FindHTMLButtonData(const Element&, 3503 ComputedStyle& aStyle) { 3504 // Buttons force a (maybe inline) block unless their display is flex or grid. 3505 // TODO(emilio): It'd be good to remove this restriction more broadly. 3506 // There are some tests that expect block baselines on e.g. a `display: table` 3507 // button, but seems like it would be doable. 3508 const auto* disp = aStyle.StyleDisplay(); 3509 const bool respectDisplay = [&] { 3510 if (disp->IsInlineFlow()) { 3511 // For compat, `display: inline` and co need to create an inline-block. 3512 return false; 3513 } 3514 switch (disp->DisplayInside()) { 3515 case StyleDisplayInside::Flex: 3516 case StyleDisplayInside::Grid: 3517 case StyleDisplayInside::FlowRoot: 3518 return true; 3519 default: 3520 return false; 3521 } 3522 }(); 3523 if (respectDisplay) { 3524 return nullptr; 3525 } 3526 static constexpr FrameConstructionData sBlockData[2] = { 3527 {&nsCSSFrameConstructor::ConstructNonScrollableBlock}, 3528 {&nsCSSFrameConstructor::ConstructScrollableBlock}, 3529 }; 3530 return &sBlockData[disp->IsScrollableOverflow()]; 3531 } 3532 3533 /* static */ 3534 const nsCSSFrameConstructor::FrameConstructionData* 3535 nsCSSFrameConstructor::FindImgData(const Element& aElement, 3536 ComputedStyle& aStyle) { 3537 if (nsImageFrame::ImageFrameTypeFor(aElement, aStyle) != 3538 nsImageFrame::ImageFrameType::ForElementRequest) { 3539 // content: url gets handled by the generic code-path. 3540 return nullptr; 3541 } 3542 3543 static constexpr FrameConstructionData sImgData(NS_NewImageFrame); 3544 return &sImgData; 3545 } 3546 3547 /* static */ 3548 const nsCSSFrameConstructor::FrameConstructionData* 3549 nsCSSFrameConstructor::FindImgControlData(const Element& aElement, 3550 ComputedStyle& aStyle) { 3551 if (nsImageFrame::ImageFrameTypeFor(aElement, aStyle) != 3552 nsImageFrame::ImageFrameType::ForElementRequest) { 3553 return nullptr; 3554 } 3555 3556 static constexpr FrameConstructionData sImgControlData( 3557 NS_NewImageControlFrame); 3558 return &sImgControlData; 3559 } 3560 3561 /* static */ 3562 const nsCSSFrameConstructor::FrameConstructionData* 3563 nsCSSFrameConstructor::FindSearchControlData(const Element& aElement, 3564 ComputedStyle& aStyle) { 3565 // Bug 1936648: Until we're absolutely sure we've solved the 3566 // accessibility issues around the clear search button, we're only 3567 // enabling the clear button in chrome contexts. See also Bug 1655503 3568 if (StaticPrefs::layout_forms_input_type_search_enabled() || 3569 aElement.OwnerDoc()->ChromeRulesEnabled()) { 3570 static constexpr FrameConstructionData sSearchControlData( 3571 NS_NewSearchControlFrame); 3572 return &sSearchControlData; 3573 } 3574 3575 static constexpr FrameConstructionData sTextControlData( 3576 NS_NewTextControlFrame); 3577 return &sTextControlData; 3578 } 3579 3580 /* static */ 3581 const nsCSSFrameConstructor::FrameConstructionData* 3582 nsCSSFrameConstructor::FindInputData(const Element& aElement, 3583 ComputedStyle& aStyle) { 3584 static constexpr FrameConstructionDataByInt sInputData[] = { 3585 SIMPLE_INT_CREATE(FormControlType::InputCheckbox, 3586 ToCreationFunc(NS_NewCheckboxRadioFrame)), 3587 SIMPLE_INT_CREATE(FormControlType::InputRadio, 3588 ToCreationFunc(NS_NewCheckboxRadioFrame)), 3589 SIMPLE_INT_CREATE(FormControlType::InputFile, NS_NewFileControlFrame), 3590 SIMPLE_INT_CHAIN(FormControlType::InputImage, 3591 nsCSSFrameConstructor::FindImgControlData), 3592 SIMPLE_INT_CREATE(FormControlType::InputEmail, NS_NewTextControlFrame), 3593 SIMPLE_INT_CREATE(FormControlType::InputText, NS_NewTextControlFrame), 3594 SIMPLE_INT_CREATE(FormControlType::InputTel, NS_NewTextControlFrame), 3595 SIMPLE_INT_CREATE(FormControlType::InputUrl, NS_NewTextControlFrame), 3596 SIMPLE_INT_CREATE(FormControlType::InputRange, NS_NewRangeFrame), 3597 SIMPLE_INT_CREATE(FormControlType::InputPassword, NS_NewTextControlFrame), 3598 SIMPLE_INT_CREATE(FormControlType::InputColor, NS_NewColorControlFrame), 3599 SIMPLE_INT_CHAIN(FormControlType::InputSearch, 3600 nsCSSFrameConstructor::FindSearchControlData), 3601 SIMPLE_INT_CREATE(FormControlType::InputNumber, NS_NewNumberControlFrame), 3602 SIMPLE_INT_CREATE(FormControlType::InputTime, NS_NewDateTimeControlFrame), 3603 SIMPLE_INT_CREATE(FormControlType::InputDate, NS_NewDateTimeControlFrame), 3604 SIMPLE_INT_CREATE(FormControlType::InputDatetimeLocal, 3605 NS_NewDateTimeControlFrame), 3606 // TODO: this is temporary until a frame is written: bug 888320 3607 SIMPLE_INT_CREATE(FormControlType::InputMonth, NS_NewTextControlFrame), 3608 // TODO: this is temporary until a frame is written: bug 888320 3609 SIMPLE_INT_CREATE(FormControlType::InputWeek, NS_NewTextControlFrame), 3610 SIMPLE_INT_CREATE(FormControlType::InputSubmit, 3611 NS_NewInputButtonControlFrame), 3612 SIMPLE_INT_CREATE(FormControlType::InputReset, 3613 NS_NewInputButtonControlFrame), 3614 SIMPLE_INT_CREATE(FormControlType::InputButton, 3615 NS_NewInputButtonControlFrame), 3616 // Keeping hidden inputs out of here on purpose for so they get frames by 3617 // display (in practice, none). 3618 }; 3619 3620 auto controlType = HTMLInputElement::FromNode(aElement)->ControlType(); 3621 3622 // radio and checkbox inputs with appearance:none should be constructed 3623 // by display type. (Note that we're not checking that appearance is 3624 // not (respectively) StyleAppearance::Radio and StyleAppearance::Checkbox.) 3625 if ((controlType == FormControlType::InputCheckbox || 3626 controlType == FormControlType::InputRadio) && 3627 !aStyle.StyleDisplay()->HasAppearance()) { 3628 return nullptr; 3629 } 3630 3631 return FindDataByInt(int32_t(controlType), aElement, aStyle, sInputData, 3632 std::size(sInputData)); 3633 } 3634 3635 /* static */ 3636 const nsCSSFrameConstructor::FrameConstructionData* 3637 nsCSSFrameConstructor::FindObjectData(const Element& aElement, 3638 ComputedStyle& aStyle) { 3639 uint32_t type; 3640 nsCOMPtr<nsIObjectLoadingContent> objContent = 3641 do_QueryInterface(const_cast<Element*>(&aElement)); 3642 NS_ASSERTION(objContent, 3643 "embed and object must implement " 3644 "nsIObjectLoadingContent!"); 3645 objContent->GetDisplayedType(&type); 3646 3647 static constexpr FrameConstructionDataByInt sObjectData[] = { 3648 // TODO(emilio): Can we remove the NS_NewEmptyFrame case and just use a 3649 // subdocument frame here? 3650 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_LOADING, 3651 NS_NewEmptyFrame), 3652 SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT, 3653 NS_NewSubDocumentFrame), 3654 // Nothing for TYPE_FALLBACK so we'll construct frames by display there 3655 }; 3656 3657 return FindDataByInt((int32_t)type, aElement, aStyle, sObjectData, 3658 std::size(sObjectData)); 3659 } 3660 3661 /* static */ 3662 const nsCSSFrameConstructor::FrameConstructionData* 3663 nsCSSFrameConstructor::FindCanvasData(const Element& aElement, 3664 ComputedStyle& aStyle) { 3665 // We want to check whether script is enabled on the document that 3666 // could be painting to the canvas. That's the owner document of 3667 // the canvas, except when the owner document is a static document, 3668 // in which case it's the original document it was cloned from. 3669 Document* doc = aElement.OwnerDoc(); 3670 if (doc->IsStaticDocument()) { 3671 doc = doc->GetOriginalDocument(); 3672 } 3673 if (!doc->IsScriptEnabled()) { 3674 return nullptr; 3675 } 3676 3677 static constexpr FrameConstructionData sCanvasData( 3678 NS_NewHTMLCanvasFrame, 0, PseudoStyleType::htmlCanvasContent); 3679 return &sCanvasData; 3680 } 3681 3682 static MOZ_NEVER_INLINE void DestroyFramesInList(PresShell* aPs, 3683 nsFrameList& aList) { 3684 nsIFrame::DestroyContext context(aPs); 3685 aList.DestroyFrames(context); 3686 } 3687 3688 void nsCSSFrameConstructor::ConstructFrameFromItemInternal( 3689 FrameConstructionItem& aItem, nsFrameConstructorState& aState, 3690 nsContainerFrame* aParentFrame, nsFrameList& aFrameList) { 3691 const FrameConstructionData* data = aItem.mFCData; 3692 NS_ASSERTION(data, "Must have frame construction data"); 3693 3694 uint32_t bits = data->mBits; 3695 3696 NS_ASSERTION(!(bits & FCDATA_FUNC_IS_DATA_GETTER), 3697 "Should have dealt with this inside the data finder"); 3698 3699 // Some sets of bits are not compatible with each other 3700 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2) \ 3701 NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \ 3702 "Only one of these bits should be set") 3703 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, 3704 FCDATA_FORCE_NULL_ABSPOS_CONTAINER); 3705 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS); 3706 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP); 3707 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH); 3708 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, 3709 FCDATA_DISALLOW_GENERATED_CONTENT); 3710 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES); 3711 CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, 3712 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS); 3713 CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS, 3714 FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS); 3715 #undef CHECK_ONLY_ONE_BIT 3716 MOZ_ASSERT( 3717 !(bits & FCDATA_IS_WRAPPER_ANON_BOX) || (bits & FCDATA_USE_CHILD_ITEMS), 3718 "Wrapper anon boxes should always have FCDATA_USE_CHILD_ITEMS"); 3719 3720 // Don't create a subdocument frame for iframes if we're creating extra frames 3721 if (aState.mCreatingExtraFrames && 3722 aItem.mContent->IsHTMLElement(nsGkAtoms::iframe)) { 3723 return; 3724 } 3725 3726 nsIContent* const content = aItem.mContent; 3727 nsIFrame* newFrame; 3728 nsIFrame* primaryFrame; 3729 ComputedStyle* const computedStyle = aItem.mComputedStyle; 3730 const nsStyleDisplay* display = computedStyle->StyleDisplay(); 3731 if (bits & FCDATA_FUNC_IS_FULL_CTOR) { 3732 newFrame = (this->*(data->mFunc.mFullConstructor))( 3733 aState, aItem, aParentFrame, display, aFrameList); 3734 MOZ_ASSERT(newFrame, "Full constructor failed"); 3735 primaryFrame = newFrame; 3736 } else { 3737 newFrame = (*data->mFunc.mCreationFunc)(mPresShell, computedStyle); 3738 3739 const bool allowOutOfFlow = !(bits & FCDATA_DISALLOW_OUT_OF_FLOW); 3740 const bool isPopup = aItem.mIsPopup; 3741 3742 nsContainerFrame* geometricParent = 3743 (isPopup || allowOutOfFlow) 3744 ? aState.GetGeometricParent(*display, aParentFrame) 3745 : aParentFrame; 3746 3747 // In the non-scrollframe case, primaryFrame and newFrame are equal; in the 3748 // scrollframe case, newFrame is the scrolled frame while primaryFrame is 3749 // the scrollframe. 3750 if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) && 3751 display->IsScrollableOverflow()) { 3752 nsContainerFrame* scrollframe = nullptr; 3753 BuildScrollContainerFrame(aState, content, computedStyle, newFrame, 3754 geometricParent, scrollframe); 3755 primaryFrame = scrollframe; 3756 } else { 3757 InitAndRestoreFrame(aState, content, geometricParent, newFrame); 3758 primaryFrame = newFrame; 3759 } 3760 3761 // If we need to create a block formatting context to wrap our 3762 // kids, do it now. 3763 nsIFrame* maybeAbsoluteContainingBlockStyleFrame = primaryFrame; 3764 nsIFrame* maybeAbsoluteContainingBlock = newFrame; 3765 nsIFrame* possiblyLeafFrame = newFrame; 3766 nsContainerFrame* outerFrame = nullptr; 3767 if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) { 3768 RefPtr<ComputedStyle> outerStyle = 3769 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 3770 data->mAnonBoxPseudo, computedStyle); 3771 #ifdef DEBUG 3772 nsContainerFrame* containerFrame = do_QueryFrame(newFrame); 3773 MOZ_ASSERT(containerFrame); 3774 #endif 3775 nsContainerFrame* container = static_cast<nsContainerFrame*>(newFrame); 3776 nsContainerFrame* innerFrame = NS_NewBlockFrame(mPresShell, outerStyle); 3777 InitAndRestoreFrame(aState, content, container, innerFrame); 3778 outerFrame = innerFrame; 3779 3780 SetInitialSingleChild(container, outerFrame); 3781 3782 container->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); 3783 3784 // Now figure out whether newFrame or outerFrame should be the 3785 // absolute container. 3786 if (outerFrame->IsAbsPosContainingBlock()) { 3787 maybeAbsoluteContainingBlock = outerFrame; 3788 maybeAbsoluteContainingBlockStyleFrame = outerFrame; 3789 innerFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 3790 } 3791 3792 // Our kids should go into the innerFrame. 3793 newFrame = innerFrame; 3794 } 3795 3796 aState.AddChild(primaryFrame, aFrameList, content, aParentFrame, 3797 allowOutOfFlow, allowOutOfFlow); 3798 3799 nsContainerFrame* newFrameAsContainer = do_QueryFrame(newFrame); 3800 if (newFrameAsContainer) { 3801 // Process the child content if requested 3802 nsFrameList childList; 3803 nsFrameConstructorSaveState absoluteSaveState; 3804 3805 if (bits & FCDATA_FORCE_NULL_ABSPOS_CONTAINER) { 3806 aState.PushAbsoluteContainingBlock(nullptr, nullptr, absoluteSaveState); 3807 } else if (!(bits & FCDATA_SKIP_ABSPOS_PUSH)) { 3808 maybeAbsoluteContainingBlock->AddStateBits( 3809 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 3810 if (maybeAbsoluteContainingBlockStyleFrame->IsAbsPosContainingBlock()) { 3811 auto* cf = 3812 static_cast<nsContainerFrame*>(maybeAbsoluteContainingBlock); 3813 aState.PushAbsoluteContainingBlock( 3814 cf, maybeAbsoluteContainingBlockStyleFrame, absoluteSaveState); 3815 } 3816 } 3817 3818 nsFrameConstructorSaveState floatSaveState; 3819 aState.MaybePushFloatContainingBlock(newFrameAsContainer, floatSaveState); 3820 3821 if (bits & FCDATA_USE_CHILD_ITEMS) { 3822 // At this point, we have not set up the auto value for this frame, and 3823 // no caller will have set it so it is not redundant and therefor will 3824 // not assert. 3825 AutoFrameConstructionPageName pageNameTracker(aState, 3826 newFrameAsContainer); 3827 ConstructFramesFromItemList( 3828 aState, aItem.mChildItems, newFrameAsContainer, 3829 bits & FCDATA_IS_WRAPPER_ANON_BOX, childList); 3830 } else { 3831 // Process the child frames. 3832 ProcessChildren(aState, content, computedStyle, newFrameAsContainer, 3833 !(bits & FCDATA_DISALLOW_GENERATED_CONTENT), childList, 3834 (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0, 3835 possiblyLeafFrame); 3836 } 3837 3838 if (bits & FCDATA_WRAP_KIDS_IN_BLOCKS) { 3839 nsFrameList newList; 3840 nsFrameList currentBlockList; 3841 nsIFrame* f; 3842 while ((f = childList.FirstChild()) != nullptr) { 3843 bool wrapFrame = IsInlineFrame(f) || IsFramePartOfIBSplit(f); 3844 if (!wrapFrame) { 3845 FlushAccumulatedBlock(aState, content, newFrameAsContainer, 3846 currentBlockList, newList); 3847 } 3848 3849 childList.RemoveFrame(f); 3850 if (wrapFrame) { 3851 currentBlockList.AppendFrame(nullptr, f); 3852 } else { 3853 newList.AppendFrame(nullptr, f); 3854 } 3855 } 3856 FlushAccumulatedBlock(aState, content, newFrameAsContainer, 3857 currentBlockList, newList); 3858 3859 if (childList.NotEmpty()) { 3860 // an error must have occurred, delete unprocessed frames 3861 DestroyFramesInList(mPresShell, childList); 3862 } 3863 3864 childList = std::move(newList); 3865 } 3866 3867 // Set the frame's initial child list. Note that MathML depends on this 3868 // being called even if childList is empty! 3869 newFrameAsContainer->SetInitialChildList(FrameChildListID::Principal, 3870 std::move(childList)); 3871 } 3872 } 3873 3874 NS_ASSERTION(newFrame->IsLineParticipant() == 3875 ((bits & FCDATA_IS_LINE_PARTICIPANT) != 0), 3876 "Incorrectly set FCDATA_IS_LINE_PARTICIPANT bits"); 3877 3878 // Even if mCreatingExtraFrames is set, we may need to SetPrimaryFrame for 3879 // generated content that doesn't have one yet. Note that we have to examine 3880 // the frame bit, because by this point mIsGeneratedContent has been cleared 3881 // on aItem. 3882 if ((!aState.mCreatingExtraFrames || 3883 (aItem.mContent->IsRootOfNativeAnonymousSubtree() && 3884 !aItem.mContent->GetPrimaryFrame())) && 3885 !(bits & FCDATA_SKIP_FRAMESET)) { 3886 aItem.mContent->SetPrimaryFrame(primaryFrame); 3887 ActiveLayerTracker::TransferActivityToFrame(aItem.mContent, primaryFrame); 3888 } 3889 } 3890 3891 static void GatherSubtreeElements(Element* aElement, 3892 nsTArray<Element*>& aElements) { 3893 aElements.AppendElement(aElement); 3894 StyleChildrenIterator iter(aElement); 3895 for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) { 3896 if (!c->IsElement()) { 3897 continue; 3898 } 3899 GatherSubtreeElements(c->AsElement(), aElements); 3900 } 3901 } 3902 3903 nsresult nsCSSFrameConstructor::GetAnonymousContent( 3904 nsIContent* aParent, nsIFrame* aParentFrame, 3905 nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent) { 3906 nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame); 3907 if (!creator) { 3908 return NS_OK; 3909 } 3910 3911 nsresult rv = creator->CreateAnonymousContent(aContent); 3912 if (NS_FAILED(rv)) { 3913 // CreateAnonymousContent failed, e.g. because the page has a <use> loop. 3914 return rv; 3915 } 3916 3917 if (aContent.IsEmpty()) { 3918 return NS_OK; 3919 } 3920 3921 const bool devtoolsEventsEnabled = 3922 mDocument->DevToolsAnonymousAndShadowEventsEnabled(); 3923 3924 MOZ_ASSERT(aParent->IsElement()); 3925 for (const auto& info : aContent) { 3926 // get our child's content and set its parent to our content 3927 nsIContent* content = info.mContent; 3928 content->SetIsNativeAnonymousRoot(); 3929 3930 BindContext context(*aParent->AsElement(), BindContext::ForNativeAnonymous); 3931 rv = content->BindToTree(context, *aParent); 3932 3933 if (NS_FAILED(rv)) { 3934 content->UnbindFromTree(); 3935 return rv; 3936 } 3937 3938 if (devtoolsEventsEnabled) { 3939 content->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ false); 3940 } 3941 } 3942 3943 // Some situations where we don't cache anonymous content styles: 3944 // 3945 // * when visibility or pointer-events is anything other than the initial 3946 // value; we rely on visibility and pointer-events inheriting into anonymous 3947 // content, but don't bother adding this state to the AnonymousContentKey, 3948 // since it's not so common. Note that with overlay scrollbars, scrollbars 3949 // always start off with pointer-events: none so we don't need to check for 3950 // that in that case. 3951 // 3952 // * when the medium is anything other than screen; some UA style sheet rules 3953 // apply in e.g. print medium, and will give different results from the 3954 // cached styles 3955 Maybe<bool> computedAllowStyleCaching; 3956 auto ComputeAllowStyleCaching = [&] { 3957 if (!StaticPrefs::layout_css_cached_scrollbar_styles_enabled()) { 3958 return false; 3959 } 3960 if (aParentFrame->StyleVisibility()->mVisible != StyleVisibility::Visible) { 3961 return false; 3962 } 3963 nsPresContext* pc = mPresShell->GetPresContext(); 3964 if (!pc->UseOverlayScrollbars() && 3965 aParentFrame->StyleUI()->ComputedPointerEvents() != 3966 StylePointerEvents::Auto) { 3967 return false; 3968 } 3969 if (pc->Medium() != nsGkAtoms::screen) { 3970 return false; 3971 } 3972 return true; 3973 }; 3974 3975 auto AllowStyleCaching = [&] { 3976 if (computedAllowStyleCaching.isNothing()) { 3977 computedAllowStyleCaching.emplace(ComputeAllowStyleCaching()); 3978 } 3979 return computedAllowStyleCaching.value(); 3980 }; 3981 3982 // Compute styles for the anonymous content tree. 3983 ServoStyleSet* styleSet = mPresShell->StyleSet(); 3984 for (auto& info : aContent) { 3985 Element* e = Element::FromNode(info.mContent); 3986 if (!e) { 3987 continue; 3988 } 3989 3990 if (info.mKey == AnonymousContentKey::None || !AllowStyleCaching()) { 3991 // Most NAC subtrees do not use caching of computed styles. Just go 3992 // ahead and eagerly style the subtree. 3993 styleSet->StyleNewSubtree(e); 3994 continue; 3995 } 3996 3997 // We have a NAC subtree for which we can use cached styles. 3998 AutoTArray<RefPtr<ComputedStyle>, 2> cachedStyles; 3999 AutoTArray<Element*, 2> elements; 4000 4001 GatherSubtreeElements(e, elements); 4002 styleSet->GetCachedAnonymousContentStyles(info.mKey, cachedStyles); 4003 4004 if (cachedStyles.IsEmpty()) { 4005 // We haven't stored cached styles for this kind of NAC subtree yet. 4006 // Eagerly compute those styles, then cache them for later. 4007 styleSet->StyleNewSubtree(e); 4008 for (Element* e : elements) { 4009 if (e->HasServoData()) { 4010 cachedStyles.AppendElement(ServoStyleSet::ResolveServoStyle(*e)); 4011 } else { 4012 cachedStyles.AppendElement(nullptr); 4013 } 4014 } 4015 styleSet->PutCachedAnonymousContentStyles(info.mKey, 4016 std::move(cachedStyles)); 4017 continue; 4018 } 4019 4020 // We previously stored cached styles for this kind of NAC subtree. 4021 // Iterate over them and set them on the subtree's elements. 4022 MOZ_ASSERT(cachedStyles.Length() == elements.Length(), 4023 "should always produce the same size NAC subtree"); 4024 for (size_t i = 0, len = cachedStyles.Length(); i != len; ++i) { 4025 if (cachedStyles[i]) { 4026 #ifdef DEBUG 4027 // Assert that our cached style is the same as one we could compute. 4028 RefPtr<ComputedStyle> cs = styleSet->ResolveStyleLazily(*elements[i]); 4029 MOZ_ASSERT( 4030 cachedStyles[i]->EqualForCachedAnonymousContentStyle(*cs), 4031 "cached anonymous content styles should be identical to those we " 4032 "would compute normally"); 4033 // All overlay scrollbars start off as inactive, so we can rely on their 4034 // pointer-events value being always none. 4035 MOZ_ASSERT(!mPresShell->GetPresContext()->UseOverlayScrollbars() || 4036 cs->StyleUI()->ComputedPointerEvents() == 4037 StylePointerEvents::None); 4038 #endif 4039 Servo_SetExplicitStyle(elements[i], cachedStyles[i]); 4040 } 4041 } 4042 } 4043 4044 return NS_OK; 4045 } 4046 4047 /* static */ 4048 const nsCSSFrameConstructor::FrameConstructionData* 4049 nsCSSFrameConstructor::FindXULTagData(const Element& aElement, 4050 ComputedStyle& aStyle) { 4051 MOZ_ASSERT(aElement.IsXULElement()); 4052 static constexpr FrameConstructionData kPopupData(NS_NewMenuPopupFrame, 4053 FCDATA_IS_POPUP); 4054 4055 static constexpr FrameConstructionDataByTag sXULTagData[] = { 4056 SIMPLE_TAG_CREATE(image, NS_NewXULImageFrame), 4057 SIMPLE_TAG_CREATE(treechildren, NS_NewTreeBodyFrame), 4058 SIMPLE_TAG_CHAIN(label, 4059 nsCSSFrameConstructor::FindXULLabelOrDescriptionData), 4060 SIMPLE_TAG_CHAIN(description, 4061 nsCSSFrameConstructor::FindXULLabelOrDescriptionData), 4062 SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame), 4063 SIMPLE_TAG_CREATE(editor, NS_NewSubDocumentFrame), 4064 SIMPLE_TAG_CREATE(browser, NS_NewSubDocumentFrame), 4065 SIMPLE_TAG_CREATE(splitter, NS_NewSplitterFrame), 4066 SIMPLE_TAG_CREATE(scrollbar, NS_NewScrollbarFrame), 4067 SIMPLE_TAG_CREATE(slider, NS_NewSliderFrame), 4068 SIMPLE_TAG_CREATE(thumb, NS_NewSimpleXULLeafFrame), 4069 SIMPLE_TAG_CREATE(scrollcorner, NS_NewSimpleXULLeafFrame), 4070 SIMPLE_TAG_CREATE(resizer, NS_NewSimpleXULLeafFrame), 4071 SIMPLE_TAG_CREATE(scrollbarbutton, NS_NewScrollbarButtonFrame), 4072 {nsGkAtoms::panel, kPopupData}, 4073 {nsGkAtoms::menupopup, kPopupData}, 4074 {nsGkAtoms::tooltip, kPopupData}, 4075 }; 4076 4077 return FindDataByTag(aElement, aStyle, sXULTagData, std::size(sXULTagData)); 4078 } 4079 4080 /* static */ 4081 const nsCSSFrameConstructor::FrameConstructionData* 4082 nsCSSFrameConstructor::FindXULLabelOrDescriptionData(const Element& aElement, 4083 ComputedStyle&) { 4084 // Follow CSS display value if no value attribute 4085 if (!aElement.HasAttr(nsGkAtoms::value)) { 4086 return nullptr; 4087 } 4088 4089 // Follow CSS display if there's no crop="center". 4090 if (!aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::crop, 4091 nsGkAtoms::center, eCaseMatters)) { 4092 return nullptr; 4093 } 4094 4095 static constexpr FrameConstructionData sMiddleCroppingData( 4096 NS_NewMiddleCroppingLabelFrame); 4097 return &sMiddleCroppingData; 4098 } 4099 4100 already_AddRefed<ComputedStyle> 4101 nsCSSFrameConstructor::BeginBuildingScrollContainerFrame( 4102 nsFrameConstructorState& aState, nsIContent* aContent, 4103 ComputedStyle* aContentStyle, nsContainerFrame* aParentFrame, 4104 PseudoStyleType aScrolledPseudo, bool aIsRoot, 4105 nsContainerFrame*& aNewFrame) { 4106 nsContainerFrame* scrollContainerFrame = aNewFrame; 4107 4108 if (!scrollContainerFrame) { 4109 scrollContainerFrame = 4110 NS_NewScrollContainerFrame(mPresShell, aContentStyle, aIsRoot); 4111 InitAndRestoreFrame(aState, aContent, aParentFrame, scrollContainerFrame); 4112 } 4113 4114 MOZ_ASSERT(scrollContainerFrame); 4115 4116 // if there are any anonymous children for the scroll frame, create 4117 // frames for them. 4118 // 4119 // We can't take the normal ProcessChildren path, because the NAC needs to 4120 // be parented to the scrollframe, and everything else needs to be parented 4121 // to the scrolledframe. 4122 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> scrollNAC; 4123 DebugOnly<nsresult> rv = 4124 GetAnonymousContent(aContent, scrollContainerFrame, scrollNAC); 4125 MOZ_ASSERT(NS_SUCCEEDED(rv)); 4126 nsFrameList anonymousList; 4127 if (!scrollNAC.IsEmpty()) { 4128 nsFrameConstructorSaveState floatSaveState; 4129 aState.MaybePushFloatContainingBlock(scrollContainerFrame, floatSaveState); 4130 4131 AutoFrameConstructionItemList items(this); 4132 AutoFrameConstructionPageName pageNameTracker(aState, scrollContainerFrame); 4133 AddFCItemsForAnonymousContent(aState, scrollContainerFrame, scrollNAC, 4134 items, pageNameTracker); 4135 ConstructFramesFromItemList(aState, items, scrollContainerFrame, 4136 /* aParentIsWrapperAnonBox = */ false, 4137 anonymousList); 4138 } 4139 4140 aNewFrame = scrollContainerFrame; 4141 scrollContainerFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); 4142 4143 // we used the style that was passed in. So resolve another one. 4144 ServoStyleSet* styleSet = mPresShell->StyleSet(); 4145 RefPtr<ComputedStyle> scrolledChildStyle = 4146 styleSet->ResolveInheritingAnonymousBoxStyle(aScrolledPseudo, 4147 aContentStyle); 4148 4149 scrollContainerFrame->SetInitialChildList(FrameChildListID::Principal, 4150 std::move(anonymousList)); 4151 4152 return scrolledChildStyle.forget(); 4153 } 4154 4155 void nsCSSFrameConstructor::FinishBuildingScrollContainerFrame( 4156 nsContainerFrame* aScrollContainerFrame, nsIFrame* aScrolledFrame) { 4157 aScrollContainerFrame->AppendFrames( 4158 FrameChildListID::Principal, nsFrameList(aScrolledFrame, aScrolledFrame)); 4159 } 4160 4161 void nsCSSFrameConstructor::BuildScrollContainerFrame( 4162 nsFrameConstructorState& aState, nsIContent* aContent, 4163 ComputedStyle* aContentStyle, nsIFrame* aScrolledFrame, 4164 nsContainerFrame* aParentFrame, nsContainerFrame*& aNewFrame) { 4165 RefPtr<ComputedStyle> scrolledContentStyle = 4166 BeginBuildingScrollContainerFrame( 4167 aState, aContent, aContentStyle, aParentFrame, 4168 PseudoStyleType::scrolledContent, false, aNewFrame); 4169 4170 aScrolledFrame->SetComputedStyleWithoutNotification(scrolledContentStyle); 4171 InitAndRestoreFrame(aState, aContent, aNewFrame, aScrolledFrame); 4172 4173 FinishBuildingScrollContainerFrame(aNewFrame, aScrolledFrame); 4174 } 4175 4176 const nsCSSFrameConstructor::FrameConstructionData* 4177 nsCSSFrameConstructor::FindDisplayData(const nsStyleDisplay& aDisplay, 4178 const Element& aElement) { 4179 static_assert(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)), 4180 "Check eParentTypeCount should not overflow"); 4181 4182 // The style system ensures that floated and positioned frames are 4183 // block-level. 4184 NS_ASSERTION( 4185 !(aDisplay.IsFloatingStyle() || aDisplay.IsAbsolutelyPositionedStyle()) || 4186 aDisplay.IsBlockOutsideStyle(), 4187 "Style system did not apply CSS2.1 section 9.7 fixups"); 4188 4189 // If this is "body", try propagating its scroll style to the viewport 4190 // Note that we need to do this even if the body is NOT scrollable; 4191 // it might have dynamically changed from scrollable to not scrollable, 4192 // and that might need to be propagated. 4193 // XXXbz is this the right place to do this? If this code moves, 4194 // make this function static. 4195 bool propagatedScrollToViewport = false; 4196 if (aElement.IsHTMLElement(nsGkAtoms::body)) { 4197 if (nsPresContext* presContext = mPresShell->GetPresContext()) { 4198 propagatedScrollToViewport = 4199 presContext->UpdateViewportScrollStylesOverride() == &aElement; 4200 MOZ_ASSERT(!propagatedScrollToViewport || 4201 !mPresShell->GetPresContext()->IsPaginated(), 4202 "Shouldn't propagate scroll in paginated contexts"); 4203 } 4204 } 4205 4206 switch (aDisplay.DisplayInside()) { 4207 case StyleDisplayInside::Flow: 4208 case StyleDisplayInside::FlowRoot: { 4209 if (aDisplay.IsInlineFlow()) { 4210 static constexpr FrameConstructionData data( 4211 &nsCSSFrameConstructor::ConstructInline, 4212 FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT); 4213 return &data; 4214 } 4215 4216 // If the frame is a block-level frame and is scrollable, then wrap it in 4217 // a scroll frame. Except we don't want to do that for paginated contexts 4218 // for frames that are block-outside and aren't frames for native 4219 // anonymous stuff. 4220 // XXX Ignore tables for the time being (except caption) 4221 const uint32_t kCaptionCtorFlags = 4222 FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable); 4223 const bool caption = aDisplay.mDisplay == StyleDisplay::TableCaption; 4224 const bool needScrollFrame = 4225 aDisplay.IsScrollableOverflow() && !propagatedScrollToViewport; 4226 if (needScrollFrame) { 4227 const bool suppressScrollFrame = 4228 mPresShell->GetPresContext()->IsPaginated() && 4229 aDisplay.IsBlockOutsideStyle() && 4230 !aElement.IsInNativeAnonymousSubtree(); 4231 if (!suppressScrollFrame) { 4232 static constexpr FrameConstructionData sScrollableBlockData[2] = { 4233 {&nsCSSFrameConstructor::ConstructScrollableBlock}, 4234 {&nsCSSFrameConstructor::ConstructScrollableBlock, 4235 kCaptionCtorFlags}}; 4236 return &sScrollableBlockData[caption]; 4237 } 4238 } 4239 4240 // Handle various non-scrollable blocks. 4241 static constexpr FrameConstructionData sNonScrollableBlockData[2] = { 4242 {&nsCSSFrameConstructor::ConstructNonScrollableBlock}, 4243 {&nsCSSFrameConstructor::ConstructNonScrollableBlock, 4244 kCaptionCtorFlags}}; 4245 return &sNonScrollableBlockData[caption]; 4246 } 4247 case StyleDisplayInside::Table: { 4248 static constexpr FrameConstructionData data( 4249 &nsCSSFrameConstructor::ConstructTable); 4250 return &data; 4251 } 4252 // NOTE: In the unlikely event that we add another table-part here that 4253 // has a desired-parent-type (& hence triggers table fixup), we'll need to 4254 // also update the flexbox chunk in ComputedStyle::ApplyStyleFixups(). 4255 case StyleDisplayInside::TableRowGroup: { 4256 static constexpr FrameConstructionData data( 4257 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup, 4258 FCDATA_IS_TABLE_PART | 4259 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)); 4260 return &data; 4261 } 4262 case StyleDisplayInside::TableColumn: { 4263 static constexpr FrameConstructionData data( 4264 &nsCSSFrameConstructor::ConstructTableCol, 4265 FCDATA_IS_TABLE_PART | 4266 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup)); 4267 return &data; 4268 } 4269 case StyleDisplayInside::TableColumnGroup: { 4270 static constexpr FrameConstructionData data( 4271 ToCreationFunc(NS_NewTableColGroupFrame), 4272 FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW | 4273 FCDATA_SKIP_ABSPOS_PUSH | 4274 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)); 4275 return &data; 4276 } 4277 case StyleDisplayInside::TableHeaderGroup: { 4278 static constexpr FrameConstructionData data( 4279 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup, 4280 FCDATA_IS_TABLE_PART | 4281 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)); 4282 return &data; 4283 } 4284 case StyleDisplayInside::TableFooterGroup: { 4285 static constexpr FrameConstructionData data( 4286 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup, 4287 FCDATA_IS_TABLE_PART | 4288 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)); 4289 return &data; 4290 } 4291 case StyleDisplayInside::TableRow: { 4292 static constexpr FrameConstructionData data( 4293 &nsCSSFrameConstructor::ConstructTableRowOrRowGroup, 4294 FCDATA_IS_TABLE_PART | 4295 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup)); 4296 return &data; 4297 } 4298 case StyleDisplayInside::TableCell: { 4299 static constexpr FrameConstructionData data( 4300 &nsCSSFrameConstructor::ConstructTableCell, 4301 FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow)); 4302 return &data; 4303 } 4304 case StyleDisplayInside::Flex: 4305 case StyleDisplayInside::WebkitBox: { 4306 static constexpr FrameConstructionData nonScrollableData( 4307 ToCreationFunc(NS_NewFlexContainerFrame)); 4308 static constexpr FrameConstructionData data( 4309 ToCreationFunc(NS_NewFlexContainerFrame), 4310 FCDATA_MAY_NEED_SCROLLFRAME); 4311 return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData 4312 : &data; 4313 } 4314 case StyleDisplayInside::Grid: { 4315 static constexpr FrameConstructionData nonScrollableData( 4316 ToCreationFunc(NS_NewGridContainerFrame)); 4317 static constexpr FrameConstructionData data( 4318 ToCreationFunc(NS_NewGridContainerFrame), 4319 FCDATA_MAY_NEED_SCROLLFRAME); 4320 return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData 4321 : &data; 4322 } 4323 case StyleDisplayInside::Ruby: { 4324 static constexpr FrameConstructionData data[] = { 4325 {&nsCSSFrameConstructor::ConstructBlockRubyFrame, 4326 FCDATA_MAY_NEED_SCROLLFRAME}, 4327 {ToCreationFunc(NS_NewRubyFrame), FCDATA_IS_LINE_PARTICIPANT}}; 4328 bool isInline = aDisplay.DisplayOutside() == StyleDisplayOutside::Inline; 4329 return &data[isInline]; 4330 } 4331 case StyleDisplayInside::RubyBase: { 4332 static constexpr FrameConstructionData data( 4333 ToCreationFunc(NS_NewRubyBaseFrame), 4334 FCDATA_IS_LINE_PARTICIPANT | 4335 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer)); 4336 return &data; 4337 } 4338 case StyleDisplayInside::RubyBaseContainer: { 4339 static constexpr FrameConstructionData data( 4340 ToCreationFunc(NS_NewRubyBaseContainerFrame), 4341 FCDATA_IS_LINE_PARTICIPANT | 4342 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby)); 4343 return &data; 4344 } 4345 case StyleDisplayInside::RubyText: { 4346 static constexpr FrameConstructionData data( 4347 ToCreationFunc(NS_NewRubyTextFrame), 4348 FCDATA_IS_LINE_PARTICIPANT | 4349 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer)); 4350 return &data; 4351 } 4352 case StyleDisplayInside::RubyTextContainer: { 4353 static constexpr FrameConstructionData data( 4354 ToCreationFunc(NS_NewRubyTextContainerFrame), 4355 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby)); 4356 return &data; 4357 } 4358 default: 4359 MOZ_ASSERT_UNREACHABLE("unknown 'display' value"); 4360 return nullptr; 4361 } 4362 } 4363 4364 nsIFrame* nsCSSFrameConstructor::ConstructScrollableBlock( 4365 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 4366 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay, 4367 nsFrameList& aFrameList) { 4368 nsIContent* const content = aItem.mContent; 4369 ComputedStyle* const computedStyle = aItem.mComputedStyle; 4370 4371 nsContainerFrame* newFrame = nullptr; 4372 RefPtr<ComputedStyle> scrolledContentStyle = 4373 BeginBuildingScrollContainerFrame( 4374 aState, content, computedStyle, 4375 aState.GetGeometricParent(*aDisplay, aParentFrame), 4376 PseudoStyleType::scrolledContent, false, newFrame); 4377 4378 // Create our block frame 4379 // pass a temporary stylecontext, the correct one will be set later 4380 nsContainerFrame* scrolledFrame = NS_NewBlockFrame(mPresShell, computedStyle); 4381 4382 // Make sure to AddChild before we call ConstructBlock so that we 4383 // end up before our descendants in fixed-pos lists as needed. 4384 aState.AddChild(newFrame, aFrameList, content, aParentFrame); 4385 4386 nsFrameList blockList; 4387 ConstructBlock(aState, content, newFrame, newFrame, scrolledContentStyle, 4388 &scrolledFrame, blockList, 4389 newFrame->IsAbsPosContainingBlock() ? newFrame : nullptr); 4390 4391 MOZ_ASSERT(blockList.OnlyChild() == scrolledFrame, 4392 "Scrollframe's frameList should be exactly the scrolled frame!"); 4393 FinishBuildingScrollContainerFrame(newFrame, scrolledFrame); 4394 4395 return newFrame; 4396 } 4397 4398 nsIFrame* nsCSSFrameConstructor::ConstructNonScrollableBlock( 4399 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 4400 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay, 4401 nsFrameList& aFrameList) { 4402 ComputedStyle* const computedStyle = aItem.mComputedStyle; 4403 nsContainerFrame* newFrame = NS_NewBlockFrame(mPresShell, computedStyle); 4404 ConstructBlock(aState, aItem.mContent, 4405 aState.GetGeometricParent(*aDisplay, aParentFrame), 4406 aParentFrame, computedStyle, &newFrame, aFrameList, 4407 newFrame->IsAbsPosContainingBlock() ? newFrame : nullptr); 4408 return newFrame; 4409 } 4410 4411 void nsCSSFrameConstructor::InitAndRestoreFrame( 4412 const nsFrameConstructorState& aState, nsIContent* aContent, 4413 nsContainerFrame* aParentFrame, nsIFrame* aNewFrame, bool aAllowCounters) { 4414 MOZ_ASSERT(aNewFrame, "Null frame cannot be initialized"); 4415 4416 // Initialize the frame 4417 aNewFrame->Init(aContent, aParentFrame, nullptr); 4418 aNewFrame->AddStateBits(aState.mAdditionalStateBits); 4419 4420 if (aState.mFrameState) { 4421 // Restore frame state for just the newly created frame. 4422 RestoreFrameStateFor(aNewFrame, aState.mFrameState); 4423 } 4424 4425 if (aAllowCounters && 4426 mContainStyleScopeManager.AddCounterChanges(aNewFrame)) { 4427 CountersDirty(); 4428 } 4429 } 4430 4431 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::ResolveComputedStyle( 4432 nsIContent* aContent) { 4433 if (auto* element = Element::FromNode(aContent)) { 4434 return ServoStyleSet::ResolveServoStyle(*element); 4435 } 4436 4437 MOZ_ASSERT(aContent->IsText(), 4438 "shouldn't waste time creating ComputedStyles for " 4439 "comments and processing instructions"); 4440 4441 Element* parent = aContent->GetFlattenedTreeParentElement(); 4442 MOZ_ASSERT(parent, "Text out of the flattened tree?"); 4443 4444 // FIXME(emilio): The const_cast is unfortunate, but it's not worse than what 4445 // we did before. 4446 // 4447 // We could use ResolveServoStyle, but that would involve extra unnecessary 4448 // refcount traffic... 4449 auto* parentStyle = 4450 const_cast<ComputedStyle*>(Servo_Element_GetMaybeOutOfDateStyle(parent)); 4451 MOZ_ASSERT(parentStyle, 4452 "How are we inserting text frames in an unstyled element?"); 4453 return mPresShell->StyleSet()->ResolveStyleForText(aContent, parentStyle); 4454 } 4455 4456 // MathML Mod - RBS 4457 void nsCSSFrameConstructor::FlushAccumulatedBlock( 4458 nsFrameConstructorState& aState, nsIContent* aContent, 4459 nsContainerFrame* aParentFrame, nsFrameList& aBlockList, 4460 nsFrameList& aNewList) { 4461 if (aBlockList.IsEmpty()) { 4462 // Nothing to do 4463 return; 4464 } 4465 4466 auto anonPseudo = PseudoStyleType::mozMathMLAnonymousBlock; 4467 4468 ComputedStyle* parentContext = 4469 nsIFrame::CorrectStyleParentFrame(aParentFrame, anonPseudo)->Style(); 4470 ServoStyleSet* styleSet = mPresShell->StyleSet(); 4471 RefPtr<ComputedStyle> blockContext = 4472 styleSet->ResolveInheritingAnonymousBoxStyle(anonPseudo, parentContext); 4473 4474 // then, create a block frame that will wrap the child frames. Make it a 4475 // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this 4476 // is not a suitable block. 4477 nsContainerFrame* blockFrame = 4478 NS_NewMathMLmathBlockFrame(mPresShell, blockContext); 4479 4480 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame); 4481 ReparentFrames(this, blockFrame, aBlockList, false); 4482 // We have to walk over aBlockList before we hand it over to blockFrame. 4483 for (nsIFrame* f : aBlockList) { 4484 f->SetParentIsWrapperAnonBox(); 4485 } 4486 // abs-pos and floats are disabled in MathML children so we don't have to 4487 // worry about messing up those. 4488 blockFrame->SetInitialChildList(FrameChildListID::Principal, 4489 std::move(aBlockList)); 4490 aNewList.AppendFrame(nullptr, blockFrame); 4491 } 4492 4493 // Only <math> elements can be floated or positioned. All other MathML 4494 // should be in-flow. 4495 #define MATHML_DATA(_func) \ 4496 FrameConstructionData { \ 4497 _func, FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_FORCE_NULL_ABSPOS_CONTAINER | \ 4498 FCDATA_WRAP_KIDS_IN_BLOCKS \ 4499 } 4500 4501 #define SIMPLE_MATHML_CREATE(_tag, _func) {nsGkAtoms::_tag, MATHML_DATA(_func)} 4502 4503 /* static */ 4504 const nsCSSFrameConstructor::FrameConstructionData* 4505 nsCSSFrameConstructor::FindMathMLData(const Element& aElement, 4506 ComputedStyle& aStyle) { 4507 MOZ_ASSERT(aElement.IsMathMLElement()); 4508 4509 nsAtom* tag = aElement.NodeInfo()->NameAtom(); 4510 4511 // Handle <math> specially, because it sometimes produces inlines 4512 if (tag == nsGkAtoms::math) { 4513 // The IsBlockOutsideStyle() check must match what 4514 // specified::Display::equivalent_block_display is checking for 4515 // already-block-outside things. Though the behavior here for the 4516 // display:table case is pretty weird... 4517 if (aStyle.StyleDisplay()->IsBlockOutsideStyle()) { 4518 static constexpr FrameConstructionData sBlockMathData( 4519 ToCreationFunc(NS_NewMathMLmathBlockFrame), 4520 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | FCDATA_WRAP_KIDS_IN_BLOCKS); 4521 return &sBlockMathData; 4522 } 4523 4524 static constexpr FrameConstructionData sInlineMathData( 4525 ToCreationFunc(NS_NewMathMLmathInlineFrame), 4526 FCDATA_FORCE_NULL_ABSPOS_CONTAINER | FCDATA_IS_LINE_PARTICIPANT | 4527 FCDATA_WRAP_KIDS_IN_BLOCKS); 4528 return &sInlineMathData; 4529 } 4530 4531 // Special case for elements with a display value other than none 4532 // specified in mathml.css that are not handled by this function. 4533 // These shouldn't be rendered as an mrow. 4534 if (tag == nsGkAtoms::mtable || tag == nsGkAtoms::mtr || 4535 tag == nsGkAtoms::mlabeledtr || tag == nsGkAtoms::mtd) { 4536 return nullptr; 4537 } 4538 4539 static constexpr FrameConstructionDataByTag sMathMLData[] = { 4540 SIMPLE_MATHML_CREATE(annotation, NS_NewMathMLTokenFrame), 4541 SIMPLE_MATHML_CREATE(annotation_xml, NS_NewMathMLmrowFrame), 4542 SIMPLE_MATHML_CREATE(mi, NS_NewMathMLTokenFrame), 4543 SIMPLE_MATHML_CREATE(mn, NS_NewMathMLTokenFrame), 4544 SIMPLE_MATHML_CREATE(ms, NS_NewMathMLTokenFrame), 4545 SIMPLE_MATHML_CREATE(mtext, NS_NewMathMLTokenFrame), 4546 SIMPLE_MATHML_CREATE(mo, NS_NewMathMLmoFrame), 4547 SIMPLE_MATHML_CREATE(mfrac, NS_NewMathMLmfracFrame), 4548 SIMPLE_MATHML_CREATE(msup, NS_NewMathMLmmultiscriptsFrame), 4549 SIMPLE_MATHML_CREATE(msub, NS_NewMathMLmmultiscriptsFrame), 4550 SIMPLE_MATHML_CREATE(msubsup, NS_NewMathMLmmultiscriptsFrame), 4551 SIMPLE_MATHML_CREATE(munder, NS_NewMathMLmunderoverFrame), 4552 SIMPLE_MATHML_CREATE(mover, NS_NewMathMLmunderoverFrame), 4553 SIMPLE_MATHML_CREATE(munderover, NS_NewMathMLmunderoverFrame), 4554 SIMPLE_MATHML_CREATE(mphantom, NS_NewMathMLmrowFrame), 4555 SIMPLE_MATHML_CREATE(mpadded, NS_NewMathMLmpaddedFrame), 4556 SIMPLE_MATHML_CREATE(mspace, NS_NewMathMLmspaceFrame), 4557 SIMPLE_MATHML_CREATE(none, NS_NewMathMLmrowFrame), 4558 SIMPLE_MATHML_CREATE(mprescripts, NS_NewMathMLmrowFrame), 4559 SIMPLE_MATHML_CREATE(mfenced, NS_NewMathMLmrowFrame), 4560 SIMPLE_MATHML_CREATE(mmultiscripts, NS_NewMathMLmmultiscriptsFrame), 4561 SIMPLE_MATHML_CREATE(mstyle, NS_NewMathMLmrowFrame), 4562 SIMPLE_MATHML_CREATE(msqrt, NS_NewMathMLmrootFrame), 4563 SIMPLE_MATHML_CREATE(mroot, NS_NewMathMLmrootFrame), 4564 SIMPLE_MATHML_CREATE(maction, NS_NewMathMLmrowFrame), 4565 SIMPLE_MATHML_CREATE(mrow, NS_NewMathMLmrowFrame), 4566 SIMPLE_MATHML_CREATE(merror, NS_NewMathMLmrowFrame), 4567 SIMPLE_MATHML_CREATE(menclose, NS_NewMathMLmencloseFrame), 4568 SIMPLE_MATHML_CREATE(semantics, NS_NewMathMLmrowFrame)}; 4569 4570 if (const auto* data = FindDataByTag(aElement, aStyle, sMathMLData, 4571 std::size(sMathMLData))) { 4572 return data; 4573 } 4574 // Unknown MathML elements render as an mrow, see: 4575 // https://w3c.github.io/mathml-core/#ref-for-dfn-unknown-mathml-element-2 4576 static constexpr FrameConstructionData sMrowData = 4577 MATHML_DATA(NS_NewMathMLmrowFrame); 4578 return &sMrowData; 4579 } 4580 4581 nsContainerFrame* nsCSSFrameConstructor::ConstructFrameWithAnonymousChild( 4582 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 4583 nsContainerFrame* aParentFrame, nsFrameList& aFrameList, 4584 ContainerFrameCreationFunc aConstructor, 4585 ContainerFrameCreationFunc aInnerConstructor, PseudoStyleType aInnerPseudo, 4586 bool aCandidateRootFrame) { 4587 nsIContent* const content = aItem.mContent; 4588 ComputedStyle* const computedStyle = aItem.mComputedStyle; 4589 4590 // Create the outer frame: 4591 nsContainerFrame* newFrame = aConstructor(mPresShell, computedStyle); 4592 4593 InitAndRestoreFrame(aState, content, 4594 aCandidateRootFrame 4595 ? aState.GetGeometricParent( 4596 *computedStyle->StyleDisplay(), aParentFrame) 4597 : aParentFrame, 4598 newFrame); 4599 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); 4600 4601 // Create the pseudo SC for the anonymous wrapper child as a child of the SC: 4602 RefPtr<ComputedStyle> scForAnon = 4603 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(aInnerPseudo, 4604 computedStyle); 4605 4606 // Create the anonymous inner wrapper frame 4607 nsContainerFrame* innerFrame = aInnerConstructor(mPresShell, scForAnon); 4608 4609 InitAndRestoreFrame(aState, content, newFrame, innerFrame); 4610 4611 // Put the newly created frames into the right child list 4612 SetInitialSingleChild(newFrame, innerFrame); 4613 4614 aState.AddChild(newFrame, aFrameList, content, aParentFrame, 4615 aCandidateRootFrame, aCandidateRootFrame); 4616 4617 if (!mRootElementFrame && aCandidateRootFrame) { 4618 mRootElementFrame = newFrame; 4619 } 4620 4621 nsFrameConstructorSaveState floatSaveState; 4622 aState.MaybePushFloatContainingBlock(innerFrame, floatSaveState); 4623 4624 nsFrameList childList; 4625 4626 // Process children 4627 if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) { 4628 ConstructFramesFromItemList( 4629 aState, aItem.mChildItems, innerFrame, 4630 aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX, childList); 4631 } else { 4632 ProcessChildren(aState, content, computedStyle, innerFrame, true, childList, 4633 false); 4634 } 4635 4636 // Set the inner wrapper frame's initial primary list 4637 innerFrame->SetInitialChildList(FrameChildListID::Principal, 4638 std::move(childList)); 4639 4640 return newFrame; 4641 } 4642 4643 nsIFrame* nsCSSFrameConstructor::ConstructOuterSVG( 4644 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 4645 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay, 4646 nsFrameList& aFrameList) { 4647 return ConstructFrameWithAnonymousChild( 4648 aState, aItem, aParentFrame, aFrameList, NS_NewSVGOuterSVGFrame, 4649 NS_NewSVGOuterSVGAnonChildFrame, PseudoStyleType::mozSVGOuterSVGAnonChild, 4650 true); 4651 } 4652 4653 nsIFrame* nsCSSFrameConstructor::ConstructMarker( 4654 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 4655 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay, 4656 nsFrameList& aFrameList) { 4657 return ConstructFrameWithAnonymousChild( 4658 aState, aItem, aParentFrame, aFrameList, NS_NewSVGMarkerFrame, 4659 NS_NewSVGMarkerAnonChildFrame, PseudoStyleType::mozSVGMarkerAnonChild, 4660 false); 4661 } 4662 4663 // Only outer <svg> elements can be floated or positioned. All other SVG 4664 // should be in-flow. 4665 #define SIMPLE_SVG_FCDATA(_func) \ 4666 FrameConstructionData(ToCreationFunc(_func), \ 4667 FCDATA_DISALLOW_OUT_OF_FLOW | \ 4668 FCDATA_SKIP_ABSPOS_PUSH | \ 4669 FCDATA_DISALLOW_GENERATED_CONTENT) 4670 #define SIMPLE_SVG_CREATE(_tag, _func) \ 4671 {nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func)} 4672 4673 /* static */ 4674 const nsCSSFrameConstructor::FrameConstructionData* 4675 nsCSSFrameConstructor::FindSVGData(const Element& aElement, 4676 nsIFrame* aParentFrame, 4677 bool aIsWithinSVGText, 4678 bool aAllowsTextPathChild, 4679 ComputedStyle& aStyle) { 4680 MOZ_ASSERT(aElement.IsSVGElement()); 4681 4682 static constexpr FrameConstructionData sSuppressData = SUPPRESS_FCDATA(); 4683 static constexpr FrameConstructionData sContainerData = 4684 SIMPLE_SVG_FCDATA(NS_NewSVGContainerFrame); 4685 4686 bool parentIsSVG = aIsWithinSVGText; 4687 nsIContent* parentContent = 4688 aParentFrame ? aParentFrame->GetContent() : nullptr; 4689 4690 nsAtom* tag = aElement.NodeInfo()->NameAtom(); 4691 4692 // XXXbz should this really be based on the tag of the parent frame's content? 4693 // Should it not be based on the type of the parent frame (e.g. whether it's 4694 // an SVG frame)? 4695 if (parentContent) { 4696 // It's not clear whether the SVG spec intends to allow any SVG 4697 // content within svg:foreignObject at all (SVG 1.1, section 4698 // 23.2), but if it does, it better be svg:svg. So given that 4699 // we're allowing it, treat it as a non-SVG parent. 4700 parentIsSVG = 4701 parentContent->IsSVGElement() && 4702 parentContent->NodeInfo()->NameAtom() != nsGkAtoms::foreignObject; 4703 } 4704 4705 if ((tag != nsGkAtoms::svg && !parentIsSVG) || 4706 (tag == nsGkAtoms::desc || tag == nsGkAtoms::title || 4707 tag == nsGkAtoms::metadata)) { 4708 // Sections 5.1 and G.4 of SVG 1.1 say that SVG elements other than 4709 // svg:svg not contained within svg:svg are incorrect, although they 4710 // don't seem to specify error handling. Ignore them, since many of 4711 // our frame classes can't deal. It *may* be that the document 4712 // should at that point be considered in error according to F.2, but 4713 // it's hard to tell. 4714 // 4715 // Style mutation can't change this situation, so don't bother 4716 // adding to the undisplayed content map. 4717 // 4718 // We don't currently handle any UI for desc/title/metadata 4719 return &sSuppressData; 4720 } 4721 4722 // We don't need frames for animation elements 4723 if (aElement.IsSVGAnimationElement()) { 4724 return &sSuppressData; 4725 } 4726 4727 if (tag == nsGkAtoms::svg && !parentIsSVG) { 4728 // We need outer <svg> elements to have an SVGOuterSVGFrame regardless 4729 // of whether they fail conditional processing attributes, since various 4730 // SVG frames assume that one exists. We handle the non-rendering 4731 // of failing outer <svg> element contents like <switch> statements, 4732 // and do the PassesConditionalProcessingTests call in 4733 // SVGOuterSVGFrame::Init. 4734 static constexpr FrameConstructionData sOuterSVGData( 4735 &nsCSSFrameConstructor::ConstructOuterSVG); 4736 return &sOuterSVGData; 4737 } 4738 4739 if (tag == nsGkAtoms::marker) { 4740 static constexpr FrameConstructionData sMarkerSVGData( 4741 &nsCSSFrameConstructor::ConstructMarker); 4742 return &sMarkerSVGData; 4743 } 4744 4745 if (!aElement.PassesConditionalProcessingTests()) { 4746 // Elements with failing conditional processing attributes never get 4747 // rendered. Note that this is not where we select which frame in a 4748 // <switch> to render! That happens in SVGSwitchFrame::PaintSVG. 4749 if (aIsWithinSVGText) { 4750 // SVGTextFrame doesn't handle conditional processing attributes, 4751 // so don't create frames for descendants of <text> with failing 4752 // attributes. We need frames not to be created so that text layout 4753 // is correct. 4754 return &sSuppressData; 4755 } 4756 // If we're not inside <text>, create an SVGContainerFrame (which is a 4757 // frame that doesn't render) so that paint servers can still be referenced, 4758 // even if they live inside an element with failing conditional processing 4759 // attributes. 4760 return &sContainerData; 4761 } 4762 4763 // Ensure that a stop frame is a child of a gradient and that gradients 4764 // can only have stop children. 4765 bool parentIsGradient = aParentFrame && static_cast<SVGGradientFrame*>( 4766 do_QueryFrame(aParentFrame)); 4767 bool stop = (tag == nsGkAtoms::stop); 4768 if ((parentIsGradient && !stop) || (!parentIsGradient && stop)) { 4769 return &sSuppressData; 4770 } 4771 4772 // Prevent bad frame types being children of filters or parents of filter 4773 // primitives. If aParentFrame is null, we know that the frame that will 4774 // be created will be an nsInlineFrame, so it can never be a filter. 4775 bool parentIsFilter = aParentFrame && aParentFrame->IsSVGFilterFrame(); 4776 if ((parentIsFilter && !aElement.IsSVGFilterPrimitiveElement()) || 4777 (!parentIsFilter && aElement.IsSVGFilterPrimitiveElement())) { 4778 return &sSuppressData; 4779 } 4780 4781 // Prevent bad frame types being children of filter primitives or parents of 4782 // filter primitive children. If aParentFrame is null, we know that the frame 4783 // that will be created will be an nsInlineFrame, so it can never be a filter 4784 // primitive. 4785 bool parentIsFEContainerFrame = 4786 aParentFrame && aParentFrame->IsSVGFEContainerFrame(); 4787 if ((parentIsFEContainerFrame && 4788 !aElement.IsSVGFilterPrimitiveChildElement()) || 4789 (!parentIsFEContainerFrame && 4790 aElement.IsSVGFilterPrimitiveChildElement())) { 4791 return &sSuppressData; 4792 } 4793 4794 // Special cases for text/tspan/textPath, because the kind of frame 4795 // they get depends on the parent frame. We ignore 'a' elements when 4796 // determining the parent, however. 4797 if (aIsWithinSVGText) { 4798 // If aIsWithinSVGText is true, then we know that the "SVG text uses 4799 // CSS frames" pref was true when this SVG fragment was first constructed. 4800 // 4801 // FIXME(bug 1588477) Don't render stuff in display: contents / Shadow DOM 4802 // subtrees, because TextCorrespondenceRecorder in the SVG text code doesn't 4803 // really know how to deal with it. This kinda sucks. :( 4804 if (aParentFrame && aParentFrame->GetContent() != aElement.GetParent()) { 4805 return &sSuppressData; 4806 } 4807 4808 // We don't use ConstructInline because we want different behavior 4809 // for generated content. 4810 static constexpr FrameConstructionData sTSpanData( 4811 ToCreationFunc(NS_NewInlineFrame), 4812 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_SKIP_ABSPOS_PUSH | 4813 FCDATA_DISALLOW_GENERATED_CONTENT | FCDATA_IS_LINE_PARTICIPANT | 4814 FCDATA_IS_INLINE | FCDATA_USE_CHILD_ITEMS); 4815 if (tag == nsGkAtoms::textPath) { 4816 if (aAllowsTextPathChild) { 4817 return &sTSpanData; 4818 } 4819 } else if (tag == nsGkAtoms::tspan || tag == nsGkAtoms::a) { 4820 return &sTSpanData; 4821 } 4822 return &sSuppressData; 4823 } else if (tag == nsGkAtoms::tspan || tag == nsGkAtoms::textPath) { 4824 return &sSuppressData; 4825 } 4826 4827 static constexpr FrameConstructionDataByTag sSVGData[] = { 4828 SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame), 4829 SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame), 4830 SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame), 4831 SIMPLE_SVG_CREATE(symbol, NS_NewSVGSymbolFrame), 4832 SIMPLE_SVG_CREATE(polygon, NS_NewSVGGeometryFrame), 4833 SIMPLE_SVG_CREATE(polyline, NS_NewSVGGeometryFrame), 4834 SIMPLE_SVG_CREATE(circle, NS_NewSVGGeometryFrame), 4835 SIMPLE_SVG_CREATE(ellipse, NS_NewSVGGeometryFrame), 4836 SIMPLE_SVG_CREATE(line, NS_NewSVGGeometryFrame), 4837 SIMPLE_SVG_CREATE(rect, NS_NewSVGGeometryFrame), 4838 SIMPLE_SVG_CREATE(path, NS_NewSVGGeometryFrame), 4839 SIMPLE_SVG_CREATE(defs, NS_NewSVGContainerFrame), 4840 {nsGkAtoms::text, 4841 {NS_NewSVGTextFrame, 4842 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_ALLOW_BLOCK_STYLES, 4843 PseudoStyleType::mozSVGText}}, 4844 {nsGkAtoms::foreignObject, 4845 {ToCreationFunc(NS_NewSVGForeignObjectFrame), 4846 FCDATA_DISALLOW_OUT_OF_FLOW, PseudoStyleType::mozSVGForeignContent}}, 4847 SIMPLE_SVG_CREATE(a, NS_NewSVGAFrame), 4848 SIMPLE_SVG_CREATE(linearGradient, NS_NewSVGLinearGradientFrame), 4849 SIMPLE_SVG_CREATE(radialGradient, NS_NewSVGRadialGradientFrame), 4850 SIMPLE_SVG_CREATE(stop, NS_NewSVGStopFrame), 4851 SIMPLE_SVG_CREATE(use, NS_NewSVGUseFrame), 4852 SIMPLE_SVG_CREATE(view, NS_NewSVGViewFrame), 4853 SIMPLE_SVG_CREATE(image, NS_NewSVGImageFrame), 4854 SIMPLE_SVG_CREATE(clipPath, NS_NewSVGClipPathFrame), 4855 SIMPLE_SVG_CREATE(filter, NS_NewSVGFilterFrame), 4856 SIMPLE_SVG_CREATE(pattern, NS_NewSVGPatternFrame), 4857 SIMPLE_SVG_CREATE(mask, NS_NewSVGMaskFrame), 4858 SIMPLE_SVG_CREATE(feDistantLight, NS_NewSVGFEUnstyledLeafFrame), 4859 SIMPLE_SVG_CREATE(fePointLight, NS_NewSVGFEUnstyledLeafFrame), 4860 SIMPLE_SVG_CREATE(feSpotLight, NS_NewSVGFEUnstyledLeafFrame), 4861 SIMPLE_SVG_CREATE(feBlend, NS_NewSVGFELeafFrame), 4862 SIMPLE_SVG_CREATE(feColorMatrix, NS_NewSVGFELeafFrame), 4863 SIMPLE_SVG_CREATE(feFuncR, NS_NewSVGFEUnstyledLeafFrame), 4864 SIMPLE_SVG_CREATE(feFuncG, NS_NewSVGFEUnstyledLeafFrame), 4865 SIMPLE_SVG_CREATE(feFuncB, NS_NewSVGFEUnstyledLeafFrame), 4866 SIMPLE_SVG_CREATE(feFuncA, NS_NewSVGFEUnstyledLeafFrame), 4867 SIMPLE_SVG_CREATE(feComposite, NS_NewSVGFELeafFrame), 4868 SIMPLE_SVG_CREATE(feComponentTransfer, NS_NewSVGFEContainerFrame), 4869 SIMPLE_SVG_CREATE(feConvolveMatrix, NS_NewSVGFELeafFrame), 4870 SIMPLE_SVG_CREATE(feDiffuseLighting, NS_NewSVGFEContainerFrame), 4871 SIMPLE_SVG_CREATE(feDisplacementMap, NS_NewSVGFELeafFrame), 4872 SIMPLE_SVG_CREATE(feDropShadow, NS_NewSVGFELeafFrame), 4873 SIMPLE_SVG_CREATE(feFlood, NS_NewSVGFELeafFrame), 4874 SIMPLE_SVG_CREATE(feGaussianBlur, NS_NewSVGFELeafFrame), 4875 SIMPLE_SVG_CREATE(feImage, NS_NewSVGFEImageFrame), 4876 SIMPLE_SVG_CREATE(feMerge, NS_NewSVGFEContainerFrame), 4877 SIMPLE_SVG_CREATE(feMergeNode, NS_NewSVGFEUnstyledLeafFrame), 4878 SIMPLE_SVG_CREATE(feMorphology, NS_NewSVGFELeafFrame), 4879 SIMPLE_SVG_CREATE(feOffset, NS_NewSVGFELeafFrame), 4880 SIMPLE_SVG_CREATE(feSpecularLighting, NS_NewSVGFEContainerFrame), 4881 SIMPLE_SVG_CREATE(feTile, NS_NewSVGFELeafFrame), 4882 SIMPLE_SVG_CREATE(feTurbulence, NS_NewSVGFELeafFrame)}; 4883 4884 const FrameConstructionData* data = 4885 FindDataByTag(aElement, aStyle, sSVGData, std::size(sSVGData)); 4886 4887 if (!data) { 4888 data = &sContainerData; 4889 } 4890 4891 return data; 4892 } 4893 4894 void nsCSSFrameConstructor::AppendPageBreakItem( 4895 nsIContent* aContent, FrameConstructionItemList& aItems) { 4896 RefPtr<ComputedStyle> pseudoStyle = 4897 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle( 4898 PseudoStyleType::pageBreak); 4899 4900 MOZ_ASSERT(pseudoStyle->StyleDisplay()->mDisplay == StyleDisplay::Block, 4901 "Unexpected display"); 4902 4903 static constexpr FrameConstructionData sPageBreakData(NS_NewPageBreakFrame, 4904 FCDATA_SKIP_FRAMESET); 4905 aItems.AppendItem(this, &sPageBreakData, aContent, pseudoStyle.forget(), 4906 true); 4907 } 4908 4909 bool nsCSSFrameConstructor::ShouldCreateItemsForChild( 4910 nsFrameConstructorState& aState, nsIContent* aContent, 4911 nsContainerFrame* aParentFrame) { 4912 aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME); 4913 // XXX the GetContent() != aContent check is needed due to bug 135040. 4914 // Remove it once that's fixed. 4915 if (aContent->GetPrimaryFrame() && 4916 aContent->GetPrimaryFrame()->GetContent() == aContent && 4917 !aState.mCreatingExtraFrames) { 4918 MOZ_ASSERT(false, 4919 "asked to create frame construction item for a node that " 4920 "already has a frame"); 4921 return false; 4922 } 4923 4924 // don't create a whitespace frame if aParent doesn't want it 4925 if (!NeedFrameFor(aState, aParentFrame, aContent)) { 4926 return false; 4927 } 4928 4929 // never create frames for comments or PIs 4930 if (aContent->IsComment() || aContent->IsProcessingInstruction()) { 4931 return false; 4932 } 4933 4934 return true; 4935 } 4936 4937 void nsCSSFrameConstructor::AddFrameConstructionItems( 4938 nsFrameConstructorState& aState, nsIContent* aContent, 4939 bool aSuppressWhiteSpaceOptimizations, const ComputedStyle& aParentStyle, 4940 const InsertionPoint& aInsertion, FrameConstructionItemList& aItems, 4941 ItemFlags aFlags) { 4942 nsContainerFrame* parentFrame = aInsertion.mParentFrame; 4943 if (!ShouldCreateItemsForChild(aState, aContent, parentFrame)) { 4944 return; 4945 } 4946 4947 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(aContent); 4948 auto flags = aFlags + ItemFlag::AllowPageBreak; 4949 if (parentFrame) { 4950 if (parentFrame->IsInSVGTextSubtree()) { 4951 flags += ItemFlag::IsWithinSVGText; 4952 } 4953 if (parentFrame->IsBlockFrame() && parentFrame->GetParent() && 4954 parentFrame->GetParent()->IsSVGTextFrame()) { 4955 flags += ItemFlag::AllowTextPathChild; 4956 } 4957 } 4958 AddFrameConstructionItemsInternal(aState, aContent, parentFrame, 4959 aSuppressWhiteSpaceOptimizations, 4960 computedStyle, flags, aItems); 4961 } 4962 4963 // Whether we should suppress frames for a child under a <select> frame. 4964 // 4965 // Never create frames for non-option/optgroup kids of <select> and non-option 4966 // kids of <optgroup> inside a <select>. 4967 static bool ShouldSuppressFrameInSelect(const nsIContent* aParent, 4968 const nsIContent& aChild) { 4969 if (!aParent || 4970 !aParent->IsAnyOfHTMLElements(nsGkAtoms::select, nsGkAtoms::optgroup, 4971 nsGkAtoms::option)) { 4972 return false; 4973 } 4974 4975 // Allow native anonymous content no matter what. 4976 if (aChild.IsRootOfNativeAnonymousSubtree()) { 4977 return false; 4978 } 4979 4980 // Options with labels have their label text added in ::before by forms.css. 4981 // Suppress frames for their child text. 4982 if (aParent->IsHTMLElement(nsGkAtoms::option)) { 4983 return aParent->AsElement()->HasNonEmptyAttr(nsGkAtoms::label); 4984 } 4985 4986 // If we're in any display: contents subtree, just suppress the frame. 4987 // 4988 // We can't be regular NAC, since display: contents has no frame to generate 4989 // them off. 4990 if (aChild.GetParent() != aParent) { 4991 return true; 4992 } 4993 4994 // <option> and <hr> are always fine. 4995 if (aChild.IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::hr)) { 4996 return false; 4997 } 4998 4999 // <optgroup> is OK in <select> but not in <optgroup>. 5000 if (aChild.IsHTMLElement(nsGkAtoms::optgroup) && 5001 aParent->IsHTMLElement(nsGkAtoms::select)) { 5002 return false; 5003 } 5004 5005 // Anything else is not ok. 5006 return true; 5007 } 5008 5009 const nsCSSFrameConstructor::FrameConstructionData* 5010 nsCSSFrameConstructor::FindDataForContent(nsIContent& aContent, 5011 ComputedStyle& aStyle, 5012 nsIFrame* aParentFrame, 5013 ItemFlags aFlags) { 5014 MOZ_ASSERT(aStyle.StyleDisplay()->mDisplay != StyleDisplay::None && 5015 aStyle.StyleDisplay()->mDisplay != StyleDisplay::Contents, 5016 "These two special display values should be handled earlier"); 5017 5018 if (auto* text = Text::FromNode(aContent)) { 5019 return FindTextData(*text, aParentFrame); 5020 } 5021 5022 return FindElementData(*aContent.AsElement(), aStyle, aParentFrame, aFlags); 5023 } 5024 5025 const nsCSSFrameConstructor::FrameConstructionData* 5026 nsCSSFrameConstructor::FindElementData(const Element& aElement, 5027 ComputedStyle& aStyle, 5028 nsIFrame* aParentFrame, 5029 ItemFlags aFlags) { 5030 // Don't create frames for non-SVG element children of SVG elements. 5031 if (!aElement.IsSVGElement()) { 5032 if (aParentFrame && IsFrameForSVG(aParentFrame) && 5033 !aParentFrame->IsSVGForeignObjectFrame()) { 5034 return nullptr; 5035 } 5036 if (aFlags.contains(ItemFlag::IsWithinSVGText)) { 5037 return nullptr; 5038 } 5039 } 5040 5041 if (auto* data = FindElementTagData(aElement, aStyle, aParentFrame, aFlags)) { 5042 return data; 5043 } 5044 5045 // Check for 'content: <image-url>' on the element (which makes us ignore 5046 // 'display' values other than 'none' or 'contents'). 5047 if (nsImageFrame::ShouldCreateImageFrameForContentProperty(aElement, 5048 aStyle)) { 5049 static constexpr FrameConstructionData sImgData( 5050 NS_NewImageFrameForContentProperty); 5051 return &sImgData; 5052 } 5053 5054 const bool shouldBlockify = aFlags.contains(ItemFlag::IsForRenderedLegend) || 5055 aFlags.contains(ItemFlag::IsForOutsideMarker); 5056 if (shouldBlockify && !aStyle.StyleDisplay()->IsBlockOutsideStyle()) { 5057 // Make a temp copy of StyleDisplay and blockify its mDisplay value. 5058 auto display = *aStyle.StyleDisplay(); 5059 bool isRootElement = false; 5060 uint16_t rawDisplayValue = 5061 Servo_ComputedValues_BlockifiedDisplay(&aStyle, isRootElement); 5062 display.mDisplay = StyleDisplay{rawDisplayValue}; 5063 return FindDisplayData(display, aElement); 5064 } 5065 5066 const auto& display = *aStyle.StyleDisplay(); 5067 return FindDisplayData(display, aElement); 5068 } 5069 5070 const nsCSSFrameConstructor::FrameConstructionData* 5071 nsCSSFrameConstructor::FindElementTagData(const Element& aElement, 5072 ComputedStyle& aStyle, 5073 nsIFrame* aParentFrame, 5074 ItemFlags aFlags) { 5075 switch (aElement.GetNameSpaceID()) { 5076 case kNameSpaceID_XHTML: 5077 return FindHTMLData(aElement, aParentFrame, aStyle); 5078 case kNameSpaceID_MathML: 5079 return FindMathMLData(aElement, aStyle); 5080 case kNameSpaceID_SVG: 5081 return FindSVGData(aElement, aParentFrame, 5082 aFlags.contains(ItemFlag::IsWithinSVGText), 5083 aFlags.contains(ItemFlag::AllowTextPathChild), aStyle); 5084 case kNameSpaceID_XUL: 5085 return FindXULTagData(aElement, aStyle); 5086 default: 5087 return nullptr; 5088 } 5089 } 5090 5091 void nsCSSFrameConstructor::AddFrameConstructionItemsInternal( 5092 nsFrameConstructorState& aState, nsIContent* aContent, 5093 nsContainerFrame* aParentFrame, bool aSuppressWhiteSpaceOptimizations, 5094 ComputedStyle* aComputedStyle, ItemFlags aFlags, 5095 FrameConstructionItemList& aItems) { 5096 MOZ_ASSERT(aContent->IsText() || aContent->IsElement(), 5097 "Shouldn't get anything else here!"); 5098 MOZ_ASSERT(aContent->IsInComposedDoc()); 5099 MOZ_ASSERT(!aContent->GetPrimaryFrame() || aState.mCreatingExtraFrames || 5100 aContent->NodeInfo()->NameAtom() == nsGkAtoms::area); 5101 5102 const bool withinSVGText = aFlags.contains(ItemFlag::IsWithinSVGText); 5103 const bool isGeneratedContent = aFlags.contains(ItemFlag::IsGeneratedContent); 5104 MOZ_ASSERT(!isGeneratedContent || aComputedStyle->IsPseudoElement(), 5105 "Generated content should be a pseudo-element"); 5106 5107 FrameConstructionItem* item = nullptr; 5108 auto cleanupGeneratedContent = mozilla::MakeScopeExit([&]() { 5109 if (isGeneratedContent && !item) { 5110 MOZ_ASSERT(!IsDisplayContents(aContent), 5111 "This would need to change if we support display: contents " 5112 "in generated content"); 5113 aContent->UnbindFromTree(); 5114 } 5115 }); 5116 5117 // 'display:none' elements never creates any frames at all. 5118 const nsStyleDisplay& display = *aComputedStyle->StyleDisplay(); 5119 if (display.mDisplay == StyleDisplay::None) { 5120 return; 5121 } 5122 5123 if (display.mDisplay == StyleDisplay::Contents) { 5124 // See the mDisplay fixup code in StyleAdjuster::adjust. 5125 MOZ_ASSERT(!aContent->AsElement()->IsRootOfNativeAnonymousSubtree(), 5126 "display:contents on anonymous content is unsupported"); 5127 5128 // FIXME(bug 1588477): <svg:text>'s TextNodeCorrespondenceRecorder has 5129 // trouble with everything that looks like display: contents. 5130 if (withinSVGText) { 5131 return; 5132 } 5133 5134 CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(), 5135 *aComputedStyle, PseudoStyleType::before, 5136 aItems); 5137 5138 FlattenedChildIterator iter(aContent); 5139 InsertionPoint insertion(aParentFrame, aContent); 5140 for (nsIContent* child = iter.GetNextChild(); child; 5141 child = iter.GetNextChild()) { 5142 AddFrameConstructionItems(aState, child, aSuppressWhiteSpaceOptimizations, 5143 *aComputedStyle, insertion, aItems, aFlags); 5144 } 5145 aItems.SetParentHasNoShadowDOM(!iter.ShadowDOMInvolved()); 5146 5147 CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(), 5148 *aComputedStyle, PseudoStyleType::after, aItems); 5149 return; 5150 } 5151 5152 nsIContent* parent = aParentFrame ? aParentFrame->GetContent() : nullptr; 5153 if (ShouldSuppressFrameInSelect(parent, *aContent)) { 5154 return; 5155 } 5156 5157 if (aContent->IsHTMLElement(nsGkAtoms::legend) && aParentFrame) { 5158 const nsFieldSetFrame* const fs = GetFieldSetFrameFor(aParentFrame); 5159 if (fs && !fs->GetLegend() && !aState.mHasRenderedLegend && 5160 !aComputedStyle->StyleDisplay()->IsFloatingStyle() && 5161 !aComputedStyle->StyleDisplay()->IsAbsolutelyPositionedStyle()) { 5162 aState.mHasRenderedLegend = true; 5163 aFlags += ItemFlag::IsForRenderedLegend; 5164 } 5165 } 5166 5167 const FrameConstructionData* const data = 5168 FindDataForContent(*aContent, *aComputedStyle, aParentFrame, aFlags); 5169 if (!data || data->mBits & FCDATA_SUPPRESS_FRAME) { 5170 return; 5171 } 5172 5173 const bool isPopup = data->mBits & FCDATA_IS_POPUP; 5174 5175 const uint32_t bits = data->mBits; 5176 5177 // Inside colgroups, suppress everything except columns. 5178 if (aParentFrame && aParentFrame->IsTableColGroupFrame() && 5179 (!(bits & FCDATA_IS_TABLE_PART) || 5180 display.mDisplay != StyleDisplay::TableColumn)) { 5181 return; 5182 } 5183 5184 const bool canHavePageBreak = 5185 aFlags.contains(ItemFlag::AllowPageBreak) && 5186 aState.mPresContext->IsPaginated() && 5187 !display.IsAbsolutelyPositionedStyle() && 5188 !(aParentFrame && aParentFrame->IsFlexOrGridContainer()) && 5189 !(bits & FCDATA_IS_TABLE_PART) && !(bits & FCDATA_IS_SVG_TEXT); 5190 if (canHavePageBreak && display.BreakBefore()) { 5191 AppendPageBreakItem(aContent, aItems); 5192 } 5193 5194 if (!item) { 5195 item = aItems.AppendItem(this, data, aContent, do_AddRef(aComputedStyle), 5196 aSuppressWhiteSpaceOptimizations); 5197 if (aFlags.contains(ItemFlag::IsForRenderedLegend)) { 5198 item->mIsRenderedLegend = true; 5199 } 5200 } 5201 item->mIsText = !aContent->IsElement(); 5202 item->mIsGeneratedContent = isGeneratedContent; 5203 if (isGeneratedContent) { 5204 // We need to keep this alive until the frame takes ownership. 5205 // This corresponds to the Release in ConstructFramesFromItem. 5206 item->mContent->AddRef(); 5207 } 5208 item->mIsPopup = isPopup; 5209 5210 if (canHavePageBreak && display.BreakAfter()) { 5211 AppendPageBreakItem(aContent, aItems); 5212 } 5213 5214 if (bits & FCDATA_IS_INLINE) { 5215 // To correctly set item->mIsAllInline we need to build up our child items 5216 // right now. 5217 BuildInlineChildItems(aState, *item, 5218 aFlags.contains(ItemFlag::IsWithinSVGText), 5219 aFlags.contains(ItemFlag::AllowTextPathChild)); 5220 item->mIsBlock = false; 5221 } else { 5222 // Compute a boolean isInline which is guaranteed to be false for blocks 5223 // (but may also be false for some inlines). 5224 const bool isInline = 5225 // Table-internal things are inline-outside if and only if they're kids 5226 // of inlines, since they'll trigger construction of inline-table 5227 // pseudos. 5228 ((bits & FCDATA_IS_TABLE_PART) && 5229 (!aParentFrame || // No aParentFrame means inline 5230 aParentFrame->StyleDisplay()->IsInlineFlow())) || 5231 // Things that are inline-outside but aren't inline frames are inline 5232 display.IsInlineOutsideStyle() || 5233 // Popups that are certainly out of flow. 5234 isPopup; 5235 5236 // Set mIsAllInline conservatively. It just might be that even an inline 5237 // that has mIsAllInline false doesn't need an {ib} split. So this is just 5238 // an optimization to keep from doing too much work in cases when we can 5239 // show that mIsAllInline is true.. 5240 item->mIsAllInline = 5241 isInline || 5242 // Figure out whether we're guaranteed this item will be out of flow. 5243 // This is not a precise test, since one of our ancestor inlines might 5244 // add an absolute containing block (if it's relatively positioned) when 5245 // there wasn't such a containing block before. But it's conservative 5246 // in the sense that anything that will really end up as an in-flow 5247 // non-inline will test false here. In other words, if this test is 5248 // true we're guaranteed to be inline; if it's false we don't know what 5249 // we'll end up as. 5250 // 5251 // If we make this test precise, we can remove some of the code dealing 5252 // with the imprecision in ConstructInline and adjust the comments on 5253 // mIsAllInline and mIsBlock in the header. 5254 (!(bits & FCDATA_DISALLOW_OUT_OF_FLOW) && 5255 aState.GetGeometricParent(display, nullptr)); 5256 5257 // Set mIsBlock conservatively. It's OK to set it false for some real 5258 // blocks, but not OK to set it true for things that aren't blocks. Since 5259 // isOutOfFlow might be false even in cases when the frame will end up 5260 // out-of-flow, we can't use it here. But we _can_ say that the frame will 5261 // for sure end up in-flow if it's not floated or absolutely positioned. 5262 item->mIsBlock = !isInline && !display.IsAbsolutelyPositionedStyle() && 5263 !display.IsFloatingStyle() && !(bits & FCDATA_IS_SVG_TEXT); 5264 } 5265 5266 if (item->mIsAllInline) { 5267 aItems.InlineItemAdded(); 5268 } else if (item->mIsBlock) { 5269 aItems.BlockItemAdded(); 5270 } 5271 } 5272 5273 /** 5274 * Return true if the frame construction item pointed to by aIter will 5275 * create a frame adjacent to a line boundary in the frame tree, and that 5276 * line boundary is induced by a content node adjacent to the frame's 5277 * content node in the content tree. The latter condition is necessary so 5278 * that ContentAppended/ContentInserted/ContentWillBeRemoved can easily find any 5279 * text nodes that were suppressed here. 5280 */ 5281 bool nsCSSFrameConstructor::AtLineBoundary(FCItemIterator& aIter) { 5282 if (aIter.item().mSuppressWhiteSpaceOptimizations) { 5283 return false; 5284 } 5285 5286 if (aIter.AtStart()) { 5287 if (aIter.List()->HasLineBoundaryAtStart() && 5288 !aIter.item().mContent->GetPreviousSibling()) { 5289 return true; 5290 } 5291 } else { 5292 FCItemIterator prev = aIter; 5293 prev.Prev(); 5294 if (prev.item().IsLineBoundary() && 5295 !prev.item().mSuppressWhiteSpaceOptimizations && 5296 aIter.item().mContent->GetPreviousSibling() == prev.item().mContent) { 5297 return true; 5298 } 5299 } 5300 5301 FCItemIterator next = aIter; 5302 next.Next(); 5303 if (next.IsDone()) { 5304 if (aIter.List()->HasLineBoundaryAtEnd() && 5305 !aIter.item().mContent->GetNextSibling()) { 5306 return true; 5307 } 5308 } else { 5309 if (next.item().IsLineBoundary() && 5310 !next.item().mSuppressWhiteSpaceOptimizations && 5311 aIter.item().mContent->GetNextSibling() == next.item().mContent) { 5312 return true; 5313 } 5314 } 5315 5316 return false; 5317 } 5318 5319 void nsCSSFrameConstructor::ConstructFramesFromItem( 5320 nsFrameConstructorState& aState, FCItemIterator& aIter, 5321 nsContainerFrame* aParentFrame, nsFrameList& aFrameList) { 5322 FrameConstructionItem& item = aIter.item(); 5323 ComputedStyle* computedStyle = item.mComputedStyle; 5324 if (item.mIsText) { 5325 // If this is collapsible whitespace next to a line boundary, 5326 // don't create a frame. item.IsWhitespace() also sets the 5327 // NS_CREATE_FRAME_IF_NON_WHITESPACE flag in the text node. (If we 5328 // end up creating a frame, nsTextFrame::Init will clear the flag.) 5329 // We don't do this for generated content, because some generated 5330 // text content is empty text nodes that are about to be initialized. 5331 // (We check mAdditionalStateBits because only the generated content 5332 // container's frame construction item is marked with 5333 // mIsGeneratedContent, and we might not have an aParentFrame.) 5334 // We don't do it for content that may have Shadow DOM siblings / insertion 5335 // points, because they make it difficult to correctly create the frame due 5336 // to dynamic changes. 5337 // We don't do it for SVG text, since we might need to position and 5338 // measure the white space glyphs due to x/y/dx/dy attributes. 5339 if (AtLineBoundary(aIter) && 5340 !computedStyle->StyleText()->WhiteSpaceOrNewlineIsSignificant() && 5341 aIter.List()->ParentHasNoShadowDOM() && 5342 !(aState.mAdditionalStateBits & NS_FRAME_GENERATED_CONTENT) && 5343 (item.mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) && 5344 !(item.mFCData->mBits & FCDATA_IS_SVG_TEXT) && 5345 !mAlwaysCreateFramesForIgnorableWhitespace && 5346 item.IsWhitespace(aState)) { 5347 return; 5348 } 5349 5350 ConstructTextFrame(item.mFCData, aState, item.mContent, aParentFrame, 5351 computedStyle, aFrameList); 5352 return; 5353 } 5354 5355 AutoRestore<nsFrameState> savedStateBits(aState.mAdditionalStateBits); 5356 if (item.mIsGeneratedContent) { 5357 // Ensure that frames created here are all tagged with 5358 // NS_FRAME_GENERATED_CONTENT. 5359 aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT; 5360 } 5361 5362 // XXXbz maybe just inline ConstructFrameFromItemInternal here or something? 5363 ConstructFrameFromItemInternal(item, aState, aParentFrame, aFrameList); 5364 5365 if (item.mIsGeneratedContent) { 5366 // This corresponds to the AddRef in AddFrameConstructionItemsInternal. 5367 // The frame owns the generated content now. 5368 item.mContent->Release(); 5369 5370 // Now that we've passed ownership of item.mContent to the frame, unset 5371 // our generated content flag so we don't release or unbind it ourselves. 5372 item.mIsGeneratedContent = false; 5373 } 5374 } 5375 5376 nsContainerFrame* nsCSSFrameConstructor::GetAbsoluteContainingBlock( 5377 nsIFrame* aFrame, ContainingBlockType aType) { 5378 // Starting with aFrame, look for a frame that is absolutely positioned or 5379 // relatively positioned (and transformed, if aType is FIXED) 5380 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) { 5381 if (frame->IsMathMLFrame()) { 5382 // If it's mathml, bail out -- no absolute positioning out from inside 5383 // mathml frames. Note that we don't make this part of the loop 5384 // condition because of the stuff at the end of this method... 5385 return nullptr; 5386 } 5387 5388 // Look for the ICB. 5389 if (aType == FIXED_POS) { 5390 LayoutFrameType t = frame->Type(); 5391 if (t == LayoutFrameType::Viewport || t == LayoutFrameType::PageContent) { 5392 return static_cast<nsContainerFrame*>(frame); 5393 } 5394 } 5395 5396 // If the frame is positioned, we will probably return it as the containing 5397 // block (see the exceptions below). Otherwise, we'll start looking at the 5398 // parent frame, unless we're dealing with a scrollframe. 5399 // Scrollframes are special since they're not positioned, but their 5400 // scrolledframe might be. So, we need to check this special case to return 5401 // the correct containing block (the scrolledframe) in that case. 5402 // If we're looking for a fixed-pos containing block and the frame is 5403 // not transformed, skip it. 5404 if (!frame->IsAbsPosContainingBlock()) { 5405 continue; 5406 } 5407 if (aType == FIXED_POS && !frame->IsFixedPosContainingBlock()) { 5408 continue; 5409 } 5410 nsIFrame* absPosCBCandidate = frame; 5411 LayoutFrameType type = absPosCBCandidate->Type(); 5412 if (type == LayoutFrameType::FieldSet) { 5413 absPosCBCandidate = 5414 static_cast<nsFieldSetFrame*>(absPosCBCandidate)->GetInner(); 5415 if (!absPosCBCandidate) { 5416 continue; 5417 } 5418 type = absPosCBCandidate->Type(); 5419 } 5420 if (type == LayoutFrameType::ScrollContainer) { 5421 ScrollContainerFrame* scrollContainerFrame = 5422 do_QueryFrame(absPosCBCandidate); 5423 absPosCBCandidate = scrollContainerFrame->GetScrolledFrame(); 5424 if (!absPosCBCandidate) { 5425 continue; 5426 } 5427 type = absPosCBCandidate->Type(); 5428 } 5429 // Only first continuations can be containing blocks. 5430 absPosCBCandidate = absPosCBCandidate->FirstContinuation(); 5431 // Is the frame really an absolute container? 5432 if (!absPosCBCandidate->IsAbsoluteContainer()) { 5433 continue; 5434 } 5435 5436 // For tables, skip the inner frame and consider the table wrapper frame. 5437 if (type == LayoutFrameType::Table) { 5438 continue; 5439 } 5440 // For table wrapper frames, we can just return absPosCBCandidate. 5441 MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(absPosCBCandidate), 5442 "abs.pos. containing block must be nsContainerFrame sub-class"); 5443 return static_cast<nsContainerFrame*>(absPosCBCandidate); 5444 } 5445 5446 MOZ_ASSERT(aType != FIXED_POS, "no ICB in this frame tree?"); 5447 5448 // It is possible for the search for the containing block to fail, because 5449 // no absolute container can be found in the parent chain. In those cases, 5450 // we fall back to the document element's containing block. 5451 return mDocElementContainingBlock; 5452 } 5453 5454 nsContainerFrame* nsCSSFrameConstructor::GetFloatContainingBlock( 5455 nsIFrame* aFrame) { 5456 // Starting with aFrame, look for a frame that is a float containing block. 5457 // If we hit a frame which prevents its descendants from floating, bail out. 5458 // The logic here needs to match the logic in MaybePushFloatContainingBlock(). 5459 for (nsIFrame* containingBlock = aFrame; 5460 containingBlock && !ShouldSuppressFloatingOfDescendants(containingBlock); 5461 containingBlock = containingBlock->GetParent()) { 5462 if (containingBlock->IsFloatContainingBlock()) { 5463 MOZ_ASSERT((nsContainerFrame*)do_QueryFrame(containingBlock), 5464 "float containing block must be nsContainerFrame sub-class"); 5465 return static_cast<nsContainerFrame*>(containingBlock); 5466 } 5467 } 5468 5469 // If we didn't find a containing block, then there just isn't 5470 // one.... return null 5471 return nullptr; 5472 } 5473 5474 /** 5475 * This function will get the previous sibling to use for an append operation. 5476 * 5477 * It takes a parent frame (must not be null) and the next insertion sibling, if 5478 * the parent content is display: contents or has ::after content (may be null). 5479 */ 5480 static nsIFrame* FindAppendPrevSibling(nsIFrame* aParentFrame, 5481 nsIFrame* aNextSibling) { 5482 aParentFrame->DrainSelfOverflowList(); 5483 5484 if (aNextSibling) { 5485 MOZ_ASSERT( 5486 aNextSibling->GetParent()->GetContentInsertionFrame() == aParentFrame, 5487 "Wrong parent"); 5488 return aNextSibling->GetPrevSibling(); 5489 } 5490 5491 return aParentFrame->PrincipalChildList().LastChild(); 5492 } 5493 5494 /** 5495 * Finds the right parent frame to append content to aParentFrame. 5496 * 5497 * Cannot return or receive null. 5498 */ 5499 static nsContainerFrame* ContinuationToAppendTo( 5500 nsContainerFrame* aParentFrame) { 5501 MOZ_ASSERT(aParentFrame); 5502 5503 if (IsFramePartOfIBSplit(aParentFrame)) { 5504 // If the frame we are manipulating is a ib-split frame (that is, one that's 5505 // been created as a result of a block-in-inline situation) then we need to 5506 // append to the last ib-split sibling, not to the frame itself. 5507 // 5508 // Always make sure to look at the last continuation of the frame for the 5509 // {ib} case, even if that continuation is empty. 5510 // 5511 // We don't do this for the non-ib-split-frame case, since in the other 5512 // cases appending to the last nonempty continuation is fine and in fact not 5513 // doing that can confuse code that doesn't know to pull kids from 5514 // continuations other than its next one. 5515 return static_cast<nsContainerFrame*>( 5516 GetLastIBSplitSibling(aParentFrame)->LastContinuation()); 5517 } 5518 5519 return nsLayoutUtils::LastContinuationWithChild(aParentFrame); 5520 } 5521 5522 /** 5523 * This function will get the next sibling for a frame insert operation given 5524 * the parent and previous sibling. aPrevSibling may be null. 5525 */ 5526 static nsIFrame* GetInsertNextSibling(nsIFrame* aParentFrame, 5527 nsIFrame* aPrevSibling) { 5528 if (aPrevSibling) { 5529 return aPrevSibling->GetNextSibling(); 5530 } 5531 5532 return aParentFrame->PrincipalChildList().FirstChild(); 5533 } 5534 5535 void nsCSSFrameConstructor::AppendFramesToParent( 5536 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame, 5537 nsFrameList& aFrameList, nsIFrame* aPrevSibling, bool aIsRecursiveCall) { 5538 MOZ_ASSERT( 5539 !IsFramePartOfIBSplit(aParentFrame) || !GetIBSplitSibling(aParentFrame) || 5540 !GetIBSplitSibling(aParentFrame)->PrincipalChildList().FirstChild(), 5541 "aParentFrame has a ib-split sibling with kids?"); 5542 MOZ_ASSERT(!aPrevSibling || aPrevSibling->GetParent() == aParentFrame, 5543 "Parent and prevsibling don't match"); 5544 MOZ_ASSERT( 5545 !aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) || 5546 !IsFramePartOfIBSplit(aParentFrame), 5547 "We should have wiped aParentFrame in WipeContainingBlock() " 5548 "if it's part of an IB split!"); 5549 5550 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling); 5551 5552 NS_ASSERTION(nextSibling || !aParentFrame->GetNextContinuation() || 5553 !aParentFrame->GetNextContinuation() 5554 ->PrincipalChildList() 5555 .FirstChild() || 5556 aIsRecursiveCall, 5557 "aParentFrame has later continuations with kids?"); 5558 NS_ASSERTION( 5559 nextSibling || !IsFramePartOfIBSplit(aParentFrame) || 5560 (IsInlineFrame(aParentFrame) && !GetIBSplitSibling(aParentFrame) && 5561 !aParentFrame->GetNextContinuation()) || 5562 aIsRecursiveCall, 5563 "aParentFrame is not last?"); 5564 5565 // If we're inserting a list of frames at the end of the trailing inline 5566 // of an {ib} split, we may need to create additional {ib} siblings to parent 5567 // them. 5568 if (!nextSibling && IsFramePartOfIBSplit(aParentFrame)) { 5569 // When we get here, our frame list might start with a block. If it does 5570 // so, and aParentFrame is an inline, and it and all its previous 5571 // continuations have no siblings, then put the initial blocks from the 5572 // frame list into the previous block of the {ib} split. Note that we 5573 // didn't want to stop at the block part of the split when figuring out 5574 // initial parent, because that could screw up float parenting; it's easier 5575 // to do this little fixup here instead. 5576 if (aFrameList.NotEmpty() && aFrameList.FirstChild()->IsBlockOutside()) { 5577 // See whether our trailing inline is empty 5578 nsIFrame* firstContinuation = aParentFrame->FirstContinuation(); 5579 if (firstContinuation->PrincipalChildList().IsEmpty()) { 5580 // Our trailing inline is empty. Collect our starting blocks from 5581 // aFrameList, get the right parent frame for them, and put them in. 5582 nsFrameList blockKids = 5583 aFrameList.Split([](nsIFrame* f) { return !f->IsBlockOutside(); }); 5584 NS_ASSERTION(blockKids.NotEmpty(), "No blocks?"); 5585 5586 nsContainerFrame* prevBlock = GetIBSplitPrevSibling(firstContinuation); 5587 prevBlock = 5588 static_cast<nsContainerFrame*>(prevBlock->LastContinuation()); 5589 NS_ASSERTION(prevBlock, "Should have previous block here"); 5590 5591 MoveChildrenTo(aParentFrame, prevBlock, blockKids); 5592 } 5593 } 5594 5595 // We want to put some of the frames into this inline frame. 5596 nsFrameList inlineKids = 5597 aFrameList.Split([](nsIFrame* f) { return f->IsBlockOutside(); }); 5598 5599 if (!inlineKids.IsEmpty()) { 5600 AppendFrames(aParentFrame, FrameChildListID::Principal, 5601 std::move(inlineKids)); 5602 } 5603 5604 if (!aFrameList.IsEmpty()) { 5605 nsFrameList ibSiblings; 5606 CreateIBSiblings(aState, aParentFrame, 5607 aParentFrame->IsAbsPosContainingBlock(), aFrameList, 5608 ibSiblings); 5609 5610 // Make sure to trigger reflow of the inline that used to be our 5611 // last one and now isn't anymore, since its GetSkipSides() has 5612 // changed. 5613 mPresShell->FrameNeedsReflow(aParentFrame, 5614 IntrinsicDirty::FrameAndAncestors, 5615 NS_FRAME_HAS_DIRTY_CHILDREN); 5616 5617 // Recurse so we create new ib siblings as needed for aParentFrame's 5618 // parent 5619 return AppendFramesToParent(aState, aParentFrame->GetParent(), ibSiblings, 5620 aParentFrame, true); 5621 } 5622 return; 5623 } 5624 5625 // If we're appending a list of frames to the last continuations of a 5626 // ::-moz-column-content, we may need to create column-span siblings for them. 5627 if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) { 5628 // Extract any initial non-column-span kids, and append them to 5629 // ::-moz-column-content's child list. 5630 nsFrameList initialNonColumnSpanKids = 5631 aFrameList.Split([](nsIFrame* f) { return f->IsColumnSpan(); }); 5632 AppendFrames(aParentFrame, FrameChildListID::Principal, 5633 std::move(initialNonColumnSpanKids)); 5634 5635 if (aFrameList.IsEmpty()) { 5636 // No more kids to process (there weren't any column-span kids). 5637 return; 5638 } 5639 5640 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings( 5641 aState, aParentFrame, aFrameList, 5642 // Column content should never be a absolute/fixed positioned containing 5643 // block. Pass nullptr as aPositionedFrame. 5644 nullptr); 5645 5646 nsContainerFrame* columnSetWrapper = aParentFrame->GetParent(); 5647 while (!columnSetWrapper->IsColumnSetWrapperFrame()) { 5648 columnSetWrapper = columnSetWrapper->GetParent(); 5649 } 5650 MOZ_ASSERT(columnSetWrapper, 5651 "No ColumnSetWrapperFrame ancestor for -moz-column-content?"); 5652 5653 FinishBuildingColumns(aState, columnSetWrapper, aParentFrame, 5654 columnSpanSiblings); 5655 5656 MOZ_ASSERT(columnSpanSiblings.IsEmpty(), 5657 "The column-span siblings should be moved to the proper place!"); 5658 return; 5659 } 5660 5661 // Insert the frames after our aPrevSibling 5662 InsertFrames(aParentFrame, FrameChildListID::Principal, aPrevSibling, 5663 std::move(aFrameList)); 5664 } 5665 5666 template <nsCSSFrameConstructor::SiblingDirection aDirection> 5667 nsIFrame* nsCSSFrameConstructor::FindSiblingInternal( 5668 FlattenedChildIterator& aIter) { 5669 auto nextDomSibling = [](FlattenedChildIterator& aIter) -> nsIContent* { 5670 return aDirection == SiblingDirection::Forward ? aIter.GetNextChild() 5671 : aIter.GetPreviousChild(); 5672 }; 5673 5674 auto getInsideMarkerFrame = [](const nsIContent* aContent) -> nsIFrame* { 5675 auto* marker = nsLayoutUtils::GetMarkerFrame(aContent); 5676 const bool isInsideMarker = 5677 marker && marker->GetInFlowParent()->StyleList()->mListStylePosition == 5678 StyleListStylePosition::Inside; 5679 return isInsideMarker ? marker : nullptr; 5680 }; 5681 5682 auto getNearPseudo = [&](const nsIContent* aContent) -> nsIFrame* { 5683 if (aDirection == SiblingDirection::Forward) { 5684 if (auto* backdrop = nsLayoutUtils::GetBackdropFrame(aContent)) { 5685 return backdrop; 5686 } 5687 if (auto* marker = getInsideMarkerFrame(aContent)) { 5688 return marker; 5689 } 5690 return nsLayoutUtils::GetBeforeFrame(aContent); 5691 } 5692 return nsLayoutUtils::GetAfterFrame(aContent); 5693 }; 5694 5695 auto getFarPseudo = [&](const nsIContent* aContent) -> nsIFrame* { 5696 if (aDirection == SiblingDirection::Forward) { 5697 return nsLayoutUtils::GetAfterFrame(aContent); 5698 } 5699 if (auto* before = nsLayoutUtils::GetBeforeFrame(aContent)) { 5700 return before; 5701 } 5702 if (auto* marker = getInsideMarkerFrame(aContent)) { 5703 return marker; 5704 } 5705 return nsLayoutUtils::GetBackdropFrame(aContent); 5706 }; 5707 5708 while (nsIContent* sibling = nextDomSibling(aIter)) { 5709 // NOTE(emilio): It's important to check GetPrimaryFrame() before 5710 // IsDisplayContents to get the correct insertion point when multiple 5711 // siblings go from display: non-none to display: contents. 5712 if (nsIFrame* primaryFrame = sibling->GetPrimaryFrame()) { 5713 // XXX the GetContent() == sibling check is needed due to bug 135040. 5714 // Remove it once that's fixed. 5715 // The rendered legend check is needed because it is conceptually out of 5716 // flow. There can only be one rendered legend at a time, and we reframe 5717 // when that changes, so this is ok. Table captions are a similar case, 5718 // but there can be multiple of them and they instead get pulled out (and 5719 // the sibling fixed up) after building the frame list. 5720 if (primaryFrame->GetContent() == sibling && 5721 !primaryFrame->IsRenderedLegend()) [[likely]] { 5722 return primaryFrame; 5723 } 5724 } 5725 5726 if (IsDisplayContents(sibling)) { 5727 if (nsIFrame* frame = getNearPseudo(sibling)) { 5728 return frame; 5729 } 5730 5731 const bool startFromBeginning = aDirection == SiblingDirection::Forward; 5732 FlattenedChildIterator iter(sibling, startFromBeginning); 5733 nsIFrame* sibling = FindSiblingInternal<aDirection>(iter); 5734 if (sibling) { 5735 return sibling; 5736 } 5737 } 5738 } 5739 5740 return getFarPseudo(aIter.Parent()); 5741 } 5742 5743 nsIFrame* nsCSSFrameConstructor::AdjustSiblingFrame( 5744 nsIFrame* aSibling, SiblingDirection aDirection) { 5745 if (!aSibling) { 5746 return nullptr; 5747 } 5748 5749 if (aSibling->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { 5750 aSibling = aSibling->GetPlaceholderFrame(); 5751 MOZ_ASSERT(aSibling); 5752 } 5753 5754 MOZ_ASSERT(!aSibling->GetPrevContinuation(), "How?"); 5755 if (aDirection == SiblingDirection::Backward) { 5756 // The frame may be a ib-split frame (a split inline frame that contains a 5757 // block). Get the last part of that split. 5758 if (IsFramePartOfIBSplit(aSibling)) { 5759 aSibling = GetLastIBSplitSibling(aSibling); 5760 } 5761 5762 // The frame may have a continuation. If so, we want the last 5763 // non-overflow-container continuation as our previous sibling. 5764 aSibling = aSibling->GetTailContinuation(); 5765 } 5766 5767 return aSibling; 5768 } 5769 5770 nsIFrame* nsCSSFrameConstructor::FindPreviousSibling( 5771 const FlattenedChildIterator& aIter) { 5772 return AdjustSiblingFrame(FindSibling<SiblingDirection::Backward>(aIter), 5773 SiblingDirection::Backward); 5774 } 5775 5776 nsIFrame* nsCSSFrameConstructor::FindNextSibling( 5777 const FlattenedChildIterator& aIter) { 5778 return AdjustSiblingFrame(FindSibling<SiblingDirection::Forward>(aIter), 5779 SiblingDirection::Forward); 5780 } 5781 5782 template <nsCSSFrameConstructor::SiblingDirection aDirection> 5783 nsIFrame* nsCSSFrameConstructor::FindSibling( 5784 const FlattenedChildIterator& aIter) { 5785 FlattenedChildIterator siblingIter = aIter; 5786 nsIFrame* sibling = FindSiblingInternal<aDirection>(siblingIter); 5787 if (sibling) { 5788 return sibling; 5789 } 5790 5791 // Our siblings (if any) do not have a frame to guide us. The frame for the 5792 // target content should be inserted whereever a frame for the container would 5793 // be inserted. This is needed when inserting into display: contents nodes. 5794 const nsIContent* current = aIter.Parent(); 5795 while (IsDisplayContents(current)) { 5796 const nsIContent* parent = current->GetFlattenedTreeParent(); 5797 MOZ_ASSERT(parent, "No display: contents on the root"); 5798 5799 FlattenedChildIterator iter(parent); 5800 iter.Seek(current); 5801 sibling = FindSiblingInternal<aDirection>(iter); 5802 if (sibling) { 5803 return sibling; 5804 } 5805 5806 current = parent; 5807 } 5808 5809 return nullptr; 5810 } 5811 5812 nsIFrame* nsCSSFrameConstructor::GetInsertionPrevSibling( 5813 InsertionPoint* aInsertion, nsIContent* aChild, bool* aIsAppend) { 5814 MOZ_ASSERT(aInsertion->mParentFrame, "Must have parent frame to start with"); 5815 5816 *aIsAppend = false; 5817 5818 // Find the frame that precedes the insertion point. 5819 FlattenedChildIterator iter(aInsertion->mContainer); 5820 if (!aChild->IsRootOfNativeAnonymousSubtree()) { 5821 // The check for IsRootOfNativeAnonymousSubtree() is because editor is 5822 // severely broken and calls us directly for native anonymous 5823 // nodes that it creates. 5824 iter.Seek(aChild); 5825 } else { 5826 // Prime the iterator for the call to FindPreviousSibling. 5827 iter.GetNextChild(); 5828 MOZ_ASSERT(aChild->GetProperty(nsGkAtoms::restylableAnonymousNode), 5829 "Someone passed native anonymous content directly into frame " 5830 "construction. Stop doing that!"); 5831 } 5832 5833 // Note that FindPreviousSibling is passed the iterator by value, so that 5834 // the later usage of the iterator starts from the same place. 5835 nsIFrame* prevSibling = FindPreviousSibling(iter); 5836 5837 // Now, find the geometric parent so that we can handle 5838 // continuations properly. Use the prev sibling if we have it; 5839 // otherwise use the next sibling. 5840 if (prevSibling) { 5841 aInsertion->mParentFrame = 5842 prevSibling->GetParent()->GetContentInsertionFrame(); 5843 *aIsAppend = 5844 !::GetInsertNextSibling(aInsertion->mParentFrame, prevSibling) && 5845 !nsLayoutUtils::GetNextContinuationOrIBSplitSibling( 5846 aInsertion->mParentFrame) && 5847 !IsWrapperPseudo(aInsertion->mParentFrame); 5848 } else if (nsIFrame* nextSibling = FindNextSibling(iter)) { 5849 // If there is no previous sibling, then find the frame that follows 5850 aInsertion->mParentFrame = 5851 nextSibling->GetParent()->GetContentInsertionFrame(); 5852 } else { 5853 // No previous or next sibling, so treat this like an appended frame. 5854 *aIsAppend = true; 5855 5856 aInsertion->mParentFrame = 5857 ::ContinuationToAppendTo(aInsertion->mParentFrame); 5858 5859 prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, nullptr); 5860 } 5861 5862 return prevSibling; 5863 } 5864 5865 nsContainerFrame* nsCSSFrameConstructor::GetContentInsertionFrameFor( 5866 nsIContent* aContent) { 5867 nsIFrame* frame; 5868 while (!(frame = aContent->GetPrimaryFrame())) { 5869 if (!IsDisplayContents(aContent)) { 5870 return nullptr; 5871 } 5872 5873 aContent = aContent->GetFlattenedTreeParent(); 5874 if (!aContent) { 5875 return nullptr; 5876 } 5877 } 5878 5879 // If the content of the frame is not the desired content then this is not 5880 // really a frame for the desired content. 5881 // XXX This check is needed due to bug 135040. Remove it once that's fixed. 5882 if (frame->GetContent() != aContent) { 5883 return nullptr; 5884 } 5885 5886 nsContainerFrame* insertionFrame = frame->GetContentInsertionFrame(); 5887 5888 NS_ASSERTION(!insertionFrame || insertionFrame == frame || !frame->IsLeaf(), 5889 "The insertion frame is the primary frame or the primary frame " 5890 "isn't a leaf"); 5891 5892 return insertionFrame; 5893 } 5894 5895 static bool IsSpecialFramesetChild(nsIContent* aContent) { 5896 // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init. 5897 return aContent->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame); 5898 } 5899 5900 static void InvalidateCanvasIfNeeded(PresShell* aPresShell, nsIContent* aNode); 5901 5902 void nsCSSFrameConstructor::AddTextItemIfNeeded( 5903 nsFrameConstructorState& aState, const ComputedStyle& aParentStyle, 5904 const InsertionPoint& aInsertion, nsIContent* aPossibleTextContent, 5905 FrameConstructionItemList& aItems) { 5906 MOZ_ASSERT(aPossibleTextContent, "Must have node"); 5907 if (!aPossibleTextContent->IsText() || 5908 !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) || 5909 aPossibleTextContent->HasFlag(NODE_NEEDS_FRAME)) { 5910 // Not text, or not suppressed due to being all-whitespace (if it were being 5911 // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or 5912 // going to be reframed anyway. 5913 return; 5914 } 5915 MOZ_ASSERT(!aPossibleTextContent->GetPrimaryFrame(), 5916 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE"); 5917 AddFrameConstructionItems(aState, aPossibleTextContent, false, aParentStyle, 5918 aInsertion, aItems); 5919 } 5920 5921 void nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aContent) { 5922 if (!aContent->IsText() || 5923 !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) || 5924 aContent->HasFlag(NODE_NEEDS_FRAME)) { 5925 // Not text, or not suppressed due to being all-whitespace (if it were being 5926 // suppressed, it would have the NS_CREATE_FRAME_IF_NON_WHITESPACE flag), or 5927 // going to be reframed anyway. 5928 return; 5929 } 5930 MOZ_ASSERT(!aContent->GetPrimaryFrame(), 5931 "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE"); 5932 ContentInserted(aContent, InsertionKind::Async); 5933 } 5934 5935 #ifdef DEBUG 5936 void nsCSSFrameConstructor::CheckBitsForLazyFrameConstruction( 5937 nsIContent* aParent) { 5938 // If we hit a node with no primary frame, or the NODE_NEEDS_FRAME bit set 5939 // we want to assert, but leaf frames that process their own children and may 5940 // ignore anonymous children (eg framesets) make this complicated. So we set 5941 // these two booleans if we encounter these situations and unset them if we 5942 // hit a node with a leaf frame. 5943 // 5944 // It's fine if one of node without primary frame is in a display:none 5945 // subtree. 5946 // 5947 // Also, it's fine if one of the nodes without primary frame is a display: 5948 // contents node. 5949 bool noPrimaryFrame = false; 5950 bool needsFrameBitSet = false; 5951 nsIContent* content = aParent; 5952 while (content && !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) { 5953 if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) { 5954 noPrimaryFrame = needsFrameBitSet = false; 5955 } 5956 if (!noPrimaryFrame && !content->GetPrimaryFrame()) { 5957 noPrimaryFrame = !IsDisplayContents(content); 5958 } 5959 if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) { 5960 needsFrameBitSet = true; 5961 } 5962 5963 content = content->GetFlattenedTreeParent(); 5964 } 5965 if (content && content->GetPrimaryFrame() && 5966 content->GetPrimaryFrame()->IsLeaf()) { 5967 noPrimaryFrame = needsFrameBitSet = false; 5968 } 5969 MOZ_ASSERT(!noPrimaryFrame, 5970 "Ancestors of nodes with frames to be " 5971 "constructed lazily should have frames"); 5972 MOZ_ASSERT(!needsFrameBitSet, 5973 "Ancestors of nodes with frames to be " 5974 "constructed lazily should not have NEEDS_FRAME bit set"); 5975 } 5976 #endif 5977 5978 // Returns true if this operation can be lazy, false if not. 5979 // 5980 // NOTE(emilio): This function assumes that the flattened tree 5981 // parent of all the appended children is the same, which holds because this 5982 // gets called after GetRangeInsertionPoint which notifies individually. 5983 void nsCSSFrameConstructor::ConstructLazily(nsIContent* aStartChild, 5984 nsIContent* aEndChild) { 5985 MOZ_ASSERT(aStartChild->GetParent()); 5986 5987 // We can construct lazily; just need to set suitable bits in the content 5988 // tree. 5989 Element* parent = aStartChild->GetFlattenedTreeParentElement(); 5990 if (!parent) { 5991 // Not part of the flat tree, nothing to do. 5992 return; 5993 } 5994 5995 if (Servo_Element_IsDisplayNone(parent)) { 5996 // Nothing to do either. 5997 // 5998 // FIXME(emilio): This should be an assert, except for weird <frameset> 5999 // stuff that does its own frame construction. Such an assert would fire in 6000 // layout/style/crashtests/1411478.html, for example. 6001 return; 6002 } 6003 6004 // Set NODE_NEEDS_FRAME on the new nodes. 6005 for (nsIContent* child = aStartChild; child != aEndChild; 6006 child = child->GetNextSibling()) { 6007 NS_ASSERTION(!child->GetPrimaryFrame() || 6008 child->GetPrimaryFrame()->GetContent() != child, 6009 // XXX the child->GetPrimaryFrame()->GetContent() != child 6010 // check is needed due to bug 135040. Remove it once that's 6011 // fixed. 6012 "setting NEEDS_FRAME on a node that already has a frame?"); 6013 child->SetFlags(NODE_NEEDS_FRAME); 6014 } 6015 6016 CheckBitsForLazyFrameConstruction(parent); 6017 parent->NoteDescendantsNeedFramesForServo(); 6018 } 6019 6020 void nsCSSFrameConstructor::IssueSingleInsertNofications( 6021 nsIContent* aStartChild, nsIContent* aEndChild, 6022 InsertionKind aInsertionKind) { 6023 for (nsIContent* child = aStartChild; child != aEndChild; 6024 child = child->GetNextSibling()) { 6025 // XXX the GetContent() != child check is needed due to bug 135040. 6026 // Remove it once that's fixed. 6027 MOZ_ASSERT(!child->GetPrimaryFrame() || 6028 child->GetPrimaryFrame()->GetContent() != child); 6029 6030 // Call ContentRangeInserted with this node. 6031 ContentRangeInserted(child, child->GetNextSibling(), aInsertionKind); 6032 } 6033 } 6034 6035 nsCSSFrameConstructor::InsertionPoint 6036 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aStartChild, 6037 nsIContent* aEndChild, 6038 InsertionKind aInsertionKind) { 6039 MOZ_ASSERT(aStartChild); 6040 MOZ_ASSERT(aStartChild->GetParentNode()); 6041 6042 // If the children of the container may be distributed to different insertion 6043 // points, insert them separately and bail out, letting ContentInserted handle 6044 // the mess. 6045 if (aStartChild->GetParentNode()->GetShadowRoot()) { 6046 IssueSingleInsertNofications(aStartChild, aEndChild, aInsertionKind); 6047 return {}; 6048 } 6049 6050 #ifdef DEBUG 6051 { 6052 nsIContent* expectedParent = aStartChild->GetFlattenedTreeParent(); 6053 for (nsIContent* child = aStartChild->GetNextSibling(); child; 6054 child = child->GetNextSibling()) { 6055 MOZ_ASSERT(child->GetFlattenedTreeParent() == expectedParent); 6056 } 6057 } 6058 #endif 6059 6060 // Now the flattened tree parent of all the siblings is the same, just use the 6061 // same insertion point. 6062 return GetInsertionPoint(aStartChild); 6063 } 6064 6065 bool nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame, 6066 nsIContent* aStartChild, 6067 nsIContent* aEndChild) { 6068 if (aParentFrame->IsFrameSetFrame()) { 6069 // Check whether we have any kids we care about. 6070 for (nsIContent* cur = aStartChild; cur != aEndChild; 6071 cur = cur->GetNextSibling()) { 6072 if (IsSpecialFramesetChild(cur)) { 6073 // Just reframe the parent, since framesets are weird like that. 6074 RecreateFramesForContent(aParentFrame->GetContent(), 6075 InsertionKind::Async); 6076 return true; 6077 } 6078 } 6079 } 6080 return false; 6081 } 6082 6083 void nsCSSFrameConstructor::LazilyStyleNewChildRange(nsIContent* aStartChild, 6084 nsIContent* aEndChild) { 6085 for (nsIContent* child = aStartChild; child != aEndChild; 6086 child = child->GetNextSibling()) { 6087 if (child->IsElement()) { 6088 child->AsElement()->NoteDirtyForServo(); 6089 } 6090 } 6091 } 6092 6093 #ifdef DEBUG 6094 static bool IsFlattenedTreeChild(nsIContent* aParent, nsIContent* aChild) { 6095 FlattenedChildIterator iter(aParent); 6096 for (nsIContent* node = iter.GetNextChild(); node; 6097 node = iter.GetNextChild()) { 6098 if (node == aChild) { 6099 return true; 6100 } 6101 } 6102 return false; 6103 } 6104 #endif 6105 6106 void nsCSSFrameConstructor::StyleNewChildRange(nsIContent* aStartChild, 6107 nsIContent* aEndChild) { 6108 ServoStyleSet* styleSet = mPresShell->StyleSet(); 6109 6110 for (nsIContent* child = aStartChild; child != aEndChild; 6111 child = child->GetNextSibling()) { 6112 if (!child->IsElement()) { 6113 continue; 6114 } 6115 6116 Element* childElement = child->AsElement(); 6117 6118 // We only come in here from non-lazy frame construction, so the children 6119 // should be unstyled. 6120 MOZ_ASSERT(!childElement->HasServoData()); 6121 6122 #ifdef DEBUG 6123 { 6124 // Furthermore, all of them should have the same flattened tree parent 6125 // (GetRangeInsertionPoint ensures it). And that parent should be styled, 6126 // otherwise we would've never found an insertion point at all. 6127 Element* parent = childElement->GetFlattenedTreeParentElement(); 6128 MOZ_ASSERT(parent); 6129 MOZ_ASSERT(parent->HasServoData()); 6130 MOZ_ASSERT( 6131 IsFlattenedTreeChild(parent, child), 6132 "GetFlattenedTreeParent and ChildIterator don't agree, fix this!"); 6133 } 6134 #endif 6135 6136 styleSet->StyleNewSubtree(childElement); 6137 } 6138 } 6139 6140 // This is a bit slow, but sometimes we need it. 6141 static bool ParentIsWrapperAnonBox(nsIFrame* aParent) { 6142 nsIFrame* maybeAnonBox = aParent; 6143 if (maybeAnonBox->Style()->GetPseudoType() == PseudoStyleType::cellContent) { 6144 // The thing that would maybe be a wrapper anon box is the cell. 6145 maybeAnonBox = maybeAnonBox->GetParent(); 6146 } 6147 return maybeAnonBox->Style()->IsWrapperAnonBox(); 6148 } 6149 6150 void nsCSSFrameConstructor::ContentAppended(nsIContent* aFirstNewContent, 6151 InsertionKind aInsertionKind) { 6152 return ContentRangeInserted(aFirstNewContent, nullptr, aInsertionKind); 6153 } 6154 6155 void nsCSSFrameConstructor::ContentInserted(nsIContent* aChild, 6156 InsertionKind aInsertionKind) { 6157 ContentRangeInserted(aChild, aChild->GetNextSibling(), aInsertionKind); 6158 } 6159 6160 // It's very likely that we don't have any existing captions (because we only 6161 // render one of them, see bug 144517). We also don't fragment table captions. 6162 // So, prefer just walking the existing captions and searching it, rather than 6163 // adding a special GetInsertionPrevSibling() version that skips everything but 6164 // table captions. 6165 static nsIFrame* FindCaptionPrevSibling(nsTableWrapperFrame* aTable, 6166 nsIContent* aCaptionContent) { 6167 nsIFrame* prevSibling = aTable->InnerTableFrame(); 6168 if (nsIFrame* firstCaption = prevSibling->GetNextSibling()) { 6169 nsContentUtils::NodeIndexCache cache; 6170 for (auto* caption = firstCaption; caption; 6171 caption = caption->GetNextSibling()) { 6172 if (nsContentUtils::CompareTreePosition<TreeKind::Flat>( 6173 caption->GetContent(), aCaptionContent, nullptr, &cache) >= 0) { 6174 break; 6175 } 6176 prevSibling = caption; 6177 } 6178 } 6179 return prevSibling; 6180 } 6181 6182 // ContentRangeInserted handles creating frames for a range of nodes that 6183 // might not be at the end of their childlist. ContentRangeInserted isn't a real 6184 // content notification, but rather it handles regular ContentInserted calls 6185 // for a single node as well as the lazy construction of frames for a range of 6186 // nodes when called from CreateNeededFrames. For a range of nodes to be 6187 // suitable to have its frames constructed all at once they must meet the same 6188 // conditions that ContentAppended imposes (GetRangeInsertionPoint checks 6189 // these), plus more. 6190 void nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aStartChild, 6191 nsIContent* aEndChild, 6192 InsertionKind aInsertionKind) { 6193 AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ContentRangeInserted", 6194 LAYOUT_FrameConstruction); 6195 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); 6196 6197 MOZ_ASSERT(aStartChild, "must always pass a child"); 6198 6199 #ifdef DEBUG 6200 if (gNoisyContentUpdates) { 6201 printf( 6202 "nsCSSFrameConstructor::ContentRangeInserted container=%p " 6203 "start-child=%p end-child=%p lazy=%d\n", 6204 aStartChild->GetParent(), aStartChild, aEndChild, 6205 aInsertionKind == InsertionKind::Async); 6206 if (gReallyNoisyContentUpdates) { 6207 if (aStartChild->GetParent()) { 6208 aStartChild->GetParent()->List(stdout, 0); 6209 } else { 6210 aStartChild->List(stdout, 0); 6211 } 6212 } 6213 } 6214 6215 for (nsIContent* child = aStartChild; child != aEndChild; 6216 child = child->GetNextSibling()) { 6217 // XXX the GetContent() != child check is needed due to bug 135040. 6218 // Remove it once that's fixed. 6219 MOZ_ASSERT( 6220 !child->GetPrimaryFrame() || 6221 child->GetPrimaryFrame()->GetContent() != child, 6222 "asked to construct a frame for a node that already has a frame"); 6223 } 6224 #endif 6225 6226 // If we have a null parent, then this must be the document element being 6227 // inserted, or some other child of the document in the DOM (might be a 6228 // processing instruction or comment). 6229 if (!aStartChild->GetParent()) { 6230 Element* docElement = mDocument->GetRootElement(); 6231 const bool foundRoot = [&] { 6232 for (nsIContent* cur = aStartChild; cur != aEndChild; 6233 cur = cur->GetNextSibling()) { 6234 if (cur == docElement) { 6235 return true; 6236 } 6237 } 6238 return false; 6239 }(); 6240 6241 if (!foundRoot) { 6242 // Not the root element (could be e.g. a comment), just bail out 6243 return; 6244 } 6245 6246 MOZ_ASSERT(!mRootElementFrame, "root element frame already created"); 6247 if (aInsertionKind == InsertionKind::Async) { 6248 docElement->SetFlags(NODE_NEEDS_FRAME); 6249 LazilyStyleNewChildRange(docElement, nullptr); 6250 return; 6251 } 6252 6253 // Create frames for the document element and its child elements 6254 if (ConstructDocElementFrame(docElement)) { 6255 InvalidateCanvasIfNeeded(mPresShell, docElement); 6256 #ifdef DEBUG 6257 if (gReallyNoisyContentUpdates) { 6258 printf( 6259 "nsCSSFrameConstructor::ContentRangeInserted: resulting frame " 6260 "model:\n"); 6261 mRootElementFrame->List(stdout); 6262 } 6263 #endif 6264 } 6265 6266 #ifdef ACCESSIBILITY 6267 if (nsAccessibilityService* accService = GetAccService()) { 6268 accService->ContentRangeInserted(mPresShell, aStartChild, aEndChild); 6269 } 6270 #endif 6271 return; 6272 } 6273 6274 const bool isSingleInsert = aStartChild->GetNextSibling() == aEndChild; 6275 InsertionPoint insertion; 6276 if (isSingleInsert) { 6277 // See if we have a Shadow DOM insertion point. If so, then that's our real 6278 // parent frame; if not, then the frame hasn't been built yet and we just 6279 // bail. 6280 insertion = GetInsertionPoint(aStartChild); 6281 } else { 6282 // Get our insertion point. If we need to issue single ContentInserteds 6283 // GetRangeInsertionPoint will take care of that for us. 6284 LAYOUT_PHASE_TEMP_EXIT(); 6285 insertion = GetRangeInsertionPoint(aStartChild, aEndChild, aInsertionKind); 6286 LAYOUT_PHASE_TEMP_REENTER(); 6287 } 6288 6289 if (!insertion.mParentFrame) { 6290 // We're punting on frame construction because there's no container frame. 6291 // The Servo-backed style system handles this case like the lazy frame 6292 // construction case, except when we're already constructing frames, in 6293 // which case we shouldn't need to do anything else. 6294 if (aInsertionKind == InsertionKind::Async) { 6295 LazilyStyleNewChildRange(aStartChild, aEndChild); 6296 } 6297 return; 6298 } 6299 6300 if (aInsertionKind == InsertionKind::Async) { 6301 ConstructLazily(aStartChild, aEndChild); 6302 LazilyStyleNewChildRange(aStartChild, aEndChild); 6303 return; 6304 } 6305 6306 bool isAppend; 6307 nsIFrame* prevSibling = 6308 GetInsertionPrevSibling(&insertion, aStartChild, &isAppend); 6309 6310 LayoutFrameType frameType = insertion.mParentFrame->Type(); 6311 LAYOUT_PHASE_TEMP_EXIT(); 6312 if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild, 6313 aEndChild)) { 6314 LAYOUT_PHASE_TEMP_REENTER(); 6315 return; 6316 } 6317 LAYOUT_PHASE_TEMP_REENTER(); 6318 6319 // Don't construct kids of leaves 6320 if (insertion.mParentFrame->IsLeaf()) { 6321 // Clear lazy bits so we don't try to construct again. 6322 ClearLazyBits(aStartChild, aEndChild); 6323 return; 6324 } 6325 6326 LAYOUT_PHASE_TEMP_EXIT(); 6327 if (WipeInsertionParent(insertion.mParentFrame)) { 6328 LAYOUT_PHASE_TEMP_REENTER(); 6329 return; 6330 } 6331 LAYOUT_PHASE_TEMP_REENTER(); 6332 6333 nsFrameConstructorState state( 6334 mPresShell, GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS), 6335 GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS), 6336 GetFloatContainingBlock(insertion.mParentFrame), 6337 do_AddRef(mFrameTreeState)); 6338 6339 // Recover state for the containing block - we need to know if 6340 // it has :first-letter or :first-line style applied to it. The 6341 // reason we care is that the internal structure in these cases 6342 // is not the normal structure and requires custom updating 6343 // logic. 6344 nsContainerFrame* containingBlock = state.mFloatedList.mContainingBlock; 6345 bool haveFirstLetterStyle = false; 6346 bool haveFirstLineStyle = false; 6347 6348 // In order to shave off some cycles, we only dig up the 6349 // containing block haveFirst* flags if the parent frame where 6350 // the insertion/append is occurring is an inline or block 6351 // container. For other types of containers this isn't relevant. 6352 StyleDisplayInside parentDisplayInside = 6353 insertion.mParentFrame->StyleDisplay()->DisplayInside(); 6354 6355 // Examine the insertion.mParentFrame where the insertion is taking 6356 // place. If it's a certain kind of container then some special 6357 // processing is done. 6358 if (StyleDisplayInside::Flow == parentDisplayInside) { 6359 // Recover the special style flags for the containing block 6360 if (containingBlock) { 6361 haveFirstLetterStyle = HasFirstLetterStyle(containingBlock); 6362 haveFirstLineStyle = ShouldHaveFirstLineStyle( 6363 containingBlock->GetContent(), containingBlock->Style()); 6364 } 6365 6366 if (haveFirstLetterStyle) { 6367 // If our current insertion.mParentFrame is a Letter frame, use its parent 6368 // as our new parent hint 6369 if (insertion.mParentFrame->IsLetterFrame()) { 6370 // If insertion.mParentFrame is out of flow, then we actually want the 6371 // parent of the placeholder frame. 6372 if (insertion.mParentFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { 6373 nsPlaceholderFrame* placeholderFrame = 6374 insertion.mParentFrame->GetPlaceholderFrame(); 6375 NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?"); 6376 insertion.mParentFrame = placeholderFrame->GetParent(); 6377 } else { 6378 insertion.mParentFrame = insertion.mParentFrame->GetParent(); 6379 } 6380 } 6381 6382 // Remove the old letter frames before doing the insertion 6383 RemoveLetterFrames(mPresShell, state.mFloatedList.mContainingBlock); 6384 6385 // Removing the letterframes messes around with the frame tree, removing 6386 // and creating frames. We need to reget our prevsibling, parent frame, 6387 // etc. 6388 prevSibling = GetInsertionPrevSibling(&insertion, aStartChild, &isAppend); 6389 frameType = insertion.mParentFrame->Type(); 6390 } 6391 } 6392 6393 // This handles fallback to 'list-style-type' when a 'list-style-image' fails 6394 // to load. 6395 if (aStartChild->IsInNativeAnonymousSubtree() && 6396 aStartChild->IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage)) { 6397 MOZ_ASSERT(isSingleInsert); 6398 MOZ_ASSERT(insertion.mParentFrame->Style()->GetPseudoType() == 6399 PseudoStyleType::marker, 6400 "we can only handle ::marker fallback for now"); 6401 nsIContent* const nextSibling = aStartChild->GetNextSibling(); 6402 MOZ_ASSERT(nextSibling && nextSibling->IsText(), 6403 "expected a text node after the list-style-image image"); 6404 DestroyContext context(mPresShell); 6405 RemoveFrame(context, FrameChildListID::Principal, 6406 nextSibling->GetPrimaryFrame()); 6407 auto* const container = aStartChild->GetParent()->AsElement(); 6408 nsIContent* firstNewChild = nullptr; 6409 auto InsertChild = [this, container, nextSibling, 6410 &firstNewChild](RefPtr<nsIContent>&& aChild) { 6411 // We don't strictly have to set NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE 6412 // here; it would get set under AppendChildTo. But AppendChildTo might 6413 // think that we're going from not being anonymous to being anonymous and 6414 // do some extra work; setting the flag here avoids that. 6415 aChild->SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); 6416 container->InsertChildBefore(aChild, nextSibling, false, IgnoreErrors()); 6417 if (auto* childElement = Element::FromNode(aChild)) { 6418 // If we created any children elements, Servo needs to traverse them, 6419 // but the root is already set up. 6420 mPresShell->StyleSet()->StyleNewSubtree(childElement); 6421 } 6422 if (!firstNewChild) { 6423 firstNewChild = aChild; 6424 } 6425 }; 6426 CreateGeneratedContentFromListStyleType( 6427 state, *insertion.mContainer->AsElement(), 6428 *insertion.mParentFrame->Style(), InsertChild); 6429 if (!firstNewChild) { 6430 // No fallback content - we're done. 6431 return; 6432 } 6433 aStartChild = firstNewChild; 6434 MOZ_ASSERT(firstNewChild->GetNextSibling() == nextSibling, 6435 "list-style-type should only create one child"); 6436 } 6437 6438 AutoFrameConstructionItemList items(this); 6439 RefPtr<ComputedStyle> parentStyle = 6440 ResolveComputedStyle(insertion.mContainer); 6441 ParentType parentType = GetParentType(frameType); 6442 FlattenedChildIterator iter(insertion.mContainer); 6443 const bool haveNoShadowDOM = 6444 !iter.ShadowDOMInvolved() || !iter.GetNextChild(); 6445 if (aStartChild->GetPreviousSibling() && parentType == eTypeBlock && 6446 haveNoShadowDOM) { 6447 // If there's a text node in the normal content list just before the 6448 // new nodes, and it has no frame, make a frame construction item for 6449 // it, because it might need a frame now. No need to do this if our 6450 // parent type is not block, though, since WipeContainingBlock 6451 // already handles that situation. 6452 AddTextItemIfNeeded(state, *parentStyle, insertion, 6453 aStartChild->GetPreviousSibling(), items); 6454 } 6455 6456 const bool suppressWhiteSpaceOptimizations = 6457 isSingleInsert && aStartChild->IsRootOfNativeAnonymousSubtree(); 6458 for (nsIContent* child = aStartChild; child != aEndChild; 6459 child = child->GetNextSibling()) { 6460 AddFrameConstructionItems(state, child, suppressWhiteSpaceOptimizations, 6461 *parentStyle, insertion, items); 6462 } 6463 6464 if (aEndChild && parentType == eTypeBlock && haveNoShadowDOM) { 6465 // If there's a text node in the normal content list just after the 6466 // new nodes, and it has no frame, make a frame construction item for 6467 // it, because it might need a frame now. No need to do this if our 6468 // parent type is not block, though, since WipeContainingBlock 6469 // already handles that situation. 6470 AddTextItemIfNeeded(state, *parentStyle, insertion, aEndChild, items); 6471 } 6472 6473 // Perform special check for diddling around with the frames in a special 6474 // inline frame. If we're appending before :after content, then we're not 6475 // really appending, so let WipeContainingBlock know that. 6476 LAYOUT_PHASE_TEMP_EXIT(); 6477 if (WipeContainingBlock(state, containingBlock, insertion.mParentFrame, items, 6478 isAppend, prevSibling)) { 6479 LAYOUT_PHASE_TEMP_REENTER(); 6480 return; 6481 } 6482 LAYOUT_PHASE_TEMP_REENTER(); 6483 6484 // If the parent is a block frame, and we're not in a special case 6485 // where frames can be moved around, determine if the list is for the 6486 // start or end of the block. 6487 if (insertion.mParentFrame->IsBlockFrameOrSubclass() && 6488 !haveFirstLetterStyle && !haveFirstLineStyle && 6489 !IsFramePartOfIBSplit(insertion.mParentFrame)) { 6490 items.SetLineBoundaryAtStart(!prevSibling || 6491 !prevSibling->IsInlineOutside() || 6492 prevSibling->IsBrFrame()); 6493 auto* nextSibling = 6494 ::GetInsertNextSibling(insertion.mParentFrame, prevSibling); 6495 items.SetLineBoundaryAtEnd(!nextSibling || 6496 !nextSibling->IsInlineOutside() || 6497 nextSibling->IsBrFrame()); 6498 } 6499 // To suppress whitespace-only text frames, we have to verify that 6500 // our container's DOM child list matches its flattened tree child list. 6501 items.SetParentHasNoShadowDOM(haveNoShadowDOM); 6502 6503 nsFrameConstructorSaveState floatSaveState; 6504 state.MaybePushFloatContainingBlock(insertion.mParentFrame, floatSaveState); 6505 6506 if (state.mPresContext->IsPaginated()) { 6507 // Because this function can be called outside frame construction, we need 6508 // to set state.mAutoPageNameValue based on what the parent frame's auto 6509 // value is. 6510 // Calling this from outside the frame constructor can violate many of the 6511 // expectations in AutoFrameConstructionPageName, and unlike during frame 6512 // construction we already have an auto value from parentFrame, so we do 6513 // not use AutoFrameConstructionPageName here. 6514 state.mAutoPageNameValue = insertion.mParentFrame->GetAutoPageValue(); 6515 #ifdef DEBUG 6516 insertion.mParentFrame->mWasVisitedByAutoFrameConstructionPageName = true; 6517 #endif 6518 } 6519 6520 // If the container is a table and a caption will be appended, it needs to be 6521 // put in the table wrapper frame's additional child list. 6522 // We make no attempt here to set flags to indicate whether the list 6523 // will be at the start or end of a block. It doesn't seem worthwhile. 6524 nsFrameList frameList, captionList; 6525 ConstructFramesFromItemList(state, items, insertion.mParentFrame, 6526 ParentIsWrapperAnonBox(insertion.mParentFrame), 6527 frameList); 6528 6529 if (frameList.NotEmpty()) { 6530 for (nsIContent* child = aStartChild; child != aEndChild; 6531 child = child->GetNextSibling()) { 6532 InvalidateCanvasIfNeeded(mPresShell, child); 6533 } 6534 6535 if (LayoutFrameType::Table == frameType || 6536 LayoutFrameType::TableWrapper == frameType) { 6537 PullOutCaptionFrames(frameList, captionList); 6538 if (prevSibling && prevSibling->IsTableCaption()) { 6539 // This can happen, but only if the table is empty (otherwise 6540 // SafeToInsertPseudoNeedingChildren bails). 6541 prevSibling = nullptr; 6542 } 6543 } 6544 } 6545 6546 if (haveFirstLineStyle && insertion.mParentFrame == containingBlock && 6547 isAppend) { 6548 // It's possible that the new frame goes into a first-line 6549 // frame. Look at it and see... 6550 AppendFirstLineFrames(state, containingBlock->GetContent(), containingBlock, 6551 frameList); 6552 } else if (insertion.mParentFrame->Style()->IsInFirstLineSubtree()) { 6553 CheckForFirstLineInsertion(insertion.mParentFrame, frameList); 6554 CheckForFirstLineInsertion(insertion.mParentFrame, captionList); 6555 } 6556 6557 // We might have captions; put them into the principal child list of the table 6558 // wrapper frame. 6559 if (captionList.NotEmpty()) { 6560 NS_ASSERTION(LayoutFrameType::Table == frameType || 6561 LayoutFrameType::TableWrapper == frameType, 6562 "parent for caption is not table?"); 6563 // We need to determine where to put the caption items; start with the 6564 // the parent frame that has already been determined and get the insertion 6565 // prevsibling of the first caption item. 6566 nsContainerFrame* outerTable = insertion.mParentFrame->IsTableFrame() 6567 ? insertion.mParentFrame->GetParent() 6568 : insertion.mParentFrame; 6569 6570 // If the parent is not a table wrapper frame we will try to add frames 6571 // to a named child list that the parent does not honor and the frames 6572 // will get lost. 6573 MOZ_ASSERT(outerTable->IsTableWrapperFrame(), 6574 "Pseudo frame construction failure; " 6575 "a caption can be only a child of a table wrapper frame"); 6576 6577 nsIFrame* captionPrevSibling = 6578 FindCaptionPrevSibling(static_cast<nsTableWrapperFrame*>(outerTable), 6579 captionList.FirstChild()->GetContent()); 6580 captionList.ApplySetParent(outerTable); 6581 if (!captionPrevSibling->GetNextSibling()) { 6582 AppendFrames(outerTable, FrameChildListID::Principal, 6583 std::move(captionList)); 6584 } else { 6585 InsertFrames(outerTable, FrameChildListID::Principal, captionPrevSibling, 6586 std::move(captionList)); 6587 } 6588 } 6589 6590 LAYOUT_PHASE_TEMP_EXIT(); 6591 if (MaybeRecreateForColumnSpan(state, insertion.mParentFrame, frameList, 6592 prevSibling)) { 6593 LAYOUT_PHASE_TEMP_REENTER(); 6594 return; 6595 } 6596 LAYOUT_PHASE_TEMP_REENTER(); 6597 6598 if (frameList.NotEmpty()) { 6599 // Notify the parent frame 6600 if (isAppend) { 6601 AppendFramesToParent(state, insertion.mParentFrame, frameList, 6602 prevSibling); 6603 } else { 6604 InsertFrames(insertion.mParentFrame, FrameChildListID::Principal, 6605 prevSibling, std::move(frameList)); 6606 } 6607 } 6608 6609 if (haveFirstLetterStyle) { 6610 // Recover the letter frames for the containing block when 6611 // it has first-letter style. 6612 RecoverLetterFrames(state.mFloatedList.mContainingBlock); 6613 } 6614 6615 #ifdef DEBUG 6616 if (gReallyNoisyContentUpdates && insertion.mParentFrame) { 6617 printf( 6618 "nsCSSFrameConstructor::ContentRangeInserted: resulting frame " 6619 "model:\n"); 6620 insertion.mParentFrame->List(stdout); 6621 } 6622 #endif 6623 6624 #ifdef ACCESSIBILITY 6625 if (nsAccessibilityService* accService = GetAccService()) { 6626 accService->ContentRangeInserted(mPresShell, aStartChild, aEndChild); 6627 } 6628 #endif 6629 } 6630 6631 static bool IsWhitespaceFrame(const nsIFrame* aFrame) { 6632 MOZ_ASSERT(aFrame, "invalid argument"); 6633 return aFrame->IsTextFrame() && aFrame->GetContent()->TextIsOnlyWhitespace(); 6634 } 6635 6636 static bool IsSyntheticColGroup(const nsIFrame* aFrame) { 6637 return aFrame->IsTableColGroupFrame() && 6638 static_cast<const nsTableColGroupFrame*>(aFrame)->IsSynthetic(); 6639 } 6640 6641 static bool IsOnlyNonWhitespaceFrameInList( 6642 const nsFrameList& aFrameList, const nsIFrame* aFrame, 6643 const nsIFrame* aIgnoreFrame = nullptr) { 6644 for (const nsIFrame* f : aFrameList) { 6645 if (f == aIgnoreFrame) { 6646 continue; 6647 } 6648 if (f == aFrame) { 6649 // If we have continuations, ignore them too. 6650 aFrame = aFrame->GetNextContinuation(); 6651 } else if (!IsWhitespaceFrame(f) && !IsSyntheticColGroup(f)) { 6652 // Synthetic colgroups get created unconditionally, so let's not consider 6653 // them as giving us a non-whitespace frame. 6654 return false; 6655 } 6656 } 6657 return true; 6658 } 6659 6660 static bool AllChildListsAreEffectivelyEmpty(nsIFrame* aFrame) { 6661 for (auto& [list, listID] : aFrame->ChildLists()) { 6662 if (list.IsEmpty()) { 6663 continue; 6664 } 6665 // We have some existing frame, usually that would be considered as making 6666 // this list nonempty. But let's make an exception for the synthetic 6667 // colgroup that tables have, since that gets created unconditionally. 6668 if (listID == FrameChildListID::ColGroup) { 6669 if (nsIFrame* f = list.OnlyChild(); f && IsSyntheticColGroup(f)) { 6670 continue; 6671 } 6672 } 6673 return false; 6674 } 6675 return true; 6676 } 6677 6678 static bool SafeToInsertPseudoNeedingChildren(nsIFrame* aFrame) { 6679 return AllChildListsAreEffectivelyEmpty(aFrame); 6680 } 6681 6682 // Returns true if aFrame is the only meaningful child of aParent (which is 6683 // known to be a wrapper-pseudo). This lets us determine whether aParent can be 6684 // removed, as a result of aFrame being removed. 6685 static bool IsOnlyMeaningfulChildOfWrapperPseudo(nsIFrame* aFrame, 6686 nsIFrame* aParent) { 6687 MOZ_ASSERT(IsWrapperPseudo(aParent)); 6688 // Handle a few special cases with tables and colgroups / captions. 6689 if (aParent->IsTableFrame()) { 6690 auto* wrapper = aParent->GetParent(); 6691 MOZ_ASSERT(wrapper); 6692 MOZ_ASSERT(wrapper->IsTableWrapperFrame()); 6693 MOZ_ASSERT(!aFrame->IsTableCaption(), 6694 "Caption parent should be the wrapper"); 6695 // We can't remove the table if there are any captions present (captions are 6696 // never anonymous themselves), because table wrapper always relies on 6697 // having a table frame. 6698 if (!wrapper->PrincipalChildList().OnlyChild()) { 6699 return false; 6700 } 6701 // Similarly we can't remove the table if there's still a non-anonymous col 6702 // group (unless aFrame _is_ the non-anonymous colgroup). 6703 if (aFrame->IsTableColGroupFrame()) { 6704 return aParent->PrincipalChildList().IsEmpty() && 6705 IsOnlyNonWhitespaceFrameInList( 6706 aParent->GetChildList(FrameChildListID::ColGroup), aFrame); 6707 } 6708 const auto& colGroupList = 6709 aParent->GetChildList(FrameChildListID::ColGroup); 6710 if (!colGroupList.IsEmpty()) { 6711 nsIFrame* f = colGroupList.OnlyChild(); 6712 if (!f || !IsSyntheticColGroup(f)) { 6713 return false; 6714 } 6715 } 6716 } 6717 if (aFrame->IsTableCaption()) { 6718 MOZ_ASSERT(aParent->IsTableWrapperFrame()); 6719 auto* table = static_cast<nsTableWrapperFrame*>(aParent)->InnerTableFrame(); 6720 MOZ_ASSERT(table); 6721 return IsOnlyNonWhitespaceFrameInList(aParent->PrincipalChildList(), aFrame, 6722 /* aIgnoreFrame = */ table) && 6723 // This checks for both colgroups and the principal list of the table 6724 // frame. 6725 AllChildListsAreEffectivelyEmpty(table); 6726 } 6727 MOZ_ASSERT(!aFrame->IsTableColGroupFrame()); 6728 return IsOnlyNonWhitespaceFrameInList(aParent->PrincipalChildList(), aFrame); 6729 } 6730 6731 static bool CanRemoveWrapperPseudoForChildRemoval(nsIFrame* aFrame, 6732 nsIFrame* aParent) { 6733 if (!IsOnlyMeaningfulChildOfWrapperPseudo(aFrame, aParent)) { 6734 return false; 6735 } 6736 if (aParent->IsRubyBaseContainerFrame()) { 6737 // We can't remove the first ruby base container of a ruby frame unless 6738 // it has no siblings. See CreateNeededPseudoSiblings. 6739 return aParent->GetPrevSibling() || !aParent->GetNextSibling(); 6740 } 6741 return true; 6742 } 6743 6744 bool nsCSSFrameConstructor::ContentWillBeRemoved(nsIContent* aChild, 6745 RemovalKind aKind) { 6746 MOZ_ASSERT(aChild); 6747 MOZ_ASSERT( 6748 !aChild->IsRootOfNativeAnonymousSubtree() || !aChild->GetNextSibling(), 6749 "Anonymous roots don't have siblings"); 6750 AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ContentWillBeRemoved", 6751 LAYOUT_FrameConstruction); 6752 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); 6753 nsPresContext* presContext = mPresShell->GetPresContext(); 6754 MOZ_ASSERT(presContext, "Our presShell should have a valid presContext"); 6755 6756 const bool wasRemovingContent = mRemovingContent; 6757 auto _ = MakeScopeExit([&] { mRemovingContent = wasRemovingContent; }); 6758 mRemovingContent = true; 6759 6760 // We want to detect when the viewport override element stored in the 6761 // prescontext is in the subtree being removed. Except in fullscreen cases 6762 // (which are handled in Element::UnbindFromTree and do not get stored on the 6763 // prescontext), the override element is always either the root element or a 6764 // <body> child of the root element. So we can only be removing the stored 6765 // override element if the thing being removed is either the override element 6766 // itself or the root element (which can be a parent of the override element). 6767 // 6768 // The !wasRemovingContent check makes sure that we don't re-enter here from 6769 // other ContentWillBeRemoved calls, as that'd be useless work, and we don't 6770 // want to incorrectly pick aChild again as our viewport scroll style element 6771 // if it's getting removed from the DOM. 6772 if ((aChild == presContext->GetViewportScrollStylesOverrideElement() || 6773 aChild->IsRootElement()) && 6774 !wasRemovingContent) { 6775 // We might be removing the element that we propagated viewport scrollbar 6776 // styles from. Recompute those. (This clause covers two of the three 6777 // possible scrollbar-propagation sources: the <body> [as aChild or a 6778 // descendant] and the root node. The other possible scrollbar-propagation 6779 // source is a fullscreen element, and we have code elsewhere to update 6780 // scrollbars after fullscreen elements are removed -- specifically, it's 6781 // part of the fullscreen cleanup code called by Element::UnbindFromTree. 6782 // We don't handle the fullscreen case here, because it doesn't change the 6783 // scrollbar styles override element stored on the prescontext.) 6784 const Element* removingElement = 6785 aKind == RemovalKind::Dom ? aChild->AsElement() : nullptr; 6786 Element* newOverrideElement = 6787 presContext->UpdateViewportScrollStylesOverride(removingElement); 6788 6789 // If aChild is the root, then we don't need to do any reframing of 6790 // newOverrideElement, because we're about to tear down the whole frame tree 6791 // anyway. And we need to make sure we don't do any such reframing, because 6792 // reframing the <body> can trigger a reframe of the <html> and then reenter 6793 // here. 6794 // 6795 // But if aChild is not the root, and if newOverrideElement is not the root 6796 // and isn't aChild (which it could be if all we're doing here is reframing 6797 // the current override element), it needs reframing. In particular, it 6798 // used to have a scrollframe (because its overflow was not "visible"), but 6799 // now it will propagate its overflow to the viewport, so it should not need 6800 // a scrollframe anymore. 6801 if (aChild->GetParent() && newOverrideElement && 6802 newOverrideElement->GetParent() && newOverrideElement != aChild) { 6803 LAYOUT_PHASE_TEMP_EXIT(); 6804 RecreateFramesForContent(newOverrideElement, InsertionKind::Async); 6805 LAYOUT_PHASE_TEMP_REENTER(); 6806 } 6807 } 6808 6809 #ifdef DEBUG 6810 if (gNoisyContentUpdates) { 6811 printf( 6812 "nsCSSFrameConstructor::ContentWillBeRemoved container=%p child=%p\n", 6813 aChild->GetParent(), aChild); 6814 if (gReallyNoisyContentUpdates) { 6815 aChild->GetParent()->List(stdout, 0); 6816 } 6817 } 6818 #endif 6819 6820 nsIFrame* childFrame = aChild->GetPrimaryFrame(); 6821 if (!childFrame || childFrame->GetContent() != aChild) { 6822 // XXXbz the GetContent() != aChild check is needed due to bug 135040. 6823 // Remove it once that's fixed. 6824 childFrame = nullptr; 6825 } 6826 6827 // If we're removing the root, then make sure to remove things starting at 6828 // the viewport's child instead of the primary frame (which might even be 6829 // null if the root was display:none, even though the frames above it got 6830 // created). Detecting removal of a root is a little exciting; in particular, 6831 // having no parent is necessary but NOT sufficient. 6832 // 6833 // Due to how we process reframes, the content node might not even be in our 6834 // document by now. So explicitly check whether the viewport's first kid's 6835 // content node is aChild. 6836 // 6837 // FIXME(emilio): I think the "might not be in our document" bit is impossible 6838 // now. 6839 bool isRoot = false; 6840 if (!aChild->GetParent()) { 6841 if (nsIFrame* viewport = GetRootFrame()) { 6842 nsIFrame* firstChild = viewport->PrincipalChildList().FirstChild(); 6843 if (firstChild && firstChild->GetContent() == aChild) { 6844 isRoot = true; 6845 childFrame = firstChild; 6846 NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?"); 6847 } 6848 } 6849 } 6850 6851 // We need to be conservative about when to determine whether something has 6852 // display: contents or not because at this point our actual display may be 6853 // different. 6854 // 6855 // Consider the case of: 6856 // 6857 // <div id="A" style="display: contents"><div id="B"></div></div> 6858 // 6859 // If we reconstruct A because its display changed to "none", we still need to 6860 // cleanup the frame on B, but A's display is now "none", so we can't poke at 6861 // the style of it. 6862 // 6863 // FIXME(emilio, bug 1450366): We can make this faster without adding much 6864 // complexity for the display: none -> other case, which right now 6865 // unnecessarily walks the content tree down. 6866 auto CouldHaveBeenDisplayContents = [aKind](nsIContent* aContent) -> bool { 6867 return aContent->IsElement() && (aKind != RemovalKind::Dom || 6868 IsDisplayContents(aContent->AsElement())); 6869 }; 6870 6871 if (!childFrame) { 6872 if (CouldHaveBeenDisplayContents(aChild)) { 6873 // NOTE(emilio): We may iterate through ::before and ::after here and they 6874 // may be gone after the respective ContentWillBeRemoved call. Right now 6875 // StyleChildrenIterator handles that properly, so it's not an issue. 6876 StyleChildrenIterator iter(aChild); 6877 for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) { 6878 if (c->GetPrimaryFrame() || CouldHaveBeenDisplayContents(c)) { 6879 LAYOUT_PHASE_TEMP_EXIT(); 6880 bool didReconstruct = ContentWillBeRemoved(c, aKind); 6881 LAYOUT_PHASE_TEMP_REENTER(); 6882 if (didReconstruct) { 6883 return true; 6884 } 6885 } 6886 } 6887 } 6888 return false; 6889 } 6890 6891 if (aKind != RemovalKind::Dom) { 6892 // Before removing the frames associated with the content object, 6893 // ask them to save their state onto our state object. 6894 CaptureStateForFramesOf(aChild, mFrameTreeState); 6895 } 6896 6897 InvalidateCanvasIfNeeded(mPresShell, aChild); 6898 6899 // See whether we need to remove more than just childFrame 6900 LAYOUT_PHASE_TEMP_EXIT(); 6901 if (MaybeRecreateContainerForFrameRemoval(childFrame)) { 6902 LAYOUT_PHASE_TEMP_REENTER(); 6903 return true; 6904 } 6905 LAYOUT_PHASE_TEMP_REENTER(); 6906 6907 // Get the childFrame's parent frame 6908 nsIFrame* parentFrame = childFrame->GetParent(); 6909 LayoutFrameType parentType = parentFrame->Type(); 6910 6911 if (parentType == LayoutFrameType::FrameSet && 6912 IsSpecialFramesetChild(aChild)) { 6913 // Just reframe the parent, since framesets are weird like that. 6914 LAYOUT_PHASE_TEMP_EXIT(); 6915 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async); 6916 LAYOUT_PHASE_TEMP_REENTER(); 6917 return true; 6918 } 6919 6920 // If we're a child of MathML, then we should reframe the MathML content. 6921 // If we're non-MathML, then we would be wrapped in a block so we need to 6922 // check our grandparent in that case. 6923 nsIFrame* possibleMathMLAncestor = parentType == LayoutFrameType::Block 6924 ? parentFrame->GetParent() 6925 : parentFrame; 6926 if (possibleMathMLAncestor->IsMathMLFrame()) { 6927 LAYOUT_PHASE_TEMP_EXIT(); 6928 RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async); 6929 LAYOUT_PHASE_TEMP_REENTER(); 6930 return true; 6931 } 6932 6933 #ifdef ACCESSIBILITY 6934 if (aKind != RemovalKind::ForReconstruction) { 6935 if (nsAccessibilityService* accService = GetAccService()) { 6936 accService->ContentRemoved(mPresShell, aChild); 6937 } 6938 } 6939 #endif 6940 6941 // Examine the containing-block for the removed content and see if 6942 // :first-letter style applies. 6943 nsIFrame* inflowChild = childFrame; 6944 if (childFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { 6945 inflowChild = childFrame->GetPlaceholderFrame(); 6946 NS_ASSERTION(inflowChild, "No placeholder for out-of-flow?"); 6947 } 6948 nsContainerFrame* containingBlock = 6949 GetFloatContainingBlock(inflowChild->GetParent()); 6950 bool haveFLS = containingBlock && HasFirstLetterStyle(containingBlock); 6951 if (haveFLS) { 6952 // Trap out to special routine that handles adjusting a blocks 6953 // frame tree when first-letter style is present. 6954 #ifdef NOISY_FIRST_LETTER 6955 printf("ContentWillBeRemoved: containingBlock="); 6956 containingBlock->ListTag(stdout); 6957 printf(" parentFrame="); 6958 parentFrame->ListTag(stdout); 6959 printf(" childFrame="); 6960 childFrame->ListTag(stdout); 6961 printf("\n"); 6962 #endif 6963 6964 // First update the containing blocks structure by removing the 6965 // existing letter frames. This makes the subsequent logic 6966 // simpler. 6967 RemoveLetterFrames(mPresShell, containingBlock); 6968 6969 // Recover childFrame and parentFrame 6970 childFrame = aChild->GetPrimaryFrame(); 6971 if (!childFrame || childFrame->GetContent() != aChild) { 6972 // XXXbz the GetContent() != aChild check is needed due to bug 135040. 6973 // Remove it once that's fixed. 6974 return false; 6975 } 6976 parentFrame = childFrame->GetParent(); 6977 parentType = parentFrame->Type(); 6978 6979 #ifdef NOISY_FIRST_LETTER 6980 printf(" ==> revised parentFrame="); 6981 parentFrame->ListTag(stdout); 6982 printf(" childFrame="); 6983 childFrame->ListTag(stdout); 6984 printf("\n"); 6985 #endif 6986 } 6987 6988 #ifdef DEBUG 6989 if (gReallyNoisyContentUpdates) { 6990 printf("nsCSSFrameConstructor::ContentWillBeRemoved: childFrame="); 6991 childFrame->ListTag(stdout); 6992 putchar('\n'); 6993 parentFrame->List(stdout); 6994 } 6995 #endif 6996 6997 // Notify the parent frame that it should delete the frame 6998 if (childFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { 6999 childFrame = childFrame->GetPlaceholderFrame(); 7000 NS_ASSERTION(childFrame, "Missing placeholder frame for out of flow."); 7001 parentFrame = childFrame->GetParent(); 7002 } 7003 7004 // Take care of wrapper anonymous boxes that we might need to remove while 7005 // at it. Note that MaybeRecreateContainerForFrameRemoval takes care of 7006 // harder cases (merging sibling anonymous boxes etc). 7007 while (IsWrapperPseudo(parentFrame) && 7008 CanRemoveWrapperPseudoForChildRemoval(childFrame, parentFrame)) { 7009 childFrame = parentFrame; 7010 parentFrame = childFrame->GetParent(); 7011 } 7012 7013 const bool canSkipWhitespaceFixup = [&] { 7014 if (aKind == RemovalKind::ForReconstruction) { 7015 // If we're just reconstructing frames for the element, then the 7016 // following ContentInserted notification on the element will 7017 // take care of fixing up any adjacent white-space text nodes. 7018 return true; 7019 } 7020 switch (parentFrame->Type()) { 7021 case LayoutFrameType::Table: 7022 case LayoutFrameType::TableRow: 7023 case LayoutFrameType::TableRowGroup: 7024 case LayoutFrameType::TableCol: 7025 case LayoutFrameType::TableColGroup: 7026 case LayoutFrameType::TableWrapper: { 7027 // Tables ignore all whitespace within their wrappers, so we can avoid 7028 // reconstructing adjacent whitespace if the table is not anonymous. 7029 if (!IsInAnonymousTable(parentFrame)) { 7030 return true; 7031 } 7032 // If the table is anonymous and the child is at the edges, we might 7033 // need to create whitespace at the edges of the table that wasn't 7034 // there before, so we can only skip the check if childFrame has frames 7035 // around. Captions are extra-special here, because they're siblings 7036 // with the main table frame, so we can't count that as a relevant 7037 // sibling for our purposes. 7038 auto* prevSibling = childFrame->GetPrevSibling(); 7039 return prevSibling && !prevSibling->IsTableFrame() && 7040 childFrame->GetNextSibling(); 7041 } 7042 case LayoutFrameType::GridContainer: 7043 case LayoutFrameType::FlexContainer: 7044 // Flex and grid containers similarly skip whitespace and wrap 7045 // non-whitespace in anonymous flex items, so any change to 7046 // white-space that could matter would have triggered the 7047 // reconstruction of the container itself due to merging anonymous 7048 // grid / flex items. 7049 return true; 7050 default: 7051 break; 7052 } 7053 return false; 7054 }(); 7055 DestroyContext context(mPresShell); 7056 RemoveFrame(context, nsLayoutUtils::GetChildListNameFor(childFrame), 7057 childFrame); 7058 7059 // NOTE(emilio): aChild could be dead here already if it is a ::before or 7060 // ::after pseudo-element (since in that case it was owned by childFrame, 7061 // which we just destroyed). 7062 7063 if (isRoot) { 7064 mRootElementFrame = nullptr; 7065 mRootElementStyleFrame = nullptr; 7066 mDocElementContainingBlock = nullptr; 7067 mCanvasFrame = nullptr; 7068 mPageSequenceFrame = nullptr; 7069 } 7070 7071 if (haveFLS && mRootElementFrame) { 7072 RecoverLetterFrames(containingBlock); 7073 } 7074 7075 if (!canSkipWhitespaceFixup) { 7076 MOZ_ASSERT(aChild->GetParentNode(), 7077 "How did we have a sibling without a parent?"); 7078 // Adjacent whitespace-only text nodes might have been suppressed if 7079 // this node does not have inline ends. Create frames for them now 7080 // if necessary. 7081 // Reframe any text node just before the node being removed, if there is 7082 // one, and if it's not the last child or the first child. If a whitespace 7083 // textframe was being suppressed and it's now the last child or first 7084 // child then it can stay suppressed since the parent must be a block 7085 // and hence it's adjacent to a block end. 7086 // If aOldNextSibling is null, then the text node before the node being 7087 // removed is the last node, and we don't need to worry about it. 7088 nsIContent* prevSibling = aChild->GetPreviousSibling(); 7089 if (prevSibling && prevSibling->GetPreviousSibling()) { 7090 LAYOUT_PHASE_TEMP_EXIT(); 7091 ReframeTextIfNeeded(prevSibling); 7092 LAYOUT_PHASE_TEMP_REENTER(); 7093 } 7094 // Reframe any text node just after the node being removed, if there is 7095 // one, and if it's not the last child or the first child. 7096 nsIContent* nextSibling = aChild->GetNextSibling(); 7097 if (nextSibling && prevSibling && nextSibling->GetNextSibling()) { 7098 LAYOUT_PHASE_TEMP_EXIT(); 7099 ReframeTextIfNeeded(nextSibling); 7100 LAYOUT_PHASE_TEMP_REENTER(); 7101 } 7102 } 7103 7104 #ifdef DEBUG 7105 if (gReallyNoisyContentUpdates && parentFrame) { 7106 printf( 7107 "nsCSSFrameConstructor::ContentWillBeRemoved: resulting frame " 7108 "model:\n"); 7109 parentFrame->List(stdout); 7110 } 7111 #endif 7112 7113 return false; 7114 } 7115 7116 /** 7117 * This method invalidates the canvas when frames are removed or added for a 7118 * node that might have its background propagated to the canvas, i.e., a 7119 * document root node or an HTML BODY which is a child of the root node. 7120 * 7121 * @param aFrame a frame for a content node about to be removed or a frame that 7122 * was just created for a content node that was inserted. 7123 */ 7124 static void InvalidateCanvasIfNeeded(PresShell* aPresShell, nsIContent* aNode) { 7125 MOZ_ASSERT(aPresShell->GetRootFrame(), "What happened here?"); 7126 MOZ_ASSERT(aPresShell->GetPresContext(), "Say what?"); 7127 7128 // Note that both in ContentWillBeRemoved and ContentInserted the content 7129 // node will still have the right parent pointer, so looking at that is ok. 7130 7131 nsIContent* parent = aNode->GetParent(); 7132 if (parent) { 7133 // Has a parent; might not be what we want 7134 nsIContent* grandParent = parent->GetParent(); 7135 if (grandParent) { 7136 // Has a grandparent, so not what we want 7137 return; 7138 } 7139 7140 // Check whether it's an HTML body 7141 if (!aNode->IsHTMLElement(nsGkAtoms::body)) { 7142 return; 7143 } 7144 } 7145 7146 // At this point the node has no parent or it's an HTML <body> child of the 7147 // root. We might not need to invalidate in this case (eg we might be in 7148 // XHTML or something), but chances are we want to. Play it safe. 7149 // Invalidate the viewport. 7150 7151 nsIFrame* rootFrame = aPresShell->GetRootFrame(); 7152 rootFrame->InvalidateFrameSubtree(); 7153 } 7154 7155 bool nsCSSFrameConstructor::EnsureFrameForTextNodeIsCreatedAfterFlush( 7156 CharacterData* aContent) { 7157 if (!aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) { 7158 return false; 7159 } 7160 7161 if (mAlwaysCreateFramesForIgnorableWhitespace) { 7162 return false; 7163 } 7164 7165 // Text frame may have been suppressed. Disable suppression and signal that a 7166 // flush should be performed. We do this on a document-wide basis so that 7167 // pages that repeatedly query metrics for collapsed-whitespace text nodes 7168 // don't trigger pathological behavior. 7169 mAlwaysCreateFramesForIgnorableWhitespace = true; 7170 Element* root = mDocument->GetRootElement(); 7171 if (!root) { 7172 return false; 7173 } 7174 7175 RestyleManager()->PostRestyleEvent(root, RestyleHint{0}, 7176 nsChangeHint_ReconstructFrame); 7177 return true; 7178 } 7179 7180 void nsCSSFrameConstructor::CharacterDataChanged( 7181 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) { 7182 AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::CharacterDataChanged", 7183 LAYOUT_FrameConstruction); 7184 AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC); 7185 7186 if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) && 7187 !aContent->TextIsOnlyWhitespace()) || 7188 (aContent->HasFlag(NS_REFRAME_IF_WHITESPACE) && 7189 aContent->TextIsOnlyWhitespace())) { 7190 #ifdef DEBUG 7191 nsIFrame* frame = aContent->GetPrimaryFrame(); 7192 NS_ASSERTION(!frame || !frame->IsGeneratedContentFrame(), 7193 "Bit should never be set on generated content"); 7194 #endif 7195 LAYOUT_PHASE_TEMP_EXIT(); 7196 RecreateFramesForContent(aContent, InsertionKind::Async); 7197 LAYOUT_PHASE_TEMP_REENTER(); 7198 return; 7199 } 7200 7201 // It's possible the frame whose content changed isn't inserted into the 7202 // frame hierarchy yet, or that there is no frame that maps the content 7203 if (nsIFrame* frame = aContent->GetPrimaryFrame()) { 7204 #if 0 7205 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, 7206 ("nsCSSFrameConstructor::CharacterDataChanged: content=%p[%s] subcontent=%p frame=%p", 7207 aContent, ContentTag(aContent, 0), 7208 aSubContent, frame)); 7209 #endif 7210 7211 if (frame->HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI)) { 7212 LAYOUT_PHASE_TEMP_EXIT(); 7213 RecreateFramesForContent(aContent, InsertionKind::Async); 7214 LAYOUT_PHASE_TEMP_REENTER(); 7215 return; 7216 } 7217 7218 // Special check for text content that is a child of a letter frame. If 7219 // this happens, we should remove the letter frame, do whatever we're 7220 // planning to do with this notification, then put the letter frame back. 7221 // Note that this is basically what RecreateFramesForContent ends up doing; 7222 // the reason we dont' want to call that here is that our text content 7223 // could be native anonymous, in which case RecreateFramesForContent would 7224 // completely barf on it. And recreating the non-anonymous ancestor would 7225 // just lead us to come back into this notification (e.g. if quotes or 7226 // counters are involved), leading to a loop. 7227 nsContainerFrame* block = GetFloatContainingBlock(frame); 7228 bool haveFirstLetterStyle = false; 7229 if (block) { 7230 // See if the block has first-letter style applied to it. 7231 haveFirstLetterStyle = HasFirstLetterStyle(block); 7232 if (haveFirstLetterStyle) { 7233 RemoveLetterFrames(mPresShell, block); 7234 // Reget |frame|, since we might have killed it. 7235 // Do we really need to call CharacterDataChanged in this case, though? 7236 frame = aContent->GetPrimaryFrame(); 7237 NS_ASSERTION(frame, "Should have frame here!"); 7238 } 7239 } 7240 7241 // Notify the first frame that maps the content. It will generate a reflow 7242 // command 7243 frame->CharacterDataChanged(aInfo); 7244 7245 if (haveFirstLetterStyle) { 7246 RecoverLetterFrames(block); 7247 } 7248 } 7249 } 7250 7251 void nsCSSFrameConstructor::RecalcQuotesAndCounters() { 7252 nsAutoScriptBlocker scriptBlocker; 7253 7254 if (mQuotesDirty) { 7255 mQuotesDirty = false; 7256 mContainStyleScopeManager.RecalcAllQuotes(); 7257 } 7258 7259 if (mCountersDirty) { 7260 mCountersDirty = false; 7261 mContainStyleScopeManager.RecalcAllCounters(); 7262 } 7263 7264 NS_ASSERTION(!mQuotesDirty, "Quotes updates will be lost"); 7265 NS_ASSERTION(!mCountersDirty, "Counter updates will be lost"); 7266 } 7267 7268 void nsCSSFrameConstructor::NotifyCounterStylesAreDirty() { 7269 mContainStyleScopeManager.SetAllCountersDirty(); 7270 CountersDirty(); 7271 } 7272 7273 void nsCSSFrameConstructor::WillDestroyFrameTree() { 7274 #if defined(DEBUG_dbaron_off) 7275 mContainStyleScopeManager.DumpCounters(); 7276 #endif 7277 7278 // Prevent frame tree destruction from being O(N^2) 7279 mContainStyleScopeManager.Clear(); 7280 nsFrameManager::Destroy(); 7281 } 7282 7283 // STATIC 7284 7285 // XXXbz I'd really like this method to go away. Once we have inline-block and 7286 // I can just use that for sized broken images, that can happen, maybe. 7287 // 7288 // NOTE(emilio): This needs to match MozAltContent handling. 7289 void nsCSSFrameConstructor::GetAlternateTextFor(const Element& aElement, 7290 nsAString& aAltText) { 7291 // The "alt" attribute specifies alternate text that is rendered 7292 // when the image can not be displayed. 7293 if (aElement.GetAttr(nsGkAtoms::alt, aAltText)) { 7294 return; 7295 } 7296 7297 if (aElement.IsHTMLElement(nsGkAtoms::input)) { 7298 // If there's no "alt" attribute, and aElement is an input element, then use 7299 // the value of the "value" attribute. 7300 if (aElement.GetAttr(nsGkAtoms::value, aAltText)) { 7301 return; 7302 } 7303 7304 // If there's no "value" attribute either, then use the localized string for 7305 // "Submit" as the alternate text. 7306 nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES, 7307 "Submit", aElement.OwnerDoc(), 7308 aAltText); 7309 } 7310 } 7311 7312 nsIFrame* nsCSSFrameConstructor::CreateContinuingOuterTableFrame( 7313 nsIFrame* aFrame, nsContainerFrame* aParentFrame, nsIContent* aContent, 7314 ComputedStyle* aComputedStyle) { 7315 nsTableWrapperFrame* newFrame = 7316 NS_NewTableWrapperFrame(mPresShell, aComputedStyle); 7317 7318 newFrame->Init(aContent, aParentFrame, aFrame); 7319 7320 // Create a continuing inner table frame. Note we don't replicate the 7321 // captions: a comment used to hint at that, but the code dealing with that 7322 // never worked and was removed in bug 309322. 7323 nsFrameList newChildFrames; 7324 7325 MOZ_ASSERT(aFrame->IsTableWrapperFrame()); 7326 if (nsTableFrame* childFrame = 7327 static_cast<nsTableWrapperFrame*>(aFrame)->InnerTableFrame()) { 7328 nsIFrame* continuingTableFrame = 7329 CreateContinuingFrame(childFrame, newFrame); 7330 newChildFrames.AppendFrame(nullptr, continuingTableFrame); 7331 } 7332 7333 // Set the table wrapper's initial child list 7334 newFrame->SetInitialChildList(FrameChildListID::Principal, 7335 std::move(newChildFrames)); 7336 7337 return newFrame; 7338 } 7339 7340 nsIFrame* nsCSSFrameConstructor::CreateContinuingTableFrame( 7341 nsIFrame* aFrame, nsContainerFrame* aParentFrame, nsIContent* aContent, 7342 ComputedStyle* aComputedStyle) { 7343 nsTableFrame* newFrame = NS_NewTableFrame(mPresShell, aComputedStyle); 7344 7345 newFrame->Init(aContent, aParentFrame, aFrame); 7346 7347 // Replicate any header/footer frames 7348 nsFrameList childFrames; 7349 for (nsIFrame* childFrame : aFrame->PrincipalChildList()) { 7350 // See if it's a header/footer, possibly wrapped in a scroll frame. 7351 nsTableRowGroupFrame* rowGroupFrame = 7352 static_cast<nsTableRowGroupFrame*>(childFrame); 7353 // If the row group was continued, then don't replicate it. 7354 nsIFrame* rgNextInFlow = rowGroupFrame->GetNextInFlow(); 7355 if (rgNextInFlow) { 7356 rowGroupFrame->SetRepeatable(false); 7357 } else if (rowGroupFrame->IsRepeatable()) { 7358 // Replicate the header/footer frame. 7359 nsTableRowGroupFrame* headerFooterFrame; 7360 nsFrameList childList; 7361 7362 nsFrameConstructorState state( 7363 mPresShell, GetAbsoluteContainingBlock(newFrame, FIXED_POS), 7364 GetAbsoluteContainingBlock(newFrame, ABS_POS), nullptr); 7365 state.mCreatingExtraFrames = true; 7366 7367 ComputedStyle* const headerFooterComputedStyle = rowGroupFrame->Style(); 7368 headerFooterFrame = static_cast<nsTableRowGroupFrame*>( 7369 NS_NewTableRowGroupFrame(mPresShell, headerFooterComputedStyle)); 7370 7371 nsIContent* headerFooter = rowGroupFrame->GetContent(); 7372 headerFooterFrame->Init(headerFooter, newFrame, nullptr); 7373 7374 nsFrameConstructorSaveState absoluteSaveState; 7375 MakeTablePartAbsoluteContainingBlock(state, absoluteSaveState, 7376 headerFooterFrame); 7377 7378 nsFrameConstructorSaveState floatSaveState; 7379 state.MaybePushFloatContainingBlock(headerFooterFrame, floatSaveState); 7380 7381 ProcessChildren(state, headerFooter, rowGroupFrame->Style(), 7382 headerFooterFrame, true, childList, false, nullptr); 7383 NS_ASSERTION(state.mFloatedList.IsEmpty(), "unexpected floated element"); 7384 headerFooterFrame->SetInitialChildList(FrameChildListID::Principal, 7385 std::move(childList)); 7386 headerFooterFrame->SetRepeatable(true); 7387 7388 // Table specific initialization 7389 headerFooterFrame->InitRepeatedFrame(rowGroupFrame); 7390 7391 // XXX Deal with absolute and fixed frames... 7392 childFrames.AppendFrame(nullptr, headerFooterFrame); 7393 } 7394 } 7395 7396 // Set the table frame's initial child list 7397 newFrame->SetInitialChildList(FrameChildListID::Principal, 7398 std::move(childFrames)); 7399 7400 return newFrame; 7401 } 7402 7403 nsIFrame* nsCSSFrameConstructor::CreateContinuingFrame( 7404 nsIFrame* aFrame, nsContainerFrame* aParentFrame, bool aIsFluid) { 7405 ComputedStyle* computedStyle = aFrame->Style(); 7406 nsIFrame* newFrame = nullptr; 7407 nsIFrame* nextContinuation = aFrame->GetNextContinuation(); 7408 nsIFrame* nextInFlow = aFrame->GetNextInFlow(); 7409 7410 // Use the frame type to determine what type of frame to create 7411 LayoutFrameType frameType = aFrame->Type(); 7412 nsIContent* content = aFrame->GetContent(); 7413 7414 if (LayoutFrameType::Text == frameType) { 7415 newFrame = NS_NewContinuingTextFrame(mPresShell, computedStyle); 7416 newFrame->Init(content, aParentFrame, aFrame); 7417 } else if (LayoutFrameType::Inline == frameType) { 7418 newFrame = NS_NewInlineFrame(mPresShell, computedStyle); 7419 newFrame->Init(content, aParentFrame, aFrame); 7420 } else if (LayoutFrameType::Block == frameType) { 7421 MOZ_ASSERT(!aFrame->IsTableCaption(), 7422 "no support for fragmenting table captions yet"); 7423 newFrame = NS_NewBlockFrame(mPresShell, computedStyle); 7424 newFrame->Init(content, aParentFrame, aFrame); 7425 } else if (LayoutFrameType::ColumnSetWrapper == frameType) { 7426 newFrame = 7427 NS_NewColumnSetWrapperFrame(mPresShell, computedStyle, nsFrameState(0)); 7428 newFrame->Init(content, aParentFrame, aFrame); 7429 } else if (LayoutFrameType::ColumnSet == frameType) { 7430 MOZ_ASSERT(!aFrame->IsTableCaption(), 7431 "no support for fragmenting table captions yet"); 7432 newFrame = NS_NewColumnSetFrame(mPresShell, computedStyle, nsFrameState(0)); 7433 newFrame->Init(content, aParentFrame, aFrame); 7434 } else if (LayoutFrameType::PrintedSheet == frameType) { 7435 newFrame = ConstructPrintedSheetFrame(mPresShell, aParentFrame, aFrame); 7436 } else if (LayoutFrameType::Page == frameType) { 7437 nsCanvasFrame* canvasFrame; // (unused outparam for ConstructPageFrame) 7438 newFrame = 7439 ConstructPageFrame(mPresShell, aParentFrame, aFrame, canvasFrame); 7440 } else if (LayoutFrameType::TableWrapper == frameType) { 7441 newFrame = CreateContinuingOuterTableFrame(aFrame, aParentFrame, content, 7442 computedStyle); 7443 } else if (LayoutFrameType::Table == frameType) { 7444 newFrame = CreateContinuingTableFrame(aFrame, aParentFrame, content, 7445 computedStyle); 7446 } else if (LayoutFrameType::TableRowGroup == frameType) { 7447 newFrame = NS_NewTableRowGroupFrame(mPresShell, computedStyle); 7448 newFrame->Init(content, aParentFrame, aFrame); 7449 } else if (LayoutFrameType::TableRow == frameType) { 7450 nsTableRowFrame* rowFrame = NS_NewTableRowFrame(mPresShell, computedStyle); 7451 7452 rowFrame->Init(content, aParentFrame, aFrame); 7453 7454 // Create a continuing frame for each table cell frame 7455 nsFrameList newChildList; 7456 nsIFrame* cellFrame = aFrame->PrincipalChildList().FirstChild(); 7457 while (cellFrame) { 7458 // See if it's a table cell frame 7459 if (cellFrame->IsTableCellFrame()) { 7460 nsIFrame* continuingCellFrame = 7461 CreateContinuingFrame(cellFrame, rowFrame); 7462 newChildList.AppendFrame(nullptr, continuingCellFrame); 7463 } 7464 cellFrame = cellFrame->GetNextSibling(); 7465 } 7466 7467 rowFrame->SetInitialChildList(FrameChildListID::Principal, 7468 std::move(newChildList)); 7469 newFrame = rowFrame; 7470 7471 } else if (LayoutFrameType::TableCell == frameType) { 7472 // Warning: If you change this and add a wrapper frame around table cell 7473 // frames, make sure Bug 368554 doesn't regress! 7474 // See IsInAutoWidthTableCellForQuirk() in nsImageFrame.cpp. 7475 nsTableFrame* tableFrame = 7476 static_cast<nsTableRowFrame*>(aParentFrame)->GetTableFrame(); 7477 nsTableCellFrame* cellFrame = 7478 NS_NewTableCellFrame(mPresShell, computedStyle, tableFrame); 7479 7480 cellFrame->Init(content, aParentFrame, aFrame); 7481 7482 // Create a continuing area frame 7483 nsIFrame* blockFrame = aFrame->PrincipalChildList().FirstChild(); 7484 nsIFrame* continuingBlockFrame = 7485 CreateContinuingFrame(blockFrame, cellFrame); 7486 7487 SetInitialSingleChild(cellFrame, continuingBlockFrame); 7488 newFrame = cellFrame; 7489 } else if (LayoutFrameType::Line == frameType) { 7490 newFrame = NS_NewFirstLineFrame(mPresShell, computedStyle); 7491 newFrame->Init(content, aParentFrame, aFrame); 7492 } else if (LayoutFrameType::Letter == frameType) { 7493 newFrame = NS_NewFirstLetterFrame(mPresShell, computedStyle); 7494 newFrame->Init(content, aParentFrame, aFrame); 7495 } else if (LayoutFrameType::Image == frameType) { 7496 auto* imageFrame = static_cast<nsImageFrame*>(aFrame); 7497 newFrame = imageFrame->CreateContinuingFrame(mPresShell, computedStyle); 7498 newFrame->Init(content, aParentFrame, aFrame); 7499 } else if (LayoutFrameType::ImageControl == frameType) { 7500 newFrame = NS_NewImageControlFrame(mPresShell, computedStyle); 7501 newFrame->Init(content, aParentFrame, aFrame); 7502 } else if (LayoutFrameType::FieldSet == frameType) { 7503 newFrame = NS_NewFieldSetFrame(mPresShell, computedStyle); 7504 newFrame->Init(content, aParentFrame, aFrame); 7505 } else if (LayoutFrameType::FlexContainer == frameType) { 7506 newFrame = NS_NewFlexContainerFrame(mPresShell, computedStyle); 7507 newFrame->Init(content, aParentFrame, aFrame); 7508 } else if (LayoutFrameType::GridContainer == frameType) { 7509 newFrame = NS_NewGridContainerFrame(mPresShell, computedStyle); 7510 newFrame->Init(content, aParentFrame, aFrame); 7511 } else if (LayoutFrameType::Ruby == frameType) { 7512 newFrame = NS_NewRubyFrame(mPresShell, computedStyle); 7513 newFrame->Init(content, aParentFrame, aFrame); 7514 } else if (LayoutFrameType::RubyBaseContainer == frameType) { 7515 newFrame = NS_NewRubyBaseContainerFrame(mPresShell, computedStyle); 7516 newFrame->Init(content, aParentFrame, aFrame); 7517 } else if (LayoutFrameType::RubyTextContainer == frameType) { 7518 newFrame = NS_NewRubyTextContainerFrame(mPresShell, computedStyle); 7519 newFrame->Init(content, aParentFrame, aFrame); 7520 } else { 7521 MOZ_CRASH("unexpected frame type"); 7522 } 7523 7524 // Init() set newFrame to be a fluid continuation of aFrame. 7525 // If we want a non-fluid continuation, we need to call SetPrevContinuation() 7526 // to reset NS_FRAME_IS_FLUID_CONTINUATION. 7527 if (!aIsFluid) { 7528 newFrame->SetPrevContinuation(aFrame); 7529 } 7530 7531 // If a continuing frame needs to carry frame state bits from its previous 7532 // continuation or parent, set them in nsIFrame::Init(), or in any derived 7533 // frame class's Init() if the bits are belong to specific group. 7534 7535 if (nextInFlow) { 7536 nextInFlow->SetPrevInFlow(newFrame); 7537 newFrame->SetNextInFlow(nextInFlow); 7538 } else if (nextContinuation) { 7539 nextContinuation->SetPrevContinuation(newFrame); 7540 newFrame->SetNextContinuation(nextContinuation); 7541 } 7542 7543 // aFrame cannot be a dynamic reflow root because it has a continuation now. 7544 aFrame->RemoveStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT); 7545 7546 MOZ_ASSERT(!newFrame->GetNextSibling(), "unexpected sibling"); 7547 return newFrame; 7548 } 7549 7550 void nsCSSFrameConstructor::MaybeSetNextPageContentFramePageName( 7551 const nsIFrame* aFrame) { 7552 MOZ_ASSERT(aFrame, "Frame should not be null"); 7553 // No parent means the root frame, which isn't what this funciton is for. 7554 MOZ_ASSERT(aFrame->GetParent(), 7555 "Frame should be the first child placed on a new page, not the " 7556 "root frame."); 7557 if (mNextPageContentFramePageName) { 7558 return; 7559 } 7560 const nsAtom* const autoValue = aFrame->GetParent()->GetAutoPageValue(); 7561 mNextPageContentFramePageName = aFrame->ComputePageValue(autoValue); 7562 } 7563 7564 nsresult nsCSSFrameConstructor::ReplicateFixedFrames( 7565 nsPageContentFrame* aParentFrame) { 7566 // Now deal with fixed-pos things.... They should appear on all pages, 7567 // so we want to move over the placeholders when processing the child 7568 // of the pageContentFrame. 7569 7570 nsIFrame* prevPageContentFrame = aParentFrame->GetPrevInFlow(); 7571 if (!prevPageContentFrame) { 7572 return NS_OK; 7573 } 7574 nsContainerFrame* canvasFrame = 7575 do_QueryFrame(aParentFrame->PrincipalChildList().FirstChild()); 7576 nsIFrame* prevCanvasFrame = 7577 prevPageContentFrame->PrincipalChildList().FirstChild(); 7578 if (!canvasFrame || !prevCanvasFrame) { 7579 // document's root element frame missing 7580 return NS_ERROR_UNEXPECTED; 7581 } 7582 7583 nsFrameList fixedPlaceholders; 7584 nsIFrame* firstFixed = 7585 prevPageContentFrame->GetChildList(FrameChildListID::Fixed).FirstChild(); 7586 if (!firstFixed) { 7587 return NS_OK; 7588 } 7589 7590 // Don't allow abs-pos descendants of the fixed content to escape the content. 7591 // This should not normally be possible (because fixed-pos elements should 7592 // be absolute containers) but fixed-pos tables currently aren't abs-pos 7593 // containers. 7594 nsFrameConstructorState state(mPresShell, aParentFrame, nullptr, 7595 mRootElementFrame); 7596 state.mCreatingExtraFrames = true; 7597 7598 // We can't use an ancestor filter here, because we're not going to 7599 // be usefully recurring down the tree. This means that other 7600 // places in frame construction can't assume a filter is 7601 // initialized! 7602 7603 // Iterate across fixed frames and replicate each whose placeholder is a 7604 // descendant of aFrame. (We don't want to explicitly copy placeholders that 7605 // are within fixed frames, because that would cause duplicates on the new 7606 // page - bug 389619) 7607 for (nsIFrame* fixed = firstFixed; fixed; fixed = fixed->GetNextSibling()) { 7608 nsIFrame* prevPlaceholder = fixed->GetPlaceholderFrame(); 7609 if (prevPlaceholder && nsLayoutUtils::IsProperAncestorFrame( 7610 prevCanvasFrame, prevPlaceholder)) { 7611 // We want to use the same style as the primary style frame for 7612 // our content 7613 nsIContent* content = fixed->GetContent(); 7614 ComputedStyle* computedStyle = 7615 nsLayoutUtils::GetStyleFrame(content)->Style(); 7616 AutoFrameConstructionItemList items(this); 7617 AddFrameConstructionItemsInternal(state, content, canvasFrame, true, 7618 computedStyle, 7619 {ItemFlag::AllowPageBreak}, items); 7620 ConstructFramesFromItemList(state, items, canvasFrame, 7621 /* aParentIsWrapperAnonBox = */ false, 7622 fixedPlaceholders); 7623 } 7624 } 7625 7626 // Add the placeholders to our primary child list. 7627 // XXXbz this is a little screwed up, since the fixed frames will have 7628 // broken auto-positioning. Oh, well. 7629 NS_ASSERTION(!canvasFrame->PrincipalChildList().FirstChild(), 7630 "leaking frames; doc root continuation must be empty"); 7631 canvasFrame->SetInitialChildList(FrameChildListID::Principal, 7632 std::move(fixedPlaceholders)); 7633 return NS_OK; 7634 } 7635 7636 nsCSSFrameConstructor::InsertionPoint nsCSSFrameConstructor::GetInsertionPoint( 7637 nsIContent* aChild) { 7638 MOZ_ASSERT(aChild); 7639 nsIContent* insertionElement = aChild->GetFlattenedTreeParent(); 7640 if (!insertionElement) { 7641 // The element doesn't belong in the flattened tree, and thus we don't want 7642 // to render it. 7643 return {}; 7644 } 7645 7646 return {GetContentInsertionFrameFor(insertionElement), insertionElement}; 7647 } 7648 7649 // Capture state for the frame tree rooted at the frame associated with the 7650 // content object, aContent 7651 void nsCSSFrameConstructor::CaptureStateForFramesOf( 7652 nsIContent* aContent, nsILayoutHistoryState* aHistoryState) { 7653 if (!aHistoryState) { 7654 return; 7655 } 7656 nsIFrame* frame = aContent->GetPrimaryFrame(); 7657 if (frame == mRootElementFrame) { 7658 frame = mRootElementFrame 7659 ? GetAbsoluteContainingBlock(mRootElementFrame, FIXED_POS) 7660 : GetRootFrame(); 7661 } 7662 for (; frame; 7663 frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) { 7664 CaptureFrameState(frame, aHistoryState); 7665 } 7666 } 7667 7668 static bool IsWhitespaceFrame(nsIFrame* aFrame) { 7669 MOZ_ASSERT(aFrame, "invalid argument"); 7670 return aFrame->IsTextFrame() && aFrame->GetContent()->TextIsOnlyWhitespace(); 7671 } 7672 7673 static nsIFrame* FindNextNonWhitespaceSibling(nsIFrame* aFrame) { 7674 nsIFrame* f = aFrame; 7675 do { 7676 f = f->GetNextSibling(); 7677 } while (f && IsWhitespaceFrame(f)); 7678 return f; 7679 } 7680 7681 static nsIFrame* FindPreviousNonWhitespaceSibling(nsIFrame* aFrame) { 7682 nsIFrame* f = aFrame; 7683 do { 7684 f = f->GetPrevSibling(); 7685 } while (f && IsWhitespaceFrame(f)); 7686 return f; 7687 } 7688 7689 bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval( 7690 nsIFrame* aFrame) { 7691 #define TRACE(reason) \ 7692 PROFILER_MARKER("MaybeRecreateContainerForFrameRemoval: " reason, LAYOUT, \ 7693 {}, Tracing, "Layout") 7694 MOZ_ASSERT(aFrame, "Must have a frame"); 7695 MOZ_ASSERT(aFrame->GetParent(), "Frame shouldn't be root"); 7696 MOZ_ASSERT(aFrame == aFrame->FirstContinuation(), 7697 "aFrame not the result of GetPrimaryFrame()?"); 7698 7699 nsIFrame* inFlowFrame = aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) 7700 ? aFrame->GetPlaceholderFrame() 7701 : aFrame; 7702 MOZ_ASSERT(inFlowFrame, "How did that happen?"); 7703 MOZ_ASSERT(inFlowFrame == inFlowFrame->FirstContinuation(), 7704 "placeholder for primary frame has previous continuations?"); 7705 nsIFrame* parent = inFlowFrame->GetParent(); 7706 7707 if (inFlowFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) { 7708 nsIFrame* grandparent = parent->GetParent(); 7709 MOZ_ASSERT(grandparent); 7710 7711 bool needsReframe = 7712 // 1. Removing a column-span may lead to an empty 7713 // ::-moz-column-span-wrapper. 7714 inFlowFrame->IsColumnSpan() || 7715 // 2. Removing a frame which has any column-span siblings may also 7716 // lead to an empty ::-moz-column-span-wrapper subtree. The 7717 // column-span siblings were the frame's children, but later become 7718 // the frame's siblings after CreateColumnSpanSiblings(). 7719 inFlowFrame->HasColumnSpanSiblings() || 7720 // 3. Removing the only child of a ::-moz-column-content, whose 7721 // ColumnSet grandparent has a previous column-span sibling, requires 7722 // reframing since we might connect the ColumnSet's next column-span 7723 // sibling (if there's one). Note that this isn't actually needed if 7724 // the ColumnSet is at the end of ColumnSetWrapper since we create 7725 // empty ones at the end anyway, but we're not worried about 7726 // optimizing that case. 7727 (parent->Style()->GetPseudoType() == PseudoStyleType::columnContent && 7728 // The only child in ::-moz-column-content (might be tall enough to 7729 // split across columns) 7730 !inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() && 7731 // That ::-moz-column-content is the first column. 7732 !parent->GetPrevInFlow() && 7733 // The ColumnSet grandparent has a previous sibling that is a 7734 // column-span. 7735 grandparent->GetPrevSibling()); 7736 7737 if (needsReframe) { 7738 nsContainerFrame* containingBlock = 7739 GetMultiColumnContainingBlockFor(inFlowFrame); 7740 7741 #ifdef DEBUG 7742 if (IsFramePartOfIBSplit(inFlowFrame)) { 7743 nsIFrame* ibContainingBlock = GetIBContainingBlockFor(inFlowFrame); 7744 MOZ_ASSERT(containingBlock == ibContainingBlock || 7745 nsLayoutUtils::IsProperAncestorFrame(containingBlock, 7746 ibContainingBlock), 7747 "Multi-column containing block should be equal to or be the " 7748 "ancestor of the IB containing block!"); 7749 } 7750 #endif 7751 7752 TRACE("Multi-column"); 7753 RecreateFramesForContent(containingBlock->GetContent(), 7754 InsertionKind::Async); 7755 return true; 7756 } 7757 } 7758 7759 if (IsFramePartOfIBSplit(aFrame)) { 7760 // The removal functions can't handle removal of an {ib} split directly; we 7761 // need to rebuild the containing block. 7762 TRACE("IB split removal"); 7763 ReframeContainingBlock(aFrame); 7764 return true; 7765 } 7766 7767 if (inFlowFrame->IsRenderedLegend()) { 7768 TRACE("Fieldset / Legend"); 7769 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async); 7770 return true; 7771 } 7772 7773 // Might need to reconstruct things if this frame's nextSibling is a table 7774 // or ruby pseudo, since removal of this frame might mean that this pseudo 7775 // needs to get merged with the frame's prevSibling if that's also a table 7776 // or ruby pseudo. 7777 nsIFrame* nextSibling = 7778 FindNextNonWhitespaceSibling(inFlowFrame->LastContinuation()); 7779 NS_ASSERTION(!IsWrapperPseudo(inFlowFrame), 7780 "Shouldn't happen here (we start removals from primary frames)"); 7781 // Effectively, for the ruby pseudo sibling case, only pseudo <ruby> frame 7782 // need to be checked here, since all other types of such frames will have 7783 // a ruby container parent, and be catched by "Check ruby containers" below. 7784 if (nextSibling && IsWrapperPseudo(nextSibling)) { 7785 nsIFrame* prevSibling = FindPreviousNonWhitespaceSibling(inFlowFrame); 7786 if (prevSibling && IsWrapperPseudo(prevSibling)) { 7787 TRACE("Pseudo sibling"); 7788 // Good enough to recreate frames for aFrame's parent's content; even if 7789 // aFrame's parent is a pseudo, that'll be the right content node. 7790 // FIXME(emilio): Consider doing a more subtle check here like, only if 7791 // prevSibling and nextSibling share frame type. Or even consider just 7792 // moving the frames around and destroying nextSibling? 7793 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async); 7794 return true; 7795 } 7796 } 7797 7798 // Check ruby containers 7799 LayoutFrameType parentType = parent->Type(); 7800 if (parentType == LayoutFrameType::Ruby || 7801 RubyUtils::IsRubyContainerBox(parentType)) { 7802 // In ruby containers, pseudo frames may be created from 7803 // whitespaces or even nothing. There are two cases we actually 7804 // need to handle here, but hard to check exactly: 7805 // 1. Status of spaces beside the frame may vary, and related 7806 // frames may be constructed or destroyed accordingly. 7807 // 2. The type of the first child of a ruby frame determines 7808 // whether a pseudo ruby base container should exist. 7809 TRACE("Ruby container"); 7810 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async); 7811 return true; 7812 } 7813 7814 // Reconstruct if inflowFrame is parent's only child, and parent is, or has, 7815 // a non-fluid continuation, i.e. it was split by bidi resolution 7816 if (!inFlowFrame->GetPrevSibling() && !inFlowFrame->GetNextSibling() && 7817 ((parent->GetPrevContinuation() && !parent->GetPrevInFlow()) || 7818 (parent->GetNextContinuation() && !parent->GetNextInFlow()))) { 7819 TRACE("Removing last child of non-fluid split parent"); 7820 RecreateFramesForContent(parent->GetContent(), InsertionKind::Async); 7821 return true; 7822 } 7823 7824 // We might still need to reconstruct things if the parent of inFlowFrame is 7825 // ib-split, since in that case the removal of aFrame might affect the 7826 // splitting of its parent. 7827 if (!IsFramePartOfIBSplit(parent)) { 7828 return false; 7829 } 7830 7831 // If inFlowFrame is not the only in-flow child of |parent|, then removing 7832 // it will change nothing about the {ib} split. 7833 if (inFlowFrame != parent->PrincipalChildList().FirstChild() || 7834 inFlowFrame->LastContinuation()->GetNextSibling()) { 7835 return false; 7836 } 7837 7838 // If the parent is the first or last part of the {ib} split, then 7839 // removing one of its kids will have no effect on the splitting. 7840 // Get the first continuation up front so we don't have to do it twice. 7841 nsIFrame* parentFirstContinuation = parent->FirstContinuation(); 7842 if (!GetIBSplitSibling(parentFirstContinuation) || 7843 !GetIBSplitPrevSibling(parentFirstContinuation)) { 7844 return false; 7845 } 7846 7847 TRACE("IB split parent"); 7848 ReframeContainingBlock(parent); 7849 return true; 7850 #undef TRACE 7851 } 7852 7853 void nsCSSFrameConstructor::UpdateTableCellSpans(nsIContent* aContent) { 7854 nsTableCellFrame* cellFrame = do_QueryFrame(aContent->GetPrimaryFrame()); 7855 7856 // It's possible that this warning could fire if some other style change 7857 // simultaneously changes the 'display' of the element and makes it no 7858 // longer be a table cell. 7859 NS_WARNING_ASSERTION(cellFrame, "Hint should only be posted on table cells!"); 7860 7861 if (cellFrame) { 7862 cellFrame->GetTableFrame()->RowOrColSpanChanged(cellFrame); 7863 } 7864 } 7865 7866 static nsIContent* GetTopmostMathMLElement(nsIContent* aMathMLContent) { 7867 MOZ_ASSERT(aMathMLContent->IsMathMLElement()); 7868 MOZ_ASSERT(aMathMLContent->GetPrimaryFrame()); 7869 MOZ_ASSERT(aMathMLContent->GetPrimaryFrame()->IsMathMLFrame()); 7870 nsIContent* root = aMathMLContent; 7871 7872 for (nsIContent* parent = aMathMLContent->GetFlattenedTreeParent(); parent; 7873 parent = parent->GetFlattenedTreeParent()) { 7874 nsIFrame* frame = parent->GetPrimaryFrame(); 7875 if (!frame || !frame->IsMathMLFrame()) { 7876 break; 7877 } 7878 root = parent; 7879 } 7880 7881 return root; 7882 } 7883 7884 // We don't know how to re-insert an anonymous subtree root, so recreate the 7885 // closest non-generated ancestor instead, except for a few special cases... 7886 static bool ShouldRecreateContainerForNativeAnonymousContentRoot( 7887 nsIContent* aContent) { 7888 if (!aContent->IsRootOfNativeAnonymousSubtree()) { 7889 return false; 7890 } 7891 if (ManualNACPtr::IsManualNAC(aContent)) { 7892 // Editor NAC, would enter an infinite loop, and we sorta get away with it 7893 // because it's all abspos. 7894 return false; 7895 } 7896 if (auto* el = Element::FromNode(aContent)) { 7897 if (el->GetPseudoElementType() == 7898 PseudoStyleType::mozSnapshotContainingBlock) { 7899 // Much like above, all abspos and on its own top layer so insertion order 7900 // wouldn't really matter anyways. 7901 return false; 7902 } 7903 if (auto* classes = el->GetClasses()) { 7904 if (classes->Contains(nsGkAtoms::mozCustomContentContainer, 7905 eCaseMatters)) { 7906 // Canvas anonymous content (like the custom content container) is also 7907 // fine, because its only sibling is a tooltip which is also abspos, so 7908 // relative insertion order doesn't really matter. 7909 // 7910 // This is important because the inspector uses it, and we don't want 7911 // inspecting the page to change behavior heavily (and reframing 7912 // unfortunately has side-effects sometimes, even though they're bugs). 7913 return false; 7914 } 7915 } 7916 } 7917 7918 return true; 7919 } 7920 7921 void nsCSSFrameConstructor::RecreateFramesForContent( 7922 nsIContent* aContent, InsertionKind aInsertionKind) { 7923 MOZ_ASSERT(aContent); 7924 7925 // If there is no document, we don't want to recreate frames for it. (You 7926 // shouldn't generally be giving this method content without a document 7927 // anyway). 7928 // Rebuilding the frame tree can have bad effects, especially if it's the 7929 // frame tree for chrome (see bug 157322). 7930 if (NS_WARN_IF(!aContent->GetComposedDoc())) { 7931 return; 7932 } 7933 7934 // TODO(emilio): We technically can find the right insertion point nowadays 7935 // using StyleChildrenIterator rather than FlattenedChildIterator. But we'd 7936 // need to tweak the setup to insert into replaced elements to filter which 7937 // anonymous roots can be allowed, and which can't. 7938 // 7939 // TODO(emilio, 2022): Is this true? If we have a replaced element we wouldn't 7940 // have generated e.g., a ::before/::after pseudo-element to begin with (which 7941 // is what this code is about, so maybe we can just remove this piece of code 7942 // altogether). 7943 if (ShouldRecreateContainerForNativeAnonymousContentRoot(aContent)) { 7944 do { 7945 aContent = aContent->GetParent(); 7946 } while (ShouldRecreateContainerForNativeAnonymousContentRoot(aContent)); 7947 return RecreateFramesForContent(aContent, InsertionKind::Async); 7948 } 7949 7950 nsIFrame* frame = aContent->GetPrimaryFrame(); 7951 if (frame && frame->IsMathMLFrame()) { 7952 // Reframe the topmost MathML element to prevent exponential blowup 7953 // (see bug 397518). 7954 aContent = GetTopmostMathMLElement(aContent); 7955 frame = aContent->GetPrimaryFrame(); 7956 } 7957 7958 if (frame) { 7959 nsIFrame* parent = frame->GetParent(); 7960 nsIContent* parentContent = parent ? parent->GetContent() : nullptr; 7961 // If the parent frame is a leaf then the subsequent insert will fail to 7962 // create a frame, so we need to recreate the parent content. This happens 7963 // with native anonymous content from the editor. 7964 if (parent && parent->IsLeaf() && parentContent && 7965 parentContent != aContent) { 7966 return RecreateFramesForContent(parentContent, InsertionKind::Async); 7967 } 7968 } 7969 7970 if (frame && MaybeRecreateContainerForFrameRemoval(frame)) { 7971 return; 7972 } 7973 7974 MOZ_ASSERT(aContent->GetParentNode()); 7975 const auto removalKind = [&] { 7976 if (aInsertionKind == InsertionKind::Sync && aContent->IsElement() && 7977 Servo_Element_IsDisplayNone(aContent->AsElement())) { 7978 // If we know we're not going to have frames after reconstructing, it's 7979 // more efficient to do some of that work (a11y notifications, fixing-up 7980 // text nodes) earlier. 7981 return RemovalKind::ForDisplayNoneChange; 7982 } 7983 return RemovalKind::ForReconstruction; 7984 }(); 7985 const bool didReconstruct = ContentWillBeRemoved(aContent, removalKind); 7986 if (didReconstruct || removalKind == RemovalKind::ForDisplayNoneChange) { 7987 // If ContentWillBeRemoved triggered reconstruction, then we don't need to 7988 // do anything else because the frames will already have been built. 7989 return; 7990 } 7991 if (aInsertionKind == InsertionKind::Async && aContent->IsElement()) { 7992 // FIXME(emilio, bug 1397239): There's nothing removing the frame state 7993 // for elements that go away before we come back to the frame constructor. 7994 // 7995 // Also, it'd be nice to just use the `ContentRangeInserted` path for 7996 // both elements and non-elements, but we need to make lazy frame 7997 // construction to apply to all elements first. 7998 // 7999 // TODO(emilio): I think lazy frame construction works everywhere now, so 8000 // maybe we can remove this altogether? 8001 RestyleManager()->PostRestyleEvent(aContent->AsElement(), RestyleHint{0}, 8002 nsChangeHint_ReconstructFrame); 8003 return; 8004 } 8005 // Now, recreate the frames associated with this content object. 8006 ContentRangeInserted(aContent, aContent->GetNextSibling(), aInsertionKind); 8007 } 8008 8009 bool nsCSSFrameConstructor::DestroyFramesFor(nsIContent* aContent) { 8010 MOZ_ASSERT(aContent && aContent->GetParentNode()); 8011 return ContentWillBeRemoved(aContent, RemovalKind::ForReconstruction); 8012 } 8013 8014 ////////////////////////////////////////////////////////////////////// 8015 8016 // Block frame construction code 8017 8018 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::GetFirstLetterStyle( 8019 nsIContent* aContent, ComputedStyle* aComputedStyle) { 8020 if (aContent) { 8021 return mPresShell->StyleSet()->ResolvePseudoElementStyle( 8022 *aContent->AsElement(), PseudoStyleType::firstLetter, nullptr, 8023 aComputedStyle); 8024 } 8025 return nullptr; 8026 } 8027 8028 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::GetFirstLineStyle( 8029 nsIContent* aContent, ComputedStyle* aComputedStyle) { 8030 if (aContent) { 8031 return mPresShell->StyleSet()->ResolvePseudoElementStyle( 8032 *aContent->AsElement(), PseudoStyleType::firstLine, nullptr, 8033 aComputedStyle); 8034 } 8035 return nullptr; 8036 } 8037 8038 // Predicate to see if a given content (block element) has 8039 // first-letter style applied to it. 8040 bool nsCSSFrameConstructor::ShouldHaveFirstLetterStyle( 8041 nsIContent* aContent, ComputedStyle* aComputedStyle) { 8042 return nsLayoutUtils::HasPseudoStyle(aContent, aComputedStyle, 8043 PseudoStyleType::firstLetter, 8044 mPresShell->GetPresContext()); 8045 } 8046 8047 bool nsCSSFrameConstructor::HasFirstLetterStyle(nsIFrame* aBlockFrame) { 8048 MOZ_ASSERT(aBlockFrame, "Need a frame"); 8049 NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?"); 8050 8051 return aBlockFrame->HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE); 8052 } 8053 8054 bool nsCSSFrameConstructor::ShouldHaveFirstLineStyle( 8055 nsIContent* aContent, ComputedStyle* aComputedStyle) { 8056 bool hasFirstLine = nsLayoutUtils::HasPseudoStyle( 8057 aContent, aComputedStyle, PseudoStyleType::firstLine, 8058 mPresShell->GetPresContext()); 8059 return hasFirstLine && !aContent->IsHTMLElement(nsGkAtoms::fieldset); 8060 } 8061 8062 void nsCSSFrameConstructor::ShouldHaveSpecialBlockStyle( 8063 nsIContent* aContent, ComputedStyle* aComputedStyle, 8064 bool* aHaveFirstLetterStyle, bool* aHaveFirstLineStyle) { 8065 *aHaveFirstLetterStyle = ShouldHaveFirstLetterStyle(aContent, aComputedStyle); 8066 *aHaveFirstLineStyle = ShouldHaveFirstLineStyle(aContent, aComputedStyle); 8067 } 8068 8069 /* static */ 8070 const nsCSSFrameConstructor::PseudoParentData 8071 nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = { 8072 // Cell 8073 {{&nsCSSFrameConstructor::ConstructTableCell, 8074 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS | 8075 FCDATA_IS_WRAPPER_ANON_BOX | 8076 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow)}, 8077 PseudoStyleType::tableCell}, 8078 // Row 8079 {{&nsCSSFrameConstructor::ConstructTableRowOrRowGroup, 8080 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS | 8081 FCDATA_IS_WRAPPER_ANON_BOX | 8082 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup)}, 8083 PseudoStyleType::tableRow}, 8084 // Row group 8085 {{&nsCSSFrameConstructor::ConstructTableRowOrRowGroup, 8086 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS | 8087 FCDATA_IS_WRAPPER_ANON_BOX | 8088 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)}, 8089 PseudoStyleType::tableRowGroup}, 8090 // Column group 8091 {{ToCreationFunc(NS_NewTableColGroupFrame), 8092 FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET | 8093 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS | 8094 FCDATA_SKIP_ABSPOS_PUSH | 8095 // Not FCDATA_IS_WRAPPER_ANON_BOX, because we don't need to 8096 // restyle these: they have non-inheriting styles. 8097 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable)}, 8098 PseudoStyleType::tableColGroup}, 8099 // Table 8100 {{&nsCSSFrameConstructor::ConstructTable, 8101 FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS | 8102 FCDATA_IS_WRAPPER_ANON_BOX}, 8103 PseudoStyleType::table}, 8104 // Ruby 8105 {{ToCreationFunc(NS_NewRubyFrame), 8106 FCDATA_IS_LINE_PARTICIPANT | FCDATA_USE_CHILD_ITEMS | 8107 FCDATA_IS_WRAPPER_ANON_BOX | FCDATA_SKIP_FRAMESET}, 8108 PseudoStyleType::ruby}, 8109 // Ruby Base 8110 {{ToCreationFunc(NS_NewRubyBaseFrame), 8111 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT | 8112 FCDATA_IS_WRAPPER_ANON_BOX | 8113 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer) | 8114 FCDATA_SKIP_FRAMESET}, 8115 PseudoStyleType::rubyBase}, 8116 // Ruby Base Container 8117 {{ToCreationFunc(NS_NewRubyBaseContainerFrame), 8118 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT | 8119 FCDATA_IS_WRAPPER_ANON_BOX | 8120 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) | 8121 FCDATA_SKIP_FRAMESET}, 8122 PseudoStyleType::rubyBaseContainer}, 8123 // Ruby Text 8124 {{ToCreationFunc(NS_NewRubyTextFrame), 8125 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_LINE_PARTICIPANT | 8126 FCDATA_IS_WRAPPER_ANON_BOX | 8127 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer) | 8128 FCDATA_SKIP_FRAMESET}, 8129 PseudoStyleType::rubyText}, 8130 // Ruby Text Container 8131 {{ToCreationFunc(NS_NewRubyTextContainerFrame), 8132 FCDATA_USE_CHILD_ITEMS | FCDATA_IS_WRAPPER_ANON_BOX | 8133 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) | 8134 FCDATA_SKIP_FRAMESET}, 8135 PseudoStyleType::rubyTextContainer}}; 8136 8137 void nsCSSFrameConstructor::CreateNeededAnonFlexOrGridItems( 8138 nsFrameConstructorState& aState, FrameConstructionItemList& aItems, 8139 nsIFrame* aParentFrame) { 8140 if (aItems.IsEmpty()) { 8141 return; 8142 } 8143 8144 if (!aParentFrame->IsFlexOrGridContainer()) { 8145 return; 8146 } 8147 8148 const bool isLegacyWebKitBox = 8149 IsFlexContainerForLegacyWebKitBox(aParentFrame); 8150 FCItemIterator iter(aItems); 8151 do { 8152 // Advance iter past children that don't want to be wrapped 8153 if (iter.SkipItemsThatDontNeedAnonFlexOrGridItem(aState, 8154 isLegacyWebKitBox)) { 8155 // Hit the end of the items without finding any remaining children that 8156 // need to be wrapped. We're finished! 8157 return; 8158 } 8159 8160 // If our next potentially-wrappable child is whitespace, then see if 8161 // there's anything wrappable immediately after it. If not, we just drop 8162 // the whitespace and move on. (We're not supposed to create any anonymous 8163 // flex/grid items that _only_ contain whitespace). 8164 // (BUT if this is generated content, then we don't give whitespace nodes 8165 // any special treatment, because they're probably not really whitespace -- 8166 // they're just temporarily empty, waiting for their generated text.) 8167 // XXXdholbert If this node's generated text will *actually end up being 8168 // entirely whitespace*, then we technically should still skip over it, per 8169 // the CSS grid & flexbox specs. I'm not bothering with that at this point, 8170 // since it's a pretty extreme edge case. 8171 if (!aParentFrame->IsGeneratedContentFrame() && 8172 iter.item().IsWhitespace(aState)) { 8173 FCItemIterator afterWhitespaceIter(iter); 8174 bool hitEnd = afterWhitespaceIter.SkipWhitespace(aState); 8175 bool nextChildNeedsAnonItem = 8176 !hitEnd && afterWhitespaceIter.item().NeedsAnonFlexOrGridItem( 8177 aState, isLegacyWebKitBox); 8178 8179 if (!nextChildNeedsAnonItem) { 8180 // There's nothing after the whitespace that we need to wrap, so we 8181 // just drop this run of whitespace. 8182 iter.DeleteItemsTo(this, afterWhitespaceIter); 8183 if (hitEnd) { 8184 // Nothing left to do -- we're finished! 8185 return; 8186 } 8187 // else, we have a next child and it does not want to be wrapped. So, 8188 // we jump back to the beginning of the loop to skip over that child 8189 // (and anything else non-wrappable after it) 8190 MOZ_ASSERT(!iter.IsDone() && !iter.item().NeedsAnonFlexOrGridItem( 8191 aState, isLegacyWebKitBox), 8192 "hitEnd and/or nextChildNeedsAnonItem lied"); 8193 continue; 8194 } 8195 } 8196 8197 // Now |iter| points to the first child that needs to be wrapped in an 8198 // anonymous flex/grid item. Now we see how many children after it also want 8199 // to be wrapped in an anonymous flex/grid item. 8200 FCItemIterator endIter(iter); // iterator to find the end of the group 8201 endIter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyWebKitBox); 8202 8203 NS_ASSERTION(iter != endIter, 8204 "Should've had at least one wrappable child to seek past"); 8205 8206 // Now, we create the anonymous flex or grid item to contain the children 8207 // between |iter| and |endIter|. 8208 nsIContent* parentContent = aParentFrame->GetContent(); 8209 RefPtr<ComputedStyle> wrapperStyle = 8210 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 8211 PseudoStyleType::anonymousItem, aParentFrame->Style()); 8212 8213 static constexpr FrameConstructionData sBlockFCData( 8214 ToCreationFunc(NS_NewBlockFrame), FCDATA_SKIP_FRAMESET | 8215 FCDATA_USE_CHILD_ITEMS | 8216 FCDATA_IS_WRAPPER_ANON_BOX); 8217 8218 // Use the content of our parent frame 8219 auto* newItem = new (this) FrameConstructionItem( 8220 &sBlockFCData, parentContent, wrapperStyle.forget(), true); 8221 8222 newItem->mIsAllInline = 8223 newItem->mComputedStyle->StyleDisplay()->IsInlineOutsideStyle(); 8224 newItem->mIsBlock = !newItem->mIsAllInline; 8225 8226 MOZ_ASSERT(!newItem->mIsAllInline && newItem->mIsBlock, 8227 "expecting anonymous flex/grid items to be block-level " 8228 "(this will make a difference when we encounter " 8229 "'align-items: baseline')"); 8230 8231 // Anonymous flex and grid items induce line boundaries around their 8232 // contents. 8233 newItem->mChildItems.SetLineBoundaryAtStart(true); 8234 newItem->mChildItems.SetLineBoundaryAtEnd(true); 8235 // The parent of the items in aItems is also the parent of the items 8236 // in mChildItems 8237 newItem->mChildItems.SetParentHasNoShadowDOM(aItems.ParentHasNoShadowDOM()); 8238 8239 // Eat up all items between |iter| and |endIter| and put them in our 8240 // wrapper. This advances |iter| to point to |endIter|. 8241 iter.AppendItemsToList(this, endIter, newItem->mChildItems); 8242 8243 iter.InsertItem(newItem); 8244 } while (!iter.IsDone()); 8245 } 8246 8247 /* static */ nsCSSFrameConstructor::RubyWhitespaceType 8248 nsCSSFrameConstructor::ComputeRubyWhitespaceType(StyleDisplay aPrevDisplay, 8249 StyleDisplay aNextDisplay) { 8250 MOZ_ASSERT(aPrevDisplay.IsRuby() && aNextDisplay.IsRuby()); 8251 if (aPrevDisplay == aNextDisplay && 8252 (aPrevDisplay == StyleDisplay::RubyBase || 8253 aPrevDisplay == StyleDisplay::RubyText)) { 8254 return eRubyInterLeafWhitespace; 8255 } 8256 if (aNextDisplay == StyleDisplay::RubyText || 8257 aNextDisplay == StyleDisplay::RubyTextContainer) { 8258 return eRubyInterLevelWhitespace; 8259 } 8260 return eRubyInterSegmentWhitespace; 8261 } 8262 8263 /** 8264 * This function checks the content from |aStartIter| to |aEndIter|, 8265 * determines whether it contains only whitespace, and if yes, 8266 * interprets the type of whitespace. This method does not change 8267 * any of the iters. 8268 */ 8269 /* static */ nsCSSFrameConstructor::RubyWhitespaceType 8270 nsCSSFrameConstructor::InterpretRubyWhitespace(nsFrameConstructorState& aState, 8271 const FCItemIterator& aStartIter, 8272 const FCItemIterator& aEndIter) { 8273 if (!aStartIter.item().IsWhitespace(aState)) { 8274 return eRubyNotWhitespace; 8275 } 8276 8277 FCItemIterator spaceEndIter(aStartIter); 8278 spaceEndIter.SkipWhitespace(aState); 8279 if (spaceEndIter != aEndIter) { 8280 return eRubyNotWhitespace; 8281 } 8282 8283 // Any leading or trailing whitespace in non-pseudo ruby box 8284 // should have been trimmed, hence there should not be any 8285 // whitespace at the start or the end. 8286 MOZ_ASSERT(!aStartIter.AtStart() && !aEndIter.IsDone()); 8287 FCItemIterator prevIter(aStartIter); 8288 prevIter.Prev(); 8289 return ComputeRubyWhitespaceType( 8290 prevIter.item().mComputedStyle->StyleDisplay()->mDisplay, 8291 aEndIter.item().mComputedStyle->StyleDisplay()->mDisplay); 8292 } 8293 8294 /** 8295 * This function eats up consecutive items which do not want the current 8296 * parent into either a ruby base box or a ruby text box. When it 8297 * returns, |aIter| points to the first item it doesn't wrap. 8298 */ 8299 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLeafBox( 8300 FCItemIterator& aIter, ComputedStyle* aParentStyle, 8301 nsIContent* aParentContent) { 8302 StyleDisplay parentDisplay = aParentStyle->StyleDisplay()->mDisplay; 8303 ParentType parentType, wrapperType; 8304 if (parentDisplay == StyleDisplay::RubyTextContainer) { 8305 parentType = eTypeRubyTextContainer; 8306 wrapperType = eTypeRubyText; 8307 } else { 8308 MOZ_ASSERT(parentDisplay == StyleDisplay::RubyBaseContainer); 8309 parentType = eTypeRubyBaseContainer; 8310 wrapperType = eTypeRubyBase; 8311 } 8312 8313 MOZ_ASSERT(aIter.item().DesiredParentType() != parentType, 8314 "Should point to something needs to be wrapped."); 8315 8316 FCItemIterator endIter(aIter); 8317 endIter.SkipItemsNotWantingParentType(parentType); 8318 8319 WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter, 8320 endIter); 8321 } 8322 8323 /** 8324 * This function eats up consecutive items into a ruby level container. 8325 * It may create zero or one level container. When it returns, |aIter| 8326 * points to the first item it doesn't wrap. 8327 */ 8328 void nsCSSFrameConstructor::WrapItemsInPseudoRubyLevelContainer( 8329 nsFrameConstructorState& aState, FCItemIterator& aIter, 8330 ComputedStyle* aParentStyle, nsIContent* aParentContent) { 8331 MOZ_ASSERT(aIter.item().DesiredParentType() != eTypeRuby, 8332 "Pointing to a level container?"); 8333 8334 FrameConstructionItem& firstItem = aIter.item(); 8335 ParentType wrapperType = firstItem.DesiredParentType(); 8336 if (wrapperType != eTypeRubyTextContainer) { 8337 // If the first item is not ruby text, 8338 // it should be in a base container. 8339 wrapperType = eTypeRubyBaseContainer; 8340 } 8341 8342 FCItemIterator endIter(aIter); 8343 do { 8344 if (endIter.SkipItemsWantingParentType(wrapperType) || 8345 // If the skipping above stops at some item which wants a 8346 // different ruby parent, then we have finished. 8347 IsRubyParentType(endIter.item().DesiredParentType())) { 8348 // No more items need to be wrapped in this level container. 8349 break; 8350 } 8351 8352 FCItemIterator contentEndIter(endIter); 8353 contentEndIter.SkipItemsNotWantingRubyParent(); 8354 // endIter must be on something doesn't want a ruby parent. 8355 MOZ_ASSERT(contentEndIter != endIter); 8356 8357 // InterpretRubyWhitespace depends on the fact that any leading or 8358 // trailing whitespace described in the spec have been trimmed at 8359 // this point. With this precondition, it is safe not to check 8360 // whether contentEndIter has been done. 8361 RubyWhitespaceType whitespaceType = 8362 InterpretRubyWhitespace(aState, endIter, contentEndIter); 8363 if (whitespaceType == eRubyInterLevelWhitespace) { 8364 // Remove inter-level whitespace. 8365 bool atStart = (aIter == endIter); 8366 endIter.DeleteItemsTo(this, contentEndIter); 8367 if (atStart) { 8368 aIter = endIter; 8369 } 8370 } else if (whitespaceType == eRubyInterSegmentWhitespace) { 8371 // If this level container starts with inter-segment whitespaces, 8372 // wrap them. Break at contentEndIter. Otherwise, leave it here. 8373 // Break at endIter. They will be wrapped when we are here again. 8374 if (aIter == endIter) { 8375 MOZ_ASSERT(wrapperType == eTypeRubyBaseContainer, 8376 "Inter-segment whitespace should be wrapped in rbc"); 8377 endIter = contentEndIter; 8378 } 8379 break; 8380 } else if (wrapperType == eTypeRubyTextContainer && 8381 whitespaceType != eRubyInterLeafWhitespace) { 8382 // Misparented inline content that's not inter-annotation 8383 // whitespace doesn't belong in a pseudo ruby text container. 8384 // Break at endIter. 8385 break; 8386 } else { 8387 endIter = contentEndIter; 8388 } 8389 } while (!endIter.IsDone()); 8390 8391 // It is possible that everything our parent wants us to wrap is 8392 // simply an inter-level whitespace, which has been trimmed, or 8393 // an inter-segment whitespace, which will be wrapped later. 8394 // In those cases, don't create anything. 8395 if (aIter != endIter) { 8396 WrapItemsInPseudoParent(aParentContent, aParentStyle, wrapperType, aIter, 8397 endIter); 8398 } 8399 } 8400 8401 /** 8402 * This function trims leading and trailing whitespaces 8403 * in the given item list. 8404 */ 8405 void nsCSSFrameConstructor::TrimLeadingAndTrailingWhitespaces( 8406 nsFrameConstructorState& aState, FrameConstructionItemList& aItems) { 8407 FCItemIterator iter(aItems); 8408 if (!iter.IsDone() && iter.item().IsWhitespace(aState)) { 8409 FCItemIterator spaceEndIter(iter); 8410 spaceEndIter.SkipWhitespace(aState); 8411 iter.DeleteItemsTo(this, spaceEndIter); 8412 } 8413 8414 iter.SetToEnd(); 8415 if (!iter.AtStart()) { 8416 FCItemIterator spaceEndIter(iter); 8417 do { 8418 iter.Prev(); 8419 if (iter.AtStart()) { 8420 // It's fine to not check the first item, because we 8421 // should have trimmed leading whitespaces above. 8422 break; 8423 } 8424 } while (iter.item().IsWhitespace(aState)); 8425 iter.Next(); 8426 if (iter != spaceEndIter) { 8427 iter.DeleteItemsTo(this, spaceEndIter); 8428 } 8429 } 8430 } 8431 8432 /** 8433 * This function walks through the child list (aItems) and creates 8434 * needed pseudo ruby boxes to wrap misparented children. 8435 */ 8436 void nsCSSFrameConstructor::CreateNeededPseudoInternalRubyBoxes( 8437 nsFrameConstructorState& aState, FrameConstructionItemList& aItems, 8438 nsIFrame* aParentFrame) { 8439 const ParentType ourParentType = GetParentType(aParentFrame); 8440 if (!IsRubyParentType(ourParentType) || 8441 aItems.AllWantParentType(ourParentType)) { 8442 return; 8443 } 8444 8445 if (!IsRubyPseudo(aParentFrame) || 8446 ourParentType == eTypeRuby /* for 'display:block ruby' */) { 8447 // Normally, ruby pseudo frames start from and end at some elements, 8448 // which means they don't have leading and trailing whitespaces at 8449 // all. But there are two cases where they do actually have leading 8450 // or trailing whitespaces: 8451 // 1. It is an inter-segment whitespace which in an individual ruby 8452 // base container. 8453 // 2. The pseudo frame starts from or ends at consecutive inline 8454 // content, which is not pure whitespace, but includes some. 8455 // In either case, the whitespaces are not the leading or trailing 8456 // whitespaces defined in the spec, and thus should not be trimmed. 8457 TrimLeadingAndTrailingWhitespaces(aState, aItems); 8458 } 8459 8460 FCItemIterator iter(aItems); 8461 nsIContent* parentContent = aParentFrame->GetContent(); 8462 ComputedStyle* parentStyle = aParentFrame->Style(); 8463 while (!iter.IsDone()) { 8464 if (!iter.SkipItemsWantingParentType(ourParentType)) { 8465 if (ourParentType == eTypeRuby) { 8466 WrapItemsInPseudoRubyLevelContainer(aState, iter, parentStyle, 8467 parentContent); 8468 } else { 8469 WrapItemsInPseudoRubyLeafBox(iter, parentStyle, parentContent); 8470 } 8471 } 8472 } 8473 } 8474 8475 /* 8476 * This function works as follows: we walk through the child list (aItems) and 8477 * find items that cannot have aParentFrame as their parent. We wrap 8478 * continuous runs of such items into a FrameConstructionItem for a frame that 8479 * gets them closer to their desired parents. For example, a run of non-row 8480 * children of a row-group will get wrapped in a row. When we later construct 8481 * the frame for this wrapper (in this case for the row), it'll be the correct 8482 * parent for the cells in the set of items we wrapped or we'll wrap cells 8483 * around everything else. At the end of this method, aItems is guaranteed to 8484 * contain only items for frames that can be direct kids of aParentFrame. 8485 */ 8486 void nsCSSFrameConstructor::CreateNeededPseudoContainers( 8487 nsFrameConstructorState& aState, FrameConstructionItemList& aItems, 8488 nsIFrame* aParentFrame) { 8489 ParentType ourParentType = GetParentType(aParentFrame); 8490 if (IsRubyParentType(ourParentType) || 8491 aItems.AllWantParentType(ourParentType)) { 8492 // Nothing to do here 8493 return; 8494 } 8495 8496 FCItemIterator iter(aItems); 8497 do { 8498 if (iter.SkipItemsWantingParentType(ourParentType)) { 8499 // Nothing else to do here; we're finished 8500 return; 8501 } 8502 8503 // Now we're pointing to the first child that wants a different parent 8504 // type. 8505 8506 // Now try to figure out what kids we can group together. We can generally 8507 // group everything that has a different desired parent type from us. Two 8508 // exceptions to this: 8509 // 1) If our parent type is table, we can't group columns with anything 8510 // else other than whitespace. 8511 // 2) Whitespace that lies between two things we can group which both want 8512 // a non-block parent should be dropped, even if we can't group them 8513 // with each other and even if the whitespace wants a parent of 8514 // ourParentType. Ends of the list count as things that don't want a 8515 // block parent (so that for example we'll drop a whitespace-only list). 8516 8517 FCItemIterator endIter(iter); /* iterator to find the end of the group */ 8518 ParentType groupingParentType = endIter.item().DesiredParentType(); 8519 if (aItems.AllWantParentType(groupingParentType) && 8520 groupingParentType != eTypeBlock) { 8521 // Just group them all and be done with it. We need the check for 8522 // eTypeBlock here to catch the "all the items are whitespace" case 8523 // described above. 8524 endIter.SetToEnd(); 8525 } else { 8526 // Locate the end of the group. 8527 8528 // Keep track of the type the previous item wanted, in case we have to 8529 // deal with whitespace. Start it off with ourParentType, since that's 8530 // the last thing |iter| would have skipped over. 8531 ParentType prevParentType = ourParentType; 8532 do { 8533 // Walk an iterator past any whitespace that we might be able to drop 8534 // from the list 8535 FCItemIterator spaceEndIter(endIter); 8536 if (prevParentType != eTypeBlock && 8537 !aParentFrame->IsGeneratedContentFrame() && 8538 spaceEndIter.item().IsWhitespace(aState)) { 8539 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState); 8540 8541 // We drop the whitespace in the following cases: 8542 // 1) If these are not trailing spaces and the next item wants a table 8543 // or table-part parent 8544 // 2) If these are trailing spaces and aParentFrame is a 8545 // tabular container according to rule 1.3 of CSS 2.1 Sec 17.2.1. 8546 // (Being a tabular container pretty much means ourParentType is 8547 // not eTypeBlock besides the eTypeColGroup case, which won't 8548 // reach here.) 8549 if ((!trailingSpaces && 8550 IsTableParentType(spaceEndIter.item().DesiredParentType())) || 8551 (trailingSpaces && ourParentType != eTypeBlock)) { 8552 bool updateStart = (iter == endIter); 8553 endIter.DeleteItemsTo(this, spaceEndIter); 8554 NS_ASSERTION(trailingSpaces == endIter.IsDone(), 8555 "These should match"); 8556 8557 if (updateStart) { 8558 iter = endIter; 8559 } 8560 8561 if (trailingSpaces) { 8562 break; /* Found group end */ 8563 } 8564 8565 if (updateStart) { 8566 // Update groupingParentType, since it might have been eTypeBlock 8567 // just because of the whitespace. 8568 groupingParentType = iter.item().DesiredParentType(); 8569 } 8570 } 8571 } 8572 8573 // Now endIter points to a non-whitespace item or a non-droppable 8574 // whitespace item. In the latter case, if this is the end of the group 8575 // we'll traverse this whitespace again. But it'll all just be quick 8576 // DesiredParentType() checks which will match ourParentType (that's 8577 // what it means that this is the group end), so it's OK. 8578 // However, when we are grouping a ruby parent, and endIter points to 8579 // a non-droppable whitespace, if the next non-whitespace item also 8580 // wants a ruby parent, the whitespace should also be included into 8581 // the current ruby container. 8582 prevParentType = endIter.item().DesiredParentType(); 8583 if (prevParentType == ourParentType && 8584 (endIter == spaceEndIter || spaceEndIter.IsDone() || 8585 !IsRubyParentType(groupingParentType) || 8586 !IsRubyParentType(spaceEndIter.item().DesiredParentType()))) { 8587 // End the group at endIter. 8588 break; 8589 } 8590 8591 if (ourParentType == eTypeTable && 8592 (prevParentType == eTypeColGroup) != 8593 (groupingParentType == eTypeColGroup)) { 8594 // Either we started with columns and now found something else, or 8595 // vice versa. In any case, end the grouping. 8596 break; 8597 } 8598 8599 // If we have some whitespace that we were not able to drop and there is 8600 // an item after the whitespace that is already properly parented, then 8601 // make sure to include the spaces in our group but stop the group after 8602 // that. 8603 if (spaceEndIter != endIter && !spaceEndIter.IsDone() && 8604 ourParentType == spaceEndIter.item().DesiredParentType()) { 8605 endIter = spaceEndIter; 8606 break; 8607 } 8608 8609 // Include the whitespace we didn't drop (if any) in the group. 8610 endIter = spaceEndIter; 8611 prevParentType = endIter.item().DesiredParentType(); 8612 8613 endIter.Next(); 8614 } while (!endIter.IsDone()); 8615 } 8616 8617 if (iter == endIter) { 8618 // Nothing to wrap here; just skipped some whitespace 8619 continue; 8620 } 8621 8622 // Now group together all the items between iter and endIter. The right 8623 // parent type to use depends on ourParentType. 8624 ParentType wrapperType; 8625 switch (ourParentType) { 8626 case eTypeRow: 8627 // The parent type for a cell is eTypeBlock, since that's what a cell 8628 // looks like to its kids. 8629 wrapperType = eTypeBlock; 8630 break; 8631 case eTypeRowGroup: 8632 wrapperType = eTypeRow; 8633 break; 8634 case eTypeTable: 8635 // Either colgroup or rowgroup, depending on what we're grouping. 8636 wrapperType = 8637 groupingParentType == eTypeColGroup ? eTypeColGroup : eTypeRowGroup; 8638 break; 8639 case eTypeColGroup: 8640 MOZ_CRASH("Colgroups should be suppresing non-col child items"); 8641 default: 8642 NS_ASSERTION(ourParentType == eTypeBlock, "Unrecognized parent type"); 8643 if (IsRubyParentType(groupingParentType)) { 8644 wrapperType = eTypeRuby; 8645 } else { 8646 NS_ASSERTION(IsTableParentType(groupingParentType), 8647 "groupingParentType should be either Ruby or table"); 8648 wrapperType = eTypeTable; 8649 } 8650 } 8651 8652 ComputedStyle* parentStyle = aParentFrame->Style(); 8653 WrapItemsInPseudoParent(aParentFrame->GetContent(), parentStyle, 8654 wrapperType, iter, endIter); 8655 8656 // Now |iter| points to the item that was the first one we didn't wrap; 8657 // loop and see whether we need to skip it or wrap it in something 8658 // different. 8659 } while (!iter.IsDone()); 8660 } 8661 8662 /** 8663 * This method wraps frame construction item from |aIter| to 8664 * |aEndIter|. After it returns, aIter points to the first item 8665 * after the wrapper. 8666 */ 8667 void nsCSSFrameConstructor::WrapItemsInPseudoParent( 8668 nsIContent* aParentContent, ComputedStyle* aParentStyle, 8669 ParentType aWrapperType, FCItemIterator& aIter, 8670 const FCItemIterator& aEndIter) { 8671 const PseudoParentData& pseudoData = sPseudoParentData[aWrapperType]; 8672 PseudoStyleType pseudoType = pseudoData.mPseudoType; 8673 auto& parentDisplay = *aParentStyle->StyleDisplay(); 8674 auto parentDisplayInside = parentDisplay.DisplayInside(); 8675 8676 // XXXmats should we use IsInlineInsideStyle() here instead? seems odd to 8677 // exclude RubyBaseContainer/RubyTextContainer... 8678 if (pseudoType == PseudoStyleType::table && 8679 (parentDisplay.IsInlineFlow() || 8680 parentDisplayInside == StyleDisplayInside::RubyBase || 8681 parentDisplayInside == StyleDisplayInside::RubyText)) { 8682 pseudoType = PseudoStyleType::inlineTable; 8683 } 8684 8685 RefPtr<ComputedStyle> wrapperStyle; 8686 if (pseudoData.mFCData.mBits & FCDATA_IS_WRAPPER_ANON_BOX) { 8687 wrapperStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 8688 pseudoType, aParentStyle); 8689 } else { 8690 wrapperStyle = 8691 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle( 8692 pseudoType); 8693 } 8694 8695 // Use the content of our parent frame 8696 auto* newItem = new (this) FrameConstructionItem( 8697 &pseudoData.mFCData, aParentContent, wrapperStyle.forget(), true); 8698 8699 const nsStyleDisplay* disp = newItem->mComputedStyle->StyleDisplay(); 8700 // Here we're cheating a tad... technically, table-internal items should be 8701 // inline if aParentFrame is inline, but they'll get wrapped in an 8702 // inline-table in the end, so it'll all work out. In any case, arguably 8703 // we don't need to maintain this state at this point... but it's better 8704 // to, I guess. 8705 newItem->mIsAllInline = disp->IsInlineOutsideStyle(); 8706 8707 bool isRuby = disp->IsRubyDisplayType(); 8708 if (!isRuby) { 8709 // Table pseudo frames always induce line boundaries around their 8710 // contents. 8711 newItem->mChildItems.SetLineBoundaryAtStart(true); 8712 newItem->mChildItems.SetLineBoundaryAtEnd(true); 8713 } 8714 // The parent of the items in aItems is also the parent of the items 8715 // in mChildItems 8716 newItem->mChildItems.SetParentHasNoShadowDOM( 8717 aIter.List()->ParentHasNoShadowDOM()); 8718 8719 // Eat up all items between |aIter| and |aEndIter| and put them in our 8720 // wrapper Advances |aIter| to point to |aEndIter|. 8721 aIter.AppendItemsToList(this, aEndIter, newItem->mChildItems); 8722 8723 aIter.InsertItem(newItem); 8724 } 8725 8726 void nsCSSFrameConstructor::CreateNeededPseudoSiblings( 8727 nsFrameConstructorState& aState, FrameConstructionItemList& aItems, 8728 nsIFrame* aParentFrame) { 8729 if (aItems.IsEmpty() || GetParentType(aParentFrame) != eTypeRuby) { 8730 return; 8731 } 8732 8733 FCItemIterator iter(aItems); 8734 StyleDisplay firstDisplay = 8735 iter.item().mComputedStyle->StyleDisplay()->mDisplay; 8736 if (firstDisplay == StyleDisplay::RubyBaseContainer) { 8737 return; 8738 } 8739 NS_ASSERTION(firstDisplay == StyleDisplay::RubyTextContainer, 8740 "Child of ruby frame should either a rbc or a rtc"); 8741 8742 const PseudoParentData& pseudoData = 8743 sPseudoParentData[eTypeRubyBaseContainer]; 8744 RefPtr<ComputedStyle> pseudoStyle = 8745 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 8746 pseudoData.mPseudoType, aParentFrame->Style()); 8747 FrameConstructionItem* newItem = new (this) FrameConstructionItem( 8748 &pseudoData.mFCData, 8749 // Use the content of the parent frame 8750 aParentFrame->GetContent(), pseudoStyle.forget(), true); 8751 newItem->mIsAllInline = true; 8752 newItem->mChildItems.SetParentHasNoShadowDOM(true); 8753 iter.InsertItem(newItem); 8754 } 8755 8756 #ifdef DEBUG 8757 /** 8758 * Returns true iff aFrame should be wrapped in an anonymous flex/grid item, 8759 * rather than being a direct child of aContainerFrame. 8760 * 8761 * NOTE: aContainerFrame must be a flex or grid container - this function is 8762 * purely for sanity-checking the children of these container types. 8763 * NOTE: See also NeedsAnonFlexOrGridItem(), for the non-debug version of this 8764 * logic (which operates a bit earlier, on FCData instead of frames). 8765 */ 8766 static bool FrameWantsToBeInAnonymousItem(const nsIFrame* aContainerFrame, 8767 const nsIFrame* aFrame) { 8768 MOZ_ASSERT(aContainerFrame->IsFlexOrGridContainer()); 8769 8770 // Any line-participant frames (e.g. text) definitely want to be wrapped in 8771 // an anonymous flex/grid item. 8772 if (aFrame->IsLineParticipant()) { 8773 return true; 8774 } 8775 8776 // If the container is a -webkit-{inline-}box container, then placeholders 8777 // also need to be wrapped, for compatibility. 8778 if (IsFlexContainerForLegacyWebKitBox(aContainerFrame) && 8779 aFrame->IsPlaceholderFrame()) { 8780 return true; 8781 } 8782 8783 return false; 8784 } 8785 #endif 8786 8787 static void VerifyGridFlexContainerChildren(nsIFrame* aParentFrame, 8788 const nsFrameList& aChildren) { 8789 #ifdef DEBUG 8790 if (!aParentFrame->IsFlexOrGridContainer()) { 8791 return; 8792 } 8793 8794 bool prevChildWasAnonItem = false; 8795 for (const nsIFrame* child : aChildren) { 8796 MOZ_ASSERT(!FrameWantsToBeInAnonymousItem(aParentFrame, child), 8797 "frame wants to be inside an anonymous item, but it isn't"); 8798 if (IsAnonymousItem(child)) { 8799 AssertAnonymousFlexOrGridItemParent(child, aParentFrame); 8800 MOZ_ASSERT(!prevChildWasAnonItem, "two anon items in a row"); 8801 nsIFrame* firstWrappedChild = child->PrincipalChildList().FirstChild(); 8802 MOZ_ASSERT(firstWrappedChild, "anonymous item shouldn't be empty"); 8803 prevChildWasAnonItem = true; 8804 } else { 8805 prevChildWasAnonItem = false; 8806 } 8807 } 8808 #endif 8809 } 8810 8811 static bool FrameHasOnlyPlaceholderPrevSiblings(const nsIFrame* aFrame) { 8812 // Check for prev siblings, ignoring placeholder frames. 8813 MOZ_ASSERT(aFrame, "frame must not be null"); 8814 const nsIFrame* prevSibling = aFrame; 8815 do { 8816 prevSibling = prevSibling->GetPrevSibling(); 8817 } while (prevSibling && prevSibling->IsPlaceholderFrame()); 8818 return !prevSibling; 8819 } 8820 8821 static bool FrameHasOnlyPlaceholderNextSiblings(const nsIFrame* aFrame) { 8822 // Check for next siblings, ignoring placeholder frames. 8823 MOZ_ASSERT(aFrame, "frame must not be null"); 8824 const nsIFrame* nextSibling = aFrame; 8825 do { 8826 nextSibling = nextSibling->GetNextSibling(); 8827 } while (nextSibling && nextSibling->IsPlaceholderFrame()); 8828 return !nextSibling; 8829 } 8830 8831 static void SetPageValues(nsIFrame* const aFrame, 8832 const nsAtom* const aAutoValue, 8833 const nsAtom* const aStartValue, 8834 const nsAtom* const aEndValue) { 8835 MOZ_ASSERT(aAutoValue, "Auto page value should never be null"); 8836 MOZ_ASSERT(aStartValue || aEndValue, "Should not have called with no values"); 8837 nsIFrame::PageValues* pageValues = 8838 aFrame->GetProperty(nsIFrame::PageValuesProperty()); 8839 8840 if (aStartValue) { 8841 if (aStartValue == aAutoValue) { 8842 // If the page value struct already exists, set the start value to null 8843 // to indicate the auto value. 8844 if (pageValues) { 8845 pageValues->mStartPageValue = nullptr; 8846 } 8847 } else { 8848 // The start value is not auto, so we need to store it, creating the 8849 // page values struct if it does not already exist. 8850 if (!pageValues) { 8851 pageValues = new nsIFrame::PageValues(); 8852 aFrame->SetProperty(nsIFrame::PageValuesProperty(), pageValues); 8853 } 8854 pageValues->mStartPageValue = aStartValue; 8855 } 8856 } 8857 if (aEndValue) { 8858 if (aEndValue == aAutoValue) { 8859 // If the page value struct already exists, set the end value to null 8860 // to indicate the auto value. 8861 if (pageValues) { 8862 pageValues->mEndPageValue = nullptr; 8863 } 8864 } else { 8865 // The end value is not auto, so we need to store it, creating the 8866 // page values struct if it does not already exist. 8867 if (!pageValues) { 8868 pageValues = new nsIFrame::PageValues(); 8869 aFrame->SetProperty(nsIFrame::PageValuesProperty(), pageValues); 8870 } 8871 pageValues->mEndPageValue = aEndValue; 8872 } 8873 } 8874 } 8875 8876 inline void nsCSSFrameConstructor::ConstructFramesFromItemList( 8877 nsFrameConstructorState& aState, FrameConstructionItemList& aItems, 8878 nsContainerFrame* aParentFrame, bool aParentIsWrapperAnonBox, 8879 nsFrameList& aFrameList) { 8880 #ifdef DEBUG 8881 // The assertion condition should match the logic in 8882 // MaybePushFloatContainingBlock(). 8883 MOZ_ASSERT(!(ShouldSuppressFloatingOfDescendants(aParentFrame) || 8884 aParentFrame->IsFloatContainingBlock()) || 8885 aState.mFloatCBCandidate == aParentFrame, 8886 "Our caller or ProcessChildren()'s caller should call " 8887 "MaybePushFloatContainingBlock() to handle the float containing " 8888 "block candidate!"); 8889 aState.mFloatCBCandidate = nullptr; 8890 #endif 8891 8892 // Ensure aParentIsWrapperAnonBox is correct. We _could_ compute it directly, 8893 // but it would be a bit slow, which is why we pass it from callers, who have 8894 // that information offhand in many cases. 8895 MOZ_ASSERT(ParentIsWrapperAnonBox(aParentFrame) == aParentIsWrapperAnonBox); 8896 8897 // Note: we explicitly exclude TableColGroupFrame because it doesn't 8898 // have the FCDATA_IS_WRAPPER_ANON_BOX on pseudos so aParentIsWrapperAnonBox 8899 // is false for such pseudos (see sPseudoParentData below). 8900 if (!aParentIsWrapperAnonBox && aState.mHasRenderedLegend && 8901 aParentFrame->GetContent()->IsHTMLElement(nsGkAtoms::fieldset) && 8902 !aParentFrame->IsTableColGroupFrame()) { 8903 DebugOnly<bool> found = false; 8904 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) { 8905 if (iter.item().mIsRenderedLegend) { 8906 // This makes the rendered legend the first frame in the fieldset child 8907 // list which makes keyboard traversal follow the visual order. 8908 nsFieldSetFrame* fieldSetFrame = GetFieldSetFrameFor(aParentFrame); 8909 nsFrameList renderedLegend; 8910 ConstructFramesFromItem(aState, iter, fieldSetFrame, renderedLegend); 8911 MOZ_ASSERT(renderedLegend.OnlyChild(), 8912 "a rendered legend should have exactly one frame"); 8913 fieldSetFrame->InsertFrames(FrameChildListID::Principal, nullptr, 8914 nullptr, std::move(renderedLegend)); 8915 FCItemIterator next = iter; 8916 next.Next(); 8917 iter.DeleteItemsTo(this, next); 8918 found = true; 8919 break; 8920 } 8921 } 8922 MOZ_ASSERT(found, "should have found our rendered legend"); 8923 } 8924 8925 CreateNeededPseudoContainers(aState, aItems, aParentFrame); 8926 CreateNeededAnonFlexOrGridItems(aState, aItems, aParentFrame); 8927 CreateNeededPseudoInternalRubyBoxes(aState, aItems, aParentFrame); 8928 CreateNeededPseudoSiblings(aState, aItems, aParentFrame); 8929 8930 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) { 8931 MOZ_ASSERT(!iter.item().mIsRenderedLegend, 8932 "Only one item can be the rendered legend, " 8933 "and it should've been handled above"); 8934 NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame), 8935 "Needed pseudos didn't get created; expect bad things"); 8936 ConstructFramesFromItem(aState, iter, aParentFrame, aFrameList); 8937 } 8938 8939 VerifyGridFlexContainerChildren(aParentFrame, aFrameList); 8940 8941 // Calculate and propagate page-name values for each frame in the frame list. 8942 // We do not want to compute and propagate page-name values from frames that 8943 // are children of any subclasses of block frames, but not actually a block 8944 // frame. The page-name property does not apply to frames which cannot create 8945 // class A breakpoints (currently no subclass of BlockFrame can). Because the 8946 // property does not apply, those children also cannot propagate page-name 8947 // values. 8948 // This assumption helps avoid unnecessarily handling page-names for frames 8949 // such as form controls, which also avoids bug 1819468. 8950 if (aState.mPresContext->IsPaginated() && aParentFrame->IsBlockFrame()) { 8951 // Set the start/end page values while iterating the frame list, to walk 8952 // up the frame tree only once after iterating the frame list. 8953 // This also avoids extra property lookups on these frames. 8954 MOZ_ASSERT(aState.mAutoPageNameValue == aParentFrame->GetAutoPageValue(), 8955 "aState.mAutoPageNameValue should have been equivalent to " 8956 "the auto value stored on our parent frame."); 8957 // Even though we store null for page values that equal the "auto" resolved 8958 // value on frames, we always want startPageValue/endPageValue to be the 8959 // actual atoms reflecting the start/end values. This is because when we 8960 // propagate the values up the frame tree, we will need to compare them to 8961 // the auto value for each ancestor. This value might be different than the 8962 // auto value for this frame. 8963 const nsAtom* startPageValue = nullptr; 8964 const nsAtom* endPageValue = nullptr; 8965 for (nsIFrame* f : aFrameList) { 8966 if (f->IsPlaceholderFrame()) { 8967 continue; 8968 } 8969 // Resolve auto against the parent frame's used page name, which has been 8970 // determined and set on aState.mAutoPageNameValue. If this item is not 8971 // block-level then we use the value that auto resolves to. 8972 // 8973 // This is to achieve the propagation behavior described in the spec: 8974 // 8975 // "A start page value and end page value is determined for each box as 8976 // the value (if any) propagated from its first or last child box 8977 // (respectively), else the used value on the box itself." 8978 // 8979 // "A child propagates its own start or end page value if and only if the 8980 // page property applies to it." 8981 // 8982 // The page property only applies to "boxes that create class A break 8983 // points". When taken together, this means that non block-level children 8984 // do not propagate start/end page values, and instead we use "the used 8985 // value on the box itself", the "box itself" being aParentFrame. This 8986 // value has been determined and saved as aState.mAutoPageNameValue 8987 // 8988 // https://www.w3.org/TR/css-page-3/#using-named-pages 8989 // https://www.w3.org/TR/css-break-3/#btw-blocks 8990 const StylePageName& pageName = f->StylePage()->mPage; 8991 const nsAtom* const pageNameAtom = 8992 (pageName.IsPageName() && f->IsBlockOutside()) 8993 ? pageName.AsPageName().AsAtom() 8994 : aState.mAutoPageNameValue; 8995 nsIFrame::PageValues* pageValues = 8996 f->GetProperty(nsIFrame::PageValuesProperty()); 8997 // If this frame has any children, it will already have had its page 8998 // values set at this point. However, if no page values have been set, 8999 // we must ensure that the appropriate PageValuesProperty value has been 9000 // set. 9001 // If the page name is equal to the auto value, then PageValuesProperty 9002 // should remain null to indicate that the start/end values are both 9003 // equal to the auto value. 9004 if (pageNameAtom != aState.mAutoPageNameValue && !pageValues) { 9005 pageValues = new nsIFrame::PageValues{pageNameAtom, pageNameAtom}; 9006 f->SetProperty(nsIFrame::PageValuesProperty(), pageValues); 9007 } 9008 // We don't want to use GetStartPageValue() or GetEndPageValue(), as each 9009 // requires a property lookup which we can avoid here. 9010 if (!startPageValue) { 9011 startPageValue = (pageValues && pageValues->mStartPageValue) 9012 ? pageValues->mStartPageValue.get() 9013 : aState.mAutoPageNameValue; 9014 } 9015 endPageValue = (pageValues && pageValues->mEndPageValue) 9016 ? pageValues->mEndPageValue.get() 9017 : aState.mAutoPageNameValue; 9018 MOZ_ASSERT(startPageValue && endPageValue, 9019 "Should have found start/end page value"); 9020 } 9021 MOZ_ASSERT(!startPageValue == !endPageValue, 9022 "Should have set both or neither page values"); 9023 if (startPageValue) { 9024 // Walk up the frame tree from our parent frame, propagating start and 9025 // end page values. 9026 // As we go, if we find that, for a frame, we are not contributing one of 9027 // the start/end page values, then our subtree will not contribute this 9028 // value from that frame onward. startPageValue/endPageValue are set to 9029 // null to indicate this. 9030 // Stop iterating when we are not contributing either start or end 9031 // values, when we hit the root frame (no parent), or when we find a 9032 // frame that is not a block frame. 9033 for (nsContainerFrame* ancestorFrame = aParentFrame; 9034 (startPageValue || endPageValue) && ancestorFrame && 9035 ancestorFrame->IsBlockFrame(); 9036 ancestorFrame = ancestorFrame->GetParent()) { 9037 MOZ_ASSERT(!ancestorFrame->GetPrevInFlow(), 9038 "Should not have fragmentation yet"); 9039 MOZ_ASSERT(ancestorFrame->mWasVisitedByAutoFrameConstructionPageName, 9040 "Frame should have been visited by " 9041 "AutoFrameConstructionPageName"); 9042 { 9043 // Get what the auto value is, based on this frame's parent. 9044 // For the root frame, `auto` resolves to the empty atom. 9045 const nsContainerFrame* const parent = ancestorFrame->GetParent(); 9046 const nsAtom* const parentAuto = MOZ_LIKELY(parent) 9047 ? parent->GetAutoPageValue() 9048 : nsGkAtoms::_empty; 9049 SetPageValues(ancestorFrame, parentAuto, startPageValue, 9050 endPageValue); 9051 } 9052 // Once we stop contributing start/end values, we know there is a 9053 // sibling subtree that contributed that value to our shared parent 9054 // instead of our starting frame's subtree. This means once 9055 // startPageValue/endPageValue becomes null, indicating that we are no 9056 // longer contributing that page value, it should stay null and we no 9057 // longer need to check for siblings in that direction. 9058 if (startPageValue && 9059 !FrameHasOnlyPlaceholderPrevSiblings(ancestorFrame)) { 9060 startPageValue = nullptr; 9061 } 9062 if (endPageValue && 9063 !FrameHasOnlyPlaceholderNextSiblings(ancestorFrame)) { 9064 endPageValue = nullptr; 9065 } 9066 } 9067 } 9068 } 9069 9070 if (aParentIsWrapperAnonBox) { 9071 for (nsIFrame* f : aFrameList) { 9072 f->SetParentIsWrapperAnonBox(); 9073 } 9074 } 9075 } 9076 9077 void nsCSSFrameConstructor::AddFCItemsForAnonymousContent( 9078 nsFrameConstructorState& aState, nsContainerFrame* aFrame, 9079 const nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems, 9080 FrameConstructionItemList& aItemsToConstruct, 9081 const AutoFrameConstructionPageName&) { 9082 for (const auto& info : aAnonymousItems) { 9083 nsIContent* content = info.mContent; 9084 // Gecko-styled nodes should have no pending restyle flags. 9085 // Assert some things about this content 9086 MOZ_ASSERT(!(content->GetFlags() & 9087 (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)), 9088 "Should not be marked as needing frames"); 9089 MOZ_ASSERT(!content->GetPrimaryFrame(), "Should have no existing frame"); 9090 MOZ_ASSERT(!content->IsComment() && !content->IsProcessingInstruction(), 9091 "Why is someone creating garbage anonymous content"); 9092 9093 // Make sure we eagerly performed the servo cascade when the anonymous 9094 // nodes were created. 9095 MOZ_ASSERT(!content->IsElement() || content->AsElement()->HasServoData()); 9096 9097 RefPtr<ComputedStyle> computedStyle = ResolveComputedStyle(content); 9098 9099 AddFrameConstructionItemsInternal(aState, content, aFrame, true, 9100 computedStyle, {ItemFlag::AllowPageBreak}, 9101 aItemsToConstruct); 9102 } 9103 } 9104 9105 void nsCSSFrameConstructor::ProcessChildren( 9106 nsFrameConstructorState& aState, nsIContent* aContent, 9107 ComputedStyle* aComputedStyle, nsContainerFrame* aFrame, 9108 const bool aCanHaveGeneratedContent, nsFrameList& aFrameList, 9109 const bool aAllowBlockStyles, nsIFrame* aPossiblyLeafFrame) { 9110 MOZ_ASSERT(aFrame, "Must have parent frame here"); 9111 MOZ_ASSERT(aFrame->GetContentInsertionFrame() == aFrame, 9112 "Parent frame in ProcessChildren should be its own " 9113 "content insertion frame"); 9114 9115 const uint32_t kMaxDepth = 2 * MAX_REFLOW_DEPTH; 9116 static_assert(kMaxDepth <= UINT16_MAX, "mCurrentDepth type is too narrow"); 9117 AutoRestore<uint16_t> savedDepth(mCurrentDepth); 9118 if (mCurrentDepth != UINT16_MAX) { 9119 ++mCurrentDepth; 9120 } 9121 9122 if (!aPossiblyLeafFrame) { 9123 aPossiblyLeafFrame = aFrame; 9124 } 9125 9126 // XXXbz ideally, this would do all the pushing of various 9127 // containing blocks as needed, so callers don't have to do it... 9128 9129 // Check that our parent frame is a block before allowing ::first-letter/line. 9130 // E.g. <button style="display:grid"> should not allow it. 9131 const bool allowFirstPseudos = 9132 aAllowBlockStyles && aFrame->IsBlockFrameOrSubclass(); 9133 bool haveFirstLetterStyle = false, haveFirstLineStyle = false; 9134 if (allowFirstPseudos) { 9135 ShouldHaveSpecialBlockStyle(aContent, aComputedStyle, &haveFirstLetterStyle, 9136 &haveFirstLineStyle); 9137 } 9138 9139 AutoFrameConstructionItemList itemsToConstruct(this); 9140 AutoFrameConstructionPageName pageNameTracker(aState, aFrame); 9141 9142 // If we have first-letter or first-line style then frames can get 9143 // moved around so don't set these flags. 9144 if (allowFirstPseudos && !haveFirstLetterStyle && !haveFirstLineStyle) { 9145 itemsToConstruct.SetLineBoundaryAtStart(true); 9146 itemsToConstruct.SetLineBoundaryAtEnd(true); 9147 } 9148 9149 // Create any anonymous frames we need here. 9150 AutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems; 9151 GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems); 9152 #ifdef DEBUG 9153 for (auto& item : anonymousItems) { 9154 MOZ_ASSERT(item.mContent->IsRootOfNativeAnonymousSubtree(), 9155 "Content should know it's an anonymous subtree"); 9156 } 9157 #endif 9158 AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems, 9159 itemsToConstruct, pageNameTracker); 9160 9161 nsBlockFrame* listItem = nullptr; 9162 bool isOutsideMarker = false; 9163 if (!aPossiblyLeafFrame->IsLeaf()) { 9164 // Generated content should have the same style parent as normal kids. 9165 // 9166 // Note that we don't use this style for looking up things like special 9167 // block styles because in some cases involving table pseudo-frames it has 9168 // nothing to do with the parent frame's desired behavior. 9169 auto* styleParentFrame = 9170 nsIFrame::CorrectStyleParentFrame(aFrame, PseudoStyleType::NotPseudo); 9171 ComputedStyle* parentStyle = styleParentFrame->Style(); 9172 if (aCanHaveGeneratedContent) { 9173 if (parentStyle->StyleDisplay()->mTopLayer == StyleTopLayer::Auto && 9174 !aContent->IsInNativeAnonymousSubtree()) { 9175 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(), 9176 *parentStyle, PseudoStyleType::backdrop, 9177 itemsToConstruct); 9178 } 9179 if (parentStyle->StyleDisplay()->IsListItem() && 9180 (listItem = do_QueryFrame(aFrame)) && 9181 !styleParentFrame->IsFieldSetFrame()) { 9182 isOutsideMarker = parentStyle->StyleList()->mListStylePosition == 9183 StyleListStylePosition::Outside; 9184 ItemFlags extraFlags; 9185 if (isOutsideMarker) { 9186 extraFlags += ItemFlag::IsForOutsideMarker; 9187 } 9188 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(), 9189 *parentStyle, PseudoStyleType::marker, 9190 itemsToConstruct, extraFlags); 9191 } 9192 // Probe for generated content before 9193 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(), 9194 *parentStyle, PseudoStyleType::before, 9195 itemsToConstruct); 9196 } 9197 9198 const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth); 9199 if (!addChildItems) { 9200 NS_WARNING("ProcessChildren max depth exceeded"); 9201 } 9202 9203 FlattenedChildIterator iter(aContent); 9204 const InsertionPoint insertion(aFrame, aContent); 9205 for (nsIContent* child = iter.GetNextChild(); child; 9206 child = iter.GetNextChild()) { 9207 MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(child).mContainer, 9208 "GetInsertionPoint should agree with us"); 9209 if (addChildItems) { 9210 AddFrameConstructionItems(aState, child, iter.ShadowDOMInvolved(), 9211 *parentStyle, insertion, itemsToConstruct); 9212 } else { 9213 ClearLazyBits(child, child->GetNextSibling()); 9214 } 9215 } 9216 itemsToConstruct.SetParentHasNoShadowDOM(!iter.ShadowDOMInvolved()); 9217 9218 if (aCanHaveGeneratedContent) { 9219 // Probe for generated content after 9220 CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(), 9221 *parentStyle, PseudoStyleType::after, 9222 itemsToConstruct); 9223 } 9224 } else { 9225 ClearLazyBits(aContent->GetFirstChild(), nullptr); 9226 } 9227 9228 ConstructFramesFromItemList(aState, itemsToConstruct, aFrame, 9229 /* aParentIsWrapperAnonBox = */ false, 9230 aFrameList); 9231 9232 if (listItem) { 9233 if (auto* markerFrame = nsLayoutUtils::GetMarkerFrame(aContent)) { 9234 for (auto* childFrame : aFrameList) { 9235 if (markerFrame == childFrame) { 9236 if (isOutsideMarker) { 9237 // SetMarkerFrameForListItem will add childFrame to the marker list. 9238 aFrameList.RemoveFrame(childFrame); 9239 auto* grandParent = listItem->GetParent()->GetParent(); 9240 if (listItem->Style()->GetPseudoType() == 9241 PseudoStyleType::columnContent && 9242 grandParent && grandParent->IsColumnSetWrapperFrame()) { 9243 listItem = do_QueryFrame(grandParent); 9244 MOZ_ASSERT(listItem, 9245 "ColumnSetWrapperFrame is expected to be " 9246 "a nsBlockFrame subclass"); 9247 childFrame->SetParent(listItem); 9248 } 9249 } 9250 listItem->SetMarkerFrameForListItem(childFrame); 9251 MOZ_ASSERT(listItem->HasOutsideMarker() == isOutsideMarker); 9252 #ifdef ACCESSIBILITY 9253 if (nsAccessibilityService* accService = GetAccService()) { 9254 auto* marker = markerFrame->GetContent(); 9255 accService->ContentRangeInserted(mPresShell, marker, nullptr); 9256 } 9257 #endif 9258 break; 9259 } 9260 } 9261 } 9262 } 9263 9264 if (haveFirstLetterStyle) { 9265 WrapFramesInFirstLetterFrame(aFrame, aFrameList); 9266 } 9267 if (haveFirstLineStyle) { 9268 WrapFramesInFirstLineFrame(aState, aContent, aFrame, nullptr, aFrameList); 9269 } 9270 } 9271 9272 //---------------------------------------------------------------------- 9273 9274 // Support for :first-line style 9275 9276 // Special routine to handle placing a list of frames into a block 9277 // frame that has first-line style. The routine ensures that the first 9278 // collection of inline frames end up in a first-line frame. 9279 // NOTE: aState may have containing block information related to a 9280 // different part of the frame tree than where the first line occurs. 9281 // In particular aState may be set up for where ContentInserted or 9282 // ContentAppended is inserting content, which may be some 9283 // non-first-in-flow continuation of the block to which the first-line 9284 // belongs. So this function needs to be careful about how it uses 9285 // aState. 9286 void nsCSSFrameConstructor::WrapFramesInFirstLineFrame( 9287 nsFrameConstructorState& aState, nsIContent* aBlockContent, 9288 nsContainerFrame* aBlockFrame, nsFirstLineFrame* aLineFrame, 9289 nsFrameList& aFrameList) { 9290 // Extract any initial inline frames from aFrameList so we can put them 9291 // in the first-line. 9292 nsFrameList firstLineChildren = 9293 aFrameList.Split([](nsIFrame* f) { return !f->IsInlineOutside(); }); 9294 9295 if (firstLineChildren.IsEmpty()) { 9296 // Nothing is supposed to go into the first-line; nothing to do 9297 return; 9298 } 9299 9300 if (!aLineFrame) { 9301 // Create line frame 9302 ComputedStyle* parentStyle = nsIFrame::CorrectStyleParentFrame( 9303 aBlockFrame, PseudoStyleType::firstLine) 9304 ->Style(); 9305 RefPtr<ComputedStyle> firstLineStyle = 9306 GetFirstLineStyle(aBlockContent, parentStyle); 9307 9308 aLineFrame = NS_NewFirstLineFrame(mPresShell, firstLineStyle); 9309 9310 // Initialize the line frame 9311 InitAndRestoreFrame(aState, aBlockContent, aBlockFrame, aLineFrame); 9312 9313 // The lineFrame will be the block's first child; the rest of the 9314 // frame list (after lastInlineFrame) will be the second and 9315 // subsequent children; insert lineFrame into aFrameList. 9316 aFrameList.InsertFrame(nullptr, nullptr, aLineFrame); 9317 9318 NS_ASSERTION(aLineFrame->Style() == firstLineStyle, 9319 "Bogus style on line frame"); 9320 } 9321 9322 // Give the inline frames to the lineFrame <b>after</b> reparenting them 9323 ReparentFrames(this, aLineFrame, firstLineChildren, true); 9324 if (aLineFrame->PrincipalChildList().IsEmpty() && 9325 aLineFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { 9326 aLineFrame->SetInitialChildList(FrameChildListID::Principal, 9327 std::move(firstLineChildren)); 9328 } else { 9329 AppendFrames(aLineFrame, FrameChildListID::Principal, 9330 std::move(firstLineChildren)); 9331 } 9332 } 9333 9334 // Special routine to handle appending a new frame to a block frame's 9335 // child list. Takes care of placing the new frame into the right 9336 // place when first-line style is present. 9337 void nsCSSFrameConstructor::AppendFirstLineFrames( 9338 nsFrameConstructorState& aState, nsIContent* aBlockContent, 9339 nsContainerFrame* aBlockFrame, nsFrameList& aFrameList) { 9340 // It's possible that aBlockFrame needs to have a first-line frame 9341 // created because it doesn't currently have any children. 9342 const nsFrameList& blockKids = aBlockFrame->PrincipalChildList(); 9343 if (blockKids.IsEmpty()) { 9344 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, nullptr, 9345 aFrameList); 9346 return; 9347 } 9348 9349 // Examine the last block child - if it's a first-line frame then 9350 // appended frames need special treatment. 9351 nsIFrame* lastBlockKid = blockKids.LastChild(); 9352 if (!lastBlockKid->IsLineFrame()) { 9353 // No first-line frame at the end of the list, therefore there is 9354 // an intervening block between any first-line frame the frames 9355 // we are appending. Therefore, we don't need any special 9356 // treatment of the appended frames. 9357 return; 9358 } 9359 9360 nsFirstLineFrame* lineFrame = static_cast<nsFirstLineFrame*>(lastBlockKid); 9361 WrapFramesInFirstLineFrame(aState, aBlockContent, aBlockFrame, lineFrame, 9362 aFrameList); 9363 } 9364 9365 void nsCSSFrameConstructor::CheckForFirstLineInsertion( 9366 nsIFrame* aParentFrame, nsFrameList& aFrameList) { 9367 MOZ_ASSERT(aParentFrame->Style()->IsInFirstLineSubtree(), 9368 "Why were we called?"); 9369 9370 if (aFrameList.IsEmpty()) { 9371 // Happens often enough, with the caption stuff. No need to do the ancestor 9372 // walk here. 9373 return; 9374 } 9375 9376 class RestyleManager* restyleManager = RestyleManager(); 9377 9378 // Check whether there's a ::first-line on the path up from aParentFrame. 9379 // Note that we can't stop until we've run out of ancestors with 9380 // pseudo-element data, because the first-letter might be somewhere way up the 9381 // tree; in particular it might be past our containing block. 9382 nsIFrame* ancestor = aParentFrame; 9383 while (ancestor) { 9384 if (!ancestor->Style()->IsInFirstLineSubtree()) { 9385 // We know we won't find a ::first-line now. 9386 return; 9387 } 9388 9389 if (!ancestor->IsLineFrame()) { 9390 ancestor = ancestor->GetParent(); 9391 continue; 9392 } 9393 9394 if (!ancestor->Style()->IsPseudoElement()) { 9395 // This is a continuation lineframe, not the first line; no need to do 9396 // anything to the styles. 9397 return; 9398 } 9399 9400 // Fix up the styles of aFrameList for ::first-line. 9401 for (nsIFrame* f : aFrameList) { 9402 restyleManager->ReparentComputedStyleForFirstLine(f); 9403 } 9404 return; 9405 } 9406 } 9407 9408 //---------------------------------------------------------------------- 9409 9410 // First-letter support 9411 9412 // Determine how many characters in the text fragment apply to the 9413 // first letter 9414 static int32_t FirstLetterCount( 9415 const CharacterDataBuffer* aCharacterDataBuffer) { 9416 int32_t count = 0; 9417 int32_t firstLetterLength = 0; 9418 9419 const uint32_t n = aCharacterDataBuffer->GetLength(); 9420 for (uint32_t i = 0; i < n; i++) { 9421 const char16_t ch = aCharacterDataBuffer->CharAt(i); 9422 // FIXME: take content language into account when deciding whitespace. 9423 if (dom::IsSpaceCharacter(ch)) { 9424 if (firstLetterLength) { 9425 break; 9426 } 9427 count++; 9428 continue; 9429 } 9430 // XXX I18n 9431 if ((ch == '\'') || (ch == '\"')) { 9432 if (firstLetterLength) { 9433 break; 9434 } 9435 // keep looping 9436 firstLetterLength = 1; 9437 } else { 9438 count++; 9439 break; 9440 } 9441 } 9442 9443 return count; 9444 } 9445 9446 static bool NeedFirstLetterContinuation(Text* aText) { 9447 MOZ_ASSERT(aText, "null ptr"); 9448 int32_t flc = FirstLetterCount(&aText->DataBuffer()); 9449 int32_t tl = aText->TextDataLength(); 9450 return flc < tl; 9451 } 9452 9453 static bool IsFirstLetterContent(Text* aText) { 9454 return aText->TextDataLength() && !aText->TextIsOnlyWhitespace(); 9455 } 9456 9457 /** 9458 * Create a letter frame, only make it a floating frame. 9459 */ 9460 nsFirstLetterFrame* nsCSSFrameConstructor::CreateFloatingLetterFrame( 9461 nsFrameConstructorState& aState, Text* aTextContent, nsIFrame* aTextFrame, 9462 nsContainerFrame* aParentFrame, ComputedStyle* aParentStyle, 9463 ComputedStyle* aComputedStyle, nsFrameList& aResult) { 9464 MOZ_ASSERT(aParentStyle); 9465 9466 nsFirstLetterFrame* letterFrame = 9467 NS_NewFloatingFirstLetterFrame(mPresShell, aComputedStyle); 9468 // We don't want to use a text content for a non-text frame (because we want 9469 // its primary frame to be a text frame). 9470 nsIContent* letterContent = aParentFrame->GetContent(); 9471 nsContainerFrame* containingBlock = 9472 aState.GetGeometricParent(*aComputedStyle->StyleDisplay(), aParentFrame); 9473 InitAndRestoreFrame(aState, letterContent, containingBlock, letterFrame); 9474 9475 // Init the text frame to refer to the letter frame. 9476 // 9477 // Make sure we get a proper style for it (the one passed in is for the letter 9478 // frame and will have the float property set on it; the text frame shouldn't 9479 // have that set). 9480 ServoStyleSet* styleSet = mPresShell->StyleSet(); 9481 RefPtr<ComputedStyle> textSC = 9482 styleSet->ResolveStyleForText(aTextContent, aComputedStyle); 9483 aTextFrame->SetComputedStyleWithoutNotification(textSC); 9484 InitAndRestoreFrame(aState, aTextContent, letterFrame, aTextFrame); 9485 9486 // And then give the text frame to the letter frame 9487 SetInitialSingleChild(letterFrame, aTextFrame); 9488 9489 // See if we will need to continue the text frame (does it contain 9490 // more than just the first-letter text or not?) If it does, then we 9491 // create (in advance) a continuation frame for it. 9492 nsIFrame* nextTextFrame = nullptr; 9493 if (NeedFirstLetterContinuation(aTextContent)) { 9494 // Create continuation 9495 nextTextFrame = CreateContinuingFrame(aTextFrame, aParentFrame); 9496 RefPtr<ComputedStyle> newSC = 9497 styleSet->ResolveStyleForText(aTextContent, aParentStyle); 9498 nextTextFrame->SetComputedStyle(newSC); 9499 } 9500 9501 NS_ASSERTION(aResult.IsEmpty(), "aResult should be an empty nsFrameList!"); 9502 // Put the new float before any of the floats in the block we're doing 9503 // first-letter for, that is, before any floats whose parent is 9504 // containingBlock. 9505 nsIFrame* prevSibling = nullptr; 9506 for (nsIFrame* f : aState.mFloatedList) { 9507 if (f->GetParent() == containingBlock) { 9508 break; 9509 } 9510 prevSibling = f; 9511 } 9512 9513 aState.AddChild(letterFrame, aResult, letterContent, aParentFrame, false, 9514 true, true, prevSibling); 9515 9516 if (nextTextFrame) { 9517 aResult.AppendFrame(nullptr, nextTextFrame); 9518 } 9519 9520 return letterFrame; 9521 } 9522 9523 /** 9524 * Create a new letter frame for aTextFrame. The letter frame will be 9525 * a child of aParentFrame. 9526 */ 9527 void nsCSSFrameConstructor::CreateLetterFrame( 9528 nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation, 9529 Text* aTextContent, nsContainerFrame* aParentFrame, nsFrameList& aResult) { 9530 NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?"); 9531 9532 // Get a ComputedStyle for the first-letter-frame. 9533 // 9534 // Keep this in sync with nsBlockFrame::UpdatePseudoElementStyles. 9535 nsIFrame* parentFrame = nsIFrame::CorrectStyleParentFrame( 9536 aParentFrame, PseudoStyleType::firstLetter); 9537 9538 ComputedStyle* parentComputedStyle = parentFrame->Style(); 9539 ComputedStyle* parentComputedStyleIgnoringFirstLine = parentComputedStyle; 9540 if (parentFrame->IsLineFrame()) { 9541 parentComputedStyleIgnoringFirstLine = 9542 nsIFrame::CorrectStyleParentFrame(aBlockFrame, 9543 PseudoStyleType::firstLetter) 9544 ->Style(); 9545 } 9546 9547 // Use content from containing block so that we can actually 9548 // find a matching style rule. 9549 nsIContent* blockContent = aBlockFrame->GetContent(); 9550 9551 // Create first-letter style rule, ignoring first line. If we already have a 9552 // first-line we'll reparent the style below. 9553 RefPtr<ComputedStyle> sc = 9554 GetFirstLetterStyle(blockContent, parentComputedStyleIgnoringFirstLine); 9555 9556 if (sc) { 9557 if (parentComputedStyleIgnoringFirstLine != parentComputedStyle) { 9558 sc = mPresShell->StyleSet()->ReparentComputedStyle( 9559 sc, parentComputedStyle, parentComputedStyle, 9560 blockContent->AsElement()); 9561 } 9562 9563 RefPtr<ComputedStyle> textSC = 9564 mPresShell->StyleSet()->ResolveStyleForText(aTextContent, sc); 9565 9566 // Create a new text frame (the original one will be discarded) 9567 // pass a temporary stylecontext, the correct one will be set 9568 // later. Start off by unsetting the primary frame for 9569 // aTextContent, so it's no longer pointing to the to-be-destroyed 9570 // frame. 9571 // XXXbz it would be really nice to destroy the old frame _first_, 9572 // then create the new one, so we could avoid this hack. 9573 aTextContent->SetPrimaryFrame(nullptr); 9574 nsIFrame* textFrame = NS_NewTextFrame(mPresShell, textSC); 9575 9576 NS_ASSERTION(aBlockContinuation == GetFloatContainingBlock(aParentFrame), 9577 "Containing block is confused"); 9578 nsFrameConstructorState state( 9579 mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS), 9580 GetAbsoluteContainingBlock(aParentFrame, ABS_POS), aBlockContinuation); 9581 9582 // Create the right type of first-letter frame 9583 const nsStyleDisplay* display = sc->StyleDisplay(); 9584 nsFirstLetterFrame* letterFrame; 9585 if (display->IsFloatingStyle() && !aParentFrame->IsInSVGTextSubtree()) { 9586 // Make a floating first-letter frame 9587 letterFrame = CreateFloatingLetterFrame(state, aTextContent, textFrame, 9588 aParentFrame, parentComputedStyle, 9589 sc, aResult); 9590 } else { 9591 // Make an inflow first-letter frame 9592 letterFrame = NS_NewFirstLetterFrame(mPresShell, sc); 9593 9594 // Initialize the first-letter-frame. We don't want to use a text 9595 // content for a non-text frame (because we want its primary frame to 9596 // be a text frame). 9597 nsIContent* letterContent = aParentFrame->GetContent(); 9598 letterFrame->Init(letterContent, aParentFrame, nullptr); 9599 9600 InitAndRestoreFrame(state, aTextContent, letterFrame, textFrame); 9601 9602 SetInitialSingleChild(letterFrame, textFrame); 9603 aResult.Clear(); 9604 aResult.AppendFrame(nullptr, letterFrame); 9605 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(), 9606 "should have the first continuation here"); 9607 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD); 9608 } 9609 MOZ_ASSERT( 9610 !aBlockFrame->GetPrevContinuation(), 9611 "Setting up a first-letter frame on a non-first block continuation?"); 9612 auto parent = 9613 static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation()); 9614 if (MOZ_UNLIKELY(parent->IsLineFrame())) { 9615 parent = static_cast<nsContainerFrame*>( 9616 parent->GetParent()->FirstContinuation()); 9617 } 9618 parent->SetHasFirstLetterChild(); 9619 aBlockFrame->SetProperty(nsContainerFrame::FirstLetterProperty(), 9620 letterFrame); 9621 aTextContent->SetPrimaryFrame(textFrame); 9622 } 9623 } 9624 9625 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame( 9626 nsContainerFrame* aBlockFrame, nsFrameList& aBlockFrames) { 9627 aBlockFrame->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE); 9628 9629 nsContainerFrame* parentFrame = nullptr; 9630 nsIFrame* textFrame = nullptr; 9631 nsIFrame* prevFrame = nullptr; 9632 nsFrameList letterFrames; 9633 bool stopLooking = false; 9634 WrapFramesInFirstLetterFrame( 9635 aBlockFrame, aBlockFrame, aBlockFrame, aBlockFrames.FirstChild(), 9636 &parentFrame, &textFrame, &prevFrame, letterFrames, &stopLooking); 9637 if (!parentFrame) { 9638 return; 9639 } 9640 DestroyContext context(mPresShell); 9641 if (parentFrame == aBlockFrame) { 9642 // Take textFrame out of the block's frame list and substitute the 9643 // letter frame(s) instead. 9644 aBlockFrames.DestroyFrame(context, textFrame); 9645 aBlockFrames.InsertFrames(nullptr, prevFrame, std::move(letterFrames)); 9646 } else { 9647 // Take the old textFrame out of the inline parent's child list 9648 RemoveFrame(context, FrameChildListID::Principal, textFrame); 9649 9650 // Insert in the letter frame(s) 9651 parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr, 9652 std::move(letterFrames)); 9653 } 9654 } 9655 9656 void nsCSSFrameConstructor::WrapFramesInFirstLetterFrame( 9657 nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation, 9658 nsContainerFrame* aParentFrame, nsIFrame* aParentFrameList, 9659 nsContainerFrame** aModifiedParent, nsIFrame** aTextFrame, 9660 nsIFrame** aPrevFrame, nsFrameList& aLetterFrames, bool* aStopLooking) { 9661 nsIFrame* prevFrame = nullptr; 9662 nsIFrame* frame = aParentFrameList; 9663 9664 // This loop attempts to implement "Finding the First Letter": 9665 // https://drafts.csswg.org/css-pseudo-4/#application-in-css 9666 // FIXME: we don't handle nested blocks correctly yet though (bug 214004) 9667 while (frame) { 9668 nsIFrame* nextFrame = frame->GetNextSibling(); 9669 9670 // Skip all ::markers and placeholders. 9671 if (frame->Style()->GetPseudoType() == PseudoStyleType::marker || 9672 frame->IsPlaceholderFrame()) { 9673 prevFrame = frame; 9674 frame = nextFrame; 9675 continue; 9676 } 9677 LayoutFrameType frameType = frame->Type(); 9678 if (LayoutFrameType::Text == frameType) { 9679 // Wrap up first-letter content in a letter frame 9680 Text* textContent = frame->GetContent()->AsText(); 9681 if (IsFirstLetterContent(textContent)) { 9682 // Create letter frame to wrap up the text 9683 CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent, 9684 aParentFrame, aLetterFrames); 9685 9686 // Provide adjustment information for parent 9687 *aModifiedParent = aParentFrame; 9688 *aTextFrame = frame; 9689 *aPrevFrame = prevFrame; 9690 *aStopLooking = true; 9691 return; 9692 } 9693 } else if (IsInlineFrame(frame) && frameType != LayoutFrameType::Br) { 9694 nsIFrame* kids = frame->PrincipalChildList().FirstChild(); 9695 WrapFramesInFirstLetterFrame(aBlockFrame, aBlockContinuation, 9696 static_cast<nsContainerFrame*>(frame), kids, 9697 aModifiedParent, aTextFrame, aPrevFrame, 9698 aLetterFrames, aStopLooking); 9699 if (*aStopLooking) { 9700 return; 9701 } 9702 } else { 9703 // This will stop us looking to create more letter frames. For 9704 // example, maybe the frame-type is "letterFrame" or 9705 // "placeholderFrame". This keeps us from creating extra letter 9706 // frames, and also prevents us from creating letter frames when 9707 // the first real content child of a block is not text (e.g. an 9708 // image, hr, etc.) 9709 *aStopLooking = true; 9710 break; 9711 } 9712 9713 prevFrame = frame; 9714 frame = nextFrame; 9715 } 9716 } 9717 9718 static nsIFrame* FindFirstLetterFrame(nsIFrame* aFrame, 9719 FrameChildListID aListID) { 9720 for (nsIFrame* f : aFrame->GetChildList(aListID)) { 9721 if (f->IsLetterFrame()) { 9722 return f; 9723 } 9724 } 9725 return nullptr; 9726 } 9727 9728 static void ClearHasFirstLetterChildFrom(nsContainerFrame* aParentFrame) { 9729 MOZ_ASSERT(aParentFrame); 9730 auto* parent = 9731 static_cast<nsContainerFrame*>(aParentFrame->FirstContinuation()); 9732 if (MOZ_UNLIKELY(parent->IsLineFrame())) { 9733 MOZ_ASSERT(!parent->HasFirstLetterChild()); 9734 parent = static_cast<nsContainerFrame*>( 9735 parent->GetParent()->FirstContinuation()); 9736 } 9737 MOZ_ASSERT(parent->HasFirstLetterChild()); 9738 parent->ClearHasFirstLetterChild(); 9739 } 9740 9741 void nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames( 9742 PresShell* aPresShell, nsIFrame* aBlockFrame) { 9743 // Look for the first letter frame on the FrameChildListID::Float, then 9744 // FrameChildListID::PushedFloats. 9745 nsIFrame* floatFrame = 9746 ::FindFirstLetterFrame(aBlockFrame, FrameChildListID::Float); 9747 if (!floatFrame) { 9748 floatFrame = 9749 ::FindFirstLetterFrame(aBlockFrame, FrameChildListID::PushedFloats); 9750 if (!floatFrame) { 9751 return; 9752 } 9753 } 9754 9755 // Take the text frame away from the letter frame (so it isn't 9756 // destroyed when we destroy the letter frame). 9757 nsIFrame* textFrame = floatFrame->PrincipalChildList().FirstChild(); 9758 if (!textFrame) { 9759 return; 9760 } 9761 9762 // Discover the placeholder frame for the letter frame 9763 nsPlaceholderFrame* placeholderFrame = floatFrame->GetPlaceholderFrame(); 9764 if (!placeholderFrame) { 9765 // Somethings really wrong 9766 return; 9767 } 9768 nsContainerFrame* parentFrame = placeholderFrame->GetParent(); 9769 if (!parentFrame) { 9770 // Somethings really wrong 9771 return; 9772 } 9773 9774 ClearHasFirstLetterChildFrom(parentFrame); 9775 9776 // Create a new text frame with the right style that maps all of the content 9777 // that was previously part of the letter frame (and probably continued 9778 // elsewhere). 9779 ComputedStyle* parentSC = parentFrame->Style(); 9780 nsIContent* textContent = textFrame->GetContent(); 9781 if (!textContent) { 9782 return; 9783 } 9784 RefPtr<ComputedStyle> newSC = 9785 aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC); 9786 nsIFrame* newTextFrame = NS_NewTextFrame(aPresShell, newSC); 9787 newTextFrame->Init(textContent, parentFrame, nullptr); 9788 9789 // Destroy the old text frame's continuations (the old text frame 9790 // will be destroyed when its letter frame is destroyed). 9791 nsIFrame* frameToDelete = textFrame->LastContinuation(); 9792 DestroyContext context(mPresShell); 9793 while (frameToDelete != textFrame) { 9794 nsIFrame* nextFrameToDelete = frameToDelete->GetPrevContinuation(); 9795 RemoveFrame(context, FrameChildListID::Principal, frameToDelete); 9796 frameToDelete = nextFrameToDelete; 9797 } 9798 9799 nsIFrame* prevSibling = placeholderFrame->GetPrevSibling(); 9800 9801 // Now that everything is set... 9802 #ifdef NOISY_FIRST_LETTER 9803 printf( 9804 "RemoveFloatingFirstLetterFrames: textContent=%p oldTextFrame=%p " 9805 "newTextFrame=%p\n", 9806 textContent.get(), textFrame, newTextFrame); 9807 #endif 9808 9809 // Remove placeholder frame and the float 9810 RemoveFrame(context, FrameChildListID::Principal, placeholderFrame); 9811 9812 // Now that the old frames are gone, we can start pointing to our 9813 // new primary frame. 9814 textContent->SetPrimaryFrame(newTextFrame); 9815 9816 // Wallpaper bug 822910. 9817 bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame(); 9818 if (offsetsNeedFixing) { 9819 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING); 9820 } 9821 9822 // Insert text frame in its place 9823 InsertFrames(parentFrame, FrameChildListID::Principal, prevSibling, 9824 nsFrameList(newTextFrame, newTextFrame)); 9825 9826 if (offsetsNeedFixing) { 9827 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING); 9828 } 9829 } 9830 9831 void nsCSSFrameConstructor::RemoveFirstLetterFrames( 9832 PresShell* aPresShell, nsContainerFrame* aFrame, 9833 nsContainerFrame* aBlockFrame, bool* aStopLooking) { 9834 nsIFrame* prevSibling = nullptr; 9835 nsIFrame* kid = aFrame->PrincipalChildList().FirstChild(); 9836 9837 while (kid) { 9838 if (kid->IsLetterFrame()) { 9839 ClearHasFirstLetterChildFrom(aFrame); 9840 nsIFrame* textFrame = kid->PrincipalChildList().FirstChild(); 9841 if (!textFrame) { 9842 break; 9843 } 9844 9845 // Create a new textframe 9846 ComputedStyle* parentSC = aFrame->Style(); 9847 if (!parentSC) { 9848 break; 9849 } 9850 nsIContent* textContent = textFrame->GetContent(); 9851 if (!textContent) { 9852 break; 9853 } 9854 RefPtr<ComputedStyle> newSC = 9855 aPresShell->StyleSet()->ResolveStyleForText(textContent, parentSC); 9856 textFrame = NS_NewTextFrame(aPresShell, newSC); 9857 textFrame->Init(textContent, aFrame, nullptr); 9858 9859 DestroyContext context(mPresShell); 9860 9861 // Next rip out the kid and replace it with the text frame 9862 RemoveFrame(context, FrameChildListID::Principal, kid); 9863 9864 // Now that the old frames are gone, we can start pointing to our 9865 // new primary frame. 9866 textContent->SetPrimaryFrame(textFrame); 9867 9868 // Wallpaper bug 822910. 9869 bool offsetsNeedFixing = prevSibling && prevSibling->IsTextFrame(); 9870 if (offsetsNeedFixing) { 9871 prevSibling->AddStateBits(TEXT_OFFSETS_NEED_FIXING); 9872 } 9873 9874 // Insert text frame in its place 9875 InsertFrames(aFrame, FrameChildListID::Principal, prevSibling, 9876 nsFrameList(textFrame, textFrame)); 9877 9878 if (offsetsNeedFixing) { 9879 prevSibling->RemoveStateBits(TEXT_OFFSETS_NEED_FIXING); 9880 } 9881 9882 *aStopLooking = true; 9883 NS_ASSERTION(!aBlockFrame->GetPrevContinuation(), 9884 "should have the first continuation here"); 9885 aBlockFrame->RemoveStateBits(NS_BLOCK_HAS_FIRST_LETTER_CHILD); 9886 break; 9887 } 9888 if (IsInlineFrame(kid)) { 9889 nsContainerFrame* kidAsContainerFrame = do_QueryFrame(kid); 9890 if (kidAsContainerFrame) { 9891 // Look inside child inline frame for the letter frame. 9892 RemoveFirstLetterFrames(aPresShell, kidAsContainerFrame, aBlockFrame, 9893 aStopLooking); 9894 if (*aStopLooking) { 9895 break; 9896 } 9897 } 9898 } 9899 prevSibling = kid; 9900 kid = kid->GetNextSibling(); 9901 } 9902 } 9903 9904 void nsCSSFrameConstructor::RemoveLetterFrames(PresShell* aPresShell, 9905 nsContainerFrame* aBlockFrame) { 9906 aBlockFrame = 9907 static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation()); 9908 aBlockFrame->RemoveProperty(nsContainerFrame::FirstLetterProperty()); 9909 nsContainerFrame* continuation = aBlockFrame; 9910 9911 bool stopLooking = false; 9912 do { 9913 RemoveFloatingFirstLetterFrames(aPresShell, continuation); 9914 RemoveFirstLetterFrames(aPresShell, continuation, aBlockFrame, 9915 &stopLooking); 9916 if (stopLooking) { 9917 break; 9918 } 9919 continuation = 9920 static_cast<nsContainerFrame*>(continuation->GetNextContinuation()); 9921 } while (continuation); 9922 } 9923 9924 // Fixup the letter frame situation for the given block 9925 void nsCSSFrameConstructor::RecoverLetterFrames(nsContainerFrame* aBlockFrame) { 9926 aBlockFrame = 9927 static_cast<nsContainerFrame*>(aBlockFrame->FirstContinuation()); 9928 nsContainerFrame* continuation = aBlockFrame; 9929 9930 nsContainerFrame* parentFrame = nullptr; 9931 nsIFrame* textFrame = nullptr; 9932 nsIFrame* prevFrame = nullptr; 9933 nsFrameList letterFrames; 9934 bool stopLooking = false; 9935 do { 9936 // XXX shouldn't this bit be set already (bug 408493), assert instead? 9937 continuation->AddStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE); 9938 WrapFramesInFirstLetterFrame( 9939 aBlockFrame, continuation, continuation, 9940 continuation->PrincipalChildList().FirstChild(), &parentFrame, 9941 &textFrame, &prevFrame, letterFrames, &stopLooking); 9942 if (stopLooking) { 9943 break; 9944 } 9945 continuation = 9946 static_cast<nsContainerFrame*>(continuation->GetNextContinuation()); 9947 } while (continuation); 9948 9949 if (!parentFrame) { 9950 return; 9951 } 9952 // Take the old textFrame out of the parent's child list 9953 DestroyContext context(mPresShell); 9954 RemoveFrame(context, FrameChildListID::Principal, textFrame); 9955 9956 // Insert in the letter frame(s) 9957 parentFrame->InsertFrames(FrameChildListID::Principal, prevFrame, nullptr, 9958 std::move(letterFrames)); 9959 } 9960 9961 //---------------------------------------------------------------------- 9962 9963 void nsCSSFrameConstructor::ConstructBlock( 9964 nsFrameConstructorState& aState, nsIContent* aContent, 9965 nsContainerFrame* aParentFrame, nsContainerFrame* aContentParentFrame, 9966 ComputedStyle* aComputedStyle, nsContainerFrame** aNewFrame, 9967 nsFrameList& aFrameList, nsIFrame* aPositionedFrameForAbsPosContainer) { 9968 // clang-format off 9969 // 9970 // If a block frame is in a multi-column subtree, its children may need to 9971 // be chopped into runs of blocks containing column-spans and runs of 9972 // blocks containing no column-spans. Each run containing column-spans 9973 // will be wrapped by an anonymous block. See CreateColumnSpanSiblings() for 9974 // the implementation. 9975 // 9976 // If a block frame is a multi-column container, its children will need to 9977 // be processed as above. Moreover, it creates a ColumnSetWrapperFrame as 9978 // its outermost frame, and its children which have no 9979 // -moz-column-span-wrapper pseudo will be wrapped in ColumnSetFrames. See 9980 // FinishBuildingColumns() for the implementation. 9981 // 9982 // The multi-column subtree maintains the following invariants: 9983 // 9984 // 1) All the frames have the frame state bit 9985 // NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR set, except for top-level 9986 // ColumnSetWrapperFrame and those children in the column-span subtrees. 9987 // 9988 // 2) The first and last frame under ColumnSetWrapperFrame are always 9989 // ColumnSetFrame. 9990 // 9991 // 3) ColumnSetFrames are linked together as continuations. 9992 // 9993 // 4) Those column-span wrappers are *not* linked together with themselves nor 9994 // with the original block frame. The continuation chain consists of the 9995 // original block frame and the original block's continuations wrapping 9996 // non-column-spans. 9997 // 9998 // For example, this HTML 9999 // <div id="x" style="column-count: 2;"> 10000 // <div style="column-span: all">a</div> 10001 // <div id="y"> 10002 // b 10003 // <div style="column-span: all">c</div> 10004 // <div style="column-span: all">d</div> 10005 // e 10006 // </div> 10007 // </div> 10008 // <div style="column-span: all">f</div> 10009 // 10010 // yields the following frame tree. 10011 // 10012 // A) ColumnSetWrapper (original style) 10013 // B) ColumnSet (-moz-column-set) <-- always created by BeginBuildingColumns 10014 // C) Block (-moz-column-content) 10015 // D) Block (-moz-column-span-wrapper, created by x) 10016 // E) Block (div) 10017 // F) Text ("a") 10018 // G) ColumnSet (-moz-column-set) 10019 // H) Block (-moz-column-content, created by x) 10020 // I) Block (div, y) 10021 // J) Text ("b") 10022 // K) Block (-moz-column-span-wrapper, created by x) 10023 // L) Block (-moz-column-span-wrapper, created by y) 10024 // M) Block (div, new BFC) 10025 // N) Text ("c") 10026 // O) Block (div, new BFC) 10027 // P) Text ("d") 10028 // Q) ColumnSet (-moz-column-set) 10029 // R) Block (-moz-column-content, created by x) 10030 // S) Block (div, y) 10031 // T) Text ("e") 10032 // U) Block (div, new BFC) <-- not in multi-column hierarchy 10033 // V) Text ("f") 10034 // 10035 // ColumnSet linkage described in 3): B -> G -> Q 10036 // 10037 // Block linkage described in 4): C -> H -> R and I -> S 10038 // 10039 // clang-format on 10040 10041 nsBlockFrame* blockFrame = do_QueryFrame(*aNewFrame); 10042 MOZ_ASSERT(blockFrame && blockFrame->IsBlockFrame(), "not a block frame?"); 10043 10044 // Create column hierarchy if necessary. 10045 const bool needsColumn = 10046 aComputedStyle->StyleColumn()->IsColumnContainerStyle(); 10047 if (needsColumn) { 10048 *aNewFrame = BeginBuildingColumns(aState, aContent, aParentFrame, 10049 blockFrame, aComputedStyle); 10050 10051 if (aPositionedFrameForAbsPosContainer == blockFrame) { 10052 aPositionedFrameForAbsPosContainer = *aNewFrame; 10053 } 10054 } else { 10055 // No need to create column hierarchy. Initialize block frame. 10056 blockFrame->SetComputedStyleWithoutNotification(aComputedStyle); 10057 InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame); 10058 } 10059 10060 aState.AddChild(*aNewFrame, aFrameList, aContent, 10061 aContentParentFrame ? aContentParentFrame : aParentFrame); 10062 if (!mRootElementFrame) { 10063 mRootElementFrame = *aNewFrame; 10064 } 10065 10066 // We should make the outer frame be the absolute containing block, 10067 // if one is required. We have to do this because absolute 10068 // positioning must be computed with respect to the CSS dimensions 10069 // of the element, which are the dimensions of the outer block. But 10070 // we can't really do that because only blocks can have absolute 10071 // children. So use the block and try to compensate with hacks 10072 // in nsBlockFrame::CalculateContainingBlockSizeForAbsolutes. 10073 nsFrameConstructorSaveState absoluteSaveState; 10074 (*aNewFrame)->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 10075 if (aPositionedFrameForAbsPosContainer) { 10076 aState.PushAbsoluteContainingBlock( 10077 *aNewFrame, aPositionedFrameForAbsPosContainer, absoluteSaveState); 10078 } 10079 10080 nsFrameConstructorSaveState floatSaveState; 10081 aState.MaybePushFloatContainingBlock(blockFrame, floatSaveState); 10082 10083 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) && 10084 !ShouldSuppressColumnSpanDescendants(aParentFrame)) { 10085 blockFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR); 10086 } 10087 10088 // Process the child content 10089 nsFrameList childList; 10090 ProcessChildren(aState, aContent, aComputedStyle, blockFrame, true, childList, 10091 true); 10092 10093 if (!MayNeedToCreateColumnSpanSiblings(blockFrame, childList)) { 10094 // No need to create column-span siblings. 10095 blockFrame->SetInitialChildList(FrameChildListID::Principal, 10096 std::move(childList)); 10097 return; 10098 } 10099 10100 // Extract any initial non-column-span kids, and put them in block frame's 10101 // child list. 10102 nsFrameList initialNonColumnSpanKids = 10103 childList.Split([](nsIFrame* f) { return f->IsColumnSpan(); }); 10104 blockFrame->SetInitialChildList(FrameChildListID::Principal, 10105 std::move(initialNonColumnSpanKids)); 10106 10107 if (childList.IsEmpty()) { 10108 // No more kids to process (there weren't any column-span kids). 10109 return; 10110 } 10111 10112 nsFrameList columnSpanSiblings = CreateColumnSpanSiblings( 10113 aState, blockFrame, childList, 10114 // If we're constructing a column container, pass nullptr as 10115 // aPositionedFrame to forbid reparenting absolute/fixed positioned frames 10116 // to column contents or column-span wrappers. 10117 needsColumn ? nullptr : aPositionedFrameForAbsPosContainer); 10118 10119 if (needsColumn) { 10120 // We're constructing a column container; need to finish building it. 10121 FinishBuildingColumns(aState, *aNewFrame, blockFrame, columnSpanSiblings); 10122 } else { 10123 // We're constructing a normal block which has column-span children in a 10124 // column hierarchy such as "x" in the following example. 10125 // 10126 // <div style="column-count: 2"> 10127 // <div id="x"> 10128 // <div>normal child</div> 10129 // <div style="column-span">spanner</div> 10130 // </div> 10131 // </div> 10132 aFrameList.AppendFrames(nullptr, std::move(columnSpanSiblings)); 10133 } 10134 10135 MOZ_ASSERT(columnSpanSiblings.IsEmpty(), 10136 "The column-span siblings should be moved to the proper place!"); 10137 } 10138 10139 nsBlockFrame* nsCSSFrameConstructor::BeginBuildingColumns( 10140 nsFrameConstructorState& aState, nsIContent* aContent, 10141 nsContainerFrame* aParentFrame, nsContainerFrame* aColumnContent, 10142 ComputedStyle* aComputedStyle) { 10143 MOZ_ASSERT(aColumnContent->IsBlockFrame(), 10144 "aColumnContent should be a block frame."); 10145 MOZ_ASSERT(aComputedStyle->StyleColumn()->IsColumnContainerStyle(), 10146 "No need to build a column hierarchy!"); 10147 10148 // The initial column hierarchy looks like this: 10149 // 10150 // ColumnSetWrapper (original style) 10151 // ColumnSet (-moz-column-set) 10152 // Block (-moz-column-content) 10153 // 10154 nsBlockFrame* columnSetWrapper = NS_NewColumnSetWrapperFrame( 10155 mPresShell, aComputedStyle, nsFrameState(NS_FRAME_OWNS_ANON_BOXES)); 10156 InitAndRestoreFrame(aState, aContent, aParentFrame, columnSetWrapper); 10157 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) && 10158 !ShouldSuppressColumnSpanDescendants(aParentFrame)) { 10159 columnSetWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR); 10160 } 10161 10162 AutoFrameConstructionPageName pageNameTracker(aState, columnSetWrapper); 10163 RefPtr<ComputedStyle> columnSetStyle = 10164 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 10165 PseudoStyleType::columnSet, aComputedStyle); 10166 nsContainerFrame* columnSet = NS_NewColumnSetFrame( 10167 mPresShell, columnSetStyle, nsFrameState(NS_FRAME_OWNS_ANON_BOXES)); 10168 InitAndRestoreFrame(aState, aContent, columnSetWrapper, columnSet); 10169 columnSet->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR); 10170 10171 RefPtr<ComputedStyle> blockStyle = 10172 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 10173 PseudoStyleType::columnContent, columnSetStyle); 10174 aColumnContent->SetComputedStyleWithoutNotification(blockStyle); 10175 InitAndRestoreFrame(aState, aContent, columnSet, aColumnContent); 10176 aColumnContent->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR); 10177 10178 // Set up the parent-child chain. 10179 SetInitialSingleChild(columnSetWrapper, columnSet); 10180 SetInitialSingleChild(columnSet, aColumnContent); 10181 10182 return columnSetWrapper; 10183 } 10184 10185 void nsCSSFrameConstructor::FinishBuildingColumns( 10186 nsFrameConstructorState& aState, nsContainerFrame* aColumnSetWrapper, 10187 nsContainerFrame* aColumnContent, nsFrameList& aColumnContentSiblings) { 10188 nsContainerFrame* prevColumnSet = aColumnContent->GetParent(); 10189 10190 MOZ_ASSERT(prevColumnSet->IsColumnSetFrame() && 10191 prevColumnSet->GetParent() == aColumnSetWrapper, 10192 "Should have established column hierarchy!"); 10193 10194 // Tag the first ColumnSet to have column-span siblings so that the bit can 10195 // propagate to all the continuations. We don't want the last ColumnSet to 10196 // have this bit, so we will unset the bit for it at the end of this function. 10197 prevColumnSet->SetHasColumnSpanSiblings(true); 10198 10199 nsFrameList finalList; 10200 while (aColumnContentSiblings.NotEmpty()) { 10201 nsIFrame* f = aColumnContentSiblings.RemoveFirstChild(); 10202 if (f->IsColumnSpan()) { 10203 // Do nothing for column-span wrappers. Just move it to the final 10204 // items. 10205 finalList.AppendFrame(aColumnSetWrapper, f); 10206 } else { 10207 auto* continuingColumnSet = static_cast<nsContainerFrame*>( 10208 CreateContinuingFrame(prevColumnSet, aColumnSetWrapper, false)); 10209 MOZ_ASSERT(continuingColumnSet->HasColumnSpanSiblings(), 10210 "The bit should propagate to the next continuation!"); 10211 10212 f->SetParent(continuingColumnSet); 10213 SetInitialSingleChild(continuingColumnSet, f); 10214 finalList.AppendFrame(aColumnSetWrapper, continuingColumnSet); 10215 prevColumnSet = continuingColumnSet; 10216 } 10217 } 10218 10219 // Unset the bit because the last ColumnSet has no column-span siblings. 10220 prevColumnSet->SetHasColumnSpanSiblings(false); 10221 10222 aColumnSetWrapper->AppendFrames(FrameChildListID::Principal, 10223 std::move(finalList)); 10224 } 10225 10226 bool nsCSSFrameConstructor::MayNeedToCreateColumnSpanSiblings( 10227 nsContainerFrame* aBlockFrame, const nsFrameList& aChildList) { 10228 if (!aBlockFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) { 10229 // The block frame isn't in a multi-column block formatting context. 10230 return false; 10231 } 10232 10233 if (ShouldSuppressColumnSpanDescendants(aBlockFrame)) { 10234 // No need to create column-span siblings for a frame that suppresses them. 10235 return false; 10236 } 10237 10238 if (aChildList.IsEmpty()) { 10239 // No child needs to be processed. 10240 return false; 10241 } 10242 10243 // Need to actually look into the child list. 10244 return true; 10245 } 10246 10247 nsFrameList nsCSSFrameConstructor::CreateColumnSpanSiblings( 10248 nsFrameConstructorState& aState, nsContainerFrame* aInitialBlock, 10249 nsFrameList& aChildList, nsIFrame* aPositionedFrame) { 10250 MOZ_ASSERT(aInitialBlock->IsBlockFrameOrSubclass()); 10251 MOZ_ASSERT(!aPositionedFrame || aPositionedFrame->IsAbsPosContainingBlock()); 10252 10253 nsIContent* const content = aInitialBlock->GetContent(); 10254 nsContainerFrame* const parentFrame = aInitialBlock->GetParent(); 10255 const bool isInitialBlockFloatCB = aInitialBlock->IsFloatContainingBlock(); 10256 10257 nsFrameList siblings; 10258 nsContainerFrame* lastNonColumnSpanWrapper = aInitialBlock; 10259 10260 // Tag the first non-column-span wrapper to have column-span siblings so that 10261 // the bit can propagate to all the continuations. We don't want the last 10262 // wrapper to have this bit, so we will unset the bit for it at the end of 10263 // this function. 10264 lastNonColumnSpanWrapper->SetHasColumnSpanSiblings(true); 10265 do { 10266 MOZ_ASSERT(aChildList.NotEmpty(), "Why call this if child list is empty?"); 10267 MOZ_ASSERT(aChildList.FirstChild()->IsColumnSpan(), 10268 "Must have the child starting with column-span!"); 10269 10270 // Grab the consecutive column-span kids, and reparent them into a 10271 // block frame. 10272 RefPtr<ComputedStyle> columnSpanWrapperStyle = 10273 mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle( 10274 PseudoStyleType::columnSpanWrapper); 10275 nsBlockFrame* columnSpanWrapper = 10276 NS_NewBlockFrame(mPresShell, columnSpanWrapperStyle); 10277 InitAndRestoreFrame(aState, content, parentFrame, columnSpanWrapper, false); 10278 columnSpanWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR | 10279 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 10280 10281 nsFrameList columnSpanKids = 10282 aChildList.Split([](nsIFrame* f) { return !f->IsColumnSpan(); }); 10283 columnSpanKids.ApplySetParent(columnSpanWrapper); 10284 columnSpanWrapper->SetInitialChildList(FrameChildListID::Principal, 10285 std::move(columnSpanKids)); 10286 if (aPositionedFrame) { 10287 aState.ReparentAbsoluteItems(columnSpanWrapper); 10288 } 10289 10290 siblings.AppendFrame(nullptr, columnSpanWrapper); 10291 10292 // Grab the consecutive non-column-span kids, and reparent them into a new 10293 // continuation of the last non-column-span wrapper frame. 10294 auto* nonColumnSpanWrapper = static_cast<nsContainerFrame*>( 10295 CreateContinuingFrame(lastNonColumnSpanWrapper, parentFrame, false)); 10296 nonColumnSpanWrapper->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR | 10297 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 10298 MOZ_ASSERT(nonColumnSpanWrapper->HasColumnSpanSiblings(), 10299 "The bit should propagate to the next continuation!"); 10300 10301 if (aChildList.NotEmpty()) { 10302 nsFrameList nonColumnSpanKids = 10303 aChildList.Split([](nsIFrame* f) { return f->IsColumnSpan(); }); 10304 10305 nonColumnSpanKids.ApplySetParent(nonColumnSpanWrapper); 10306 nonColumnSpanWrapper->SetInitialChildList(FrameChildListID::Principal, 10307 std::move(nonColumnSpanKids)); 10308 if (aPositionedFrame) { 10309 aState.ReparentAbsoluteItems(nonColumnSpanWrapper); 10310 } 10311 if (isInitialBlockFloatCB) { 10312 aState.ReparentFloats(nonColumnSpanWrapper); 10313 } 10314 } 10315 10316 siblings.AppendFrame(nullptr, nonColumnSpanWrapper); 10317 10318 lastNonColumnSpanWrapper = nonColumnSpanWrapper; 10319 } while (aChildList.NotEmpty()); 10320 10321 // Unset the bit because the last non-column-span wrapper has no column-span 10322 // siblings. 10323 lastNonColumnSpanWrapper->SetHasColumnSpanSiblings(false); 10324 10325 return siblings; 10326 } 10327 10328 bool nsCSSFrameConstructor::MaybeRecreateForColumnSpan( 10329 nsFrameConstructorState& aState, nsContainerFrame* aParentFrame, 10330 nsFrameList& aFrameList, nsIFrame* aPrevSibling) { 10331 if (!aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) { 10332 return false; 10333 } 10334 10335 if (aFrameList.IsEmpty()) { 10336 return false; 10337 } 10338 10339 MOZ_ASSERT(!IsFramePartOfIBSplit(aParentFrame), 10340 "We should have wiped aParentFrame in " 10341 "WipeContainingBlock if it's part of IB split!"); 10342 10343 nsIFrame* nextSibling = ::GetInsertNextSibling(aParentFrame, aPrevSibling); 10344 if (!nextSibling && IsLastContinuationForColumnContent(aParentFrame)) { 10345 // We are appending a list of frames to the last continuation of a 10346 // ::-moz-column-content. This is the case where we can fix the frame tree 10347 // instead of reframing the containing block. Return false and let 10348 // AppendFramesToParent() deal with this. 10349 return false; 10350 } 10351 10352 auto HasColumnSpan = [](const nsFrameList& aList) { 10353 for (nsIFrame* f : aList) { 10354 if (f->IsColumnSpan()) { 10355 return true; 10356 } 10357 } 10358 return false; 10359 }; 10360 10361 if (HasColumnSpan(aFrameList)) { 10362 // If any frame in the frame list has "column-span:all" style, i.e. a 10363 // -moz-column-span-wrapper frame, we need to reframe the multi-column 10364 // containing block. 10365 // 10366 // We can only be here if none of the new inserted nsIContent* nodes (via 10367 // ContentAppended or ContentRangeInserted) have column-span:all style, yet 10368 // some of them have column-span:all descendants. Sadly, there's no way to 10369 // detect this by checking FrameConstructionItems in WipeContainingBlock(). 10370 // Otherwise, we would have already wiped the multi-column containing block. 10371 PROFILER_MARKER("Reframe multi-column after constructing frame list", 10372 LAYOUT, {}, Tracing, "Layout"); 10373 10374 // aFrameList can contain placeholder frames. In order to destroy their 10375 // associated out-of-flow frames properly, we need to manually flush all the 10376 // out-of-flow frames in aState to their container frames. 10377 aState.ProcessFrameInsertionsForAllLists(); 10378 DestroyContext context(mPresShell); 10379 aFrameList.DestroyFrames(context); 10380 RecreateFramesForContent( 10381 GetMultiColumnContainingBlockFor(aParentFrame)->GetContent(), 10382 InsertionKind::Async); 10383 return true; 10384 } 10385 10386 return false; 10387 } 10388 10389 nsIFrame* nsCSSFrameConstructor::ConstructInline( 10390 nsFrameConstructorState& aState, FrameConstructionItem& aItem, 10391 nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay, 10392 nsFrameList& aFrameList) { 10393 // If an inline frame has non-inline kids, then we chop up the child list 10394 // into runs of blocks and runs of inlines, create anonymous block frames to 10395 // contain the runs of blocks, inline frames with our style for the runs of 10396 // inlines, and put all these frames, in order, into aFrameList. 10397 // 10398 // When there are column-span blocks in a run of blocks, instead of creating 10399 // an anonymous block to wrap them, we create multiple anonymous blocks, 10400 // wrapping runs of non-column-spans and runs of column-spans. 10401 // 10402 // We return the the first one. The whole setup is called an {ib} 10403 // split; in what follows "frames in the split" refers to the anonymous blocks 10404 // and inlines that contain our children. 10405 // 10406 // {ib} splits maintain the following invariants: 10407 // 1) All frames in the split have the NS_FRAME_PART_OF_IBSPLIT bit 10408 // set. 10409 // 10410 // 2) Each frame in the split has the nsIFrame::IBSplitSibling 10411 // property pointing to the next frame in the split, except for the last 10412 // one, which does not have it set. 10413 // 10414 // 3) Each frame in the split has the nsIFrame::IBSplitPrevSibling 10415 // property pointing to the previous frame in the split, except for the 10416 // first one, which does not have it set. 10417 // 10418 // 4) The first and last frame in the split are always inlines. 10419 // 10420 // 5) The frames wrapping runs of non-column-spans are linked together as 10421 // continuations. The frames wrapping runs of column-spans are *not* 10422 // linked with each other nor with other non-column-span wrappers. 10423 // 10424 // 6) The first and last frame in the chains of blocks are always wrapping 10425 // non-column-spans. Both of them are created even if they're empty. 10426 // 10427 // An invariant that is NOT maintained is that the wrappers are actually 10428 // linked via GetNextSibling linkage. A simple example is an inline 10429 // containing an inline that contains a block. The three parts of the inner 10430 // inline end up with three different parents. 10431 // 10432 // For example, this HTML: 10433 // <span> 10434 // <div>a</div> 10435 // <span> 10436 // b 10437 // <div>c</div> 10438 // </span> 10439 // d 10440 // <div>e</div> 10441 // f 10442 // </span> 10443 // Gives the following frame tree: 10444 // 10445 // Inline (outer span) 10446 // Block (anonymous, outer span) 10447 // Block (div) 10448 // Text("a") 10449 // Inline (outer span) 10450 // Inline (inner span) 10451 // Text("b") 10452 // Block (anonymous, outer span) 10453 // Block (anonymous, inner span) 10454 // Block (div) 10455 // Text("c") 10456 // Inline (outer span) 10457 // Inline (inner span) 10458 // Text("d") 10459 // Block (anonymous, outer span) 10460 // Block (div) 10461 // Text("e") 10462 // Inline (outer span) 10463 // Text("f") 10464 10465 nsIContent* const content = aItem.mContent; 10466 ComputedStyle* const computedStyle = aItem.mComputedStyle; 10467 10468 nsInlineFrame* newFrame = NS_NewInlineFrame(mPresShell, computedStyle); 10469 10470 // Initialize the frame 10471 InitAndRestoreFrame(aState, content, aParentFrame, newFrame); 10472 10473 // definition cannot be inside next block because the object's destructor is 10474 // significant. this is part of the fix for bug 42372 10475 nsFrameConstructorSaveState absoluteSaveState; 10476 10477 bool isAbsPosCB = newFrame->IsAbsPosContainingBlock(); 10478 newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 10479 if (isAbsPosCB) { 10480 // Relatively positioned frames becomes a container for child 10481 // frames that are positioned 10482 aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState); 10483 } 10484 10485 if (aParentFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR) && 10486 !ShouldSuppressColumnSpanDescendants(aParentFrame)) { 10487 newFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR); 10488 } 10489 10490 // Process the child content 10491 nsFrameList childList; 10492 ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame, 10493 /* aParentIsWrapperAnonBox = */ false, childList); 10494 10495 nsIFrame* firstBlock = nullptr; 10496 if (!aItem.mIsAllInline) { 10497 for (nsIFrame* f : childList) { 10498 if (f->IsBlockOutside()) { 10499 firstBlock = f; 10500 break; 10501 } 10502 } 10503 } 10504 10505 if (aItem.mIsAllInline || !firstBlock) { 10506 // This part is easy. We either already know we have no non-inline kids, 10507 // or haven't found any when constructing actual frames (the latter can 10508 // happen only if out-of-flows that we thought had no containing block 10509 // acquired one when ancestor inline frames and {ib} splits got 10510 // constructed). Just put all the kids into the single inline frame and 10511 // bail. 10512 newFrame->SetInitialChildList(FrameChildListID::Principal, 10513 std::move(childList)); 10514 aState.AddChild(newFrame, aFrameList, content, aParentFrame); 10515 return newFrame; 10516 } 10517 10518 // This inline frame contains several types of children. Therefore this frame 10519 // has to be chopped into several pieces, as described above. 10520 10521 // Grab the first inline's kids 10522 nsFrameList firstInlineKids = childList.TakeFramesBefore(firstBlock); 10523 newFrame->SetInitialChildList(FrameChildListID::Principal, 10524 std::move(firstInlineKids)); 10525 10526 aFrameList.AppendFrame(nullptr, newFrame); 10527 10528 newFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES); 10529 CreateIBSiblings(aState, newFrame, isAbsPosCB, childList, aFrameList); 10530 10531 return newFrame; 10532 } 10533 10534 void nsCSSFrameConstructor::CreateIBSiblings(nsFrameConstructorState& aState, 10535 nsContainerFrame* aInitialInline, 10536 bool aIsAbsPosCB, 10537 nsFrameList& aChildList, 10538 nsFrameList& aSiblings) { 10539 MOZ_ASSERT(aIsAbsPosCB == aInitialInline->IsAbsPosContainingBlock()); 10540 10541 nsIContent* content = aInitialInline->GetContent(); 10542 ComputedStyle* computedStyle = aInitialInline->Style(); 10543 nsContainerFrame* parentFrame = aInitialInline->GetParent(); 10544 10545 // Resolve the right style for our anonymous blocks. 10546 // 10547 // The distinction in styles is needed because of CSS 2.1, section 10548 // 9.2.1.1, which says: 10549 // 10550 // When such an inline box is affected by relative positioning, any 10551 // resulting translation also affects the block-level box contained 10552 // in the inline box. 10553 RefPtr<ComputedStyle> blockSC = 10554 mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle( 10555 PseudoStyleType::mozBlockInsideInlineWrapper, computedStyle); 10556 10557 nsContainerFrame* lastNewInline = 10558 static_cast<nsContainerFrame*>(aInitialInline->FirstContinuation()); 10559 do { 10560 // On entry to this loop aChildList is not empty and the first frame in it 10561 // is block-level. 10562 MOZ_ASSERT(aChildList.NotEmpty(), "Should have child items"); 10563 MOZ_ASSERT(aChildList.FirstChild()->IsBlockOutside(), 10564 "Must have list starting with block"); 10565 10566 // The initial run of blocks belongs to an anonymous block that we create 10567 // right now. The anonymous block will be the parent of these block 10568 // children of the inline. 10569 nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, blockSC); 10570 InitAndRestoreFrame(aState, content, parentFrame, blockFrame, false); 10571 if (aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) { 10572 blockFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR); 10573 } 10574 10575 // Find the first non-block child which defines the end of our block kids 10576 // and the start of our next inline's kids 10577 nsFrameList blockKids = 10578 aChildList.Split([](nsIFrame* f) { return !f->IsBlockOutside(); }); 10579 10580 if (!aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) { 10581 MoveChildrenTo(aInitialInline, blockFrame, blockKids); 10582 10583 SetFrameIsIBSplit(lastNewInline, blockFrame); 10584 aSiblings.AppendFrame(nullptr, blockFrame); 10585 } else { 10586 // Extract any initial non-column-span frames, and put them in 10587 // blockFrame's child list. 10588 nsFrameList initialNonColumnSpanKids = 10589 blockKids.Split([](nsIFrame* f) { return f->IsColumnSpan(); }); 10590 MoveChildrenTo(aInitialInline, blockFrame, initialNonColumnSpanKids); 10591 10592 SetFrameIsIBSplit(lastNewInline, blockFrame); 10593 aSiblings.AppendFrame(nullptr, blockFrame); 10594 10595 if (blockKids.NotEmpty()) { 10596 // Although SetFrameIsIBSplit() will add NS_FRAME_PART_OF_IBSPLIT for 10597 // blockFrame later, we manually add the bit earlier here to make all 10598 // the continuations of blockFrame created in 10599 // CreateColumnSpanSiblings(), i.e. non-column-span wrappers, have the 10600 // bit via nsIFrame::Init(). 10601 blockFrame->AddStateBits(NS_FRAME_PART_OF_IBSPLIT); 10602 10603 nsFrameList columnSpanSiblings = 10604 CreateColumnSpanSiblings(aState, blockFrame, blockKids, 10605 aIsAbsPosCB ? aInitialInline : nullptr); 10606 aSiblings.AppendFrames(nullptr, std::move(columnSpanSiblings)); 10607 } 10608 } 10609 10610 // Now grab the initial inlines in aChildList and put them into an inline 10611 // frame. 10612 nsInlineFrame* inlineFrame = NS_NewInlineFrame(mPresShell, computedStyle); 10613 InitAndRestoreFrame(aState, content, parentFrame, inlineFrame, false); 10614 inlineFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN); 10615 if (aInitialInline->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) { 10616 inlineFrame->AddStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR); 10617 } 10618 10619 if (aIsAbsPosCB) { 10620 inlineFrame->MarkAsAbsoluteContainingBlock(); 10621 } 10622 10623 if (aChildList.NotEmpty()) { 10624 nsFrameList inlineKids = 10625 aChildList.Split([](nsIFrame* f) { return f->IsBlockOutside(); }); 10626 MoveChildrenTo(aInitialInline, inlineFrame, inlineKids); 10627 } 10628 10629 SetFrameIsIBSplit(blockFrame, inlineFrame); 10630 aSiblings.AppendFrame(nullptr, inlineFrame); 10631 lastNewInline = inlineFrame; 10632 } while (aChildList.NotEmpty()); 10633 10634 SetFrameIsIBSplit(lastNewInline, nullptr); 10635 } 10636 10637 void nsCSSFrameConstructor::BuildInlineChildItems( 10638 nsFrameConstructorState& aState, FrameConstructionItem& aParentItem, 10639 bool aItemIsWithinSVGText, bool aItemAllowsTextPathChild) { 10640 ComputedStyle* const parentStyle = aParentItem.mComputedStyle; 10641 nsIContent* const parentContent = aParentItem.mContent; 10642 10643 if (!aItemIsWithinSVGText) { 10644 if (parentStyle->StyleDisplay()->IsListItem()) { 10645 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(), 10646 *parentStyle, PseudoStyleType::marker, 10647 aParentItem.mChildItems); 10648 } 10649 // Probe for generated content before 10650 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(), 10651 *parentStyle, PseudoStyleType::before, 10652 aParentItem.mChildItems); 10653 } 10654 10655 ItemFlags flags; 10656 if (aItemIsWithinSVGText) { 10657 flags += ItemFlag::IsWithinSVGText; 10658 } 10659 if (aItemAllowsTextPathChild && 10660 aParentItem.mContent->IsSVGElement(nsGkAtoms::a)) { 10661 flags += ItemFlag::AllowTextPathChild; 10662 } 10663 10664 FlattenedChildIterator iter(parentContent); 10665 for (nsIContent* content = iter.GetNextChild(); content; 10666 content = iter.GetNextChild()) { 10667 AddFrameConstructionItems(aState, content, iter.ShadowDOMInvolved(), 10668 *parentStyle, InsertionPoint(), 10669 aParentItem.mChildItems, flags); 10670 } 10671 10672 if (!aItemIsWithinSVGText) { 10673 // Probe for generated content after 10674 CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(), 10675 *parentStyle, PseudoStyleType::after, 10676 aParentItem.mChildItems); 10677 } 10678 10679 aParentItem.mIsAllInline = aParentItem.mChildItems.AreAllItemsInline(); 10680 } 10681 10682 // return whether it's ok to append (in the AppendFrames sense) to 10683 // aParentFrame if our nextSibling is aNextSibling. aParentFrame must 10684 // be an ib-split inline. 10685 static bool IsSafeToAppendToIBSplitInline(nsIFrame* aParentFrame, 10686 nsIFrame* aNextSibling) { 10687 MOZ_ASSERT(IsInlineFrame(aParentFrame), "Must have an inline parent here"); 10688 10689 do { 10690 NS_ASSERTION(IsFramePartOfIBSplit(aParentFrame), 10691 "How is this not part of an ib-split?"); 10692 if (aNextSibling || aParentFrame->GetNextContinuation() || 10693 GetIBSplitSibling(aParentFrame)) { 10694 return false; 10695 } 10696 10697 aNextSibling = aParentFrame->GetNextSibling(); 10698 aParentFrame = aParentFrame->GetParent(); 10699 } while (IsInlineFrame(aParentFrame)); 10700 10701 return true; 10702 } 10703 10704 bool nsCSSFrameConstructor::WipeInsertionParent(nsContainerFrame* aFrame) { 10705 #define TRACE(reason) \ 10706 PROFILER_MARKER("WipeInsertionParent: " reason, LAYOUT, {}, Tracing, \ 10707 "Layout"); 10708 10709 const LayoutFrameType frameType = aFrame->Type(); 10710 10711 // FIXME(emilio): This looks terribly inefficient if you insert elements deep 10712 // in a MathML subtree. 10713 if (aFrame->IsMathMLFrame()) { 10714 TRACE("MathML"); 10715 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async); 10716 return true; 10717 } 10718 10719 // A ruby-related frame that's getting new children. 10720 // The situation for ruby is complex, especially when interacting with 10721 // spaces. It contains these two special cases apart from tables: 10722 // 1) There are effectively three types of white spaces in ruby frames 10723 // we handle differently: leading/tailing/inter-level space, 10724 // inter-base/inter-annotation space, and inter-segment space. 10725 // These three types of spaces can be converted to each other when 10726 // their sibling changes. 10727 // 2) The first effective child of a ruby frame must always be a ruby 10728 // base container. It should be created or destroyed accordingly. 10729 if (IsRubyPseudo(aFrame) || frameType == LayoutFrameType::Ruby || 10730 RubyUtils::IsRubyContainerBox(frameType)) { 10731 // We want to optimize it better, and avoid reframing as much as 10732 // possible. But given the cases above, and the fact that a ruby 10733 // usually won't be very large, it should be fine to reframe it. 10734 TRACE("Ruby"); 10735 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async); 10736 return true; 10737 } 10738 10739 // Reframe the multi-column container whenever elements insert/append 10740 // into it because we need to reconstruct column-span split. 10741 if (aFrame->IsColumnSetWrapperFrame()) { 10742 TRACE("Multi-column"); 10743 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async); 10744 return true; 10745 } 10746 10747 return false; 10748 10749 #undef TRACE 10750 } 10751 10752 bool nsCSSFrameConstructor::WipeContainingBlock( 10753 nsFrameConstructorState& aState, nsIFrame* aContainingBlock, 10754 nsIFrame* aFrame, FrameConstructionItemList& aItems, bool aIsAppend, 10755 nsIFrame* aPrevSibling) { 10756 #define TRACE(reason) \ 10757 PROFILER_MARKER("WipeContainingBlock: " reason, LAYOUT, {}, Tracing, \ 10758 "Layout"); 10759 10760 if (aItems.IsEmpty()) { 10761 return false; 10762 } 10763 10764 // Before we go and append the frames, we must check for several 10765 // special situations. 10766 10767 if (aFrame->GetContent() == mDocument->GetRootElement()) { 10768 // Situation #1 is when we insert content that becomes the canonical body 10769 // element, and its used WritingMode is different from the root element's 10770 // used WritingMode. 10771 // We need to reframe the root element so that the root element's frames has 10772 // the correct writing-mode propagated from body element. (See 10773 // nsCSSFrameConstructor::ConstructDocElementFrame.) 10774 // 10775 // Bug 1594297: When inserting a new <body>, we may need to reframe the old 10776 // <body> which has a "overflow" value other than simple "visible". But it's 10777 // tricky, see bug 1593752. 10778 nsIContent* bodyElement = mDocument->GetBodyElement(); 10779 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) { 10780 const WritingMode bodyWM(iter.item().mComputedStyle); 10781 if (iter.item().mContent == bodyElement && 10782 bodyWM != aFrame->GetWritingMode()) { 10783 TRACE("Root"); 10784 RecreateFramesForContent(mDocument->GetRootElement(), 10785 InsertionKind::Async); 10786 return true; 10787 } 10788 } 10789 } 10790 10791 nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling); 10792 10793 // Situation #2 is a flex / grid container frame into which we're inserting 10794 // new inline non-replaced children, adjacent to an existing anonymous flex or 10795 // grid item. 10796 if (aFrame->IsFlexOrGridContainer()) { 10797 FCItemIterator iter(aItems); 10798 10799 // Check if we're adding to-be-wrapped content right *after* an existing 10800 // anonymous flex or grid item (which would need to absorb this content). 10801 const bool isLegacyWebKitBox = IsFlexContainerForLegacyWebKitBox(aFrame); 10802 if (aPrevSibling && IsAnonymousItem(aPrevSibling) && 10803 iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyWebKitBox)) { 10804 TRACE("Inserting inline after anon flex or grid item"); 10805 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async); 10806 return true; 10807 } 10808 10809 // Check if we're adding to-be-wrapped content right *before* an existing 10810 // anonymous flex or grid item (which would need to absorb this content). 10811 if (nextSibling && IsAnonymousItem(nextSibling)) { 10812 // Jump to the last entry in the list 10813 iter.SetToEnd(); 10814 iter.Prev(); 10815 if (iter.item().NeedsAnonFlexOrGridItem(aState, isLegacyWebKitBox)) { 10816 TRACE("Inserting inline before anon flex or grid item"); 10817 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async); 10818 return true; 10819 } 10820 } 10821 } 10822 10823 // Situation #3 is an anonymous flex or grid item that's getting new children 10824 // who don't want to be wrapped. 10825 if (IsAnonymousItem(aFrame)) { 10826 AssertAnonymousFlexOrGridItemParent(aFrame, aFrame->GetParent()); 10827 10828 // We need to push a null float containing block to be sure that 10829 // "NeedsAnonFlexOrGridItem" will know we're not honoring floats for this 10830 // inserted content. (In particular, this is necessary in order for 10831 // its "GetGeometricParent" call to return the correct result.) 10832 // We're not honoring floats on this content because it has the 10833 // _flex/grid container_ as its parent in the content tree. 10834 nsFrameConstructorSaveState floatSaveState; 10835 aState.PushFloatContainingBlock(nullptr, floatSaveState); 10836 10837 FCItemIterator iter(aItems); 10838 // Skip over things that _do_ need an anonymous flex item, because 10839 // they're perfectly happy to go here -- they won't cause a reframe. 10840 nsIFrame* containerFrame = aFrame->GetParent(); 10841 const bool isLegacyWebKitBox = 10842 IsFlexContainerForLegacyWebKitBox(containerFrame); 10843 if (!iter.SkipItemsThatNeedAnonFlexOrGridItem(aState, isLegacyWebKitBox)) { 10844 // We hit something that _doesn't_ need an anonymous flex item! 10845 // Rebuild the flex container to bust it out. 10846 TRACE("Inserting non-inlines inside anon flex or grid item"); 10847 RecreateFramesForContent(containerFrame->GetContent(), 10848 InsertionKind::Async); 10849 return true; 10850 } 10851 10852 // If we get here, then everything in |aItems| needs to be wrapped in 10853 // an anonymous flex or grid item. That's where it's already going - good! 10854 } 10855 10856 // Situation #4 is a case when table pseudo-frames don't work out right 10857 ParentType parentType = GetParentType(aFrame); 10858 // If all the kids want a parent of the type that aFrame is, then we're all 10859 // set to go. Indeed, there won't be any table pseudo-frames created between 10860 // aFrame and the kids, so those won't need to be merged with any table 10861 // pseudo-frames that might already be kids of aFrame. If aFrame itself is a 10862 // table pseudo-frame, then all the kids in this list would have wanted a 10863 // frame of that type wrapping them anyway, so putting them inside it is ok. 10864 if (!aItems.AllWantParentType(parentType)) { 10865 // Don't give up yet. If parentType is not eTypeBlock and the parent is 10866 // not a generated content frame, then try filtering whitespace out of the 10867 // list. 10868 if (parentType != eTypeBlock && !aFrame->IsGeneratedContentFrame()) { 10869 // For leading whitespace followed by a kid that wants our parent type, 10870 // there are four cases: 10871 // 1) We have a previous sibling which is not a table pseudo. That means 10872 // that previous sibling wanted a (non-block) parent of the type we're 10873 // looking at. Then the whitespace comes between two table-internal 10874 // elements, so should be collapsed out. 10875 // 2) We have a previous sibling which is a table pseudo. It might have 10876 // kids who want this whitespace, so we need to reframe. 10877 // 3) We have no previous sibling and our parent frame is not a table 10878 // pseudo. That means that we'll be at the beginning of our actual 10879 // non-block-type parent, and the whitespace is OK to collapse out. 10880 // If something is ever inserted before us, it'll find our own parent 10881 // as its parent and if it's something that would care about the 10882 // whitespace it'll want a block parent, so it'll trigger a reframe at 10883 // that point. 10884 // 4) We have no previous sibling and our parent frame is a table pseudo. 10885 // Need to reframe. 10886 // All that is predicated on finding the correct previous sibling. We 10887 // might have to walk backwards along continuations from aFrame to do so. 10888 // 10889 // It's always OK to drop whitespace between any two items that want a 10890 // parent of type parentType. 10891 // 10892 // For trailing whitespace preceded by a kid that wants our parent type, 10893 // there are four cases: 10894 // 1) We have a next sibling which is not a table pseudo. That means 10895 // that next sibling wanted a (non-block) parent of the type we're 10896 // looking at. Then the whitespace comes between two table-internal 10897 // elements, so should be collapsed out. 10898 // 2) We have a next sibling which is a table pseudo. It might have 10899 // kids who want this whitespace, so we need to reframe. 10900 // 3) We have no next sibling and our parent frame is not a table 10901 // pseudo. That means that we'll be at the end of our actual 10902 // non-block-type parent, and the whitespace is OK to collapse out. 10903 // If something is ever inserted after us, it'll find our own parent 10904 // as its parent and if it's something that would care about the 10905 // whitespace it'll want a block parent, so it'll trigger a reframe at 10906 // that point. 10907 // 4) We have no next sibling and our parent frame is a table pseudo. 10908 // Need to reframe. 10909 // All that is predicated on finding the correct next sibling. We might 10910 // have to walk forward along continuations from aFrame to do so. That 10911 // said, in the case when nextSibling is null at this point and aIsAppend 10912 // is true, we know we're in case 3. Furthermore, in that case we don't 10913 // even have to worry about the table pseudo situation; we know our 10914 // parent is not a table pseudo there. 10915 FCItemIterator iter(aItems); 10916 FCItemIterator start(iter); 10917 do { 10918 if (iter.SkipItemsWantingParentType(parentType)) { 10919 break; 10920 } 10921 10922 // iter points to an item that wants a different parent. If it's not 10923 // whitespace, we're done; no more point scanning the list. 10924 if (!iter.item().IsWhitespace(aState)) { 10925 break; 10926 } 10927 10928 if (iter == start) { 10929 // Leading whitespace. How to handle this depends on our 10930 // previous sibling and aFrame. See the long comment above. 10931 nsIFrame* prevSibling = aPrevSibling; 10932 if (!prevSibling) { 10933 // Try to find one after all 10934 nsIFrame* parentPrevCont = aFrame->GetPrevContinuation(); 10935 while (parentPrevCont) { 10936 prevSibling = parentPrevCont->PrincipalChildList().LastChild(); 10937 if (prevSibling) { 10938 break; 10939 } 10940 parentPrevCont = parentPrevCont->GetPrevContinuation(); 10941 } 10942 }; 10943 if (prevSibling) { 10944 if (IsTablePseudo(prevSibling)) { 10945 // need to reframe 10946 break; 10947 } 10948 } else if (IsTablePseudo(aFrame)) { 10949 // need to reframe 10950 break; 10951 } 10952 } 10953 10954 FCItemIterator spaceEndIter(iter); 10955 // Advance spaceEndIter past any whitespace 10956 bool trailingSpaces = spaceEndIter.SkipWhitespace(aState); 10957 10958 bool okToDrop; 10959 if (trailingSpaces) { 10960 // Trailing whitespace. How to handle this depeds on aIsAppend, our 10961 // next sibling and aFrame. See the long comment above. 10962 okToDrop = aIsAppend && !nextSibling; 10963 if (!okToDrop) { 10964 if (!nextSibling) { 10965 // Try to find one after all 10966 nsIFrame* parentNextCont = aFrame->GetNextContinuation(); 10967 while (parentNextCont) { 10968 nextSibling = parentNextCont->PrincipalChildList().FirstChild(); 10969 if (nextSibling) { 10970 break; 10971 } 10972 parentNextCont = parentNextCont->GetNextContinuation(); 10973 } 10974 } 10975 10976 okToDrop = (nextSibling && !IsTablePseudo(nextSibling)) || 10977 (!nextSibling && !IsTablePseudo(aFrame)); 10978 } else { 10979 NS_ASSERTION(!IsTablePseudo(aFrame), "How did that happen?"); 10980 } 10981 } else { 10982 okToDrop = (spaceEndIter.item().DesiredParentType() == parentType); 10983 } 10984 10985 if (okToDrop) { 10986 iter.DeleteItemsTo(this, spaceEndIter); 10987 } else { 10988 // We're done: we don't want to drop the whitespace, and it has the 10989 // wrong parent type. 10990 break; 10991 } 10992 10993 // Now loop, since |iter| points to item right after the whitespace we 10994 // removed. 10995 } while (!iter.IsDone()); 10996 } 10997 10998 // We might be able to figure out some sort of optimizations here, but they 10999 // would have to depend on having a correct aPrevSibling and a correct next 11000 // sibling. For example, we can probably avoid reframing if none of 11001 // aFrame, aPrevSibling, and next sibling are table pseudo-frames. But it 11002 // doesn't seem worth it to worry about that for now, especially since we 11003 // in fact do not have a reliable aPrevSibling, nor any next sibling, in 11004 // this method. 11005 11006 // aItems might have changed, so recheck the parent type thing. In fact, 11007 // it might be empty, so recheck that too. 11008 if (aItems.IsEmpty()) { 11009 return false; 11010 } 11011 11012 // If aFrame is empty, the insertion process will be able to take care of 11013 // creating any needed pseudo-parents. 11014 if (!aItems.AllWantParentType(parentType) && 11015 !SafeToInsertPseudoNeedingChildren(aFrame)) { 11016 // Reframing aFrame->GetContent() is good enough, since the content of 11017 // table pseudo-frames is the ancestor content. 11018 TRACE("Pseudo-frames going wrong"); 11019 RecreateFramesForContent(aFrame->GetContent(), InsertionKind::Async); 11020 return true; 11021 } 11022 } 11023 11024 // Situation #5 is a frame in multicol subtree that's getting new children. 11025 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR)) { 11026 bool anyColumnSpanItems = false; 11027 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) { 11028 if (iter.item().mComputedStyle->StyleColumn()->IsColumnSpanStyle()) { 11029 anyColumnSpanItems = true; 11030 break; 11031 } 11032 } 11033 11034 bool needsReframe = 11035 // 1. Insert / append any column-span children. 11036 anyColumnSpanItems || 11037 // 2. GetInsertionPrevSibling() modifies insertion parent. If the prev 11038 // sibling is a column-span, aFrame ends up being the 11039 // column-span-wrapper. 11040 aFrame->Style()->GetPseudoType() == 11041 PseudoStyleType::columnSpanWrapper || 11042 // 3. Append into {ib} split container. There might be room for 11043 // optimization, but let's reframe for correctness... 11044 IsFramePartOfIBSplit(aFrame); 11045 11046 if (needsReframe) { 11047 TRACE("Multi-column"); 11048 RecreateFramesForContent( 11049 GetMultiColumnContainingBlockFor(aFrame)->GetContent(), 11050 InsertionKind::Async); 11051 return true; 11052 } 11053 11054 // If we get here, then we need further check for {ib} split to decide 11055 // whether to reframe. For example, appending a block into an empty inline 11056 // that is not part of an {ib} split, but should become an {ib} split. 11057 } 11058 11059 // A <fieldset> may need to pick up a new rendered legend from aItems. 11060 // We currently can't handle this case without recreating frames for 11061 // the fieldset. 11062 // XXXmats we should be able to optimize this when the fieldset doesn't 11063 // currently have a rendered legend. ContentRangeInserted needs to be fixed 11064 // to use the inner frame as the content insertion frame in that case. 11065 if (const auto* fieldset = GetFieldSetFrameFor(aFrame)) { 11066 // Check if any item is eligible to be a rendered legend. 11067 for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) { 11068 const auto& item = iter.item(); 11069 if (!item.mContent->IsHTMLElement(nsGkAtoms::legend)) { 11070 continue; 11071 } 11072 const auto* display = item.mComputedStyle->StyleDisplay(); 11073 if (display->IsFloatingStyle() || 11074 display->IsAbsolutelyPositionedStyle()) { 11075 continue; 11076 } 11077 TRACE("Fieldset with rendered legend"); 11078 RecreateFramesForContent(fieldset->GetContent(), InsertionKind::Async); 11079 return true; 11080 } 11081 } 11082 11083 // Now we have several cases involving {ib} splits. Put them all in a 11084 // do/while with breaks to take us to the "go and reconstruct" code. 11085 do { 11086 if (IsInlineFrame(aFrame)) { 11087 if (aItems.AreAllItemsInline()) { 11088 // We can just put the kids in. 11089 return false; 11090 } 11091 11092 if (!IsFramePartOfIBSplit(aFrame)) { 11093 // Need to go ahead and reconstruct. 11094 break; 11095 } 11096 11097 // Now we're adding kids including some blocks to an inline part of an 11098 // {ib} split. If we plan to call AppendFrames, and don't have a next 11099 // sibling for the new frames, and our parent is the last continuation of 11100 // the last part of the {ib} split, and the same is true of all our 11101 // ancestor inlines (they have no following continuations and they're the 11102 // last part of their {ib} splits and we'd be adding to the end for all 11103 // of them), then AppendFrames will handle things for us. Bail out in 11104 // that case. 11105 if (aIsAppend && IsSafeToAppendToIBSplitInline(aFrame, nextSibling)) { 11106 return false; 11107 } 11108 11109 // Need to reconstruct. 11110 break; 11111 } 11112 11113 // Now we know we have a block parent. If it's not part of an 11114 // ib-split, we're all set. 11115 if (!IsFramePartOfIBSplit(aFrame)) { 11116 return false; 11117 } 11118 11119 // We're adding some kids to a block part of an {ib} split. If all the 11120 // kids are blocks, we don't need to reconstruct. 11121 if (aItems.AreAllItemsBlock()) { 11122 return false; 11123 } 11124 11125 // We might have some inline kids for this block. Just fall out of the 11126 // loop and reconstruct. 11127 } while (false); 11128 11129 // If we don't have a containing block, start with aFrame and look for one. 11130 if (!aContainingBlock) { 11131 aContainingBlock = aFrame; 11132 } 11133 11134 // To find the right block to reframe, just walk up the tree until we find a 11135 // frame that is: 11136 // 1) Not part of an IB split 11137 // 2) Not a pseudo-frame 11138 // 3) Not an inline frame 11139 // We're guaranteed to find one, since ComputedStyle::ApplyStyleFixups 11140 // enforces that the root is display:none, display:table, or display:block. 11141 // Note that walking up "too far" is OK in terms of correctness, even if it 11142 // might be a little inefficient. This is why we walk out of all 11143 // pseudo-frames -- telling which ones are or are not OK to walk out of is 11144 // too hard (and I suspect that we do in fact need to walk out of all of 11145 // them). 11146 while (IsFramePartOfIBSplit(aContainingBlock) || 11147 aContainingBlock->IsInlineOutside() || 11148 aContainingBlock->Style()->IsPseudoOrAnonBox()) { 11149 aContainingBlock = aContainingBlock->GetParent(); 11150 NS_ASSERTION(aContainingBlock, 11151 "Must have non-inline, non-ib-split, non-pseudo frame as " 11152 "root (or child of root, for a table root)!"); 11153 } 11154 11155 // Tell parent of the containing block to reformulate the 11156 // entire block. This is painful and definitely not optimal 11157 // but it will *always* get the right answer. 11158 11159 nsIContent* blockContent = aContainingBlock->GetContent(); 11160 TRACE("IB splits"); 11161 RecreateFramesForContent(blockContent, InsertionKind::Async); 11162 return true; 11163 #undef TRACE 11164 } 11165 11166 void nsCSSFrameConstructor::ReframeContainingBlock(nsIFrame* aFrame) { 11167 // XXXbz how exactly would we get here while isReflowing anyway? Should this 11168 // whole test be ifdef DEBUG? 11169 if (mPresShell->IsReflowLocked()) { 11170 // don't ReframeContainingBlock, this will result in a crash 11171 // if we remove a tree that's in reflow - see bug 121368 for testcase 11172 NS_ERROR( 11173 "Atemptted to nsCSSFrameConstructor::ReframeContainingBlock during a " 11174 "Reflow!!!"); 11175 return; 11176 } 11177 11178 // Get the first "normal" ancestor of the target frame. 11179 nsIFrame* containingBlock = GetIBContainingBlockFor(aFrame); 11180 if (containingBlock) { 11181 // From here we look for the containing block in case the target 11182 // frame is already a block (which can happen when an inline frame 11183 // wraps some of its content in an anonymous block; see 11184 // ConstructInline) 11185 11186 // NOTE: We used to get the FloatContainingBlock here, but it was often 11187 // wrong. GetIBContainingBlock works much better and provides the correct 11188 // container in all cases so GetFloatContainingBlock(aFrame) has been 11189 // removed 11190 11191 // And get the containingBlock's content 11192 if (nsIContent* blockContent = containingBlock->GetContent()) { 11193 #ifdef DEBUG 11194 if (gNoisyContentUpdates) { 11195 printf(" ==> blockContent=%p\n", blockContent); 11196 } 11197 #endif 11198 RecreateFramesForContent(blockContent, InsertionKind::Async); 11199 return; 11200 } 11201 } 11202 11203 // If we get here, we're screwed! 11204 RecreateFramesForContent(mPresShell->GetDocument()->GetRootElement(), 11205 InsertionKind::Async); 11206 } 11207 11208 ////////////////////////////////////////////////////////// 11209 // nsCSSFrameConstructor::FrameConstructionItem methods // 11210 ////////////////////////////////////////////////////////// 11211 bool nsCSSFrameConstructor::FrameConstructionItem::IsWhitespace( 11212 nsFrameConstructorState& aState) const { 11213 MOZ_ASSERT(aState.mCreatingExtraFrames || !mContent->GetPrimaryFrame(), 11214 "How did that happen?"); 11215 if (!mIsText) { 11216 return false; 11217 } 11218 mContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | 11219 NS_REFRAME_IF_WHITESPACE); 11220 return mContent->TextIsOnlyWhitespace(); 11221 } 11222 11223 ////////////////////////////////////////////////////////////// 11224 // nsCSSFrameConstructor::FrameConstructionItemList methods // 11225 ////////////////////////////////////////////////////////////// 11226 void nsCSSFrameConstructor::FrameConstructionItemList::AdjustCountsForItem( 11227 FrameConstructionItem* aItem, int32_t aDelta) { 11228 MOZ_ASSERT(aDelta == 1 || aDelta == -1, "Unexpected delta"); 11229 mItemCount += aDelta; 11230 if (aItem->mIsAllInline) { 11231 mInlineCount += aDelta; 11232 } 11233 if (aItem->mIsBlock) { 11234 mBlockCount += aDelta; 11235 } 11236 mDesiredParentCounts[aItem->DesiredParentType()] += aDelta; 11237 } 11238 11239 //////////////////////////////////////////////////////////////////////// 11240 // nsCSSFrameConstructor::FrameConstructionItemList::Iterator methods // 11241 //////////////////////////////////////////////////////////////////////// 11242 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator:: 11243 SkipItemsWantingParentType(ParentType aParentType) { 11244 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet"); 11245 while (item().DesiredParentType() == aParentType) { 11246 Next(); 11247 if (IsDone()) { 11248 return true; 11249 } 11250 } 11251 return false; 11252 } 11253 11254 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator:: 11255 SkipItemsNotWantingParentType(ParentType aParentType) { 11256 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet"); 11257 while (item().DesiredParentType() != aParentType) { 11258 Next(); 11259 if (IsDone()) { 11260 return true; 11261 } 11262 } 11263 return false; 11264 } 11265 11266 // Note: we implement -webkit-{inline-}box using nsFlexContainerFrame, but we 11267 // use different rules for what gets wrapped in an anonymous flex item. 11268 bool nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexOrGridItem( 11269 const nsFrameConstructorState& aState, bool aIsLegacyWebKitBox) { 11270 if (mFCData->mBits & FCDATA_IS_LINE_PARTICIPANT) { 11271 // This will be an inline non-replaced box. 11272 return true; 11273 } 11274 11275 if (aIsLegacyWebKitBox) { 11276 if (mComputedStyle->StyleDisplay()->IsInlineOutsideStyle()) { 11277 // In an emulated legacy box, all inline-level content gets wrapped in an 11278 // anonymous flex item. 11279 return true; 11280 } 11281 if (mIsPopup || 11282 (!(mFCData->mBits & FCDATA_DISALLOW_OUT_OF_FLOW) && 11283 aState.GetGeometricParent(*mComputedStyle->StyleDisplay(), nullptr))) { 11284 // We're abspos or fixedpos (or a XUL popup), which means we'll spawn a 11285 // placeholder which (because our container is an emulated legacy box) 11286 // we'll need to wrap in an anonymous flex item. So, we just treat 11287 // _this_ frame as if _it_ needs to be wrapped in an anonymous flex item, 11288 // and then when we spawn the placeholder, it'll end up in the right 11289 // spot. 11290 return true; 11291 } 11292 } 11293 11294 return false; 11295 } 11296 11297 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator:: 11298 SkipItemsThatNeedAnonFlexOrGridItem(const nsFrameConstructorState& aState, 11299 bool aIsLegacyWebKitBox) { 11300 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet"); 11301 while (item().NeedsAnonFlexOrGridItem(aState, aIsLegacyWebKitBox)) { 11302 Next(); 11303 if (IsDone()) { 11304 return true; 11305 } 11306 } 11307 return false; 11308 } 11309 11310 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator:: 11311 SkipItemsThatDontNeedAnonFlexOrGridItem( 11312 const nsFrameConstructorState& aState, bool aIsLegacyWebKitBox) { 11313 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet"); 11314 while (!(item().NeedsAnonFlexOrGridItem(aState, aIsLegacyWebKitBox))) { 11315 Next(); 11316 if (IsDone()) { 11317 return true; 11318 } 11319 } 11320 return false; 11321 } 11322 11323 inline bool nsCSSFrameConstructor::FrameConstructionItemList::Iterator:: 11324 SkipItemsNotWantingRubyParent() { 11325 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet"); 11326 while (!IsRubyParentType(item().DesiredParentType())) { 11327 Next(); 11328 if (IsDone()) { 11329 return true; 11330 } 11331 } 11332 return false; 11333 } 11334 11335 inline bool 11336 nsCSSFrameConstructor::FrameConstructionItemList::Iterator::SkipWhitespace( 11337 nsFrameConstructorState& aState) { 11338 MOZ_ASSERT(!IsDone(), "Shouldn't be done yet"); 11339 MOZ_ASSERT(item().IsWhitespace(aState), "Not pointing to whitespace?"); 11340 do { 11341 Next(); 11342 if (IsDone()) { 11343 return true; 11344 } 11345 } while (item().IsWhitespace(aState)); 11346 11347 return false; 11348 } 11349 11350 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator:: 11351 AppendItemToList(FrameConstructionItemList& aTargetList) { 11352 NS_ASSERTION(&aTargetList != &mList, "Unexpected call"); 11353 MOZ_ASSERT(!IsDone(), "should not be done"); 11354 11355 FrameConstructionItem* item = mCurrent; 11356 Next(); 11357 item->remove(); 11358 aTargetList.mItems.insertBack(item); 11359 11360 mList.AdjustCountsForItem(item, -1); 11361 aTargetList.AdjustCountsForItem(item, 1); 11362 } 11363 11364 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator:: 11365 AppendItemsToList(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd, 11366 FrameConstructionItemList& aTargetList) { 11367 NS_ASSERTION(&aTargetList != &mList, "Unexpected call"); 11368 MOZ_ASSERT(&mList == &aEnd.mList, "End iterator for some other list?"); 11369 11370 // We can't just move our guts to the other list if it already has 11371 // some information or if we're not moving our entire list. 11372 if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty()) { 11373 do { 11374 AppendItemToList(aTargetList); 11375 } while (*this != aEnd); 11376 return; 11377 } 11378 11379 // Move our entire list of items into the empty target list. 11380 aTargetList.mItems = std::move(mList.mItems); 11381 11382 // Copy over the various counters 11383 aTargetList.mInlineCount = mList.mInlineCount; 11384 aTargetList.mBlockCount = mList.mBlockCount; 11385 aTargetList.mItemCount = mList.mItemCount; 11386 memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts, 11387 sizeof(aTargetList.mDesiredParentCounts)); 11388 11389 // reset mList 11390 mList.Reset(aFCtor); 11391 11392 // Point ourselves to aEnd, as advertised 11393 SetToEnd(); 11394 MOZ_ASSERT(*this == aEnd, "How did that happen?"); 11395 } 11396 11397 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::InsertItem( 11398 FrameConstructionItem* aItem) { 11399 if (IsDone()) { 11400 mList.mItems.insertBack(aItem); 11401 } else { 11402 // Just insert the item before us. There's no magic here. 11403 mCurrent->setPrevious(aItem); 11404 } 11405 mList.AdjustCountsForItem(aItem, 1); 11406 11407 MOZ_ASSERT(aItem->getNext() == mCurrent, "How did that happen?"); 11408 } 11409 11410 void nsCSSFrameConstructor::FrameConstructionItemList::Iterator::DeleteItemsTo( 11411 nsCSSFrameConstructor* aFCtor, const Iterator& aEnd) { 11412 MOZ_ASSERT(&mList == &aEnd.mList, "End iterator for some other list?"); 11413 MOZ_ASSERT(*this != aEnd, "Shouldn't be at aEnd yet"); 11414 11415 do { 11416 NS_ASSERTION(!IsDone(), "Ran off end of list?"); 11417 FrameConstructionItem* item = mCurrent; 11418 Next(); 11419 item->remove(); 11420 mList.AdjustCountsForItem(item, -1); 11421 item->Delete(aFCtor); 11422 } while (*this != aEnd); 11423 } 11424 11425 void nsCSSFrameConstructor::QuotesDirty() { 11426 mQuotesDirty = true; 11427 mPresShell->SetNeedLayoutFlush(); 11428 } 11429 11430 void nsCSSFrameConstructor::CountersDirty() { 11431 mCountersDirty = true; 11432 mPresShell->SetNeedLayoutFlush(); 11433 } 11434 11435 void* nsCSSFrameConstructor::AllocateFCItem() { 11436 void* item; 11437 if (mFirstFreeFCItem) { 11438 item = mFirstFreeFCItem; 11439 mFirstFreeFCItem = mFirstFreeFCItem->mNext; 11440 } else { 11441 item = mFCItemPool.Allocate(sizeof(FrameConstructionItem)); 11442 } 11443 ++mFCItemsInUse; 11444 return item; 11445 } 11446 11447 void nsCSSFrameConstructor::FreeFCItem(FrameConstructionItem* aItem) { 11448 MOZ_ASSERT(mFCItemsInUse != 0); 11449 if (--mFCItemsInUse == 0) { 11450 // The arena is now unused - clear it but retain one chunk. 11451 mFirstFreeFCItem = nullptr; 11452 mFCItemPool.Clear(); 11453 } else { 11454 // Prepend it to the list of free items. 11455 FreeFCItemLink* item = reinterpret_cast<FreeFCItemLink*>(aItem); 11456 item->mNext = mFirstFreeFCItem; 11457 mFirstFreeFCItem = item; 11458 } 11459 } 11460 11461 void nsCSSFrameConstructor::AddSizeOfIncludingThis( 11462 nsWindowSizes& aSizes) const { 11463 if (nsIFrame* rootFrame = GetRootFrame()) { 11464 rootFrame->AddSizeOfExcludingThisForTree(aSizes); 11465 if (RetainedDisplayListBuilder* builder = 11466 rootFrame->GetProperty(RetainedDisplayListBuilder::Cached())) { 11467 builder->AddSizeOfIncludingThis(aSizes); 11468 } 11469 } 11470 11471 // This must be done after measuring from the frame tree, since frame 11472 // manager will measure sizes of staled computed values and style 11473 // structs, which only make sense after we know what are being used. 11474 nsFrameManager::AddSizeOfIncludingThis(aSizes); 11475 11476 // Measurement of the following members may be added later if DMD finds it 11477 // is worthwhile: 11478 // - mFCItemPool 11479 // - mContainStyleScopeManager 11480 }