nsContainerFrame.h (48236B)
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 /* base class #1 for rendering objects that have child lists */ 8 9 #ifndef nsContainerFrame_h___ 10 #define nsContainerFrame_h___ 11 12 #include "LayoutConstants.h" 13 #include "mozilla/Attributes.h" 14 #include "nsFrameList.h" 15 #include "nsISelectionDisplay.h" 16 #include "nsLineBox.h" 17 #include "nsSplittableFrame.h" 18 #include "nsTHashSet.h" 19 20 class nsOverflowContinuationTracker; 21 22 namespace mozilla { 23 class PresShell; 24 struct StylePositionArea; 25 } // namespace mozilla 26 27 // Some macros for container classes to do sanity checking on 28 // width/height/x/y values computed during reflow. 29 // NOTE: AppUnitsPerCSSPixel value hardwired here to remove the 30 // dependency on nsDeviceContext.h. It doesn't matter if it's a 31 // little off. 32 #ifdef DEBUG 33 // 10 million pixels, converted to app units. Note that this a bit larger 34 // than 1/4 of nscoord_MAX. So, if any content gets to be this large, we're 35 // definitely in danger of grazing up against nscoord_MAX; hence, it's ABSURD. 36 # define ABSURD_COORD (10000000 * 60) 37 # define ABSURD_SIZE(_x) (((_x) < -ABSURD_COORD) || ((_x) > ABSURD_COORD)) 38 #endif 39 40 /** 41 * Implementation of a container frame. 42 */ 43 class nsContainerFrame : public nsSplittableFrame { 44 public: 45 NS_DECL_ABSTRACT_FRAME(nsContainerFrame) 46 NS_DECL_QUERYFRAME_TARGET(nsContainerFrame) 47 NS_DECL_QUERYFRAME 48 49 // nsIFrame overrides 50 nsContainerFrame* GetContentInsertionFrame() override { return this; } 51 52 const nsFrameList& GetChildList(ChildListID aList) const override; 53 void GetChildLists(nsTArray<ChildList>* aLists) const override; 54 void Destroy(DestroyContext&) override; 55 56 void ChildIsDirty(nsIFrame* aChild) override; 57 58 FrameSearchResult PeekOffsetNoAmount(bool aForward, 59 int32_t* aOffset) override; 60 FrameSearchResult PeekOffsetCharacter( 61 bool aForward, int32_t* aOffset, 62 PeekOffsetCharacterOptions aOptions = 63 PeekOffsetCharacterOptions()) override; 64 65 #ifdef DEBUG_FRAME_DUMP 66 void List(FILE* out = stderr, const char* aPrefix = "", 67 ListFlags aFlags = ListFlags()) const override; 68 void ListWithMatchedRules(FILE* out = stderr, 69 const char* aPrefix = "") const override; 70 void ListChildLists(FILE* aOut, const char* aPrefix, ListFlags aFlags, 71 ChildListIDs aSkippedListIDs) const; 72 virtual void ExtraContainerFrameInfo(nsACString& aTo, 73 bool aListOnlyDeterministic) const; 74 #endif 75 76 // nsContainerFrame methods 77 78 /** 79 * Called to set the initial list of frames. This happens after the frame 80 * has been initialized. 81 * 82 * This is only called once for a given child list, and won't be called 83 * at all for child lists with no initial list of frames. 84 * 85 * @param aListID the child list identifier. 86 * @param aChildList list of child frames. Each of the frames has its 87 * NS_FRAME_IS_DIRTY bit set. Must not be empty. 88 * This method cannot handle the child list returned by 89 * GetAbsoluteListID(). 90 * @see #Init() 91 */ 92 virtual void SetInitialChildList(ChildListID aListID, 93 nsFrameList&& aChildList); 94 95 /** 96 * This method is responsible for appending frames to the frame 97 * list. The implementation should append the frames to the specified 98 * child list and then generate a reflow command. 99 * 100 * @param aListID the child list identifier. 101 * @param aFrameList list of child frames to append. Each of the frames has 102 * its NS_FRAME_IS_DIRTY bit set. Must not be empty. 103 */ 104 virtual void AppendFrames(ChildListID aListID, nsFrameList&& aFrameList); 105 106 /** 107 * This method is responsible for inserting frames into the frame 108 * list. The implementation should insert the new frames into the specified 109 * child list and then generate a reflow command. 110 * 111 * @param aListID the child list identifier. 112 * @param aPrevFrame the frame to insert frames <b>after</b> 113 * @param aPrevFrameLine (optional) if present (i.e., not null), the line 114 * box that aPrevFrame is part of. 115 * @param aFrameList list of child frames to insert <b>after</b> aPrevFrame. 116 * Each of the frames has its NS_FRAME_IS_DIRTY bit set 117 */ 118 virtual void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, 119 const nsLineList::iterator* aPrevFrameLine, 120 nsFrameList&& aFrameList); 121 122 /** 123 * This method is responsible for removing a frame in the frame 124 * list. The implementation should do something with the removed frame 125 * and then generate a reflow command. The implementation is responsible 126 * for destroying the frame (the caller mustn't destroy it). 127 */ 128 virtual void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*); 129 130 /** 131 * Helper method to create next-in-flows if necessary. If aFrame 132 * already has a next-in-flow then this method does 133 * nothing. Otherwise, a new continuation frame is created and 134 * linked into the flow. In addition, the new frame is inserted 135 * into the principal child list after aFrame. 136 * @note calling this method on a block frame is illegal. Use 137 * nsBlockFrame::CreateContinuationFor() instead. 138 * @return the next-in-flow <b>if and only if</b> one is created. If 139 * a next-in-flow already exists, nullptr will be returned. 140 */ 141 nsIFrame* CreateNextInFlow(nsIFrame* aFrame); 142 143 /** 144 * Delete aNextInFlow and its next-in-flows. 145 * @param aDeletingEmptyFrames if set, then the reflow for aNextInFlow's 146 * content was complete before aNextInFlow, so aNextInFlow and its 147 * next-in-flows no longer map any real content. 148 */ 149 virtual void DeleteNextInFlowChild(DestroyContext&, nsIFrame* aNextInFlow, 150 bool aDeletingEmptyFrames); 151 152 /** 153 * Reparent aFrame from aOldParent to aNewParent. 154 */ 155 static void ReparentFrame(nsIFrame* aFrame, nsContainerFrame* aOldParent, 156 nsContainerFrame* aNewParent); 157 158 /** 159 * Reparent all the frames in aFrameList from aOldParent to aNewParent. 160 * 161 * Note: Reparenting a large frame list can be have huge performance impact. 162 * For example, instead of using this method, nsInlineFrame uses a "lazy 163 * reparenting" technique that it reparents a child frame just before 164 * reflowing the child. (See InlineReflowInput::mSetParentPointer.) 165 */ 166 static void ReparentFrames(nsFrameList& aFrameList, 167 nsContainerFrame* aOldParent, 168 nsContainerFrame* aNewParent); 169 170 /** 171 * Converts the minimum and maximum sizes given in inner window app units to 172 * outer window device pixel sizes and assigns these constraints to the 173 * widget. 174 * 175 * @param aPresContext pres context 176 * @param aWidget widget for this frame 177 * @param minimum size of the window in app units 178 * @param maxmimum size of the window in app units 179 */ 180 static void SetSizeConstraints(nsPresContext* aPresContext, 181 nsIWidget* aWidget, const nsSize& aMinSize, 182 const nsSize& aMaxSize); 183 184 /** 185 * Helper for calculating intrinsic inline size for inline containers. 186 * 187 * @param aData the intrinsic inline size data, either an InlineMinISizeData 188 * or an InlinePrefISizeData 189 * @param aHandleChildren a callback function invoked for each in-flow 190 * continuation, with the continuation frame and the intrinsic inline size 191 * data passed into it. 192 */ 193 template <typename ISizeData, typename F> 194 void DoInlineIntrinsicISize(ISizeData* aData, F& aHandleChildren); 195 196 void DoInlineMinISize(const mozilla::IntrinsicSizeInput& aInput, 197 InlineMinISizeData* aData); 198 void DoInlinePrefISize(const mozilla::IntrinsicSizeInput& aInput, 199 InlinePrefISizeData* aData); 200 201 /** 202 * This is the CSS block concept of computing 'auto' widths, which most 203 * classes derived from nsContainerFrame want. 204 */ 205 virtual mozilla::LogicalSize ComputeAutoSize( 206 const SizeComputationInput& aSizingInput, mozilla::WritingMode aWM, 207 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize, 208 const mozilla::LogicalSize& aMargin, 209 const mozilla::LogicalSize& aBorderPadding, 210 const mozilla::StyleSizeOverrides& aSizeOverrides, 211 mozilla::ComputeSizeFlags aFlags) override; 212 213 /** 214 * Positions aKidFrame and its view (if requested), and then calls Reflow(). 215 * If the reflow status after reflowing the child is FullyComplete then any 216 * next-in-flows are deleted using DeleteNextInFlowChild(). 217 * 218 * @param aReflowInput the reflow input for aKidFrame. 219 * @param aWM aPos's writing-mode (any writing mode will do). 220 * @param aPos Position of the aKidFrame to be moved, in terms of aWM. 221 * @param aContainerSize Size of the border-box of the containing frame. 222 * 223 * Note: If ReflowChildFlags::NoMoveFrame is requested, both aPos and 224 * aContainerSize are ignored. 225 */ 226 void ReflowChild(nsIFrame* aKidFrame, nsPresContext* aPresContext, 227 ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, 228 const mozilla::WritingMode& aWM, 229 const mozilla::LogicalPoint& aPos, 230 const nsSize& aContainerSize, ReflowChildFlags aFlags, 231 nsReflowStatus& aStatus, 232 nsOverflowContinuationTracker* aTracker = nullptr); 233 234 /** 235 * The second half of frame reflow. Does the following: 236 * - sets the frame's bounds 237 * - sizes and positions (if requested) the frame's view. If the frame's final 238 * position differs from the current position and the frame itself does not 239 * have a view, then any child frames with views are positioned so they stay 240 * in sync 241 * - sets the view's visibility, opacity, content transparency, and clip 242 * - invoked the DidReflow() function 243 * 244 * @param aReflowInput the reflow input for aKidFrame. 245 * @param aWM aPos's writing-mode (any writing mode will do). 246 * @param aPos Position of the aKidFrame to be moved, in terms of aWM. 247 * @param aContainerSize Size of the border-box of the containing frame. 248 * 249 * Note: If ReflowChildFlags::NoMoveFrame is requested, both aPos and 250 * aContainerSize are ignored unless 251 * ReflowChildFlags::ApplyRelativePositioning is requested. 252 */ 253 static void FinishReflowChild( 254 nsIFrame* aKidFrame, nsPresContext* aPresContext, 255 const ReflowOutput& aDesiredSize, const ReflowInput* aReflowInput, 256 const mozilla::WritingMode& aWM, const mozilla::LogicalPoint& aPos, 257 const nsSize& aContainerSize, ReflowChildFlags aFlags); 258 259 // XXX temporary: hold on to a copy of the old physical versions of 260 // ReflowChild and FinishReflowChild so that we can convert callers 261 // incrementally. 262 void ReflowChild(nsIFrame* aKidFrame, nsPresContext* aPresContext, 263 ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, 264 nscoord aX, nscoord aY, ReflowChildFlags aFlags, 265 nsReflowStatus& aStatus, 266 nsOverflowContinuationTracker* aTracker = nullptr); 267 268 static void FinishReflowChild(nsIFrame* aKidFrame, 269 nsPresContext* aPresContext, 270 const ReflowOutput& aDesiredSize, 271 const ReflowInput* aReflowInput, nscoord aX, 272 nscoord aY, ReflowChildFlags aFlags); 273 274 /** 275 * Let the absolutely positioned containing block reflow any absolutely 276 * positioned child frames that need to be reflowed. 277 * 278 * @param aStatus The reflow statuses of any reflowed absolute children will 279 * be merged into aStatus; aside from that, this method won't modify aStatus. 280 */ 281 void ReflowAbsoluteFrames(nsPresContext* aPresContext, 282 ReflowOutput& aDesiredSize, 283 const ReflowInput& aReflowInput, 284 nsReflowStatus& aStatus); 285 286 /** 287 * A convenience method to call ReflowAbsoluteFrames() and 288 * FinishAndStoreOverflow(). 289 */ 290 void FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext, 291 ReflowOutput& aDesiredSize, 292 const ReflowInput& aReflowInput, 293 nsReflowStatus& aStatus); 294 295 // ========================================================================== 296 /* Overflow containers are continuation frames that hold overflow. They 297 * are created when the frame runs out of computed block-size, but still has 298 * too much content to fit in the AvailableBSize. The parent creates a 299 * continuation as usual, but marks it as NS_FRAME_IS_OVERFLOW_CONTAINER 300 * and adds it to its next-in-flow's overflow container list, either by 301 * adding it directly or by putting it in its own excess overflow containers 302 * list (to be drained by the next-in-flow when it calls 303 * ReflowOverflowContainerChildren). The parent continues reflow as if 304 * the frame was complete once it ran out of computed block-size, but returns 305 * a reflow status with either IsIncomplete() or IsOverflowIncomplete() equal 306 * to true to request a next-in-flow. The parent's next-in-flow is then 307 * responsible for calling ReflowOverflowContainerChildren to (drain and) 308 * reflow these overflow continuations. Overflow containers do not affect 309 * other frames' size or position during reflow (but do affect their 310 * parent's overflow area). 311 * 312 * Overflow container continuations are different from normal continuations 313 * in that 314 * - more than one child of the frame can have its next-in-flow broken 315 * off and pushed into the frame's next-in-flow 316 * - new continuations may need to be spliced into the middle of the list 317 * or deleted continuations slipped out 318 * e.g. A, B, C are all fixed-size containers on one page, all have 319 * overflow beyond AvailableBSize, and content is dynamically added 320 * and removed from B 321 * As a result, it is not possible to simply prepend the new continuations 322 * to the old list as with the OverflowProperty mechanism. To avoid 323 * complicated list splicing, the code assumes only one overflow containers 324 * list exists for a given frame: either its own OverflowContainersProperty 325 * or its prev-in-flow's ExcessOverflowContainersProperty, not both. 326 * 327 * The nsOverflowContinuationTracker helper class should be used for tracking 328 * overflow containers and adding them to the appropriate list. 329 * See nsBlockFrame::Reflow for a sample implementation. 330 * 331 * For more information, see 332 * https://firefox-source-docs.mozilla.org/layout/LayoutOverview.html#fragmentation 333 * 334 * Note that Flex/GridContainerFrame doesn't use nsOverflowContinuationTracker 335 * so the above doesn't apply. Flex/Grid containers may have items that 336 * aren't in document order between fragments, due to the 'order' property, 337 * but they do maintain the invariant that children in the same nsFrameList 338 * are in document order. This means that when pushing/pulling items or 339 * merging lists, the result needs to be sorted to restore the order. 340 * However, given that lists are individually sorted, it's a simple merge 341 * operation of the two lists to make the result sorted. 342 * DrainExcessOverflowContainersList takes a merging function to perform that 343 * operation. (By "document order" here we mean normal frame tree order, 344 * which is approximately flattened DOM tree order.) 345 */ 346 347 friend class nsOverflowContinuationTracker; 348 349 typedef void (*ChildFrameMerger)(nsFrameList& aDest, nsFrameList& aSrc, 350 nsContainerFrame* aParent); 351 static inline void DefaultChildFrameMerge(nsFrameList& aDest, 352 nsFrameList& aSrc, 353 nsContainerFrame* aParent) { 354 aDest.AppendFrames(nullptr, std::move(aSrc)); 355 } 356 357 /** 358 * Reflow overflow container children. They are invisible to normal reflow 359 * (i.e. don't affect sizing or placement of other children) and inherit 360 * width and horizontal position from their prev-in-flow. 361 * 362 * This method 363 * 1. Pulls excess overflow containers from the prev-in-flow and adds 364 * them to our overflow container list 365 * 2. Reflows all our overflow container kids 366 * 3. Expands aOverflowRect as necessary to accomodate these children. 367 * 4. Sets aStatus's mOverflowIncomplete flag (along with 368 * mNextInFlowNeedsReflow as necessary) if any overflow children 369 * are incomplete and 370 * 5. Prepends a list of their continuations to our excess overflow 371 * container list, to be drained into our next-in-flow when it is 372 * reflowed. 373 * 374 * The caller is responsible for tracking any new overflow container 375 * continuations it makes, removing them from its child list, and 376 * making sure they are stored properly in the overflow container lists. 377 * The nsOverflowContinuationTracker helper class should be used for this. 378 * 379 * @param aFlags is passed through to ReflowChild 380 * @param aMergeFunc is passed to DrainExcessOverflowContainersList 381 * @param aContainerSize is used only for converting logical coordinate to 382 * physical coordinate. If a tentative container size is used, caller 383 * may need to adjust the position of our overflow container children 384 * once the real size is known if our writing mode is vertical-rl. 385 */ 386 void ReflowOverflowContainerChildren( 387 nsPresContext* aPresContext, const ReflowInput& aReflowInput, 388 mozilla::OverflowAreas& aOverflowRects, ReflowChildFlags aFlags, 389 nsReflowStatus& aStatus, 390 ChildFrameMerger aMergeFunc = DefaultChildFrameMerge, 391 Maybe<nsSize> aContainerSize = Nothing()); 392 393 /** 394 * Move any frames on our overflow list to the end of our principal list. 395 * @return true if there were any overflow frames 396 */ 397 virtual bool DrainSelfOverflowList() override; 398 399 /** 400 * Move all frames on our prev-in-flow's and our own ExcessOverflowContainers 401 * lists to our OverflowContainers list. If there are frames on multiple 402 * lists they are merged using aMergeFunc. 403 * @return a pointer to our OverflowContainers list, if any 404 */ 405 nsFrameList* DrainExcessOverflowContainersList( 406 ChildFrameMerger aMergeFunc = DefaultChildFrameMerge); 407 408 /** 409 * Removes aChild without destroying it and without requesting reflow. 410 * Continuations are not affected. Checks the principal and overflow lists, 411 * and also the [excess] overflow containers lists if the frame bit 412 * NS_FRAME_IS_OVERFLOW_CONTAINER is set. It does not check any other lists. 413 * aChild must be in one of the above mentioned lists, or an assertion is 414 * triggered. 415 * 416 * Note: This method can destroy either overflow list or [excess] overflow 417 * containers list if aChild is the only child in the list. Any pointer to the 418 * list obtained prior to calling this method shouldn't be used. 419 */ 420 virtual void StealFrame(nsIFrame* aChild); 421 422 /** 423 * Removes the next-siblings of aChild without destroying them and without 424 * requesting reflow. Checks the principal and overflow lists (not 425 * overflow containers / excess overflow containers). Does not check any 426 * other auxiliary lists. 427 * @param aChild a child frame or nullptr 428 * @return If aChild is non-null, the next-siblings of aChild, if any. 429 * If aChild is null, all child frames on the principal list, if any. 430 */ 431 nsFrameList StealFramesAfter(nsIFrame* aChild); 432 433 /** 434 * Add overflow containers to the display list 435 */ 436 void DisplayOverflowContainers(nsDisplayListBuilder* aBuilder, 437 const nsDisplayListSet& aLists); 438 439 /** 440 * Add pushed absolute frames to the display list. 441 * 442 * Note: for an absolute frame's first-in-flow without the 443 * NS_FRAME_IS_PUSHED_OUT_OF_FLOW bit, it will be painted through its 444 * placeholder frame. 445 */ 446 void DisplayPushedAbsoluteFrames(nsDisplayListBuilder* aBuilder, 447 const nsDisplayListSet& aLists); 448 449 /** 450 * Builds display lists for the children. The background 451 * of each child is placed in the Content() list (suitable for inline 452 * children and other elements that behave like inlines, 453 * but not for in-flow block children of blocks). DOES NOT 454 * paint the background/borders/outline of this frame. This should 455 * probably be avoided and eventually removed. It's currently here 456 * to emulate what nsContainerFrame::Paint did. 457 */ 458 virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, 459 const nsDisplayListSet& aLists) override; 460 461 /** 462 * Returns a CSS Box Alignment constant which the caller can use to align 463 * the absolutely-positioned child (whose ReflowInput is aChildRI) within 464 * a CSS Box Alignment area associated with this container. Used for 465 * computing the static position of an absolutely positioned box. 466 * 467 * The lower 8 bits of the returned value are guaranteed to form a valid 468 * argument for CSSAlignUtils::AlignJustifySelf(). (The upper 8 bits may 469 * encode an <overflow-position>.) 470 * 471 * @param aChildRI A ReflowInput for the positioned child frame that's being 472 * aligned. 473 * @param aLogicalAxis The axis (of this container frame) in which the caller 474 * would like to align the child frame. 475 */ 476 virtual mozilla::StyleAlignFlags CSSAlignmentForAbsPosChild( 477 const ReflowInput& aChildRI, mozilla::LogicalAxis aLogicalAxis) const; 478 479 /** 480 * Default implementation of `CSSAlignmentForAbsPosChild`, where we treat 481 * this frame as a plain absolute containing block instead of depending 482 * on its type (By overriding `CSSAlignmentForAbsPosChild`). 483 */ 484 mozilla::StyleAlignFlags CSSAlignmentForAbsPosChildWithinContainingBlock( 485 const SizeComputationInput& aSizingInput, 486 mozilla::LogicalAxis aLogicalAxis, 487 const mozilla::StylePositionArea& aResolvedPositionArea, 488 const mozilla::LogicalSize& aContainingBlockSize) const; 489 490 #define NS_DECLARE_FRAME_PROPERTY_FRAMELIST(prop) \ 491 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(prop, nsFrameList) 492 493 using FrameListPropertyDescriptor = 494 mozilla::FrameProperties::Descriptor<nsFrameList>; 495 496 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowProperty) 497 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty) 498 NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty) 499 500 // Only really used on nsBlockFrame instances, but the caller thinks it could 501 // have arbitrary nsContainerFrames. 502 NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(FirstLetterProperty, nsIFrame) 503 504 void SetHasFirstLetterChild() { mHasFirstLetterChild = true; } 505 506 void ClearHasFirstLetterChild() { mHasFirstLetterChild = false; } 507 508 #ifdef DEBUG 509 // Use this to suppress the ABSURD_SIZE assertions. 510 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(DebugReflowingWithInfiniteISize, bool) 511 bool IsAbsurdSizeAssertSuppressed() const { 512 return GetProperty(DebugReflowingWithInfiniteISize()); 513 } 514 #endif 515 516 // Incorporate the child overflow areas into aOverflowAreas. 517 // If the child does not have a overflow, use the child area. 518 void ConsiderChildOverflow(mozilla::OverflowAreas& aOverflowAreas, 519 nsIFrame* aChildFrame, bool aAsIfScrolled = false); 520 521 protected: 522 nsContainerFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, 523 ClassID aID) 524 : nsSplittableFrame(aStyle, aPresContext, aID) {} 525 526 ~nsContainerFrame(); 527 528 /** 529 * Helper for DestroyFrom. DestroyAbsoluteFrames is called before 530 * destroying frames on lists that can contain placeholders. 531 * Derived classes must do that too, if they destroy such frame lists. 532 * See nsBlockFrame::DestroyFrom for an example. 533 */ 534 void DestroyAbsoluteFrames(DestroyContext&); 535 536 /** 537 * Helper for StealFrame. Returns true if aChild was removed from its list. 538 */ 539 bool MaybeStealOverflowContainerFrame(nsIFrame* aChild); 540 541 /** 542 * Builds a display list for non-block children that behave like 543 * inlines. This puts the background of each child into the 544 * Content() list (suitable for inline children but not for 545 * in-flow block children of blocks). 546 * @param aForcePseudoStack forces each child into a pseudo-stacking-context 547 * so its background and all other display items (except for positioned 548 * display items) go into the Content() list. 549 */ 550 void BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder, 551 const nsDisplayListSet& aLists, 552 DisplayChildFlags aFlags = {}); 553 554 /** 555 * A version of BuildDisplayList that use DisplayChildFlag::Inline. 556 * Intended as a convenience for derived classes. 557 */ 558 void BuildDisplayListForInline(nsDisplayListBuilder* aBuilder, 559 const nsDisplayListSet& aLists) { 560 DisplayBorderBackgroundOutline(aBuilder, aLists); 561 BuildDisplayListForNonBlockChildren(aBuilder, aLists, 562 DisplayChildFlag::Inline); 563 } 564 565 // ========================================================================== 566 /* Overflow Frames are frames that did not fit and must be pulled by 567 * our next-in-flow during its reflow. (The same concept for overflow 568 * containers is called "excess frames". We should probably make the 569 * names match.) 570 */ 571 572 /** 573 * Get the frames on the overflow list, overflow containers list, or excess 574 * overflow containers list. Can return null if there are no frames in the 575 * list. 576 * 577 * The caller does NOT take ownership of the list; it's still owned by this 578 * frame. A non-null return value indicates that the list is non-empty. 579 */ 580 [[nodiscard]] nsFrameList* GetOverflowFrames() const { 581 nsFrameList* list = GetProperty(OverflowProperty()); 582 NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list"); 583 return list; 584 } 585 [[nodiscard]] nsFrameList* GetOverflowContainers() const { 586 nsFrameList* list = GetProperty(OverflowContainersProperty()); 587 NS_ASSERTION(!list || !list->IsEmpty(), 588 "Unexpected empty overflow containers list"); 589 return list; 590 } 591 [[nodiscard]] nsFrameList* GetExcessOverflowContainers() const { 592 nsFrameList* list = GetProperty(ExcessOverflowContainersProperty()); 593 NS_ASSERTION(!list || !list->IsEmpty(), 594 "Unexpected empty overflow containers list"); 595 return list; 596 } 597 598 /** 599 * Same as the Get methods above, but also remove and the property from this 600 * frame. 601 * 602 * The caller is responsible for deleting nsFrameList and either passing 603 * ownership of the frames to someone else or destroying the frames. A 604 * non-null return value indicates that the list is non-empty. The recommended 605 * way to use this function it to assign its return value into an 606 * AutoFrameListPtr. 607 */ 608 [[nodiscard]] nsFrameList* StealOverflowFrames() { 609 nsFrameList* list = TakeProperty(OverflowProperty()); 610 NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list"); 611 return list; 612 } 613 [[nodiscard]] nsFrameList* StealOverflowContainers() { 614 nsFrameList* list = TakeProperty(OverflowContainersProperty()); 615 NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list"); 616 return list; 617 } 618 [[nodiscard]] nsFrameList* StealExcessOverflowContainers() { 619 nsFrameList* list = TakeProperty(ExcessOverflowContainersProperty()); 620 NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list"); 621 return list; 622 } 623 624 /** 625 * Set the overflow list, overflow containers list, or excess overflow 626 * containers list. The argument must be a *non-empty* list. 627 * 628 * After this operation, the argument becomes an empty list. 629 * 630 * @return the frame list associated with the property. 631 */ 632 nsFrameList* SetOverflowFrames(nsFrameList&& aOverflowFrames) { 633 MOZ_ASSERT(aOverflowFrames.NotEmpty(), "Shouldn't be called"); 634 auto* list = new (PresShell()) nsFrameList(std::move(aOverflowFrames)); 635 SetProperty(OverflowProperty(), list); 636 return list; 637 } 638 nsFrameList* SetOverflowContainers(nsFrameList&& aOverflowContainers) { 639 MOZ_ASSERT(aOverflowContainers.NotEmpty(), "Shouldn't set an empty list!"); 640 MOZ_ASSERT(!GetProperty(OverflowContainersProperty()), 641 "Shouldn't override existing list!"); 642 MOZ_ASSERT(CanContainOverflowContainers(), 643 "This type of frame can't have overflow containers!"); 644 auto* list = new (PresShell()) nsFrameList(std::move(aOverflowContainers)); 645 SetProperty(OverflowContainersProperty(), list); 646 return list; 647 } 648 nsFrameList* SetExcessOverflowContainers( 649 nsFrameList&& aExcessOverflowContainers) { 650 MOZ_ASSERT(aExcessOverflowContainers.NotEmpty(), 651 "Shouldn't set an empty list!"); 652 MOZ_ASSERT(!GetProperty(ExcessOverflowContainersProperty()), 653 "Shouldn't override existing list!"); 654 MOZ_ASSERT(CanContainOverflowContainers(), 655 "This type of frame can't have overflow containers!"); 656 auto* list = 657 new (PresShell()) nsFrameList(std::move(aExcessOverflowContainers)); 658 SetProperty(ExcessOverflowContainersProperty(), list); 659 return list; 660 } 661 662 /** 663 * Destroy the overflow list, overflow containers list, or excess overflow 664 * containers list. 665 * 666 * The list to be destroyed must be empty. That is, the caller is responsible 667 * for either passing ownership of the frames to someone else or destroying 668 * the frames before calling these methods. 669 */ 670 void DestroyOverflowList() { 671 nsFrameList* list = TakeProperty(OverflowProperty()); 672 MOZ_ASSERT(list && list->IsEmpty()); 673 list->Delete(PresShell()); 674 } 675 void DestroyOverflowContainers() { 676 nsFrameList* list = TakeProperty(OverflowContainersProperty()); 677 MOZ_ASSERT(list && list->IsEmpty()); 678 list->Delete(PresShell()); 679 } 680 void DestroyExcessOverflowContainers() { 681 nsFrameList* list = TakeProperty(ExcessOverflowContainersProperty()); 682 MOZ_ASSERT(list && list->IsEmpty()); 683 list->Delete(PresShell()); 684 } 685 686 /** 687 * Moves any frames on both the prev-in-flow's overflow list and the 688 * receiver's overflow to the receiver's child list. 689 * 690 * Resets the overlist pointers to nullptr, and updates the receiver's child 691 * count and content mapping. 692 * 693 * @return true if any frames were moved and false otherwise 694 */ 695 bool MoveOverflowToChildList(); 696 697 /** 698 * Merge a sorted frame list into our overflow list. aList becomes empty after 699 * this call. 700 */ 701 void MergeSortedOverflow(nsFrameList& aList); 702 703 /** 704 * Merge a sorted frame list into our excess overflow containers list. aList 705 * becomes empty after this call. 706 */ 707 void MergeSortedExcessOverflowContainers(nsFrameList& aList); 708 709 /** 710 * Moves all frames from aSrc into aDest such that the resulting aDest 711 * is still sorted in document content order and continuation order. aSrc 712 * becomes empty after this call. 713 * 714 * Precondition: both |aSrc| and |aDest| must be sorted to begin with. 715 * @param aCommonAncestor a hint for nsLayoutUtils::CompareTreePosition 716 */ 717 static void MergeSortedFrameLists(nsFrameList& aDest, nsFrameList& aSrc, 718 nsIContent* aCommonAncestor); 719 720 /** 721 * This is intended to be used as a ChildFrameMerger argument for 722 * ReflowOverflowContainerChildren() and DrainExcessOverflowContainersList(). 723 */ 724 static inline void MergeSortedFrameListsFor(nsFrameList& aDest, 725 nsFrameList& aSrc, 726 nsContainerFrame* aParent) { 727 MergeSortedFrameLists(aDest, aSrc, aParent->GetContent()); 728 } 729 730 /** 731 * Basically same as MoveOverflowToChildList, except that this is for 732 * handling inline children where children of prev-in-flow can be 733 * pushed to overflow list even if a next-in-flow exists. 734 * 735 * @param aLineContainer the line container of the current frame. 736 * 737 * @return true if any frames were moved and false otherwise 738 */ 739 bool MoveInlineOverflowToChildList(nsIFrame* aLineContainer); 740 741 /** 742 * Push aFromChild and its next siblings to the overflow list. 743 * 744 * @param aFromChild the first child frame to push. It is disconnected 745 * from aPrevSibling 746 * @param aPrevSibling aFrameChild's previous sibling. Must not be null. 747 * It's an error to push a parent's first child frame. 748 */ 749 void PushChildrenToOverflow(nsIFrame* aFromChild, nsIFrame* aPrevSibling); 750 751 /** 752 * Iterate our children in our principal child list in the normal document 753 * order, and append them (or their next-in-flows) to either our overflow list 754 * or excess overflow container list according to their presence in 755 * aPushedItems, aIncompleteItems, or aOverflowIncompleteItems. 756 * 757 * Note: This method is only intended for Grid / Flex containers. 758 * aPushedItems, aIncompleteItems, and aOverflowIncompleteItems are expected 759 * to contain only Grid / Flex items. That is, they should contain only 760 * in-flow children. 761 * 762 * @return true if any items are moved; false otherwise. 763 */ 764 using FrameHashtable = nsTHashSet<nsIFrame*>; 765 bool PushIncompleteChildren(const FrameHashtable& aPushedItems, 766 const FrameHashtable& aIncompleteItems, 767 const FrameHashtable& aOverflowIncompleteItems); 768 769 /** 770 * Prepare our child lists so that they are ready to reflow by the following 771 * operations: 772 * 773 * - Merge overflow list from our prev-in-flow into our principal child list. 774 * - Merge our own overflow list into our principal child list, 775 * - Push any child's next-in-flows in our principal child list to our 776 * overflow list. 777 * - Pull up any first-in-flow child we might have pushed from our 778 * next-in-flows. 779 */ 780 void NormalizeChildLists(); 781 782 /** 783 * Helper to implement AppendFrames / InsertFrames for flex / grid 784 * containers. 785 */ 786 void NoteNewChildren(ChildListID aListID, const nsFrameList& aFrameList); 787 788 /** 789 * Helper to implement DrainSelfOverflowList() for flex / grid containers. 790 */ 791 bool DrainAndMergeSelfOverflowList(); 792 793 /** 794 * Helper to find the first non-anonymous-box frame in the subtree rooted at 795 * aFrame. 796 */ 797 static nsIFrame* GetFirstNonAnonBoxInSubtree(nsIFrame* aFrame); 798 799 /** 800 * Reparent floats whose placeholders are inline descendants of aFrame from 801 * whatever block they're currently parented by to aOurBlock. 802 * @param aReparentSiblings if this is true, we follow aFrame's 803 * GetNextSibling chain reparenting them all 804 */ 805 static void ReparentFloatsForInlineChild(nsIFrame* aOurBlock, 806 nsIFrame* aFrame, 807 bool aReparentSiblings); 808 809 /** 810 * Try to remove aChildToRemove from the frame list stored in aProp. 811 * If aChildToRemove was removed from the aProp list and that list became 812 * empty, then aProp is removed from this frame and deleted. 813 * @note if aChildToRemove isn't on the aProp frame list, it might still be 814 * removed from whatever list it happens to be on, so use this method 815 * carefully. This method is primarily meant for removing frames from the 816 * [Excess]OverflowContainers lists. 817 * @return true if aChildToRemove was removed from some list 818 */ 819 bool TryRemoveFrame(FrameListPropertyDescriptor aProp, 820 nsIFrame* aChildToRemove); 821 822 // ========================================================================== 823 /* 824 * Convenience methods for traversing continuations 825 */ 826 827 struct ContinuationTraversingState { 828 nsContainerFrame* mNextInFlow; 829 explicit ContinuationTraversingState(nsContainerFrame* aFrame) 830 : mNextInFlow(static_cast<nsContainerFrame*>(aFrame->GetNextInFlow())) { 831 } 832 }; 833 834 /** 835 * Find the first frame that is a child of this frame's next-in-flows, 836 * considering both their principal child lists and overflow lists. 837 */ 838 nsIFrame* GetNextInFlowChild(ContinuationTraversingState& aState, 839 bool* aIsInOverflow = nullptr); 840 841 /** 842 * Remove the result of GetNextInFlowChild from its current parent and 843 * append it to this frame's principal child list. 844 */ 845 nsIFrame* PullNextInFlowChild(ContinuationTraversingState& aState); 846 847 /** 848 * Safely destroy the frames on the nsFrameList stored on aProp for this 849 * frame then remove the property and delete the frame list. 850 * Nothing happens if the property doesn't exist. 851 */ 852 void SafelyDestroyFrameListProp(DestroyContext&, 853 mozilla::PresShell* aPresShell, 854 FrameListPropertyDescriptor aProp); 855 856 // ========================================================================== 857 858 // Helper used by Progress and Meter frames. Returns true if the bar should 859 // be rendered vertically, based on writing-mode and -moz-orient properties. 860 bool ResolvedOrientationIsVertical() const; 861 862 /** 863 * Calculate the used values for 'width' and 'height' for a replaced element. 864 * http://www.w3.org/TR/CSS21/visudet.html#min-max-widths 865 * 866 * @param aAspectRatio the aspect ratio calculated by GetAspectRatio(). 867 */ 868 mozilla::LogicalSize ComputeSizeWithIntrinsicDimensions( 869 gfxContext* aRenderingContext, mozilla::WritingMode aWM, 870 const mozilla::IntrinsicSize& aIntrinsicSize, 871 const mozilla::AspectRatio& aAspectRatio, 872 const mozilla::LogicalSize& aCBSize, const mozilla::LogicalSize& aMargin, 873 const mozilla::LogicalSize& aBorderPadding, 874 const mozilla::StyleSizeOverrides& aSizeOverrides, 875 mozilla::ComputeSizeFlags aFlags); 876 877 // Compute tight bounds assuming this frame honours its border, background 878 // and outline, its children's tight bounds, and nothing else. 879 nsRect ComputeSimpleTightBounds(mozilla::gfx::DrawTarget* aDrawTarget) const; 880 881 /* 882 * If this frame is dirty, marks all absolutely-positioned children of this 883 * frame dirty. If this frame isn't dirty, or if there are no 884 * absolutely-positioned children, does nothing. 885 * 886 * It's necessary to use PushDirtyBitToAbsoluteFrames() when you plan to 887 * reflow this frame's absolutely-positioned children after the dirty bit on 888 * this frame has already been cleared, which prevents ReflowInput from 889 * propagating the dirty bit normally. This situation generally only arises 890 * when a multipass layout algorithm is used. 891 */ 892 void PushDirtyBitToAbsoluteFrames(); 893 894 // Helper function that tests if the frame tree is too deep; if it is 895 // it marks the frame as "unflowable", zeroes out the metrics, sets 896 // the reflow status, and returns true. Otherwise, the frame is 897 // unmarked "unflowable" and the metrics and reflow status are not 898 // touched and false is returned. 899 bool IsFrameTreeTooDeep(const ReflowInput& aReflowInput, 900 ReflowOutput& aMetrics, nsReflowStatus& aStatus); 901 902 /** 903 * @return true if we should avoid a page/column break in this frame. 904 */ 905 bool ShouldAvoidBreakInside(const ReflowInput& aReflowInput) const; 906 907 /** 908 * To be called by |BuildDisplayLists| of this class or derived classes to add 909 * a translucent overlay if this frame's content is selected. 910 * @param aContentType an nsISelectionDisplay DISPLAY_ constant identifying 911 * which kind of content this is for 912 */ 913 void DisplaySelectionOverlay( 914 nsDisplayListBuilder* aBuilder, nsDisplayList* aList, 915 uint16_t aContentType = nsISelectionDisplay::DISPLAY_FRAMES); 916 917 /** 918 * Helper for ruby frames that want to accumulate the max ascent/descent of 919 * their children. 920 */ 921 mozilla::RubyMetrics RubyMetricsIncludingChildren( 922 float aRubyMetricsFactor) const; 923 924 // ========================================================================== 925 926 #ifdef DEBUG 927 // A helper for flex / grid container to sanity check child lists before 928 // reflow. Intended to be called after calling NormalizeChildLists(). 929 void SanityCheckChildListsBeforeReflow() const; 930 931 // A helper to set mDidPushItemsBitMayLie if needed. Intended to be called 932 // only in flex / grid container's RemoveFrame. 933 void SetDidPushItemsBitIfNeeded(ChildListID aListID, nsIFrame* aOldFrame); 934 935 // A flag for flex / grid containers. If true, NS_STATE_GRID_DID_PUSH_ITEMS or 936 // NS_STATE_FLEX_DID_PUSH_ITEMS may be set even though all pushed frames may 937 // have been removed. This is used to suppress an assertion in case 938 // RemoveFrame removed all associated child frames. 939 bool mDidPushItemsBitMayLie{false}; 940 #endif 941 942 nsFrameList mFrames; 943 }; 944 945 // ========================================================================== 946 /* The out-of-flow-related code below is for a hacky way of splitting 947 * absolutely-positioned frames. Basically what we do is split the frame 948 * in nsAbsoluteContainingBlock and pretend the continuation is an overflow 949 * container. This isn't an ideal solution, but it lets us print the content 950 * at least. See bug 154892. 951 */ 952 953 /** 954 * Helper class for tracking overflow container continuations during reflow. 955 * 956 * A frame is related to two sets of overflow containers: those that /are/ 957 * its own children, and those that are /continuations/ of its children. 958 * This tracker walks through those continuations (the frame's NIF's children) 959 * and their prev-in-flows (a subset of the frame's normal and overflow 960 * container children) in parallel. It allows the reflower to synchronously 961 * walk its overflow continuations while it loops through and reflows its 962 * children. This makes it possible to insert new continuations at the correct 963 * place in the overflow containers list. 964 * 965 * The reflower is expected to loop through its children in the same order it 966 * looped through them the last time (if there was a last time). 967 * For each child, the reflower should either 968 * - call Skip for the child if was not reflowed in this pass 969 * - call Insert for the overflow continuation if the child was reflowed 970 * but has incomplete overflow 971 * - call Finished for the child if it was reflowed in this pass but 972 * is either complete or has a normal next-in-flow. This call can 973 * be skipped if the child did not previously have an overflow 974 * continuation. 975 */ 976 class nsOverflowContinuationTracker { 977 public: 978 /** 979 * Initializes an nsOverflowContinuationTracker to help track overflow 980 * continuations of aFrame's children. Typically invoked on 'this'. 981 * 982 * aWalkOOFFrames determines whether the walker skips out-of-flow frames 983 * or skips non-out-of-flow frames. 984 * 985 * Don't set aSkipOverflowContainerChildren to false unless you plan 986 * to walk your own overflow container children. (Usually they are handled 987 * by calling ReflowOverflowContainerChildren.) aWalkOOFFrames is ignored 988 * if aSkipOverflowContainerChildren is false. 989 */ 990 nsOverflowContinuationTracker(nsContainerFrame* aFrame, bool aWalkOOFFrames, 991 bool aSkipOverflowContainerChildren = true); 992 /** 993 * This function adds an overflow continuation to our running list and 994 * sets its NS_FRAME_IS_OVERFLOW_CONTAINER flag. 995 * 996 * aReflowStatus should preferably be specific to the recently-reflowed 997 * child and not influenced by any of its siblings' statuses. This 998 * function sets the NS_FRAME_IS_DIRTY bit on aOverflowCont if it needs 999 * to be reflowed. (Its need for reflow depends on changes to its 1000 * prev-in-flow, not to its parent--for whom it is invisible, reflow-wise.) 1001 * 1002 * The caller MUST disconnect the frame from its parent's child list 1003 * if it was not previously an NS_FRAME_IS_OVERFLOW_CONTAINER (because 1004 * StealFrame is much more inefficient than disconnecting in place 1005 * during Reflow, which the caller is able to do but we are not). 1006 * 1007 * The caller MUST NOT disconnect the frame from its parent's 1008 * child list if it is already an NS_FRAME_IS_OVERFLOW_CONTAINER. 1009 * (In this case we will disconnect and reconnect it ourselves.) 1010 */ 1011 nsresult Insert(nsIFrame* aOverflowCont, nsReflowStatus& aReflowStatus); 1012 /** 1013 * Begin/EndFinish() must be called for each child that is reflowed 1014 * but no longer has an overflow continuation. (It may be called for 1015 * other children, but in that case has no effect.) It increments our 1016 * walker and makes sure we drop any dangling pointers to its 1017 * next-in-flow. This function MUST be called before stealing or 1018 * deleting aChild's next-in-flow. 1019 * The AutoFinish helper object does that for you. Use it like so: 1020 * if (kidNextInFlow) { 1021 * nsOverflowContinuationTracker::AutoFinish fini(tracker, kid); 1022 * ... DeleteNextInFlowChild/StealFrame(kidNextInFlow) here ... 1023 * } 1024 */ 1025 class MOZ_RAII AutoFinish { 1026 public: 1027 AutoFinish(nsOverflowContinuationTracker* aTracker, nsIFrame* aChild) 1028 : mTracker(aTracker), mChild(aChild) { 1029 if (mTracker) { 1030 mTracker->BeginFinish(mChild); 1031 } 1032 } 1033 ~AutoFinish() { 1034 if (mTracker) { 1035 mTracker->EndFinish(mChild); 1036 } 1037 } 1038 1039 private: 1040 nsOverflowContinuationTracker* mTracker; 1041 nsIFrame* mChild; 1042 }; 1043 1044 /** 1045 * This function should be called for each child that isn't reflowed. 1046 * It increments our walker and sets the mOverflowIncomplete 1047 * reflow flag if it encounters an overflow continuation so that our 1048 * next-in-flow doesn't get prematurely deleted. It MUST be called on 1049 * each unreflowed child that has an overflow container continuation; 1050 * it MAY be called on other children, but it isn't necessary (doesn't 1051 * do anything). 1052 */ 1053 void Skip(nsIFrame* aChild, nsReflowStatus& aReflowStatus) { 1054 MOZ_ASSERT(aChild, "null ptr"); 1055 if (aChild == mSentry) { 1056 StepForward(); 1057 if (aReflowStatus.IsComplete()) { 1058 aReflowStatus.SetOverflowIncomplete(); 1059 } 1060 } 1061 } 1062 1063 private: 1064 /** 1065 * @see class AutoFinish 1066 */ 1067 void BeginFinish(nsIFrame* aChild); 1068 void EndFinish(nsIFrame* aChild); 1069 1070 void SetupOverflowContList(); 1071 void SetUpListWalker(); 1072 void StepForward(); 1073 1074 /* We hold a pointer to either the next-in-flow's overflow containers list 1075 or, if that doesn't exist, our frame's excess overflow containers list. 1076 We need to make sure that we drop that pointer if the list becomes 1077 empty and is deleted elsewhere. */ 1078 nsFrameList* mOverflowContList; 1079 /* We hold a pointer to the most recently-reflowed child that has an 1080 overflow container next-in-flow. We do this because it's a known 1081 good point; this pointer won't be deleted on us. We can use it to 1082 recover our place in the list. */ 1083 nsIFrame* mPrevOverflowCont; 1084 /* This is a pointer to the next overflow container's prev-in-flow, which 1085 is (or should be) a child of our frame. When we hit this, we will need 1086 to increment this walker to the next overflow container. */ 1087 nsIFrame* mSentry; 1088 /* Parent of all frames in mOverflowContList. If our mOverflowContList 1089 is an excessOverflowContainersProperty, or null, then this is our frame 1090 (the frame that was passed in to our constructor). Otherwise this is 1091 that frame's next-in-flow, and our mOverflowContList is mParent's 1092 overflowContainersProperty */ 1093 nsContainerFrame* mParent; 1094 /* Tells SetUpListWalker whether or not to walk us past any continuations 1095 of overflow containers. aWalkOOFFrames is ignored when this is false. */ 1096 bool mSkipOverflowContainerChildren; 1097 /* Tells us whether to pay attention to OOF frames or non-OOF frames */ 1098 bool mWalkOOFFrames; 1099 }; 1100 1101 #endif /* nsContainerFrame_h___ */