diff options
Diffstat (limited to '')
44 files changed, 695 insertions, 497 deletions
diff --git a/layout/generic/BlockReflowState.cpp b/layout/generic/BlockReflowState.cpp index 6163a200a3..78dab82e98 100644 --- a/layout/generic/BlockReflowState.cpp +++ b/layout/generic/BlockReflowState.cpp @@ -47,7 +47,8 @@ BlockReflowState::BlockReflowState( mMinLineHeight(aReflowInput.GetLineHeight()), mLineNumber(0), mTrailingClearFromPIF(StyleClear::None), - mConsumedBSize(aConsumedBSize) { + mConsumedBSize(aConsumedBSize), + mAlignContentShift(mBlock->GetAlignContentShift()) { NS_ASSERTION(mConsumedBSize != NS_UNCONSTRAINEDSIZE, "The consumed block-size should be constrained!"); @@ -87,8 +88,8 @@ BlockReflowState::BlockReflowState( // the "overflow" property. When we don't have a specified style block-size, // then we may end up limiting our block-size if the available block-size is // constrained (this situation occurs when we are paginated). - if (const nscoord availableBSize = aReflowInput.AvailableBSize(); - availableBSize != NS_UNCONSTRAINEDSIZE) { + const nscoord availableBSize = aReflowInput.AvailableBSize(); + if (availableBSize != NS_UNCONSTRAINEDSIZE) { // We are in a paginated situation. The block-end edge of the available // space to reflow the children is within our block-end border and padding. // If we're cloning our border and padding, and we're going to request @@ -112,10 +113,34 @@ BlockReflowState::BlockReflowState( mContentArea.IStart(wm) = mBorderPadding.IStart(wm); mBCoord = mContentArea.BStart(wm) = mBorderPadding.BStart(wm); + // Account for existing cached shift, we'll re-position in AlignContent() if + // needed. + if (mAlignContentShift) { + mBCoord += mAlignContentShift; + mContentArea.BStart(wm) += mAlignContentShift; + + if (availableBSize != NS_UNCONSTRAINEDSIZE) { + mContentArea.BSize(wm) += mAlignContentShift; + } + } + mPrevChild = nullptr; mCurrentLine = aFrame->LinesEnd(); } +void BlockReflowState::UndoAlignContentShift() { + if (!mAlignContentShift) { + return; + } + + mBCoord -= mAlignContentShift; + mContentArea.BStart(mReflowInput.GetWritingMode()) -= mAlignContentShift; + + if (mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) { + mContentArea.BSize(mReflowInput.GetWritingMode()) -= mAlignContentShift; + } +} + void BlockReflowState::ComputeFloatAvoidingOffsets( nsIFrame* aFloatAvoidingBlock, const LogicalRect& aFloatAvailableSpace, nscoord& aIStartResult, nscoord& aIEndResult) const { diff --git a/layout/generic/BlockReflowState.h b/layout/generic/BlockReflowState.h index 1dfaa4d9ad..6f05b6ab0a 100644 --- a/layout/generic/BlockReflowState.h +++ b/layout/generic/BlockReflowState.h @@ -41,16 +41,16 @@ class BlockReflowState { mCanHaveOverflowMarkers(false) {} // Set in the BlockReflowState constructor when reflowing a "block margin - // root" frame (i.e. a frame with any of the NS_BLOCK_BFC_STATE_BITS flag - // set, for which margins apply by default). + // root" frame (i.e. a frame with any of the NS_BLOCK_BFC flag set, for + // which margins apply by default). // // The flag is also set when reflowing a frame whose computed BStart border // padding is non-zero. bool mIsBStartMarginRoot : 1; // Set in the BlockReflowState constructor when reflowing a "block margin - // root" frame (i.e. a frame with any of the NS_BLOCK_BFC_STATE_BITS flag - // set, for which margins apply by default). + // root" frame (i.e. a frame with any of the NS_BLOCK_BFC flag set, for + // which margins apply by default). // // The flag is also set when reflowing a frame whose computed BEnd border // padding is non-zero. @@ -102,6 +102,13 @@ class BlockReflowState { const nscoord aInset = 0); /** + * Unshifts coords, restores availableBSize to reality. + * (Constructor applies any cached shift before reflow + * so that frames are reflowed with cached shift) + */ + void UndoAlignContentShift(); + + /** * Get the available reflow space (the area not occupied by floats) * for the current y coordinate. The available space is relative to * our coordinate system, which is the content box, with (0, 0) in the @@ -407,6 +414,11 @@ class BlockReflowState { // continuations. const nscoord mConsumedBSize; + // The amount of block-axis alignment shift to assume during reflow. + // Cached between reflows in the AlignContentShift property. + // (This system optimizes reflow for not changing the shift.) + nscoord mAlignContentShift; + // Cache the current line's BSize if nsBlockFrame::PlaceLine() fails to // place the line. When redoing the line, it will be used to query the // accurate float available space in AddFloat() and diff --git a/layout/generic/FrameClasses.py b/layout/generic/FrameClasses.py index d1d0a0b13b..36c1af10d1 100644 --- a/layout/generic/FrameClasses.py +++ b/layout/generic/FrameClasses.py @@ -14,6 +14,7 @@ COMMON = { LEAF = {"Leaf"} MATHML = {"MathML"} SVG = {"SVG"} +BFC = {"BlockFormattingContext"} BLOCK = COMMON | {"CanContainOverflowContainers"} @@ -54,18 +55,13 @@ FRAME_CLASSES = [ Frame("nsCheckboxRadioFrame", "CheckboxRadio", REPLACED_WITH_BLOCK | LEAF), Frame("nsColorControlFrame", "ColorControl", REPLACED_WITH_BLOCK | LEAF), Frame("nsColumnSetFrame", "ColumnSet", COMMON), - Frame("ColumnSetWrapperFrame", "ColumnSetWrapper", BLOCK), - Frame("nsComboboxControlFrame", "ComboboxControl", BLOCK | REPLACED_WITH_BLOCK), - # FIXME(emilio, bug 1362907): Revisit these after that bug, this is the - # only frame that has ReplacedContainsBlock but not Replaced, which is - # sketchy. - Frame( - "nsComboboxDisplayFrame", "ComboboxDisplay", REPLACED_WITH_BLOCK - {"Replaced"} - ), + Frame("ColumnSetWrapperFrame", "ColumnSetWrapper", BLOCK | BFC), + Frame("nsComboboxControlFrame", "ComboboxControl", REPLACED_WITH_BLOCK | LEAF), + Frame("ComboboxLabelFrame", "Block", BLOCK), Frame("nsContinuingTextFrame", "Text", TEXT), Frame("nsDateTimeControlFrame", "DateTimeControl", REPLACED_WITH_BLOCK), Frame("nsFieldSetFrame", "FieldSet", BLOCK), - Frame("nsFileControlFrame", "Block", REPLACED_WITH_BLOCK | LEAF), + Frame("nsFileControlFrame", "Block", REPLACED_WITH_BLOCK | LEAF | BFC), Frame("FileControlLabelFrame", "Block", BLOCK | LEAF), Frame("nsFirstLetterFrame", "Letter", INLINE), Frame("nsFloatingFirstLetterFrame", "Letter", INLINE - {"LineParticipant"}), @@ -84,7 +80,7 @@ FRAME_CLASSES = [ Frame("nsImageFrame", "Image", REPLACED_SIZING | {"LeafDynamic"}), Frame("nsInlineFrame", "Inline", INLINE), Frame("nsListControlFrame", "ListControl", REPLACED_WITH_BLOCK), - Frame("nsMathMLmathBlockFrame", "Block", BLOCK | MATHML), + Frame("nsMathMLmathBlockFrame", "Block", BLOCK | MATHML | BFC), Frame("nsMathMLmathInlineFrame", "Inline", INLINE | MATHML), Frame("nsMathMLmencloseFrame", "None", MATHML_CONTAINER), Frame("nsMathMLmfracFrame", "None", MATHML_CONTAINER), @@ -120,7 +116,7 @@ FRAME_CLASSES = [ Frame("nsScrollbarButtonFrame", "SimpleXULLeaf", COMMON | LEAF), Frame("nsScrollbarFrame", "Scrollbar", COMMON), Frame("nsSearchControlFrame", "SearchControl", LEAF), - Frame("nsSelectsAreaFrame", "Block", BLOCK), + Frame("nsSelectsAreaFrame", "Block", BLOCK | BFC), Frame("nsPageSequenceFrame", "PageSequence", COMMON), Frame("nsSliderFrame", "Slider", COMMON), Frame("nsSplitterFrame", "SimpleXULLeaf", COMMON | LEAF), diff --git a/layout/generic/ScrollAnchorContainer.cpp b/layout/generic/ScrollAnchorContainer.cpp index 5e1d8aa56d..93336480f7 100644 --- a/layout/generic/ScrollAnchorContainer.cpp +++ b/layout/generic/ScrollAnchorContainer.cpp @@ -539,11 +539,8 @@ void ScrollAnchorContainer::ApplyAdjustments() { MOZ_RELEASE_ASSERT(!mApplyingAnchorAdjustment); // We should use AutoRestore here, but that doesn't work with bitfields mApplyingAnchorAdjustment = true; - Frame()->ScrollToInternal( - Frame()->GetScrollPosition() + physicalAdjustment, ScrollMode::Instant, - StaticPrefs::layout_css_scroll_anchoring_absolute_update() - ? ScrollOrigin::AnchorAdjustment - : ScrollOrigin::Relative); + Frame()->ScrollToInternal(Frame()->GetScrollPosition() + physicalAdjustment, + ScrollMode::Instant, ScrollOrigin::Relative); mApplyingAnchorAdjustment = false; if (Frame()->mIsRoot) { diff --git a/layout/generic/ScrollAnimationMSDPhysics.cpp b/layout/generic/ScrollAnimationMSDPhysics.cpp index de67f9c59a..8f0ae32d3b 100644 --- a/layout/generic/ScrollAnimationMSDPhysics.cpp +++ b/layout/generic/ScrollAnimationMSDPhysics.cpp @@ -5,7 +5,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ScrollAnimationMSDPhysics.h" +#include "mozilla/Logging.h" #include "mozilla/StaticPrefs_general.h" +#include "mozilla/ToString.h" + +static mozilla::LazyLogModule sApzMsdLog("apz.msd"); +#define MSD_LOG(...) MOZ_LOG(sApzMsdLog, LogLevel::Debug, (__VA_ARGS__)) using namespace mozilla; @@ -108,13 +113,17 @@ double ScrollAnimationMSDPhysics::ComputeSpringConstant( } void ScrollAnimationMSDPhysics::SimulateUntil(const TimeStamp& aTime) { - if (!mLastSimulatedTime || aTime < mLastSimulatedTime) { + if (!mLastSimulatedTime || aTime <= mLastSimulatedTime) { return; } TimeDuration delta = aTime - mLastSimulatedTime; mModelX.Simulate(delta); mModelY.Simulate(delta); mLastSimulatedTime = aTime; + MSD_LOG("Simulated for duration %f, finished %d position %s velocity %s\n", + delta.ToMilliseconds(), IsFinished(aTime), + ToString(CSSPoint::FromAppUnits(PositionAt(aTime))).c_str(), + ToString(CSSPoint::FromAppUnits(VelocityAt(aTime))).c_str()); } nsPoint ScrollAnimationMSDPhysics::PositionAt(const TimeStamp& aTime) { @@ -152,6 +161,9 @@ ScrollAnimationMSDPhysics::NonOscillatingAxisPhysicsMSDModel:: ClampVelocityToMaximum(aInitialVelocity, aInitialPosition, aInitialDestination, aSpringConstant), aSpringConstant, aDampingRatio) { + MSD_LOG("Constructing axis physics model with parameters %f %f %f %f %f\n", + aInitialPosition, aInitialDestination, aInitialVelocity, + aSpringConstant, aDampingRatio); MOZ_ASSERT(aDampingRatio >= 1.0, "Damping ratio must be >= 1.0 to avoid oscillation"); } diff --git a/layout/generic/ScrollOrigin.h b/layout/generic/ScrollOrigin.h index f1ad0e3367..edffa2d4f6 100644 --- a/layout/generic/ScrollOrigin.h +++ b/layout/generic/ScrollOrigin.h @@ -33,8 +33,6 @@ enum class ScrollOrigin : uint8_t { // The scroll came from an attempt by the main thread to re-clamp the scroll // position after a reflow. Clamp, - // The scroll came from a scroll adjustment triggered by scroll anchoring. - AnchorAdjustment, // The following scroll origins also are associated with prefs of the form // general.smoothScroll.<origin>(.*) diff --git a/layout/generic/ScrollPositionUpdate.cpp b/layout/generic/ScrollPositionUpdate.cpp index 9607d2d18a..a680768525 100644 --- a/layout/generic/ScrollPositionUpdate.cpp +++ b/layout/generic/ScrollPositionUpdate.cpp @@ -93,20 +93,6 @@ ScrollPositionUpdate ScrollPositionUpdate::NewPureRelativeScroll( return ret; } -/*static*/ -ScrollPositionUpdate ScrollPositionUpdate::NewMergeableScroll( - ScrollOrigin aOrigin, nsPoint aDestination) { - MOZ_ASSERT(aOrigin == ScrollOrigin::AnchorAdjustment); - - ScrollPositionUpdate ret; - ret.mScrollGeneration = sGenerationCounter.NewMainThreadGeneration(); - ret.mType = ScrollUpdateType::MergeableAbsolute; - ret.mScrollMode = ScrollMode::Instant; - ret.mScrollOrigin = aOrigin; - ret.mDestination = CSSPoint::FromAppUnits(aDestination); - return ret; -} - bool ScrollPositionUpdate::operator==( const ScrollPositionUpdate& aOther) const { // instances are immutable, and all the fields are set when the generation @@ -126,7 +112,6 @@ ScrollOrigin ScrollPositionUpdate::GetOrigin() const { return mScrollOrigin; } CSSPoint ScrollPositionUpdate::GetDestination() const { MOZ_ASSERT(mType == ScrollUpdateType::Absolute || - mType == ScrollUpdateType::MergeableAbsolute || mType == ScrollUpdateType::Relative); return mDestination; } diff --git a/layout/generic/ScrollPositionUpdate.h b/layout/generic/ScrollPositionUpdate.h index 0e8dc020c1..ca22da8af0 100644 --- a/layout/generic/ScrollPositionUpdate.h +++ b/layout/generic/ScrollPositionUpdate.h @@ -34,9 +34,6 @@ enum class ScrollUpdateType { // The delta should be applied to whatever the current scroll position is // on the receiver side. PureRelative, - // Similar to |Absolute|, but even if there's an active async scroll animation - // the position update will NOT cancel the async scroll animation. - MergeableAbsolute, }; enum class ScrollTriggeredByScript : bool { No, Yes }; @@ -86,9 +83,6 @@ class ScrollPositionUpdate { ScrollMode aMode, const nsPoint& aDelta); - static ScrollPositionUpdate NewMergeableScroll(ScrollOrigin aOrigin, - nsPoint aDestination); - bool operator==(const ScrollPositionUpdate& aOther) const; MainThreadScrollGeneration GetGeneration() const; diff --git a/layout/generic/StickyScrollContainer.cpp b/layout/generic/StickyScrollContainer.cpp index 2e9d32ab5f..416bbbf4c4 100644 --- a/layout/generic/StickyScrollContainer.cpp +++ b/layout/generic/StickyScrollContainer.cpp @@ -186,12 +186,17 @@ void StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, // Containing block limits for the position of aFrame relative to its parent. // The margin box of the sticky element stays within the content box of the - // contaning-block element. + // containing-block element. if (cbFrame == scrolledFrame) { // cbFrame is the scrolledFrame, and it won't have continuations. Unlike the - // else clause, we consider scrollable overflow rect because and the union - // of its in-flow rects doesn't include the scrollable overflow area. + // else clause, we consider scrollable overflow rect because the union of + // its in-flow rects doesn't include the scrollable overflow area. We need + // to subtract the padding however, which _is_ included in the scrollable + // area, since we want the content box. + MOZ_ASSERT(cbFrame->GetUsedBorder() == nsMargin(), + "How did the ::-moz-scrolled-frame end up with border?"); *aContain = cbFrame->ScrollableOverflowRectRelativeToSelf(); + aContain->Deflate(cbFrame->GetUsedPadding()); nsLayoutUtils::TransformRect(cbFrame, aFrame->GetParent(), *aContain); } else { *aContain = nsLayoutUtils::GetAllInFlowRectsUnion( diff --git a/layout/generic/TextOverflow.cpp b/layout/generic/TextOverflow.cpp index f529a2268d..758b61a99b 100644 --- a/layout/generic/TextOverflow.cpp +++ b/layout/generic/TextOverflow.cpp @@ -29,8 +29,7 @@ using mozilla::layout::TextDrawTarget; -namespace mozilla { -namespace css { +namespace mozilla::css { class LazyReferenceRenderingDrawTargetGetterFromFrame final : public gfxFontGroup::LazyReferenceDrawTargetGetter { @@ -165,8 +164,7 @@ class nsDisplayTextOverflowMarker final : public nsPaintedDisplayItem { virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const override { *aSnap = false; - nsRect shadowRect = nsLayoutUtils::GetTextShadowRectsUnion(mRect, mFrame); - return mRect.Union(shadowRect); + return nsLayoutUtils::GetTextShadowRectsUnion(mRect, mFrame); } virtual nsRect GetComponentAlphaBounds( @@ -835,9 +833,10 @@ bool TextOverflow::CanHaveOverflowMarkers(nsBlockFrame* aBlockFrame, return false; } - // Skip ComboboxControlFrame because it would clip the drop-down arrow. - // Its anon block inherits 'text-overflow' and does what is expected. - if (aBlockFrame->IsComboboxControlFrame()) { + // Skip the combobox anonymous block because it would clip the drop-down + // arrow. The inner label inherits 'text-overflow' and does the right thing. + if (aBlockFrame->GetParent() && + aBlockFrame->GetParent()->IsComboboxControlFrame()) { return false; } @@ -932,5 +931,4 @@ void TextOverflow::Marker::SetupString(nsIFrame* aFrame) { mInitialized = true; } -} // namespace css -} // namespace mozilla +} // namespace mozilla::css diff --git a/layout/generic/ViewportFrame.cpp b/layout/generic/ViewportFrame.cpp index 1837441e86..812aaa8e33 100644 --- a/layout/generic/ViewportFrame.cpp +++ b/layout/generic/ViewportFrame.cpp @@ -21,7 +21,6 @@ #include "nsCanvasFrame.h" #include "nsLayoutUtils.h" #include "nsSubDocumentFrame.h" -#include "nsIMozBrowserFrame.h" #include "nsPlaceholderFrame.h" #include "MobileViewportManager.h" diff --git a/layout/generic/WritingModes.h b/layout/generic/WritingModes.h index 2f28b73775..e84c5e276d 100644 --- a/layout/generic/WritingModes.h +++ b/layout/generic/WritingModes.h @@ -62,11 +62,11 @@ constexpr auto AllLogicalSides() { eLogicalSideIEnd); } -enum LogicalCorner { - eLogicalCornerBStartIStart = 0, - eLogicalCornerBStartIEnd = 1, - eLogicalCornerBEndIEnd = 2, - eLogicalCornerBEndIStart = 3 +enum class LogicalCorner : uint8_t { + BStartIStart, + BStartIEnd, + BEndIEnd, + BEndIStart, }; // Physical axis constants. diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 51589c9aba..1c184739d7 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -63,14 +63,12 @@ load 363722-1.html load 363722-2.html load 364220.html load 364407-1.html -load 364686-1.xhtml load 366021-1.xhtml load 366667-1.html load 366952-1.html load 367246-1.html load 367360.html load 368330-1.html -load 368461-1.xhtml load 368568.html load 368752.html load 368860-1.html @@ -86,7 +84,6 @@ load 370174-2.html load 370174-3.html load 370699-1.html load 370794-1.html -load 370884-1.xhtml load 371348-1.xhtml load 371561-1.html load 371566-1.xhtml @@ -105,11 +102,8 @@ load 381152-1.html load 382129-1.xhtml load 382131-1.html load 382199-1.html -load 382208-1.xhtml load 382262-1.html -load 382396-1.xhtml load 383089-1.html -load 385265-1.xhtml load 385295-1.xhtml load 385344-1.html load 385344-2.html @@ -158,7 +152,6 @@ load 394818-1.html load 394818-2.html load 394820-1.html load 395316-1.html -load 395450-1.xhtml load 397007-1.html load 397187-1.html load 397844-1.xhtml @@ -316,7 +309,6 @@ load 467487-1.html load 467493-1.html load 467493-2.html load 467875-1.xhtml -load 467914-1.html asserts-if(winWidget,0-2) load 468207-1.html # bug 1647811 load 468771-1.xhtml load 468771-2.xhtml @@ -391,7 +383,6 @@ load 541714-1.html load 541714-2.html load 542136-1.html load 545571-1.html -load 547843-1.xhtml load 551635-1.html load 553504-1.xhtml load 564368-1.xhtml @@ -457,7 +448,6 @@ load 683712.html load 688996-1.html load 688996-2.html load 691210.html -load 700031.xhtml load 709398-1.html load 718516.html load 723108.html @@ -553,7 +543,7 @@ load 942794-1.html load 943509-1.html asserts(2-3) load 944909-1.html # bogus sizes load 946167-1.html -skip-if(Android&&browserIsRemote) load 947158.html # bug 1507207 +skip-if(Android) load 947158.html # bug 1507207 load 949932.html load 963878.html load 964078.html @@ -587,7 +577,6 @@ load 1137723-2.html asserts(1) load 1140043-1.html asserts(1) load 1140043-2.html asserts(1) load 1140043-3.html -load 1140268-1.html load 1145768.html load 1145931.html load 1145950-1.html @@ -631,7 +620,7 @@ load first-letter-638937-1.html load first-letter-638937-2.html load flex-nested-abspos-1.html pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLine,15) asserts(0-100) load font-inflation-762332.html # bug 762332 -asserts-if(Android||OSX,0-2) load outline-on-frameset.xhtml # bug 762332, bug 1594135 +asserts-if(Android||cocoaWidget,0-2) load outline-on-frameset.xhtml # bug 762332, bug 1594135 load summary-position-out-of-flow.html pref(widget.windows.window_occlusion_tracking.enabled,false) load text-overflow-bug666751-1.html # Bug 1819154 pref(widget.windows.window_occlusion_tracking.enabled,false) load text-overflow-bug666751-2.html # Bug 1819154 @@ -672,7 +661,7 @@ load 1343552-2.html load 1346454-1.html load 1346454-2.html load 1349650.html -asserts-if(browserIsRemote,0-5) load 1349816-1.html # bug 1350352 +asserts(0-5) load 1349816-1.html # bug 1350352 load 1350372.html load 1364361-1.html load 1367413-1.html diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 54976ecc47..a25e4e996e 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -41,7 +41,6 @@ #include "nsPresContextInlines.h" #include "nsHTMLParts.h" #include "nsGkAtoms.h" -#include "nsAttrValueInlines.h" #include "mozilla/Sprintf.h" #include "nsFloatManager.h" #include "prenv.h" @@ -50,22 +49,15 @@ #include <algorithm> #include "nsLayoutUtils.h" #include "nsDisplayList.h" -#include "nsCSSAnonBoxes.h" #include "nsCSSFrameConstructor.h" #include "TextOverflow.h" #include "nsIFrameInlines.h" #include "CounterStyleManager.h" -#include "mozilla/dom/HTMLDetailsElement.h" -#include "mozilla/dom/HTMLSummaryElement.h" #include "mozilla/dom/Selection.h" #include "mozilla/PresShell.h" #include "mozilla/RestyleManager.h" #include "mozilla/ServoStyleSet.h" -#include "mozilla/Telemetry.h" #include "nsFlexContainerFrame.h" -#include "nsFileControlFrame.h" -#include "nsMathMLContainerFrame.h" -#include "nsSelectsAreaFrame.h" #include "nsBidiPresUtils.h" @@ -95,7 +87,7 @@ static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock) { static void MarkSameFloatManagerLinesDirty(nsBlockFrame* aBlock) { nsBlockFrame* blockWithFloatMgr = aBlock; - while (!blockWithFloatMgr->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS)) { + while (!blockWithFloatMgr->HasAnyStateBits(NS_BLOCK_BFC)) { nsBlockFrame* bf = do_QueryFrame(blockWithFloatMgr->GetParent()); if (!bf) { break; @@ -130,47 +122,37 @@ static bool BlockHasAnyFloats(nsIFrame* aFrame) { return false; } -/** - * Determines whether the given frame is visible or has - * visible children that participate in the same line. Frames - * that are not line participants do not have their - * children checked. - */ -static bool FrameHasVisibleInlineContent(nsIFrame* aFrame) { +// Determines whether the given frame is visible text or has visible text that +// participate in the same line. Frames that are not line participants do not +// have their children checked. +static bool FrameHasVisibleInlineText(nsIFrame* aFrame) { MOZ_ASSERT(aFrame, "Frame argument cannot be null"); - - if (aFrame->StyleVisibility()->IsVisible()) { - return true; + if (!aFrame->IsLineParticipant()) { + return false; } - - if (aFrame->IsLineParticipant()) { - for (nsIFrame* kid : aFrame->PrincipalChildList()) { - if (kid->StyleVisibility()->IsVisible() || - FrameHasVisibleInlineContent(kid)) { - return true; - } + if (aFrame->IsTextFrame()) { + return aFrame->StyleVisibility()->IsVisible() && + NS_GET_A(aFrame->StyleText()->mWebkitTextFillColor.CalcColor( + aFrame)) != 0; + } + for (nsIFrame* kid : aFrame->PrincipalChildList()) { + if (FrameHasVisibleInlineText(kid)) { + return true; } } return false; } -/** - * Determines whether any of the frames descended from the - * given line have inline content with 'visibility: visible'. - * This function calls FrameHasVisibleInlineContent to process - * each frame in the line's child list. - */ -static bool LineHasVisibleInlineContent(nsLineBox* aLine) { +// Determines whether any of the frames from the given line have visible text. +static bool LineHasVisibleInlineText(nsLineBox* aLine) { nsIFrame* kid = aLine->mFirstChild; int32_t n = aLine->GetChildCount(); while (n-- > 0) { - if (FrameHasVisibleInlineContent(kid)) { + if (FrameHasVisibleInlineText(kid)) { return true; } - kid = kid->GetNextSibling(); } - return false; } @@ -446,13 +428,6 @@ nsBlockFrame* NS_NewBlockFrame(PresShell* aPresShell, ComputedStyle* aStyle) { return new (aPresShell) nsBlockFrame(aStyle, aPresShell->GetPresContext()); } -nsBlockFrame* NS_NewBlockFormattingContext(PresShell* aPresShell, - ComputedStyle* aComputedStyle) { - nsBlockFrame* blockFrame = NS_NewBlockFrame(aPresShell, aComputedStyle); - blockFrame->AddStateBits(NS_BLOCK_STATIC_BFC); - return blockFrame; -} - NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame) nsBlockFrame::~nsBlockFrame() = default; @@ -1194,7 +1169,7 @@ static LogicalSize CalculateContainingBlockSizeForAbsolutes( */ static const nsBlockFrame* GetAsLineClampDescendant(const nsIFrame* aFrame) { if (const nsBlockFrame* block = do_QueryFrame(aFrame)) { - if (!block->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS)) { + if (!block->HasAnyStateBits(NS_BLOCK_BFC)) { return block; } } @@ -1211,7 +1186,7 @@ static bool IsLineClampRoot(const nsBlockFrame* aFrame) { return false; } - if (!aFrame->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS)) { + if (!aFrame->HasAnyStateBits(NS_BLOCK_BFC)) { return false; } @@ -2023,6 +1998,9 @@ nsReflowStatus nsBlockFrame::TrialReflow(nsPresContext* aPresContext, ComputeFinalSize(aReflowInput, state, aMetrics); aTrialState.mContainerWidth = state.ContainerSize().width; + // Align content + AlignContent(state, aMetrics, aTrialState.mBlockEndEdgeOfChildren); + return state.mReflowStatus; } @@ -2191,6 +2169,11 @@ nscoord nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput, aState.ClearFloats(blockEndEdgeOfChildren, StyleClear::Both); } + // undo cached alignment shift for sizing purposes + // (we used shifted positions because the float manager uses them) + blockEndEdgeOfChildren -= aState.mAlignContentShift; + aState.UndoAlignContentShift(); + if (NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize()) { // Note: We don't use blockEndEdgeOfChildren because it includes the // previous margin. @@ -2221,97 +2204,83 @@ nscoord nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput, // calculated from aspect-ratio. i.e. Don't carry out block margin-end if it // is replaced by the block size from aspect-ratio and inline size. aMetrics.mCarriedOutBEndMargin.Zero(); - } else { - Maybe<nscoord> containBSize = ContainIntrinsicBSize( - IsComboboxControlFrame() ? NS_UNCONSTRAINEDSIZE : 0); - if (containBSize && *containBSize != NS_UNCONSTRAINEDSIZE) { - // If we're size-containing in block axis and we don't have a specified - // block size, then our final size should actually be computed from only - // our border, padding and contain-intrinsic-block-size, ignoring the - // actual contents. Hence this case is a simplified version of the case - // below. - // - // NOTE: We exempt the nsComboboxControlFrame subclass from taking this - // special case when it has 'contain-intrinsic-block-size: none', because - // comboboxes implicitly honors the size-containment behavior on its - // nsComboboxDisplayFrame child (which it shrinkwraps) rather than on the - // nsComboboxControlFrame. (Moreover, the DisplayFrame child doesn't even - // need any special content-size-ignoring behavior in its reflow method, - // because that method just resolves "auto" BSize values to one - // line-height rather than by measuring its contents' BSize.) - nscoord contentBSize = *containBSize; - nscoord autoBSize = - aReflowInput.ApplyMinMaxBSize(contentBSize, aState.mConsumedBSize); + } else if (Maybe<nscoord> containBSize = ContainIntrinsicBSize()) { + // If we're size-containing in block axis and we don't have a specified + // block size, then our final size should actually be computed from only + // our border, padding and contain-intrinsic-block-size, ignoring the + // actual contents. Hence this case is a simplified version of the case + // below. + nscoord contentBSize = *containBSize; + nscoord autoBSize = + aReflowInput.ApplyMinMaxBSize(contentBSize, aState.mConsumedBSize); + aMetrics.mCarriedOutBEndMargin.Zero(); + autoBSize += borderPadding.BStartEnd(wm); + finalSize.BSize(wm) = autoBSize; + } else if (aState.mReflowStatus.IsInlineBreakBefore()) { + // Our parent is expected to push this frame to the next page/column so + // what size we set here doesn't really matter. + finalSize.BSize(wm) = aReflowInput.AvailableBSize(); + } else if (aState.mReflowStatus.IsComplete()) { + const nscoord lineClampedContentBlockEndEdge = + ApplyLineClamp(aReflowInput, this, blockEndEdgeOfChildren); + + const nscoord bpBStart = borderPadding.BStart(wm); + const nscoord contentBSize = blockEndEdgeOfChildren - bpBStart; + const nscoord lineClampedContentBSize = + lineClampedContentBlockEndEdge - bpBStart; + + const nscoord autoBSize = aReflowInput.ApplyMinMaxBSize( + lineClampedContentBSize, aState.mConsumedBSize); + if (autoBSize != contentBSize) { + // Our min-block-size, max-block-size, or -webkit-line-clamp value made + // our bsize change. Don't carry out our kids' block-end margins. aMetrics.mCarriedOutBEndMargin.Zero(); - autoBSize += borderPadding.BStartEnd(wm); - finalSize.BSize(wm) = autoBSize; - } else if (aState.mReflowStatus.IsInlineBreakBefore()) { - // Our parent is expected to push this frame to the next page/column so - // what size we set here doesn't really matter. - finalSize.BSize(wm) = aReflowInput.AvailableBSize(); - } else if (aState.mReflowStatus.IsComplete()) { - const nscoord lineClampedContentBlockEndEdge = - ApplyLineClamp(aReflowInput, this, blockEndEdgeOfChildren); - - const nscoord bpBStart = borderPadding.BStart(wm); - const nscoord contentBSize = blockEndEdgeOfChildren - bpBStart; - const nscoord lineClampedContentBSize = - lineClampedContentBlockEndEdge - bpBStart; - - const nscoord autoBSize = aReflowInput.ApplyMinMaxBSize( - lineClampedContentBSize, aState.mConsumedBSize); - if (autoBSize != contentBSize) { - // Our min-block-size, max-block-size, or -webkit-line-clamp value made - // our bsize change. Don't carry out our kids' block-end margins. - aMetrics.mCarriedOutBEndMargin.Zero(); - } - nscoord bSize = autoBSize + borderPadding.BStartEnd(wm); - if (MOZ_UNLIKELY(autoBSize > contentBSize && - bSize > aReflowInput.AvailableBSize() && - aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)) { - // Applying `min-size` made us overflow our available size. - // Clamp it and report that we're Incomplete, or BreakBefore if we have - // 'break-inside: avoid' that is applicable. - bSize = aReflowInput.AvailableBSize(); - if (ShouldAvoidBreakInside(aReflowInput)) { - aState.mReflowStatus.SetInlineLineBreakBeforeAndReset(); - } else { - aState.mReflowStatus.SetIncomplete(); - } + } + nscoord bSize = autoBSize + borderPadding.BStartEnd(wm); + if (MOZ_UNLIKELY(autoBSize > contentBSize && + bSize > aReflowInput.AvailableBSize() && + aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)) { + // Applying `min-size` made us overflow our available size. + // Clamp it and report that we're Incomplete, or BreakBefore if we have + // 'break-inside: avoid' that is applicable. + bSize = aReflowInput.AvailableBSize(); + if (ShouldAvoidBreakInside(aReflowInput)) { + aState.mReflowStatus.SetInlineLineBreakBeforeAndReset(); + } else { + aState.mReflowStatus.SetIncomplete(); } - finalSize.BSize(wm) = bSize; - } else { - NS_ASSERTION( - aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE, - "Shouldn't be incomplete if availableBSize is UNCONSTRAINED."); - nscoord bSize = std::max(aState.mBCoord, aReflowInput.AvailableBSize()); - if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) { - // This should never happen, but it does. See bug 414255 - bSize = aState.mBCoord; - } - const nscoord maxBSize = aReflowInput.ComputedMaxBSize(); - if (maxBSize != NS_UNCONSTRAINEDSIZE && - aState.mConsumedBSize + bSize - borderPadding.BStart(wm) > maxBSize) { - // Compute this fragment's block-size, with the max-block-size - // constraint taken into consideration. - const nscoord clampedBSizeWithoutEndBP = - std::max(0, maxBSize - aState.mConsumedBSize) + - borderPadding.BStart(wm); - const nscoord clampedBSize = - clampedBSizeWithoutEndBP + borderPadding.BEnd(wm); - if (clampedBSize <= aReflowInput.AvailableBSize()) { - // We actually fit after applying `max-size` so we should be - // Overflow-Incomplete instead. - bSize = clampedBSize; - aState.mReflowStatus.SetOverflowIncomplete(); - } else { - // We cannot fit after applying `max-size` with our block-end BP, so - // we should draw it in our next continuation. - bSize = clampedBSizeWithoutEndBP; - } + } + finalSize.BSize(wm) = bSize; + } else { + NS_ASSERTION(aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE, + "Shouldn't be incomplete if availableBSize is UNCONSTRAINED."); + nscoord bSize = std::max(aState.mBCoord, aReflowInput.AvailableBSize()); + if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) { + // This should never happen, but it does. See bug 414255 + bSize = aState.mBCoord; + } + const nscoord maxBSize = aReflowInput.ComputedMaxBSize(); + if (maxBSize != NS_UNCONSTRAINEDSIZE && + aState.mConsumedBSize + bSize - borderPadding.BStart(wm) > maxBSize) { + // Compute this fragment's block-size, with the max-block-size + // constraint taken into consideration. + const nscoord clampedBSizeWithoutEndBP = + std::max(0, maxBSize - aState.mConsumedBSize) + + borderPadding.BStart(wm); + const nscoord clampedBSize = + clampedBSizeWithoutEndBP + borderPadding.BEnd(wm); + if (clampedBSize <= aReflowInput.AvailableBSize()) { + // We actually fit after applying `max-size` so we should be + // Overflow-Incomplete instead. + bSize = clampedBSize; + aState.mReflowStatus.SetOverflowIncomplete(); + } else { + // We cannot fit after applying `max-size` with our block-end BP, so + // we should draw it in our next continuation. + bSize = clampedBSizeWithoutEndBP; } - finalSize.BSize(wm) = bSize; } + finalSize.BSize(wm) = bSize; } if (IsTrueOverflowContainer()) { @@ -2381,6 +2350,77 @@ nscoord nsBlockFrame::ComputeFinalSize(const ReflowInput& aReflowInput, return blockEndEdgeOfChildren; } +void nsBlockFrame::AlignContent(BlockReflowState& aState, + ReflowOutput& aMetrics, + nscoord aBEndEdgeOfChildren) { + if (!StaticPrefs::layout_css_align_content_blocks_enabled()) { + return; + } + + StyleAlignFlags alignment = StylePosition()->mAlignContent.primary; + alignment &= ~StyleAlignFlags::FLAG_BITS; + + // Short circuit + const bool isCentered = alignment == StyleAlignFlags::CENTER || + alignment == StyleAlignFlags::SPACE_AROUND || + alignment == StyleAlignFlags::SPACE_EVENLY; + const bool isEndAlign = alignment == StyleAlignFlags::END || + alignment == StyleAlignFlags::FLEX_END || + alignment == StyleAlignFlags::LAST_BASELINE; + if (!isEndAlign && !isCentered && !aState.mAlignContentShift) { + // desired shift = 0, no cached shift to undo + return; + } + + // NOTE: ComputeFinalSize already called aState.UndoAlignContentShift(), + // so metrics no longer include cached shift. + // NOTE: Content is currently positioned at cached shift + // NOTE: Content has been fragmented against 0-shift assumption. + + // Calculate shift + nscoord shift = 0; + WritingMode wm = aState.mReflowInput.GetWritingMode(); + if ((isCentered || isEndAlign) && !mLines.empty() && + aState.mReflowStatus.IsFullyComplete() && !GetPrevInFlow()) { + nscoord availB = aState.mReflowInput.AvailableBSize(); + nscoord endB = aMetrics.BSize(wm) - aState.BorderPadding().BEnd(wm); + shift = std::min(availB, endB) - aBEndEdgeOfChildren; + + // note: these measures all include start BP, so it subtracts out + if (!(StylePosition()->mAlignContent.primary & StyleAlignFlags::UNSAFE)) { + shift = std::max(0, shift); + } + if (isCentered) { + shift = shift / 2; + } + } + // else: zero shift if start-aligned or if fragmented + + nscoord delta = shift - aState.mAlignContentShift; + if (delta) { + // Shift children + LogicalPoint translation(wm, 0, delta); + for (nsLineBox& line : Lines()) { + SlideLine(aState, &line, delta); + } + for (nsIFrame* kid : GetChildList(FrameChildListID::Float)) { + kid->MovePositionBy(wm, translation); + nsContainerFrame::PlaceFrameView(kid); + } + if (HasOutsideMarker() && !mLines.empty()) { + nsIFrame* marker = GetOutsideMarker(); + marker->MovePositionBy(wm, translation); + } + } + + if (shift) { + // Cache shift + SetProperty(AlignContentShift(), shift); + } else { + RemoveProperty(AlignContentShift()); + } +} + void nsBlockFrame::ConsiderBlockEndEdgeOfChildren( OverflowAreas& aOverflowAreas, nscoord aBEndEdgeOfChildren, const nsStyleDisplay* aDisplay) const { @@ -3153,9 +3193,12 @@ bool nsBlockFrame::ReflowDirtyLines(BlockReflowState& aState) { // Immediately fragment for page-name. It is possible we could break // out of the loop right here, but this should make it more similar to // what happens when reflow causes fragmentation. - PushTruncatedLine(aState, line, &keepGoing); + // Set the page name, so that PushTruncatedLine does not need to + // recalculate the new page name. PresShell()->FrameConstructor()->SetNextPageContentFramePageName( nextPageName ? nextPageName : GetAutoPageValue()); + PushTruncatedLine(aState, line, &keepGoing, + ComputeNewPageNameIfNeeded::No); } else { // Reflow the dirty line. If it's an incremental reflow, then force // it to invalidate the dirty area if necessary @@ -3867,7 +3910,7 @@ bool nsBlockFrame::IsSelfEmpty() { // Blocks which are margin-roots (including inline-blocks) cannot be treated // as empty for margin-collapsing and other purposes. They're more like // replaced elements. - if (HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS)) { + if (HasAnyStateBits(NS_BLOCK_BFC)) { return false; } @@ -4730,11 +4773,25 @@ void nsBlockFrame::SetBreakBeforeStatusBeforeLine(BlockReflowState& aState, *aKeepReflowGoing = false; } -void nsBlockFrame::PushTruncatedLine(BlockReflowState& aState, - LineIterator aLine, - bool* aKeepReflowGoing) { +void nsBlockFrame::PushTruncatedLine( + BlockReflowState& aState, LineIterator aLine, bool* aKeepReflowGoing, + ComputeNewPageNameIfNeeded aComputeNewPageName) { PushLines(aState, aLine.prev()); *aKeepReflowGoing = false; + + if (aComputeNewPageName == ComputeNewPageNameIfNeeded::Yes) { + // mCanHaveClassABreakpoints can only be true during paginated reflow, and + // we expect this function to only be called when the available bsize is + // constrained. + const WritingMode wm = GetWritingMode(); + const bool canBreakForPageNames = + aState.mReflowInput.mFlags.mCanHaveClassABreakpoints && + !PresShell()->GetRootFrame()->GetWritingMode().IsOrthogonalTo(wm); + if (canBreakForPageNames) { + PresShell()->FrameConstructor()->MaybeSetNextPageContentFramePageName( + aLine->mFirstChild); + } + } aState.mReflowStatus.SetIncomplete(); } @@ -5177,7 +5234,7 @@ void nsBlockFrame::SplitFloat(BlockReflowState& aState, nsIFrame* aFloat, } aState.AppendPushedFloatChain(nextInFlow); - if (MOZ_LIKELY(!HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS)) || + if (MOZ_LIKELY(!HasAnyStateBits(NS_BLOCK_BFC)) || MOZ_UNLIKELY(IsTrueOverflowContainer())) { aState.mReflowStatus.SetOverflowIncomplete(); } else { @@ -6290,7 +6347,7 @@ nsContainerFrame* nsBlockFrame::GetRubyContentPseudoFrame() { auto* firstChild = PrincipalChildList().FirstChild(); if (firstChild && firstChild->IsRubyFrame() && firstChild->Style()->GetPseudoType() == - mozilla::PseudoStyleType::blockRubyContent) { + PseudoStyleType::blockRubyContent) { return static_cast<nsContainerFrame*>(firstChild); } return nullptr; @@ -6425,15 +6482,80 @@ nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame, *aFoundValidLine = FindValidLine(); } +static bool AnonymousBoxIsBFC(const ComputedStyle* aStyle) { + switch (aStyle->GetPseudoType()) { + case PseudoStyleType::fieldsetContent: + case PseudoStyleType::columnContent: + case PseudoStyleType::buttonContent: + case PseudoStyleType::cellContent: + case PseudoStyleType::scrolledContent: + case PseudoStyleType::anonymousItem: + return true; + default: + return false; + } +} + static bool StyleEstablishesBFC(const ComputedStyle* aStyle) { // paint/layout containment boxes and multi-column containers establish an // independent formatting context. // https://drafts.csswg.org/css-contain/#containment-paint // https://drafts.csswg.org/css-contain/#containment-layout + // https://drafts.csswg.org/css-align/#distribution-block // https://drafts.csswg.org/css-multicol/#columns - return aStyle->StyleDisplay()->IsContainPaint() || - aStyle->StyleDisplay()->IsContainLayout() || - aStyle->GetPseudoType() == PseudoStyleType::columnContent; + const auto* disp = aStyle->StyleDisplay(); + return disp->IsContainPaint() || disp->IsContainLayout() || + disp->DisplayInside() == StyleDisplayInside::FlowRoot || + disp->IsAbsolutelyPositionedStyle() || disp->IsFloatingStyle() || + aStyle->StylePosition()->mAlignContent.primary != + StyleAlignFlags::NORMAL || + aStyle->IsRootElementStyle() || AnonymousBoxIsBFC(aStyle); +} + +static bool EstablishesBFC(const nsBlockFrame* aFrame) { + if (aFrame->HasAnyClassFlag(LayoutFrameClassFlags::BlockFormattingContext)) { + return true; + } + + if (nsIFrame* parent = aFrame->GetParent()) { + if (parent->IsFieldSetFrame()) { + // A rendered legend always establishes a new formatting context, and so + // does the fieldset content frame, so we can just return true here. + // https://html.spec.whatwg.org/#rendered-legend + return true; + } + + const auto wm = aFrame->GetWritingMode(); + const auto parentWM = parent->GetWritingMode(); + if (wm.GetBlockDir() != parentWM.GetBlockDir() || + wm.IsVerticalSideways() != parentWM.IsVerticalSideways()) { + // If a box has a different writing-mode value than its containing block + // [...] if the box is a block container, then it establishes a new block + // formatting context. + // https://drafts.csswg.org/css-writing-modes/#block-flow + return true; + } + } + + if (aFrame->IsColumnSpan()) { + return true; + } + + if (aFrame->IsSuppressedScrollableBlockForPrint()) { + return true; + } + + const auto* style = aFrame->Style(); + if (style->GetPseudoType() == PseudoStyleType::marker) { + if (aFrame->GetParent() && + aFrame->GetParent()->StyleList()->mListStylePosition == + StyleListStylePosition::Outside) { + // An outside ::marker needs to be an independent formatting context + // to avoid being influenced by the float manager etc. + return true; + } + } + return StyleEstablishesBFC(style); } void nsBlockFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) { @@ -6442,24 +6564,16 @@ void nsBlockFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) { return; } - // If NS_BLOCK_STATIC_BFC flag was set when the frame was initialized, it - // remains set during the lifetime of the frame and always forces it to be - // treated as a BFC, independently of the value of NS_BLOCK_DYNAMIC_BFC. - // Consequently, we don't bother invalidating or updating that latter flag. - if (HasAnyStateBits(NS_BLOCK_STATIC_BFC)) { - return; - } - - bool isBFC = StyleEstablishesBFC(Style()); - if (StyleEstablishesBFC(aOldStyle) != isBFC) { + const bool isBFC = EstablishesBFC(this); + if (HasAnyStateBits(NS_BLOCK_BFC) != isBFC) { if (MaybeHasFloats()) { // If the frame contains floats, this update may change their float // manager. Be safe by dirtying all descendant lines of the nearest // ancestor's float manager. - RemoveStateBits(NS_BLOCK_DYNAMIC_BFC); + RemoveStateBits(NS_BLOCK_BFC); MarkSameFloatManagerLinesDirty(this); } - AddOrRemoveStateBits(NS_BLOCK_DYNAMIC_BFC, isBFC); + AddOrRemoveStateBits(NS_BLOCK_BFC, isBFC); } } @@ -7469,11 +7583,11 @@ void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, // We'll try to draw an accessibility backplate behind text (to ensure it's // readable over any possible background-images), if all of the following // hold: - // (A) the backplate feature is preffed on - // (B) we are not honoring the document colors + // (A) we are not honoring the document colors + // (B) the backplate feature is preffed on // (C) the force color adjust property is set to auto - if (StaticPrefs::browser_display_permit_backplate() && - PresContext()->ForcingColors() && !IsComboboxControlFrame() && + if (PresContext()->ForcingColors() && + StaticPrefs::browser_display_permit_backplate() && StyleText()->mForcedColorAdjust != StyleForcedColorAdjust::None) { backplateColor.emplace(GetBackplateColor(this)); } @@ -7565,7 +7679,7 @@ void nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, } lastY = lineArea.y; lastYMost = lineArea.YMost(); - if (lineInLine && backplateColor && LineHasVisibleInlineContent(line)) { + if (lineInLine && backplateColor && LineHasVisibleInlineText(line)) { nsRect lineBackplate = GetLineTextArea(line, aBuilder) + aBuilder->ToReferenceFrame(this); if (curBackplateArea.IsEmpty()) { @@ -7762,35 +7876,15 @@ void nsBlockFrame::ChildIsDirty(nsIFrame* aChild) { nsContainerFrame::ChildIsDirty(aChild); } -static bool AlwaysEstablishesBFC(const nsBlockFrame* aFrame) { - switch (aFrame->Type()) { - case LayoutFrameType::ColumnSetWrapper: - // CSS Multi-column level 1 section 2: A multi-column container - // establishes a new block formatting context, as per CSS 2.1 section - // 9.4.1. - case LayoutFrameType::ComboboxControl: - return true; - case LayoutFrameType::Block: - return static_cast<const nsFileControlFrame*>(do_QueryFrame(aFrame)) || - // Ensure that the options inside the select aren't expanded by - // right floats outside the select. - static_cast<const nsSelectsAreaFrame*>(do_QueryFrame(aFrame)) || - // See bug 1373767 and bug 353894. - static_cast<const nsMathMLmathBlockFrame*>(do_QueryFrame(aFrame)); - default: - return false; - } -} - void nsBlockFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) { // These are all the block specific frame bits, they are copied from // the prev-in-flow to a newly created next-in-flow, except for the // NS_BLOCK_FLAGS_NON_INHERITED_MASK bits below. constexpr nsFrameState NS_BLOCK_FLAGS_MASK = - NS_BLOCK_BFC_STATE_BITS | NS_BLOCK_CLIP_PAGINATED_OVERFLOW | - NS_BLOCK_HAS_FIRST_LETTER_STYLE | NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER | - NS_BLOCK_HAS_FIRST_LETTER_CHILD | NS_BLOCK_FRAME_HAS_INSIDE_MARKER; + NS_BLOCK_BFC | NS_BLOCK_HAS_FIRST_LETTER_STYLE | + NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER | NS_BLOCK_HAS_FIRST_LETTER_CHILD | + NS_BLOCK_FRAME_HAS_INSIDE_MARKER; // This is the subset of NS_BLOCK_FLAGS_MASK that is NOT inherited // by default. They should only be set on the first-in-flow. @@ -7812,37 +7906,12 @@ void nsBlockFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION); } - // A display:flow-root box establishes a block formatting context. - // - // If a box has a different writing-mode value than its containing block: - // ... - // If the box is a block container, then it establishes a new block - // formatting context. - // (https://drafts.csswg.org/css-writing-modes/#block-flow) - // - // If the box has contain: paint or contain:layout (or contain:strict), - // then it should also establish a formatting context. - // - // Per spec, a column-span always establishes a new block formatting context. - // - // Other more specific frame types also always establish a BFC. - // - if (StyleDisplay()->mDisplay == mozilla::StyleDisplay::FlowRoot || - (GetParent() && - (GetWritingMode().GetBlockDir() != - GetParent()->GetWritingMode().GetBlockDir() || - GetWritingMode().IsVerticalSideways() != - GetParent()->GetWritingMode().IsVerticalSideways())) || - IsColumnSpan() || AlwaysEstablishesBFC(this)) { - AddStateBits(NS_BLOCK_STATIC_BFC); - } - - if (StyleEstablishesBFC(Style())) { - AddStateBits(NS_BLOCK_DYNAMIC_BFC); + if (EstablishesBFC(this)) { + AddStateBits(NS_BLOCK_BFC); } if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER) && - HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS)) { + HasAnyStateBits(NS_BLOCK_BFC)) { AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); } } @@ -7866,13 +7935,13 @@ void nsBlockFrame::SetInitialChildList(ChildListID aListID, (pseudo == PseudoStyleType::cellContent && !GetParent()->Style()->IsPseudoOrAnonBox()) || pseudo == PseudoStyleType::fieldsetContent || - pseudo == PseudoStyleType::buttonContent || + (pseudo == PseudoStyleType::buttonContent && + !GetParent()->IsComboboxControlFrame()) || pseudo == PseudoStyleType::columnContent || (pseudo == PseudoStyleType::scrolledContent && !GetParent()->IsListControlFrame()) || pseudo == PseudoStyleType::mozSVGText) && - !IsComboboxControlFrame() && !IsMathMLFrame() && - !IsColumnSetWrapperFrame() && + !IsMathMLFrame() && !IsColumnSetWrapperFrame() && RefPtr<ComputedStyle>(GetFirstLetterStyle(PresContext())) != nullptr; NS_ASSERTION(haveFirstLetterStyle == HasAnyStateBits(NS_BLOCK_HAS_FIRST_LETTER_STYLE), @@ -7895,11 +7964,6 @@ void nsBlockFrame::SetMarkerFrameForListItem(nsIFrame* aMarkerFrame) { SetProperty(InsideMarkerProperty(), aMarkerFrame); AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_MARKER); } else { - if (nsBlockFrame* marker = do_QueryFrame(aMarkerFrame)) { - // An outside ::marker needs to be an independent formatting context - // to avoid being influenced by the float manager etc. - marker->AddStateBits(NS_BLOCK_STATIC_BFC); - } SetProperty(OutsideMarkerProperty(), new (PresShell()) nsFrameList(aMarkerFrame, aMarkerFrame)); AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER); @@ -8100,7 +8164,7 @@ void nsBlockFrame::CheckFloats(BlockReflowState& aState) { void nsBlockFrame::IsMarginRoot(bool* aBStartMarginRoot, bool* aBEndMarginRoot) { nsIFrame* parent = GetParent(); - if (!HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS)) { + if (!HasAnyStateBits(NS_BLOCK_BFC)) { if (!parent || parent->IsFloatContainingBlock()) { *aBStartMarginRoot = false; *aBEndMarginRoot = false; @@ -8127,14 +8191,14 @@ bool nsBlockFrame::BlockNeedsFloatManager(nsIFrame* aBlock) { NS_ASSERTION(aBlock->IsBlockFrameOrSubclass(), "aBlock must be a block"); nsIFrame* parent = aBlock->GetParent(); - return aBlock->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS) || + return aBlock->HasAnyStateBits(NS_BLOCK_BFC) || (parent && !parent->IsFloatContainingBlock()); } /* static */ bool nsBlockFrame::BlockCanIntersectFloats(nsIFrame* aFrame) { return aFrame->IsBlockFrameOrSubclass() && !aFrame->IsReplaced() && - !aFrame->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS); + !aFrame->HasAnyStateBits(NS_BLOCK_BFC); } // Note that this width can vary based on the vertical position. diff --git a/layout/generic/nsBlockFrame.h b/layout/generic/nsBlockFrame.h index 15dd4c3278..9fb909430c 100644 --- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -492,6 +492,14 @@ class nsBlockFrame : public nsContainerFrame { BlockReflowState& aState, ReflowOutput& aMetrics); /** + * Calculates the necessary shift to honor 'align-content' and applies it. + */ + void AlignContent(BlockReflowState& aState, ReflowOutput& aMetrics, + nscoord aBEndEdgeOfChildren); + // Stash the effective align-content shift value between reflows + NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(AlignContentShift, nscoord) + + /** * Helper method for Reflow(). Computes the overflow areas created by our * children, and includes them into aOverflowAreas. */ @@ -540,6 +548,16 @@ class nsBlockFrame : public nsContainerFrame { */ bool IsVisualFormControl(nsPresContext* aPresContext); + /** Whether this block has an effective align-content property */ + bool IsAligned() const { + return StylePosition()->mAlignContent.primary != + mozilla::StyleAlignFlags::NORMAL; + } + + nscoord GetAlignContentShift() const { + return IsAligned() ? GetProperty(AlignContentShift()) : 0; + } + /** * For text-wrap:balance, we iteratively try reflowing with adjusted inline * size to find the "best" result (the tightest size that can be applied @@ -849,12 +867,23 @@ class nsBlockFrame : public nsContainerFrame { bool* aKeepReflowGoing); /** + * Indicates if we need to compute a page name for the next page when pushing + * a truncated line. + * + * Using a value of No saves work when a new page name has already been set + * with nsCSSFrameConstructor::SetNextPageContentFramePageName. + */ + enum class ComputeNewPageNameIfNeeded : uint8_t { Yes, No }; + + /** * Push aLine (and any after it), since it cannot be placed on this * page/column. Set aKeepReflowGoing to false and set * flag aState.mReflowStatus as incomplete. */ void PushTruncatedLine(BlockReflowState& aState, LineIterator aLine, - bool* aKeepReflowGoing); + bool* aKeepReflowGoing, + ComputeNewPageNameIfNeeded aComputeNewPageName = + ComputeNewPageNameIfNeeded::Yes); void SplitLine(BlockReflowState& aState, nsLineLayout& aLineLayout, LineIterator aLine, nsIFrame* aFrame, diff --git a/layout/generic/nsBlockReflowContext.cpp b/layout/generic/nsBlockReflowContext.cpp index ceae9f4849..23f3b83645 100644 --- a/layout/generic/nsBlockReflowContext.cpp +++ b/layout/generic/nsBlockReflowContext.cpp @@ -275,7 +275,7 @@ void nsBlockReflowContext::ReflowBlock(const LogicalRect& aSpace, tI = space.LineLeft(mWritingMode, mContainerSize); tB = mBCoord; - if (!mFrame->HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS)) { + if (!mFrame->HasAnyStateBits(NS_BLOCK_BFC)) { aFrameRI.mBlockDelta = mOuterReflowInput.mBlockDelta + mBCoord - aLine->BStart(); } diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index 9629905968..2577a7a00d 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -1124,7 +1124,6 @@ void nsContainerFrame::ReflowOverflowContainerChildren( // isn't dirty. if (shouldReflowAllKids || frame->IsSubtreeDirty() || ScrollableOverflowExceedsAvailableBSize(frame)) { - // Get prev-in-flow nsIFrame* prevInFlow = frame->GetPrevInFlow(); NS_ASSERTION(prevInFlow, "overflow container frame must have a prev-in-flow"); @@ -1132,38 +1131,45 @@ void nsContainerFrame::ReflowOverflowContainerChildren( frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER), "overflow container frame must have overflow container bit set"); WritingMode wm = frame->GetWritingMode(); - nsSize containerSize = - aContainerSize ? *aContainerSize - : aReflowInput.AvailableSize(wm).GetPhysicalSize(wm); - LogicalRect prevRect = prevInFlow->GetLogicalRect(wm, containerSize); - - // Initialize reflow params - LogicalSize availSpace(wm, prevRect.ISize(wm), - aReflowInput.AvailableSize(wm).BSize(wm)); - ReflowOutput desiredSize(aReflowInput); + // Note: aReflowInput's available inline-size is technically wrong for us + // to hand off to children here, because it doesn't account for the space + // that's been used for the container's margin/border/padding (and some + // other space that a concrete container type, e.g. fieldset and grid [1], + // might reserve before setting up the available space for their + // children). Since we don't have a way to query the specific available + // inline-size each container type used, nor do we know how the container + // computes its non-overflow-container children's inline-size, we just + // unconditionally override the frame's inline-size, so that the available + // inline-size for the children doesn't really matter anyway. + // + // [1] For example, fieldset uses its computed inline-size with padding as + // the available inline-size to reflow its inner child frame. + // https://searchfox.org/mozilla-central/rev/04f7743d94691fa24212fb43099f9d84c3bfc890/layout/forms/nsFieldSetFrame.cpp#535-536 + const LogicalSize availSpace = aReflowInput.AvailableSize(wm); StyleSizeOverrides sizeOverride; - if (frame->IsFlexItem()) { - // A flex item's size is determined by the flex algorithm, not solely by - // its style. Thus, the following overrides are necessary. - // - // Use the overflow container flex item's prev-in-flow inline-size since - // this continuation's inline-size is the same. - sizeOverride.mStyleISize.emplace( - StyleSize::LengthPercentage(LengthPercentage::FromAppUnits( - frame->StylePosition()->mBoxSizing == StyleBoxSizing::Border - ? prevRect.ISize(wm) - : prevInFlow->ContentISize(wm)))); + // We override current continuation's inline-size by using the + // prev-in-flow's inline-size since both should be the same. + sizeOverride.mStyleISize.emplace( + StyleSize::LengthPercentage(LengthPercentage::FromAppUnits( + frame->StylePosition()->mBoxSizing == StyleBoxSizing::Border + ? prevInFlow->ISize(wm) + : prevInFlow->ContentISize(wm)))); + if (frame->IsFlexItem()) { // An overflow container's block-size must be 0. sizeOverride.mStyleBSize.emplace( StyleSize::LengthPercentage(LengthPercentage::FromAppUnits(0))); } + ReflowOutput desiredSize(wm); ReflowInput reflowInput(aPresContext, aReflowInput, frame, availSpace, Nothing(), {}, sizeOverride); - - LogicalPoint pos(wm, prevRect.IStart(wm), 0); + const nsSize containerSize = + aContainerSize ? *aContainerSize + : aReflowInput.AvailableSize(wm).GetPhysicalSize(wm); + const LogicalPoint pos(wm, prevInFlow->IStart(wm, containerSize), 0); nsReflowStatus frameStatus; + ReflowChild(frame, aPresContext, desiredSize, reflowInput, wm, pos, containerSize, aFlags, frameStatus, &tracker); FinishReflowChild(frame, aPresContext, desiredSize, &reflowInput, wm, pos, diff --git a/layout/generic/nsFloatManager.h b/layout/generic/nsFloatManager.h index f947567383..eb12d44e1d 100644 --- a/layout/generic/nsFloatManager.h +++ b/layout/generic/nsFloatManager.h @@ -70,8 +70,8 @@ struct nsFlowAreaRect { /** * nsFloatManager is responsible for implementing CSS's rules for * positioning floats. An nsFloatManager object is created during reflow for - * any block with NS_BLOCK_BFC_STATE_BITS. During reflow, the float manager for - * the nearest such ancestor block is found in ReflowInput::mFloatManager. + * any block with NS_BLOCK_BFC. During reflow, the float manager for the nearest + * such ancestor block is found in ReflowInput::mFloatManager. * * According to the line-relative mappings in CSS Writing Modes spec [1], * line-right and line-left are calculated with respect to the writing mode diff --git a/layout/generic/nsFrameSelection.cpp b/layout/generic/nsFrameSelection.cpp index 002d26c4fa..f1a8c7e27c 100644 --- a/layout/generic/nsFrameSelection.cpp +++ b/layout/generic/nsFrameSelection.cpp @@ -31,7 +31,6 @@ #include "nsFrameTraversal.h" #include "nsString.h" #include "nsISelectionListener.h" -#include "nsContentCID.h" #include "nsDeviceContext.h" #include "nsIContent.h" #include "nsRange.h" @@ -48,7 +47,6 @@ #include "nsGkAtoms.h" #include "nsLayoutUtils.h" -#include "nsLayoutCID.h" #include "nsBidiPresUtils.h" #include "nsTextFrame.h" diff --git a/layout/generic/nsFrameStateBits.h b/layout/generic/nsFrameStateBits.h index a9a1700737..db00deab95 100644 --- a/layout/generic/nsFrameStateBits.h +++ b/layout/generic/nsFrameStateBits.h @@ -541,41 +541,30 @@ FRAME_STATE_BIT(Block, 21, NS_BLOCK_HAS_PUSHED_FLOATS) // 2. This indicates that a block frame should create its own float manager. // This is required by each block frame that can contain floats. The float // manager is used to reserve space for the floated frames. -FRAME_STATE_BIT(Block, 22, NS_BLOCK_STATIC_BFC) +FRAME_STATE_BIT(Block, 22, NS_BLOCK_BFC) -// This is the same as NS_BLOCK_STATIC_BFC but can be updated dynamically after -// the frame construction (e.g. paint/layout containment). -// FIXME(bug 1874823): Try and merge this and NS_BLOCK_STATIC_BFC. -FRAME_STATE_BIT(Block, 23, NS_BLOCK_DYNAMIC_BFC) +FRAME_STATE_BIT(Block, 23, NS_BLOCK_HAS_LINE_CURSOR) -// For testing the relevant bits on a block formatting context: -#define NS_BLOCK_BFC_STATE_BITS (NS_BLOCK_STATIC_BFC | NS_BLOCK_DYNAMIC_BFC) +FRAME_STATE_BIT(Block, 24, NS_BLOCK_HAS_OVERFLOW_LINES) -FRAME_STATE_BIT(Block, 24, NS_BLOCK_HAS_LINE_CURSOR) - -FRAME_STATE_BIT(Block, 25, NS_BLOCK_HAS_OVERFLOW_LINES) - -FRAME_STATE_BIT(Block, 26, NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) +FRAME_STATE_BIT(Block, 25, NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) // Set on any block that has descendant frames in the normal // flow with 'clear' set to something other than 'none' // (including <BR CLEAR="..."> frames) -FRAME_STATE_BIT(Block, 27, NS_BLOCK_HAS_CLEAR_CHILDREN) - -// NS_BLOCK_CLIP_PAGINATED_OVERFLOW is only set in paginated prescontexts, on -// blocks which were forced to not have scrollframes but still need to clip -// the display of their kids. -FRAME_STATE_BIT(Block, 28, NS_BLOCK_CLIP_PAGINATED_OVERFLOW) +FRAME_STATE_BIT(Block, 26, NS_BLOCK_HAS_CLEAR_CHILDREN) // NS_BLOCK_HAS_FIRST_LETTER_STYLE means that the block has first-letter style, // even if it has no actual first-letter frame among its descendants. -FRAME_STATE_BIT(Block, 29, NS_BLOCK_HAS_FIRST_LETTER_STYLE) +FRAME_STATE_BIT(Block, 27, NS_BLOCK_HAS_FIRST_LETTER_STYLE) // NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER and NS_BLOCK_FRAME_HAS_INSIDE_MARKER // means the block has an associated ::marker frame, they are mutually // exclusive. -FRAME_STATE_BIT(Block, 30, NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER) -FRAME_STATE_BIT(Block, 31, NS_BLOCK_FRAME_HAS_INSIDE_MARKER) +FRAME_STATE_BIT(Block, 28, NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER) +FRAME_STATE_BIT(Block, 29, NS_BLOCK_FRAME_HAS_INSIDE_MARKER) + +// bits 30 and 31 free. // NS_BLOCK_HAS_LINE_CLAMP_ELLIPSIS indicates that exactly one line in this // block has the LineClampEllipsis flag set, and that such a line must be found diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index b51f3eccb7..361847b753 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1172,10 +1172,16 @@ void nsHTMLScrollFrame::PlaceScrollArea(ScrollReflowInput& aState, scrolledArea, ReflowChildFlags::Default); } -nscoord nsHTMLScrollFrame::IntrinsicScrollbarGutterSizeAtInlineEdges() { - const bool isVerticalWM = GetWritingMode().IsVertical(); +nscoord nsHTMLScrollFrame::IntrinsicScrollbarGutterSizeAtInlineEdges() const { + const auto wm = GetWritingMode(); + const LogicalMargin gutter(wm, IntrinsicScrollbarGutterSize()); + return gutter.IStartEnd(wm); +} + +nsMargin nsHTMLScrollFrame::IntrinsicScrollbarGutterSize() const { if (PresContext()->UseOverlayScrollbars()) { - return 0; + // Overlay scrollbars do not consume space per spec. + return {}; } const auto* styleForScrollbar = nsLayoutUtils::StyleForScrollbar(this); @@ -1183,28 +1189,30 @@ nscoord nsHTMLScrollFrame::IntrinsicScrollbarGutterSizeAtInlineEdges() { styleForScrollbar->StyleUIReset()->ScrollbarWidth(); if (styleScrollbarWidth == StyleScrollbarWidth::None) { // Scrollbar shouldn't appear at all with "scrollbar-width: none". - return 0; + return {}; } const auto& styleScrollbarGutter = styleForScrollbar->StyleDisplay()->mScrollbarGutter; - ScrollStyles ss = GetScrollStyles(); - const StyleOverflow& inlineEndStyleOverflow = - isVerticalWM ? ss.mHorizontal : ss.mVertical; - - // Return the scrollbar-gutter size only if we have "overflow:scroll" or - // non-auto "scrollbar-gutter", so early-return here if the conditions aren't - // satisfied. - if (inlineEndStyleOverflow != StyleOverflow::Scroll && - styleScrollbarGutter == StyleScrollbarGutter::AUTO) { - return 0; + nsMargin gutter = + ComputeStableScrollbarGutter(styleScrollbarWidth, styleScrollbarGutter); + if (gutter.LeftRight() == 0 || gutter.TopBottom() == 0) { + // If there is no stable scrollbar-gutter at vertical or horizontal + // dimension, check if a scrollbar is always shown at that dimension. + ScrollStyles scrollStyles = GetScrollStyles(); + const nscoord scrollbarSize = + GetNonOverlayScrollbarSize(PresContext(), styleScrollbarWidth); + if (gutter.LeftRight() == 0 && + scrollStyles.mVertical == StyleOverflow::Scroll) { + (IsScrollbarOnRight() ? gutter.right : gutter.left) = scrollbarSize; + } + if (gutter.TopBottom() == 0 && + scrollStyles.mHorizontal == StyleOverflow::Scroll) { + // The horizontal scrollbar is always at the bottom side. + gutter.bottom = scrollbarSize; + } } - - const nscoord scrollbarSize = - GetNonOverlayScrollbarSize(PresContext(), styleScrollbarWidth); - const auto bothEdges = - bool(styleScrollbarGutter & StyleScrollbarGutter::BOTH_EDGES); - return bothEdges ? scrollbarSize * 2 : scrollbarSize; + return gutter; } nsMargin nsHTMLScrollFrame::ComputeStableScrollbarGutter( @@ -2040,8 +2048,13 @@ class nsHTMLScrollFrame::AsyncSmoothMSDScroll final * Should be used at most once during the lifetime of this object. */ void SetRefreshObserver(nsHTMLScrollFrame* aCallee) { - NS_ASSERTION(aCallee && !mCallee, - "AsyncSmoothMSDScroll::SetRefreshObserver - Invalid usage."); + MOZ_ASSERT(aCallee, + "AsyncSmoothMSDScroll::SetRefreshObserver needs " + "a non-null aCallee in order to get a refresh driver"); + MOZ_RELEASE_ASSERT(!mCallee, + "AsyncSmoothMSDScroll::SetRefreshObserver " + "shouldn't be called if we're already registered with " + "a refresh driver, via a preexisting mCallee"); RefreshDriver(aCallee)->AddRefreshObserver(this, FlushType::Style, "Smooth scroll (MSD) animation"); @@ -2168,8 +2181,13 @@ class nsHTMLScrollFrame::AsyncScroll final : public nsARefreshObserver { * Should be used at most once during the lifetime of this object. */ void SetRefreshObserver(nsHTMLScrollFrame* aCallee) { - NS_ASSERTION(aCallee && !mCallee, - "AsyncScroll::SetRefreshObserver - Invalid usage."); + MOZ_ASSERT(aCallee, + "AsyncScroll::SetRefreshObserver needs " + "a non-null aCallee in order to get a refresh driver"); + MOZ_RELEASE_ASSERT(!mCallee, + "AsyncScroll::SetRefreshObserver " + "shouldn't be called if we're already registered with " + "a refresh driver, via a preexisting mCallee"); RefreshDriver(aCallee)->AddRefreshObserver(this, FlushType::Style, "Smooth scroll animation"); @@ -2235,12 +2253,7 @@ void nsHTMLScrollFrame::AsyncScroll::InitSmoothScroll( case ScrollOrigin::Apz: // Likewise we should never get APZ-triggered scrolls here, and if that // changes something is likely broken somewhere. - MOZ_ASSERT_UNREACHABLE( - "APZ scroll position updates should never be smooth"); - break; - case ScrollOrigin::AnchorAdjustment: - MOZ_ASSERT_UNREACHABLE( - "scroll anchor adjustments should never be smooth"); + MOZ_ASSERT(false); break; default: break; @@ -3016,7 +3029,6 @@ void nsHTMLScrollFrame::ScrollToImpl( (mLastScrollOrigin != ScrollOrigin::None && mLastScrollOrigin != ScrollOrigin::NotSpecified && mLastScrollOrigin != ScrollOrigin::Relative && - mLastScrollOrigin != ScrollOrigin::AnchorAdjustment && mLastScrollOrigin != ScrollOrigin::Apz)) { aOrigin = ScrollOrigin::Other; } @@ -3069,10 +3081,8 @@ void nsHTMLScrollFrame::ScrollToImpl( // may simplify this a bit and should be fine from the APZ side. if (mApzSmoothScrollDestination && aOrigin != ScrollOrigin::Clamp) { if (aOrigin == ScrollOrigin::Relative) { - AppendScrollUpdate(ScrollPositionUpdate::NewRelativeScroll( - // Clamp |mApzScrollPos| here. See the comment for this clamping - // reason below NewRelativeScroll call. - GetLayoutScrollRange().ClampPoint(mApzScrollPos), pt)); + AppendScrollUpdate( + ScrollPositionUpdate::NewRelativeScroll(mApzScrollPos, pt)); mApzScrollPos = pt; } else if (aOrigin != ScrollOrigin::Apz) { ScrollOrigin origin = @@ -3159,15 +3169,8 @@ void nsHTMLScrollFrame::ScrollToImpl( if (aOrigin == ScrollOrigin::Relative) { MOZ_ASSERT(!isScrollOriginDowngrade); MOZ_ASSERT(mLastScrollOrigin == ScrollOrigin::Relative); - AppendScrollUpdate(ScrollPositionUpdate::NewRelativeScroll( - // It's possible that |mApzScrollPos| is no longer within the scroll - // range, we need to clamp it to the current scroll range, otherwise - // calculating a relative scroll distance from the outside point will - // result a point far from the desired point. - GetLayoutScrollRange().ClampPoint(mApzScrollPos), pt)); - mApzScrollPos = pt; - } else if (aOrigin == ScrollOrigin::AnchorAdjustment) { - AppendScrollUpdate(ScrollPositionUpdate::NewMergeableScroll(aOrigin, pt)); + AppendScrollUpdate( + ScrollPositionUpdate::NewRelativeScroll(mApzScrollPos, pt)); mApzScrollPos = pt; } else if (aOrigin != ScrollOrigin::Apz) { AppendScrollUpdate(ScrollPositionUpdate::NewScroll(mLastScrollOrigin, pt)); diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index e05a2a077b..ce2a75a3df 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -110,7 +110,13 @@ class nsHTMLScrollFrame : public nsContainerFrame, // Return the sum of inline-size of the scrollbar gutters (if any) at the // inline-start and inline-end edges of the scroll frame (for a potential // scrollbar that scrolls in the block axis). - nscoord IntrinsicScrollbarGutterSizeAtInlineEdges(); + nscoord IntrinsicScrollbarGutterSizeAtInlineEdges() const; + + // Return the size of space created by scrollbar-gutter or actual scrollbars, + // assuming that the content is *not* overflowing the container. In other + // words, this space is created by stable scrollbar-gutter or by scrollbars + // due to "overflow: scroll". + nsMargin IntrinsicScrollbarGutterSize() const; // Compute stable scrollbar-gutter from scrollbar-width and scrollbar-gutter // properties. diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index 97d8549cb0..5eb9cde7da 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -28,6 +28,7 @@ #include "nsCSSFrameConstructor.h" #include "nsDisplayList.h" #include "nsFieldSetFrame.h" +#include "nsGfxScrollFrame.h" #include "nsHashKeys.h" #include "nsIFrameInlines.h" // for nsIFrame::GetLogicalNormalPosition (don't remove) #include "nsLayoutUtils.h" @@ -3685,7 +3686,8 @@ static Subgrid* SubgridComputeMarginBorderPadding( sz.ComputedLogicalMargin(cbWM) + sz.ComputedLogicalBorderPadding(cbWM); if (aGridItem.mFrame != subgridFrame) { - nsIScrollableFrame* scrollFrame = aGridItem.mFrame->GetScrollTargetFrame(); + nsHTMLScrollFrame* scrollFrame = + do_QueryFrame(aGridItem.mFrame->GetScrollTargetFrame()); if (scrollFrame) { MOZ_ASSERT( sz.ComputedLogicalMargin(cbWM) == LogicalMargin(cbWM) && @@ -3699,7 +3701,7 @@ static Subgrid* SubgridComputeMarginBorderPadding( szScrollFrame.ComputedLogicalMargin(cbWM) + szScrollFrame.ComputedLogicalBorder(cbWM); - nsMargin ssz = scrollFrame->GetActualScrollbarSizes(); + nsMargin ssz = scrollFrame->IntrinsicScrollbarGutterSize(); subgrid->mMarginBorderPadding += LogicalMargin(cbWM, ssz); } @@ -4032,16 +4034,6 @@ static void AlignSelf(const nsGridContainerFrame::GridItemInfo& aGridItem, aAlignSelf == StyleAlignFlags::LAST_BASELINE) { aAlignSelf = aGridItem.GetSelfBaseline(aAlignSelf, eLogicalAxisBlock, &baselineAdjust); - // Adjust the baseline alignment value if the baseline affects the opposite - // side of what AlignJustifySelf expects. - auto state = aGridItem.mState[eLogicalAxisBlock]; - if (aAlignSelf == StyleAlignFlags::LAST_BASELINE && - !GridItemInfo::BaselineAlignmentAffectsEndSide(state)) { - aAlignSelf = StyleAlignFlags::BASELINE; - } else if (aAlignSelf == StyleAlignFlags::BASELINE && - GridItemInfo::BaselineAlignmentAffectsEndSide(state)) { - aAlignSelf = StyleAlignFlags::LAST_BASELINE; - } } bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM); @@ -4083,16 +4075,6 @@ static void JustifySelf(const nsGridContainerFrame::GridItemInfo& aGridItem, aJustifySelf == StyleAlignFlags::LAST_BASELINE) { aJustifySelf = aGridItem.GetSelfBaseline(aJustifySelf, eLogicalAxisInline, &baselineAdjust); - // Adjust the baseline alignment value if the baseline affects the opposite - // side of what AlignJustifySelf expects. - auto state = aGridItem.mState[eLogicalAxisInline]; - if (aJustifySelf == StyleAlignFlags::LAST_BASELINE && - !GridItemInfo::BaselineAlignmentAffectsEndSide(state)) { - aJustifySelf = StyleAlignFlags::BASELINE; - } else if (aJustifySelf == StyleAlignFlags::BASELINE && - GridItemInfo::BaselineAlignmentAffectsEndSide(state)) { - aJustifySelf = StyleAlignFlags::LAST_BASELINE; - } } bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM); @@ -6006,6 +5988,12 @@ void nsGridContainerFrame::Tracks::InitializeItemBaselines( const WritingMode containerWM = aState.mWM; ComputedStyle* containerStyle = aState.mFrame->Style(); + // The physical side of the container's block start side. We use it to match + // against the physical block start side of the child to determine its + // baseline sharing group. + auto containerBlockStartSide = + containerWM.PhysicalSide(MakeLogicalSide(mAxis, eLogicalEdgeStart)); + for (GridItemInfo& gridItem : aGridItems) { if (gridItem.IsSubgrid(mAxis)) { // A subgrid itself is never baseline-aligned. @@ -6101,6 +6089,27 @@ void nsGridContainerFrame::Tracks::InitializeItemBaselines( } if (state & ItemState::eIsBaselineAligned) { + // The item is baseline aligned, so calculate the baseline sharing group. + // <https://drafts.csswg.org/css-align-3/#baseline-terms> + BaselineSharingGroup baselineAlignment = + (state & ItemState::eFirstBaseline) ? BaselineSharingGroup::First + : BaselineSharingGroup::Last; + + BaselineSharingGroup baselineSharingGroup = [&]() { + { + auto childAxis = isOrthogonal ? GetOrthogonalAxis(mAxis) : mAxis; + auto childBlockStartSide = childWM.PhysicalSide( + MakeLogicalSide(childAxis, eLogicalEdgeStart)); + bool isFirstBaseline = (state & ItemState::eFirstBaseline) != 0; + const bool containerAndChildHasEqualBaselineSide = + containerBlockStartSide == childBlockStartSide; + + return isFirstBaseline == containerAndChildHasEqualBaselineSide + ? BaselineSharingGroup::First + : BaselineSharingGroup::Last; + } + }(); + // XXXmats if |child| is a descendant of a subgrid then the metrics // below needs to account for the accumulated MPB somehow... @@ -6128,16 +6137,13 @@ void nsGridContainerFrame::Tracks::InitializeItemBaselines( : margin.BStartEnd(containerWM)); Maybe<nscoord> baseline; - auto baselineSharingGroup = state & ItemState::eFirstBaseline - ? BaselineSharingGroup::First - : BaselineSharingGroup::Last; if (grid) { baseline.emplace((isOrthogonal == isInlineAxis) - ? grid->GetBBaseline(baselineSharingGroup) - : grid->GetIBaseline(baselineSharingGroup)); + ? grid->GetBBaseline(baselineAlignment) + : grid->GetIBaseline(baselineAlignment)); } else { baseline = child->GetNaturalBaselineBOffset( - childWM, baselineSharingGroup, BaselineExportContext::Other); + childWM, baselineAlignment, BaselineExportContext::Other); if (!baseline) { // If baseline alignment is specified on a grid item whose size in @@ -6162,7 +6168,7 @@ void nsGridContainerFrame::Tracks::InitializeItemBaselines( if (!isTrackAutoSize || !gridItem.IsBSizeDependentOnContainerSize(containerWM)) { baseline.emplace(Baseline::SynthesizeBOffsetFromBorderBox( - child, containerWM, baselineSharingGroup)); + child, containerWM, baselineAlignment)); } } } @@ -6172,15 +6178,19 @@ void nsGridContainerFrame::Tracks::InitializeItemBaselines( NS_ASSERTION(finalBaseline != NS_INTRINSIC_ISIZE_UNKNOWN, "about to use an unknown baseline"); + nscoord marginAdjust = 0; if (baselineSharingGroup == BaselineSharingGroup::First) { - finalBaseline += isInlineAxis ? margin.IStart(containerWM) - : margin.BStart(containerWM); - + marginAdjust = isInlineAxis ? margin.IStart(containerWM) + : margin.BStart(containerWM); } else { - finalBaseline += isInlineAxis ? margin.IEnd(containerWM) - : margin.BEnd(containerWM); - state |= ItemState::eEndSideBaseline; + marginAdjust = isInlineAxis ? margin.IEnd(containerWM) + : margin.BEnd(containerWM); + + // This flag is used in ::AlignSelf(...) to check whether the item is + // last baseline aligned, but this flag should go away. + state |= GridItemInfo::eEndSideBaseline; } + finalBaseline += marginAdjust; auto& baselineItems = (baselineSharingGroup == BaselineSharingGroup::First) @@ -7602,7 +7612,12 @@ void nsGridContainerFrame::ReflowInFlowChild( // This happens when the subtree overflows its track. // XXX spec issue? it's unclear how to handle this. baselineAdjust = nscoord(0); - } else if (GridItemInfo::BaselineAlignmentAffectsEndSide(state)) { + } else if (state & ItemState::eLastBaseline) { + // FIXME: We're not setting the ItemState::eEndSideBaseline flag any + // more as the new baseline sharing group calculation handles most of + // the cases we need. For non-masonry grids this flag was always set + // for LAST_BASELINE items, so we're just mimicking that behavior here. + // That said, masonry grids might not work 100% any more.. baselineAdjust = -baselineAdjust; } if (baselineAdjust != nscoord(0)) { diff --git a/layout/generic/nsGridContainerFrame.h b/layout/generic/nsGridContainerFrame.h index d785d65a50..cf3a3b5776 100644 --- a/layout/generic/nsGridContainerFrame.h +++ b/layout/generic/nsGridContainerFrame.h @@ -106,12 +106,10 @@ class nsGridContainerFrame final : public nsContainerFrame, using NamedArea = mozilla::StyleNamedArea; template <typename T> - using PerBaseline = mozilla::EnumeratedArray<BaselineSharingGroup, - BaselineSharingGroup(2), T>; + using PerBaseline = mozilla::EnumeratedArray<BaselineSharingGroup, T, 2>; template <typename T> - using PerLogicalAxis = - mozilla::EnumeratedArray<LogicalAxis, LogicalAxis(2), T>; + using PerLogicalAxis = mozilla::EnumeratedArray<LogicalAxis, T, 2>; // nsIFrame overrides void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, diff --git a/layout/generic/nsHTMLParts.h b/layout/generic/nsHTMLParts.h index e1d7ef4d2d..e3cab48343 100644 --- a/layout/generic/nsHTMLParts.h +++ b/layout/generic/nsHTMLParts.h @@ -57,10 +57,6 @@ nsresult NS_NewAttributeContent(nsNodeInfoManager* aNodeInfoManager, nsContainerFrame* NS_NewSelectsAreaFrame(mozilla::PresShell* aPresShell, mozilla::ComputedStyle* aStyle); -// Create a block formatting context blockframe -nsBlockFrame* NS_NewBlockFormattingContext(mozilla::PresShell* aPresShell, - mozilla::ComputedStyle* aStyle); - nsIFrame* NS_NewBRFrame(mozilla::PresShell* aPresShell, mozilla::ComputedStyle* aStyle); diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index 81109151ff..479c26d18b 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -2117,8 +2117,8 @@ nsIFrame::CaretBlockAxisMetrics nsIFrame::GetCaretBlockAxisMetrics( return CaretBlockAxisMetrics{.mOffset = baseline - ascent, .mExtent = height}; } -const nsAtom* nsIFrame::ComputePageValue() const { - const nsAtom* value = nsGkAtoms::_empty; +const nsAtom* nsIFrame::ComputePageValue(const nsAtom* aAutoValue) const { + const nsAtom* value = aAutoValue ? aAutoValue : nsGkAtoms::_empty; const nsIFrame* frame = this; // Find what CSS page name value this frame's subtree has, if any. // Starting with this frame, check if a page name other than auto is present, @@ -2484,7 +2484,7 @@ bool nsIFrame::CanBeDynamicReflowRoot() const { // If we participate in a container's block reflow context, or margins // can collapse through us, we can't be a dynamic reflow root. - if (IsBlockFrameOrSubclass() && !HasAnyStateBits(NS_BLOCK_BFC_STATE_BITS)) { + if (IsBlockFrameOrSubclass() && !HasAnyStateBits(NS_BLOCK_BFC)) { return false; } @@ -3409,6 +3409,9 @@ void nsIFrame::BuildDisplayListForStackingContext( ApplyClipProp(transformedCssClip); } + uint32_t numActiveScrollframesEncounteredBefore = + aBuilder->GetNumActiveScrollframesEncountered(); + nsDisplayListCollection set(aBuilder); Maybe<nsRect> clipForMask; { @@ -3692,17 +3695,22 @@ void nsIFrame::BuildDisplayListForStackingContext( if (transformItem) { resultList.AppendToTop(transformItem); createdContainer = true; - } - if (hasPerspective) { - transformItem->MarkWithAssociatedPerspective(); + if (numActiveScrollframesEncounteredBefore != + aBuilder->GetNumActiveScrollframesEncountered()) { + transformItem->SetContainsASRs(true); + } + + if (hasPerspective) { + transformItem->MarkWithAssociatedPerspective(); - if (clipCapturedBy == ContainerItemType::Perspective) { - clipState.Restore(); + if (clipCapturedBy == ContainerItemType::Perspective) { + clipState.Restore(); + } + resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this, + &resultList); + createdContainer = true; } - resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this, - &resultList); - createdContainer = true; } } @@ -5413,7 +5421,15 @@ static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, const nsPoint& aPoint, uint32_t aFlags); -static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags) { +static bool SelfIsSelectable(nsIFrame* aFrame, nsIFrame* aParentFrame, + uint32_t aFlags) { + // We should not move selection into a native anonymous subtree when handling + // selection outside it. + if ((aFlags & nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE) && + aParentFrame->GetClosestNativeAnonymousSubtreeRoot() != + aFrame->GetClosestNativeAnonymousSubtreeRoot()) { + return false; + } if ((aFlags & nsIFrame::SKIP_HIDDEN) && !aFrame->StyleVisibility()->IsVisible()) { return false; @@ -5476,21 +5492,28 @@ static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, bool aEndFrame, nsIFrame* result = nullptr; nsIFrame* frame = aFrame->PrincipalChildList().FirstChild(); if (!aEndFrame) { - while (frame && (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty())) + while (frame && + (!SelfIsSelectable(frame, aFrame, aFlags) || frame->IsEmpty())) { frame = frame->GetNextSibling(); - if (frame) result = frame; + } + if (frame) { + result = frame; + } } else { // Because the frame tree is singly linked, to find the last frame, // we have to iterate through all the frames // XXX I have a feeling this could be slow for long blocks, although // I can't find any slowdowns while (frame) { - if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags)) + if (!frame->IsEmpty() && SelfIsSelectable(frame, aFrame, aFlags)) { result = frame; + } frame = frame->GetNextSibling(); } } - if (result) return DrillDownToSelectionFrame(result, aEndFrame, aFlags); + if (result) { + return DrillDownToSelectionFrame(result, aEndFrame, aFlags); + } } // If the current frame has no targetable children, target the current frame return FrameTarget{aFrame, true, aEndFrame}; @@ -5502,8 +5525,9 @@ static FrameTarget GetSelectionClosestFrameForLine( nsBlockFrame* aParent, nsBlockFrame::LineIterator aLine, const nsPoint& aPoint, uint32_t aFlags) { // Account for end of lines (any iterator from the block is valid) - if (aLine == aParent->LinesEnd()) + if (aLine == aParent->LinesEnd()) { return DrillDownToSelectionFrame(aParent, true, aFlags); + } nsIFrame* frame = aLine->mFirstChild; nsIFrame* closestFromIStart = nullptr; nsIFrame* closestFromIEnd = nullptr; @@ -5519,7 +5543,7 @@ static FrameTarget GetSelectionClosestFrameForLine( // the previous thing had a different editableness than us, since then we // may end up not being able to select after it if the br is the last thing // on the line. - if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty() || + if (!SelfIsSelectable(frame, aParent, aFlags) || frame->IsEmpty() || (canSkipBr && frame->IsBrFrame() && lastFrameWasEditable == frame->GetContent()->IsEditable())) { continue; @@ -5699,7 +5723,7 @@ static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame, // Go through all the child frames to find the closest one nsIFrame::FrameWithDistance closest = {nullptr, nscoord_MAX, nscoord_MAX}; for (; kid; kid = kid->GetNextSibling()) { - if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty()) { + if (!SelfIsSelectable(kid, aFrame, aFlags) || kid->IsEmpty()) { continue; } @@ -9368,7 +9392,8 @@ nsresult nsIFrame::PeekOffsetForLineEdge(PeekOffsetStruct* aPos) { } } } - FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame, endOfLine, 0); + FrameTarget targetFrame = DrillDownToSelectionFrame( + baseFrame, endOfLine, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE); SetPeekResultFromFrame(*aPos, targetFrame.frame, endOfLine ? -1 : 0, OffsetIsAtLineEdge::Yes); if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) { @@ -11538,11 +11563,44 @@ nsIFrame::PhysicalAxes nsIFrame::ShouldApplyOverflowClipping( return PhysicalAxes::None; } - // If we're paginated and a block, and have NS_BLOCK_CLIP_PAGINATED_OVERFLOW - // set, then we want to clip our overflow. - bool clip = HasAnyStateBits(NS_BLOCK_CLIP_PAGINATED_OVERFLOW) && - PresContext()->IsPaginated() && IsBlockFrame(); - return clip ? PhysicalAxes::Both : PhysicalAxes::None; + return IsSuppressedScrollableBlockForPrint() ? PhysicalAxes::Both + : PhysicalAxes::None; +} + +bool nsIFrame::IsSuppressedScrollableBlockForPrint() const { + // This condition needs to match the suppressScrollFrame logic in the frame + // constructor. + if (!PresContext()->IsPaginated() || !IsBlockFrame() || + !StyleDisplay()->IsScrollableOverflow() || + !StyleDisplay()->IsBlockOutsideStyle() || + mContent->IsInNativeAnonymousSubtree()) { + return false; + } + if (auto* element = Element::FromNode(mContent); + element && PresContext()->ElementWouldPropagateScrollStyles(*element)) { + return false; + } + return true; +} + +bool nsIFrame::HasUnreflowedContainerQueryAncestor() const { + // If this frame has done the first reflow, its ancestors are guaranteed to + // have as well. + if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW) || + !PresContext()->HasContainerQueryFrames()) { + return false; + } + for (nsIFrame* cur = GetInFlowParent(); cur; cur = cur->GetInFlowParent()) { + if (!cur->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { + // Done first reflow from this ancestor up, including query containers. + return false; + } + if (cur->StyleDisplay()->IsQueryContainer()) { + return true; + } + } + // No query container from this frame up to root. + return false; } #ifdef DEBUG diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index a29786488f..16f3d17d64 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -562,6 +562,8 @@ enum class LayoutFrameClassFlags : uint16_t { SupportsContainLayoutAndPaint = 1 << 13, // Whether this frame class supports the `aspect-ratio` property. SupportsAspectRatio = 1 << 14, + // Whether this frame class is always a BFC. + BlockFormattingContext = 1 << 15, }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(LayoutFrameClassFlags) @@ -1380,6 +1382,8 @@ class nsIFrame : public nsQueryFrame { return nullptr; } + bool HasUnreflowedContainerQueryAncestor() const; + private: // The value that the CSS page-name "auto" keyword resolves to for children // of this frame. @@ -1643,7 +1647,13 @@ class nsIFrame : public nsQueryFrame { // This is intended to be used either on the root frame to find the first // page's page-name, or on a newly created continuation to find what the new // page's page-name will be. - const nsAtom* ComputePageValue() const MOZ_NONNULL_RETURN; + // + // The auto page value can be set by the caller. This is useful when trying + // to compute a page value in the middle of a frame tree. In that case the + // auto value can be found from the AutoPageValue frame property of the + // parent frame. A null auto value is interpreted as the empty-string atom. + const nsAtom* ComputePageValue(const nsAtom* aAutoValue = nullptr) const + MOZ_NONNULL_RETURN; /////////////////////////////////////////////////////////////////////////////// // The public visibility API. @@ -3027,6 +3037,9 @@ class nsIFrame : public nsQueryFrame { nsSize OverflowClipMargin(PhysicalAxes aClipAxes) const; // Returns the axes on which this frame should apply overflow clipping. PhysicalAxes ShouldApplyOverflowClipping(const nsStyleDisplay* aDisp) const; + // Returns whether this frame is a block that was supposed to be a + // scrollframe, but that was suppressed for print. + bool IsSuppressedScrollableBlockForPrint() const; /** * Helper method used by block reflow to identify runs of text so diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index e0a6243ed2..c1e69df6c9 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -18,6 +18,7 @@ #include "mozilla/DebugOnly.h" #include "mozilla/Encoding.h" #include "mozilla/HTMLEditor.h" +#include "mozilla/dom/FetchPriority.h" #include "mozilla/dom/ImageTracker.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Helpers.h" @@ -268,7 +269,7 @@ BrokenImageIcon::BrokenImageIcon(const nsImageFrame& aFrame) { loadFlags, nullptr, contentPolicyType, u""_ns, false, /* aUseUrgentStartForChannel */ false, /* aLinkPreload */ - 0, getter_AddRefs(mImage)); + 0, FetchPriority::Auto, getter_AddRefs(mImage)); Unused << NS_WARN_IF(NS_FAILED(rv)); } @@ -554,15 +555,16 @@ void nsImageFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) { // // TODO(emilio): We might want to do the same for regular list-style-image or // even simple content: url() changes. - if (mKind == Kind::XULImage) { - if (!mContent->AsElement()->HasNonEmptyAttr(nsGkAtoms::src) && aOldStyle && + if (mKind == Kind::XULImage && aOldStyle) { + if (!mContent->AsElement()->HasNonEmptyAttr(nsGkAtoms::src) && aOldStyle->StyleList()->mListStyleImage != StyleList()->mListStyleImage) { UpdateXULImage(); } - if (!mOwnedRequest && aOldStyle && - aOldStyle->StyleDisplay()->EffectiveAppearance() != - StyleDisplay()->EffectiveAppearance()) { + // If we have no image our intrinsic size might be themed. We need to + // update the size even if the effective appearance hasn't changed to + // deal correctly with theme changes. + if (!mOwnedRequest) { UpdateIntrinsicSize(); } } diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index 03aa6e87b3..ab6924faa4 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -430,7 +430,7 @@ void nsLineLayout::BeginSpan(nsIFrame* aFrame, psd->mIStart = aIStart; psd->mICoord = aIStart; psd->mIEnd = aIEnd; - psd->mInset = mCurrentSpan->mInset; + psd->mInset = 0; // inset applies only to the root span psd->mBaseline = aBaseline; nsIFrame* frame = aSpanReflowInput->mFrame; @@ -735,8 +735,7 @@ static bool IsPercentageAware(const nsIFrame* aFrame, WritingMode aWM) { disp->DisplayInside() == StyleDisplayInside::Table)) || fType == LayoutFrameType::HTMLButtonControl || fType == LayoutFrameType::GfxButtonControl || - fType == LayoutFrameType::FieldSet || - fType == LayoutFrameType::ComboboxDisplay) { + fType == LayoutFrameType::FieldSet) { return true; } diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 369722fe8d..9afefc5c28 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -226,6 +226,7 @@ struct nsTextFrame::DrawTextRunParams { float textStrokeWidth = 0.0f; bool drawSoftHyphen = false; bool hasTextShadow = false; + bool paintingShadows = false; DrawTextRunParams(gfxContext* aContext, mozilla::gfx::PaletteCache& aPaletteCache) : context(aContext), paletteCache(aPaletteCache) {} @@ -276,6 +277,7 @@ struct nsTextFrame::PaintShadowParams { Point framePt; Point textBaselinePt; gfxContext* context; + DrawPathCallbacks* callbacks = nullptr; nscolor foregroundColor = NS_RGBA(0, 0, 0, 0); const ClipEdges* clipEdges = nullptr; PropertyProvider* provider = nullptr; @@ -5374,9 +5376,8 @@ void nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext, // Text-shadow overflows if (aIncludeShadows) { - nsRect shadowRect = + *aInkOverflowRect = nsLayoutUtils::GetTextShadowRectsUnion(*aInkOverflowRect, this); - aInkOverflowRect->UnionRect(*aInkOverflowRect, shadowRect); } // When this frame is not selected, the text-decoration area must be in @@ -5460,6 +5461,7 @@ struct nsTextFrame::PaintDecorationLineParams gfxFloat baselineOffset = 0.0f; DecorationType decorationType = DecorationType::Normal; DrawPathCallbacks* callbacks = nullptr; + bool paintingShadows = false; }; void nsTextFrame::PaintDecorationLine( @@ -5474,9 +5476,11 @@ void nsTextFrame::PaintDecorationLine( if (aParams.callbacks) { Rect path = nsCSSRendering::DecorationLineToPath(params); if (aParams.decorationType == DecorationType::Normal) { - aParams.callbacks->PaintDecorationLine(path, params.color); + aParams.callbacks->PaintDecorationLine(path, aParams.paintingShadows, + params.color); } else { - aParams.callbacks->PaintSelectionDecorationLine(path, params.color); + aParams.callbacks->PaintSelectionDecorationLine( + path, aParams.paintingShadows, params.color); } } else { nsCSSRendering::PaintDecorationLine(this, *aParams.context->GetDrawTarget(), @@ -5938,6 +5942,7 @@ void nsTextFrame::PaintOneShadow(const PaintShadowParams& aParams, gfxFloat advanceWidth; nsTextPaintStyle textPaintStyle(this); DrawTextParams params(shadowContext, PresContext()->FontPaletteCache()); + params.paintingShadows = true; params.advanceWidth = &advanceWidth; params.dirtyRect = aParams.dirtyRect; params.framePt = aParams.framePt + shadowGfxOffset; @@ -5945,9 +5950,10 @@ void nsTextFrame::PaintOneShadow(const PaintShadowParams& aParams, params.textStyle = &textPaintStyle; params.textColor = aParams.context == shadowContext ? shadowColor : NS_RGB(0, 0, 0); + params.callbacks = aParams.callbacks; params.clipEdges = aParams.clipEdges; params.drawSoftHyphen = HasAnyStateBits(TEXT_HYPHEN_BREAK); - // Multi-color shadow is not allowed, so we use the same color of the text + // Multi-color shadow is not allowed, so we use the same color as the text // color. params.decorationOverrideColor = ¶ms.textColor; params.fontPalette = StyleFont()->GetFontPaletteAtom(); @@ -6253,6 +6259,7 @@ bool nsTextFrame::PaintTextWithSelectionColors( PaintShadowParams shadowParams(aParams); shadowParams.provider = aParams.provider; + shadowParams.callbacks = aParams.callbacks; shadowParams.clipEdges = &aClipEdges; // Draw text @@ -6815,6 +6822,7 @@ void nsTextFrame::PaintText(const PaintTextParams& aParams, shadowParams.textBaselinePt = textBaselinePt; shadowParams.leftSideOffset = snappedStartEdge; shadowParams.provider = &provider; + shadowParams.callbacks = aParams.callbacks; shadowParams.foregroundColor = foregroundColor; shadowParams.clipEdges = &clipEdges; PaintShadows(textStyle->mTextShadow.AsSpan(), shadowParams); @@ -6854,7 +6862,8 @@ static void DrawTextRun(const gfxTextRun* aTextRun, params.callbacks = aParams.callbacks; params.hasTextShadow = aParams.hasTextShadow; if (aParams.callbacks) { - aParams.callbacks->NotifyBeforeText(aParams.textColor); + aParams.callbacks->NotifyBeforeText(aParams.paintingShadows, + aParams.textColor); params.drawMode = DrawMode::GLYPH_PATH; aTextRun->Draw(aRange, aTextBaselinePt, params); aParams.callbacks->NotifyAfterText(); @@ -6995,6 +7004,7 @@ void nsTextFrame::DrawTextRunAndDecorations( params.callbacks = aParams.callbacks; params.glyphRange = aParams.glyphRange; params.provider = aParams.provider; + params.paintingShadows = aParams.paintingShadows; // pt is the physical point where the decoration is to be drawn, // relative to the frame; one of its coordinates will be updated below. params.pt = Point(x / app, y / app); diff --git a/layout/generic/nsTextFrame.h b/layout/generic/nsTextFrame.h index 568d3333c2..707d39dba2 100644 --- a/layout/generic/nsTextFrame.h +++ b/layout/generic/nsTextFrame.h @@ -513,20 +513,22 @@ class nsTextFrame : public nsIFrame { * Called before (for under/over-line) or after (for line-through) the text * is drawn to have a text decoration line drawn. */ - virtual void PaintDecorationLine(Rect aPath, nscolor aColor) {} + virtual void PaintDecorationLine(Rect aPath, bool aPaintingShadows, + nscolor aColor) {} /** * Called after selected text is drawn to have a decoration line drawn over * the text. (All types of text decoration are drawn after the text when * text is selected.) */ - virtual void PaintSelectionDecorationLine(Rect aPath, nscolor aColor) {} + virtual void PaintSelectionDecorationLine(Rect aPath, bool aPaintingShadows, + nscolor aColor) {} /** * Called just before any paths have been emitted to the gfxContext * for the glyphs of the frame's text. */ - virtual void NotifyBeforeText(nscolor aColor) {} + virtual void NotifyBeforeText(bool aPaintingShadows, nscolor aColor) {} /** * Called just after all the paths have been emitted to the gfxContext diff --git a/layout/generic/nsTextPaintStyle.cpp b/layout/generic/nsTextPaintStyle.cpp index 0eff737602..e97a059b07 100644 --- a/layout/generic/nsTextPaintStyle.cpp +++ b/layout/generic/nsTextPaintStyle.cpp @@ -417,8 +417,8 @@ struct StyleIDs { LookAndFeel::IntID mLineStyle; LookAndFeel::FloatID mLineRelativeSize; }; -EnumeratedArray<nsTextPaintStyle::SelectionStyleIndex, - nsTextPaintStyle::SelectionStyleIndex::Count, StyleIDs> +EnumeratedArray<nsTextPaintStyle::SelectionStyleIndex, StyleIDs, + size_t(nsTextPaintStyle::SelectionStyleIndex::Count)> SelectionStyleIDs = { StyleIDs{LookAndFeel::ColorID::IMERawInputForeground, LookAndFeel::ColorID::IMERawInputBackground, diff --git a/layout/generic/nsTextPaintStyle.h b/layout/generic/nsTextPaintStyle.h index a99ee9fd46..adf28fdad8 100644 --- a/layout/generic/nsTextPaintStyle.h +++ b/layout/generic/nsTextPaintStyle.h @@ -144,8 +144,9 @@ class MOZ_STACK_CLASS nsTextPaintStyle { StyleTextDecorationStyle mUnderlineStyle; float mUnderlineRelativeSize; }; - mozilla::EnumeratedArray<SelectionStyleIndex, SelectionStyleIndex::Count, - mozilla::Maybe<nsSelectionStyle>> + mozilla::EnumeratedArray<SelectionStyleIndex, + mozilla::Maybe<nsSelectionStyle>, + size_t(SelectionStyleIndex::Count)> mSelectionStyle; // Color initializations diff --git a/layout/generic/crashtests/1140268-1.html b/testing/web-platform/tests/mathml/crashtests/mozilla/1140268-1.html index 5e5510ba7f..5e5510ba7f 100644 --- a/layout/generic/crashtests/1140268-1.html +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/1140268-1.html diff --git a/layout/generic/crashtests/364686-1.xhtml b/testing/web-platform/tests/mathml/crashtests/mozilla/364686-1.xhtml index 93a1eeaa4b..add9238d8f 100644 --- a/layout/generic/crashtests/364686-1.xhtml +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/364686-1.xhtml @@ -1,6 +1,6 @@ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML"> - + <body> <math:merror> @@ -9,4 +9,3 @@ </body> </html> - diff --git a/layout/generic/crashtests/368461-1.xhtml b/testing/web-platform/tests/mathml/crashtests/mozilla/368461-1.xhtml index d5baccf523..d5baccf523 100644 --- a/layout/generic/crashtests/368461-1.xhtml +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/368461-1.xhtml diff --git a/layout/generic/crashtests/370884-1.xhtml b/testing/web-platform/tests/mathml/crashtests/mozilla/370884-1.xhtml index 3959d4b178..3959d4b178 100644 --- a/layout/generic/crashtests/370884-1.xhtml +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/370884-1.xhtml diff --git a/layout/generic/crashtests/382208-1.xhtml b/testing/web-platform/tests/mathml/crashtests/mozilla/382208-1.xhtml index 5264b8845d..88d2728bfe 100644 --- a/layout/generic/crashtests/382208-1.xhtml +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/382208-1.xhtml @@ -1,7 +1,7 @@ -<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML">
-<body>
-
-<div><math:mfrac><math:mmultiscripts/><math:mi/></math:mfrac></div>
-
-</body>
-</html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:math="http://www.w3.org/1998/Math/MathML"> +<body> + +<div><math:mfrac><math:mmultiscripts/><math:mi/></math:mfrac></div> + +</body> +</html> diff --git a/layout/generic/crashtests/382396-1.xhtml b/testing/web-platform/tests/mathml/crashtests/mozilla/382396-1.xhtml index f334bbfdf5..89af7ea18d 100644 --- a/layout/generic/crashtests/382396-1.xhtml +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/382396-1.xhtml @@ -3,5 +3,5 @@ <p style="text-indent: 0%">a<math:ms/></p> -</body> +</body> </html> diff --git a/layout/generic/crashtests/385265-1.xhtml b/testing/web-platform/tests/mathml/crashtests/mozilla/385265-1.xhtml index 7994653ffa..7994653ffa 100644 --- a/layout/generic/crashtests/385265-1.xhtml +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/385265-1.xhtml diff --git a/layout/generic/crashtests/395450-1.xhtml b/testing/web-platform/tests/mathml/crashtests/mozilla/395450-1.xhtml index 79510267ba..79510267ba 100644 --- a/layout/generic/crashtests/395450-1.xhtml +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/395450-1.xhtml diff --git a/layout/generic/crashtests/467914-1.html b/testing/web-platform/tests/mathml/crashtests/mozilla/467914-1.html index 4f518f09df..4f518f09df 100644 --- a/layout/generic/crashtests/467914-1.html +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/467914-1.html diff --git a/layout/generic/crashtests/547843-1.xhtml b/testing/web-platform/tests/mathml/crashtests/mozilla/547843-1.xhtml index 0ad086d90c..0ad086d90c 100644 --- a/layout/generic/crashtests/547843-1.xhtml +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/547843-1.xhtml diff --git a/layout/generic/crashtests/700031.xhtml b/testing/web-platform/tests/mathml/crashtests/mozilla/700031.xhtml index 70f924279e..70f924279e 100644 --- a/layout/generic/crashtests/700031.xhtml +++ b/testing/web-platform/tests/mathml/crashtests/mozilla/700031.xhtml |