diff options
Diffstat (limited to 'layout')
412 files changed, 7947 insertions, 6229 deletions
diff --git a/layout/base/AccessibleCaretManager.cpp b/layout/base/AccessibleCaretManager.cpp index 17597d287d..7c125cf023 100644 --- a/layout/base/AccessibleCaretManager.cpp +++ b/layout/base/AccessibleCaretManager.cpp @@ -19,6 +19,7 @@ #include "mozilla/dom/NodeFilterBinding.h" #include "mozilla/dom/Selection.h" #include "mozilla/dom/TreeWalker.h" +#include "mozilla/FocusModel.h" #include "mozilla/IMEStateManager.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/PresShell.h" @@ -222,27 +223,17 @@ bool AccessibleCaretManager::IsCaretDisplayableInCursorMode( if (!caret || !caret->IsVisible()) { return false; } - - int32_t offset = 0; - nsIFrame* frame = - nsCaret::GetFrameAndOffset(GetSelection(), nullptr, 0, &offset); - - if (!frame) { - return false; - } - - if (!GetEditingHostForFrame(frame)) { + auto frameData = + nsCaret::GetFrameAndOffset(nsCaret::CaretPositionFor(GetSelection())); + if (!GetEditingHostForFrame(frameData.mFrame)) { return false; } - if (aOutFrame) { - *aOutFrame = frame; + *aOutFrame = frameData.mFrame; } - if (aOutOffset) { - *aOutOffset = offset; + *aOutOffset = frameData.mOffsetInFrameContent; } - return true; } @@ -889,7 +880,7 @@ nsIFrame* AccessibleCaretManager::GetFocusableFrame(nsIFrame* aFrame) const { // Look for the nearest enclosing focusable frame. nsIFrame* focusableFrame = aFrame; while (focusableFrame) { - if (focusableFrame->IsFocusable(/* aWithMouse = */ true)) { + if (focusableFrame->IsFocusable(IsFocusableFlags::WithMouse)) { break; } focusableFrame = focusableFrame->GetParent(); @@ -1333,10 +1324,9 @@ nsPoint AccessibleCaretManager::AdjustDragBoundary( const nsPoint& aPoint) const { nsPoint adjustedPoint = aPoint; - int32_t focusOffset = 0; - nsIFrame* focusFrame = - nsCaret::GetFrameAndOffset(GetSelection(), nullptr, 0, &focusOffset); - Element* editingHost = GetEditingHostForFrame(focusFrame); + auto frameData = + nsCaret::GetFrameAndOffset(nsCaret::CaretPositionFor(GetSelection())); + Element* editingHost = GetEditingHostForFrame(frameData.mFrame); if (editingHost) { nsIFrame* editingHostFrame = editingHost->GetPrimaryFrame(); @@ -1471,8 +1461,7 @@ void AccessibleCaretManager::DispatchCaretStateChangedEvent( // Send isEditable info w/ event detail. This info can help determine // whether to show cut command on selection dialog or not. - init.mSelectionEditable = - commonAncestorFrame && GetEditingHostForFrame(commonAncestorFrame); + init.mSelectionEditable = GetEditingHostForFrame(commonAncestorFrame); init.mBoundingClientRect = domRect; init.mReason = aReason; diff --git a/layout/base/MotionPathUtils.cpp b/layout/base/MotionPathUtils.cpp index 4045f2304a..d620064b73 100644 --- a/layout/base/MotionPathUtils.cpp +++ b/layout/base/MotionPathUtils.cpp @@ -434,7 +434,7 @@ Maybe<ResolvedMotionPathData> MotionPathUtils::ResolveMotionPath( static inline bool IsClosedLoop(const StyleSVGPathData& aPathData) { return !aPathData._0.AsSpan().empty() && - aPathData._0.AsSpan().rbegin()->IsClosePath(); + aPathData._0.AsSpan().rbegin()->IsClose(); } // Create a path for "inset(0 round X)", where X is the value of border-radius @@ -466,8 +466,8 @@ static already_AddRefed<gfx::Path> BuildDefaultPathForURL( return nullptr; } - Array<const StylePathCommand, 1> array(StylePathCommand::MoveTo( - StyleCoordPair(gfx::Point{0.0, 0.0}), StyleIsAbsolute::No)); + Array<const StylePathCommand, 1> array(StylePathCommand::Move( + StyleByTo::By, StyleCoordinatePair<StyleCSSFloat>{0.0, 0.0})); return SVGPathData::BuildPath(array, aBuilder, StyleStrokeLinecap::Butt, 0.0); } @@ -695,6 +695,21 @@ already_AddRefed<gfx::Path> MotionPathUtils::BuildSVGPath( 0.0); } +static already_AddRefed<gfx::Path> BuildShape( + const Span<const StyleShapeCommand>& aShape, gfx::PathBuilder* aPathBuilder, + const nsRect& aCoordBox) { + if (!aPathBuilder) { + return nullptr; + } + + // For motion path, we always use CSSPixel unit to compute the offset + // transform (i.e. motion path transform). + const auto rect = CSSRect::FromAppUnits(aCoordBox); + return SVGPathData::BuildPath(aShape, aPathBuilder, StyleStrokeLinecap::Butt, + 0.0, rect.Size(), + rect.TopLeft().ToUnknownPoint()); +} + /* static */ already_AddRefed<gfx::Path> MotionPathUtils::BuildPath( const StyleBasicShape& aBasicShape, @@ -725,12 +740,22 @@ already_AddRefed<gfx::Path> MotionPathUtils::BuildPath( case StyleBasicShape::Tag::Polygon: return ShapeUtils::BuildPolygonPath(aBasicShape, aCoordBox, AppUnitsPerCSSPixel(), aPathBuilder); - case StyleBasicShape::Tag::Path: + case StyleBasicShape::Tag::PathOrShape: { // FIXME: Bug 1836847. Once we support "at <position>" for path(), we have // to also check its containing block as well. For now, we are still // building its gfx::Path directly by its SVGPathData without other // reference. https://github.com/w3c/fxtf-drafts/issues/504 - return BuildSVGPath(aBasicShape.AsPath().path, aPathBuilder); + const auto& pathOrShape = aBasicShape.AsPathOrShape(); + if (pathOrShape.IsPath()) { + return BuildSVGPath(pathOrShape.AsPath().path, aPathBuilder); + } + + // Note that shape() always defines the initial position, i.e. "from x y", + // by its first move command, so |aOffsetPosition|, i.e. offset-position + // property, is ignored. + return BuildShape(pathOrShape.AsShape().commands.AsSpan(), aPathBuilder, + aCoordBox); + } } return nullptr; diff --git a/layout/base/PositionedEventTargeting.cpp b/layout/base/PositionedEventTargeting.cpp index a975372dea..9af132d96d 100644 --- a/layout/base/PositionedEventTargeting.cpp +++ b/layout/base/PositionedEventTargeting.cpp @@ -6,6 +6,7 @@ #include "PositionedEventTargeting.h" +#include "Units.h" #include "mozilla/EventListenerManager.h" #include "mozilla/MouseEvents.h" #include "mozilla/Preferences.h" @@ -13,18 +14,23 @@ #include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_ui.h" #include "mozilla/ToString.h" +#include "mozilla/ViewportUtils.h" #include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/gfx/Matrix.h" +#include "mozilla/layers/LayersTypes.h" #include "nsContainerFrame.h" +#include "nsCoord.h" #include "nsFrameList.h" // for DEBUG_FRAME_DUMP #include "nsHTMLParts.h" #include "nsLayoutUtils.h" #include "nsGkAtoms.h" #include "nsFontMetrics.h" +#include "nsIContentInlines.h" +#include "nsPresContext.h" #include "nsPrintfCString.h" #include "mozilla/dom/Element.h" #include "nsRegion.h" #include "nsDeviceContext.h" -#include "nsIContentInlines.h" #include "nsIFrame.h" #include <algorithm> @@ -276,15 +282,40 @@ static nsIContent* GetClickableAncestor( return nullptr; } -static nscoord AppUnitsFromMM(RelativeTo aFrame, uint32_t aMM) { - nsPresContext* pc = aFrame.mFrame->PresContext(); - float result = float(aMM) * (pc->DeviceContext()->AppUnitsPerPhysicalInch() / - MM_PER_INCH_FLOAT); - if (aFrame.mViewportType == ViewportType::Layout) { +static Scale2D AppUnitsToMMScale(RelativeTo aFrame) { + nsPresContext* presContext = aFrame.mFrame->PresContext(); + + const int32_t appUnitsPerInch = + presContext->DeviceContext()->AppUnitsPerPhysicalInch(); + const float appUnits = + static_cast<float>(appUnitsPerInch) / MM_PER_INCH_FLOAT; + + // Visual coordinates are only used for quantities relative to the + // cross-process root content document's root frame. There should + // not be an enclosing resolution or transform scale above that. + if (aFrame.mViewportType != ViewportType::Layout) { + const nscoord scale = NSToCoordRound(appUnits); + return Scale2D{static_cast<float>(scale), static_cast<float>(scale)}; + } + + Scale2D localResolution{1.0f, 1.0f}; + Scale2D enclosingResolution{1.0f, 1.0f}; + + if (auto* pc = presContext->GetInProcessRootContentDocumentPresContext()) { PresShell* presShell = pc->PresShell(); - result = result / presShell->GetResolution(); + localResolution = {presShell->GetResolution(), presShell->GetResolution()}; + enclosingResolution = ViewportUtils::TryInferEnclosingResolution(presShell); } - return NSToCoordRound(result); + + const gfx::MatrixScales parentScale = + nsLayoutUtils::GetTransformToAncestorScale(aFrame.mFrame); + const Scale2D resolution = + localResolution * parentScale * enclosingResolution; + + const nscoord scaleX = NSToCoordRound(appUnits / resolution.xScale); + const nscoord scaleY = NSToCoordRound(appUnits / resolution.yScale); + + return {static_cast<float>(scaleX), static_cast<float>(scaleY)}; } /** @@ -303,10 +334,11 @@ static nsRect GetTargetRect(RelativeTo aRootFrame, const nsPoint& aPointRelativeToRootFrame, const nsIFrame* aRestrictToDescendants, const EventRadiusPrefs& aPrefs, uint32_t aFlags) { - nsMargin m(AppUnitsFromMM(aRootFrame, aPrefs.mRadiusTopmm), - AppUnitsFromMM(aRootFrame, aPrefs.mRadiusRightmm), - AppUnitsFromMM(aRootFrame, aPrefs.mRadiusBottommm), - AppUnitsFromMM(aRootFrame, aPrefs.mRadiusLeftmm)); + const Scale2D scale = AppUnitsToMMScale(aRootFrame); + nsMargin m(aPrefs.mRadiusTopmm * scale.yScale, + aPrefs.mRadiusRightmm * scale.xScale, + aPrefs.mRadiusBottommm * scale.yScale, + aPrefs.mRadiusLeftmm * scale.xScale); nsRect r(aPointRelativeToRootFrame, nsSize(0, 0)); r.Inflate(m); if (!(aFlags & INPUT_IGNORE_ROOT_SCROLL_FRAME)) { diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index 31c21c3377..cd41da3730 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -14,6 +14,7 @@ #include "mozilla/dom/AncestorIterator.h" #include "mozilla/dom/FontFaceSet.h" #include "mozilla/dom/ElementBinding.h" +#include "mozilla/dom/FragmentDirective.h" #include "mozilla/dom/LargestContentfulPaint.h" #include "mozilla/dom/MouseEventBinding.h" #include "mozilla/dom/PerformanceMainThread.h" @@ -1281,10 +1282,13 @@ void PresShell::Destroy() { ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DiscardImages)); - if (mCaret) { + if (mOriginalCaret) { + mOriginalCaret->Terminate(); + } + if (mCaret && mCaret != mOriginalCaret) { mCaret->Terminate(); - mCaret = nullptr; } + mCaret = mOriginalCaret = nullptr; mFocusedFrameSelection = nullptr; @@ -2238,9 +2242,20 @@ PresShell::GetAccessibleCaretEventHub() const { return eventHub.forget(); } -void PresShell::SetCaret(nsCaret* aNewCaret) { mCaret = aNewCaret; } +void PresShell::SetCaret(nsCaret* aNewCaret) { + if (mCaret == aNewCaret) { + return; + } + if (mCaret) { + mCaret->SchedulePaint(); + } + mCaret = aNewCaret; + if (aNewCaret) { + aNewCaret->SchedulePaint(); + } +} -void PresShell::RestoreCaret() { mCaret = mOriginalCaret; } +void PresShell::RestoreCaret() { SetCaret(mOriginalCaret); } NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable) { bool oldEnabled = mCaretEnabled; @@ -3277,6 +3292,46 @@ nsresult PresShell::ScrollToAnchor() { ScrollAxis(), ScrollFlags::AnchorScrollFlags); } +bool PresShell::HighlightAndGoToTextFragment(bool aScrollToTextFragment) { + MOZ_ASSERT(mDocument); + if (!StaticPrefs::dom_text_fragments_enabled()) { + return false; + } + const RefPtr<FragmentDirective> fragmentDirective = + mDocument->FragmentDirective(); + + nsTArray<RefPtr<nsRange>> textDirectiveRanges = + fragmentDirective->FindTextFragmentsInDocument(); + if (textDirectiveRanges.IsEmpty()) { + return false; + } + + const RefPtr<Selection> targetTextSelection = + GetCurrentSelection(SelectionType::eTargetText); + if (!targetTextSelection) { + return false; + } + for (RefPtr<nsRange> range : textDirectiveRanges) { + targetTextSelection->AddRangeAndSelectFramesAndNotifyListeners( + *range, IgnoreErrors()); + } + if (!aScrollToTextFragment) { + return false; + } + + // Scroll the last text directive into view. + nsRange* lastRange = textDirectiveRanges.LastElement(); + MOZ_ASSERT(lastRange); + if (RefPtr<nsIContent> lastRangeStartContent = + nsIContent::FromNode(lastRange->GetStartContainer())) { + return ScrollContentIntoView( + lastRangeStartContent, + ScrollAxis(WhereToScroll::Center, WhenToScroll::Always), + ScrollAxis(), ScrollFlags::AnchorScrollFlags) == NS_OK; + } + return false; +} + /* * Helper (per-continuation) for ScrollContentIntoView. * @@ -3876,11 +3931,9 @@ void PresShell::ScheduleViewManagerFlush() { return; } - nsPresContext* presContext = GetPresContext(); - if (presContext) { + if (nsPresContext* presContext = GetPresContext()) { presContext->RefreshDriver()->ScheduleViewManagerFlush(); } - SetNeedLayoutFlush(); } void PresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent) { @@ -4473,6 +4526,26 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::ElementStateChanged( } } +MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::CustomStatesWillChange( + Element& aElement) { + if (MOZ_UNLIKELY(!mDidInitialize)) { + return; + } + + mPresContext->RestyleManager()->CustomStatesWillChange(aElement); +} + +MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::CustomStateChanged( + Element& aElement, nsAtom* aState) { + MOZ_ASSERT(!mIsDocumentGone, "Unexpected CustomStateChanged"); + MOZ_ASSERT(aState, "Unexpected empty state"); + + if (mDidInitialize) { + nsAutoCauseReflowNotifier crNotifier(this); + mPresContext->RestyleManager()->CustomStateChanged(aElement, aState); + } +} + void PresShell::DocumentStatesChanged(DocumentState aStateMask) { MOZ_ASSERT(!mIsDocumentGone, "Unexpected DocumentStatesChanged"); MOZ_ASSERT(mDocument); @@ -5582,7 +5655,7 @@ nsresult PresShell::SetResolutionAndScaleTo(float aResolution, // GetResolution handles mResolution being nothing by returning 1 so this // is checking that the resolution is actually changing. - bool resolutionUpdated = (aResolution != GetResolution()); + bool resolutionUpdated = aResolution != GetResolution(); mLastResolutionChangeOrigin = aOrigin; @@ -5647,7 +5720,9 @@ void PresShell::SetRenderingState(const RenderingState& aState) { } void PresShell::SynthesizeMouseMove(bool aFromScroll) { - if (!StaticPrefs::layout_reflow_synthMouseMove()) return; + if (!StaticPrefs::layout_reflow_synthMouseMove()) { + return; + } if (mPaintingSuppressed || !mIsActive || !mPresContext) { return; @@ -5660,8 +5735,9 @@ void PresShell::SynthesizeMouseMove(bool aFromScroll) { return; } - if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) + if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) { return; + } if (!mSynthMouseMoveEvent.IsPending()) { RefPtr<nsSynthMouseMoveEvent> ev = @@ -6488,7 +6564,7 @@ void PresShell::PaintInternal(nsView* aViewToPaint, PaintInternalFlags aFlags) { // We also force sync-decoding via pref for reftests. if (aFlags & PaintInternalFlags::PaintSyncDecodeImages || mDocument->IsStaticDocument() || - StaticPrefs::image_decode_sync_enabled()) { + StaticPrefs::image_testing_decode_sync_enabled()) { flags |= PaintFrameFlags::SyncDecodeImages; } if (renderer->GetBackendType() == layers::LayersBackend::LAYERS_WR) { @@ -11182,10 +11258,8 @@ void PresShell::SetIsActive(bool aIsActive) { #if defined(MOZ_WIDGET_ANDROID) if (!aIsActive && presContext && presContext->IsRootContentDocumentCrossProcess()) { - if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) { - // Reset the dynamic toolbar offset state. - presContext->UpdateDynamicToolbarOffset(0); - } + // Reset the dynamic toolbar offset state. + presContext->UpdateDynamicToolbarOffset(0); } #endif } @@ -11220,8 +11294,7 @@ Maybe<MobileViewportManager::ManagerType> UseMobileViewportManager( if (nsLayoutUtils::ShouldHandleMetaViewport(aDocument)) { return Some(MobileViewportManager::ManagerType::VisualAndMetaViewport); } - if (StaticPrefs::apz_mvm_force_enabled() || - nsLayoutUtils::AllowZoomingForDocument(aDocument)) { + if (nsLayoutUtils::AllowZoomingForDocument(aDocument)) { return Some(MobileViewportManager::ManagerType::VisualViewportOnly); } return Nothing(); @@ -11244,6 +11317,11 @@ void PresShell::MaybeRecreateMobileViewportManager(bool aAfterInitialization) { return; } + if (!mPresContext->IsRootContentDocumentCrossProcess()) { + MOZ_ASSERT(!mMobileViewportManager, "We never create MVMs for subframes"); + return; + } + if (mMobileViewportManager) { // We have one, but we need to either destroy it completely to replace it // with another one of the correct type. So either way, let's destroy the @@ -11253,16 +11331,6 @@ void PresShell::MaybeRecreateMobileViewportManager(bool aAfterInitialization) { mMVMContext = nullptr; ResetVisualViewportSize(); - - // After we clear out the MVM and the MVMContext, also reset the - // resolution to its pre-MVM value. - SetResolutionAndScaleTo(mDocument->GetSavedResolutionBeforeMVM(), - ResolutionChangeOrigin::MainThreadRestore); - - if (aAfterInitialization) { - // Force a reflow to our correct view manager size. - ForceResizeReflowWithCurrentDimensions(); - } } if (mvmType) { @@ -11270,32 +11338,33 @@ void PresShell::MaybeRecreateMobileViewportManager(bool aAfterInitialization) { // have one. MOZ_ASSERT(!mMobileViewportManager); - if (mPresContext->IsRootContentDocumentCrossProcess()) { - // Store the resolution so we can restore to this resolution when - // the MVM is destroyed. - mDocument->SetSavedResolutionBeforeMVM(mResolution.valueOr(1.0f)); - - mMVMContext = new GeckoMVMContext(mDocument, this); - mMobileViewportManager = new MobileViewportManager(mMVMContext, *mvmType); - if (MOZ_UNLIKELY( - MOZ_LOG_TEST(MobileViewportManager::gLog, LogLevel::Debug))) { - nsIURI* uri = mDocument->GetDocumentURI(); - MOZ_LOG(MobileViewportManager::gLog, LogLevel::Debug, - ("Created MVM %p (type %d) for URI %s", - mMobileViewportManager.get(), (int)*mvmType, - uri ? uri->GetSpecOrDefault().get() : "(null)")); - } - - if (aAfterInitialization) { - // Setting the initial viewport will trigger a reflow. - mMobileViewportManager->SetInitialViewport(); - } + mMVMContext = new GeckoMVMContext(mDocument, this); + mMobileViewportManager = new MobileViewportManager(mMVMContext, *mvmType); + if (MOZ_UNLIKELY( + MOZ_LOG_TEST(MobileViewportManager::gLog, LogLevel::Debug))) { + nsIURI* uri = mDocument->GetDocumentURI(); + MOZ_LOG( + MobileViewportManager::gLog, LogLevel::Debug, + ("Created MVM %p (type %d) for URI %s", mMobileViewportManager.get(), + (int)*mvmType, uri ? uri->GetSpecOrDefault().get() : "(null)")); } } + if (aAfterInitialization) { + // Setting the initial viewport will trigger a reflow. + if (mMobileViewportManager) { + mMobileViewportManager->SetInitialViewport(); + } else { + // Force a reflow to our correct view manager size. + ForceResizeReflowWithCurrentDimensions(); + } + // After we clear out the MVM and the MVMContext, also reset the + // resolution to 1. + SetResolutionAndScaleTo(1.0f, ResolutionChangeOrigin::MainThreadRestore); + } } bool PresShell::UsesMobileViewportSizing() const { - return mMobileViewportManager != nullptr && + return mMobileViewportManager && nsLayoutUtils::ShouldHandleMetaViewport(mDocument); } @@ -11708,8 +11777,7 @@ static bool IsTopLevelWidget(nsIWidget* aWidget) { auto windowType = aWidget->GetWindowType(); return windowType == WindowType::TopLevel || - windowType == WindowType::Dialog || windowType == WindowType::Popup || - windowType == WindowType::Sheet; + windowType == WindowType::Dialog || windowType == WindowType::Popup; } PresShell::WindowSizeConstraints PresShell::GetWindowSizeConstraints() { @@ -11777,10 +11845,9 @@ void PresShell::SyncWindowProperties(bool aSync) { canvas ? canvas : rootFrame, rootFrame); windowWidget->SetTransparencyMode(mode); - // For macOS, apply color scheme overrides to the top level window widget. - if (auto scheme = pc->GetOverriddenOrEmbedderColorScheme()) { - windowWidget->SetColorScheme(scheme); - } + // For macOS, apply color scheme to the top level window widget. + windowWidget->SetColorScheme( + Some(LookAndFeel::ColorSchemeForFrame(rootFrame))); } if (!weak.IsAlive()) { @@ -12030,13 +12097,6 @@ void PresShell::EventHandler::EventTargetData::UpdateWheelEventTarget( return; } - // If the browsing context is no longer the same as the context of the - // current wheel transaction, do not override the event target. - if (!groupFrame->PresContext() || !groupFrame->PresShell() || - groupFrame->PresContext() != GetPresContext()) { - return; - } - // If dom.event.wheel-event-groups.enabled is set and whe have a stored // event target from the wheel transaction, override the event target. SetFrameAndComputePresShellAndContent(groupFrame, aGUIEvent); diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h index 482ace1421..2b43b3c492 100644 --- a/layout/base/PresShell.h +++ b/layout/base/PresShell.h @@ -531,6 +531,12 @@ class PresShell final : public nsStubDocumentObserver, dom::HTMLSlotElement* aOldSlot, dom::HTMLSlotElement* aNewSlot); + /** + * Handles when a CustomStateSet state is about to be removed or added. + */ + void CustomStatesWillChange(Element& aElement); + void CustomStateChanged(Element& aElement, nsAtom* aState); + void PostRecreateFramesFor(Element*); void RestyleForAnimation(Element*, RestyleHint); @@ -1604,6 +1610,18 @@ class PresShell final : public nsStubDocumentObserver, MOZ_CAN_RUN_SCRIPT nsresult ScrollToAnchor(); /** + * Finds text fragments ranes in the document, highlights the ranges and + * scrolls to the last text fragment range on the page if + * `aScrollToTextFragment` is true. + * + * @param aScrollToTextFragment If true, scrolls the view to the last text + * fragment. + * @return True if scrolling happened. + */ + MOZ_CAN_RUN_SCRIPT bool HighlightAndGoToTextFragment( + bool aScrollToTextFragment); + + /** * When scroll anchoring adjusts positions in the root frame during page load, * it may move our scroll position in the root frame. * diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 8c07512093..e00493467f 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -2087,6 +2087,28 @@ void RestyleManager::AnimationsWithDestroyedFrame ::StopAnimationsWithoutFrame( } } +// When using handled hints by an ancestor, we need to make sure that our +// ancestor in the DOM tree is actually our ancestor in the flat tree. +// Otherwise, we can't guarantee that e.g. a repaint from an ancestor in the DOM +// will really end up repainting us. +static bool CanUseHandledHintsFromAncestors(const nsIFrame* aFrame) { + if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { + // An out of flow can be parented in other part of the tree. + return false; + } + if (aFrame->IsColumnSpanInMulticolSubtree()) { + // Any column-spanner's parent frame is not its DOM parent's primary frame. + return false; + } + if (aFrame->IsTableCaption()) { + // This one is more subtle. captions are in-flow children of the table + // frame. But they are parented to the table wrapper. So hints handled for + // the inner table might not be applicable to us. + return false; + } + return true; +} + #ifdef DEBUG static bool IsAnonBox(const nsIFrame* aFrame) { return aFrame->Style()->IsAnonBox(); @@ -2185,8 +2207,7 @@ static bool IsInReplicatedFixedPosTree(const nsIFrame* aFrame) { void ServoRestyleState::AssertOwner(const ServoRestyleState& aParent) const { MOZ_ASSERT(mOwner); - MOZ_ASSERT(!mOwner->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)); - MOZ_ASSERT(!mOwner->IsColumnSpanInMulticolSubtree()); + MOZ_ASSERT(CanUseHandledHintsFromAncestors(mOwner)); // We allow aParent.mOwner to be null, for cases when we're not starting at // the root of the tree. We also allow aParent.mOwner to be somewhere up our // expected owner chain not our immediate owner, which allows us creating long @@ -2309,7 +2330,7 @@ size_t ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent, nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent); if (parentForRestyle != aParent) { parentRestyleState.emplace(*parentForRestyle, *this, nsChangeHint_Empty, - Type::InFlow); + CanUseHandledHints::Yes); } ServoRestyleState& curRestyleState = parentRestyleState ? *parentRestyleState : *this; @@ -2332,7 +2353,7 @@ size_t ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent, // the other hand, presumably our mChangesHandled already has the bits // we really want here so in practice it doesn't matter. ServoRestyleState childState(*cur, curRestyleState, nsChangeHint_Empty, - Type::InFlow, + CanUseHandledHints::Yes, /* aAssertWrapperRestyleLength = */ false); numProcessed += childState.ProcessMaybeNestedWrapperRestyle(cur, aIndex + 1); @@ -2652,8 +2673,7 @@ static void UpdateOneAdditionalComputedStyle(nsIFrame* aFrame, uint32_t aIndex, uint32_t equalStructs; // Not used, actually. nsChangeHint childHint = aOldContext.CalcStyleDifference(*newStyle, &equalStructs); - if (!aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && - !aFrame->IsColumnSpanInMulticolSubtree()) { + if (CanUseHandledHintsFromAncestors(aFrame)) { childHint = NS_RemoveSubsumedHints(childHint, aRestyleState.ChangesHandledFor(aFrame)); } @@ -2812,11 +2832,8 @@ bool RestyleManager::ProcessPostTraversal(Element* aElement, const bool isOutOfFlow = primaryFrame && primaryFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW); - // We need this because any column-spanner's parent frame is not its DOM - // parent's primary frame. We need some special check similar to out-of-flow - // frames. - const bool isColumnSpan = - primaryFrame && primaryFrame->IsColumnSpanInMulticolSubtree(); + const bool canUseHandledHints = + primaryFrame && CanUseHandledHintsFromAncestors(primaryFrame); // Grab the change hint from Servo. bool wasRestyled = false; @@ -2848,11 +2865,12 @@ bool RestyleManager::ProcessPostTraversal(Element* aElement, maybeAnonBoxChild = primaryFrame->GetPlaceholderFrame(); } else { maybeAnonBoxChild = primaryFrame; - // Do not subsume change hints for the column-spanner. - if (!isColumnSpan) { - changeHint = NS_RemoveSubsumedHints( - changeHint, aRestyleState.ChangesHandledFor(styleFrame)); - } + } + + // Do not subsume change hints for the column-spanner. + if (canUseHandledHints) { + changeHint = NS_RemoveSubsumedHints( + changeHint, aRestyleState.ChangesHandledFor(styleFrame)); } // If the parent wasn't restyled, the styles of our anon box parents won't @@ -2944,10 +2962,9 @@ bool RestyleManager::ProcessPostTraversal(Element* aElement, Maybe<ServoRestyleState> thisFrameRestyleState; if (styleFrame) { - auto type = isOutOfFlow || isColumnSpan ? ServoRestyleState::Type::OutOfFlow - : ServoRestyleState::Type::InFlow; - - thisFrameRestyleState.emplace(*styleFrame, aRestyleState, changeHint, type); + thisFrameRestyleState.emplace( + *styleFrame, aRestyleState, changeHint, + ServoRestyleState::CanUseHandledHints(canUseHandledHints)); } // We can't really assume as used changes from display: contents elements (or @@ -3229,6 +3246,12 @@ void RestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags) { // construction). nsTArray<RefPtr<Element>> anchorsToSuppress; + // Unfortunately, the frame constructor and ProcessPostTraversal can + // generate new change hints while processing existing ones. We redirect + // those into a secondary queue and iterate until there's nothing left. + ReentrantChangeList newChanges; + mReentrantChanges = &newChanges; + { DocumentStyleRootIterator iter(doc->GetServoRestyleRoot()); while (Element* root = iter.GetNextStyleRoot()) { @@ -3254,34 +3277,26 @@ void RestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags) { doc->ClearServoRestyleRoot(); ClearSnapshots(); - // Process the change hints. - // - // Unfortunately, the frame constructor can generate new change hints while - // processing existing ones. We redirect those into a secondary queue and - // iterate until there's nothing left. - { - ReentrantChangeList newChanges; - mReentrantChanges = &newChanges; - while (!currentChanges.IsEmpty()) { - ProcessRestyledFrames(currentChanges); - MOZ_ASSERT(currentChanges.IsEmpty()); - for (ReentrantChange& change : newChanges) { - if (!(change.mHint & nsChangeHint_ReconstructFrame) && - !change.mContent->GetPrimaryFrame()) { - // SVG Elements post change hints without ensuring that the primary - // frame will be there after that (see bug 1366142). - // - // Just ignore those, since we can't really process them. - continue; - } - currentChanges.AppendChange(change.mContent->GetPrimaryFrame(), - change.mContent, change.mHint); + while (!currentChanges.IsEmpty()) { + ProcessRestyledFrames(currentChanges); + MOZ_ASSERT(currentChanges.IsEmpty()); + for (ReentrantChange& change : newChanges) { + if (!(change.mHint & nsChangeHint_ReconstructFrame) && + !change.mContent->GetPrimaryFrame()) { + // SVG Elements post change hints without ensuring that the primary + // frame will be there after that (see bug 1366142). + // + // Just ignore those, since we can't really process them. + continue; } - newChanges.Clear(); + currentChanges.AppendChange(change.mContent->GetPrimaryFrame(), + change.mContent, change.mHint); } - mReentrantChanges = nullptr; + newChanges.Clear(); } + mReentrantChanges = nullptr; + // Suppress adjustments in the after-change scroll anchors if needed, now // that we're done reframing everything. for (Element* element : anchorsToSuppress) { @@ -3435,6 +3450,45 @@ void RestyleManager::ElementStateChanged(Element* aElement, MaybeRestyleForRelativeSelectorState(styleSet, aElement, aChangedBits); } +void RestyleManager::CustomStatesWillChange(Element& aElement) { + MOZ_DIAGNOSTIC_ASSERT(!mInStyleRefresh); + + IncrementUndisplayedRestyleGeneration(); + + // Relative selector invalidation travels ancestor and earlier sibling + // direction, so it's very possible that it invalidates a styled element. + if (!aElement.HasServoData() && + !(aElement.GetSelectorFlags() & + NodeSelectorFlags::RelativeSelectorSearchDirectionAncestorSibling)) { + return; + } + + ServoElementSnapshot& snapshot = SnapshotFor(aElement); + snapshot.AddCustomStates(aElement); +} + +void RestyleManager::CustomStateChanged(Element& aElement, nsAtom* aState) { + ServoStyleSet& styleSet = *StyleSet(); + MaybeRestyleForNthOfCustomState(styleSet, aElement, aState); + styleSet.MaybeInvalidateRelativeSelectorCustomStateDependency( + aElement, aState, Snapshots()); +} + +void RestyleManager::MaybeRestyleForNthOfCustomState(ServoStyleSet& aStyleSet, + Element& aChild, + nsAtom* aState) { + const auto* parentNode = aChild.GetParentNode(); + MOZ_ASSERT(parentNode); + const auto parentFlags = parentNode->GetSelectorFlags(); + if (!(parentFlags & NodeSelectorFlags::HasSlowSelectorNthOf)) { + return; + } + + if (aStyleSet.HasNthOfCustomStateDependency(aChild, aState)) { + RestyleSiblingsForNthOf(&aChild, parentFlags); + } +} + void RestyleManager::MaybeRestyleForNthOfState(ServoStyleSet& aStyleSet, Element* aChild, ElementState aChangedBits) { diff --git a/layout/base/RestyleManager.h b/layout/base/RestyleManager.h index 62fef15a16..8485fe18a0 100644 --- a/layout/base/RestyleManager.h +++ b/layout/base/RestyleManager.h @@ -66,13 +66,11 @@ class ServoRestyleState { // our children too if we're out of flow since they aren't necessarily // parented in DOM order, and thus a change handled by a DOM ancestor doesn't // necessarily mean that it's handled for an ancestor frame. - enum class Type { - InFlow, - OutOfFlow, - }; + enum class CanUseHandledHints : bool { No = false, Yes }; ServoRestyleState(const nsIFrame& aOwner, ServoRestyleState& aParentState, - nsChangeHint aHintForThisFrame, Type aType, + nsChangeHint aHintForThisFrame, + CanUseHandledHints aCanUseHandledHints, bool aAssertWrapperRestyleLength = true) : mStyleSet(aParentState.mStyleSet), mChangeList(aParentState.mChangeList), @@ -81,7 +79,7 @@ class ServoRestyleState { aParentState.mPendingScrollAnchorSuppressions), mPendingWrapperRestyleOffset( aParentState.mPendingWrapperRestyles.Length()), - mChangesHandled(aType == Type::InFlow + mChangesHandled(bool(aCanUseHandledHints) ? aParentState.mChangesHandled | aHintForThisFrame : aHintForThisFrame) #ifdef DEBUG @@ -90,7 +88,7 @@ class ServoRestyleState { mAssertWrapperRestyleLength(aAssertWrapperRestyleLength) #endif { - if (aType == Type::InFlow) { + if (bool(aCanUseHandledHints)) { AssertOwner(aParentState); } } @@ -361,6 +359,11 @@ class RestyleManager { void ElementStateChanged(Element*, dom::ElementState); + void CustomStatesWillChange(Element&); + void CustomStateChanged(Element&, nsAtom* aState); + void MaybeRestyleForNthOfCustomState(ServoStyleSet&, Element&, + nsAtom* aState); + /** * Posts restyle hints for siblings of an element and their descendants if the * element's parent has NODE_HAS_SLOW_SELECTOR_NTH_OF and the element has a diff --git a/layout/base/ViewportUtils.cpp b/layout/base/ViewportUtils.cpp index f0d18510bf..8b8e4a4d27 100644 --- a/layout/base/ViewportUtils.cpp +++ b/layout/base/ViewportUtils.cpp @@ -260,6 +260,9 @@ const nsIFrame* ViewportUtils::IsZoomedContentRoot(const nsIFrame* aFrame) { } Scale2D ViewportUtils::TryInferEnclosingResolution(PresShell* aShell) { + if (!XRE_IsContentProcess()) { + return {1.0f, 1.0f}; + } MOZ_ASSERT(aShell && aShell->GetPresContext()); MOZ_ASSERT(!aShell->GetPresContext()->GetParentPresContext(), "TryInferEnclosingResolution can only be called for a root pres " diff --git a/layout/base/ZoomConstraintsClient.cpp b/layout/base/ZoomConstraintsClient.cpp index e695c7755e..e9be0c2073 100644 --- a/layout/base/ZoomConstraintsClient.cpp +++ b/layout/base/ZoomConstraintsClient.cpp @@ -53,9 +53,20 @@ static nsIWidget* GetWidget(PresShell* aPresShell) { return nullptr; } if (nsIFrame* rootFrame = aPresShell->GetRootFrame()) { +#if defined(MOZ_WIDGET_ANDROID) + // On Android in cases of about:XX pages loaded in the browser parent + // process we need to return the nearest widget since it's the widget owning + // an IAPZCTreeManager to communicate with the APZCTreeManager for the + // browser. + // In bug 1648427 we will apply this code to desktops as well to make + // about pages zoomable on desktops, but it will be involving more works, + // see https://bugzilla.mozilla.org/show_bug.cgi?id=1648427#c7 . + return rootFrame->GetNearestWidget(); +#else if (nsView* view = rootFrame->GetView()) { return view->GetWidget(); } +#endif } return nullptr; } @@ -199,6 +210,11 @@ void ZoomConstraintsClient::RefreshZoomConstraints() { return; } + // Ignore documents which has been removed from the doc shell. + if (!mDocument->IsActive()) { + return; + } + uint32_t presShellId = 0; ScrollableLayerGuid::ViewID viewId = ScrollableLayerGuid::NULL_SCROLL_ID; bool scrollIdentifiersValid = diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index d9cf87f4d2..a379b981b6 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -385,11 +385,7 @@ load 836990-1.html load 840480.html load 842114.html load 847242.html -# This test is slow, because it uses setTimeout a lot. -# After bug 1875100, on windows, it times out because there's a left-over -# window or so, and that bug causes us to throttle timers. Disable window -# occlusion to work around this for now, see bug 1864255. -pref(widget.windows.window_occlusion_tracking.enabled,false) skip-if(ThreadSanitizer) load 852293.html +skip-if(ThreadSanitizer) load 852293.html pref(layers.force-active,true) load 859526-1.html pref(layers.force-active,true) load 859630-1.html load 860579-1.html @@ -494,7 +490,7 @@ load 1463940.html HTTP load 1464641.html load 1464737.html load 1466638.html -pref(layout.css.motion-path-ray.enabled,true) asserts(1-1) load 1467519.html # bogus size +asserts(1-1) load 1467519.html # bogus size load 1467688.html load 1467964.html load 1469354.html @@ -528,7 +524,7 @@ load 1539303.html load 1541679.html load 1547261.html load 1547391.html -pref(widget.windows.window_occlusion_tracking.enabled,false) load 1548057.html # Bug 1819154 +load 1548057.html load 1549867.html load 1553874.html load 1560328.html @@ -547,7 +543,7 @@ load 1599532.html pref(layout.accessiblecaret.enabled,true) load 1606492.html load 1654315.html load 1676301-1.html -pref(apz.mvm.force-enabled,false) pref(dom.meta-viewport.enabled,false) pref(apz.allow_zooming,false) pref(layout.dynamic-toolbar-max-height,100) load 1689371.html +pref(dom.meta-viewport.enabled,false) pref(apz.allow_zooming,false) pref(layout.dynamic-toolbar-max-height,100) load 1689371.html load 1685146.html load 1689912.html load 1690163.html diff --git a/layout/base/metrics.yaml b/layout/base/metrics.yaml index 856ff77965..631200bd48 100644 --- a/layout/base/metrics.yaml +++ b/layout/base/metrics.yaml @@ -29,6 +29,22 @@ performance.pageload: - perf-telemetry-alerts@mozilla.com expires: never + async_sheet_load: + type: timing_distribution + time_unit: millisecond + description: > + Time spent in milliseconds since a style sheet started loading async + until it finished. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892660 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892660 + data_sensitivity: + - technical + notification_emails: + - emilio@mozilla.com + - perf-telemetry-alerts@mozilla.com + expires: 132 performance.responsiveness: req_anim_frame_callback: diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 149f2f24bf..ba43eb70b9 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -1537,12 +1537,11 @@ already_AddRefed<nsIContent> nsCSSFrameConstructor::CreateGenConTextNode( void nsCSSFrameConstructor::CreateGeneratedContent( nsFrameConstructorState& aState, Element& aOriginatingElement, - ComputedStyle& aPseudoStyle, uint32_t aContentIndex, - const FunctionRef<void(nsIContent*)> aAddChild) { + ComputedStyle& aPseudoStyle, const StyleContentItem& aItem, + size_t aContentIndex, const FunctionRef<void(nsIContent*)> aAddChild) { using Type = StyleContentItem::Tag; // Get the content value - const auto& item = aPseudoStyle.StyleContent()->ContentAt(aContentIndex); - const Type type = item.tag; + const Type type = aItem.tag; switch (type) { case Type::Image: { @@ -1552,7 +1551,7 @@ void nsCSSFrameConstructor::CreateGeneratedContent( } case Type::String: { - const auto string = item.AsString().AsString(); + const auto string = aItem.AsString().AsString(); if (string.IsEmpty()) { return; } @@ -1563,7 +1562,7 @@ void nsCSSFrameConstructor::CreateGeneratedContent( } case Type::Attr: { - const auto& attr = item.AsAttr(); + const auto& attr = aItem.AsAttr(); RefPtr<nsAtom> attrName = attr.attribute.AsAtom(); int32_t attrNameSpace = kNameSpaceID_None; RefPtr<nsAtom> ns = attr.namespace_url.AsAtom(); @@ -1592,11 +1591,11 @@ void nsCSSFrameConstructor::CreateGeneratedContent( CounterStylePtr ptr; nsString separator; if (type == Type::Counter) { - auto& counter = item.AsCounter(); + auto& counter = aItem.AsCounter(); name = counter._0.AsAtom(); ptr = CounterStylePtr::FromStyle(counter._1); } else { - auto& counters = item.AsCounters(); + auto& counters = aItem.AsCounters(); name = counters._0.AsAtom(); CopyUTF8toUTF16(counters._1.AsString(), separator); ptr = CounterStylePtr::FromStyle(counters._2); @@ -1947,13 +1946,14 @@ void nsCSSFrameConstructor::CreateGeneratedContentItem( mPresShell->StyleSet()->StyleNewSubtree(childElement); } }; - const uint32_t contentCount = pseudoStyle->StyleContent()->ContentCount(); - for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) { - CreateGeneratedContent(aState, aOriginatingElement, *pseudoStyle, - contentIndex, AppendChild); + auto items = pseudoStyle->StyleContent()->NonAltContentItems(); + size_t index = 0; + for (const auto& item : items) { + CreateGeneratedContent(aState, aOriginatingElement, *pseudoStyle, item, + index++, AppendChild); } // If a ::marker has no 'content' then generate it from its 'list-style-*'. - if (contentCount == 0 && aPseudoElement == PseudoStyleType::marker) { + if (index == 0 && aPseudoElement == PseudoStyleType::marker) { CreateGeneratedContentFromListStyle(aState, aOriginatingElement, *pseudoStyle, AppendChild); } @@ -4985,9 +4985,8 @@ nsCSSFrameConstructor::FindSVGData(const Element& aElement, return data; } -void nsCSSFrameConstructor::InsertPageBreakItem( - nsIContent* aContent, FrameConstructionItemList& aItems, - InsertPageBreakLocation location) { +void nsCSSFrameConstructor::AppendPageBreakItem( + nsIContent* aContent, FrameConstructionItemList& aItems) { RefPtr<ComputedStyle> pseudoStyle = mPresShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle( PseudoStyleType::pageBreak); @@ -4997,13 +4996,8 @@ void nsCSSFrameConstructor::InsertPageBreakItem( static constexpr FrameConstructionData sPageBreakData(NS_NewPageBreakFrame, FCDATA_SKIP_FRAMESET); - if (location == InsertPageBreakLocation::eBefore) { - aItems.PrependItem(this, &sPageBreakData, aContent, pseudoStyle.forget(), - true); - } else { - aItems.AppendItem(this, &sPageBreakData, aContent, pseudoStyle.forget(), - true); - } + aItems.AppendItem(this, &sPageBreakData, aContent, pseudoStyle.forget(), + true); } bool nsCSSFrameConstructor::ShouldCreateItemsForChild( @@ -5289,7 +5283,7 @@ void nsCSSFrameConstructor::AddFrameConstructionItemsInternal( aFlags.contains(ItemFlag::AllowPageBreak) && aState.mPresContext->IsPaginated() && !display.IsAbsolutelyPositionedStyle() && - !(aParentFrame && aParentFrame->IsGridContainerFrame()) && + !(aParentFrame && aParentFrame->IsFlexOrGridContainer()) && !(bits & FCDATA_IS_TABLE_PART) && !(bits & FCDATA_IS_SVG_TEXT); if (canHavePageBreak && display.BreakBefore()) { AppendPageBreakItem(aContent, aItems); @@ -6012,7 +6006,7 @@ nsIFrame* nsCSSFrameConstructor::GetInsertionPrevSibling( // Find the frame that precedes the insertion point. FlattenedChildIterator iter(aInsertion->mContainer); - if (iter.ShadowDOMInvolved() || !aChild->IsRootOfNativeAnonymousSubtree()) { + if (!aChild->IsRootOfNativeAnonymousSubtree()) { // The check for IsRootOfNativeAnonymousSubtree() is because editor is // severely broken and calls us directly for native anonymous // nodes that it creates. @@ -8390,7 +8384,7 @@ void nsCSSFrameConstructor::RecreateFramesForContent( } // TODO(emilio): We technically can find the right insertion point nowadays - // using StyleChildrenIterator rather than FlattenedTreeIterator. But we'd + // using StyleChildrenIterator rather than FlattenedChildIterator. But we'd // need to tweak the setup to insert into replaced elements to filter which // anonymous roots can be allowed, and which can't. // diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 283d1385ce..5fdea315b8 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -477,7 +477,8 @@ class nsCSSFrameConstructor final : public nsFrameManager { */ void CreateGeneratedContent( nsFrameConstructorState& aState, Element& aOriginatingElement, - ComputedStyle& aPseudoStyle, uint32_t aContentIndex, + ComputedStyle& aPseudoStyle, const mozilla::StyleContentItem& aItem, + size_t aContentIndex, const mozilla::FunctionRef<void(nsIContent*)> aAddChild); /** @@ -1423,18 +1424,8 @@ class nsCSSFrameConstructor final : public nsFrameManager { // for it. void ReframeTextIfNeeded(nsIContent* aContent); - enum InsertPageBreakLocation { eBefore, eAfter }; - inline void AppendPageBreakItem(nsIContent* aContent, - FrameConstructionItemList& aItems) { - InsertPageBreakItem(aContent, aItems, InsertPageBreakLocation::eAfter); - } - inline void PrependPageBreakItem(nsIContent* aContent, - FrameConstructionItemList& aItems) { - InsertPageBreakItem(aContent, aItems, InsertPageBreakLocation::eBefore); - } - void InsertPageBreakItem(nsIContent* aContent, - FrameConstructionItemList& aItems, - InsertPageBreakLocation location); + void AppendPageBreakItem(nsIContent* aContent, + FrameConstructionItemList& aItems); // Function to find FrameConstructionData for aElement. Will return // null if aElement is not HTML. diff --git a/layout/base/nsCaret.cpp b/layout/base/nsCaret.cpp index d66d55d6bb..8707dcba5f 100644 --- a/layout/base/nsCaret.cpp +++ b/layout/base/nsCaret.cpp @@ -50,16 +50,7 @@ using BidiEmbeddingLevel = mozilla::intl::BidiEmbeddingLevel; // like an insignificant dot static const int32_t kMinBidiIndicatorPixels = 2; -nsCaret::nsCaret() - : mOverrideOffset(0), - mBlinkCount(-1), - mBlinkRate(0), - mHideCount(0), - mIsBlinkOn(false), - mVisible(false), - mReadOnly(false), - mShowDuringSelection(false), - mIgnoreUserModify(true) {} +nsCaret::nsCaret() = default; nsCaret::~nsCaret() { StopBlinking(); } @@ -70,10 +61,6 @@ nsresult nsCaret::Init(PresShell* aPresShell) { do_GetWeakReference(aPresShell); // the presshell owns us, so no addref NS_ASSERTION(mPresShell, "Hey, pres shell should support weak refs"); - mShowDuringSelection = - LookAndFeel::GetInt(LookAndFeel::IntID::ShowCaretDuringSelection, - mShowDuringSelection ? 1 : 0) != 0; - RefPtr<Selection> selection = aPresShell->GetSelection(nsISelectionController::SELECTION_NORMAL); if (!selection) { @@ -82,6 +69,7 @@ nsresult nsCaret::Init(PresShell* aPresShell) { selection->AddSelectionListener(this); mDomSelectionWeak = selection; + UpdateCaretPositionFromSelectionIfNeeded(); return NS_OK; } @@ -137,8 +125,7 @@ void nsCaret::Terminate() { } mDomSelectionWeak = nullptr; mPresShell = nullptr; - - mOverrideContent = nullptr; + mCaretPosition = {}; } NS_IMPL_ISUPPORTS(nsCaret, nsISelectionListener) @@ -148,15 +135,30 @@ Selection* nsCaret::GetSelection() { return mDomSelectionWeak; } void nsCaret::SetSelection(Selection* aDOMSel) { MOZ_ASSERT(aDOMSel); mDomSelectionWeak = aDOMSel; + UpdateCaretPositionFromSelectionIfNeeded(); ResetBlinking(); - SchedulePaint(aDOMSel); + SchedulePaint(); } -void nsCaret::SetVisible(bool inMakeVisible) { - mVisible = inMakeVisible; - mIgnoreUserModify = mVisible; +void nsCaret::SetVisible(bool aVisible) { + const bool wasVisible = mVisible; + mVisible = aVisible; + mIgnoreUserModify = aVisible; + if (mVisible != wasVisible) { + CaretVisibilityMaybeChanged(); + } +} + +bool nsCaret::IsVisible() const { return mVisible && !mHideCount; } + +void nsCaret::CaretVisibilityMaybeChanged() { ResetBlinking(); SchedulePaint(); + if (IsVisible()) { + // We ignore caret position updates when the caret is not visible, so we + // update the caret position here if needed. + UpdateCaretPositionFromSelectionIfNeeded(); + } } void nsCaret::AddForceHide() { @@ -164,20 +166,18 @@ void nsCaret::AddForceHide() { if (++mHideCount > 1) { return; } - ResetBlinking(); - SchedulePaint(); + CaretVisibilityMaybeChanged(); } void nsCaret::RemoveForceHide() { if (!mHideCount || --mHideCount) { return; } - ResetBlinking(); - SchedulePaint(); + CaretVisibilityMaybeChanged(); } -void nsCaret::SetCaretReadOnly(bool inMakeReadonly) { - mReadOnly = inMakeReadonly; +void nsCaret::SetCaretReadOnly(bool aReadOnly) { + mReadOnly = aReadOnly; ResetBlinking(); SchedulePaint(); } @@ -333,61 +333,48 @@ nsRect nsCaret::GetGeometryForFrame(nsIFrame* aFrame, int32_t aFrameOffset, return rect; } -nsIFrame* nsCaret::GetFrameAndOffset(const Selection* aSelection, - nsINode* aOverrideNode, - int32_t aOverrideOffset, - int32_t* aFrameOffset, - nsIFrame** aUnadjustedFrame) { - if (aUnadjustedFrame) { - *aUnadjustedFrame = nullptr; +auto nsCaret::CaretPositionFor(const Selection* aSelection) -> CaretPosition { + if (!aSelection) { + return {}; } - - nsINode* focusNode; - int32_t focusOffset; - - if (aOverrideNode) { - focusNode = aOverrideNode; - focusOffset = aOverrideOffset; - } else if (aSelection) { - focusNode = aSelection->GetFocusNode(); - focusOffset = aSelection->FocusOffset(); - } else { - return nullptr; + const nsFrameSelection* frameSelection = aSelection->GetFrameSelection(); + if (!frameSelection) { + return {}; } + nsINode* node = aSelection->GetFocusNode(); + if (!node) { + return {}; + } + return { + node, + int32_t(aSelection->FocusOffset()), + frameSelection->GetHint(), + frameSelection->GetCaretBidiLevel(), + }; +} - if (!focusNode || !focusNode->IsContent() || !aSelection) { - return nullptr; +CaretFrameData nsCaret::GetFrameAndOffset(const CaretPosition& aPosition) { + nsINode* focusNode = aPosition.mContent; + int32_t focusOffset = aPosition.mOffset; + + if (!focusNode || !focusNode->IsContent()) { + return {}; } nsIContent* contentNode = focusNode->AsContent(); - nsFrameSelection* frameSelection = aSelection->GetFrameSelection(); - BidiEmbeddingLevel bidiLevel = frameSelection->GetCaretBidiLevel(); - const CaretFrameData result = - SelectionMovementUtils::GetCaretFrameForNodeOffset( - frameSelection, contentNode, focusOffset, frameSelection->GetHint(), - bidiLevel, ForceEditableRegion::No); - // FIXME: It's odd to update nsFrameSelection within this method which is - // named as a getter. - if (result.mFrame) { - frameSelection->SetHint(result.mHint); - } - if (aUnadjustedFrame) { - *aUnadjustedFrame = result.mUnadjustedFrame; - } - if (aFrameOffset) { - *aFrameOffset = result.mOffsetInFrameContent; - } - return result.mFrame; + return SelectionMovementUtils::GetCaretFrameForNodeOffset( + nullptr, contentNode, focusOffset, aPosition.mHint, aPosition.mBidiLevel, + ForceEditableRegion::No); } /* static */ nsIFrame* nsCaret::GetGeometry(const Selection* aSelection, nsRect* aRect) { - int32_t frameOffset; - nsIFrame* frame = GetFrameAndOffset(aSelection, nullptr, 0, &frameOffset); - if (frame) { - *aRect = GetGeometryForFrame(frame, frameOffset, nullptr); + auto data = GetFrameAndOffset(CaretPositionFor(aSelection)); + if (data.mFrame) { + *aRect = + GetGeometryForFrame(data.mFrame, data.mOffsetInFrameContent, nullptr); } - return frame; + return data.mFrame; } [[nodiscard]] static nsIFrame* GetContainingBlockIfNeeded(nsIFrame* aFrame) { @@ -397,39 +384,56 @@ nsIFrame* nsCaret::GetGeometry(const Selection* aSelection, nsRect* aRect) { return aFrame->GetContainingBlock(); } -void nsCaret::SchedulePaint(Selection* aSelection) { - Selection* selection; - if (aSelection) { - selection = aSelection; - } else { - selection = GetSelection(); +void nsCaret::SchedulePaint() { + if (mLastPaintedFrame) { + mLastPaintedFrame->SchedulePaint(); + mLastPaintedFrame = nullptr; } - - int32_t frameOffset; - nsIFrame* frame = GetFrameAndOffset(selection, mOverrideContent, - mOverrideOffset, &frameOffset); - if (!frame) { + auto data = GetFrameAndOffset(mCaretPosition); + if (!data.mFrame) { return; } - + nsIFrame* frame = data.mFrame; if (nsIFrame* cb = GetContainingBlockIfNeeded(frame)) { - cb->SchedulePaint(); - } else { - frame->SchedulePaint(); + frame = cb; } + frame->SchedulePaint(); } void nsCaret::SetVisibilityDuringSelection(bool aVisibility) { + if (mShowDuringSelection == aVisibility) { + return; + } mShowDuringSelection = aVisibility; + if (mHiddenDuringSelection && aVisibility) { + RemoveForceHide(); + mHiddenDuringSelection = false; + } SchedulePaint(); } -void nsCaret::SetCaretPosition(nsINode* aNode, int32_t aOffset) { - mOverrideContent = aNode; - mOverrideOffset = aOffset; +void nsCaret::UpdateCaretPositionFromSelectionIfNeeded() { + if (mFixedCaretPosition) { + return; + } + CaretPosition newPos = CaretPositionFor(GetSelection()); + if (newPos == mCaretPosition) { + return; + } + mCaretPosition = newPos; + SchedulePaint(); +} +void nsCaret::SetCaretPosition(nsINode* aNode, int32_t aOffset) { + // Schedule a paint with the old position to invalidate. + mFixedCaretPosition = !!aNode; + if (mFixedCaretPosition) { + mCaretPosition = {aNode, aOffset}; + SchedulePaint(); + } else { + UpdateCaretPositionFromSelectionIfNeeded(); + } ResetBlinking(); - SchedulePaint(); } void nsCaret::CheckSelectionLanguageChange() { @@ -478,16 +482,15 @@ nsIFrame* nsCaret::GetPaintGeometry(nsRect* aCaretRect, nsRect* aHookRect, // taken into account when computing the caret position below. CheckSelectionLanguageChange(); - int32_t frameOffset; - nsIFrame* unadjustedFrame = nullptr; - nsIFrame* frame = - GetFrameAndOffset(GetSelection(), mOverrideContent, mOverrideOffset, - &frameOffset, &unadjustedFrame); - MOZ_ASSERT(!!frame == !!unadjustedFrame); - if (!frame) { + auto data = GetFrameAndOffset(mCaretPosition); + MOZ_ASSERT(!!data.mFrame == !!data.mUnadjustedFrame); + if (!data.mFrame) { return nullptr; } + nsIFrame* frame = data.mFrame; + nsIFrame* unadjustedFrame = data.mUnadjustedFrame; + int32_t frameOffset(data.mOffsetInFrameContent); // Now we have a frame, check whether it's appropriate to show the caret here. // Note we need to check the unadjusted frame, otherwise consider the // following case: @@ -555,26 +558,35 @@ void nsCaret::PaintCaret(DrawTarget& aDrawTarget, nsIFrame* aForFrame, NS_IMETHODIMP nsCaret::NotifySelectionChanged(Document*, Selection* aDomSel, int16_t aReason, int32_t aAmount) { - // Note that aDomSel, per the comment below may not be the same as our - // selection, but that's OK since if that is the case, it wouldn't have - // mattered what IsVisible() returns here, so we just opt for checking - // the selection later down below. - if ((aReason & nsISelectionListener::MOUSEUP_REASON) || - !IsVisible(aDomSel)) // this wont do - return NS_OK; - // The same caret is shared amongst the document and any text widgets it // may contain. This means that the caret could get notifications from // multiple selections. // // If this notification is for a selection that is not the one the - // the caret is currently interested in (mDomSelectionWeak), then there - // is nothing to do! + // the caret is currently interested in (mDomSelectionWeak), or the caret + // position is fixed, then there is nothing to do! + if (mDomSelectionWeak != aDomSel) { + return NS_OK; + } - if (mDomSelectionWeak != aDomSel) return NS_OK; + // Check if we need to hide / un-hide the caret due to the selection being + // collapsed. + if (!mShowDuringSelection && + !aDomSel->IsCollapsed() != mHiddenDuringSelection) { + if (mHiddenDuringSelection) { + RemoveForceHide(); + } else { + AddForceHide(); + } + mHiddenDuringSelection = !mHiddenDuringSelection; + } - ResetBlinking(); - SchedulePaint(aDomSel); + // We don't bother computing the caret position when invisible. We'll do it if + // we become visible in CaretVisibilityMaybeChanged(). + if (IsVisible()) { + UpdateCaretPositionFromSelectionIfNeeded(); + ResetBlinking(); + } return NS_OK; } @@ -589,7 +601,7 @@ void nsCaret::ResetBlinking() { mIsBlinkOn = true; - if (mReadOnly || !mVisible || mHideCount) { + if (mReadOnly || !IsVisible()) { StopBlinking(); return; } @@ -647,49 +659,6 @@ size_t nsCaret::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { return total; } -bool nsCaret::IsMenuPopupHidingCaret() { - // Check if there are open popups. - nsXULPopupManager* popMgr = nsXULPopupManager::GetInstance(); - nsTArray<nsIFrame*> popups; - popMgr->GetVisiblePopups(popups); - - if (popups.Length() == 0) - return false; // No popups, so caret can't be hidden by them. - - // Get the selection focus content, that's where the caret would - // go if it was drawn. - if (!mDomSelectionWeak) { - return true; // No selection/caret to draw. - } - nsCOMPtr<nsIContent> caretContent = - nsIContent::FromNodeOrNull(mDomSelectionWeak->GetFocusNode()); - if (!caretContent) return true; // No selection/caret to draw. - - // If there's a menu popup open before the popup with - // the caret, don't show the caret. - for (uint32_t i = 0; i < popups.Length(); i++) { - nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame*>(popups[i]); - nsIContent* popupContent = popupFrame->GetContent(); - - if (caretContent->IsInclusiveDescendantOf(popupContent)) { - // The caret is in this popup. There were no menu popups before this - // popup, so don't hide the caret. - return false; - } - - if (popupFrame->GetPopupType() == widget::PopupType::Menu && - !popupFrame->IsContextMenu()) { - // This is an open menu popup. It does not contain the caret (else we'd - // have returned above). Even if the caret is in a subsequent popup, - // or another document/frame, it should be hidden. - return true; - } - } - - // There are no open menu popups, no need to hide the caret. - return false; -} - void nsCaret::ComputeCaretRects(nsIFrame* aFrame, int32_t aFrameOffset, nsRect* aCaretRect, nsRect* aHookRect) { NS_ASSERTION(aFrame, "Should have a frame here"); diff --git a/layout/base/nsCaret.h b/layout/base/nsCaret.h index 43329c827f..cba719962c 100644 --- a/layout/base/nsCaret.h +++ b/layout/base/nsCaret.h @@ -10,10 +10,10 @@ #define nsCaret_h__ #include "mozilla/MemoryReporting.h" -#include "mozilla/dom/Selection.h" +#include "mozilla/SelectionMovementUtils.h" #include "nsCoord.h" +#include "nsIFrame.h" #include "nsISelectionListener.h" -#include "nsIWeakReferenceUtils.h" #include "nsPoint.h" #include "nsRect.h" @@ -46,10 +46,10 @@ class nsCaret final : public nsISelectionListener { using CaretAssociationHint = mozilla::CaretAssociationHint; - nsresult Init(mozilla::PresShell* aPresShell); + nsresult Init(mozilla::PresShell*); void Terminate(); - void SetSelection(mozilla::dom::Selection* aDOMSel); + void SetSelection(mozilla::dom::Selection*); mozilla::dom::Selection* GetSelection(); /** @@ -61,40 +61,18 @@ class nsCaret final : public nsISelectionListener { * those with user-modify: read-only */ void SetIgnoreUserModify(bool aIgnoreUserModify); - /** SetVisible will set the visibility of the caret - * @param inMakeVisible true to show the caret, false to hide it + /** + * SetVisible will set the visibility of the caret + * @param aVisible true to show the caret, false to hide it */ - void SetVisible(bool intMakeVisible); - /** IsVisible will get the visibility of the caret. - * This returns false if the caret is hidden because it was set - * to not be visible, or because the selection is not collapsed, or - * because an open popup is hiding the caret. - * It does not take account of blinking or the caret being hidden - * because we're in non-editable/disabled content. + void SetVisible(bool aVisible); + /** + * IsVisible will get the visibility of the caret. + * It does not take account of blinking or the caret being hidden because + * we're in non-editable/disabled content. */ - bool IsVisible(mozilla::dom::Selection* aSelection = nullptr) { - if (!mVisible || mHideCount) { - return false; - } - - if (!mShowDuringSelection) { - mozilla::dom::Selection* selection; - if (aSelection) { - selection = aSelection; - } else { - selection = GetSelection(); - } - if (!selection || !selection->IsCollapsed()) { - return false; - } - } - - if (IsMenuPopupHidingCaret()) { - return false; - } + bool IsVisible() const; - return true; - } /** * AddForceHide() increases mHideCount and hide the caret even if * SetVisible(true) has been or will be called. This is useful when the @@ -114,7 +92,7 @@ class nsCaret final : public nsISelectionListener { * @param inMakeReadonly true to show the caret in a 'read only' state, * false to show the caret in normal, editing state */ - void SetCaretReadOnly(bool inMakeReadonly); + void SetCaretReadOnly(bool aReadOnly); /** * @param aVisibility true if the caret should be visible even when the * selection is not collapsed. @@ -132,7 +110,10 @@ class nsCaret final : public nsISelectionListener { * Schedule a repaint for the frame where the caret would appear. * Does not check visibility etc. */ - void SchedulePaint(mozilla::dom::Selection* aSelection = nullptr); + void SchedulePaint(); + + nsIFrame* GetLastPaintedFrame() { return mLastPaintedFrame; } + void SetLastPaintedFrame(nsIFrame* aFrame) { mLastPaintedFrame = aFrame; } /** * Returns a frame to paint in, and the bounds of the painted caret @@ -142,6 +123,7 @@ class nsCaret final : public nsISelectionListener { * off). */ nsIFrame* GetPaintGeometry(nsRect* aRect); + /** * Same as the overload above, but returns the caret and hook rects * separately, and also computes the color if requested. @@ -165,6 +147,22 @@ class nsCaret final : public nsISelectionListener { // nsISelectionListener interface NS_DECL_NSISELECTIONLISTENER + /** The current caret position. */ + struct CaretPosition { + nsCOMPtr<nsINode> mContent; + int32_t mOffset = 0; + CaretAssociationHint mHint{0}; + mozilla::intl::BidiEmbeddingLevel mBidiLevel; + + bool operator==(const CaretPosition& aOther) const { + return mContent == aOther.mContent && mOffset == aOther.mOffset && + mHint == aOther.mHint && mBidiLevel == aOther.mBidiLevel; + } + explicit operator bool() const { return !!mContent; } + }; + + static CaretPosition CaretPositionFor(const mozilla::dom::Selection*); + /** * Gets the position and size of the caret that would be drawn for * the focus node/offset of aSelection (assuming it would be drawn, @@ -181,18 +179,9 @@ class nsCaret final : public nsISelectionListener { static nsRect GetGeometryForFrame(nsIFrame* aFrame, int32_t aFrameOffset, nscoord* aBidiIndicatorSize); - // Get the frame and frame offset based on the focus node and focus offset - // of aSelection. If aOverrideNode and aOverride are provided, use them - // instead. - // @param aFrameOffset return the frame offset if non-null. - // @param aUnadjustedFrame return the original frame that the selection is - // targeting, without any adjustment for painting. - // @return the frame of the focus node. - static nsIFrame* GetFrameAndOffset(const mozilla::dom::Selection* aSelection, - nsINode* aOverrideNode, - int32_t aOverrideOffset, - int32_t* aFrameOffset, - nsIFrame** aUnadjustedFrame = nullptr); + // Get the frame and frame offset based on aPosition. + static mozilla::CaretFrameData GetFrameAndOffset( + const CaretPosition& aPosition); size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; @@ -200,6 +189,7 @@ class nsCaret final : public nsISelectionListener { static void CaretBlinkCallback(nsITimer* aTimer, void* aClosure); void CheckSelectionLanguageChange(); + void CaretVisibilityMaybeChanged(); void ResetBlinking(); void StopBlinking(); @@ -213,72 +203,73 @@ class nsCaret final : public nsISelectionListener { void ComputeCaretRects(nsIFrame* aFrame, int32_t aFrameOffset, nsRect* aCaretRect, nsRect* aHookRect); - // Returns true if we should not draw the caret because of XUL menu popups. - // The caret should be hidden if: - // 1. An open popup contains the caret, but a menu popup exists before the - // caret-owning popup in the popup list (i.e. a menu is in front of the - // popup with the caret). If the menu itself contains the caret we don't - // hide it. - // 2. A menu popup is open, but there is no caret present in any popup. - // 3. The caret selection is empty. - bool IsMenuPopupHidingCaret(); + // If we're tracking the selection, this updates the caret position and + // invalidates paint as needed. + void UpdateCaretPositionFromSelectionIfNeeded(); nsWeakPtr mPresShell; mozilla::WeakPtr<mozilla::dom::Selection> mDomSelectionWeak; nsCOMPtr<nsITimer> mBlinkTimer; - /** - * The content to draw the caret at. If null, we use mDomSelectionWeak's - * focus node instead. - */ - nsCOMPtr<nsINode> mOverrideContent; - /** - * The character offset to draw the caret at. - * Ignored if mOverrideContent is null. - */ - int32_t mOverrideOffset; + CaretPosition mCaretPosition; + + // The last frame we painted the caret in. + WeakFrame mLastPaintedFrame; + /** * mBlinkCount is used to control the number of times to blink the caret * before stopping the blink. This is reset each time we reset the * blinking. */ - int32_t mBlinkCount; + int32_t mBlinkCount = -1; /** * mBlinkRate is the rate of the caret blinking the last time we read it. * It is used as a way to optimize whether we need to reset the blinking * timer. 0 or a negative value means no blinking. */ - int32_t mBlinkRate; + int32_t mBlinkRate = 0; /** * mHideCount is not 0, it means that somebody doesn't want the caret * to be visible. See AddForceHide() and RemoveForceHide(). */ - uint32_t mHideCount; + uint32_t mHideCount = 0; /** * mIsBlinkOn is true when we're in a blink cycle where the caret is on. */ - bool mIsBlinkOn; + bool mIsBlinkOn = false; /** * mIsVisible is true when SetVisible was last called with 'true'. */ - bool mVisible; + bool mVisible = false; /** * mReadOnly is true when the caret is set to "read only" mode (i.e., * it doesn't blink). */ - bool mReadOnly; + bool mReadOnly = false; /** * mShowDuringSelection is true when the caret should be shown even when * the selection is not collapsed. */ - bool mShowDuringSelection; + bool mShowDuringSelection = false; /** * mIgnoreUserModify is true when the caret should be shown even when * it's in non-user-modifiable content. */ - bool mIgnoreUserModify; + bool mIgnoreUserModify = true; + + /** + * If the caret position is fixed, it's been overridden externally and it + * will not track the selection. + */ + bool mFixedCaretPosition = false; + + /** + * If we're currently hiding the caret due to the selection not being + * collapsed. Can only be true if mShowDuringSelection is false. + */ + bool mHiddenDuringSelection = false; }; #endif // nsCaret_h__ diff --git a/layout/base/nsCounterManager.cpp b/layout/base/nsCounterManager.cpp index 7c2e9bb776..f2bbb5e50e 100644 --- a/layout/base/nsCounterManager.cpp +++ b/layout/base/nsCounterManager.cpp @@ -203,7 +203,11 @@ void nsCounterList::SetScope(nsCounterNode* aNode) { // the element itself, then we use that scope. // Otherwise, fall through to consider scopes created by siblings (and // their descendants) in reverse document order. - if (aNode->mType != nsCounterNode::USE && + // Do this only for the list-item counter, while the CSSWG discusses what the + // right thing to do here is, see bug 1548753 and + // https://github.com/w3c/csswg-drafts/issues/5477. + if (mCounterName == nsGkAtoms::list_item && + aNode->mType != nsCounterNode::USE && StaticPrefs::layout_css_counter_ancestor_scope_enabled()) { for (auto* p = aNode->mPseudoFrame; p; p = p->GetParent()) { // This relies on the fact that a RESET node is always the first diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index d1cf9bf237..bbd43a830e 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -449,6 +449,7 @@ class nsDocumentViewer final : public nsIDocumentViewer, #ifdef NS_PRINTING unsigned mClosingWhilePrinting : 1; + unsigned mCloseWindowAfterPrint : 1; # if NS_PRINT_PREVIEW RefPtr<nsPrintJob> mPrintJob; @@ -520,6 +521,7 @@ nsDocumentViewer::nsDocumentViewer() mInPermitUnloadPrompt(false), #ifdef NS_PRINTING mClosingWhilePrinting(false), + mCloseWindowAfterPrint(false), #endif // NS_PRINTING mReloadEncodingSource(kCharsetUninitialized), mReloadEncoding(nullptr), @@ -2424,7 +2426,7 @@ NS_IMETHODIMP nsDocumentViewer::CopyLinkLocation() { NS_ENSURE_SUCCESS(rv, rv); // copy the href onto the clipboard - return clipboard->CopyString(locationText); + return clipboard->CopyString(locationText, mDocument->GetWindowContext()); } NS_IMETHODIMP nsDocumentViewer::CopyImage(int32_t aCopyFlags) { @@ -2434,7 +2436,8 @@ NS_IMETHODIMP nsDocumentViewer::CopyImage(int32_t aCopyFlags) { NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); nsCOMPtr<nsILoadContext> loadContext(mContainer); - return nsCopySupport::ImageCopy(node, loadContext, aCopyFlags); + return nsCopySupport::ImageCopy(node, loadContext, aCopyFlags, + mDocument->GetWindowContext()); } NS_IMETHODIMP nsDocumentViewer::GetCopyable(bool* aCopyable) { @@ -3140,6 +3143,20 @@ nsDocumentViewer::GetDoingPrintPreview(bool* aDoingPrintPreview) { } NS_IMETHODIMP +nsDocumentViewer::GetCloseWindowAfterPrint(bool* aCloseWindowAfterPrint) { + NS_ENSURE_ARG_POINTER(aCloseWindowAfterPrint); + + *aCloseWindowAfterPrint = mCloseWindowAfterPrint; + return NS_OK; +} + +NS_IMETHODIMP +nsDocumentViewer::SetCloseWindowAfterPrint(bool aCloseWindowAfterPrint) { + mCloseWindowAfterPrint = aCloseWindowAfterPrint; + return NS_OK; +} + +NS_IMETHODIMP nsDocumentViewer::ExitPrintPreview() { NS_ENSURE_TRUE(mPrintJob, NS_ERROR_FAILURE); @@ -3301,15 +3318,23 @@ void nsDocumentViewer::OnDonePrinting() { printJob->Destroy(); } - // We are done printing, now clean up. - // - // For non-print-preview jobs, we are actually responsible for cleaning up - // our whole <browser> or window (see the OPEN_PRINT_BROWSER code), so gotta - // run window.close(), which will take care of this. - // - // For print preview jobs the front-end code is responsible for cleaning the - // UI. - if (!printJob->CreatedForPrintPreview()) { +// We are done printing, now clean up. +// +// If the original document to print was not a static clone, we opened a new +// window and are responsible for cleaning up the whole <browser> or window +// (see the OPEN_PRINT_BROWSER code, specifically +// handleStaticCloneCreatedForPrint()), so gotta run window.close(), which +// will take care of this. +// +// Otherwise the front-end code is responsible for cleaning the UI. +# ifdef ANDROID + // Android doesn't support Content Analysis and prints in a different way, + // so use different logic to clean up. + bool closeWindowAfterPrint = !printJob->CreatedForPrintPreview(); +# else + bool closeWindowAfterPrint = GetCloseWindowAfterPrint(); +# endif + if (closeWindowAfterPrint) { if (mContainer) { if (nsCOMPtr<nsPIDOMWindowOuter> win = mContainer->GetWindow()) { win->Close(); diff --git a/layout/base/nsGenConList.cpp b/layout/base/nsGenConList.cpp index b770ee539a..175e6fe7d5 100644 --- a/layout/base/nsGenConList.cpp +++ b/layout/base/nsGenConList.cpp @@ -12,12 +12,13 @@ #include "nsIFrame.h" void nsGenConNode::CheckFrameAssertions() { - NS_ASSERTION( - mContentIndex < int32_t(mPseudoFrame->StyleContent()->ContentCount()) || - // Special-case for the USE node created for the legacy markers, - // which don't use the content property. - mContentIndex == 0, - "index out of range"); + NS_ASSERTION(mContentIndex < int32_t(mPseudoFrame->StyleContent() + ->NonAltContentItems() + .Length()) || + // Special-case for the USE node created for the legacy + // markers, which don't use the content property. + mContentIndex == 0, + "index out of range"); // We allow negative values of mContentIndex for 'counter-reset' and // 'counter-increment'. diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index db766f6603..3f84a97250 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -714,12 +714,9 @@ bool nsLayoutUtils::AllowZoomingForDocument( return false; } // True if we allow zooming for all documents on this platform, or if we are - // in RDM and handling meta viewports, which force zoom under some - // circumstances. - BrowsingContext* bc = aDocument ? aDocument->GetBrowsingContext() : nullptr; - return StaticPrefs::apz_allow_zooming() || - (bc && bc->InRDMPane() && - nsLayoutUtils::ShouldHandleMetaViewport(aDocument)); + // in RDM. + BrowsingContext* bc = aDocument->GetBrowsingContext(); + return StaticPrefs::apz_allow_zooming() || (bc && bc->InRDMPane()); } static bool HasVisibleAnonymousContents(Document* aDoc) { @@ -903,7 +900,7 @@ void nsLayoutUtils::GetMarkerSpokenText(const nsIContent* aContent, return; } - if (frame->StyleContent()->ContentCount() > 0) { + if (!frame->StyleContent()->NonAltContentItems().IsEmpty()) { for (nsIFrame* child : frame->PrincipalChildList()) { nsIFrame::RenderedText text = child->GetRenderedText(); aText += text.mString; @@ -2134,6 +2131,7 @@ const nsIFrame* nsLayoutUtils::FindNearestCommonAncestorFrameWithinBlock( bool nsLayoutUtils::AuthorSpecifiedBorderBackgroundDisablesTheming( StyleAppearance aAppearance) { return aAppearance == StyleAppearance::NumberInput || + aAppearance == StyleAppearance::PasswordInput || aAppearance == StyleAppearance::Button || aAppearance == StyleAppearance::Textfield || aAppearance == StyleAppearance::Textarea || @@ -3501,7 +3499,7 @@ nsIFrame* nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame* aFrame) { struct BoxToRect : public nsLayoutUtils::BoxCallback { const nsIFrame* mRelativeTo; RectCallback* mCallback; - uint32_t mFlags; + nsLayoutUtils::GetAllInFlowRectsFlags mFlags; // If the frame we're measuring relative to is the root, we know all frames // are descendants of it, so we don't need to compute the common ancestor // between a frame and mRelativeTo. @@ -3513,7 +3511,8 @@ struct BoxToRect : public nsLayoutUtils::BoxCallback { bool mRelativeToIsTarget; BoxToRect(const nsIFrame* aTargetFrame, const nsIFrame* aRelativeTo, - RectCallback* aCallback, uint32_t aFlags) + RectCallback* aCallback, + nsLayoutUtils::GetAllInFlowRectsFlags aFlags) : mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags), @@ -3526,21 +3525,34 @@ struct BoxToRect : public nsLayoutUtils::BoxCallback { const bool usingSVGOuterFrame = !!outer; if (!outer) { outer = aFrame; - switch (mFlags & nsLayoutUtils::RECTS_WHICH_BOX_MASK) { - case nsLayoutUtils::RECTS_USE_CONTENT_BOX: - r = aFrame->GetContentRectRelativeToSelf(); - break; - case nsLayoutUtils::RECTS_USE_PADDING_BOX: - r = aFrame->GetPaddingRectRelativeToSelf(); - break; - case nsLayoutUtils::RECTS_USE_MARGIN_BOX: - r = aFrame->GetMarginRectRelativeToSelf(); - break; - default: // Use the border box - r = aFrame->GetRectRelativeToSelf(); + if (mFlags.contains( + nsLayoutUtils::GetAllInFlowRectsFlag::UseContentBox)) { + r = aFrame->GetContentRectRelativeToSelf(); + } else if (mFlags.contains( + nsLayoutUtils::GetAllInFlowRectsFlag::UsePaddingBox)) { + r = aFrame->GetPaddingRectRelativeToSelf(); + } else if (mFlags.contains( + nsLayoutUtils::GetAllInFlowRectsFlag::UseMarginBox)) { + r = aFrame->GetMarginRectRelativeToSelf(); + } else if (mFlags.contains(nsLayoutUtils::GetAllInFlowRectsFlag:: + UseMarginBoxWithAutoResolvedAsZero)) { + r = aFrame->GetRectRelativeToSelf(); + const auto& styleMargin = aFrame->StyleMargin()->mMargin; + nsMargin usedMargin = + aFrame->GetUsedMargin().ApplySkipSides(aFrame->GetSkipSides()); + for (const Side side : AllPhysicalSides()) { + if (styleMargin.Get(side).IsAuto()) { + usedMargin.Side(side) = 0; + } + } + r.Inflate(usedMargin); + } else { + // Use the border-box. + r = aFrame->GetRectRelativeToSelf(); } } - if (mFlags & nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS) { + if (mFlags.contains( + nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms)) { const bool isAncestorKnown = [&] { if (mRelativeToIsRoot) { return true; @@ -3571,7 +3583,7 @@ struct MOZ_RAII BoxToRectAndText : public BoxToRect { BoxToRectAndText(const nsIFrame* aTargetFrame, const nsIFrame* aRelativeTo, RectCallback* aCallback, Sequence<nsString>* aTextList, - uint32_t aFlags) + nsLayoutUtils::GetAllInFlowRectsFlags aFlags) : BoxToRect(aTargetFrame, aRelativeTo, aCallback, aFlags), mTextList(aTextList) {} @@ -3612,7 +3624,7 @@ struct MOZ_RAII BoxToRectAndText : public BoxToRect { void nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, const nsIFrame* aRelativeTo, RectCallback* aCallback, - uint32_t aFlags) { + GetAllInFlowRectsFlags aFlags) { BoxToRect converter(aFrame, aRelativeTo, aCallback, aFlags); GetAllInFlowBoxes(aFrame, &converter); } @@ -3621,7 +3633,7 @@ void nsLayoutUtils::GetAllInFlowRectsAndTexts(nsIFrame* aFrame, const nsIFrame* aRelativeTo, RectCallback* aCallback, Sequence<nsString>* aTextList, - uint32_t aFlags) { + GetAllInFlowRectsFlags aFlags) { BoxToRectAndText converter(aFrame, aRelativeTo, aCallback, aTextList, aFlags); GetAllInFlowBoxes(aFrame, &converter); } @@ -3652,7 +3664,7 @@ nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame) { nsRect nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, const nsIFrame* aRelativeTo, - uint32_t aFlags) { + GetAllInFlowRectsFlags aFlags) { RectAccumulator accumulator; GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags); return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect @@ -4613,7 +4625,7 @@ static nscoord AddIntrinsicSizeOffset( LayoutDeviceIntSize devSize = pc->Theme()->GetMinimumWidgetSize( pc, aFrame, disp->EffectiveAppearance()); nscoord themeSize = pc->DevPixelsToAppUnits( - aAxis == eAxisVertical ? devSize.height : devSize.width); + aAxis == PhysicalAxis::Vertical ? devSize.height : devSize.width); // GetMinimumWidgetSize() returns a border-box width. themeSize += aOffsets.margin; if (themeSize > result) { @@ -4644,7 +4656,7 @@ nscoord nsLayoutUtils::IntrinsicForAxis( aPercentageBasis.isSome(), "grid layout should always pass a percentage basis"); - const bool horizontalAxis = MOZ_LIKELY(aAxis == eAxisHorizontal); + const bool horizontalAxis = MOZ_LIKELY(aAxis == PhysicalAxis::Horizontal); #ifdef DEBUG_INTRINSIC_WIDTH nsIFrame::IndentBy(stderr, gNoiseIndent); aFrame->ListTag(stderr); @@ -4676,7 +4688,7 @@ nscoord nsLayoutUtils::IntrinsicForAxis( horizontalAxis ? stylePos->mMaxWidth : stylePos->mMaxHeight; PhysicalAxis ourInlineAxis = - aFrame->GetWritingMode().PhysicalAxis(eLogicalAxisInline); + aFrame->GetWritingMode().PhysicalAxis(LogicalAxis::Inline); const bool isInlineAxis = aAxis == ourInlineAxis; auto resetIfKeywords = [](StyleSize& aSize, StyleSize& aMinSize, @@ -4897,8 +4909,8 @@ nscoord nsLayoutUtils::IntrinsicForAxis( // We are computing the size of |aFrame|, so we use the inline & block // dimensions of |aFrame|. result = ratio.ComputeRatioDependentSize( - isInlineAxis ? eLogicalAxisInline : eLogicalAxisBlock, childWM, h, - *contentBoxSizeToBoxSizingAdjust); + isInlineAxis ? LogicalAxis::Inline : LogicalAxis::Block, childWM, + h, *contentBoxSizeToBoxSizingAdjust); // We have get the inlineSizeForAspectRatio value, so we don't have to // recompute this again below. inlineSizeFromAspectRatio.emplace(result); @@ -4910,8 +4922,8 @@ nscoord nsLayoutUtils::IntrinsicForAxis( GetPercentBSize(styleMaxBSize, aFrame, horizontalAxis, h))) { h = std::max(0, h - bSizeTakenByBoxSizing); nscoord maxISize = ratio.ComputeRatioDependentSize( - isInlineAxis ? eLogicalAxisInline : eLogicalAxisBlock, childWM, h, - *contentBoxSizeToBoxSizingAdjust); + isInlineAxis ? LogicalAxis::Inline : LogicalAxis::Block, childWM, + h, *contentBoxSizeToBoxSizingAdjust); if (maxISize < result) { result = maxISize; } @@ -4926,8 +4938,8 @@ nscoord nsLayoutUtils::IntrinsicForAxis( GetPercentBSize(styleMinBSize, aFrame, horizontalAxis, h))) { h = std::max(0, h - bSizeTakenByBoxSizing); nscoord minISize = ratio.ComputeRatioDependentSize( - isInlineAxis ? eLogicalAxisInline : eLogicalAxisBlock, childWM, h, - *contentBoxSizeToBoxSizingAdjust); + isInlineAxis ? LogicalAxis::Inline : LogicalAxis::Block, childWM, + h, *contentBoxSizeToBoxSizingAdjust); if (minISize > result) { result = minISize; } @@ -4990,9 +5002,9 @@ nscoord nsLayoutUtils::IntrinsicForAxis( GetDefiniteSizeTakenByBoxSizing(boxSizingForAR, aFrame, !isInlineAxis, ignorePadding, aPercentageBasis); bSize -= bSizeTakenByBoxSizing; - inlineSizeFromAspectRatio.emplace(ar.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, childWM, bSize, - *contentBoxSizeToBoxSizingAdjust)); + inlineSizeFromAspectRatio.emplace( + ar.ComputeRatioDependentSize(LogicalAxis::Inline, childWM, bSize, + *contentBoxSizeToBoxSizingAdjust)); } } @@ -5027,7 +5039,7 @@ nscoord nsLayoutUtils::IntrinsicForContainer(gfxContext* aRenderingContext, MOZ_ASSERT(aFrame && aFrame->GetParent()); // We want the size aFrame will contribute to its parent's inline-size. PhysicalAxis axis = - aFrame->GetParent()->GetWritingMode().PhysicalAxis(eLogicalAxisInline); + aFrame->GetParent()->GetWritingMode().PhysicalAxis(LogicalAxis::Inline); return IntrinsicForAxis(axis, aRenderingContext, aFrame, aType, Nothing(), aFlags); } @@ -5051,12 +5063,13 @@ nscoord nsLayoutUtils::MinSizeContributionForAxis( // Note: this method is only meant for grid/flex items. const nsStylePosition* const stylePos = aFrame->StylePosition(); - StyleSize size = - aAxis == eAxisHorizontal ? stylePos->mMinWidth : stylePos->mMinHeight; - StyleMaxSize maxSize = - aAxis == eAxisHorizontal ? stylePos->mMaxWidth : stylePos->mMaxHeight; + StyleSize size = aAxis == PhysicalAxis::Horizontal ? stylePos->mMinWidth + : stylePos->mMinHeight; + StyleMaxSize maxSize = aAxis == PhysicalAxis::Horizontal + ? stylePos->mMaxWidth + : stylePos->mMaxHeight; auto childWM = aFrame->GetWritingMode(); - PhysicalAxis ourInlineAxis = childWM.PhysicalAxis(eLogicalAxisInline); + PhysicalAxis ourInlineAxis = childWM.PhysicalAxis(LogicalAxis::Inline); // According to the spec, max-content and min-content should behave as the // property's initial values in block axis. // It also make senses to use the initial values for -moz-fit-content and @@ -5080,7 +5093,8 @@ nscoord nsLayoutUtils::MinSizeContributionForAxis( minSize = 0; fixedMinSize = &minSize; } else { - size = aAxis == eAxisHorizontal ? stylePos->mWidth : stylePos->mHeight; + size = aAxis == PhysicalAxis::Horizontal ? stylePos->mWidth + : stylePos->mHeight; // This is same as above: keywords should behaves as property's initial // values in block axis. if (aAxis != ourInlineAxis && size.BehavesLikeInitialValueOnBlockAxis()) { @@ -5323,7 +5337,6 @@ nscoord nsLayoutUtils::MinISizeFromInline(nsIFrame* aFrame, "should not be container for font size inflation"); nsIFrame::InlineMinISizeData data; - DISPLAY_MIN_INLINE_SIZE(aFrame, data.mPrevLines); aFrame->AddInlineMinISize(aRenderingContext, &data); data.ForceBreak(); return data.mPrevLines; @@ -5336,7 +5349,6 @@ nscoord nsLayoutUtils::PrefISizeFromInline(nsIFrame* aFrame, "should not be container for font size inflation"); nsIFrame::InlinePrefISizeData data; - DISPLAY_PREF_INLINE_SIZE(aFrame, data.mPrevLines); aFrame->AddInlinePrefISize(aRenderingContext, &data); data.ForceBreak(); return data.mPrevLines; @@ -7449,7 +7461,7 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement( SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement( HTMLVideoElement* aElement, uint32_t aSurfaceFlags, - RefPtr<DrawTarget>& aTarget) { + RefPtr<DrawTarget>& aTarget, bool aOptimizeSourceSurface) { SurfaceFromElementResult result; result.mAlphaType = gfxAlphaType::Opaque; // Assume opaque. @@ -7484,7 +7496,7 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement( result.mIsWriteOnly = CanvasUtils::CheckWriteOnlySecurity( result.mCORSUsed, result.mPrincipal, result.mHadCrossOriginRedirects); - if (aTarget) { + if (aTarget && aOptimizeSourceSurface) { // They gave us a DrawTarget to optimize for, so even though we have a // layers::Image, we should unconditionally try to grab a SourceSurface and // try to optimize it. @@ -8901,13 +8913,7 @@ ScrollMetadata nsLayoutUtils::ComputeScrollMetadata( } if (primaryFrame) { WritingMode writingModeOfRootScrollFrame = primaryFrame->GetWritingMode(); - WritingMode::BlockDir blockDirOfRootScrollFrame = - writingModeOfRootScrollFrame.GetBlockDir(); - WritingMode::InlineDir inlineDirOfRootScrollFrame = - writingModeOfRootScrollFrame.GetInlineDir(); - if (blockDirOfRootScrollFrame == WritingMode::BlockDir::eBlockRL || - (blockDirOfRootScrollFrame == WritingMode::BlockDir::eBlockTB && - inlineDirOfRootScrollFrame == WritingMode::InlineDir::eInlineRTL)) { + if (writingModeOfRootScrollFrame.IsPhysicalRTL()) { metadata.SetIsAutoDirRootContentRTL(true); } } @@ -9228,7 +9234,8 @@ CSSRect nsLayoutUtils::GetBoundingFrameRect( CSSRect result; nsIFrame* relativeTo = aRootScrollFrame->GetScrolledFrame(); result = CSSRect::FromAppUnits(nsLayoutUtils::GetAllInFlowRectsUnion( - aFrame, relativeTo, nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS)); + aFrame, relativeTo, + nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms)); // If the element is contained in a scrollable frame that is not // the root scroll frame, make sure to clip the result so that it is @@ -9769,24 +9776,8 @@ void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont, /* static */ bool nsLayoutUtils::ShouldHandleMetaViewport(const Document* aDocument) { - auto metaViewportOverride = nsIDocShell::META_VIEWPORT_OVERRIDE_NONE; - if (aDocument) { - if (nsIDocShell* docShell = aDocument->GetDocShell()) { - metaViewportOverride = docShell->GetMetaViewportOverride(); - } - } - switch (metaViewportOverride) { - case nsIDocShell::META_VIEWPORT_OVERRIDE_ENABLED: - return true; - case nsIDocShell::META_VIEWPORT_OVERRIDE_DISABLED: - return false; - default: - MOZ_ASSERT(metaViewportOverride == - nsIDocShell::META_VIEWPORT_OVERRIDE_NONE); - // The META_VIEWPORT_OVERRIDE_NONE case means that there is no override - // and we rely solely on the StaticPrefs. - return StaticPrefs::dom_meta_viewport_enabled(); - } + BrowsingContext* bc = aDocument->GetBrowsingContext(); + return StaticPrefs::dom_meta_viewport_enabled() || (bc && bc->InRDMPane()); } /* static */ diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 697b139ed9..6b6e45b3e4 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -1241,52 +1241,57 @@ class nsLayoutUtils { static nsIFrame* GetContainingBlockForClientRect(nsIFrame* aFrame); - enum { - RECTS_ACCOUNT_FOR_TRANSFORMS = 0x01, - // Two bits for specifying which box type to use. - // With neither bit set (default), use the border box. - RECTS_USE_CONTENT_BOX = 0x02, - RECTS_USE_PADDING_BOX = 0x04, - RECTS_USE_MARGIN_BOX = 0x06, // both bits set - RECTS_WHICH_BOX_MASK = 0x06 // bitmask for these two bits - }; /** * Collect all CSS boxes (content, padding, border, or margin) associated * with aFrame and its continuations, "drilling down" through table wrapper * frames and some anonymous blocks since they're not real CSS boxes. + * * The boxes are positioned relative to aRelativeTo (taking scrolling * into account) and passed to the callback in frame-tree order. * If aFrame is null, no boxes are returned. + * * For SVG frames, returns one rectangle, the bounding box. - * If aFlags includes RECTS_ACCOUNT_FOR_TRANSFORMS, then when converting - * the boxes into aRelativeTo coordinates, transforms (including CSS - * and SVG transforms) are taken into account. - * If aFlags includes one of RECTS_USE_CONTENT_BOX, RECTS_USE_PADDING_BOX, - * or RECTS_USE_MARGIN_BOX, the corresponding type of box is used. - * Otherwise (by default), the border box is used. - */ + * + * If aFlags includes 'AccountForTransforms', then when converting the boxes + * into aRelativeTo coordinates, transforms (including CSS and SVG transforms) + * are taken into account. + * + * If aFlags includes one of 'UseContentBox', 'UsePaddingBox', 'UseMarginBox', + * or 'UseMarginBoxWithAutoResolvedAsZero', the corresponding type of box is + * used. Otherwise (by default), the border box is used. Note that these "Box" + * flags are meant to be mutually exclusive, though we don't enforce that. If + * multiple "Box" flags are used, we'll gracefully just use the first one in + * the order of the enum. + */ + enum class GetAllInFlowRectsFlag : uint8_t { + AccountForTransforms, + UseContentBox, + UsePaddingBox, + UseMarginBox, + // Similar to UseMarginBox, but the 'auto' margins are resolved as zero. + UseMarginBoxWithAutoResolvedAsZero, + }; + using GetAllInFlowRectsFlags = mozilla::EnumSet<GetAllInFlowRectsFlag>; static void GetAllInFlowRects(nsIFrame* aFrame, const nsIFrame* aRelativeTo, mozilla::RectCallback* aCallback, - uint32_t aFlags = 0); + GetAllInFlowRectsFlags aFlags = {}); static void GetAllInFlowRectsAndTexts( nsIFrame* aFrame, const nsIFrame* aRelativeTo, mozilla::RectCallback* aCallback, - mozilla::dom::Sequence<nsString>* aTextList, uint32_t aFlags = 0); + mozilla::dom::Sequence<nsString>* aTextList, + GetAllInFlowRectsFlags aFlags = {}); /** * Computes the union of all rects returned by GetAllInFlowRects. If * the union is empty, returns the first rect. - * If aFlags includes RECTS_ACCOUNT_FOR_TRANSFORMS, then when converting - * the boxes into aRelativeTo coordinates, transforms (including CSS - * and SVG transforms) are taken into account. - * If aFlags includes one of RECTS_USE_CONTENT_BOX, RECTS_USE_PADDING_BOX, - * or RECTS_USE_MARGIN_BOX, the corresponding type of box is used. - * Otherwise (by default), the border box is used. + * + * See GetAllInFlowRects() documentation for the meaning of aRelativeTo and + * aFlags. */ static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, const nsIFrame* aRelativeTo, - uint32_t aFlags = 0); + GetAllInFlowRectsFlags aFlags = {}); enum { EXCLUDE_BLUR_SHADOWS = 0x01 }; /** @@ -2230,7 +2235,7 @@ class nsLayoutUtils { } static mozilla::SurfaceFromElementResult SurfaceFromElement( mozilla::dom::HTMLVideoElement* aElement, uint32_t aSurfaceFlags, - RefPtr<DrawTarget>& aTarget); + RefPtr<DrawTarget>& aTarget, bool aOptimizeSourceSurface = true); /** * When the document is editable by contenteditable attribute of its root diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 7d9515c495..0786ec25d9 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -336,6 +336,7 @@ static const char* gExactCallbackPrefs[] = { "intl.accept_languages", "layout.css.devPixelsPerPx", "layout.css.dpi", + "layout.css.letter-spacing.model", "layout.css.text-transform.uppercase-eszett.enabled", "privacy.trackingprotection.enabled", "ui.use_standins_for_native_colors", @@ -608,7 +609,8 @@ void nsPresContext::PreferenceChanged(const char* aPrefName) { } if (prefName.EqualsLiteral( - "layout.css.text-transform.uppercase-eszett.enabled")) { + "layout.css.text-transform.uppercase-eszett.enabled") || + prefName.EqualsLiteral("layout.css.letter-spacing.model")) { changeHint |= NS_STYLE_HINT_REFLOW; } @@ -1410,6 +1412,11 @@ void nsPresContext::SetInRDMPane(bool aInRDMPane) { } mInRDMPane = aInRDMPane; RecomputeTheme(); + if (mPresShell) { + nsContentUtils::AddScriptRunner(NewRunnableMethod<bool>( + "PresShell::MaybeRecreateMobileViewportManager", mPresShell, + &PresShell::MaybeRecreateMobileViewportManager, true)); + } } float nsPresContext::GetDeviceFullZoom() { diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index a5c2b1ded9..218d158a47 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -831,11 +831,11 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer { const TimeDuration previousRate = mVsyncRate; const TimeDuration rate = GetTimerRate(); - hal::PerformanceHintSession* const performanceHintSession = - GetPerformanceHintSession(); - if (performanceHintSession && rate != previousRate) { - performanceHintSession->UpdateTargetWorkDuration( - ContentChild::GetPerformanceHintTarget(rate)); + if (rate != previousRate) { + if (auto* const performanceHintSession = GetPerformanceHintSession()) { + performanceHintSession->UpdateTargetWorkDuration( + ContentChild::GetPerformanceHintTarget(rate)); + } } if (TimeDuration::FromMilliseconds(nsRefreshDriver::DefaultInterval()) > @@ -862,7 +862,7 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer { TimeStamp tickEnd = TimeStamp::Now(); - if (performanceHintSession) { + if (auto* const performanceHintSession = GetPerformanceHintSession()) { performanceHintSession->ReportActualWorkDuration(tickEnd - tickStart); } @@ -1528,6 +1528,16 @@ void nsRefreshDriver::PostScrollEvent(mozilla::Runnable* aScrollEvent, } } +void nsRefreshDriver::PostScrollEndEvent(mozilla::Runnable* aScrollEndEvent, + bool aDelayed) { + if (aDelayed) { + mDelayedScrollEndEvents.AppendElement(aScrollEndEvent); + } else { + mScrollEndEvents.AppendElement(aScrollEndEvent); + EnsureTimerStarted(); + } +} + void nsRefreshDriver::DispatchScrollEvents() { // Scroll events are one-shot, so after running them we can drop them. // However, dispatching a scroll event can potentially cause more scroll @@ -1539,6 +1549,13 @@ void nsRefreshDriver::DispatchScrollEvents() { } } +void nsRefreshDriver::DispatchScrollEndEvents() { + ScrollEventArray events = std::move(mScrollEndEvents); + for (auto& event : events) { + event->Run(); + } +} + void nsRefreshDriver::PostVisualViewportScrollEvent( VVPScrollEvent* aScrollEvent) { mVisualViewportScrollEvents.AppendElement(aScrollEvent); @@ -1673,6 +1690,9 @@ void nsRefreshDriver::RunDelayedEventsSoon() { mScrollEvents.AppendElements(mDelayedScrollEvents); mDelayedScrollEvents.Clear(); + mScrollEndEvents.AppendElements(mDelayedScrollEvents); + mDelayedScrollEndEvents.Clear(); + mResizeEventFlushObservers.AppendElements(mDelayedResizeEventFlushObservers); mDelayedResizeEventFlushObservers.Clear(); @@ -2008,7 +2028,7 @@ auto nsRefreshDriver::GetReasonsToTick() const -> TickReasons { if (!mVisualViewportResizeEvents.IsEmpty()) { reasons |= TickReasons::eHasVisualViewportResizeEvents; } - if (!mScrollEvents.IsEmpty()) { + if (!mScrollEvents.IsEmpty() || !mScrollEndEvents.IsEmpty()) { reasons |= TickReasons::eHasScrollEvents; } if (!mVisualViewportScrollEvents.IsEmpty()) { @@ -2208,23 +2228,7 @@ void nsRefreshDriver::RunFullscreenSteps() { void nsRefreshDriver::UpdateIntersectionObservations(TimeStamp aNowTime) { AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Compute intersections", LAYOUT); - - AutoTArray<RefPtr<Document>, 32> documents; - - if (mPresContext->Document()->HasIntersectionObservers()) { - documents.AppendElement(mPresContext->Document()); - } - - mPresContext->Document()->CollectDescendantDocuments( - documents, [](const Document* document) -> bool { - return document->HasIntersectionObservers(); - }); - - for (const auto& doc : documents) { - doc->UpdateIntersectionObservations(aNowTime); - doc->ScheduleIntersectionObserverNotification(); - } - + mPresContext->Document()->UpdateIntersections(aNowTime); mNeedToUpdateIntersectionObservations = false; } @@ -2476,6 +2480,7 @@ bool nsRefreshDriver::TickObserverArray(uint32_t aIdx, TimeStamp aNowTime) { FlushAutoFocusDocuments(); DispatchScrollEvents(); DispatchVisualViewportScrollEvents(); + DispatchScrollEndEvents(); EvaluateMediaQueriesAndReportChanges(); DispatchAnimationEvents(); RunFullscreenSteps(); diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index cd050f7431..7bd0f883f4 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -114,7 +114,10 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, void DispatchVisualViewportResizeEvents(); void PostScrollEvent(mozilla::Runnable* aScrollEvent, bool aDelayed = false); + void PostScrollEndEvent(mozilla::Runnable* aScrollEndEvent, + bool aDelayed = false); void DispatchScrollEvents(); + void DispatchScrollEndEvents(); void PostVisualViewportScrollEvent(VVPScrollEvent* aScrollEvent); void DispatchVisualViewportScrollEvents(); @@ -694,10 +697,12 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners; VisualViewportResizeEventArray mVisualViewportResizeEvents; ScrollEventArray mScrollEvents; + ScrollEventArray mScrollEndEvents; VisualViewportScrollEventArray mVisualViewportScrollEvents; // Scroll events on documents that might have events suppressed. ScrollEventArray mDelayedScrollEvents; + ScrollEventArray mDelayedScrollEndEvents; AutoTArray<mozilla::PresShell*, 16> mResizeEventFlushObservers; AutoTArray<mozilla::PresShell*, 16> mDelayedResizeEventFlushObservers; diff --git a/layout/base/tests/browser.toml b/layout/base/tests/browser.toml index a5b279145f..cc8df52306 100644 --- a/layout/base/tests/browser.toml +++ b/layout/base/tests/browser.toml @@ -3,6 +3,12 @@ prefs = [ "layout.css.properties-and-values.enabled=true", ] +["browser_animatedImageLeak.js"] +skip-if = ["!debug"] +support-files = [ + "helper_animatedImageLeak.html" +] + ["browser_bug617076.js"] ["browser_bug1701027-1.js"] diff --git a/layout/base/tests/browser_animatedImageLeak.js b/layout/base/tests/browser_animatedImageLeak.js new file mode 100644 index 0000000000..2c34ed9d89 --- /dev/null +++ b/layout/base/tests/browser_animatedImageLeak.js @@ -0,0 +1,226 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +requestLongerTimeout(4); + +/* + * This tests that when we have an animated image in a minimized window we + * don't leak. + * We've encountered this bug in 3 different ways: + * -bug 1830753 - images in top level chrome processes + * (we avoid processing them due to their CompositorBridgeChild being paused) + * -bug 1839109 - images in content processes + * (we avoid processing them due to their refresh driver being throttled, this + * would also fix the above case) + * -bug 1875100 - images that are in a content iframe that is not the content + * of a tab, so something like an extension iframe in the sidebar + * (this was fixed by making the content of a tab declare that it manually + * manages its activeness and having all other iframes inherit their + * activeness from their parent) + * In order to hit this bug we require + * -the same image to be in a minimized window and in a non-mininmized window + * so that the image is animated. + * -the animated image to go over the + * image.animated.decode-on-demand.threshold-kb threshold so that we do not + * keep all of its frames around (if we keep all its frame around then we + * don't try to keep allocating frames and not freeing the old ones) + * -it has to be the same Image object in memory, not just the same uri + * Then the visible copy of the image keeps generating new frames, those frames + * get sent to the minimized copies of the image but they never get processed + * or marked displayed so they can never be freed/reused. + * + * Note that due to bug 1889840, in order to test this we can't use an image + * loaded at the top level (see the last point above). We must use an html page + * that contains the image. + */ + +// this test is based in part on https://searchfox.org/mozilla-central/rev/c09764753ea40725eb50decad2c51edecbd33308/browser/components/extensions/test/browser/browser_ext_sidebarAction.js + +async function pushPrefs1() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["image.animated.decode-on-demand.threshold-kb", 1], + ["image.animated.decode-on-demand.batch-size", 2], + ], + }); +} + +async function openWindowsAndMinimize(taskToPerformBeforeMinimize) { + let wins = [null, null, null, null]; + for (let i = 0; i < wins.length; i++) { + let win = await BrowserTestUtils.openNewBrowserWindow(); + await win.delayedStartupPromise; + await taskToPerformBeforeMinimize(win); + + // Leave the last window un-minimized. + if (i < wins.length - 1) { + let promiseSizeModeChange = BrowserTestUtils.waitForEvent( + win, + "sizemodechange" + ); + win.minimize(); + await promiseSizeModeChange; + } + + wins[i] = win; + } + return wins; +} + +async function pushPrefs2() { + // wait so that at least one frame of the animation has been shown while the + // below pref is not set so that the counter gets reset. + await new Promise(resolve => setTimeout(resolve, 500)); + + await SpecialPowers.pushPrefEnv({ + set: [["gfx.testing.assert-render-textures-increase", 75]], + }); +} + +async function waitForEnoughFrames() { + // we want to wait for over 75 frames of the image, it has a delay of 200ms + // Windows debug test machines seem to animate at about 10 fps though + await new Promise(resolve => setTimeout(resolve, 20000)); +} + +async function closeWindows(wins) { + for (let i = 0; i < wins.length; i++) { + await BrowserTestUtils.closeWindow(wins[i]); + } +} + +async function popPrefs() { + await SpecialPowers.popPrefEnv(); + await SpecialPowers.popPrefEnv(); +} + +add_task(async () => { + async function runTest(theTestPath) { + await pushPrefs1(); + + let wins = await openWindowsAndMinimize(async function (win) { + let tab = await BrowserTestUtils.openNewForegroundTab( + win.gBrowser, + theTestPath + ); + }); + + await pushPrefs2(); + + await waitForEnoughFrames(); + + await closeWindows(wins); + + await popPrefs(); + + ok(true, "got here without assserting"); + } + + function fileURL(filename) { + let ifile = getChromeDir(getResolvedURI(gTestPath)); + ifile.append(filename); + return Services.io.newFileURI(ifile).spec; + } + + // This tests the image in content process case + await runTest(fileURL("helper_animatedImageLeak.html")); + // This tests the image in chrome process case + await runTest(getRootDirectory(gTestPath) + "helper_animatedImageLeak.html"); +}); + +// Now we test the image in a sidebar loaded via an extension case. + +/* + * The data uri below is a 2kb apng that is 3000x200 with 22 frames with delay + * of 200ms, it just toggles the color of one pixel from black to red so it's + * tiny. We use the same data uri (although that is not important to this test) + * in helper_animatedImageLeak.html. + */ + +/* + * This is just data to create a simple extension that creates a sidebar with + * an image in it. + */ +let extData = { + manifest: { + sidebar_action: { + default_panel: "sidebar.html", + }, + }, + useAddonManager: "temporary", + + files: { + "sidebar.html": ` +<!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8"> + <script src="sidebar.js"></script> + </head> + <body><p>Sidebar</p> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAC7gAAADICAMAAABP7lxwAAAACGFjVEwAAAAWAAAAAGbtojIAAAAJUExURQAAAAAAAP8AAD373S0AAAABdFJOUwBA5thmAAAAGmZjVEwAAAAAAAALuAAAAMgAAAAAAAAAAADIA+gAALdBHhgAAAJgSURBVHja7dABCQAAAAKg+n+6HYFOMAEAAA5UAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArwZGYwACQRkAGQAAABpmY1RMAAAAAQAAAAEAAAABAAAAAQAAAAEAyAPoAAD6Iy/6AAAADmZkQVQAAAACeNpjYAIAAAQAAzBzKbkAAAAaZmNUTAAAAAMAAAABAAAAAQAAAAEAAAABAMgD6AAAF7X8EwAAAA5mZEFUAAAABHjaY2AEAAADAAJ81yb0AAAAGmZjVEwAAAAFAAAAAQAAAAEAAAABAAAAAQDIA+gAAPp/jmkAAAAOZmRBVAAAAAZ42mNgAgAABAADgKpaOwAAABpmY1RMAAAABwAAAAEAAAABAAAAAQAAAAEAyAPoAAAX6V2AAAAADmZkQVQAAAAIeNpjYAQAAAMAAnbNtDMAAAAaZmNUTAAAAAkAAAABAAAAAQAAAAEAAAABAMgD6AAA+pps3AAAAA5mZEFUAAAACnjaY2ACAAAEAAOKsMj8AAAAGmZjVEwAAAALAAAAAQAAAAEAAAABAAAAAQDIA+gAABcMvzUAAAAOZmRBVAAAAAx42mNgBAAAAwACxhTHsQAAABpmY1RMAAAADQAAAAEAAAABAAAAAQAAAAEAyAPoAAD6xs1PAAAADmZkQVQAAAAOeNpjYAIAAAQAAzppu34AAAAaZmNUTAAAAA8AAAABAAAAAQAAAAEAAAABAMgD6AAAF1AepgAAAA5mZEFUAAAAEHjaY2AEAAADAAJi+JG9AAAAGmZjVEwAAAARAAAAAQAAAAEAAAABAAAAAQDIA+gAAPtRqbYAAAAOZmRBVAAAABJ42mNgAgAABAADnoXtcgAAABpmY1RMAAAAEwAAAAEAAAABAAAAAQAAAAEAyAPoAAAWx3pfAAAADmZkQVQAAAAUeNpjYAQAAAMAAtIh4j8AAAAaZmNUTAAAABUAAAABAAAAAQAAAAEAAAABAMgD6AAA+w0IJQAAAA5mZEFUAAAAFnjaY2ACAAAEAAMuXJ7wAAAAGmZjVEwAAAAXAAAAAQAAAAEAAAABAAAAAQDIA+gAABab28wAAAAOZmRBVAAAABh42mNgBAAAAwAC2Dtw+AAAABpmY1RMAAAAGQAAAAEAAAABAAAAAQAAAAEAyAPoAAD76OqQAAAADmZkQVQAAAAaeNpjYAIAAAQAAyRGDDcAAAAaZmNUTAAAABsAAAABAAAAAQAAAAEAAAABAMgD6AAAFn45eQAAAA5mZEFUAAAAHHjaY2AEAAADAAJo4gN6AAAAGmZjVEwAAAAdAAAAAQAAAAEAAAABAAAAAQDIA+gAAPu0SwMAAAAOZmRBVAAAAB542mNgAgAABAADlJ9/tQAAABpmY1RMAAAAHwAAAAEAAAABAAAAAQAAAAEAyAPoAAAWIpjqAAAADmZkQVQAAAAgeNpjYAQAAAMAAkqS2qEAAAAaZmNUTAAAACEAAAABAAAAAQAAAAEAAAABAMgD6AAA+MYjYgAAAA5mZEFUAAAAInjaY2ACAAAEAAO276ZuAAAAGmZjVEwAAAAjAAAAAQAAAAEAAAABAAAAAQDIA+gAABVQ8IsAAAAOZmRBVAAAACR42mNgBAAAAwAC+kupIwAAABpmY1RMAAAAJQAAAAEAAAABAAAAAQAAAAEAyAPoAAD4moLxAAAADmZkQVQAAAAmeNpjYAIAAAQAAwY21ewAAAAaZmNUTAAAACcAAAABAAAAAQAAAAEAAAABAMgD6AAAFQxRGAAAAA5mZEFUAAAAKHjaY2AEAAADAALwUTvkAAAAGmZjVEwAAAApAAAAAQAAAAEAAAABAAAAAQDIA+gAAPh/YEQAAAAOZmRBVAAAACp42mNgAgAABAADDCxHKwAAABt0RVh0U29mdHdhcmUAQVBORyBBc3NlbWJsZXIgMy4wXkUsHAAAAABJRU5ErkJggg=="/> + </body> +</html> + `, + + "sidebar.js": function () { + window.onload = () => { + browser.test.sendMessage("sidebar"); + }; + }, + }, +}; + +function getExtData(manifestUpdates = {}) { + return { + ...extData, + manifest: { + ...extData.manifest, + ...manifestUpdates, + }, + }; +} + +async function sendMessage(ext, msg, data = undefined) { + ext.sendMessage({ msg, data }); + await ext.awaitMessage("done"); +} + +add_task(async function sidebar_initial_install() { + await pushPrefs1(); + + ok( + document.getElementById("sidebar-box").hidden, + "sidebar box is not visible" + ); + + let extension = ExtensionTestUtils.loadExtension(getExtData()); + await extension.startup(); + await extension.awaitMessage("sidebar"); + + // Test sidebar is opened on install + ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible"); + + // the sidebar appears on all new windows automatically. + let wins = await openWindowsAndMinimize(async function (win) { + await extension.awaitMessage("sidebar"); + }); + + await pushPrefs2(); + + await waitForEnoughFrames(); + + await extension.unload(); + // Test that the sidebar was closed on unload. + ok( + document.getElementById("sidebar-box").hidden, + "sidebar box is not visible" + ); + + await closeWindows(wins); + + await popPrefs(); + + ok(true, "got here without assserting"); +}); diff --git a/layout/base/tests/bug1896051-ref.html b/layout/base/tests/bug1896051-ref.html new file mode 100644 index 0000000000..39b26f148d --- /dev/null +++ b/layout/base/tests/bug1896051-ref.html @@ -0,0 +1,29 @@ +<!doctype html> +<html class="reftest-wait"> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<style> + textarea { + font: 13px / 1 monospace; + border: 1px solid; + padding: 0; + overflow: hidden; + resize: none; + } +</style> +<textarea rows=5> +a +b +c +d +</textarea> +<script> +SimpleTest.waitForFocus(function() { + let textarea = document.querySelector("textarea"); + textarea.focus(); + textarea.selectionStart = textarea.selectionEnd = textarea.value.length; + setTimeout(() => { + document.documentElement.removeAttribute("class"); + }, 0); +}); +</script> diff --git a/layout/base/tests/bug1896051.html b/layout/base/tests/bug1896051.html new file mode 100644 index 0000000000..ba35339475 --- /dev/null +++ b/layout/base/tests/bug1896051.html @@ -0,0 +1,38 @@ +<!doctype html> +<html class="reftest-wait"> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<style> + textarea { + font: 13px / 1 monospace; + border: 1px solid; + padding: 0; + overflow: hidden; + resize: none; + } +</style> +<textarea rows=5> +a +b +c +d +</textarea> +<script> +SimpleTest.waitForFocus(function() { + let textarea = document.querySelector("textarea"); + textarea.focus(); + if (navigator.platform.startsWith("Mac")) { + // On mac there is no page down key that moves the selection, afaict. + // (Fn+Arrow moves the scroll position but not selection). + // Do the next thing which would be something like Cmd+Down to move to the + // end. That tests a different code-path altogether, but for this test it + // doesn't matter. + synthesizeKey("KEY_ArrowDown", { metaKey: true }); + } else { + synthesizeKey("KEY_PageDown"); + } + setTimeout(() => { + document.documentElement.removeAttribute("class") + }, 0); +}); +</script> diff --git a/layout/base/tests/chrome/chrome.toml b/layout/base/tests/chrome/chrome.toml index 6636b224e6..63e3da9609 100644 --- a/layout/base/tests/chrome/chrome.toml +++ b/layout/base/tests/chrome/chrome.toml @@ -66,6 +66,12 @@ support-files = [ "printpreview_pps16_ref.html", "printpreview_prettyprint.xml", "printpreview_prettyprint_ref.xhtml", + "printpreview_scale_test_001.html", + "printpreview_scale_test_001_ref.html", + "printpreview_scale_test_002.html", + "printpreview_scale_test_002_ref.html", + "printpreview_scale_test_003.html", + "printpreview_scale_test_003_ref.html", "printpreview_mask.html", "print_page_size1.html", "print_page_size1_ref.html", diff --git a/layout/base/tests/chrome/printpreview_helper.xhtml b/layout/base/tests/chrome/printpreview_helper.xhtml index e40d2e4d5b..119c55d777 100644 --- a/layout/base/tests/chrome/printpreview_helper.xhtml +++ b/layout/base/tests/chrome/printpreview_helper.xhtml @@ -1708,9 +1708,252 @@ async function runTest58() { let test = "printpreview_mixed_page_size_002.html"; // The params are just to give the file unique URLs. await compareFiles(test + "?test", test + "?ref"); + requestAnimationFrame(() => setTimeout(runTest59)); +} + +// Creates a data URL that has a single div of |divSize| em square, on a page +// of size |pageSize| inches square. +// |center| determines if the div should be centered horizontally. +function createScalingTestSource(pageSize, center, name) { + // Styling always used. + let baseStyle = 'background: blue;'; + if (center) { + baseStyle += 'margin: auto;'; + } + const div = '<div style="width: 100px; height: 100px;' + baseStyle + '"></div>'; + const style = '<style>@page{size:' + pageSize + 'in}body{margin:0}</style>'; + // Add the name as a comment, to ensure every test has a unique source even + // if the parameters are identical. + const comment = '<!-- ' + name + ' -->'; + return 'data:text/html,' + style + div + comment; +} + +async function runScalingCenteredTest(refPageSize, testPageSize, paperSize, + name, center = true, fuzz = null) { + const printSettings = { + settings: { + paperWidth: paperSize, + paperHeight: paperSize, + paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches, + marginTop: 0, + marginRight: 0, + marginBottom: 0, + marginLeft: 0, + unwriteableMarginTop: 0, + unwriteableMarginRight: 0, + unwriteableMarginBottom: 0, + unwriteableMarginLeft: 0, + } + }; + let settings = Object.create(fuzz, { + ref: {value: printSettings}, + test: {value: printSettings} + }); + const testSrc = createScalingTestSource(testPageSize, center, name); + const refSrc = createScalingTestSource(refPageSize, center, name); + return compareFiles(testSrc, refSrc, settings); +} + +// Tests that auto-detection and use of page centering. +// Has a smaller page on a larger sheet, where the difference is within the +// tolerance for auto-detection. +async function runTest59() { + await SpecialPowers.pushPrefEnv({ + set: [["print.center_page_on_sheet", 2]] + }); + // See bug 1680838 + const fuzz = navigator.platform.includes("Win") ? + { maxDifferent: 180, maxDifference: 255 } : + null; + await runScalingCenteredTest(10, 9.5, 10, "runTest59", true, fuzz); + await SpecialPowers.popPrefEnv(); + requestAnimationFrame(() => setTimeout(runTest60)); +} + +// Tests that centering won't occur when the pref disables it, using the same +// values as runTest59. +async function runTest60() { + await SpecialPowers.pushPrefEnv({ + set: [["print.center_page_on_sheet", 0]] + }); + // See bug 1680838 + const fuzz = navigator.platform.includes("Win") ? + { maxDifferent: 180, maxDifference: 255 } : + null; + await runScalingCenteredTest(10, 9.5, 10, "runTest60", false, fuzz); + await SpecialPowers.popPrefEnv(); + requestAnimationFrame(() => setTimeout(runTest61)); +} + +// Tests that auto-detection will reject too big a difference for page +// centering. Has a much smaller page on a larger sheet, where the difference +// is outside the threshold for auto-detection. +async function runTest61() { + await SpecialPowers.pushPrefEnv({ + set: [["print.center_page_on_sheet", 2]] + }); + // See bug 1680838 + const fuzz = navigator.platform.includes("Win") ? + { maxDifferent: 450, maxDifference: 255 } : + null; + await runScalingCenteredTest(10, 8.9, 10, "runTest61", false, fuzz); + await SpecialPowers.popPrefEnv(); + requestAnimationFrame(() => setTimeout(runTest62)); +} + +// Tests that we can force page centering with the pref, using the same values +// as runTest61. +async function runTest62() { + await SpecialPowers.pushPrefEnv({ + set: [["print.center_page_on_sheet", 1]] + }); + // See bug 1680838 + const fuzz = navigator.platform.includes("Win") ? + { maxDifferent: 450, maxDifference: 255 } : + null; + await runScalingCenteredTest(10, 8.9, 10, "runTest62", true, fuzz); + await SpecialPowers.popPrefEnv(); + requestAnimationFrame(() => setTimeout(runTest63)); +} + +// Tests that centering will always happen if the pref forces it. +// The sizes used in these files are very large and the scale factor very high +// here to ensure that any errors in the calculation for centering will be +// magnified. +async function runTest63() { + let test = "printpreview_scale_test_001.html"; + let ref = "printpreview_scale_test_001_ref.html"; + + await SpecialPowers.pushPrefEnv({ + set: [["print.center_page_on_sheet", 1]] + }); + await compareFiles(test, ref, { + test: { + settings: { + paperWidth: 8.5, + paperHeight: 11, + paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches, + marginTop: 0, + marginRight: 0, + marginBottom: 0, + marginLeft: 0, + unwriteableMarginTop: 0, + unwriteableMarginRight: 0, + unwriteableMarginBottom: 0, + unwriteableMarginLeft: 0, + }, + }, + ref: { + settings: { + paperWidth: 8.5, + paperHeight: 11, + paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches, + marginTop: 0, + marginRight: 0, + marginBottom: 0, + marginLeft: 0, + unwriteableMarginTop: 0, + unwriteableMarginRight: 0, + unwriteableMarginBottom: 0, + unwriteableMarginLeft: 0, + }, + }, + }); + await SpecialPowers.popPrefEnv(); + requestAnimationFrame(() => setTimeout(runTest64)); +} + +// Tests that printing A4 pages on US Letter will be a close enough fit +// that we will automatically center the page. +async function runTest64() { + let test = "printpreview_scale_test_002.html"; + let ref = "printpreview_scale_test_002_ref.html"; + + await SpecialPowers.pushPrefEnv({ + set: [["print.center_page_on_sheet", 2]] + }); + await compareFiles(test, ref, { + test: { + settings: { + paperWidth: 8.5, + paperHeight: 11, + paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches, + marginTop: 0, + marginRight: 0, + marginBottom: 0, + marginLeft: 0, + unwriteableMarginTop: 0, + unwriteableMarginRight: 0, + unwriteableMarginBottom: 0, + unwriteableMarginLeft: 0, + }, + }, + ref: { + settings: { + paperWidth: 8.5, + paperHeight: 11, + paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches, + marginTop: 0, + marginRight: 0, + marginBottom: 0, + marginLeft: 0, + unwriteableMarginTop: 0, + unwriteableMarginRight: 0, + unwriteableMarginBottom: 0, + unwriteableMarginLeft: 0, + }, + }, + }); + await SpecialPowers.popPrefEnv(); + requestAnimationFrame(() => setTimeout(runTest65)); +} + +// Tests that auto-detection will reject a large enough difference in width +// when downscaling is used to make the page fit on the paper. +async function runTest65() { + let test = "printpreview_scale_test_003.html"; + let ref = "printpreview_scale_test_003_ref.html"; + + await SpecialPowers.pushPrefEnv({ + set: [["print.center_page_on_sheet", 2]] + }); + await compareFiles(test, ref, { + test: { + settings: { + paperWidth: 8.5, + paperHeight: 11, + paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches, + marginTop: 0, + marginRight: 0, + marginBottom: 0, + marginLeft: 0, + unwriteableMarginTop: 0, + unwriteableMarginRight: 0, + unwriteableMarginBottom: 0, + unwriteableMarginLeft: 0, + }, + }, + ref: { + settings: { + paperWidth: 8.5, + paperHeight: 11, + paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches, + marginTop: 0, + marginRight: 0, + marginBottom: 0, + marginLeft: 0, + unwriteableMarginTop: 0, + unwriteableMarginRight: 0, + unwriteableMarginBottom: 0, + unwriteableMarginLeft: 0, + }, + }, + }); + await SpecialPowers.popPrefEnv(); finish(); } + ]]></script> <table style="border: 1px solid black;" xmlns="http://www.w3.org/1999/xhtml"> <tr><th>Print preview canvas 1</th><th>Print preview canvas 2</th></tr> diff --git a/layout/base/tests/chrome/printpreview_scale_test_001.html b/layout/base/tests/chrome/printpreview_scale_test_001.html new file mode 100644 index 0000000000..e9d3122b6b --- /dev/null +++ b/layout/base/tests/chrome/printpreview_scale_test_001.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<head> + <style> +@page { + size: 10in 22in; + margin: 0; +} +body{ + margin:0; +} +div{ + width: 2in; + height: 2in; + background: blue; + margin-left: 4in; +} + </style> +</head> +<body> + <div></div> +</body> diff --git a/layout/base/tests/chrome/printpreview_scale_test_001_ref.html b/layout/base/tests/chrome/printpreview_scale_test_001_ref.html new file mode 100644 index 0000000000..2ed4571ef1 --- /dev/null +++ b/layout/base/tests/chrome/printpreview_scale_test_001_ref.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<head> + <style> +@page { + size: 4in 44in; + margin: 0; +} +body{ + margin:0; +} +div{ + height: 4in; + width: 4in; + background: blue; +} + </style> +</head> +<body> + <div></div> +</body> diff --git a/layout/base/tests/chrome/printpreview_scale_test_002.html b/layout/base/tests/chrome/printpreview_scale_test_002.html new file mode 100644 index 0000000000..94c35ab3c3 --- /dev/null +++ b/layout/base/tests/chrome/printpreview_scale_test_002.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<head> + <style> +@page { + size: A4; + margin: 0; +} +body{ + margin:0; +} +div{ + height: 200px; + width: 200px; + background: blue; + margin: auto; +} + </style> +</head> +<body> + <div></div> +</body> diff --git a/layout/base/tests/chrome/printpreview_scale_test_002_ref.html b/layout/base/tests/chrome/printpreview_scale_test_002_ref.html new file mode 100644 index 0000000000..d73de86fe5 --- /dev/null +++ b/layout/base/tests/chrome/printpreview_scale_test_002_ref.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<head> + <style> +@page { + size: letter; + margin: 0; +} +body{ + margin:0; +} +div{ + /* A4-on-letter requires a 0.9407 downscale. 11in = 279.4mm, and + * 279.4mm / 297mm = 0.9407407407.. + * The unscaled reference case has a 200px square div, so reverse the scale + * to match that (rounding to 0.940741) + */ + height: calc(0.940741 * 200px); + width: calc(0.940741 * 200px); + background: blue; + margin: auto; +} + </style> +</head> +<body> + <div></div> +</body> diff --git a/layout/base/tests/chrome/printpreview_scale_test_003.html b/layout/base/tests/chrome/printpreview_scale_test_003.html new file mode 100644 index 0000000000..2b1d68ff60 --- /dev/null +++ b/layout/base/tests/chrome/printpreview_scale_test_003.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<head> + <style> +@page { + size: 8.5in 13in; + margin: 0; +} +body{ + margin:0; +} +div{ + /* 13in / 11in = 1.1818181... */ + height: calc(1.1818182 * 200px); + width: calc(1.1818182 * 200px); + background: blue; +} + </style> +</head> +<body> + <div></div> +</body> diff --git a/layout/base/tests/chrome/printpreview_scale_test_003_ref.html b/layout/base/tests/chrome/printpreview_scale_test_003_ref.html new file mode 100644 index 0000000000..41a3f87889 --- /dev/null +++ b/layout/base/tests/chrome/printpreview_scale_test_003_ref.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<head> + <style> +@page { + size: 8.5in 11in; + margin: 0; +} +body{ + margin:0; +} +div{ + /* 11in / 13in = 0.8461538 */ + height: 200px; + width: 200px; + background: blue; +} + </style> +</head> +<body> + <div></div> +</body> diff --git a/layout/base/tests/chrome/test_bug420499.xhtml b/layout/base/tests/chrome/test_bug420499.xhtml index 22fefd7987..8db69ff6c1 100644 --- a/layout/base/tests/chrome/test_bug420499.xhtml +++ b/layout/base/tests/chrome/test_bug420499.xhtml @@ -84,7 +84,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=420499 function popupMenuShownHandler() { window.removeEventListener("popupshown", popupMenuShownHandler); - ok(!isCaretVisible(), "Caret shouldn't be visible when menu open"); + ok(isCaretVisible(), "Caret shouldn't be visible when menu open"); window.addEventListener("popuphidden", ensureParagraphFocused); $("menu").open = false; } diff --git a/layout/base/tests/helper_animatedImageLeak.html b/layout/base/tests/helper_animatedImageLeak.html new file mode 100644 index 0000000000..a5555f1fbb --- /dev/null +++ b/layout/base/tests/helper_animatedImageLeak.html @@ -0,0 +1,10 @@ +<html> +<!-- + The data uri below is a 2kb apng that is 3000x200 with 22 frames with delay + of 200ms, it just toggles the color of one pixel from black to red so it's + tiny. We use the same data uri (although that is not important to this test) + in browser_animatedImageLeak.js. +--> + +<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAC7gAAADICAMAAABP7lxwAAAACGFjVEwAAAAWAAAAAGbtojIAAAAJUExURQAAAAAAAP8AAD373S0AAAABdFJOUwBA5thmAAAAGmZjVEwAAAAAAAALuAAAAMgAAAAAAAAAAADIA+gAALdBHhgAAAJgSURBVHja7dABCQAAAAKg+n+6HYFOMAEAAA5UAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArwZGYwACQRkAGQAAABpmY1RMAAAAAQAAAAEAAAABAAAAAQAAAAEAyAPoAAD6Iy/6AAAADmZkQVQAAAACeNpjYAIAAAQAAzBzKbkAAAAaZmNUTAAAAAMAAAABAAAAAQAAAAEAAAABAMgD6AAAF7X8EwAAAA5mZEFUAAAABHjaY2AEAAADAAJ81yb0AAAAGmZjVEwAAAAFAAAAAQAAAAEAAAABAAAAAQDIA+gAAPp/jmkAAAAOZmRBVAAAAAZ42mNgAgAABAADgKpaOwAAABpmY1RMAAAABwAAAAEAAAABAAAAAQAAAAEAyAPoAAAX6V2AAAAADmZkQVQAAAAIeNpjYAQAAAMAAnbNtDMAAAAaZmNUTAAAAAkAAAABAAAAAQAAAAEAAAABAMgD6AAA+pps3AAAAA5mZEFUAAAACnjaY2ACAAAEAAOKsMj8AAAAGmZjVEwAAAALAAAAAQAAAAEAAAABAAAAAQDIA+gAABcMvzUAAAAOZmRBVAAAAAx42mNgBAAAAwACxhTHsQAAABpmY1RMAAAADQAAAAEAAAABAAAAAQAAAAEAyAPoAAD6xs1PAAAADmZkQVQAAAAOeNpjYAIAAAQAAzppu34AAAAaZmNUTAAAAA8AAAABAAAAAQAAAAEAAAABAMgD6AAAF1AepgAAAA5mZEFUAAAAEHjaY2AEAAADAAJi+JG9AAAAGmZjVEwAAAARAAAAAQAAAAEAAAABAAAAAQDIA+gAAPtRqbYAAAAOZmRBVAAAABJ42mNgAgAABAADnoXtcgAAABpmY1RMAAAAEwAAAAEAAAABAAAAAQAAAAEAyAPoAAAWx3pfAAAADmZkQVQAAAAUeNpjYAQAAAMAAtIh4j8AAAAaZmNUTAAAABUAAAABAAAAAQAAAAEAAAABAMgD6AAA+w0IJQAAAA5mZEFUAAAAFnjaY2ACAAAEAAMuXJ7wAAAAGmZjVEwAAAAXAAAAAQAAAAEAAAABAAAAAQDIA+gAABab28wAAAAOZmRBVAAAABh42mNgBAAAAwAC2Dtw+AAAABpmY1RMAAAAGQAAAAEAAAABAAAAAQAAAAEAyAPoAAD76OqQAAAADmZkQVQAAAAaeNpjYAIAAAQAAyRGDDcAAAAaZmNUTAAAABsAAAABAAAAAQAAAAEAAAABAMgD6AAAFn45eQAAAA5mZEFUAAAAHHjaY2AEAAADAAJo4gN6AAAAGmZjVEwAAAAdAAAAAQAAAAEAAAABAAAAAQDIA+gAAPu0SwMAAAAOZmRBVAAAAB542mNgAgAABAADlJ9/tQAAABpmY1RMAAAAHwAAAAEAAAABAAAAAQAAAAEAyAPoAAAWIpjqAAAADmZkQVQAAAAgeNpjYAQAAAMAAkqS2qEAAAAaZmNUTAAAACEAAAABAAAAAQAAAAEAAAABAMgD6AAA+MYjYgAAAA5mZEFUAAAAInjaY2ACAAAEAAO276ZuAAAAGmZjVEwAAAAjAAAAAQAAAAEAAAABAAAAAQDIA+gAABVQ8IsAAAAOZmRBVAAAACR42mNgBAAAAwAC+kupIwAAABpmY1RMAAAAJQAAAAEAAAABAAAAAQAAAAEAyAPoAAD4moLxAAAADmZkQVQAAAAmeNpjYAIAAAQAAwY21ewAAAAaZmNUTAAAACcAAAABAAAAAQAAAAEAAAABAMgD6AAAFQxRGAAAAA5mZEFUAAAAKHjaY2AEAAADAALwUTvkAAAAGmZjVEwAAAApAAAAAQAAAAEAAAABAAAAAQDIA+gAAPh/YEQAAAAOZmRBVAAAACp42mNgAgAABAADDCxHKwAAABt0RVh0U29mdHdhcmUAQVBORyBBc3NlbWJsZXIgMy4wXkUsHAAAAABJRU5ErkJggg=="/> +</html> diff --git a/layout/base/tests/helper_bug1733509.html b/layout/base/tests/helper_bug1733509.html new file mode 100644 index 0000000000..4fbdacdb46 --- /dev/null +++ b/layout/base/tests/helper_bug1733509.html @@ -0,0 +1,30 @@ +<html> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/paint_listener.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="text/javascript" src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script> +<style> + body { + margin: 0; + padding: 20; + background-color: blueviolet; + } + + button { + margin: 0; + } +</style> + +<body> + <button id="btn">Click me</button> +</body> + +<script> + // Silence SimpleTest warning about missing assertions by having it wait + // indefinitely. We don't need to give it an explicit finish because the + // entire window this test runs in will be closed after the main browser test + // finished. + SimpleTest.waitForExplicitFinish(); +</script> + +</html> diff --git a/layout/base/tests/mochitest.toml b/layout/base/tests/mochitest.toml index 24924809c0..f74c6b030d 100644 --- a/layout/base/tests/mochitest.toml +++ b/layout/base/tests/mochitest.toml @@ -247,6 +247,7 @@ support-files = ["file_dynamic_toolbar_max_height.html"] ["test_emulate_color_scheme.html"] ["test_event_target_radius.html"] +support-files = ["helper_bug1733509.html"] skip-if = ["xorigin"] # JavaScript error: resource://specialpowers/SpecialPowersChild.sys.mjs, line 73: SecurityError: Permission denied to access property "windowUtils" on cross-origin object ["test_frame_reconstruction_body_table.html"] @@ -558,6 +559,8 @@ support-files = [ "bug1518339-2-ref.html", "bug1529492-1.html", "bug1529492-1-ref.html", + "bug1896051.html", + "bug1896051-ref.html", "chrome/blue-32x32.png", ] diff --git a/layout/base/tests/test_event_target_radius.html b/layout/base/tests/test_event_target_radius.html index caf046cf99..a1e8d9c16c 100644 --- a/layout/base/tests/test_event_target_radius.html +++ b/layout/base/tests/test_event_target_radius.html @@ -2,14 +2,19 @@ <html id="html" style="height:100%"> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=780847 +https://bugzilla.mozilla.org/show_bug.cgi?id=1733509 --> <head> <title>Test radii for mouse events</title> - <script src="/tests/SimpleTest/SimpleTest.js"></script> <script src="/tests/SimpleTest/EventUtils.js"></script> + <script src="/tests/SimpleTest/paint_listener.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <style> .target { position:absolute; left:100px; top:100px; width:100px; height:100px; background:blue; } + .scaled { background: green; transform: scale(0.5); } + iframe { margin:0; padding:0; width:50; height:50; border:1px solid black; background:yellowgreen; } </style> </head> <body id="body" onload="setTimeout(startTest, 0)" style="margin:0; width:100%; height:100%; overflow:hidden"> @@ -73,6 +78,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=780847 <div id="t13_touchlistener" style="width: 50px; height: 50px; background:red" ontouchend="x=1"></div> <div id="t13_notouchlistener" style="width: 50px; height: 50px; background:green"></div> </div> + + <div id="t14" class="target scaled" hidden> + <iframe id="t14iframe"></iframe> + </div> </div> <pre id="test"> <script type="application/javascript"> @@ -414,7 +423,50 @@ function testTouchable() { testTouch("t13", 10, 50 - (2*mm), "t13_touchlistener", "touch inside t13_touchlistener bottom edge"); setShowing("t13", false); - endTest(); + test4(); +} + +// https://bugzilla.mozilla.org/show_bug.cgi?id=1733509 +function test4() { + // Skip non-Fission for now because of bug 1890522 + if (SpecialPowers.Services.appinfo.fissionAutostart) { + waitUntilApzStable().then(async () => doTest4()).then(endTest); + } else { + endTest(); + } +} + +async function doTest4() { + setShowing("t14", true); + + let iframeURL = SimpleTest.getTestFileURL("helper_bug1733509.html"); + // todo: Also perform this test for an in-process iframe + // once bug 1890522 is fixed. + const iframe = document.querySelector("#t14iframe"); + iframeURL = iframeURL.replace(window.location.origin, "https://example.com"); + await setupIframe(iframe, iframeURL); + + var result = await SpecialPowers.spawn(iframe, [], async () => { + await content.wrappedJSObject.waitUntilApzStable(); + var iframeEventTarget = null; + content.window.onmousedown = function (event) { iframeEventTarget = event.target; }; + content.wrappedJSObject.synthesizeMouse(content.document.documentElement, 2, 2, {}); + return iframeEventTarget && iframeEventTarget.id === "btn"; + }); + + ok(result, "Failed to target button inside iframe"); + setShowing("t14", false); +} + +async function setupIframe(aIFrame, aURL) { + const iframeLoadPromise = promiseOneEvent(aIFrame, "load", null); + aIFrame.src = aURL; + await iframeLoadPromise; + + await SpecialPowers.spawn(aIFrame, [], async () => { + await content.wrappedJSObject.waitUntilApzStable(); + await SpecialPowers.contentTransformsReceived(content); + }); } </script> </pre> diff --git a/layout/base/tests/test_reftests_with_caret.html b/layout/base/tests/test_reftests_with_caret.html index 3935380e5d..d134bd2eb9 100644 --- a/layout/base/tests/test_reftests_with_caret.html +++ b/layout/base/tests/test_reftests_with_caret.html @@ -112,6 +112,7 @@ var tests = [ [ 'bug613807-1.html' , 'bug613807-1-ref.html' ] , [ 'bug1082486-1.html', 'bug1082486-1-ref.html'] , [ 'bug1082486-2.html', 'bug1082486-2-ref.html'] , + [ 'bug1896051.html', 'bug1896051-ref.html'], // The following test cases uses mouse events. We need to make // AccessibleCaret unhide for them. function() {SpecialPowers.pushPrefEnv({'set': [['layout.accessiblecaret.hide_carets_for_mouse_input', false]]}, nextTest);} , diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index 1738098ed2..ecc84ad286 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -183,9 +183,6 @@ nsresult nsLayoutStatics::Initialize() { nsMathMLOperators::AddRefTable(); -#ifdef DEBUG - nsIFrame::DisplayReflowStartup(); -#endif Attr::Initialize(); PopupBlocker::Initialize(); @@ -320,9 +317,6 @@ void nsLayoutStatics::Shutdown() { HTMLDNSPrefetch::Shutdown(); nsCSSRendering::Shutdown(); StaticPresData::Shutdown(); -#ifdef DEBUG - nsIFrame::DisplayReflowShutdown(); -#endif nsCellMap::Shutdown(); ActiveLayerTracker::Shutdown(); diff --git a/layout/docs/DynamicChangeHandling.md b/layout/docs/DynamicChangeHandling.md new file mode 100644 index 0000000000..50df613412 --- /dev/null +++ b/layout/docs/DynamicChangeHandling.md @@ -0,0 +1,83 @@ +# Dynamic change handling along the rendering pipeline + +The ability to make changes to the DOM from script is a major feature of +the Web platform. Web authors rely on the concept (though there are a +few exceptions, such as animations) that changing the DOM from script +leads to the same rendering that would have resulted from starting from +that DOM tree. They also rely on the performance characteristics of +these changes: small changes to the DOM that have small effects should +have proportionally small processing time. This means that Gecko needs +to efficiently propagate changes from the content tree to style, the +frame tree, the geometry of the frame tree, and the screen. + +For many types of changes, however, there is substantial overhead to +processing a change, no matter how small. For example, reflow must +propagate from the top of the frame tree down to the frames that are +dirty, no matter how small the change. One very common way around this +is to batch up changes. We batch up changes in lots of ways, for +example: + +- The content sink adds multiple nodes to the DOM tree before + notifying listeners that they\'ve been added. This allows notifying + once about an ancestor rather than for each of its descendants, or + notifying about a group of descendants all at once, which speeds up + the processing of those notifications. +- We batch up nodes that require style reresolution (recomputation of + selector matching and processing the resulting style changes). This + batching is tree based, so it not only merges multiple notifications + on the same element, but also merges a notification on an ancestor + with a notification on its descendant (since *some* of these + notifications imply that style reresolution is required on all + descendants). +- We wait to reconstruct frames that require reconstruction (after + destroying frames eagerly). This, like the tree-based style + reresolution batching, avoids duplication both for same-element + notifications and ancestor-descendant notifications, even though it + doesn\'t actually do any tree-based caching. +- We postpone doing reflows until needed. As for style reresolution, + this maintains tree-based dirty bits (see the description of + NS\_FRAME\_IS\_DIRTY and NS\_FRAME\_HAS\_DIRTY\_CHILDREN under + Reflow). +- We allow the OS to queue up multiple invalidates before repainting + (though we will likely switch to controlling that ourselves). This + leads to a single repaint of some set of pixels where there might + otherwise have been multiple (though it may also lead to more pixels + being repainted if multiple rectangles are merged to a single one). + +Having changes buffered up means, however, that various pieces of +information (layout, style, etc.) may not be up-to-date. Some things +require up-to-date information: for example, we don\'t want to expose +the details of our buffering to Web page script since the programming +model of Web page script assumes that DOM changes take effect +\"immediately\", i.e., that the script shouldn\'t be able to detect any +buffering. Many Web pages depend on this. + +We therefore have ways to flush these different sorts of buffers. There +are methods called FlushPendingNotifications on nsIDocument and +nsIPresShell, that take an argument of what things to flush: + +- Flush\_Content: create all the content nodes from data buffered in + the parser +- Flush\_ContentAndNotify: the above, plus notify document observers + about the creation of all nodes created so far +- Flush\_Style: the above, plus make sure style data are up-to-date +- Flush\_Frames: the above, plus make sure all frame construction has + happened (currently the same as Flush\_Style) +- Flush\_InterruptibleLayout: the above, plus perform layout (Reflow), + but allow interrupting layout if it takes too long +- Flush\_Layout: the above, plus ensure layout (Reflow) runs to + completion +- Flush\_Display (should never be used): the above, plus ensure + repainting happens + +The major way that notifications of changes propagate from the content +code to layout and other areas of code is through the +nsIDocumentObserver and nsIMutationObserver interfaces. Classes can +implement this interface to listen to notifications of changes for an +entire document or for a subtree of the content tree. + +WRITE ME: \... layout document observer implementations + +TODO: how style system optimizes away rerunning selector matching + +TODO: style changes and nsChangeHint diff --git a/layout/docs/LayoutOverview.md b/layout/docs/LayoutOverview.md new file mode 100644 index 0000000000..0375ab5480 --- /dev/null +++ b/layout/docs/LayoutOverview.md @@ -0,0 +1,529 @@ +# Layout Overview + +Much of the layout code deals with operations on the frame tree (or +rendering tree). In the frame tree, each node represents a rectangle +(or, for SVG, other shapes). The frame tree has a shape similar to the +content tree, since many content nodes have one corresponding frame, +though it differs in a few ways, since some content nodes have more than +one frame or don\'t have any frames at all. When elements are +display:none in CSS or undisplayed for certain other reasons, they +won\'t have any frames. When elements are broken across lines or pages, +they have multiple frames; elements may also have multiple frames when +multiple frames nested inside each other are needed to display a single +element (for example, a table, a table cell, or many types of form +controls). + +Each node in the frame tree is an instance of a class derived from +`nsIFrame`. As with the content tree, there is a substantial type +hierarchy, but the type hierarchy is very different: it includes types +like text frames, blocks and inlines, the various parts of tables, flex +and grid containers, and the various types of HTML form controls. + +Frames are allocated within an arena owned by the PresShell. Each frame +is owned by its parent; frames are not reference counted, and code must +not hold on to pointers to frames. To mitigate potential security bugs +when pointers to destroyed frames, we use [frame +poisoning](http://robert.ocallahan.org/2010/10/mitigating-dangling-pointer-bugs-using_15.html), +which takes two parts. When a frame is destroyed other than at the end +of life of the presentation, we fill its memory with a pattern +consisting of a repeated pointer to inaccessible memory, and then put +the memory on a per-frame-class freelist. This means that if code +accesses the memory through a dangling pointer, it will either crash +quickly by dereferencing the poison pattern or it will find a valid +frame. + +Like the content tree, frames must be accessed only from the UI thread. + +The frame tree should not store any important data, i.e. any data that +cannot be recomputed on-the-fly. While the frame tree does usually +persist while a page is being displayed, frames are often destroyed and +recreated in response to certain style changes, and in the future we may +do the same to reduce memory use for pages that are currently inactive. +There were a number of cases where this rule was violated in the past +and we stored important data in the frame tree; however, most (though +not quite all) such cases are now fixed. + +The rectangle represented by the frame is what CSS calls the element\'s +border box. This is the outside edge of the border (or the inside edge +of the margin). The margin lives outside the border; and the padding +lives inside the border. In addition to nsIFrame::GetRect, we also have +the APIs nsIFrame::GetPaddingRect to get the padding box (the outside +edge of the padding, or inside edge of the border) and +nsIFrame::GetContentRect to get the content box (the outside edge of the +content, or inside edge of the padding). These APIs may produce out of +date results when reflow is needed (or has not yet occurred). + +In addition to tracking a rectangle, frames also track two overflow +areas: ink overflow and scrollable overflow. These overflow areas +represent the union of the area needed by the frame and by all its +descendants. The ink overflow is used for painting-related +optimizations: it is a rectangle covering all of the area that might be +painted when the frame and all of its descendants paint. The scrollable +overflow represents the area that the user should be able to scroll to +to see the frame and all of its descendants. In some cases differences +between the frame\'s rect and its overflow happen because of descendants +that stick out of the frame; in other cases they occur because of some +characteristic of the frame itself. The two overflow areas are similar, +but there are differences: for example, margins are part of scrollable +overflow but not ink overflow, whereas text-shadows are part of ink +overflow but not scrollable overflow. + +When frames are broken across lines, columns, or pages, we create +multiple frames representing the multiple rectangles of the element. The +first one is the primary frame, and the rest are its continuations +(which are more likely to be destroyed and recreated during reflow). +These frames are linked together as continuations: they have a +doubly-linked list that can be used to traverse the continuations using +nsIFrame::GetPrevContinuation and nsIFrame::GetNextContinuation. +(Currently continuations always have the same style data, though we may +at some point want to break that invariant.) + +Continuations are sometimes siblings of each other (i.e. +nsIFrame::GetNextContinuation and nsIFrame::GetNextSibling might return +the same frame), and sometimes not. For example, if a paragraph contains +a span which contains a link, and the link is split across lines, then +the continuations of the span are siblings (since they are both children +of the paragraph), but the continuations of the link are not siblings +(since each continuation of the link is descended from a different +continuation of the span). Traversing the entire frame tree does **not** +require explicit traversal of any frames\' continuations-list, since all +of the continuations are descendants of the element containing the +break. + +We also use continuations for cases (most importantly, bidi reordering, +where left-to-right text and right-to-left text need to be separated +into different continuations since they may not form a contiguous +rectangle) where the continuations should not be rewrapped during +reflow: we call these continuations fixed rather than fluid. +nsIFrame::GetNextInFlow and nsIFrame::GetPrevInFlow traverse only the +fluid continuations and do not cross fixed continuation boundaries. + +If an inline frame has non-inline children, then we split the original +inline frame into parts. The original inline\'s children are distributed +into these parts like so: The children of the original inline are +grouped into runs of inline and non-inline, and runs of inline get an +inline parent, while runs of non-inline get an anonymous block parent. +We call this \'ib-splitting\' or \'block-inside-inline splitting\'. This +splitting proceeds recursively up the frame tree until all non-inlines +inside inlines are ancestors of a block frame with anonymous block +wrappers in between. This splitting maintains the relative order between +these child frames, and the relationship between the parts of a split +inline is maintained using an ib-sibling chain. It is important to note +that any wrappers created during frame construction (such as for tables) +might not be included in the ib-sibling chain depending on when this +wrapper creation takes place. + +TODO: nsBox craziness from +<https://bugzilla.mozilla.org/show_bug.cgi?id=524925#c64> + +TODO: link to documentation of block and inline layout + +TODO: link to documentation of scrollframes + +TODO: link to documentation of XUL frame classes + +Code (note that most files in base and generic have useful one line +descriptions at the top that show up in DXR): + +- [layout/base/](http://dxr.mozilla.org/mozilla-central/source/layout/base/) + contains objects that coordinate everything and a bunch of other + miscellaneous things +- [layout/generic/](http://dxr.mozilla.org/mozilla-central/source/layout/generic/) + contains the basic frame classes as well as support code for their + reflow methods (ReflowInput, ReflowOutput) +- [layout/forms/](http://dxr.mozilla.org/mozilla-central/source/layout/forms/) + contains frame classes for HTML form controls +- [layout/tables/](http://dxr.mozilla.org/mozilla-central/source/layout/tables/) + contains frame classes for CSS/HTML tables +- [layout/mathml/](http://dxr.mozilla.org/mozilla-central/source/layout/mathml/) + contains frame classes for MathML +- [layout/svg/](http://dxr.mozilla.org/mozilla-central/source/layout/svg/) + contains frame classes for SVG +- [layout/xul/](http://dxr.mozilla.org/mozilla-central/source/layout/xul/) + contains frame classes for the XUL box model and for various XUL + widgets + +Bugzilla: + +- All of the components whose names begin with \"Layout\" in the + \"Core\" product + +Further documentation: + +- Talk: [Introduction to graphics/layout + architecture](https://air.mozilla.org/introduction-to-graphics-layout-architecture/) + (Robert O\'Callahan, 2014-04-18) +- Talk: [Layout and + Styles](https://air.mozilla.org/bz-layout-and-styles/) (Boris + Zbarsky, 2014-10-14) + +## Frame Construction + +Frame construction is the process of creating frames. This is done when +styles change in ways that require frames to be created or recreated or +when nodes are inserted into the document. The content tree and the +frame tree don\'t have quite the same shape, and the frame construction +process does some of the work of creating the right shape for the frame +tree. It handles the aspects of creating the right shape that don\'t +depend on layout information. So for example, frame construction handles +the work needed to implement [table anonymous +objects](http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes) but +does not handle frames that need to be created when an element is broken +across lines or pages. + +The basic unit of frame construction is a run of contiguous children of +a single parent element. When asked to construct frames for such a run +of children, the frame constructor first determines, based on the +siblings and parent of the nodes involved, where in the frame tree the +new frames should be inserted. Then the frame constructor walks through +the list of content nodes involved and for each one creates a temporary +data structure called a **frame construction item**. The frame +construction item encapsulates various information needed to create the +frames for the content node: its style data, some metadata about how one +would create a frame for this node based on its namespace, tag name, and +styles, and some data about what sort of frame will be created. This +list of frame construction items is then analyzed to see whether +constructing frames based on it and inserting them at the chosen +insertion point will produce a valid frame tree. If it will not, the +frame constructor either fixes up the list of frame construction items +so that the resulting frame tree would be valid or throws away the list +of frame construction items and requests the destruction and re-creation +of the frame for the parent element so that it has a chance to create a +list of frame construction items that it `<em>`{=html}can`</em>`{=html} +fix up. + +Once the frame constructor has a list of frame construction items and an +insertion point that would lead to a valid frame tree, it goes ahead and +creates frames based on those items. Creation of a non-leaf frame +recursively attempts to create frames for the children of that frame\'s +element, so in effect frames are created in a depth-first traversal of +the content tree. + +The vast majority of the code in the frame constructor, therefore, falls +into one of these categories: + +- Code to determine the correct insertion point in the frame tree for + new frames. +- Code to create, for a given content node, frame construction items. + This involves some searches through static data tables for metadata + about the frame to be created. +- Code to analyze the list of frame construction items. +- Code to fix up the list of frame construction items. +- Code to create frames from frame construction items. + +Code: +[layout/base/nsCSSFrameConstructor.h](http://dxr.mozilla.org/mozilla-central/source/layout/base/nsCSSFrameConstructor.h) +and +[layout/base/nsCSSFrameConstructor.cpp](http://dxr.mozilla.org/mozilla-central/source/layout/base/nsCSSFrameConstructor.cpp) + +## Physical Sizes vs. Logical Sizes + +TODO: Discuss inline-size (typically width) and block size (typically +height), writing modes, and the various logical vs. physical size/rect +types. + +## Reflow + +Reflow is the process of computing the positions and sizes of frames. +(After all, frames represent rectangles, and at some point we need to +figure out exactly \*what\* rectangle.) Reflow is done recursively, with +each frame\'s Reflow method calling the Reflow methods on that frame\'s +descendants. + +In many cases, the correct results are defined by CSS specifications +(particularly [CSS 2.1](http://www.w3.org/TR/CSS21/visudet.html)). In +some cases, the details are not defined by CSS, though in some (but not +all) of those cases we are constrained by Web compatibility. When the +details are defined by CSS, however, the code to compute the layout is +generally structured somewhat differently from the way it is described +in the CSS specifications, since the CSS specifications are generally +written in terms of constraints, whereas our layout code consists of +algorithms optimized for incremental recomputation. + +The reflow generally starts from the root of the frame tree, though some +other types of frame can act as \"reflow roots\" and start a reflow from +them (nsTextControlFrame is one example; see the +[NS\_FRAME\_REFLOW\_ROOT](https://searchfox.org/mozilla-central/search?q=symbol:E_%3CT_nsFrameState%3E_NS_FRAME_REFLOW_ROOT&redirect=true) +frame state bit). Reflow roots must obey the invariant that a change +inside one of their descendants never changes their rect or overflow +areas (though currently scrollbars are reflow roots but don\'t quite +obey this invariant). + +In many cases, we want to reflow a part of the frame tree, and we want +this reflow to be efficient. For example, when content is added or +removed from the document tree or when styles change, we want the amount +of work we need to redo to be proportional to the amount of content. We +also want to efficiently handle a series of changes to the same content. + +To do this, we maintain two bits on frames: +[NS\_FRAME\_IS\_DIRTY](https://searchfox.org/mozilla-central/search?q=symbol:E_%3CT_nsFrameState%3E_NS_FRAME_IS_DIRTY&redirect=true) +indicates that a frame and all of its descendants require reflow. +[NS\_FRAME\_HAS\_DIRTY\_CHILDREN](https://searchfox.org/mozilla-central/search?q=symbol:E_%3CT_nsFrameState%3E_NS_FRAME_HAS_DIRTY_CHILDREN&redirect=true) +indicates that a frame has a descendant that is dirty or has had a +descendant removed (i.e., that it has a child that has +NS\_FRAME\_IS\_DIRTY or NS\_FRAME\_HAS\_DIRTY\_CHILDREN or it had a +child removed). These bits allow coalescing of multiple updates; this +coalescing is done in PresShell, which tracks the set of reflow roots +that require reflow. The bits are set during calls to +[PresShell::FrameNeedsReflow](https://searchfox.org/mozilla-central/search?q=PresShell%3A%3AFrameNeedsReflow&path=) +and are cleared during reflow. + +The layout algorithms used by many of the frame classes are those +specified in CSS, which are based on the traditional document formatting +model, where widths are input and heights are output. + +In some cases, however, widths need to be determined based on the +content. This depends on two *intrinsic widths*: the minimum intrinsic +width (see +[nsIFrame::GetMinISize](https://searchfox.org/mozilla-central/search?q=nsIFrame%3A%3AGetMinISize&path=)) +and the preferred intrinsic width (see +[nsIFrame::GetPrefISize](https://searchfox.org/mozilla-central/search?q=nsIFrame%3A%3AGetPrefISize&path=)). +The concept of what these widths represent is best explained by +describing what they are on a paragraph containing only text: in such a +paragraph the minimum intrinsic width is the width of the longest word, +and the preferred intrinsic width is the width of the entire paragraph +laid out on one line. + +Intrinsic widths are invalidated separately from the dirty bits +described above. When a caller informs the pres shell that a frame needs +reflow (PresShell::FrameNeedsReflow), it passes one of three options: + +- eResize indicates that no intrinsic widths are dirty +- eTreeChange indicates that intrinsic widths on it and its ancestors + are dirty (which happens, for example, if new children are added to + it) +- eStyleChange indicates that intrinsic widths on it, its ancestors, + and its descendants are dirty (for example, if the font-size + changes) + +Reflow is the area where the XUL frame classes (those that inherit from +nsBoxFrame or nsLeafBoxFrame) are most different from the rest. Instead +of using nsIFrame::Reflow, they do their layout computations using +intrinsic size methods called GetMinSize, GetPrefSize, and GetMaxSize +(which report intrinsic sizes in two dimensions) and a final layout +method called Layout. In many cases these methods defer some of the +computation to a separate object called a layout manager. + +When an individual frame\'s Reflow method is called, most of the input +is provided on an object called ReflowInput and the output is filled in +to an object called ReflowOutput. After reflow, the caller (usually the +parent) is responsible for setting the frame\'s size based on the +metrics reported. (This can make some computations during reflow +difficult, since the new size is found in either the reflow state or the +metrics, but the frame\'s size is still the old size. However, it\'s +useful for invalidating the correct areas that need to be repainted.) + +One major difference worth noting is that in XUL layout, the size of the +child is set prior to its parent calling its Layout method. (Once +invalidation uses display lists and is no longer tangled up in Reflow, +it may be worth switching non-XUL layout to work this way as well.) + +## Painting + +TODO: display lists (and event handling) + +TODO: layers + +## Pagination + +Pagination (also known as fragmentation) is a concept used in printing, +print-preview, and multicolumn layout. + +### Continuations in the Frame Tree + +To render a DOM node, represented as `nsIContent` object, Gecko creates +zero or more frames (`nsIFrame` objects). Each frame represents a +rectangular area usually corresponding to the node\'s CSS box as +described by the CSS specs. Simple elements are often representable with +exactly one frame, but sometimes an element needs to be represented with +more than one frame. For example, text breaking across lines: + + xxxxxx AAAA + AAA xxxxxxx + +The A element is a single DOM node but obviously a single rectangular +frame isn\'t going to represent its layout precisely. + +Similarly, consider text breaking across pages: + + | BBBBBBBBBB | + | BBBBBBBBBB | + +------------+ + + +------------+ + | BBBBBBBBBB | + | BBBBBBBBBB | + | | + +Again, a single rectangular frame cannot represent the layout of the +node. Columns are similar. + +Another case where a single DOM node is represented by multiple frames +is when a text node contains bidirectional text (e.g. both Hebrew and +English text). In this case, the text node and its inline ancestors are +split so that each frame contains only unidirectional text. + +The first frame for an element is called the **primary frame**. The +other frames are called **continuation frames**. Primary frames are +created by `nsCSSFrameConstructor` in response to content insertion +notifications. Continuation frames are created during bidi resolution, +and during reflow, when reflow detects that a content element cannot be +fully laid out within the constraints assigned (e.g., when inline text +will not fit within a particular width constraint, or when a block +cannot be laid out within a particular height constraint). + +Continuation frames created during reflow are called \"fluid\" +continuations (or \"in-flows\"). Other continuation frames (currently, +those created during bidi resolution), are, in contrast, \"non-fluid\". +The `NS_FRAME_IS_FLUID_CONTINUATION` state bit indicates whether a +continuation frame is fluid or not. + +The frames for an element are put in a doubly-linked list. The links are +accessible via `nsIFrame::GetNextContinuation` and +`nsIFrame::GetPrevContinuation`. If only fluid continuations are to be +accessed, `nsIFrame::GetNextInFlow` and `nsIFrame::GetPrevInFlow` are +used instead. + +The following diagram shows the relationship between the original frame +tree considering just primary frames, and a possible layout with +breaking and continuations: + + Original frame tree Frame tree with A broken into three parts + Root Root + | / | \ + A A1 A2 A3 + / \ / | | | + B C B C1 C2 C3 + | /|\ | | | \ | + D E F G D E F G1 G2 + +Certain kinds of frames create multiple child frames for the same +content element: + +- `nsPageSequenceFrame` creates multiple page children, each one + associated with the entire document, separated by page breaks +- `nsColumnSetFrame` creates multiple block children, each one + associated with the column element, separated by column breaks +- `nsBlockFrame` creates multiple inline children, each one associated + with the same inline element, separated by line breaks, or by + changes in text direction +- `nsTableColFrame` creates non-fluid continuations for itself if it + has span=\"N\" and N \> 1 +- If a block frame is a multi-column container and has + `column-span:all` children, it creates multiple `nsColumnSetFrame` + children, which are linked together as non-fluid continuations. + Similarly, if a block frame is within a multi-column formatting + context and has `column-span:all` children, it is chopped into + several flows, which are linked together as non-fluid continuations + as well. See documentation and example frame trees in + [`nsCSSFrameConstructor::ConstructBlock()`](https://searchfox.org/mozilla-central/rev/d24696b5abaf9fb75f7985952eab50d5f4ed52ac/layout/base/nsCSSFrameConstructor.cpp#10431). + +#### Overflow Container Continuations + +Sometimes the content of a frame needs to break across pages even though +the frame itself is complete. This usually happens if an element with +fixed height has overflow that doesn\'t fit on one page. In this case, +the completed frame is \"overflow incomplete\", and special +continuations are created to hold its overflow. These continuations are +called \"overflow containers\". They are invisible, and are kept on a +special list in their parent. See documentation in +[nsContainerFrame.h](https://searchfox.org/mozilla-central/source/layout/generic/nsContainerFrame.h) +and example trees in [bug 379349 comment +3](https://bugzilla.mozilla.org/show_bug.cgi?id=379349#c3). + +This infrastructure was extended in [bug +154892](https://bugzilla.mozilla.org/show_bug.cgi?id=154892) to also +manage continuations for absolutely-positioned frames. + +#### Relationship of continuations to frame tree structure + +It is worth emphasizing two points about the relationship of the +prev-continuation / next-continuation linkage to the existing frame tree +structure. + +First, if you want to traverse the frame tree or a subtree thereof to +examine all the frames once, you do `<em>`{=html}not`</em>`{=html} want +to traverse next-continuation links. All continuations are reachable by +traversing the `GetNextSibling` links from the result of `GetFirstChild` +for all child lists. + +Second, the following property holds: + +- Consider two frames F1 and F2 where F1\'s next-continuation is F2 + and their respective parent frames are P1 and P2. Then either P1\'s + next continuation is P2, or P1 == P2, because P is responsible for + breaking F1 and F2. + +In other words, continuations are sometimes siblings of each other, and +sometimes not. If their parent content was broken at the same point, +then they are not siblings, since they are children of different +continuations of the parent. So in the frame tree for the markup + +` <p>This is <b><i>some <br/>text</i></b>.</p>` + +the two continuations for the `b` element are siblings (unless the line +break is also a page break), but the two continuations for the `i` +element are not. + +There is an exception to that property when F1 is a first-in-flow float +placeholder. In that case F2\'s parent will be the next-in-flow of F1\'s +containing block. + +### Reflow statuses + +The aStatus argument of Reflow reflects that. `IsComplete()` means that +we reflowed all the content and no more next-in-flows are needed. At +that point there may still be next in flows, but the parent will delete +them. `IsIncomplete()` means \"some content did not fit in this frame\". +`IsOverflowIncomplete()` means that the frame is itself complete, but +some of its content didn\'t fit: this triggers the creation of overflow +containers for the frame\'s continuations. `IsIncomplete()` and +`NextInFlowNeedsReflow()` means \"some content did not fit in this frame +AND it must be reflowed\". These values are defined and documented in +[nsIFrame.h](https://searchfox.org/mozilla-central/source/layout/generic/nsIFrame.h) +(search for \"Reflow status\"). + +### Dynamic Reflow Considerations + +When we reflow a frame F with fluid continuations, two things can +happen: + +- Some child frames do not fit in the passed-in width or height + constraint. These frames must be \"pushed\" to F\'s next-in-flow. If + F has no next-in-flow, we must create one under F\'s parent\'s + next-in-flow \-\-- or if F\'s parent is managing the breaking of F, + then we create F\'s next in flow directly under F\'s parent. If F is + a block, it pushes overflowing child frames to its \"overflow\" + child list and forces F\'s next in flow to be reflowed. When we + reflow a block, we pull the child frames from the prev-in-flow\'s + overflow list into the current frame. +- All child frames fit in the passed-in width or height constraint. + Then child frames must be \"pulled\" from F\'s next-in-flow to fill + in the available space. If F\'s next-in-flow becomes empty, we may + be able to delete it. + +In both of these situations we might end up with a frame F containing +two child frames, one of which is a continuation of the other. This is +incorrect. We might also create holes, where there are frames P1 P2 and +P3, P1 has child F1 and P3 has child F2, but P2 has no F child. + +A strategy for avoiding these issues is this: When pulling a frame F2 +from parent P2 to prev-in-flow P1, if F2 is a breakable container, then: + +- If F2 has no prev-in-flow F1 in P1, then create a new primary frame + F1 in P1 for F2\'s content, with F2 as its next-in-flow. +- Pull children from F2 to F1 until F2 is empty or we run out of + space. If F2 goes empty, pull from the next non-empty next-in-flow. + Empty continuations with no next-in-flows can be deleted. + +When pushing a frame F1 from parent P1 to P2, where F1 has a +next-in-flow F2 (which must be a child of P2): + +- Merge F2 into F1 by moving all F2\'s children into F1, then deleting + F2 + +For inline frames F, we have our own custom strategy that coalesces +adjacent inline frames. This need not change. + +We do need to implement this strategy when F is a normal in-flow block, +a floating block, and eventually an absolutely positioned block. diff --git a/layout/docs/StyleSystemOverview.md b/layout/docs/StyleSystemOverview.md new file mode 100644 index 0000000000..bed5fd057f --- /dev/null +++ b/layout/docs/StyleSystemOverview.md @@ -0,0 +1,173 @@ +# Style System Overview + +## Quantum CSS (Stylo) + +Starting with Firefox 57 and later, Gecko makes use of the parallel +style system written in Rust that comes from Servo. There\'s an +[overview](https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/) +of this with graphics to help explain what\'s going on. The [Servo +wiki](https://github.com/servo/servo/wiki/Layout-Overview) has some more +details. + +## Gecko + +The rest of the style section section describes the Gecko style system +used in Firefox 56 and earlier. Some bits may still apply, but it likely +needs revising. + +In order to display the content, Gecko needs to compute the styles +relevant to each DOM node. It does this based on the model described in +the CSS specifications: this model applies to style specified in CSS +(e.g. by a \'style\' element, an \'xml-stylesheet\' processing +instruction or a \'style\' attribute), style specified by presentation +attributes, and the default style specified by our own user agent style +sheets. There are two major sets of data structures within the style +system: + +- first, data structures that represent sources of style data, such as + CSS style sheets or data from stylistic HTML attributes +- second, data structures that represent computed style for a given + DOM node. + +These sets of data structures are mostly distinct (for example, they +store values in different ways). + +The loading of CSS style sheets from the network is managed by the [CSS +loader](https://dxr.mozilla.org/mozilla-central/source/layout/style/Loader.h); +they are then tokenized by the [CSS +scanner](https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSScanner.h) +and parsed by the [CSS +parser](https://dxr.mozilla.org/mozilla-central/source/layout/style/nsCSSParser.h). +Those that are attached to the document also expose APIs to script that +are known as the CSS Object Model, or CSSOM. + +The style sheets that apply to a document are managed by a class called +the [style +set](https://dxr.mozilla.org/mozilla-central/source/layout/style/nsStyleSet.h). +The style set interacts with the different types of style sheets +(representing CSS style sheets, presentational attributes, and \'style\' +attributes) through two interfaces: +[nsIStyleSheet](http://dxr.mozilla.org/mozilla-central/source/layout/style/nsIStyleSheet.h) +for basic management of style sheets and +[nsIStyleRuleProcessor](http://dxr.mozilla.org/mozilla-central/source/layout/style/nsIStyleRuleProcessor.h) +for getting the style data out of them. Usually the same object +implements both interfaces, except in the most important case, CSS style +sheets, where there is a single rule processor for all of the CSS style +sheets in each origin (user/UA/author) of the CSS cascade. + +The computed style data for an element/frame are exposed to the rest of +Gecko through the class mozilla::ComputedStyle (previously called +nsStyleContext). Rather than having a member variable for each CSS +property, it breaks up the properties into groups of related properties +called style structs. These style structs obey the rule that all of the +properties in a single struct either inherit by default (what the CSS +specifications call \"Inherited: yes\" in the definition of properties; +we call these inherited structs) or all are not inherited by default (we +call these reset structs). Separating the properties in this way +improves the ability to share the structs between similar ComputedStyle +objects and reduce the amount of memory needed to store the style data. +The ComputedStyle API exposes a method for getting each struct, so +you\'ll see code like `sc->GetStyleText()->mTextAlign` for getting the +value of the text-align CSS property. (Frames (see the Layout section +below) also have the same GetStyle\* methods, which just forward the +call to the frame\'s ComputedStyle.) + +The ComputedStyles form a tree structure, in a shape somewhat like the +content tree (except that we coalesce identical sibling ComputedStyles +rather than keeping two of them around; if the parents have been +coalesced then this can apply recursively and coalasce cousins, etc.; we +do not coalesce parent/child ComputedStyles). The parent of a +ComputedStyle has the style data that the ComputedStyle inherits from +when CSS inheritance occurs. This means that the parent of the +ComputedStyle for a DOM element is generally the ComputedStyle for that +DOM element\'s parent, since that\'s how CSS says inheritance works. + +The process of turning the style sheets into computed style data goes +through three main steps, the first two of which closely relate to the +[nsIStyleRule](http://dxr.mozilla.org/mozilla-central/source/layout/style/nsIStyleRule.h) +interface, which represents an immutable source of style data, +conceptually representing (and for CSS style rules, directly storing) a +set of property:value pairs. (It is similar to the idea of a CSS style +rule, except that it is immutable; this immutability allows for +significant optimization. When a CSS style rule is changed through +script, we create a new style rule.) + +The first step of going from style sheets to computed style data is +finding the ordered sequence of style rules that apply to an element. +The order represents which rules override which other rules: if two +rules have a value for the same property, the higher ranking one wins. +(Note that there\'s another difference from CSS style rules: +declarations with !important are represented using a separate style +rule.) This is done by calling one of the +nsIStyleRuleProcessor::RulesMatching methods. The ordered sequence is +stored in a [trie](http://en.wikipedia.org/wiki/Trie) called the rule +tree: the path from the root of the rule tree to any (leaf or non-leaf) +node in the rule tree represents a sequence of rules, with the highest +ranking farthest from the root. Each rule node (except for the root) has +a pointer to a rule, but since a rule may appear in many sequences, +there are sometimes many rule nodes pointing to the same rule. Once we +have this list we create a ComputedStyle (or find an appropriate +existing sibling) with the correct parent pointer (for inheritance) and +rule node pointer (for the list of rules), and a few other pieces of +information (like the pseudo-element). + +The second step of going from style sheets to computed style data is +getting the winning property:value pairs from the rules. (This only +provides property:value pairs for some of the properties; the remaining +properties will fall back to inheritance or to their initial values +depending on whether the property is inherited by default.) We do this +step (and the third) for each style struct, the first time it is needed. +This is done in nsRuleNode::WalkRuleTree, where we ask each style rule +to fill in its property:value pairs by calling its MapRuleInfoInto +function. When called, the rule fills in only those pairs that haven\'t +been filled in already, since we\'re calling from the highest priority +rule to the lowest (since in many cases this allows us to stop before +going through the whole list, or to do partial computation that just +adds to data cached higher in the rule tree). + +The third step of going from style sheets to computed style data (which +various caching optimizations allow us to skip in many cases) is +actually doing the computation; this generally means we transform the +style data into the data type described in the \"Computed Value\" line +in the property\'s definition in the CSS specifications. This +transformation happens in functions called nsRuleNode::Compute\*Data, +where the \* in the middle represents the name of the style struct. This +is where the transformation from the style sheet value storage format to +the computed value storage format happens. + +Once we have the computed style data, we then store it: if a style +struct in the computed style data doesn\'t depend on inherited values or +on data from other style structs, then we can cache it in the rule tree +(and then reuse it, without recomputing it, for any ComputedStyles +pointing to that rule node). Otherwise, we store it on the ComputedStyle +(in which case it may be shared with the ComputedStyle\'s descendant +ComputedStyles). This is where keeping inherited and non-inherited +properties separate is useful: in the common case of relatively few +properties being specified, we can generally cache the non-inherited +structs in the rule tree, and we can generally share the inherited +structs up and down the ComputedStyle tree. + +The ownership models in style sheet structures are a mix of reference +counted structures (for things accessible from script) and directly +owned structures. ComputedStyles are reference counted, and own their +parents (from which they inherit), and rule nodes are garbage collected +with a simple mark and sweep collector (which often never needs to run). + +- code: + [layout/style/](http://dxr.mozilla.org/mozilla-central/source/layout/style/), + where most files have useful one line descriptions at the top that + show up in DXR +- Bugzilla: Style System (CSS) +- specifications + - [CSS 2.1](http://www.w3.org/TR/CSS21/) + - [CSS 2010, listing stable css3 + modules](http://www.w3.org/TR/css-2010/) + - [CSS WG editors drafts](http://dev.w3.org/csswg/) (often more + current, but sometimes more unstable than the drafts on the + technical reports page) + - [Preventing attacks on a user\'s history through CSS :visited + selectors](http://dbaron.org/mozilla/visited-privacy) +- documentation + - [style system + documentation](http://www-archive.mozilla.org/newlayout/doc/style-system.html) + (somewhat out of date) diff --git a/layout/docs/index.rst b/layout/docs/index.rst index 16d9df8b05..39455a1075 100644 --- a/layout/docs/index.rst +++ b/layout/docs/index.rst @@ -1,8 +1,9 @@ -Layout & CSS -============ +Style system (CSS) & Layout +=========================== -Here contains design documents for the Gecko's style system and layout engine. -They live in the mozilla-central repository under layout/docs directory. +Here contains the overview and design documents for Firefox's layout engine and +style system. They live in the mozilla-central repository under `layout/docs +<https://searchfox.org/mozilla-central/source/layout/docs>`__ directory. `Layout page <https://wiki.mozilla.org/Platform/Layout>`__ on mozilla wiki contains general information about layout and the layout team at Mozilla. @@ -10,6 +11,9 @@ contains general information about layout and the layout team at Mozilla. .. toctree:: :maxdepth: 1 - AccessibleCaret + StyleSystemOverview + LayoutOverview + DynamicChangeHandling Reftest LayoutDebugger + AccessibleCaret diff --git a/layout/forms/nsCheckboxRadioFrame.cpp b/layout/forms/nsCheckboxRadioFrame.cpp index e2b8541613..4bf15099aa 100644 --- a/layout/forms/nsCheckboxRadioFrame.cpp +++ b/layout/forms/nsCheckboxRadioFrame.cpp @@ -52,18 +52,12 @@ void nsCheckboxRadioFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, /* virtual */ nscoord nsCheckboxRadioFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - result = StyleDisplay()->HasAppearance() ? DefaultSize() : 0; - return result; + return StyleDisplay()->HasAppearance() ? DefaultSize() : 0; } /* virtual */ nscoord nsCheckboxRadioFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - result = StyleDisplay()->HasAppearance() ? DefaultSize() : 0; - return result; + return StyleDisplay()->HasAppearance() ? DefaultSize() : 0; } /* virtual */ @@ -76,13 +70,9 @@ LogicalSize nsCheckboxRadioFrame::ComputeAutoSize( if (!StyleDisplay()->HasAppearance()) { return size; } - - // Note: this call always set the BSize to NS_UNCONSTRAINEDSIZE. - size = nsAtomicContainerFrame::ComputeAutoSize( + return nsAtomicContainerFrame::ComputeAutoSize( aRC, aWM, aCBSize, aAvailableISize, aMargin, aBorderPadding, aSizeOverrides, aFlags); - size.BSize(aWM) = DefaultSize(); - return size; } Maybe<nscoord> nsCheckboxRadioFrame::GetNaturalBaselineBOffset( @@ -125,7 +115,6 @@ void nsCheckboxRadioFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsCheckboxRadioFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_FRAME_TRACE( NS_FRAME_TRACE_CALLS, @@ -133,10 +122,13 @@ void nsCheckboxRadioFrame::Reflow(nsPresContext* aPresContext, aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight())); const auto wm = aReflowInput.GetWritingMode(); - aDesiredSize.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm)); - + const auto contentBoxSize = + aReflowInput.ComputedSizeWithBSizeFallback([&] { return DefaultSize(); }); + aDesiredSize.SetSize( + wm, + contentBoxSize + aReflowInput.ComputedLogicalBorderPadding(wm).Size(wm)); if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) { - float inflation = nsLayoutUtils::FontSizeInflationFor(this); + const float inflation = nsLayoutUtils::FontSizeInflationFor(this); aDesiredSize.Width() *= inflation; aDesiredSize.Height() *= inflation; } diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp index 1d4ff15b4f..98486255d0 100644 --- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -190,14 +190,12 @@ nscoord nsComboboxControlFrame::GetIntrinsicISize(gfxContext* aRenderingContext, nscoord nsComboboxControlFrame::GetMinISize(gfxContext* aRenderingContext) { nscoord minISize; - DISPLAY_MIN_INLINE_SIZE(this, minISize); minISize = GetIntrinsicISize(aRenderingContext, IntrinsicISizeType::MinISize); return minISize; } nscoord nsComboboxControlFrame::GetPrefISize(gfxContext* aRenderingContext) { nscoord prefISize; - DISPLAY_PREF_INLINE_SIZE(this, prefISize); prefISize = GetIntrinsicISize(aRenderingContext, IntrinsicISizeType::PrefISize); return prefISize; diff --git a/layout/forms/nsDateTimeControlFrame.cpp b/layout/forms/nsDateTimeControlFrame.cpp index b19f787dbc..d8cdf5f277 100644 --- a/layout/forms/nsDateTimeControlFrame.cpp +++ b/layout/forms/nsDateTimeControlFrame.cpp @@ -35,33 +35,21 @@ nsDateTimeControlFrame::nsDateTimeControlFrame(ComputedStyle* aStyle, : nsContainerFrame(aStyle, aPresContext, kClassID) {} nscoord nsDateTimeControlFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - nsIFrame* kid = mFrames.FirstChild(); - if (kid) { // display:none? - result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, kid, - IntrinsicISizeType::MinISize); - } else { - result = 0; + if (!kid) { + return 0; } - - return result; + return nsLayoutUtils::IntrinsicForContainer(aRenderingContext, kid, + IntrinsicISizeType::MinISize); } nscoord nsDateTimeControlFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - nsIFrame* kid = mFrames.FirstChild(); - if (kid) { // display:none? - result = nsLayoutUtils::IntrinsicForContainer( - aRenderingContext, kid, IntrinsicISizeType::PrefISize); - } else { - result = 0; + if (!kid) { + return 0; } - - return result; + return nsLayoutUtils::IntrinsicForContainer(aRenderingContext, kid, + IntrinsicISizeType::PrefISize); } Maybe<nscoord> nsDateTimeControlFrame::GetNaturalBaselineBOffset( @@ -78,7 +66,6 @@ void nsDateTimeControlFrame::Reflow(nsPresContext* aPresContext, MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsDateTimeControlFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_FRAME_TRACE( NS_FRAME_TRACE_CALLS, diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index c96b293e82..2648cb4dd3 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -58,7 +58,7 @@ nsRect nsFieldSetFrame::VisualBorderRectRelativeToSelf() const { auto legendMargin = legend->GetLogicalUsedMargin(wm); nscoord legendStartMargin = legendMargin.BStart(wm); nscoord legendEndMargin = legendMargin.BEnd(wm); - nscoord border = GetUsedBorder().Side(wm.PhysicalSide(eLogicalSideBStart)); + nscoord border = GetUsedBorder().Side(wm.PhysicalSide(LogicalSide::BStart)); // Calculate the offset from the border area block-axis start edge needed to // center-align our border with the legend's border-box (in the block-axis). nscoord off = (legendStartMargin + legendSize / 2) - border / 2; @@ -348,19 +348,11 @@ nscoord nsFieldSetFrame::GetIntrinsicISize(gfxContext* aRenderingContext, } nscoord nsFieldSetFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result = 0; - DISPLAY_MIN_INLINE_SIZE(this, result); - - result = GetIntrinsicISize(aRenderingContext, IntrinsicISizeType::MinISize); - return result; + return GetIntrinsicISize(aRenderingContext, IntrinsicISizeType::MinISize); } nscoord nsFieldSetFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result = 0; - DISPLAY_PREF_INLINE_SIZE(this, result); - - result = GetIntrinsicISize(aRenderingContext, IntrinsicISizeType::PrefISize); - return result; + return GetIntrinsicISize(aRenderingContext, IntrinsicISizeType::PrefISize); } /* virtual */ @@ -372,7 +364,6 @@ void nsFieldSetFrame::Reflow(nsPresContext* aPresContext, MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_WARNING_ASSERTION(aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE, "Should have a precomputed inline-size!"); diff --git a/layout/forms/nsHTMLButtonControlFrame.cpp b/layout/forms/nsHTMLButtonControlFrame.cpp index cb24ccbfb9..5201e420e2 100644 --- a/layout/forms/nsHTMLButtonControlFrame.cpp +++ b/layout/forms/nsHTMLButtonControlFrame.cpp @@ -51,6 +51,7 @@ void nsHTMLButtonControlFrame::Init(nsIContent* aContent, } NS_QUERYFRAME_HEAD(nsHTMLButtonControlFrame) + NS_QUERYFRAME_ENTRY(nsHTMLButtonControlFrame) NS_QUERYFRAME_ENTRY(nsIFormControlFrame) NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) @@ -238,29 +239,21 @@ void nsHTMLButtonControlFrame::BuildDisplayList( } nscoord nsHTMLButtonControlFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); if (Maybe<nscoord> containISize = ContainIntrinsicISize()) { - result = *containISize; - } else { - nsIFrame* kid = mFrames.FirstChild(); - result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, kid, - IntrinsicISizeType::MinISize); + return *containISize; } - return result; + nsIFrame* kid = mFrames.FirstChild(); + return nsLayoutUtils::IntrinsicForContainer(aRenderingContext, kid, + IntrinsicISizeType::MinISize); } nscoord nsHTMLButtonControlFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); if (Maybe<nscoord> containISize = ContainIntrinsicISize()) { - result = *containISize; - } else { - nsIFrame* kid = mFrames.FirstChild(); - result = nsLayoutUtils::IntrinsicForContainer( - aRenderingContext, kid, IntrinsicISizeType::PrefISize); + return *containISize; } - return result; + nsIFrame* kid = mFrames.FirstChild(); + return nsLayoutUtils::IntrinsicForContainer(aRenderingContext, kid, + IntrinsicISizeType::PrefISize); } void nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext, @@ -269,7 +262,6 @@ void nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); // Reflow the child diff --git a/layout/forms/nsImageControlFrame.cpp b/layout/forms/nsImageControlFrame.cpp index 17e3893a2f..8207a427f1 100644 --- a/layout/forms/nsImageControlFrame.cpp +++ b/layout/forms/nsImageControlFrame.cpp @@ -97,7 +97,6 @@ void nsImageControlFrame::Reflow(nsPresContext* aPresContext, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsImageControlFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); return nsImageFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus); diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index 44ce9fde13..3020e99888 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -230,34 +230,30 @@ nscoord nsListControlFrame::CalcBSizeOfARow() { } nscoord nsListControlFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - // Always add scrollbar inline sizes to the pref-inline-size of the // scrolled content. Combobox frames depend on this happening in the // dropdown, and standalone listboxes are overflow:scroll so they need // it too. WritingMode wm = GetWritingMode(); Maybe<nscoord> containISize = ContainIntrinsicISize(); - result = containISize ? *containISize - : GetScrolledFrame()->GetPrefISize(aRenderingContext); + nscoord result = containISize + ? *containISize + : GetScrolledFrame()->GetPrefISize(aRenderingContext); LogicalMargin scrollbarSize(wm, GetDesiredScrollbarSizes()); result = NSCoordSaturatingAdd(result, scrollbarSize.IStartEnd(wm)); return result; } nscoord nsListControlFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - // Always add scrollbar inline sizes to the min-inline-size of the // scrolled content. Combobox frames depend on this happening in the // dropdown, and standalone listboxes are overflow:scroll so they need // it too. WritingMode wm = GetWritingMode(); Maybe<nscoord> containISize = ContainIntrinsicISize(); - result = containISize ? *containISize - : GetScrolledFrame()->GetMinISize(aRenderingContext); + nscoord result = containISize + ? *containISize + : GetScrolledFrame()->GetMinISize(aRenderingContext); LogicalMargin scrollbarSize(wm, GetDesiredScrollbarSizes()); result += scrollbarSize.IStartEnd(wm); diff --git a/layout/forms/nsMeterFrame.cpp b/layout/forms/nsMeterFrame.cpp index a72a6816d5..31ef52a000 100644 --- a/layout/forms/nsMeterFrame.cpp +++ b/layout/forms/nsMeterFrame.cpp @@ -82,7 +82,6 @@ void nsMeterFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsMeterFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_ASSERTION(mBarDiv, "Meter bar div must exist!"); @@ -93,13 +92,19 @@ void nsMeterFrame::Reflow(nsPresContext* aPresContext, nsIFrame* barFrame = mBarDiv->GetPrimaryFrame(); NS_ASSERTION(barFrame, "The meter frame should have a child with a frame!"); - ReflowBarFrame(barFrame, aPresContext, aReflowInput, aStatus); - const auto wm = aReflowInput.GetWritingMode(); - aDesiredSize.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm)); - + const auto contentBoxSize = aReflowInput.ComputedSizeWithBSizeFallback([&] { + nscoord em = OneEmInAppUnits(); + return ResolvedOrientationIsVertical() == wm.IsVertical() ? em : 5 * em; + }); + aDesiredSize.SetSize( + wm, + contentBoxSize + aReflowInput.ComputedLogicalBorderPadding(wm).Size(wm)); aDesiredSize.SetOverflowAreasToDesiredBounds(); + + ReflowBarFrame(barFrame, aPresContext, aReflowInput, contentBoxSize, aStatus); ConsiderChildOverflow(aDesiredSize.mOverflowAreas, barFrame); + FinishAndStoreOverflow(&aDesiredSize); aStatus.Reset(); // This type of frame can't be split. @@ -108,14 +113,19 @@ void nsMeterFrame::Reflow(nsPresContext* aPresContext, void nsMeterFrame::ReflowBarFrame(nsIFrame* aBarFrame, nsPresContext* aPresContext, const ReflowInput& aReflowInput, + const LogicalSize& aParentContentBoxSize, nsReflowStatus& aStatus) { bool vertical = ResolvedOrientationIsVertical(); - WritingMode wm = aBarFrame->GetWritingMode(); - LogicalSize availSize = aReflowInput.ComputedSize(wm); + const WritingMode wm = aBarFrame->GetWritingMode(); + const LogicalSize parentSizeInChildWM = + aParentContentBoxSize.ConvertTo(wm, aReflowInput.GetWritingMode()); + const nsSize parentPhysicalSize = parentSizeInChildWM.GetPhysicalSize(wm); + LogicalSize availSize = parentSizeInChildWM; availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; - ReflowInput reflowInput(aPresContext, aReflowInput, aBarFrame, availSize); + ReflowInput reflowInput(aPresContext, aReflowInput, aBarFrame, availSize, + Some(parentSizeInChildWM)); nscoord size = - vertical ? aReflowInput.ComputedHeight() : aReflowInput.ComputedWidth(); + vertical ? parentPhysicalSize.Height() : parentPhysicalSize.Width(); nscoord xoffset = aReflowInput.ComputedPhysicalBorderPadding().left; nscoord yoffset = aReflowInput.ComputedPhysicalBorderPadding().top; @@ -123,14 +133,13 @@ void nsMeterFrame::ReflowBarFrame(nsIFrame* aBarFrame, size = NSToCoordRound(size * meterElement->Position()); if (!vertical && wm.IsPhysicalRTL()) { - xoffset += aReflowInput.ComputedWidth() - size; + xoffset += parentPhysicalSize.Width() - size; } // The bar position is *always* constrained. if (vertical) { // We want the bar to begin at the bottom. - yoffset += aReflowInput.ComputedHeight() - size; - + yoffset += parentPhysicalSize.Height() - size; size -= reflowInput.ComputedPhysicalMargin().TopBottom() + reflowInput.ComputedPhysicalBorderPadding().TopBottom(); size = std::max(size, 0); @@ -169,39 +178,12 @@ nsresult nsMeterFrame::AttributeChanged(int32_t aNameSpaceID, return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); } -LogicalSize nsMeterFrame::ComputeAutoSize( - gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, - nscoord aAvailableISize, const LogicalSize& aMargin, - const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides, - ComputeSizeFlags aFlags) { - RefPtr<nsFontMetrics> fontMet = - nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f); - - const WritingMode wm = GetWritingMode(); - LogicalSize autoSize(wm); - autoSize.BSize(wm) = autoSize.ISize(wm) = - fontMet->Font().size.ToAppUnits(); // 1em - - if (ResolvedOrientationIsVertical() == wm.IsVertical()) { - autoSize.ISize(wm) *= 5; // 5em - } else { - autoSize.BSize(wm) *= 5; // 5em - } - - return autoSize.ConvertTo(aWM, wm); -} - nscoord nsMeterFrame::GetMinISize(gfxContext* aRenderingContext) { - RefPtr<nsFontMetrics> fontMet = - nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f); - - nscoord minISize = fontMet->Font().size.ToAppUnits(); // 1em - + nscoord minISize = OneEmInAppUnits(); if (ResolvedOrientationIsVertical() == GetWritingMode().IsVertical()) { // The orientation is inline - minISize *= 5; // 5em + minISize *= 5; } - return minISize; } diff --git a/layout/forms/nsMeterFrame.h b/layout/forms/nsMeterFrame.h index 4aa82c1384..7a15878beb 100644 --- a/layout/forms/nsMeterFrame.h +++ b/layout/forms/nsMeterFrame.h @@ -7,17 +7,15 @@ #ifndef nsMeterFrame_h___ #define nsMeterFrame_h___ -#include "mozilla/Attributes.h" #include "nsContainerFrame.h" #include "nsIAnonymousContentCreator.h" #include "nsCOMPtr.h" -#include "nsCSSPseudoElements.h" class nsMeterFrame final : public nsContainerFrame, public nsIAnonymousContentCreator { - typedef mozilla::dom::Element Element; + using Element = mozilla::dom::Element; public: NS_DECL_QUERYFRAME @@ -27,36 +25,25 @@ class nsMeterFrame final : public nsContainerFrame, virtual ~nsMeterFrame(); void Destroy(DestroyContext&) override; - - virtual void Reflow(nsPresContext* aCX, ReflowOutput& aDesiredSize, - const ReflowInput& aReflowInput, - nsReflowStatus& aStatus) override; + void Reflow(nsPresContext*, ReflowOutput&, const ReflowInput&, + nsReflowStatus&) override; #ifdef DEBUG_FRAME_DUMP - virtual nsresult GetFrameName(nsAString& aResult) const override { + nsresult GetFrameName(nsAString& aResult) const override { return MakeFrameName(u"Meter"_ns, aResult); } #endif // nsIAnonymousContentCreator - virtual nsresult CreateAnonymousContent( - nsTArray<ContentInfo>& aElements) override; - virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, - uint32_t aFilter) override; + nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override; + void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, + uint32_t aFilter) override; - virtual nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, - int32_t aModType) override; + nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, + int32_t aModType) override; - virtual mozilla::LogicalSize ComputeAutoSize( - gfxContext* aRenderingContext, mozilla::WritingMode aWM, - const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize, - const mozilla::LogicalSize& aMargin, - const mozilla::LogicalSize& aBorderPadding, - const mozilla::StyleSizeOverrides& aSizeOverrides, - mozilla::ComputeSizeFlags aFlags) override; - - virtual nscoord GetMinISize(gfxContext* aRenderingContext) override; - virtual nscoord GetPrefISize(gfxContext* aRenderingContext) override; + nscoord GetMinISize(gfxContext* aRenderingContext) override; + nscoord GetPrefISize(gfxContext* aRenderingContext) override; /** * Returns whether the frame and its child should use the native style. @@ -66,7 +53,9 @@ class nsMeterFrame final : public nsContainerFrame, protected: // Helper function which reflow the anonymous div frame. void ReflowBarFrame(nsIFrame* aBarFrame, nsPresContext* aPresContext, - const ReflowInput& aReflowInput, nsReflowStatus& aStatus); + const ReflowInput& aReflowInput, + const mozilla::LogicalSize& aParentContentBoxSize, + nsReflowStatus& aStatus); /** * The div used to show the meter bar. * @see nsMeterFrame::CreateAnonymousContent diff --git a/layout/forms/nsProgressFrame.cpp b/layout/forms/nsProgressFrame.cpp index 2f0d727473..1cf6bf1447 100644 --- a/layout/forms/nsProgressFrame.cpp +++ b/layout/forms/nsProgressFrame.cpp @@ -86,7 +86,6 @@ void nsProgressFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsProgressFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_ASSERTION(mBarDiv, "Progress bar div must exist!"); @@ -99,11 +98,18 @@ void nsProgressFrame::Reflow(nsPresContext* aPresContext, "need to call RegUnregAccessKey only for the first."); const auto wm = aReflowInput.GetWritingMode(); - aDesiredSize.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm)); + const auto contentBoxSize = aReflowInput.ComputedSizeWithBSizeFallback([&] { + nscoord em = OneEmInAppUnits(); + return ResolvedOrientationIsVertical() == wm.IsVertical() ? em : 10 * em; + }); + aDesiredSize.SetSize( + wm, + contentBoxSize + aReflowInput.ComputedLogicalBorderPadding(wm).Size(wm)); aDesiredSize.SetOverflowAreasToDesiredBounds(); - for (auto childFrame : PrincipalChildList()) { - ReflowChildFrame(childFrame, aPresContext, aReflowInput, aStatus); + for (nsIFrame* childFrame : PrincipalChildList()) { + ReflowChildFrame(childFrame, aPresContext, aReflowInput, contentBoxSize, + aStatus); ConsiderChildOverflow(aDesiredSize.mOverflowAreas, childFrame); } @@ -115,14 +121,19 @@ void nsProgressFrame::Reflow(nsPresContext* aPresContext, void nsProgressFrame::ReflowChildFrame(nsIFrame* aChild, nsPresContext* aPresContext, const ReflowInput& aReflowInput, + const LogicalSize& aParentContentBoxSize, nsReflowStatus& aStatus) { bool vertical = ResolvedOrientationIsVertical(); - WritingMode wm = aChild->GetWritingMode(); - LogicalSize availSize = aReflowInput.ComputedSize(wm); + const WritingMode wm = aChild->GetWritingMode(); + const LogicalSize parentSizeInChildWM = + aParentContentBoxSize.ConvertTo(wm, aReflowInput.GetWritingMode()); + const nsSize parentPhysicalSize = parentSizeInChildWM.GetPhysicalSize(wm); + LogicalSize availSize = parentSizeInChildWM; availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; - ReflowInput reflowInput(aPresContext, aReflowInput, aChild, availSize); + ReflowInput reflowInput(aPresContext, aReflowInput, aChild, availSize, + Some(parentSizeInChildWM)); nscoord size = - vertical ? aReflowInput.ComputedHeight() : aReflowInput.ComputedWidth(); + vertical ? parentPhysicalSize.Height() : parentPhysicalSize.Width(); nscoord xoffset = aReflowInput.ComputedPhysicalBorderPadding().left; nscoord yoffset = aReflowInput.ComputedPhysicalBorderPadding().top; @@ -135,7 +146,7 @@ void nsProgressFrame::ReflowChildFrame(nsIFrame* aChild, } if (!vertical && wm.IsPhysicalRTL()) { - xoffset += aReflowInput.ComputedWidth() - size; + xoffset += parentPhysicalSize.Width() - size; } // The bar size is fixed in these cases: @@ -148,8 +159,7 @@ void nsProgressFrame::ReflowChildFrame(nsIFrame* aChild, if (position != -1 || ShouldUseNativeStyle()) { if (vertical) { // We want the bar to begin at the bottom. - yoffset += aReflowInput.ComputedHeight() - size; - + yoffset += parentPhysicalSize.Height() - size; size -= reflowInput.ComputedPhysicalMargin().TopBottom() + reflowInput.ComputedPhysicalBorderPadding().TopBottom(); size = std::max(size, 0); @@ -161,10 +171,12 @@ void nsProgressFrame::ReflowChildFrame(nsIFrame* aChild, reflowInput.SetComputedWidth(size); } } else if (vertical) { - // For vertical progress bars, we need to position the bar specificly when + // For vertical progress bars, we need to position the bar specifically when // the width isn't constrained (position == -1 and !ShouldUseNativeStyle()) - // because aReflowInput.ComputedHeight() - size == 0. - yoffset += aReflowInput.ComputedHeight() - reflowInput.ComputedHeight(); + // because parentPhysiscalSize.Height() - size == 0. + // FIXME(emilio): This assumes that the bar's height is constrained, which + // seems like a wrong assumption? + yoffset += parentPhysicalSize.Height() - reflowInput.ComputedHeight(); } xoffset += reflowInput.ComputedPhysicalMargin().left; @@ -195,38 +207,11 @@ nsresult nsProgressFrame::AttributeChanged(int32_t aNameSpaceID, return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); } -LogicalSize nsProgressFrame::ComputeAutoSize( - gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, - nscoord aAvailableISize, const LogicalSize& aMargin, - const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides, - ComputeSizeFlags aFlags) { - const WritingMode wm = GetWritingMode(); - LogicalSize autoSize(wm); - autoSize.BSize(wm) = autoSize.ISize(wm) = - StyleFont() - ->mFont.size.ScaledBy(nsLayoutUtils::FontSizeInflationFor(this)) - .ToAppUnits(); // 1em - - if (ResolvedOrientationIsVertical() == wm.IsVertical()) { - autoSize.ISize(wm) *= 10; // 10em - } else { - autoSize.BSize(wm) *= 10; // 10em - } - - return autoSize.ConvertTo(aWM, wm); -} - nscoord nsProgressFrame::GetMinISize(gfxContext* aRenderingContext) { - RefPtr<nsFontMetrics> fontMet = - nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f); - - nscoord minISize = fontMet->Font().size.ToAppUnits(); // 1em - + nscoord minISize = OneEmInAppUnits(); if (ResolvedOrientationIsVertical() == GetWritingMode().IsVertical()) { - // The orientation is inline - minISize *= 10; // 10em + minISize *= 10; } - return minISize; } diff --git a/layout/forms/nsProgressFrame.h b/layout/forms/nsProgressFrame.h index 7d3618ee96..f8917ba4f2 100644 --- a/layout/forms/nsProgressFrame.h +++ b/layout/forms/nsProgressFrame.h @@ -18,8 +18,7 @@ enum class PseudoStyleType : uint8_t; class nsProgressFrame final : public nsContainerFrame, public nsIAnonymousContentCreator { - typedef mozilla::PseudoStyleType PseudoStyleType; - typedef mozilla::dom::Element Element; + using Element = mozilla::dom::Element; public: NS_DECL_QUERYFRAME @@ -30,38 +29,28 @@ class nsProgressFrame final : public nsContainerFrame, void Destroy(DestroyContext&) override; - virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, - const nsDisplayListSet& aLists) override; + void BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) override; - virtual void Reflow(nsPresContext* aCX, ReflowOutput& aDesiredSize, - const ReflowInput& aReflowInput, - nsReflowStatus& aStatus) override; + void Reflow(nsPresContext*, ReflowOutput&, const ReflowInput&, + nsReflowStatus&) override; #ifdef DEBUG_FRAME_DUMP - virtual nsresult GetFrameName(nsAString& aResult) const override { + nsresult GetFrameName(nsAString& aResult) const override { return MakeFrameName(u"Progress"_ns, aResult); } #endif // nsIAnonymousContentCreator - virtual nsresult CreateAnonymousContent( - nsTArray<ContentInfo>& aElements) override; - virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, - uint32_t aFilter) override; + nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override; + void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, + uint32_t aFilter) override; - virtual nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, - int32_t aModType) override; + nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, + int32_t aModType) override; - virtual mozilla::LogicalSize ComputeAutoSize( - gfxContext* aRenderingContext, mozilla::WritingMode aWM, - const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize, - const mozilla::LogicalSize& aMargin, - const mozilla::LogicalSize& aBorderPadding, - const mozilla::StyleSizeOverrides& aSizeOverrides, - mozilla::ComputeSizeFlags aFlags) override; - - virtual nscoord GetMinISize(gfxContext* aRenderingContext) override; - virtual nscoord GetPrefISize(gfxContext* aRenderingContext) override; + nscoord GetMinISize(gfxContext* aRenderingContext) override; + nscoord GetPrefISize(gfxContext* aRenderingContext) override; /** * Returns whether the frame and its child should use the native style. @@ -72,6 +61,7 @@ class nsProgressFrame final : public nsContainerFrame, // Helper function to reflow a child frame. void ReflowChildFrame(nsIFrame* aChild, nsPresContext* aPresContext, const ReflowInput& aReflowInput, + const mozilla::LogicalSize& aParentContentBoxSize, nsReflowStatus& aStatus); /** diff --git a/layout/forms/nsRangeFrame.cpp b/layout/forms/nsRangeFrame.cpp index 2cccff715a..3238d0372f 100644 --- a/layout/forms/nsRangeFrame.cpp +++ b/layout/forms/nsRangeFrame.cpp @@ -155,7 +155,6 @@ void nsRangeFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsRangeFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_ASSERTION(mTrackDiv, "::-moz-range-track div must exist!"); @@ -166,35 +165,17 @@ void nsRangeFrame::Reflow(nsPresContext* aPresContext, "need to call RegUnregAccessKey only for the first."); WritingMode wm = aReflowInput.GetWritingMode(); - nscoord computedBSize = aReflowInput.ComputedBSize(); - if (computedBSize == NS_UNCONSTRAINEDSIZE) { - computedBSize = 0; - } - const auto borderPadding = aReflowInput.ComputedLogicalBorderPadding(wm); - LogicalSize finalSize( - wm, aReflowInput.ComputedISize() + borderPadding.IStartEnd(wm), - computedBSize + borderPadding.BStartEnd(wm)); - aDesiredSize.SetSize(wm, finalSize); - - ReflowAnonymousContent(aPresContext, aDesiredSize, aReflowInput); - + const auto contentBoxSize = aReflowInput.ComputedSizeWithBSizeFallback([&] { + return IsInlineOriented() ? AutoCrossSize() + : OneEmInAppUnits() * MAIN_AXIS_EM_SIZE; + }); + aDesiredSize.SetSize( + wm, + contentBoxSize + aReflowInput.ComputedLogicalBorderPadding(wm).Size(wm)); aDesiredSize.SetOverflowAreasToDesiredBounds(); - nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame(); - if (trackFrame) { - ConsiderChildOverflow(aDesiredSize.mOverflowAreas, trackFrame); - } - - nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame(); - if (rangeProgressFrame) { - ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rangeProgressFrame); - } - - nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); - if (thumbFrame) { - ConsiderChildOverflow(aDesiredSize.mOverflowAreas, thumbFrame); - } - + ReflowAnonymousContent(aPresContext, aDesiredSize, contentBoxSize, + aReflowInput); FinishAndStoreOverflow(&aDesiredSize); MOZ_ASSERT(aStatus.IsEmpty(), "This type of frame can't be split."); @@ -202,111 +183,71 @@ void nsRangeFrame::Reflow(nsPresContext* aPresContext, void nsRangeFrame::ReflowAnonymousContent(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, + const LogicalSize& aContentBoxSize, const ReflowInput& aReflowInput) { + const auto parentWM = aReflowInput.GetWritingMode(); // The width/height of our content box, which is the available width/height - // for our anonymous content: - nscoord rangeFrameContentBoxWidth = aReflowInput.ComputedWidth(); - nscoord rangeFrameContentBoxHeight = aReflowInput.ComputedHeight(); - if (rangeFrameContentBoxHeight == NS_UNCONSTRAINEDSIZE) { - rangeFrameContentBoxHeight = 0; - } - - nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame(); - - if (trackFrame) { // display:none? - - // Position the track: - // The idea here is that we allow content authors to style the width, - // height, border and padding of the track, but we ignore margin and - // positioning properties and do the positioning ourself to keep the center - // of the track's border box on the center of the nsRangeFrame's content - // box. - - WritingMode wm = trackFrame->GetWritingMode(); - LogicalSize availSize = aReflowInput.ComputedSize(wm); - availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; - ReflowInput trackReflowInput(aPresContext, aReflowInput, trackFrame, - availSize); - - // Find the x/y position of the track frame such that it will be positioned - // as described above. These coordinates are with respect to the - // nsRangeFrame's border-box. - nscoord trackX = rangeFrameContentBoxWidth / 2; - nscoord trackY = rangeFrameContentBoxHeight / 2; - - // Account for the track's border and padding (we ignore its margin): - trackX -= trackReflowInput.ComputedPhysicalBorderPadding().left + - trackReflowInput.ComputedWidth() / 2; - trackY -= trackReflowInput.ComputedPhysicalBorderPadding().top + - trackReflowInput.ComputedHeight() / 2; - - // Make relative to our border box instead of our content box: - trackX += aReflowInput.ComputedPhysicalBorderPadding().left; - trackY += aReflowInput.ComputedPhysicalBorderPadding().top; - - nsReflowStatus frameStatus; - ReflowOutput trackDesiredSize(aReflowInput); - ReflowChild(trackFrame, aPresContext, trackDesiredSize, trackReflowInput, - trackX, trackY, ReflowChildFlags::Default, frameStatus); - MOZ_ASSERT( - frameStatus.IsFullyComplete(), - "We gave our child unconstrained height, so it should be complete"); - FinishReflowChild(trackFrame, aPresContext, trackDesiredSize, - &trackReflowInput, trackX, trackY, - ReflowChildFlags::Default); - } - - nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame(); - - if (thumbFrame) { // display:none? - WritingMode wm = thumbFrame->GetWritingMode(); - LogicalSize availSize = aReflowInput.ComputedSize(wm); - availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; - ReflowInput thumbReflowInput(aPresContext, aReflowInput, thumbFrame, - availSize); - - // Where we position the thumb depends on its size, so we first reflow - // the thumb at {0,0} to obtain its size, then position it afterwards. - - nsReflowStatus frameStatus; - ReflowOutput thumbDesiredSize(aReflowInput); - ReflowChild(thumbFrame, aPresContext, thumbDesiredSize, thumbReflowInput, 0, - 0, ReflowChildFlags::Default, frameStatus); - MOZ_ASSERT( - frameStatus.IsFullyComplete(), - "We gave our child unconstrained height, so it should be complete"); - FinishReflowChild(thumbFrame, aPresContext, thumbDesiredSize, - &thumbReflowInput, 0, 0, ReflowChildFlags::Default); - DoUpdateThumbPosition(thumbFrame, - nsSize(aDesiredSize.Width(), aDesiredSize.Height())); - } - - nsIFrame* rangeProgressFrame = mProgressDiv->GetPrimaryFrame(); - - if (rangeProgressFrame) { // display:none? - WritingMode wm = rangeProgressFrame->GetWritingMode(); - LogicalSize availSize = aReflowInput.ComputedSize(wm); + // for our anonymous content. + const nsSize rangeFrameContentBoxSize = + aContentBoxSize.GetPhysicalSize(parentWM); + for (auto* div : {mTrackDiv.get(), mThumbDiv.get(), mProgressDiv.get()}) { + nsIFrame* child = div->GetPrimaryFrame(); + if (!child) { + continue; + } + const WritingMode wm = child->GetWritingMode(); + const LogicalSize parentSizeInChildWM = + aContentBoxSize.ConvertTo(wm, parentWM); + LogicalSize availSize = parentSizeInChildWM; availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; - ReflowInput progressReflowInput(aPresContext, aReflowInput, - rangeProgressFrame, availSize); - - // We first reflow the range-progress frame at {0,0} to obtain its - // unadjusted dimensions, then we adjust it to so that the appropriate edge - // ends at the thumb. + ReflowInput childReflowInput(aPresContext, aReflowInput, child, availSize, + Some(parentSizeInChildWM)); + + const nsPoint pos = [&] { + if (div != mTrackDiv) { + // Where we position the thumb and range-progress depends on its size, + // so we first reflow them at {0,0} to obtain the size, then position + // them afterwards. + return nsPoint(); + } + // Find the x/y position of the track. The idea here is that we allow + // content authors to style the width, height, border and padding of the + // track, but we ignore margin and positioning properties and do the + // positioning ourself to keep the center of the track's border box on the + // center of the nsRangeFrame's content. These coordinates are with + // respect to the nsRangeFrame's border-box. + nscoord trackX = rangeFrameContentBoxSize.Width() / 2; + nscoord trackY = rangeFrameContentBoxSize.Height() / 2; + + // Account for the track's border and padding (we ignore its margin): + // FIXME(emilio): Assumes the track height is constrained, which might not + // be true if authors override it. + trackX -= childReflowInput.ComputedPhysicalBorderPadding().left + + childReflowInput.ComputedWidth() / 2; + trackY -= childReflowInput.ComputedPhysicalBorderPadding().top + + childReflowInput.ComputedHeight() / 2; + + // Make relative to our border box instead of our content box: + trackX += aReflowInput.ComputedPhysicalBorderPadding().left; + trackY += aReflowInput.ComputedPhysicalBorderPadding().top; + return nsPoint(trackX, trackY); + }(); nsReflowStatus frameStatus; - ReflowOutput progressDesiredSize(aReflowInput); - ReflowChild(rangeProgressFrame, aPresContext, progressDesiredSize, - progressReflowInput, 0, 0, ReflowChildFlags::Default, - frameStatus); + ReflowOutput childDesiredSize(aReflowInput); + ReflowChild(child, aPresContext, childDesiredSize, childReflowInput, pos.x, + pos.y, ReflowChildFlags::Default, frameStatus); MOZ_ASSERT( frameStatus.IsFullyComplete(), "We gave our child unconstrained height, so it should be complete"); - FinishReflowChild(rangeProgressFrame, aPresContext, progressDesiredSize, - &progressReflowInput, 0, 0, ReflowChildFlags::Default); - DoUpdateRangeProgressFrame( - rangeProgressFrame, - nsSize(aDesiredSize.Width(), aDesiredSize.Height())); + FinishReflowChild(child, aPresContext, childDesiredSize, &childReflowInput, + pos.x, pos.y, ReflowChildFlags::Default); + if (div == mThumbDiv) { + DoUpdateThumbPosition(child, rangeFrameContentBoxSize); + } else if (div == mProgressDiv) { + DoUpdateRangeProgressFrame(child, rangeFrameContentBoxSize); + } + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child); } } @@ -388,7 +329,7 @@ Decimal nsRangeFrame::GetValueAtEventPoint(WidgetGUIEvent* aEvent) { ->GetValueAsDecimal(); } - nsRect rangeContentRect = GetContentRectRelativeToSelf(); + const nsRect rangeContentRect = GetContentRectRelativeToSelf(); nsSize thumbSize; if (IsThemed()) { @@ -455,11 +396,12 @@ void nsRangeFrame::UpdateForValueChange() { if (!rangeProgressFrame && !thumbFrame) { return; // diplay:none? } + const nsSize contentBoxSize = GetContentRect().Size(); if (rangeProgressFrame) { - DoUpdateRangeProgressFrame(rangeProgressFrame, GetSize()); + DoUpdateRangeProgressFrame(rangeProgressFrame, contentBoxSize); } if (thumbFrame) { - DoUpdateThumbPosition(thumbFrame, GetSize()); + DoUpdateThumbPosition(thumbFrame, contentBoxSize); } if (IsThemed()) { // We don't know the exact dimensions or location of the thumb when native @@ -539,7 +481,7 @@ mozilla::dom::HTMLInputElement& nsRangeFrame::InputElement() const { } void nsRangeFrame::DoUpdateThumbPosition(nsIFrame* aThumbFrame, - const nsSize& aRangeSize) { + const nsSize& aRangeContentBoxSize) { MOZ_ASSERT(aThumbFrame); // The idea here is that we want to position the thumb so that the center @@ -553,29 +495,26 @@ void nsRangeFrame::DoUpdateThumbPosition(nsIFrame* aThumbFrame, nsMargin borderAndPadding = GetUsedBorderAndPadding(); nsPoint newPosition(borderAndPadding.left, borderAndPadding.top); - nsSize rangeContentBoxSize(aRangeSize); - rangeContentBoxSize.width -= borderAndPadding.LeftRight(); - rangeContentBoxSize.height -= borderAndPadding.TopBottom(); - nsSize thumbSize = aThumbFrame->GetSize(); double fraction = GetValueAsFractionOfRange(); MOZ_ASSERT(fraction >= 0.0 && fraction <= 1.0); if (IsHorizontal()) { - if (thumbSize.width < rangeContentBoxSize.width) { - nscoord traversableDistance = rangeContentBoxSize.width - thumbSize.width; + if (thumbSize.width < aRangeContentBoxSize.width) { + nscoord traversableDistance = + aRangeContentBoxSize.width - thumbSize.width; if (IsRightToLeft()) { newPosition.x += NSToCoordRound((1.0 - fraction) * traversableDistance); } else { newPosition.x += NSToCoordRound(fraction * traversableDistance); } - newPosition.y += (rangeContentBoxSize.height - thumbSize.height) / 2; + newPosition.y += (aRangeContentBoxSize.height - thumbSize.height) / 2; } } else { - if (thumbSize.height < rangeContentBoxSize.height) { + if (thumbSize.height < aRangeContentBoxSize.height) { nscoord traversableDistance = - rangeContentBoxSize.height - thumbSize.height; - newPosition.x += (rangeContentBoxSize.width - thumbSize.width) / 2; + aRangeContentBoxSize.height - thumbSize.height; + newPosition.x += (aRangeContentBoxSize.width - thumbSize.width) / 2; if (IsUpwards()) { newPosition.y += NSToCoordRound((1.0 - fraction) * traversableDistance); } else { @@ -586,9 +525,9 @@ void nsRangeFrame::DoUpdateThumbPosition(nsIFrame* aThumbFrame, aThumbFrame->SetPosition(newPosition); } -void nsRangeFrame::DoUpdateRangeProgressFrame(nsIFrame* aRangeProgressFrame, - const nsSize& aRangeSize) { - MOZ_ASSERT(aRangeProgressFrame); +void nsRangeFrame::DoUpdateRangeProgressFrame( + nsIFrame* aProgressFrame, const nsSize& aRangeContentBoxSize) { + MOZ_ASSERT(aProgressFrame); // The idea here is that we want to position the ::-moz-range-progress // pseudo-element so that the center line running along its length is on the @@ -598,35 +537,30 @@ void nsRangeFrame::DoUpdateRangeProgressFrame(nsIFrame* aRangeProgressFrame, // nsRangeFrame's content box, and we size the progress element's border-box // to have a length of GetValueAsFractionOfRange() times the nsRangeFrame's // content-box size. - nsMargin borderAndPadding = GetUsedBorderAndPadding(); - nsSize progSize = aRangeProgressFrame->GetSize(); + nsSize progSize = aProgressFrame->GetSize(); nsRect progRect(borderAndPadding.left, borderAndPadding.top, progSize.width, progSize.height); - nsSize rangeContentBoxSize(aRangeSize); - rangeContentBoxSize.width -= borderAndPadding.LeftRight(); - rangeContentBoxSize.height -= borderAndPadding.TopBottom(); - double fraction = GetValueAsFractionOfRange(); MOZ_ASSERT(fraction >= 0.0 && fraction <= 1.0); if (IsHorizontal()) { - nscoord progLength = NSToCoordRound(fraction * rangeContentBoxSize.width); + nscoord progLength = NSToCoordRound(fraction * aRangeContentBoxSize.width); if (IsRightToLeft()) { - progRect.x += rangeContentBoxSize.width - progLength; + progRect.x += aRangeContentBoxSize.width - progLength; } - progRect.y += (rangeContentBoxSize.height - progSize.height) / 2; + progRect.y += (aRangeContentBoxSize.height - progSize.height) / 2; progRect.width = progLength; } else { - nscoord progLength = NSToCoordRound(fraction * rangeContentBoxSize.height); - progRect.x += (rangeContentBoxSize.width - progSize.width) / 2; + nscoord progLength = NSToCoordRound(fraction * aRangeContentBoxSize.height); + progRect.x += (aRangeContentBoxSize.width - progSize.width) / 2; if (IsUpwards()) { - progRect.y += rangeContentBoxSize.height - progLength; + progRect.y += aRangeContentBoxSize.height - progLength; } progRect.height = progLength; } - aRangeProgressFrame->SetRect(progRect); + aProgressFrame->SetRect(progRect); } nsresult nsRangeFrame::AttributeChanged(int32_t aNameSpaceID, @@ -677,7 +611,7 @@ nsresult nsRangeFrame::AttributeChanged(int32_t aNameSpaceID, return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); } -nscoord nsRangeFrame::AutoCrossSize(Length aEm) { +nscoord nsRangeFrame::AutoCrossSize() { nscoord minCrossSize(0); if (IsThemed()) { nsPresContext* pc = PresContext(); @@ -686,33 +620,8 @@ nscoord nsRangeFrame::AutoCrossSize(Length aEm) { minCrossSize = pc->DevPixelsToAppUnits(IsHorizontal() ? size.height : size.width); } - return std::max(minCrossSize, aEm.ScaledBy(CROSS_AXIS_EM_SIZE).ToAppUnits()); -} - -static mozilla::Length OneEm(nsRangeFrame* aFrame) { - return aFrame->StyleFont()->mFont.size.ScaledBy( - nsLayoutUtils::FontSizeInflationFor(aFrame)); -} - -LogicalSize nsRangeFrame::ComputeAutoSize( - gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, - nscoord aAvailableISize, const LogicalSize& aMargin, - const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides, - ComputeSizeFlags aFlags) { - bool isInlineOriented = IsInlineOriented(); - auto em = OneEm(this); - - const WritingMode wm = GetWritingMode(); - LogicalSize autoSize(wm); - if (isInlineOriented) { - autoSize.ISize(wm) = em.ScaledBy(MAIN_AXIS_EM_SIZE).ToAppUnits(); - autoSize.BSize(wm) = AutoCrossSize(em); - } else { - autoSize.ISize(wm) = AutoCrossSize(em); - autoSize.BSize(wm) = em.ScaledBy(MAIN_AXIS_EM_SIZE).ToAppUnits(); - } - - return autoSize.ConvertTo(aWM, wm); + return std::max(minCrossSize, + NSToCoordRound(OneEmInAppUnits() * CROSS_AXIS_EM_SIZE)); } nscoord nsRangeFrame::GetMinISize(gfxContext* aRenderingContext) { @@ -728,11 +637,10 @@ nscoord nsRangeFrame::GetMinISize(gfxContext* aRenderingContext) { } nscoord nsRangeFrame::GetPrefISize(gfxContext* aRenderingContext) { - auto em = OneEm(this); if (IsInlineOriented()) { - return em.ScaledBy(MAIN_AXIS_EM_SIZE).ToAppUnits(); + return OneEmInAppUnits() * MAIN_AXIS_EM_SIZE; } - return AutoCrossSize(em); + return AutoCrossSize(); } bool nsRangeFrame::IsHorizontal() const { diff --git a/layout/forms/nsRangeFrame.h b/layout/forms/nsRangeFrame.h index 4d2073aa50..206103ea44 100644 --- a/layout/forms/nsRangeFrame.h +++ b/layout/forms/nsRangeFrame.h @@ -7,12 +7,10 @@ #ifndef nsRangeFrame_h___ #define nsRangeFrame_h___ -#include "mozilla/Attributes.h" #include "mozilla/Decimal.h" #include "mozilla/EventForwards.h" #include "nsContainerFrame.h" #include "nsIAnonymousContentCreator.h" -#include "nsIDOMEventListener.h" #include "nsCOMPtr.h" #include "nsTArray.h" @@ -40,8 +38,7 @@ class nsRangeFrame final : public nsContainerFrame, explicit nsRangeFrame(ComputedStyle* aStyle, nsPresContext* aPresContext); virtual ~nsRangeFrame(); - typedef mozilla::PseudoStyleType PseudoStyleType; - typedef mozilla::dom::Element Element; + using Element = mozilla::dom::Element; public: NS_DECL_QUERYFRAME @@ -53,39 +50,30 @@ class nsRangeFrame final : public nsContainerFrame, void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override; - virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, - const ReflowInput& aReflowInput, - nsReflowStatus& aStatus) override; + void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) override; #ifdef DEBUG_FRAME_DUMP - virtual nsresult GetFrameName(nsAString& aResult) const override { + nsresult GetFrameName(nsAString& aResult) const override { return MakeFrameName(u"Range"_ns, aResult); } #endif #ifdef ACCESSIBILITY - virtual mozilla::a11y::AccType AccessibleType() override; + mozilla::a11y::AccType AccessibleType() override; #endif // nsIAnonymousContentCreator - virtual nsresult CreateAnonymousContent( - nsTArray<ContentInfo>& aElements) override; - virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, - uint32_t aFilter) override; + nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override; + void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, + uint32_t aFilter) override; - virtual nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, - int32_t aModType) override; + nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, + int32_t aModType) override; - mozilla::LogicalSize ComputeAutoSize( - gfxContext* aRenderingContext, mozilla::WritingMode aWM, - const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize, - const mozilla::LogicalSize& aMargin, - const mozilla::LogicalSize& aBorderPadding, - const mozilla::StyleSizeOverrides& aSizeOverrides, - mozilla::ComputeSizeFlags aFlags) override; - - virtual nscoord GetMinISize(gfxContext* aRenderingContext) override; - virtual nscoord GetPrefISize(gfxContext* aRenderingContext) override; + nscoord GetMinISize(gfxContext* aRenderingContext) override; + nscoord GetPrefISize(gfxContext* aRenderingContext) override; /** * Returns true if the slider's thumb moves horizontally, or else false if it @@ -117,8 +105,8 @@ class nsRangeFrame final : public nsContainerFrame, bool IsUpwards() const { MOZ_ASSERT(!IsHorizontal()); mozilla::WritingMode wm = GetWritingMode(); - return wm.GetBlockDir() == mozilla::WritingMode::eBlockTB || - wm.GetInlineDir() == mozilla::WritingMode::eInlineBTT; + return wm.GetBlockDir() == mozilla::WritingMode::BlockDir::TB || + wm.GetInlineDir() == mozilla::WritingMode::InlineDir::BTT; } double GetMin() const; @@ -171,17 +159,19 @@ class nsRangeFrame final : public nsContainerFrame, private: // Return our preferred size in the cross-axis (the axis perpendicular // to the direction of movement of the thumb). - nscoord AutoCrossSize(mozilla::Length aEm); + nscoord AutoCrossSize(); // Helper function which reflows the anonymous div frames. void ReflowAnonymousContent(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, + const mozilla::LogicalSize& aContentBoxSize, const ReflowInput& aReflowInput); - void DoUpdateThumbPosition(nsIFrame* aThumbFrame, const nsSize& aRangeSize); + void DoUpdateThumbPosition(nsIFrame* aThumbFrame, + const nsSize& aRangeContentBoxSize); void DoUpdateRangeProgressFrame(nsIFrame* aProgressFrame, - const nsSize& aRangeSize); + const nsSize& aRangeContentBoxSize); /** * The div used to show the ::-moz-range-track pseudo-element. diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index c51b94c56a..3c44038d89 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -173,15 +173,14 @@ void nsTextControlFrame::Destroy(DestroyContext& aContext) { nsContainerFrame::Destroy(aContext); } -LogicalSize nsTextControlFrame::CalcIntrinsicSize( - gfxContext* aRenderingContext, WritingMode aWM, - float aFontSizeInflation) const { +LogicalSize nsTextControlFrame::CalcIntrinsicSize(gfxContext* aRenderingContext, + WritingMode aWM) const { LogicalSize intrinsicSize(aWM); + const float inflation = nsLayoutUtils::FontSizeInflationFor(this); RefPtr<nsFontMetrics> fontMet = - nsLayoutUtils::GetFontMetricsForFrame(this, aFontSizeInflation); - const nscoord lineHeight = - ReflowInput::CalcLineHeight(*Style(), PresContext(), GetContent(), - NS_UNCONSTRAINEDSIZE, aFontSizeInflation); + nsLayoutUtils::GetFontMetricsForFrame(this, inflation); + const nscoord lineHeight = ReflowInput::CalcLineHeight( + *Style(), PresContext(), GetContent(), NS_UNCONSTRAINEDSIZE, inflation); // Use the larger of the font's "average" char width or the width of the // zero glyph (if present) as the basis for resolving the size attribute. const nscoord charWidth = @@ -418,8 +417,10 @@ nsresult nsTextControlFrame::CreateAnonymousContent( // background on the placeholder doesn't obscure the caret. aElements.AppendElement(mRootNode); - if (StaticPrefs::layout_forms_reveal_password_button_enabled() && - IsPasswordTextControl()) { + if ((StaticPrefs::layout_forms_reveal_password_button_enabled() || + PresContext()->Document()->ChromeRulesEnabled()) && + IsPasswordTextControl() && + StyleDisplay()->EffectiveAppearance() != StyleAppearance::Textfield) { mRevealButton = MakeAnonElement(PseudoStyleType::mozReveal, nullptr, nsGkAtoms::button); mRevealButton->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_hidden, @@ -554,58 +555,14 @@ void nsTextControlFrame::AppendAnonymousContentTo( } nscoord nsTextControlFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result = 0; - DISPLAY_PREF_INLINE_SIZE(this, result); - float inflation = nsLayoutUtils::FontSizeInflationFor(this); WritingMode wm = GetWritingMode(); - result = CalcIntrinsicSize(aRenderingContext, wm, inflation).ISize(wm); - return result; + return CalcIntrinsicSize(aRenderingContext, wm).ISize(wm); } nscoord nsTextControlFrame::GetMinISize(gfxContext* aRenderingContext) { - // Our min inline size is just our preferred width if we have auto inline size - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - result = GetPrefISize(aRenderingContext); - return result; -} - -LogicalSize nsTextControlFrame::ComputeAutoSize( - gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize, - nscoord aAvailableISize, const LogicalSize& aMargin, - const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides, - ComputeSizeFlags aFlags) { - float inflation = nsLayoutUtils::FontSizeInflationFor(this); - LogicalSize autoSize = CalcIntrinsicSize(aRenderingContext, aWM, inflation); - - // Note: nsContainerFrame::ComputeAutoSize only computes the inline-size (and - // only for 'auto'), the block-size it returns is always NS_UNCONSTRAINEDSIZE. - const auto& styleISize = aSizeOverrides.mStyleISize - ? *aSizeOverrides.mStyleISize - : StylePosition()->ISize(aWM); - if (styleISize.IsAuto()) { - if (aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) { - // CalcIntrinsicSize isn't aware of grid-item margin-box clamping, so we - // fall back to nsContainerFrame's ComputeAutoSize to handle that. - // XXX maybe a font-inflation issue here? (per the assertion below). - autoSize.ISize(aWM) = - nsContainerFrame::ComputeAutoSize( - aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, - aBorderPadding, aSizeOverrides, aFlags) - .ISize(aWM); - } -#ifdef DEBUG - else { - LogicalSize ancestorAutoSize = nsContainerFrame::ComputeAutoSize( - aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, - aBorderPadding, aSizeOverrides, aFlags); - MOZ_ASSERT(inflation != 1.0f || - ancestorAutoSize.ISize(aWM) == autoSize.ISize(aWM), - "Incorrect size computed by ComputeAutoSize?"); - } -#endif - } - return autoSize; + // Our min inline size is just our preferred inline-size if we have auto + // inline size. + return GetPrefISize(aRenderingContext); } Maybe<nscoord> nsTextControlFrame::ComputeBaseline( @@ -641,12 +598,16 @@ void nsTextControlFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); // set values of reflow's out parameters WritingMode wm = aReflowInput.GetWritingMode(); - aDesiredSize.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm)); + const auto contentBoxSize = aReflowInput.ComputedSizeWithBSizeFallback([&] { + return CalcIntrinsicSize(aReflowInput.mRenderingContext, wm).BSize(wm); + }); + aDesiredSize.SetSize( + wm, + contentBoxSize + aReflowInput.ComputedLogicalBorderPadding(wm).Size(wm)); { // Calculate the baseline and store it in mFirstBaseline. @@ -674,7 +635,7 @@ void nsTextControlFrame::Reflow(nsPresContext* aPresContext, nscoord buttonBoxISize = 0; if (buttonBox) { ReflowTextControlChild(buttonBox, aPresContext, aReflowInput, aStatus, - aDesiredSize, buttonBoxISize); + aDesiredSize, contentBoxSize, buttonBoxISize); } // perform reflow on all kids @@ -684,7 +645,7 @@ void nsTextControlFrame::Reflow(nsPresContext* aPresContext, MOZ_ASSERT(!IsButtonBox(kid), "Should only have one button box, and should be last"); ReflowTextControlChild(kid, aPresContext, aReflowInput, aStatus, - aDesiredSize, buttonBoxISize); + aDesiredSize, contentBoxSize, buttonBoxISize); } kid = kid->GetNextSibling(); } @@ -698,22 +659,28 @@ void nsTextControlFrame::Reflow(nsPresContext* aPresContext, void nsTextControlFrame::ReflowTextControlChild( nsIFrame* aKid, nsPresContext* aPresContext, const ReflowInput& aReflowInput, nsReflowStatus& aStatus, - ReflowOutput& aParentDesiredSize, nscoord& aButtonBoxISize) { + ReflowOutput& aParentDesiredSize, const LogicalSize& aParentContentBoxSize, + nscoord& aButtonBoxISize) { const WritingMode outerWM = aReflowInput.GetWritingMode(); // compute available size and frame offsets for child const WritingMode wm = aKid->GetWritingMode(); - LogicalSize availSize = aReflowInput.ComputedSizeWithPadding(wm); + const auto parentPadding = aReflowInput.ComputedLogicalPadding(wm); + const LogicalSize contentBoxSize = + aParentContentBoxSize.ConvertTo(wm, outerWM); + const LogicalSize paddingBoxSize = contentBoxSize + parentPadding.Size(wm); + const LogicalSize borderBoxSize = + paddingBoxSize + aReflowInput.ComputedLogicalBorder(wm).Size(wm); + LogicalSize availSize = paddingBoxSize; availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; - bool isButtonBox = IsButtonBox(aKid); + const bool isButtonBox = IsButtonBox(aKid); ReflowInput kidReflowInput(aPresContext, aReflowInput, aKid, availSize, Nothing(), ReflowInput::InitFlag::CallerWillInit); // Override padding with our computed padding in case we got it from theming // or percentage, if we're not the button box. - auto overridePadding = - isButtonBox ? Nothing() : Some(aReflowInput.ComputedLogicalPadding(wm)); + auto overridePadding = isButtonBox ? Nothing() : Some(parentPadding); if (!isButtonBox && aButtonBoxISize) { // Button box respects inline-end-padding, so we don't need to. overridePadding->IEnd(outerWM) = 0; @@ -722,8 +689,7 @@ void nsTextControlFrame::ReflowTextControlChild( // We want to let our button box fill the frame in the block axis, up to the // edge of the control's border. So, we use the control's padding-box as the // containing block size for our button box. - auto overrideCBSize = - isButtonBox ? Some(aReflowInput.ComputedSizeWithPadding(wm)) : Nothing(); + auto overrideCBSize = isButtonBox ? Some(paddingBoxSize) : Nothing(); kidReflowInput.Init(aPresContext, overrideCBSize, Nothing(), overridePadding); LogicalPoint position(wm); @@ -746,14 +712,12 @@ void nsTextControlFrame::ReflowTextControlChild( // the only exception, which has an auto size). kidReflowInput.SetComputedISize( std::max(0, aReflowInput.ComputedISize() - aButtonBoxISize)); - kidReflowInput.SetComputedBSize(aReflowInput.ComputedBSize()); + kidReflowInput.SetComputedBSize(contentBoxSize.BSize(wm)); } // reflow the child ReflowOutput desiredSize(aReflowInput); - const nsSize containerSize = - aReflowInput.ComputedSizeWithBorderPadding(outerWM).GetPhysicalSize( - outerWM); + const nsSize containerSize = borderBoxSize.GetPhysicalSize(wm); ReflowChild(aKid, aPresContext, desiredSize, kidReflowInput, wm, position, containerSize, ReflowChildFlags::Default, aStatus); @@ -767,7 +731,7 @@ void nsTextControlFrame::ReflowTextControlChild( buttonRect.ISize(outerWM) = size.ISize(outerWM); buttonRect.BStart(outerWM) = bp.BStart(outerWM) + - (aReflowInput.ComputedBSize() - size.BSize(outerWM)) / 2; + (aParentContentBoxSize.BSize(outerWM) - size.BSize(outerWM)) / 2; // Align to the inline-end of the content box. buttonRect.IStart(outerWM) = bp.IStart(outerWM) + aReflowInput.ComputedISize() - size.ISize(outerWM); diff --git a/layout/forms/nsTextControlFrame.h b/layout/forms/nsTextControlFrame.h index 0d52e5849f..2eb6f18d15 100644 --- a/layout/forms/nsTextControlFrame.h +++ b/layout/forms/nsTextControlFrame.h @@ -63,14 +63,6 @@ class nsTextControlFrame : public nsContainerFrame, nscoord GetMinISize(gfxContext* aRenderingContext) override; nscoord GetPrefISize(gfxContext* aRenderingContext) override; - mozilla::LogicalSize ComputeAutoSize( - gfxContext* aRenderingContext, mozilla::WritingMode aWM, - const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize, - const mozilla::LogicalSize& aMargin, - const mozilla::LogicalSize& aBorderPadding, - const mozilla::StyleSizeOverrides& aSizeOverrides, - mozilla::ComputeSizeFlags aFlags) override; - void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) override; @@ -180,10 +172,11 @@ class nsTextControlFrame : public nsContainerFrame, /** * Launch the reflow on the child frames - see nsTextControlFrame::Reflow() */ - void ReflowTextControlChild(nsIFrame* aFrame, nsPresContext* aPresContext, + void ReflowTextControlChild(nsIFrame* aKid, nsPresContext* aPresContext, const ReflowInput& aReflowInput, nsReflowStatus& aStatus, ReflowOutput& aParentDesiredSize, + const mozilla::LogicalSize& aParentContentBoxSize, nscoord& aButtonBoxISize); public: @@ -278,8 +271,7 @@ class nsTextControlFrame : public nsContainerFrame, // etc. Just the size of our actual area for the text (and the scrollbars, // for <textarea>). mozilla::LogicalSize CalcIntrinsicSize(gfxContext* aRenderingContext, - mozilla::WritingMode aWM, - float aFontSizeInflation) const; + mozilla::WritingMode aWM) const; private: // helper methods diff --git a/layout/forms/test/mochitest.toml b/layout/forms/test/mochitest.toml index 0748041524..5eddbf6a7b 100644 --- a/layout/forms/test/mochitest.toml +++ b/layout/forms/test/mochitest.toml @@ -75,8 +75,6 @@ skip-if = ["os == 'android'"] # Bug 1635771 ["test_bug717878_input_scroll.html"] -["test_bug869314.html"] - ["test_bug903715.html"] skip-if = ["true"] diff --git a/layout/forms/test/test_bug869314.html b/layout/forms/test/test_bug869314.html deleted file mode 100644 index 7c786fccfc..0000000000 --- a/layout/forms/test/test_bug869314.html +++ /dev/null @@ -1,55 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=869314 ---> -<head> - <meta charset="utf-8"> - <title>Test for Bug 869314</title> - <script src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - - <style type="text/css"> - .selectbox { - background-color: #00FF00; - } - </style> - -</head> -<body> -<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=869314">Mozilla Bug 869314</a> -<p id="display"></p> -<div id="content"> - - <select id="selectbox1" name="non-native selectbox" class="selectbox"> - <option value="item">test item</option> - </select> - - <select id="selectbox2" name="native selectbox"> - <option value="item">test item</option> - </select> - - <script type="application/javascript"> - let Cc = SpecialPowers.Cc; - let Ci = SpecialPowers.Ci; - let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); - let osName = sysInfo.getProperty("name"); - let isNNT = SpecialPowers.getBoolPref("widget.non-native-theme.enabled"); - if (osName == "Darwin" && !isNNT) { // Native styled macOS form controls. - // This test is for macOS with native styled form controls only. See bug for more info. - ok(document.getElementById("selectbox1").clientWidth > - document.getElementById("selectbox2").clientWidth, - "Non-native styled combobox does not have enough space for a " + - "dropmarker!"); - } else { - // We need to call at least one test function to make the test harness - // happy. - ok(true, "Test wasn't ignored but should have been."); - } - </script> - -</div> -<pre id="test"> -</pre> -</body> -</html> diff --git a/layout/generic/AspectRatio.cpp b/layout/generic/AspectRatio.cpp index 2fe51fed5e..0a8ef181e4 100644 --- a/layout/generic/AspectRatio.cpp +++ b/layout/generic/AspectRatio.cpp @@ -20,7 +20,7 @@ nscoord AspectRatio::ComputeRatioDependentSize( const LogicalSize& boxSizingAdjust = mUseBoxSizing == UseBoxSizing::No ? LogicalSize(aWM) : aContentBoxSizeToBoxSizingAdjust; - return aRatioDependentAxis == LogicalAxis::eLogicalAxisInline + return aRatioDependentAxis == LogicalAxis::Inline ? ConvertToWritingMode(aWM).ApplyTo(aRatioDeterminingSize + boxSizingAdjust.BSize(aWM)) - boxSizingAdjust.ISize(aWM) diff --git a/layout/generic/AspectRatio.h b/layout/generic/AspectRatio.h index 6f48a07da3..daafd06ba8 100644 --- a/layout/generic/AspectRatio.h +++ b/layout/generic/AspectRatio.h @@ -22,7 +22,7 @@ struct ParamTraits; namespace mozilla { -enum LogicalAxis : uint8_t; +enum class LogicalAxis : uint8_t; class LogicalSize; class WritingMode; diff --git a/layout/generic/BRFrame.cpp b/layout/generic/BRFrame.cpp index 52cc3a0b46..13b3fbb9a4 100644 --- a/layout/generic/BRFrame.cpp +++ b/layout/generic/BRFrame.cpp @@ -98,7 +98,6 @@ void BRFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("BRFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); WritingMode wm = aReflowInput.GetWritingMode(); @@ -186,18 +185,10 @@ void BRFrame::AddInlinePrefISize(gfxContext* aRenderingContext, } /* virtual */ -nscoord BRFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result = 0; - DISPLAY_MIN_INLINE_SIZE(this, result); - return result; -} +nscoord BRFrame::GetMinISize(gfxContext* aRenderingContext) { return 0; } /* virtual */ -nscoord BRFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result = 0; - DISPLAY_PREF_INLINE_SIZE(this, result); - return result; -} +nscoord BRFrame::GetPrefISize(gfxContext* aRenderingContext) { return 0; } Maybe<nscoord> BRFrame::GetNaturalBaselineBOffset( WritingMode aWM, BaselineSharingGroup aBaselineGroup, diff --git a/layout/generic/CSSAlignUtils.cpp b/layout/generic/CSSAlignUtils.cpp index b88ec8bfa4..090cba3a2a 100644 --- a/layout/generic/CSSAlignUtils.cpp +++ b/layout/generic/CSSAlignUtils.cpp @@ -56,7 +56,7 @@ nscoord CSSAlignUtils::AlignJustifySelf(const StyleAlignFlags& aAlignment, WritingMode wm = aRI.GetWritingMode(); const LogicalMargin margin = aRI.ComputedLogicalMargin(wm); const auto startSide = MakeLogicalSide( - aAxis, MOZ_LIKELY(isSameSide) ? eLogicalEdgeStart : eLogicalEdgeEnd); + aAxis, MOZ_LIKELY(isSameSide) ? LogicalEdge::Start : LogicalEdge::End); const nscoord marginStart = margin.Side(startSide, wm); const auto endSide = GetOppositeSide(startSide); const nscoord marginEnd = margin.Side(endSide, wm); @@ -68,10 +68,10 @@ nscoord CSSAlignUtils::AlignJustifySelf(const StyleAlignFlags& aAlignment, // (Note: ReflowInput will have treated "auto" margins as 0, so we // don't need to do anything special to avoid expanding them.) hasAutoMarginStart = hasAutoMarginEnd = false; - } else if (aAxis == eLogicalAxisBlock) { + } else if (aAxis == LogicalAxis::Block) { hasAutoMarginStart = styleMargin.GetBStart(wm).IsAuto(); hasAutoMarginEnd = styleMargin.GetBEnd(wm).IsAuto(); - } else { /* aAxis == eLogicalAxisInline */ + } else { /* aAxis == LogicalAxis::Inline */ hasAutoMarginStart = styleMargin.GetIStart(wm).IsAuto(); hasAutoMarginEnd = styleMargin.GetIEnd(wm).IsAuto(); } diff --git a/layout/generic/ColumnSetWrapperFrame.cpp b/layout/generic/ColumnSetWrapperFrame.cpp index 7717f14df8..9f22222925 100644 --- a/layout/generic/ColumnSetWrapperFrame.cpp +++ b/layout/generic/ColumnSetWrapperFrame.cpp @@ -151,7 +151,6 @@ void ColumnSetWrapperFrame::MarkIntrinsicISizesDirty() { nscoord ColumnSetWrapperFrame::GetMinISize(gfxContext* aRenderingContext) { nscoord iSize = 0; - DISPLAY_MIN_INLINE_SIZE(this, iSize); if (Maybe<nscoord> containISize = ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) { @@ -193,7 +192,6 @@ nscoord ColumnSetWrapperFrame::GetMinISize(gfxContext* aRenderingContext) { nscoord ColumnSetWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) { nscoord iSize = 0; - DISPLAY_PREF_INLINE_SIZE(this, iSize); if (Maybe<nscoord> containISize = ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) { diff --git a/layout/generic/MiddleCroppingBlockFrame.cpp b/layout/generic/MiddleCroppingBlockFrame.cpp index 4cbbb684aa..75dc205a4f 100644 --- a/layout/generic/MiddleCroppingBlockFrame.cpp +++ b/layout/generic/MiddleCroppingBlockFrame.cpp @@ -57,18 +57,11 @@ void MiddleCroppingBlockFrame::UpdateDisplayedValueToUncroppedValue( } nscoord MiddleCroppingBlockFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - // Our min inline size is our pref inline size - result = GetPrefISize(aRenderingContext); - return result; + return GetPrefISize(aRenderingContext); } nscoord MiddleCroppingBlockFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - nsAutoString prevValue; bool restoreOldValue = false; @@ -79,7 +72,7 @@ nscoord MiddleCroppingBlockFrame::GetPrefISize(gfxContext* aRenderingContext) { UpdateDisplayedValueToUncroppedValue(false); } - result = nsBlockFrame::GetPrefISize(aRenderingContext); + nscoord result = nsBlockFrame::GetPrefISize(aRenderingContext); if (restoreOldValue) { UpdateDisplayedValue(prevValue, /* aIsCropped = */ true, false); diff --git a/layout/generic/PrintedSheetFrame.cpp b/layout/generic/PrintedSheetFrame.cpp index 5b8151bbd0..384e63e361 100644 --- a/layout/generic/PrintedSheetFrame.cpp +++ b/layout/generic/PrintedSheetFrame.cpp @@ -86,7 +86,6 @@ void PrintedSheetFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("PrintedSheetFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aReflowOutput, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); // If we have a prev-in-flow, take its overflowing content: diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp index ed7dbd2656..bdfd39aff1 100644 --- a/layout/generic/ReflowInput.cpp +++ b/layout/generic/ReflowInput.cpp @@ -13,10 +13,8 @@ #include "CounterStyleManager.h" #include "LayoutLogging.h" #include "mozilla/dom/HTMLInputElement.h" -#include "mozilla/StaticPrefs_layout.h" #include "mozilla/WritingModes.h" #include "nsBlockFrame.h" -#include "nsCSSAnonBoxes.h" #include "nsFlexContainerFrame.h" #include "nsFontInflationData.h" #include "nsFontMetrics.h" @@ -31,7 +29,6 @@ #include "nsLineBox.h" #include "nsPresContext.h" #include "nsStyleConsts.h" -#include "nsTableCellFrame.h" #include "nsTableFrame.h" #include "StickyScrollContainer.h" @@ -956,21 +953,10 @@ void ReflowInput::ApplyRelativePositioning(nsIFrame* aFrame, const nsStyleDisplay* display = aFrame->StyleDisplay(); if (StylePositionProperty::Relative == display->mPosition) { *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top); - } else if (StylePositionProperty::Sticky == display->mPosition && - !aFrame->GetNextContinuation() && !aFrame->GetPrevContinuation() && - !aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) { - // Sticky positioning for elements with multiple frames needs to be - // computed all at once. We can't safely do that here because we might be - // partway through (re)positioning the frames, so leave it until the scroll - // container reflows and calls StickyScrollContainer::UpdatePositions. - // For single-frame sticky positioned elements, though, go ahead and apply - // it now to avoid unnecessary overflow updates later. - StickyScrollContainer* ssc = - StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); - if (ssc) { - *aPosition = ssc->ComputePosition(aFrame); - } } + // For sticky positioned elements, we'll leave them until the scroll container + // reflows and calls StickyScrollContainer::UpdatePositions() to update their + // positions. } // static @@ -1126,9 +1112,9 @@ void ReflowInput::CalculateBorderPaddingMargin( nscoord* aOutsideBoxSizing) const { WritingMode wm = GetWritingMode(); mozilla::Side startSide = - wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeStart)); + wm.PhysicalSide(MakeLogicalSide(aAxis, LogicalEdge::Start)); mozilla::Side endSide = - wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeEnd)); + wm.PhysicalSide(MakeLogicalSide(aAxis, LogicalEdge::End)); nsMargin styleBorder = mStyleBorder->GetComputedBorder(); nscoord borderStartEnd = @@ -1231,18 +1217,18 @@ static bool AxisPolarityFlipped(LogicalAxis aThisAxis, WritingMode aThisWm, aThisWm.PhysicalAxis(aThisAxis) == aOtherWm.PhysicalAxis(otherAxis), "Physical axes must match!"); Side thisStartSide = - aThisWm.PhysicalSide(MakeLogicalSide(aThisAxis, eLogicalEdgeStart)); + aThisWm.PhysicalSide(MakeLogicalSide(aThisAxis, LogicalEdge::Start)); Side otherStartSide = - aOtherWm.PhysicalSide(MakeLogicalSide(otherAxis, eLogicalEdgeStart)); + aOtherWm.PhysicalSide(MakeLogicalSide(otherAxis, LogicalEdge::Start)); return thisStartSide != otherStartSide; } static bool InlinePolarityFlipped(WritingMode aThisWm, WritingMode aOtherWm) { - return AxisPolarityFlipped(eLogicalAxisInline, aThisWm, aOtherWm); + return AxisPolarityFlipped(LogicalAxis::Inline, aThisWm, aOtherWm); } static bool BlockPolarityFlipped(WritingMode aThisWm, WritingMode aOtherWm) { - return AxisPolarityFlipped(eLogicalAxisBlock, aThisWm, aOtherWm); + return AxisPolarityFlipped(LogicalAxis::Block, aThisWm, aOtherWm); } // Calculate the position of the hypothetical box that the element would have @@ -1292,7 +1278,6 @@ void ReflowInput::CalculateHypotheticalPosition( // For non-replaced inline-level elements the 'inline size' property // doesn't apply, so we don't know what the inline size would have // been without reflowing it - } else { // It's either a replaced inline-level element or a block-level element @@ -1301,17 +1286,17 @@ void ReflowInput::CalculateHypotheticalPosition( // been in the flow. Note that we ignore any 'auto' and 'inherit' // values nscoord insideBoxISizing, outsideBoxISizing; - CalculateBorderPaddingMargin(eLogicalAxisInline, blockContentSize.ISize(wm), - &insideBoxISizing, &outsideBoxISizing); + CalculateBorderPaddingMargin(LogicalAxis::Inline, + blockContentSize.ISize(wm), &insideBoxISizing, + &outsideBoxISizing); if (mFlags.mIsReplaced && isAutoISize) { - // It's a replaced element with an 'auto' inline size so the box - // inline size is its intrinsic size plus any border/padding/margin + // It's a replaced element with an 'auto' inline size so the box inline + // size is its intrinsic size plus any border/padding/margin if (intrinsicSize) { boxISize.emplace(LogicalSize(wm, *intrinsicSize).ISize(wm) + outsideBoxISizing + insideBoxISizing); } - } else if (isAutoISize) { // The box inline size is the containing block inline size boxISize.emplace(blockContentSize.ISize(wm)); @@ -1320,7 +1305,7 @@ void ReflowInput::CalculateHypotheticalPosition( // percentage based this computed value may be different from the computed // value calculated using the absolute containing block width nscoord insideBoxBSizing, dummy; - CalculateBorderPaddingMargin(eLogicalAxisBlock, + CalculateBorderPaddingMargin(LogicalAxis::Block, blockContentSize.ISize(wm), &insideBoxBSizing, &dummy); boxISize.emplace( @@ -1500,7 +1485,7 @@ void ReflowInput::CalculateHypotheticalPosition( // been in the flow. Note that we ignore any 'auto' and 'inherit' // values. nscoord insideBoxSizing, outsideBoxSizing; - CalculateBorderPaddingMargin(eLogicalAxisBlock, blockContentSize.BSize(wm), + CalculateBorderPaddingMargin(LogicalAxis::Block, blockContentSize.BSize(wm), &insideBoxSizing, &outsideBoxSizing); nscoord boxBSize; @@ -1627,7 +1612,7 @@ LogicalSize ReflowInput::CalculateAbsoluteSizeWithResolvedAutoBlockSize( : LogicalSize(wm); auto transferredISize = mStylePosition->mAspectRatio.ToLayoutRatio().ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, wm, aAutoBSize, boxSizingAdjust); + LogicalAxis::Inline, wm, aAutoBSize, boxSizingAdjust); resultSize.ISize(wm) = ApplyMinMaxISize(transferredISize); MOZ_ASSERT(mFlags.mIsBSizeSetByAspectRatio, @@ -2167,8 +2152,6 @@ void ReflowInput::InitConstraints( WritingMode wm = GetWritingMode(); LogicalSize cbSize = aContainingBlockSize.valueOr( LogicalSize(mWritingMode, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)); - DISPLAY_INIT_CONSTRAINTS(mFrame, this, cbSize.ISize(wm), cbSize.BSize(wm), - aBorder, aPadding); // If this is a reflow root, then set the computed width and // height equal to the available space @@ -2239,7 +2222,7 @@ void ReflowInput::InitConstraints( // eStyleUnit_Auto;" used to be called exclusively. if (mFlags.mIsReplaced && mStyleDisplay->IsInlineOutsideStyle()) { // Get the containing block's reflow input - NS_ASSERTION(nullptr != cbri, "no containing block"); + NS_ASSERTION(cbri, "no containing block"); // in quirks mode, get the cb height using the special quirk method if (!wm.IsVertical() && eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) { @@ -2359,46 +2342,79 @@ void ReflowInput::InitConstraints( } else { AutoMaybeDisableFontInflation an(mFrame); - const bool isBlockLevel = - ((!mStyleDisplay->IsInlineOutsideStyle() && - // internal table values on replaced elements behaves as inline - // https://drafts.csswg.org/css-tables-3/#table-structure - // "... it is handled instead as though the author had declared - // either 'block' (for 'table' display) or 'inline' (for all - // other values)" - !(mFlags.mIsReplaced && (mStyleDisplay->IsInnerTableStyle() || - mStyleDisplay->DisplayOutside() == - StyleDisplayOutside::TableCaption))) || - // The inner table frame always fills its outer wrapper table frame, - // even for 'inline-table'. - mFrame->IsTableFrame()) && - // XXX abs.pos. continuations treated like blocks, see comment in - // the else-if condition above. - (!mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) || - mStyleDisplay->IsAbsolutelyPositionedStyle()); - - if (!isBlockLevel) { - mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; - } + nsIFrame* const alignCB = [&] { + nsIFrame* cb = mFrame->GetParent(); + if (cb->IsTableWrapperFrame()) { + nsIFrame* alignCBParent = cb->GetParent(); + if (alignCBParent && alignCBParent->IsGridContainerFrame()) { + return alignCBParent; + } + } + return cb; + }(); - nsIFrame* alignCB = mFrame->GetParent(); - if (alignCB->IsTableWrapperFrame()) { - nsIFrame* alignCBParent = alignCB->GetParent(); - if (alignCBParent && alignCBParent->IsGridContainerFrame()) { - alignCB = alignCBParent; + const bool isInlineLevel = [&] { + if (mFrame->IsTableFrame()) { + // An inner table frame is not inline-level, even if it happens to + // have 'display:inline-table'. (That makes its table-wrapper frame be + // inline-level, but not the inner table frame) + return false; } - } - if (!alignCB->IsGridContainerFrame()) { - // Shrink-wrap blocks that are orthogonal to their container. - if (isBlockLevel && mCBReflowInput && + if (mStyleDisplay->IsInlineOutsideStyle()) { + return true; + } + if (mFlags.mIsReplaced && (mStyleDisplay->IsInnerTableStyle() || + mStyleDisplay->DisplayOutside() == + StyleDisplayOutside::TableCaption)) { + // Internal table values on replaced elements behave as inline + // https://drafts.csswg.org/css-tables-3/#table-structure + // + // ... it is handled instead as though the author had declared + // either 'block' (for 'table' display) or 'inline' (for all + // other values)" + // + // FIXME(emilio): The only test that covers this is + // table-anonymous-objects-211.xht, which fails on other browsers (but + // differently to us, if you just remove this condition). + return true; + } + if (mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && + !mStyleDisplay->IsAbsolutelyPositionedStyle()) { + // Floats are treated as inline-level and also shrink-wrap. + return true; + } + return false; + }(); + + const bool shouldShrinkWrap = [&] { + if (isInlineLevel) { + return true; + } + if (mFlags.mIsReplaced && !alignCB->IsFlexOrGridContainer()) { + // Shrink-wrap replaced elements when in-flow (out of flows are + // handled above). We exclude replaced elements in grid or flex + // contexts, where we don't want to shrink-wrap unconditionally (so + // that stretching can happen). When grid/flex explicitly want + // shrink-wrapping, they can request it directly using the relevant + // flag. + return true; + } + if (!alignCB->IsGridContainerFrame() && mCBReflowInput && mCBReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) { - mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; + // Shrink-wrap blocks that are orthogonal to their container (unless + // we're in a grid?) + return true; } + return false; + }(); + + if (shouldShrinkWrap) { + mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; } if (cbSize.ISize(wm) == NS_UNCONSTRAINEDSIZE) { - // For orthogonal flows, where we found a parent orthogonal-limit - // for AvailableISize() in Init(), we'll use the same here as well. + // For orthogonal flows, where we found a parent orthogonal-limit for + // AvailableISize() in Init(), we'll use the same here as well. cbSize.ISize(wm) = AvailableISize(); } @@ -2418,10 +2434,7 @@ void ReflowInput::InitConstraints( size.mAspectRatioUsage == nsIFrame::AspectRatioUsage::ToComputeBSize; const bool shouldCalculateBlockSideMargins = [&]() { - if (!isBlockLevel) { - return false; - } - if (mStyleDisplay->mDisplay == StyleDisplay::InlineTable) { + if (isInlineLevel) { return false; } if (mFrame->IsTableFrame()) { @@ -2475,7 +2488,6 @@ void SizeComputationInput::InitOffsets(WritingMode aCBWM, nscoord aPercentBasis, const Maybe<LogicalMargin>& aBorder, const Maybe<LogicalMargin>& aPadding, const nsStyleDisplay* aDisplay) { - DISPLAY_INIT_OFFSETS(mFrame, this, aPercentBasis, aCBWM, aBorder, aPadding); nsPresContext* presContext = mFrame->PresContext(); // Compute margins from the specified margin style information. These @@ -2526,14 +2538,14 @@ void SizeComputationInput::InitOffsets(WritingMode aCBWM, nscoord aPercentBasis, NS_ASSERTION(val != nscoord(0), "zero in this property is useless"); LogicalSide side; if (val > 0) { - side = MakeLogicalSide(aAxis, eLogicalEdgeStart); + side = MakeLogicalSide(aAxis, LogicalEdge::Start); } else { - side = MakeLogicalSide(aAxis, eLogicalEdgeEnd); + side = MakeLogicalSide(aAxis, LogicalEdge::End); val = -val; } mComputedPadding.Side(side, wm) += val; needPaddingProp = true; - if (aAxis == eLogicalAxisBlock && val > 0) { + if (aAxis == LogicalAxis::Block && val > 0) { // We have a baseline-adjusted block-axis start padding, so // we need this to mark lines dirty when mIsBResize is true: this->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); @@ -2541,10 +2553,10 @@ void SizeComputationInput::InitOffsets(WritingMode aCBWM, nscoord aPercentBasis, } }; if (!aFlags.contains(ComputeSizeFlag::IsGridMeasuringReflow)) { - ApplyBaselinePadding(eLogicalAxisBlock, nsIFrame::BBaselinePadProperty()); + ApplyBaselinePadding(LogicalAxis::Block, nsIFrame::BBaselinePadProperty()); } if (!aFlags.contains(ComputeSizeFlag::ShrinkWrap)) { - ApplyBaselinePadding(eLogicalAxisInline, nsIFrame::IBaselinePadProperty()); + ApplyBaselinePadding(LogicalAxis::Inline, nsIFrame::IBaselinePadProperty()); } LogicalMargin border(wm); @@ -2719,27 +2731,28 @@ void ReflowInput::CalculateBlockSideMargins() { // zeros, we should compensate this by creating extra (external) leading. // This is necessary because without this compensation, normal line height might // look too tight. -constexpr float kNormalLineHeightFactor = 1.2f; static nscoord GetNormalLineHeight(nsFontMetrics* aFontMetrics) { MOZ_ASSERT(aFontMetrics, "no font metrics"); nscoord externalLeading = aFontMetrics->ExternalLeading(); nscoord internalLeading = aFontMetrics->InternalLeading(); nscoord emHeight = aFontMetrics->EmHeight(); if (!internalLeading && !externalLeading) { - return NSToCoordRound(emHeight * kNormalLineHeightFactor); + return NSToCoordRound(static_cast<float>(emHeight) * + ReflowInput::kNormalLineHeightFactor); } return emHeight + internalLeading + externalLeading; } static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh, - const nsStyleFont& aRelativeToFont, + const nsFont& aFont, nsAtom* aLanguage, + bool aExplicitLanguage, nsPresContext* aPresContext, bool aIsVertical, nscoord aBlockBSize, float aFontSizeInflation) { if (aLh.IsLength()) { nscoord result = aLh.AsLength().ToAppUnits(); if (aFontSizeInflation != 1.0f) { - result = NSToCoordRound(result * aFontSizeInflation); + result = NSToCoordRound(static_cast<float>(result) * aFontSizeInflation); } return result; } @@ -2748,8 +2761,7 @@ static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh, // For factor units the computed value of the line-height property // is found by multiplying the factor by the font's computed size // (adjusted for min-size prefs and text zoom). - return aRelativeToFont.mFont.size - .ScaledBy(aLh.AsNumber() * aFontSizeInflation) + return aFont.size.ScaledBy(aLh.AsNumber() * aFontSizeInflation) .ToAppUnits(); } @@ -2758,17 +2770,25 @@ static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh, return aBlockBSize; } - auto size = aRelativeToFont.mFont.size; + auto size = aFont.size; size.ScaleBy(aFontSizeInflation); if (aPresContext) { - RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetMetricsFor( - aPresContext, aIsVertical, &aRelativeToFont, size, - /* aUseUserFontSet = */ true); + nsFont font = aFont; + font.size = size; + nsFontMetrics::Params params; + params.language = aLanguage; + params.explicitLanguage = aExplicitLanguage; + params.orientation = + aIsVertical ? nsFontMetrics::eVertical : nsFontMetrics::eHorizontal; + params.userFontSet = aPresContext->GetUserFontSet(); + params.textPerf = aPresContext->GetTextPerfMetrics(); + params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup(); + RefPtr<nsFontMetrics> fm = aPresContext->GetMetricsFor(font, params); return GetNormalLineHeight(fm); } // If we don't have a pres context, use a 1.2em fallback. - size.ScaleBy(kNormalLineHeightFactor); + size.ScaleBy(ReflowInput::kNormalLineHeightFactor); return size.ToAppUnits(); } @@ -2816,8 +2836,9 @@ nscoord ReflowInput::CalcLineHeight( nsPresContext* aPresContext, bool aIsVertical, const nsIContent* aContent, nscoord aBlockBSize, float aFontSizeInflation) { nscoord lineHeight = - ComputeLineHeight(aLh, aRelativeToFont, aPresContext, aIsVertical, - aBlockBSize, aFontSizeInflation); + ComputeLineHeight(aLh, aRelativeToFont.mFont, aRelativeToFont.mLanguage, + aRelativeToFont.mExplicitLanguage, aPresContext, + aIsVertical, aBlockBSize, aFontSizeInflation); NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up"); @@ -2827,8 +2848,9 @@ nscoord ReflowInput::CalcLineHeight( // have a line-height smaller than 'normal'. if (!aLh.IsNormal()) { nscoord normal = ComputeLineHeight( - StyleLineHeight::Normal(), aRelativeToFont, aPresContext, aIsVertical, - aBlockBSize, aFontSizeInflation); + StyleLineHeight::Normal(), aRelativeToFont.mFont, + aRelativeToFont.mLanguage, aRelativeToFont.mExplicitLanguage, + aPresContext, aIsVertical, aBlockBSize, aFontSizeInflation); if (lineHeight < normal) { lineHeight = normal; } @@ -2838,6 +2860,17 @@ nscoord ReflowInput::CalcLineHeight( return lineHeight; } +nscoord ReflowInput::CalcLineHeightForCanvas(const StyleLineHeight& aLh, + const nsFont& aRelativeToFont, + nsAtom* aLanguage, + bool aExplicitLanguage, + nsPresContext* aPresContext, + mozilla::WritingMode aWM) { + return ComputeLineHeight(aLh, aRelativeToFont, aLanguage, aExplicitLanguage, + aPresContext, aWM.IsVertical() && !aWM.IsSideways(), + NS_UNCONSTRAINEDSIZE, 1.0f); +} + bool SizeComputationInput::ComputeMargin(WritingMode aCBWM, nscoord aPercentBasis, LayoutFrameType aFrameType) { diff --git a/layout/generic/ReflowInput.h b/layout/generic/ReflowInput.h index 620ad0f413..7bf7c4fc73 100644 --- a/layout/generic/ReflowInput.h +++ b/layout/generic/ReflowInput.h @@ -178,20 +178,6 @@ struct SizeComputationInput { const mozilla::Maybe<mozilla::LogicalMargin>& aPadding = mozilla::Nothing()); -#ifdef DEBUG - // Reflow trace methods. Defined in nsFrame.cpp so they have access - // to the display-reflow infrastructure. - static void* DisplayInitOffsetsEnter(nsIFrame* aFrame, - SizeComputationInput* aState, - nscoord aPercentBasis, - mozilla::WritingMode aCBWritingMode, - const nsMargin* aBorder, - const nsMargin* aPadding); - static void DisplayInitOffsetsExit(nsIFrame* aFrame, - SizeComputationInput* aState, - void* aValue); -#endif - private: /** * Computes margin values from the specified margin style information, and @@ -367,6 +353,16 @@ struct ReflowInput : public SizeComputationInput { mozilla::LogicalSize AvailableSize() const { return mAvailableSize; } mozilla::LogicalSize ComputedSize() const { return mComputedSize; } + + template <typename F> + mozilla::LogicalSize ComputedSizeWithBSizeFallback(F&& aFallback) const { + auto size = mComputedSize; + if (size.BSize(mWritingMode) == NS_UNCONSTRAINEDSIZE) { + size.BSize(mWritingMode) = ApplyMinMaxBSize(aFallback()); + } + return size; + } + mozilla::LogicalSize ComputedMinSize() const { return mComputedMinSize; } mozilla::LogicalSize ComputedMaxSize() const { return mComputedMaxSize; } @@ -745,6 +741,15 @@ struct ReflowInput : public SizeComputationInput { const nsIContent* aContent, nscoord aBlockBSize, float aFontSizeInflation); + static nscoord CalcLineHeightForCanvas(const StyleLineHeight& aLh, + const nsFont& aRelativeToFont, + nsAtom* aLanguage, + bool aExplicitLanguage, + nsPresContext* aPresContext, + mozilla::WritingMode aWM); + + static constexpr float kNormalLineHeightFactor = 1.2f; + mozilla::LogicalSize ComputeContainingBlockRectangle( nsPresContext* aPresContext, const ReflowInput* aContainingBlockRI) const; @@ -888,21 +893,6 @@ struct ReflowInput : public SizeComputationInput { LogicalMargin& aMargin, LogicalMargin& aOffsets); -#ifdef DEBUG - // Reflow trace methods. Defined in nsFrame.cpp so they have access - // to the display-reflow infrastructure. - static void* DisplayInitConstraintsEnter(nsIFrame* aFrame, - ReflowInput* aState, - nscoord aCBISize, nscoord aCBBSize, - const nsMargin* aBorder, - const nsMargin* aPadding); - static void DisplayInitConstraintsExit(nsIFrame* aFrame, ReflowInput* aState, - void* aValue); - static void* DisplayInitFrameTypeEnter(nsIFrame* aFrame, ReflowInput* aState); - static void DisplayInitFrameTypeExit(nsIFrame* aFrame, ReflowInput* aState, - void* aValue); -#endif - protected: void InitCBReflowInput(); void InitResizeFlags(nsPresContext* aPresContext, diff --git a/layout/generic/ScrollAnchorContainer.cpp b/layout/generic/ScrollAnchorContainer.cpp index 93336480f7..9bf17ec3d5 100644 --- a/layout/generic/ScrollAnchorContainer.cpp +++ b/layout/generic/ScrollAnchorContainer.cpp @@ -178,15 +178,15 @@ static nsRect FindScrollAnchoringBoundingRect(const nsIFrame* aScrollFrame, // axis of the scroll frame WritingMode writingMode = aScrollFrame->GetWritingMode(); switch (writingMode.GetBlockDir()) { - case WritingMode::eBlockTB: { + case WritingMode::BlockDir::TB: { overflowRect.SetBoxY(borderRect.Y(), overflowRect.YMost()); break; } - case WritingMode::eBlockLR: { + case WritingMode::BlockDir::LR: { overflowRect.SetBoxX(borderRect.X(), overflowRect.XMost()); break; } - case WritingMode::eBlockRL: { + case WritingMode::BlockDir::RL: { overflowRect.SetBoxX(overflowRect.X(), borderRect.XMost()); break; } @@ -522,15 +522,15 @@ void ScrollAnchorContainer::ApplyAdjustments() { nsPoint physicalAdjustment; switch (writingMode.GetBlockDir()) { - case WritingMode::eBlockTB: { + case WritingMode::BlockDir::TB: { physicalAdjustment.y = logicalAdjustment; break; } - case WritingMode::eBlockLR: { + case WritingMode::BlockDir::LR: { physicalAdjustment.x = logicalAdjustment; break; } - case WritingMode::eBlockRL: { + case WritingMode::BlockDir::RL: { physicalAdjustment.x = -logicalAdjustment; break; } diff --git a/layout/generic/ScrollGeneration.h b/layout/generic/ScrollGeneration.h index 90eb243e9e..5f14844e33 100644 --- a/layout/generic/ScrollGeneration.h +++ b/layout/generic/ScrollGeneration.h @@ -7,6 +7,7 @@ #include <cstdint> #include <iosfwd> +#include <tuple> namespace mozilla { @@ -23,7 +24,7 @@ std::ostream& operator<<(std::ostream& aStream, const ScrollGeneration<Tag>& aGen); template <typename Tag> -struct ScrollGeneration { +struct ScrollGeneration final { friend struct ScrollGenerationCounter; private: @@ -43,6 +44,8 @@ struct ScrollGeneration { friend std::ostream& operator<< <>(std::ostream& aStream, const ScrollGeneration<Tag>& aGen); + auto MutTiedFields() { return std::tie(mValue); } + private: uint64_t mValue; }; diff --git a/layout/generic/ScrollSnapTargetId.h b/layout/generic/ScrollSnapTargetId.h index f53e736fa1..a4a281f5e7 100644 --- a/layout/generic/ScrollSnapTargetId.h +++ b/layout/generic/ScrollSnapTargetId.h @@ -17,6 +17,7 @@ namespace mozilla { enum class ScrollSnapTargetId : uintptr_t { None = 0, }; +inline constexpr bool IsEnumCase(ScrollSnapTargetId) { return true; } struct ScrollSnapTargetIds { CopyableTArray<ScrollSnapTargetId> mIdsOnX; diff --git a/layout/generic/StickyScrollContainer.cpp b/layout/generic/StickyScrollContainer.cpp index 416bbbf4c4..e16c3af585 100644 --- a/layout/generic/StickyScrollContainer.cpp +++ b/layout/generic/StickyScrollContainer.cpp @@ -200,11 +200,13 @@ void StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsLayoutUtils::TransformRect(cbFrame, aFrame->GetParent(), *aContain); } else { *aContain = nsLayoutUtils::GetAllInFlowRectsUnion( - cbFrame, aFrame->GetParent(), nsLayoutUtils::RECTS_USE_CONTENT_BOX); + cbFrame, aFrame->GetParent(), + nsLayoutUtils::GetAllInFlowRectsFlag::UseContentBox); } nsRect marginRect = nsLayoutUtils::GetAllInFlowRectsUnion( - aFrame, aFrame->GetParent(), nsLayoutUtils::RECTS_USE_MARGIN_BOX); + aFrame, aFrame->GetParent(), + nsLayoutUtils::GetAllInFlowRectsFlag::UseMarginBoxWithAutoResolvedAsZero); // Deflate aContain by the difference between the union of aFrame's // continuations' margin boxes and the union of their border boxes, so that diff --git a/layout/generic/ViewportFrame.cpp b/layout/generic/ViewportFrame.cpp index 812aaa8e33..d71842d013 100644 --- a/layout/generic/ViewportFrame.cpp +++ b/layout/generic/ViewportFrame.cpp @@ -271,26 +271,16 @@ void ViewportFrame::RemoveFrame(DestroyContext& aContext, ChildListID aListID, /* virtual */ nscoord ViewportFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - if (mFrames.IsEmpty()) - result = 0; - else - result = mFrames.FirstChild()->GetMinISize(aRenderingContext); - - return result; + return mFrames.IsEmpty() + ? 0 + : mFrames.FirstChild()->GetMinISize(aRenderingContext); } /* virtual */ nscoord ViewportFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - if (mFrames.IsEmpty()) - result = 0; - else - result = mFrames.FirstChild()->GetPrefISize(aRenderingContext); - - return result; + return mFrames.IsEmpty() + ? 0 + : mFrames.FirstChild()->GetPrefISize(aRenderingContext); } nsPoint ViewportFrame::AdjustReflowInputForScrollbars( @@ -330,7 +320,6 @@ void ViewportFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("ViewportFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow"); diff --git a/layout/generic/WritingModes.h b/layout/generic/WritingModes.h index e84c5e276d..4f6e9eaec1 100644 --- a/layout/generic/WritingModes.h +++ b/layout/generic/WritingModes.h @@ -11,10 +11,8 @@ #include "mozilla/intl/BidiEmbeddingLevel.h" #include "mozilla/ComputedStyle.h" -#include "mozilla/EnumeratedRange.h" - +#include "mozilla/EnumSet.h" #include "nsRect.h" -#include "nsBidiUtils.h" #include "nsStyleStruct.h" // It is the caller's responsibility to operate on logical-coordinate objects @@ -46,21 +44,18 @@ struct IMENotification; } // namespace widget // Logical axis, edge, side and corner constants for use in various places. -enum LogicalAxis : uint8_t { - eLogicalAxisBlock = 0x0, - eLogicalAxisInline = 0x1 +enum class LogicalAxis : uint8_t { + Block, + Inline, }; -enum LogicalEdge { eLogicalEdgeStart = 0x0, eLogicalEdgeEnd = 0x1 }; -enum LogicalSide : uint8_t { - eLogicalSideBStart = (eLogicalAxisBlock << 1) | eLogicalEdgeStart, // 0x0 - eLogicalSideBEnd = (eLogicalAxisBlock << 1) | eLogicalEdgeEnd, // 0x1 - eLogicalSideIStart = (eLogicalAxisInline << 1) | eLogicalEdgeStart, // 0x2 - eLogicalSideIEnd = (eLogicalAxisInline << 1) | eLogicalEdgeEnd // 0x3 +enum class LogicalEdge : uint8_t { Start, End }; + +enum class LogicalSide : uint8_t { + BStart, + BEnd, + IStart, + IEnd, }; -constexpr auto AllLogicalSides() { - return mozilla::MakeInclusiveEnumeratedRange(eLogicalSideBStart, - eLogicalSideIEnd); -} enum class LogicalCorner : uint8_t { BStartIStart, @@ -70,7 +65,7 @@ enum class LogicalCorner : uint8_t { }; // Physical axis constants. -enum PhysicalAxis { eAxisVertical = 0x0, eAxisHorizontal = 0x1 }; +enum class PhysicalAxis : uint8_t { Vertical, Horizontal }; // Represents zero or more physical axes. enum class PhysicalAxes : uint8_t { @@ -82,51 +77,50 @@ enum class PhysicalAxes : uint8_t { MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(PhysicalAxes) inline LogicalAxis GetOrthogonalAxis(LogicalAxis aAxis) { - return aAxis == eLogicalAxisBlock ? eLogicalAxisInline : eLogicalAxisBlock; + return aAxis == LogicalAxis::Block ? LogicalAxis::Inline : LogicalAxis::Block; +} + +inline bool IsInline(LogicalSide aSide) { + return (aSide == LogicalSide::IStart) || (aSide == LogicalSide::IEnd); } -inline bool IsInline(LogicalSide aSide) { return aSide & 0x2; } inline bool IsBlock(LogicalSide aSide) { return !IsInline(aSide); } -inline bool IsEnd(LogicalSide aSide) { return aSide & 0x1; } + +inline bool IsEnd(LogicalSide aSide) { + return (aSide == LogicalSide::BEnd) || (aSide == LogicalSide::IEnd); +} + inline bool IsStart(LogicalSide aSide) { return !IsEnd(aSide); } inline LogicalAxis GetAxis(LogicalSide aSide) { - return IsInline(aSide) ? eLogicalAxisInline : eLogicalAxisBlock; + return IsInline(aSide) ? LogicalAxis::Inline : LogicalAxis::Block; } inline LogicalEdge GetEdge(LogicalSide aSide) { - return IsEnd(aSide) ? eLogicalEdgeEnd : eLogicalEdgeStart; + return IsEnd(aSide) ? LogicalEdge::End : LogicalEdge::Start; } inline LogicalEdge GetOppositeEdge(LogicalEdge aEdge) { - // This relies on the only two LogicalEdge enum values being 0 and 1. - return LogicalEdge(1 - aEdge); + return aEdge == LogicalEdge::Start ? LogicalEdge::End : LogicalEdge::Start; } inline LogicalSide MakeLogicalSide(LogicalAxis aAxis, LogicalEdge aEdge) { - return LogicalSide((aAxis << 1) | aEdge); + if (aAxis == LogicalAxis::Inline) { + return aEdge == LogicalEdge::Start ? LogicalSide::IStart + : LogicalSide::IEnd; + } + return aEdge == LogicalEdge::Start ? LogicalSide::BStart : LogicalSide::BEnd; } inline LogicalSide GetOppositeSide(LogicalSide aSide) { return MakeLogicalSide(GetAxis(aSide), GetOppositeEdge(GetEdge(aSide))); } -enum LogicalSideBits { - eLogicalSideBitsNone = 0, - eLogicalSideBitsBStart = 1 << eLogicalSideBStart, - eLogicalSideBitsBEnd = 1 << eLogicalSideBEnd, - eLogicalSideBitsIEnd = 1 << eLogicalSideIEnd, - eLogicalSideBitsIStart = 1 << eLogicalSideIStart, - eLogicalSideBitsBBoth = eLogicalSideBitsBStart | eLogicalSideBitsBEnd, - eLogicalSideBitsIBoth = eLogicalSideBitsIStart | eLogicalSideBitsIEnd, - eLogicalSideBitsAll = eLogicalSideBitsBBoth | eLogicalSideBitsIBoth -}; - -enum LineRelativeDir { - eLineRelativeDirOver = eLogicalSideBStart, - eLineRelativeDirUnder = eLogicalSideBEnd, - eLineRelativeDirLeft = eLogicalSideIStart, - eLineRelativeDirRight = eLogicalSideIEnd +enum class LineRelativeDir : uint8_t { + Over = static_cast<uint8_t>(LogicalSide::BStart), + Under = static_cast<uint8_t>(LogicalSide::BEnd), + Left = static_cast<uint8_t>(LogicalSide::IStart), + Right = static_cast<uint8_t>(LogicalSide::IEnd) }; /** @@ -146,54 +140,41 @@ class WritingMode { /** * Absolute inline flow direction */ - enum InlineDir { - eInlineLTR = 0x00, // text flows horizontally left to right - eInlineRTL = 0x02, // text flows horizontally right to left - eInlineTTB = 0x01, // text flows vertically top to bottom - eInlineBTT = 0x03, // text flows vertically bottom to top + enum class InlineDir : uint8_t { + LTR, // text flows horizontally left to right + RTL, // text flows horizontally right to left + TTB, // text flows vertically top to bottom + BTT, // text flows vertically bottom to top }; /** * Absolute block flow direction */ - enum BlockDir { - eBlockTB = 0x00, // horizontal lines stack top to bottom - eBlockRL = 0x01, // vertical lines stack right to left - eBlockLR = 0x05, // vertical lines stack left to right + enum class BlockDir : uint8_t { + TB, // horizontal lines stack top to bottom + RL, // vertical lines stack right to left + LR, // vertical lines stack left to right }; /** - * Line-relative (bidi-relative) inline flow direction - */ - enum BidiDir { - eBidiLTR = 0x00, // inline flow matches bidi LTR text - eBidiRTL = 0x10, // inline flow matches bidi RTL text - }; - - /** - * Unknown writing mode (should never actually be stored or used anywhere). - */ - enum { eUnknownWritingMode = 0xff }; - - /** * Return the absolute inline flow direction as an InlineDir */ InlineDir GetInlineDir() const { - return InlineDir(mWritingMode._0 & eInlineMask); + if (IsVertical()) { + return IsInlineReversed() ? InlineDir::BTT : InlineDir::TTB; + } + return IsInlineReversed() ? InlineDir::RTL : InlineDir::LTR; } /** * Return the absolute block flow direction as a BlockDir */ BlockDir GetBlockDir() const { - return BlockDir(mWritingMode._0 & eBlockMask); - } - - /** - * Return the line-relative inline flow direction as a BidiDir - */ - BidiDir GetBidiDir() const { - return BidiDir((mWritingMode & StyleWritingMode::RTL)._0); + if (IsVertical()) { + return mWritingMode & StyleWritingMode::VERTICAL_LR ? BlockDir::LR + : BlockDir::RL; + } + return BlockDir::TB; } /** @@ -207,14 +188,14 @@ class WritingMode { } /** - * Return true if bidi direction is LTR. (Convenience method) + * Return true if bidi direction is LTR. */ - bool IsBidiLTR() const { return eBidiLTR == GetBidiDir(); } + bool IsBidiLTR() const { return !IsBidiRTL(); } /** - * Return true if bidi direction is RTL. (Convenience method) + * Return true if bidi direction is RTL. */ - bool IsBidiRTL() const { return eBidiRTL == GetBidiDir(); } + bool IsBidiRTL() const { return !!(mWritingMode & StyleWritingMode::RTL); } /** * True if it is vertical and vertical-lr, or is horizontal and bidi LTR. @@ -233,12 +214,12 @@ class WritingMode { /** * True if vertical-mode block direction is LR (convenience method). */ - bool IsVerticalLR() const { return eBlockLR == GetBlockDir(); } + bool IsVerticalLR() const { return GetBlockDir() == BlockDir::LR; } /** * True if vertical-mode block direction is RL (convenience method). */ - bool IsVerticalRL() const { return eBlockRL == GetBlockDir(); } + bool IsVerticalRL() const { return GetBlockDir() == BlockDir::RL; } /** * True if vertical writing mode, i.e. when @@ -329,11 +310,13 @@ class WritingMode { static_assert(uint8_t(StyleWritingModeProperty::HorizontalTb) == 0 && uint8_t(StyleWritingModeProperty::VerticalRl) == 1 && uint8_t(StyleWritingModeProperty::VerticalLr) == 3 && - eLogicalAxisBlock == 0 && eLogicalAxisInline == 1 && - eAxisVertical == 0 && eAxisHorizontal == 1, + uint8_t(LogicalAxis::Block) == 0 && + uint8_t(LogicalAxis::Inline) == 1 && + uint8_t(PhysicalAxis::Vertical) == 0 && + uint8_t(PhysicalAxis::Horizontal) == 1, "unexpected writing-mode, logical axis or physical axis " "constant values"); - return mozilla::PhysicalAxis((aWritingModeValue ^ aAxis) & 0x1); + return mozilla::PhysicalAxis((aWritingModeValue ^ uint8_t(aAxis)) & 0x1); } mozilla::PhysicalAxis PhysicalAxis(LogicalAxis aAxis) const { @@ -366,7 +349,7 @@ class WritingMode { // What's left of the writing-mode should be in the range 0-3: NS_ASSERTION(aWritingModeValue < 4, "invalid aWritingModeValue value"); - return kLogicalBlockSides[aWritingModeValue][aEdge]; + return kLogicalBlockSides[aWritingModeValue][static_cast<uint8_t>(aEdge)]; } mozilla::Side PhysicalSideForInlineAxis(LogicalEdge aEdge) const { @@ -403,13 +386,13 @@ class WritingMode { // StyleWritingMode::INLINE_REVERSED, StyleWritingMode::VERTICAL_LR and // StyleWritingMode::LINE_INVERTED bits. Use these four bits to index into // kLogicalInlineSides. - MOZ_ASSERT(StyleWritingMode::VERTICAL._0 == 0x01 && - StyleWritingMode::INLINE_REVERSED._0 == 0x02 && - StyleWritingMode::VERTICAL_LR._0 == 0x04 && - StyleWritingMode::LINE_INVERTED._0 == 0x08, - "unexpected mask values"); - int index = mWritingMode._0 & 0x0F; - return kLogicalInlineSides[index][aEdge]; + static_assert(StyleWritingMode::VERTICAL._0 == 0x01 && + StyleWritingMode::INLINE_REVERSED._0 == 0x02 && + StyleWritingMode::VERTICAL_LR._0 == 0x04 && + StyleWritingMode::LINE_INVERTED._0 == 0x08, + "Unexpected values for StyleWritingMode constants!"); + uint8_t index = mWritingMode._0 & 0x0F; + return kLogicalInlineSides[index][static_cast<uint8_t>(aEdge)]; } /** @@ -418,9 +401,9 @@ class WritingMode { */ mozilla::Side PhysicalSide(LogicalSide aSide) const { if (IsBlock(aSide)) { - MOZ_ASSERT(StyleWritingMode::VERTICAL._0 == 0x01 && - StyleWritingMode::VERTICAL_LR._0 == 0x04, - "unexpected mask values"); + static_assert(StyleWritingMode::VERTICAL._0 == 0x01 && + StyleWritingMode::VERTICAL_LR._0 == 0x04, + "Unexpected values for StyleWritingMode constants!"); const uint8_t wm = ((mWritingMode & StyleWritingMode::VERTICAL_LR)._0 >> 1) | (mWritingMode & StyleWritingMode::VERTICAL)._0; @@ -445,47 +428,47 @@ class WritingMode { static const LogicalSide kPhysicalToLogicalSides[][4] = { // top right // bottom left - { eLogicalSideBStart, eLogicalSideIEnd, - eLogicalSideBEnd, eLogicalSideIStart }, // horizontal-tb ltr - { eLogicalSideIStart, eLogicalSideBStart, - eLogicalSideIEnd, eLogicalSideBEnd }, // vertical-rl ltr - { eLogicalSideBStart, eLogicalSideIStart, - eLogicalSideBEnd, eLogicalSideIEnd }, // horizontal-tb rtl - { eLogicalSideIEnd, eLogicalSideBStart, - eLogicalSideIStart, eLogicalSideBEnd }, // vertical-rl rtl - { eLogicalSideBEnd, eLogicalSideIStart, - eLogicalSideBStart, eLogicalSideIEnd }, // (horizontal-bt) (inv) ltr - { eLogicalSideIStart, eLogicalSideBEnd, - eLogicalSideIEnd, eLogicalSideBStart }, // vertical-lr sw-left rtl - { eLogicalSideBEnd, eLogicalSideIEnd, - eLogicalSideBStart, eLogicalSideIStart }, // (horizontal-bt) (inv) rtl - { eLogicalSideIEnd, eLogicalSideBEnd, - eLogicalSideIStart, eLogicalSideBStart }, // vertical-lr sw-left ltr - { eLogicalSideBStart, eLogicalSideIEnd, - eLogicalSideBEnd, eLogicalSideIStart }, // horizontal-tb (inv) rtl - { eLogicalSideIStart, eLogicalSideBStart, - eLogicalSideIEnd, eLogicalSideBEnd }, // vertical-rl sw-left rtl - { eLogicalSideBStart, eLogicalSideIStart, - eLogicalSideBEnd, eLogicalSideIEnd }, // horizontal-tb (inv) ltr - { eLogicalSideIEnd, eLogicalSideBStart, - eLogicalSideIStart, eLogicalSideBEnd }, // vertical-rl sw-left ltr - { eLogicalSideBEnd, eLogicalSideIEnd, - eLogicalSideBStart, eLogicalSideIStart }, // (horizontal-bt) ltr - { eLogicalSideIStart, eLogicalSideBEnd, - eLogicalSideIEnd, eLogicalSideBStart }, // vertical-lr ltr - { eLogicalSideBEnd, eLogicalSideIStart, - eLogicalSideBStart, eLogicalSideIEnd }, // (horizontal-bt) rtl - { eLogicalSideIEnd, eLogicalSideBEnd, - eLogicalSideIStart, eLogicalSideBStart }, // vertical-lr rtl + { LogicalSide::BStart, LogicalSide::IEnd, + LogicalSide::BEnd, LogicalSide::IStart }, // horizontal-tb ltr + { LogicalSide::IStart, LogicalSide::BStart, + LogicalSide::IEnd, LogicalSide::BEnd }, // vertical-rl ltr + { LogicalSide::BStart, LogicalSide::IStart, + LogicalSide::BEnd, LogicalSide::IEnd }, // horizontal-tb rtl + { LogicalSide::IEnd, LogicalSide::BStart, + LogicalSide::IStart, LogicalSide::BEnd }, // vertical-rl rtl + { LogicalSide::BEnd, LogicalSide::IStart, + LogicalSide::BStart, LogicalSide::IEnd }, // (horizontal-bt) (inv) ltr + { LogicalSide::IStart, LogicalSide::BEnd, + LogicalSide::IEnd, LogicalSide::BStart }, // vertical-lr sw-left rtl + { LogicalSide::BEnd, LogicalSide::IEnd, + LogicalSide::BStart, LogicalSide::IStart }, // (horizontal-bt) (inv) rtl + { LogicalSide::IEnd, LogicalSide::BEnd, + LogicalSide::IStart, LogicalSide::BStart }, // vertical-lr sw-left ltr + { LogicalSide::BStart, LogicalSide::IEnd, + LogicalSide::BEnd, LogicalSide::IStart }, // horizontal-tb (inv) rtl + { LogicalSide::IStart, LogicalSide::BStart, + LogicalSide::IEnd, LogicalSide::BEnd }, // vertical-rl sw-left rtl + { LogicalSide::BStart, LogicalSide::IStart, + LogicalSide::BEnd, LogicalSide::IEnd }, // horizontal-tb (inv) ltr + { LogicalSide::IEnd, LogicalSide::BStart, + LogicalSide::IStart, LogicalSide::BEnd }, // vertical-rl sw-left ltr + { LogicalSide::BEnd, LogicalSide::IEnd, + LogicalSide::BStart, LogicalSide::IStart }, // (horizontal-bt) ltr + { LogicalSide::IStart, LogicalSide::BEnd, + LogicalSide::IEnd, LogicalSide::BStart }, // vertical-lr ltr + { LogicalSide::BEnd, LogicalSide::IStart, + LogicalSide::BStart, LogicalSide::IEnd }, // (horizontal-bt) rtl + { LogicalSide::IEnd, LogicalSide::BEnd, + LogicalSide::IStart, LogicalSide::BStart }, // vertical-lr rtl }; // clang-format on - MOZ_ASSERT(StyleWritingMode::VERTICAL._0 == 0x01 && - StyleWritingMode::INLINE_REVERSED._0 == 0x02 && - StyleWritingMode::VERTICAL_LR._0 == 0x04 && - StyleWritingMode::LINE_INVERTED._0 == 0x08, - "unexpected mask values"); - int index = mWritingMode._0 & 0x0F; + static_assert(StyleWritingMode::VERTICAL._0 == 0x01 && + StyleWritingMode::INLINE_REVERSED._0 == 0x02 && + StyleWritingMode::VERTICAL_LR._0 == 0x04 && + StyleWritingMode::LINE_INVERTED._0 == 0x08, + "Unexpected values for StyleWritingMode constants!"); + uint8_t index = mWritingMode._0 & 0x0F; return kPhysicalToLogicalSides[index][aSide]; } @@ -570,7 +553,7 @@ class WritingMode { bool ParallelAxisStartsOnSameSide(LogicalAxis aLogicalAxis, const WritingMode& aOther) const { mozilla::Side myStartSide = - this->PhysicalSide(MakeLogicalSide(aLogicalAxis, eLogicalEdgeStart)); + this->PhysicalSide(MakeLogicalSide(aLogicalAxis, LogicalEdge::Start)); // Figure out which of aOther's axes is parallel to |this| WritingMode's // aLogicalAxis, and get its physical start side as well. @@ -578,7 +561,7 @@ class WritingMode { ? GetOrthogonalAxis(aLogicalAxis) : aLogicalAxis; mozilla::Side otherWMStartSide = - aOther.PhysicalSide(MakeLogicalSide(otherWMAxis, eLogicalEdgeStart)); + aOther.PhysicalSide(MakeLogicalSide(otherWMAxis, LogicalEdge::Start)); NS_ASSERTION(myStartSide % 2 == otherWMStartSide % 2, "Should end up with sides in the same physical axis"); @@ -601,10 +584,15 @@ class WritingMode { friend struct widget::IMENotification; /** + * Unknown writing mode (should never actually be stored or used anywhere). + */ + static constexpr uint8_t kUnknownWritingMode = 0xff; + + /** * Return a WritingMode representing an unknown value. */ static inline WritingMode Unknown() { - return WritingMode(eUnknownWritingMode); + return WritingMode(kUnknownWritingMode); } /** @@ -614,12 +602,6 @@ class WritingMode { explicit WritingMode(uint8_t aValue) : mWritingMode{aValue} {} StyleWritingMode mWritingMode; - - enum Masks { - // Masks for output enums - eInlineMask = 0x03, // VERTICAL | INLINE_REVERSED - eBlockMask = 0x05, // VERTICAL | VERTICAL_LR - }; }; inline std::ostream& operator<<(std::ostream& aStream, const WritingMode& aWM) { @@ -732,7 +714,7 @@ class LogicalPoint { return mPoint.y; } nscoord Pos(LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == eLogicalAxisInline ? I(aWM) : B(aWM); + return aAxis == LogicalAxis::Inline ? I(aWM) : B(aWM); } nscoord LineRelative(WritingMode aWritingMode, const nsSize& aContainerSize) const // line-axis @@ -761,7 +743,7 @@ class LogicalPoint { return mPoint.y; } nscoord& Pos(LogicalAxis aAxis, WritingMode aWM) { - return aAxis == eLogicalAxisInline ? I(aWM) : B(aWM); + return aAxis == LogicalAxis::Inline ? I(aWM) : B(aWM); } /** @@ -808,7 +790,7 @@ class LogicalPoint { LogicalPoint operator+(const LogicalPoint& aOther) const { CHECK_WRITING_MODE(aOther.GetWritingMode()); // In non-debug builds, LogicalPoint does not store the WritingMode, - // so the first parameter here (which will always be eUnknownWritingMode) + // so the first parameter here (which will always be WritingMode::Unknown()) // is ignored. return LogicalPoint(GetWritingMode(), mPoint.x + aOther.mPoint.x, mPoint.y + aOther.mPoint.y); @@ -824,7 +806,7 @@ class LogicalPoint { LogicalPoint operator-(const LogicalPoint& aOther) const { CHECK_WRITING_MODE(aOther.GetWritingMode()); // In non-debug builds, LogicalPoint does not store the WritingMode, - // so the first parameter here (which will always be eUnknownWritingMode) + // so the first parameter here (which will always be WritingMode::Unknown()) // is ignored. return LogicalPoint(GetWritingMode(), mPoint.x - aOther.mPoint.x, mPoint.y - aOther.mPoint.y); @@ -847,7 +829,7 @@ class LogicalPoint { /** * NOTE that in non-DEBUG builds, GetWritingMode() always returns - * eUnknownWritingMode, as the current mode is not stored in the logical- + * WritingMode::Unknown(), as the current mode is not stored in the logical- * geometry classes. Therefore, this method is private; it is used ONLY * by the DEBUG-mode checking macros in this class and its friends; * other code is not allowed to ask a logical point for its writing mode, @@ -952,7 +934,7 @@ class LogicalSize { return mSize.height; } nscoord Size(LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM); + return aAxis == LogicalAxis::Inline ? ISize(aWM) : BSize(aWM); } nscoord Width(WritingMode aWritingMode) const { @@ -978,7 +960,7 @@ class LogicalSize { return mSize.height; } nscoord& Size(LogicalAxis aAxis, WritingMode aWM) { - return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM); + return aAxis == LogicalAxis::Inline ? ISize(aWM) : BSize(aWM); } /** @@ -1097,48 +1079,51 @@ class LogicalSize { * LogicalSides represents a set of logical sides. */ struct LogicalSides final { + static constexpr EnumSet<LogicalSide> BBoth{LogicalSide::BStart, + LogicalSide::BEnd}; + static constexpr EnumSet<LogicalSide> IBoth{LogicalSide::IStart, + LogicalSide::IEnd}; + static constexpr EnumSet<LogicalSide> All{ + LogicalSide::BStart, LogicalSide::BEnd, LogicalSide::IStart, + LogicalSide::IEnd}; + explicit LogicalSides(WritingMode aWritingMode) +#ifdef DEBUG + : mWritingMode(aWritingMode) +#endif + { + } + LogicalSides(WritingMode aWritingMode, LogicalSides aSides) : #ifdef DEBUG mWritingMode(aWritingMode), #endif - mBits(0) { + mSides(aSides.mSides) { } - LogicalSides(WritingMode aWritingMode, LogicalSideBits aSideBits) + LogicalSides(WritingMode aWritingMode, EnumSet<LogicalSide> aSides) : #ifdef DEBUG mWritingMode(aWritingMode), #endif - mBits(aSideBits) { - MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits"); - } - bool IsEmpty() const { return mBits == 0; } - bool BStart() const { return mBits & eLogicalSideBitsBStart; } - bool BEnd() const { return mBits & eLogicalSideBitsBEnd; } - bool IStart() const { return mBits & eLogicalSideBitsIStart; } - bool IEnd() const { return mBits & eLogicalSideBitsIEnd; } - bool Contains(LogicalSideBits aSideBits) const { - MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits"); - return (mBits & aSideBits) == aSideBits; - } - LogicalSides operator|(LogicalSides aOther) const { - CHECK_WRITING_MODE(aOther.GetWritingMode()); - return *this | LogicalSideBits(aOther.mBits); - } - LogicalSides operator|(LogicalSideBits aSideBits) const { - return LogicalSides(GetWritingMode(), LogicalSideBits(mBits | aSideBits)); - } - LogicalSides& operator|=(LogicalSides aOther) { - CHECK_WRITING_MODE(aOther.GetWritingMode()); - return *this |= LogicalSideBits(aOther.mBits); + mSides(aSides) { + } + bool IsEmpty() const { return mSides.isEmpty(); } + bool BStart() const { return mSides.contains(LogicalSide::BStart); } + bool BEnd() const { return mSides.contains(LogicalSide::BEnd); } + bool IStart() const { return mSides.contains(LogicalSide::IStart); } + bool IEnd() const { return mSides.contains(LogicalSide::IEnd); } + bool Contains(LogicalSide aSide) const { return mSides.contains(aSide); } + LogicalSides& operator+=(LogicalSides aOther) { + mSides += aOther.mSides; + return *this; } - LogicalSides& operator|=(LogicalSideBits aSideBits) { - mBits |= aSideBits; + LogicalSides& operator+=(LogicalSide aOther) { + mSides += aOther; return *this; } bool operator==(LogicalSides aOther) const { CHECK_WRITING_MODE(aOther.GetWritingMode()); - return mBits == aOther.mBits; + return mSides == aOther.mSides; } bool operator!=(LogicalSides aOther) const { CHECK_WRITING_MODE(aOther.GetWritingMode()); @@ -1155,7 +1140,7 @@ struct LogicalSides final { #ifdef DEBUG WritingMode mWritingMode; #endif - uint8_t mBits; + EnumSet<LogicalSide> mSides; }; /** @@ -1234,10 +1219,10 @@ class LogicalMargin { return mMargin.bottom; } nscoord Start(LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM); + return aAxis == LogicalAxis::Inline ? IStart(aWM) : BStart(aWM); } nscoord End(LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM); + return aAxis == LogicalAxis::Inline ? IEnd(aWM) : BEnd(aWM); } nscoord& IStart(WritingMode aWritingMode) // inline-start margin @@ -1261,10 +1246,10 @@ class LogicalMargin { return mMargin.bottom; } nscoord& Start(LogicalAxis aAxis, WritingMode aWM) { - return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM); + return aAxis == LogicalAxis::Inline ? IStart(aWM) : BStart(aWM); } nscoord& End(LogicalAxis aAxis, WritingMode aWM) { - return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM); + return aAxis == LogicalAxis::Inline ? IEnd(aWM) : BEnd(aWM); } nscoord IStartEnd(WritingMode aWritingMode) const // inline margins @@ -1278,18 +1263,18 @@ class LogicalMargin { return mMargin.TopBottom(); } nscoord StartEnd(LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == eLogicalAxisInline ? IStartEnd(aWM) : BStartEnd(aWM); + return aAxis == LogicalAxis::Inline ? IStartEnd(aWM) : BStartEnd(aWM); } nscoord Side(LogicalSide aSide, WritingMode aWM) const { switch (aSide) { - case eLogicalSideBStart: + case LogicalSide::BStart: return BStart(aWM); - case eLogicalSideBEnd: + case LogicalSide::BEnd: return BEnd(aWM); - case eLogicalSideIStart: + case LogicalSide::IStart: return IStart(aWM); - case eLogicalSideIEnd: + case LogicalSide::IEnd: return IEnd(aWM); } @@ -1298,13 +1283,13 @@ class LogicalMargin { } nscoord& Side(LogicalSide aSide, WritingMode aWM) { switch (aSide) { - case eLogicalSideBStart: + case LogicalSide::BStart: return BStart(aWM); - case eLogicalSideBEnd: + case LogicalSide::BEnd: return BEnd(aWM); - case eLogicalSideIStart: + case LogicalSide::IStart: return IStart(aWM); - case eLogicalSideIEnd: + case LogicalSide::IEnd: return IEnd(aWM); } @@ -1646,13 +1631,13 @@ class LogicalRect { } nscoord Start(LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM); + return aAxis == LogicalAxis::Inline ? IStart(aWM) : BStart(aWM); } nscoord End(LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM); + return aAxis == LogicalAxis::Inline ? IEnd(aWM) : BEnd(aWM); } nscoord Size(LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM); + return aAxis == LogicalAxis::Inline ? ISize(aWM) : BSize(aWM); } /** @@ -1680,10 +1665,10 @@ class LogicalRect { return mBSize; } nscoord& Start(LogicalAxis aAxis, WritingMode aWM) { - return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM); + return aAxis == LogicalAxis::Inline ? IStart(aWM) : BStart(aWM); } nscoord& Size(LogicalAxis aAxis, WritingMode aWM) { - return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM); + return aAxis == LogicalAxis::Inline ? ISize(aWM) : BSize(aWM); } /** @@ -2055,22 +2040,22 @@ const T& StyleRect<T>::Get(WritingMode aWM, LogicalSide aSide) const { template <typename T> const T& StyleRect<T>::GetIStart(WritingMode aWM) const { - return Get(aWM, eLogicalSideIStart); + return Get(aWM, LogicalSide::IStart); } template <typename T> const T& StyleRect<T>::GetBStart(WritingMode aWM) const { - return Get(aWM, eLogicalSideBStart); + return Get(aWM, LogicalSide::BStart); } template <typename T> const T& StyleRect<T>::GetIEnd(WritingMode aWM) const { - return Get(aWM, eLogicalSideIEnd); + return Get(aWM, LogicalSide::IEnd); } template <typename T> const T& StyleRect<T>::GetBEnd(WritingMode aWM) const { - return Get(aWM, eLogicalSideBEnd); + return Get(aWM, LogicalSide::BEnd); } template <typename T> @@ -2080,38 +2065,38 @@ T& StyleRect<T>::Get(WritingMode aWM, LogicalSide aSide) { template <typename T> T& StyleRect<T>::GetIStart(WritingMode aWM) { - return Get(aWM, eLogicalSideIStart); + return Get(aWM, LogicalSide::IStart); } template <typename T> T& StyleRect<T>::GetBStart(WritingMode aWM) { - return Get(aWM, eLogicalSideBStart); + return Get(aWM, LogicalSide::BStart); } template <typename T> T& StyleRect<T>::GetIEnd(WritingMode aWM) { - return Get(aWM, eLogicalSideIEnd); + return Get(aWM, LogicalSide::IEnd); } template <typename T> T& StyleRect<T>::GetBEnd(WritingMode aWM) { - return Get(aWM, eLogicalSideBEnd); + return Get(aWM, LogicalSide::BEnd); } template <typename T> const T& StyleRect<T>::Start(mozilla::LogicalAxis aAxis, mozilla::WritingMode aWM) const { - return Get(aWM, aAxis == mozilla::eLogicalAxisInline - ? mozilla::eLogicalSideIStart - : mozilla::eLogicalSideBStart); + return Get(aWM, aAxis == mozilla::LogicalAxis::Inline + ? mozilla::LogicalSide::IStart + : mozilla::LogicalSide::BStart); } template <typename T> const T& StyleRect<T>::End(mozilla::LogicalAxis aAxis, mozilla::WritingMode aWM) const { - return Get(aWM, aAxis == mozilla::eLogicalAxisInline - ? mozilla::eLogicalSideIEnd - : mozilla::eLogicalSideBEnd); + return Get(aWM, aAxis == mozilla::LogicalAxis::Inline + ? mozilla::LogicalSide::IEnd + : mozilla::LogicalSide::BEnd); } inline AspectRatio AspectRatio::ConvertToWritingMode( @@ -2147,15 +2132,15 @@ inline const mozilla::StyleMaxSize& nsStylePosition::MaxBSize( } inline const mozilla::StyleSize& nsStylePosition::Size( mozilla::LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == mozilla::eLogicalAxisInline ? ISize(aWM) : BSize(aWM); + return aAxis == mozilla::LogicalAxis::Inline ? ISize(aWM) : BSize(aWM); } inline const mozilla::StyleSize& nsStylePosition::MinSize( mozilla::LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == mozilla::eLogicalAxisInline ? MinISize(aWM) : MinBSize(aWM); + return aAxis == mozilla::LogicalAxis::Inline ? MinISize(aWM) : MinBSize(aWM); } inline const mozilla::StyleMaxSize& nsStylePosition::MaxSize( mozilla::LogicalAxis aAxis, WritingMode aWM) const { - return aAxis == mozilla::eLogicalAxisInline ? MaxISize(aWM) : MaxBSize(aWM); + return aAxis == mozilla::LogicalAxis::Inline ? MaxISize(aWM) : MaxBSize(aWM); } inline bool nsStylePosition::ISizeDependsOnContainer(WritingMode aWM) const { @@ -2205,26 +2190,26 @@ inline bool nsStyleMargin::HasInlineAxisAuto(mozilla::WritingMode aWM) const { } inline bool nsStyleMargin::HasAuto(mozilla::LogicalAxis aAxis, mozilla::WritingMode aWM) const { - return aAxis == mozilla::eLogicalAxisInline ? HasInlineAxisAuto(aWM) - : HasBlockAxisAuto(aWM); + return aAxis == mozilla::LogicalAxis::Inline ? HasInlineAxisAuto(aWM) + : HasBlockAxisAuto(aWM); } inline mozilla::StyleAlignFlags nsStylePosition::UsedSelfAlignment( mozilla::LogicalAxis aAxis, const mozilla::ComputedStyle* aParent) const { - return aAxis == mozilla::eLogicalAxisBlock ? UsedAlignSelf(aParent)._0 - : UsedJustifySelf(aParent)._0; + return aAxis == mozilla::LogicalAxis::Block ? UsedAlignSelf(aParent)._0 + : UsedJustifySelf(aParent)._0; } inline mozilla::StyleContentDistribution nsStylePosition::UsedContentAlignment( mozilla::LogicalAxis aAxis) const { - return aAxis == mozilla::eLogicalAxisBlock ? mAlignContent : mJustifyContent; + return aAxis == mozilla::LogicalAxis::Block ? mAlignContent : mJustifyContent; } inline mozilla::StyleContentDistribution nsStylePosition::UsedTracksAlignment( mozilla::LogicalAxis aAxis, uint32_t aIndex) const { using T = mozilla::StyleAlignFlags; const auto& tracksAlignment = - aAxis == mozilla::eLogicalAxisBlock ? mAlignTracks : mJustifyTracks; + aAxis == mozilla::LogicalAxis::Block ? mAlignTracks : mJustifyTracks; if (MOZ_LIKELY(tracksAlignment.IsEmpty())) { // An empty array encodes the initial value, 'normal', which behaves as // 'start' for Grid containers. diff --git a/layout/generic/crashtests/1410243-1.html b/layout/generic/crashtests/1410243-1.html new file mode 100644 index 0000000000..42a005e85b --- /dev/null +++ b/layout/generic/crashtests/1410243-1.html @@ -0,0 +1,26 @@ +<!-- //DDBEGIN --> +<style> +* { margin-bottom: -1em; font-size: xx-large; } +.cl1 { Max-height: 93; column-width: 0; position: fixed; } +</style> +<script> +function eventhandler1() { +try { htmlvar0002.style.setProperty("display", "grid"); } catch(e) { } +try { svgvar0001.clientWidth; } catch(e) { } +try { htmlvar0002.style.setProperty("grid-template", "0px/0px"); } catch(e) { } +} +</script> +<details class="cl1" open="" ontoggle="eventhandler1()"> +<summary id="svgvar0001"/></summary> +<a> +<ol id="htmlvar0002"> +<keygen> +> +A +AA +A +A +A +A +<a> +<!-- //DDEND --> diff --git a/layout/generic/crashtests/1741488-1.html b/layout/generic/crashtests/1741488-1.html new file mode 100644 index 0000000000..74d7c49feb --- /dev/null +++ b/layout/generic/crashtests/1741488-1.html @@ -0,0 +1,23 @@ +<html class="reftest-wait"> +<style> +#b { + writing-mode: vertical-rl; + content: url(data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=); +} +</style> +<script> +let pp; +document.addEventListener("MozReftestInvalidate", () => { + a.after("1") + a.insertAdjacentHTML("afterEnd", b.outerHTML); + pp = SpecialPowers.wrap(self).printPreview(); + setTimeout(function() { + pp.close(); + document.documentElement.className = ""; + }, 400); +}) +</script> +<math> +<munder> +<mn id="a">2</mn> +<dl id="b"></dl> diff --git a/layout/generic/crashtests/1881375-1-helper.html b/layout/generic/crashtests/1881375-1-helper.html new file mode 100644 index 0000000000..5c79e4981f --- /dev/null +++ b/layout/generic/crashtests/1881375-1-helper.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<script> +// Notify our parent and reload ourselves. +function notifyAndReload() { + if (window.parent) { + window.parent.postMessage("reloaded", "*"); + } + window.location.reload(true) +} +</script> +<style> +*:nth-child(even) { + height: 16em; + list-style-image: url(data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=); +} +#a { + display: inline-grid; + grid-auto-flow: column dense; +} +#b { + padding-top: 8192vmax; +} +* { + align-content: center; + column-width: 1em; + float: inline-end; +} +</style> +<body onload="setTimeout(notifyAndReload, 0)"> +<picture></picture> +<iframe></iframe> +<isindex> +<svg></svg> +<samp id="a"> +<q> +<a id="b"> +</q> +<li> diff --git a/layout/generic/crashtests/1881375-1.html b/layout/generic/crashtests/1881375-1.html new file mode 100644 index 0000000000..8cef260433 --- /dev/null +++ b/layout/generic/crashtests/1881375-1.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<script> +// Loop count for the handler below. This value seems to be enough for the +// crashtest to be likely to trigger the crash. +const MAX_LOADS = 3; +let loadCount = 0; + +// Handler which gets invoked by the iframe whenever it completes a load +// (and dynamically reloads itself). This lets us allow the testcase to +// reload over and over (as seems to be required to trigger the crash), +// while also keeping the test duration reasonably bounded. +window.addEventListener( + "message", + (event) => { + if (loadCount++ == MAX_LOADS) { + // After MAX_LOADS reloads of the iframe, call ourselves done. + myIframe.remove(); + document.documentElement.className = ""; + } + } +); + +</script> +<iframe id="myIframe" src="1881375-1-helper.html"></iframe> diff --git a/layout/generic/crashtests/481921.ogg b/layout/generic/crashtests/481921.ogg Binary files differdeleted file mode 100644 index 0c41c3cd6b..0000000000 --- a/layout/generic/crashtests/481921.ogg +++ /dev/null diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 1c184739d7..9044b12f8d 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -394,7 +394,7 @@ load 571618-1.svg asserts(0-1) load 571975-1.html # bug 574889 load 571995.xhtml load 574958.xhtml -asserts(0-6) load 578977.html # bug 757305 +asserts(0-8) load 578977.html # bug 757305 load 580504-1.xhtml load 582793-1.html load 585598-1.xhtml @@ -622,8 +622,8 @@ 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||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 +load text-overflow-bug666751-1.html +load text-overflow-bug666751-2.html load text-overflow-bug670564.xhtml load text-overflow-bug671796.xhtml load text-overflow-bug713610.html @@ -678,6 +678,7 @@ load 1405443.html load 1405813.html load 1405896.html load 1406252-1.html +asserts(2) load 1410243-1.html # assertion is bug 574889 load 1415185.html load 1416544.html load 1427824.html @@ -804,6 +805,7 @@ load 1728319.html asserts(2-8) load 1730506.html # asserts from integer overflow & bogus sizes asserts(1-4) load 1730570.html # asserts from integer overflow & bogus sizes load 1734015.html +skip-if(Android) load 1741488-1.html # printPreview doesn't work on android load 1776079.html asserts(0-2) load 1791606.html load 1799749.html @@ -812,3 +814,4 @@ load 1816574.html load 1821603.html load 1822118.html load 1825434.html +asserts(0-85) load 1881375-1.html # bug 1890687 - assertions about fragmentation invariants, failing many times because the test runs in a loop diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index 3cfe7b5b2d..5afd9f33f2 100644 --- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -372,8 +372,8 @@ bool nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f, // and be positioned relative to the containing block right edge. // 'left' length and 'right' auto is the only combination we can be // sure of. - if ((wm.GetInlineDir() == WritingMode::eInlineRTL || - wm.GetBlockDir() == WritingMode::eBlockRL) && + if ((wm.GetInlineDir() == WritingMode::InlineDir::RTL || + wm.GetBlockDir() == WritingMode::BlockDir::RL) && !pos->mOffset.Get(eSideRight).IsAuto()) { return true; } @@ -383,7 +383,7 @@ bool nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f, return true; } // See comment above for width changes. - if (wm.GetInlineDir() == WritingMode::eInlineBTT && + if (wm.GetInlineDir() == WritingMode::InlineDir::BTT && !pos->mOffset.Get(eSideBottom).IsAuto()) { return true; } @@ -522,7 +522,7 @@ static nscoord OffsetToAlignedStaticPos(const ReflowInput& aKidReflowInput, return 0; // (leave the child at the start of its alignment container) } - nscoord alignAreaSizeInAxis = (pcAxis == eLogicalAxisInline) + nscoord alignAreaSizeInAxis = (pcAxis == LogicalAxis::Inline) ? alignAreaSize.ISize(pcWM) : alignAreaSize.BSize(pcWM); @@ -619,7 +619,7 @@ void nsAbsoluteContainingBlock::ResolveSizeDependentOffsets( placeholderContainer = GetPlaceholderContainer(aKidReflowInput.mFrame); nscoord offset = OffsetToAlignedStaticPos( aKidReflowInput, aKidSize, logicalCBSizeOuterWM, placeholderContainer, - outerWM, eLogicalAxisInline); + outerWM, LogicalAxis::Inline); // Shift IStart from its current position (at start corner of the // alignment container) by the returned offset. And set IEnd to the // distance between the kid's end edge to containing block's end edge. @@ -639,7 +639,7 @@ void nsAbsoluteContainingBlock::ResolveSizeDependentOffsets( } nscoord offset = OffsetToAlignedStaticPos( aKidReflowInput, aKidSize, logicalCBSizeOuterWM, placeholderContainer, - outerWM, eLogicalAxisBlock); + outerWM, LogicalAxis::Block); // Shift BStart from its current position (at start corner of the // alignment container) by the returned offset. And set BEnd to the // distance between the kid's end edge to containing block's end edge. diff --git a/layout/generic/nsBackdropFrame.cpp b/layout/generic/nsBackdropFrame.cpp index 20d3fcd368..178caa3e0c 100644 --- a/layout/generic/nsBackdropFrame.cpp +++ b/layout/generic/nsBackdropFrame.cpp @@ -70,7 +70,6 @@ void nsBackdropFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsBackdropFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); // Note that this frame is a child of the viewport frame. diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index a25e4e996e..4b314bfcaf 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -813,8 +813,6 @@ nscoord nsBlockFrame::GetMinISize(gfxContext* aRenderingContext) { return firstInFlow->GetMinISize(aRenderingContext); } - DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize); - CheckIntrinsicCacheAgainstShrinkWrapState(); if (mCachedMinISize != NS_INTRINSIC_ISIZE_UNKNOWN) { @@ -902,8 +900,6 @@ nscoord nsBlockFrame::GetPrefISize(gfxContext* aRenderingContext) { return firstInFlow->GetPrefISize(aRenderingContext); } - DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize); - CheckIntrinsicCacheAgainstShrinkWrapState(); if (mCachedPrefISize != NS_INTRINSIC_ISIZE_UNKNOWN) { @@ -1190,7 +1186,8 @@ static bool IsLineClampRoot(const nsBlockFrame* aFrame) { return false; } - if (StaticPrefs::layout_css_webkit_line_clamp_block_enabled()) { + if (StaticPrefs::layout_css_webkit_line_clamp_block_enabled() || + aFrame->PresContext()->Document()->ChromeRulesEnabled()) { return true; } @@ -1348,7 +1345,6 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsBlockFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); #ifdef DEBUG @@ -1453,14 +1449,18 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, // maintain when doing text-wrap: balance. struct BalanceTarget { // If line-clamp is in effect, mContent and mOffset indicate the starting - // position of the first line after the clamp limit. If line-clamp is not - // in use, mContent is null and mOffset is the total number of lines that - // the block must contain. + // position of the first line after the clamp limit, and mBlockCoord is the + // block-axis offset of its position. + // If line-clamp is not in use, mContent is null, mOffset is the total + // number of lines that the block must contain, and mBlockCoord is its end + // edge in the block direction. nsIContent* mContent = nullptr; int32_t mOffset = -1; + nscoord mBlockCoord = 0; bool operator==(const BalanceTarget& aOther) const { - return mContent == aOther.mContent && mOffset == aOther.mOffset; + return mContent == aOther.mContent && mOffset == aOther.mOffset && + mBlockCoord == aOther.mBlockCoord; } bool operator!=(const BalanceTarget& aOther) const { return !(*this == aOther); @@ -1506,7 +1506,7 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, auto* textFrame = static_cast<nsTextFrame*>(firstChild); offset = textFrame->GetContentOffset(); } - return BalanceTarget{content, offset}; + return BalanceTarget{content, offset, iter.get()->BStart()}; }; // "balancing" is implemented by shortening the effective inline-size of the @@ -1545,6 +1545,7 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, // no balancing is needed; just break from the balance loop. break; } + balanceTarget.mBlockCoord = mLines.back()->BEnd(); // Initialize the amount of inset to try, and the iteration step size. balanceStep = aReflowInput.ComputedISize() / balanceTarget.mOffset; trialState.ResetForBalance(balanceStep); @@ -1581,7 +1582,8 @@ void nsBlockFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, } int32_t numLines = countLinesUpTo(StaticPrefs::layout_css_text_wrap_balance_limit()); - return numLines == balanceTarget.mOffset; + return numLines == balanceTarget.mOffset && + mLines.back()->BEnd() == balanceTarget.mBlockCoord; }; // If we're in the process of a balance operation, check whether we've @@ -2817,7 +2819,7 @@ void nsBlockFrame::PropagateFloatDamage(BlockReflowState& aState, static bool LineHasClear(nsLineBox* aLine) { return aLine->IsBlock() - ? (aLine->HasForcedLineBreakBefore() || + ? (aLine->HasFloatClearTypeBefore() || aLine->mFirstChild->HasAnyStateBits( NS_BLOCK_HAS_CLEAR_CHILDREN) || !nsBlockFrame::BlockCanIntersectFloats(aLine->mFirstChild)) @@ -2984,7 +2986,7 @@ bool nsBlockFrame::ReflowDirtyLines(BlockReflowState& aState) { // We have to reflow the line if it's a block whose clearance // might have changed, so detect that. if (!line->IsDirty() && - (line->HasForcedLineBreakBefore() || floatAvoidingBlock)) { + (line->HasFloatClearTypeBefore() || floatAvoidingBlock)) { nscoord curBCoord = aState.mBCoord; // See where we would be after applying any clearance due to // BRs. @@ -3934,9 +3936,9 @@ bool nsBlockFrame::IsSelfEmpty() { const nsStyleBorder* border = StyleBorder(); const nsStylePadding* padding = StylePadding(); - if (border->GetComputedBorderWidth(wm.PhysicalSide(eLogicalSideBStart)) != + if (border->GetComputedBorderWidth(wm.PhysicalSide(LogicalSide::BStart)) != 0 || - border->GetComputedBorderWidth(wm.PhysicalSide(eLogicalSideBEnd)) != 0 || + border->GetComputedBorderWidth(wm.PhysicalSide(LogicalSide::BEnd)) != 0 || !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBStart(wm)) || !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBEnd(wm))) { return false; @@ -4051,7 +4053,7 @@ void nsBlockFrame::ReflowBlockFrame(BlockReflowState& aState, // Clear past floats before the block if the clear style is not none aLine->ClearForcedLineBreak(); if (clearType != StyleClear::None) { - aLine->SetForcedLineBreakBefore(clearType); + aLine->SetFloatClearTypeBefore(clearType); } // See if we should apply the block-start margin. If the block frame being @@ -7978,7 +7980,7 @@ bool nsBlockFrame::MarkerIsEmpty() const { const nsStyleList* list = marker->StyleList(); return marker->StyleContent()->mContent.IsNone() || (list->mCounterStyle.IsNone() && list->mListStyleImage.IsNone() && - marker->StyleContent()->ContentCount() == 0); + marker->StyleContent()->NonAltContentItems().IsEmpty()); } void nsBlockFrame::ReflowOutsideMarker(nsIFrame* aMarkerFrame, diff --git a/layout/generic/nsCanvasFrame.cpp b/layout/generic/nsCanvasFrame.cpp index 64c56b1c00..36b579ed60 100644 --- a/layout/generic/nsCanvasFrame.cpp +++ b/layout/generic/nsCanvasFrame.cpp @@ -546,6 +546,8 @@ void nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, layers.mImageCount > 0 && layers.mLayers[0].mAttachment == StyleImageLayerAttachment::Fixed; + nsDisplayList list(aBuilder); + if (!hasFixedBottomLayer || needBlendContainer) { // Put a scrolled background color item in place, at the bottom of the // list. The color of this item will be filled in during @@ -557,20 +559,18 @@ void nsCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, // interleaving the two with a scrolled background color. // PresShell::AddCanvasBackgroundColorItem makes sure there always is a // non-scrolled background color item at the bottom. - aLists.BorderBackground()->AppendNewToTop<nsDisplayCanvasBackgroundColor>( - aBuilder, this); + list.AppendNewToTop<nsDisplayCanvasBackgroundColor>(aBuilder, this); } - aLists.BorderBackground()->AppendToTop(&layerItems); + list.AppendToTop(&layerItems); if (needBlendContainer) { const ActiveScrolledRoot* containerASR = contASRTracker.GetContainerASR(); DisplayListClipState::AutoSaveRestore blendContainerClip(aBuilder); - aLists.BorderBackground()->AppendToTop( - nsDisplayBlendContainer::CreateForBackgroundBlendMode( - aBuilder, this, nullptr, aLists.BorderBackground(), - containerASR)); + list.AppendToTop(nsDisplayBlendContainer::CreateForBackgroundBlendMode( + aBuilder, this, nullptr, &list, containerASR)); } + aLists.BorderBackground()->AppendToTop(&list); } for (nsIFrame* kid : PrincipalChildList()) { @@ -606,24 +606,16 @@ void nsCanvasFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt) { /* virtual */ nscoord nsCanvasFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - if (mFrames.IsEmpty()) - result = 0; - else - result = mFrames.FirstChild()->GetMinISize(aRenderingContext); - return result; + return mFrames.IsEmpty() + ? 0 + : mFrames.FirstChild()->GetMinISize(aRenderingContext); } /* virtual */ nscoord nsCanvasFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - if (mFrames.IsEmpty()) - result = 0; - else - result = mFrames.FirstChild()->GetPrefISize(aRenderingContext); - return result; + return mFrames.IsEmpty() + ? 0 + : mFrames.FirstChild()->GetPrefISize(aRenderingContext); } void nsCanvasFrame::Reflow(nsPresContext* aPresContext, @@ -632,7 +624,6 @@ void nsCanvasFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow"); diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp index cd97519a98..d78a5cb992 100644 --- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -408,7 +408,6 @@ static void MoveChildTo(nsIFrame* aChild, LogicalPoint aOrigin, WritingMode aWM, nscoord nsColumnSetFrame::GetMinISize(gfxContext* aRenderingContext) { nscoord iSize = 0; - DISPLAY_MIN_INLINE_SIZE(this, iSize); if (mFrames.FirstChild()) { // We want to ignore this in the case that we're size contained @@ -443,8 +442,6 @@ nscoord nsColumnSetFrame::GetPrefISize(gfxContext* aRenderingContext) { // the child's preferred width, times the number of columns, plus the width // of any required column gaps // XXX what about forced column breaks here? - nscoord result = 0; - DISPLAY_PREF_INLINE_SIZE(this, result); const nsStyleColumn* colStyle = StyleColumn(); nscoord colISize; @@ -466,8 +463,7 @@ nscoord nsColumnSetFrame::GetPrefISize(gfxContext* aRenderingContext) { ? 1 : colStyle->mColumnCount; nscoord colGap = ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE); - result = ColumnUtils::IntrinsicISize(numColumns, colGap, colISize); - return result; + return ColumnUtils::IntrinsicISize(numColumns, colGap, colISize); } nsColumnSetFrame::ColumnBalanceData nsColumnSetFrame::ReflowColumns( @@ -574,17 +570,17 @@ nsColumnSetFrame::ColumnBalanceData nsColumnSetFrame::ReflowColumns( // this is a calculation that affects layout. if (!reflowChild && shrinkingBSize) { switch (wm.GetBlockDir()) { - case WritingMode::eBlockTB: + case WritingMode::BlockDir::TB: if (child->ScrollableOverflowRect().YMost() > aConfig.mColBSize) { reflowChild = true; } break; - case WritingMode::eBlockLR: + case WritingMode::BlockDir::LR: if (child->ScrollableOverflowRect().XMost() > aConfig.mColBSize) { reflowChild = true; } break; - case WritingMode::eBlockRL: + case WritingMode::BlockDir::RL: // XXX not sure how to handle this, so for now just don't attempt // the optimization reflowChild = true; @@ -1202,7 +1198,6 @@ void nsColumnSetFrame::Reflow(nsPresContext* aPresContext, nsPresContext::InterruptPreventer noInterrupts(aPresContext); DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); MOZ_ASSERT(aReflowInput.mCBReflowInput->mFrame->StyleColumn() diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index 2577a7a00d..3cebfae2e7 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -780,7 +780,7 @@ void nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext, if (!(aFlags & ReflowChildFlags::NoSizeView)) { nsViewManager* vm = aView->GetViewManager(); - vm->ResizeView(aView, aInkOverflowArea, true); + vm->ResizeView(aView, aInkOverflowArea); } } @@ -814,8 +814,7 @@ LogicalSize nsContainerFrame::ComputeAutoSize( LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE); nscoord availBased = aAvailableISize - aMargin.ISize(aWM) - aBorderPadding.ISize(aWM); - // replaced elements always shrink-wrap - if (aFlags.contains(ComputeSizeFlag::ShrinkWrap) || IsReplaced()) { + if (aFlags.contains(ComputeSizeFlag::ShrinkWrap)) { // Only bother computing our 'auto' ISize if the result will be used. const auto& styleISize = aSizeOverrides.mStyleISize ? *aSizeOverrides.mStyleISize @@ -2201,9 +2200,9 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( // This variable only gets meaningfully set if isFlexItem is true. It // indicates which axis (in this frame's own WM) corresponds to its // flex container's main axis. - LogicalAxis flexMainAxis = eLogicalAxisBlock; + LogicalAxis flexMainAxis = LogicalAxis::Block; if (isFlexItem && nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)) { - flexMainAxis = eLogicalAxisInline; + flexMainAxis = LogicalAxis::Inline; } // Handle intrinsic sizes and their interaction with @@ -2263,8 +2262,8 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( aSizeOverrides, aFlags) .mISize; } else if (MOZ_UNLIKELY(isGridItem) && - !parentFrame->IsMasonry(isOrthogonal ? eLogicalAxisBlock - : eLogicalAxisInline)) { + !parentFrame->IsMasonry(isOrthogonal ? LogicalAxis::Block + : LogicalAxis::Inline)) { MOZ_ASSERT(!IsTrueOverflowContainer()); // 'auto' inline-size for grid-level box - apply 'stretch' as needed: auto cbSize = aCBSize.ISize(aWM); @@ -2291,7 +2290,7 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( const auto& maxISizeCoord = stylePos->MaxISize(aWM); if (!maxISizeCoord.IsNone() && - !(isFlexItem && flexMainAxis == eLogicalAxisInline)) { + !(isFlexItem && flexMainAxis == LogicalAxis::Inline)) { maxISize = ComputeISizeValue(aRenderingContext, aWM, aCBSize, boxSizingAdjust, boxSizingToMarginEdgeISize, maxISizeCoord, aSizeOverrides, aFlags) @@ -2307,7 +2306,7 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( const auto& minISizeCoord = stylePos->MinISize(aWM); if (!minISizeCoord.IsAuto() && - !(isFlexItem && flexMainAxis == eLogicalAxisInline)) { + !(isFlexItem && flexMainAxis == LogicalAxis::Inline)) { minISize = ComputeISizeValue(aRenderingContext, aWM, aCBSize, boxSizingAdjust, boxSizingToMarginEdgeISize, minISizeCoord, aSizeOverrides, aFlags) @@ -2326,8 +2325,8 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( boxSizingAdjust.BSize(aWM), styleBSize.AsLengthPercentage()); } else if (MOZ_UNLIKELY(isGridItem) && - !parentFrame->IsMasonry(isOrthogonal ? eLogicalAxisInline - : eLogicalAxisBlock)) { + !parentFrame->IsMasonry(isOrthogonal ? LogicalAxis::Inline + : LogicalAxis::Block)) { MOZ_ASSERT(!IsTrueOverflowContainer()); // 'auto' block-size for grid-level box - apply 'stretch' as needed: auto cbSize = aCBSize.BSize(aWM); @@ -2354,7 +2353,7 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( const auto& maxBSizeCoord = stylePos->MaxBSize(aWM); if (!nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) && - !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) { + !(isFlexItem && flexMainAxis == LogicalAxis::Block)) { maxBSize = nsLayoutUtils::ComputeBSizeValue( aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), maxBSizeCoord.AsLengthPercentage()); @@ -2365,7 +2364,7 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( const auto& minBSizeCoord = stylePos->MinBSize(aWM); if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) && - !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) { + !(isFlexItem && flexMainAxis == LogicalAxis::Block)) { minBSize = nsLayoutUtils::ComputeBSizeValue( aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), minBSizeCoord.AsLengthPercentage()); @@ -2389,8 +2388,7 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( tentISize = intrinsicISize; } else if (hasIntrinsicBSize && aspectRatio) { tentISize = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, aWM, intrinsicBSize, - boxSizingAdjust); + LogicalAxis::Inline, aWM, intrinsicBSize, boxSizingAdjust); } else if (aspectRatio) { tentISize = aCBSize.ISize(aWM) - boxSizingToMarginEdgeISize; // XXX scrollbar? @@ -2413,7 +2411,7 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( tentBSize = intrinsicBSize; } else if (aspectRatio) { tentBSize = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisBlock, aWM, tentISize, boxSizingAdjust); + LogicalAxis::Block, aWM, tentISize, boxSizingAdjust); } else { tentBSize = fallbackIntrinsicSize.BSize(aWM); } @@ -2431,29 +2429,29 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( } else if (stretchB == eStretchPreservingRatio && aspectRatio) { // 'normal' / 'stretch' tentBSize = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisBlock, aWM, iSize, boxSizingAdjust); + LogicalAxis::Block, aWM, iSize, boxSizingAdjust); } } else if (stretchB == eStretch) { tentBSize = bSize; // 'stretch' / * (except 'stretch') if (stretchI == eStretchPreservingRatio && aspectRatio) { // 'stretch' / 'normal' tentISize = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, aWM, bSize, boxSizingAdjust); + LogicalAxis::Inline, aWM, bSize, boxSizingAdjust); } } else if (stretchI == eStretchPreservingRatio && aspectRatio) { tentISize = iSize; // * (except 'stretch') / 'normal' tentBSize = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisBlock, aWM, iSize, boxSizingAdjust); + LogicalAxis::Block, aWM, iSize, boxSizingAdjust); if (stretchB == eStretchPreservingRatio && tentBSize > bSize) { // Stretch within the CB size with preserved intrinsic ratio. tentBSize = bSize; // 'normal' / 'normal' tentISize = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, aWM, bSize, boxSizingAdjust); + LogicalAxis::Inline, aWM, bSize, boxSizingAdjust); } } else if (stretchB == eStretchPreservingRatio && aspectRatio) { tentBSize = bSize; // 'normal' / * (except 'normal' and 'stretch') tentISize = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, aWM, bSize, boxSizingAdjust); + LogicalAxis::Inline, aWM, bSize, boxSizingAdjust); } // ComputeAutoSizeWithIntrinsicDimensions preserves the ratio when @@ -2480,7 +2478,7 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( if (stretchI != eStretch) { if (aspectRatio) { iSize = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, aWM, bSize, boxSizingAdjust); + LogicalAxis::Inline, aWM, bSize, boxSizingAdjust); } else if (hasIntrinsicISize) { if (!(aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize) && intrinsicISize > iSize)) { @@ -2498,8 +2496,8 @@ LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions( iSize = NS_CSS_MINMAX(iSize, minISize, maxISize); if (stretchB != eStretch) { if (aspectRatio) { - bSize = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisBlock, aWM, iSize, boxSizingAdjust); + bSize = aspectRatio.ComputeRatioDependentSize(LogicalAxis::Block, aWM, + iSize, boxSizingAdjust); } else if (hasIntrinsicBSize) { if (!(aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize) && intrinsicBSize > bSize)) { diff --git a/layout/generic/nsContainerFrame.h b/layout/generic/nsContainerFrame.h index 09f6cf76f5..cb0a09db20 100644 --- a/layout/generic/nsContainerFrame.h +++ b/layout/generic/nsContainerFrame.h @@ -1076,126 +1076,4 @@ class nsOverflowContinuationTracker { bool mWalkOOFFrames; }; -// Start Display Reflow Debugging -#ifdef DEBUG - -struct DR_cookie { - DR_cookie(nsPresContext* aPresContext, nsIFrame* aFrame, - const mozilla::ReflowInput& aReflowInput, - mozilla::ReflowOutput& aMetrics, nsReflowStatus& aStatus); - ~DR_cookie(); - void Change() const; - - nsPresContext* mPresContext; - nsIFrame* mFrame; - const mozilla::ReflowInput& mReflowInput; - mozilla::ReflowOutput& mMetrics; - nsReflowStatus& mStatus; - void* mValue; -}; - -struct DR_layout_cookie { - explicit DR_layout_cookie(nsIFrame* aFrame); - ~DR_layout_cookie(); - - nsIFrame* mFrame; - void* mValue; -}; - -struct DR_intrinsic_inline_size_cookie { - DR_intrinsic_inline_size_cookie(nsIFrame* aFrame, const char* aType, - nscoord& aResult); - ~DR_intrinsic_inline_size_cookie(); - - nsIFrame* mFrame; - const char* mType; - nscoord& mResult; - void* mValue; -}; - -struct DR_intrinsic_size_cookie { - DR_intrinsic_size_cookie(nsIFrame* aFrame, const char* aType, - nsSize& aResult); - ~DR_intrinsic_size_cookie(); - - nsIFrame* mFrame; - const char* mType; - nsSize& mResult; - void* mValue; -}; - -struct DR_init_constraints_cookie { - DR_init_constraints_cookie( - nsIFrame* aFrame, mozilla::ReflowInput* aState, nscoord aCBWidth, - nscoord aCBHeight, const mozilla::Maybe<mozilla::LogicalMargin> aBorder, - const mozilla::Maybe<mozilla::LogicalMargin> aPadding); - ~DR_init_constraints_cookie(); - - nsIFrame* mFrame; - mozilla::ReflowInput* mState; - void* mValue; -}; - -struct DR_init_offsets_cookie { - DR_init_offsets_cookie(nsIFrame* aFrame, - mozilla::SizeComputationInput* aState, - nscoord aPercentBasis, - mozilla::WritingMode aCBWritingMode, - const mozilla::Maybe<mozilla::LogicalMargin> aBorder, - const mozilla::Maybe<mozilla::LogicalMargin> aPadding); - ~DR_init_offsets_cookie(); - - nsIFrame* mFrame; - mozilla::SizeComputationInput* mState; - void* mValue; -}; - -# define DISPLAY_REFLOW(dr_pres_context, dr_frame, dr_rf_state, \ - dr_rf_metrics, dr_rf_status) \ - DR_cookie dr_cookie(dr_pres_context, dr_frame, dr_rf_state, dr_rf_metrics, \ - dr_rf_status); -# define DISPLAY_REFLOW_CHANGE() dr_cookie.Change(); -# define DISPLAY_LAYOUT(dr_frame) DR_layout_cookie dr_cookie(dr_frame); -# define DISPLAY_MIN_INLINE_SIZE(dr_frame, dr_result) \ - DR_intrinsic_inline_size_cookie dr_cookie(dr_frame, "Min", dr_result) -# define DISPLAY_PREF_INLINE_SIZE(dr_frame, dr_result) \ - DR_intrinsic_inline_size_cookie dr_cookie(dr_frame, "Pref", dr_result) -# define DISPLAY_PREF_SIZE(dr_frame, dr_result) \ - DR_intrinsic_size_cookie dr_cookie(dr_frame, "Pref", dr_result) -# define DISPLAY_MIN_SIZE(dr_frame, dr_result) \ - DR_intrinsic_size_cookie dr_cookie(dr_frame, "Min", dr_result) -# define DISPLAY_MAX_SIZE(dr_frame, dr_result) \ - DR_intrinsic_size_cookie dr_cookie(dr_frame, "Max", dr_result) -# define DISPLAY_INIT_CONSTRAINTS(dr_frame, dr_state, dr_cbw, dr_cbh, dr_bdr, \ - dr_pad) \ - DR_init_constraints_cookie dr_cookie(dr_frame, dr_state, dr_cbw, dr_cbh, \ - dr_bdr, dr_pad) -# define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_pb, dr_cbwm, dr_bdr, \ - dr_pad) \ - DR_init_offsets_cookie dr_cookie(dr_frame, dr_state, dr_pb, dr_cbwm, \ - dr_bdr, dr_pad) - -#else - -# define DISPLAY_REFLOW(dr_pres_context, dr_frame, dr_rf_state, \ - dr_rf_metrics, dr_rf_status) -# define DISPLAY_REFLOW_CHANGE() -# define DISPLAY_LAYOUT(dr_frame) PR_BEGIN_MACRO PR_END_MACRO -# define DISPLAY_MIN_INLINE_SIZE(dr_frame, dr_result) \ - PR_BEGIN_MACRO PR_END_MACRO -# define DISPLAY_PREF_INLINE_SIZE(dr_frame, dr_result) \ - PR_BEGIN_MACRO PR_END_MACRO -# define DISPLAY_PREF_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO -# define DISPLAY_MIN_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO -# define DISPLAY_MAX_SIZE(dr_frame, dr_result) PR_BEGIN_MACRO PR_END_MACRO -# define DISPLAY_INIT_CONSTRAINTS(dr_frame, dr_state, dr_cbw, dr_cbh, dr_bdr, \ - dr_pad) \ - PR_BEGIN_MACRO PR_END_MACRO -# define DISPLAY_INIT_OFFSETS(dr_frame, dr_state, dr_pb, dr_cbwm, dr_bdr, \ - dr_pad) \ - PR_BEGIN_MACRO PR_END_MACRO - -#endif -// End Display Reflow Debugging - #endif /* nsContainerFrame_h___ */ diff --git a/layout/generic/nsContainerFrameInlines.h b/layout/generic/nsContainerFrameInlines.h index f6c85d791e..bc544fa6f8 100644 --- a/layout/generic/nsContainerFrameInlines.h +++ b/layout/generic/nsContainerFrameInlines.h @@ -21,8 +21,8 @@ void nsContainerFrame::DoInlineIntrinsicISize(ISizeData* aData, if (GetPrevInFlow()) return; // Already added. WritingMode wm = GetWritingMode(); - Side startSide = wm.PhysicalSideForInlineAxis(eLogicalEdgeStart); - Side endSide = wm.PhysicalSideForInlineAxis(eLogicalEdgeEnd); + Side startSide = wm.PhysicalSideForInlineAxis(LogicalEdge::Start); + Side endSide = wm.PhysicalSideForInlineAxis(LogicalEdge::End); const nsStylePadding* stylePadding = StylePadding(); const nsStyleBorder* styleBorder = StyleBorder(); diff --git a/layout/generic/nsFirstLetterFrame.cpp b/layout/generic/nsFirstLetterFrame.cpp index fe294652c7..c08950121a 100644 --- a/layout/generic/nsFirstLetterFrame.cpp +++ b/layout/generic/nsFirstLetterFrame.cpp @@ -197,7 +197,6 @@ void nsFirstLetterFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aReflowStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aReflowStatus); MOZ_ASSERT(aReflowStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); @@ -448,12 +447,12 @@ Maybe<nscoord> nsFirstLetterFrame::GetNaturalBaselineBOffset( LogicalSides nsFirstLetterFrame::GetLogicalSkipSides() const { if (GetPrevContinuation()) { - // We shouldn't get calls to GetSkipSides for later continuations since - // they have separate ComputedStyles with initial values for all the - // properties that could trigger a call to GetSkipSides. Then again, - // it's not really an error to call GetSkipSides on any frame, so + // We shouldn't get calls to GetLogicalSkipSides for later continuations + // since they have separate ComputedStyles with initial values for all the + // properties that could trigger a call to GetLogicalSkipSides. Then again, + // it's not really an error to call GetLogicalSkipSides on any frame, so // that's why we handle it properly. - return LogicalSides(mWritingMode, eLogicalSideBitsAll); + return LogicalSides(mWritingMode, LogicalSides::All); } return LogicalSides(mWritingMode); // first continuation displays all sides } diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp index e5a719fee2..4604106a4a 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -14,14 +14,12 @@ #include "mozilla/Baseline.h" #include "mozilla/ComputedStyle.h" #include "mozilla/CSSOrderAwareFrameIterator.h" -#include "mozilla/FloatingPoint.h" #include "mozilla/Logging.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs_layout.h" #include "mozilla/WritingModes.h" #include "nsBlockFrame.h" #include "nsContentUtils.h" -#include "nsCSSAnonBoxes.h" #include "nsDebug.h" #include "nsDisplayList.h" #include "nsFieldSetFrame.h" @@ -43,10 +41,25 @@ using CachedBAxisMeasurement = nsFlexContainerFrame::CachedBAxisMeasurement; using CachedFlexItemData = nsFlexContainerFrame::CachedFlexItemData; static mozilla::LazyLogModule gFlexContainerLog("FlexContainer"); -#define FLEX_LOG(...) \ - MOZ_LOG(gFlexContainerLog, LogLevel::Debug, (__VA_ARGS__)); -#define FLEX_LOGV(...) \ - MOZ_LOG(gFlexContainerLog, LogLevel::Verbose, (__VA_ARGS__)); + +// FLEX_LOG is a top-level general log print. +#define FLEX_LOG(message, ...) \ + MOZ_LOG(gFlexContainerLog, LogLevel::Debug, (message, ##__VA_ARGS__)); + +// FLEX_ITEM_LOG is a top-level log print for flex item. +#define FLEX_ITEM_LOG(item_frame, message, ...) \ + MOZ_LOG(gFlexContainerLog, LogLevel::Debug, \ + ("Flex item %p: " message, item_frame, ##__VA_ARGS__)); + +// FLEX_LOGV is a verbose log print with built-in two spaces indentation. The +// convention to use FLEX_LOGV is that FLEX_LOGV statements should generally be +// preceded by one FLEX_LOG or FLEX_ITEM_LOG so that there's no need to repeat +// information presented in the preceding LOG statement. If you want extra level +// of indentation, just add two extra spaces at the start of the message string. +#define FLEX_LOGV(message, ...) \ + MOZ_LOG(gFlexContainerLog, LogLevel::Verbose, (" " message, ##__VA_ARGS__)); + +static const char* BoolToYesNo(bool aArg) { return aArg ? "yes" : "no"; } // Returns true if aFlexContainer is a frame for some element that has // display:-webkit-{inline-}box (or -moz-{inline-}box). aFlexContainer is @@ -143,10 +156,10 @@ class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker { // Accessors: LogicalAxis MainAxis() const { - return IsRowOriented() ? eLogicalAxisInline : eLogicalAxisBlock; + return IsRowOriented() ? LogicalAxis::Inline : LogicalAxis::Block; } LogicalAxis CrossAxis() const { - return IsRowOriented() ? eLogicalAxisBlock : eLogicalAxisInline; + return IsRowOriented() ? LogicalAxis::Block : LogicalAxis::Inline; } LogicalSide MainAxisStartSide() const; @@ -299,7 +312,7 @@ class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker { return StyleAlignFlags::START; } - MOZ_ASSERT(wm.PhysicalAxis(MainAxis()) == eAxisHorizontal, + MOZ_ASSERT(wm.PhysicalAxis(MainAxis()) == PhysicalAxis::Horizontal, "Vertical column-oriented flex container's main axis should " "be parallel to physical left <-> right axis!"); // Map 'left' or 'right' to 'start' or 'end', depending on its block flow @@ -308,7 +321,7 @@ class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker { : StyleAlignFlags::END; } - MOZ_ASSERT(MainAxis() == eLogicalAxisInline, + MOZ_ASSERT(MainAxis() == LogicalAxis::Inline, "Row-oriented flex container's main axis should be parallel to " "line-left <-> line-right axis!"); @@ -682,9 +695,8 @@ class nsFlexContainerFrame::FlexItem final { // to the [min,max] range: mMainSize = NS_CSS_MINMAX(mFlexBaseSize, mMainMinSize, mMainMaxSize); - FLEX_LOGV( - "Set flex base size: %d, hypothetical main size: %d for flex item %p", - mFlexBaseSize, mMainSize, mFrame); + FLEX_ITEM_LOG(mFrame, "Set flex base size: %d, hypothetical main size: %d", + mFlexBaseSize, mMainSize); } // Setters used while we're resolving flexible lengths @@ -1305,7 +1317,7 @@ StyleAlignFlags nsFlexContainerFrame::CSSAlignmentForAbsPosChild( // block axis), then the caller is really asking about our *main* axis. // Otherwise, the caller is asking about our cross axis. const bool isMainAxis = - (axisTracker.IsRowOriented() == (aLogicalAxis == eLogicalAxisInline)); + (axisTracker.IsRowOriented() == (aLogicalAxis == LogicalAxis::Inline)); const nsStylePosition* containerStylePos = StylePosition(); const bool isAxisReversed = isMainAxis ? axisTracker.IsMainAxisReversed() : axisTracker.IsCrossAxisReversed(); @@ -1592,7 +1604,7 @@ static nscoord PartiallyResolveAutoMinSize( if (specifiedSizeSuggestion != nscoord_MAX) { // We have the specified size suggestion. Return it now since we don't need // to consider transferred size suggestion. - FLEX_LOGV(" Specified size suggestion: %d", specifiedSizeSuggestion); + FLEX_LOGV("Specified size suggestion: %d", specifiedSizeSuggestion); return specifiedSizeSuggestion; } @@ -1611,7 +1623,7 @@ static nscoord PartiallyResolveAutoMinSize( transferredSizeSuggestion = aFlexItem.ClampMainSizeViaCrossAxisConstraints( transferredSizeSuggestion, aItemReflowInput); - FLEX_LOGV(" Transferred size suggestion: %d", transferredSizeSuggestion); + FLEX_LOGV("Transferred size suggestion: %d", transferredSizeSuggestion); return transferredSizeSuggestion; } @@ -1638,8 +1650,10 @@ void nsFlexContainerFrame::ResolveAutoFlexBasisAndMinSize( return; } - FLEX_LOGV("Resolving auto main size or auto min main size for flex item %p", - aFlexItem.Frame()); + FLEX_ITEM_LOG( + aFlexItem.Frame(), + "Resolving auto main size? %s; resolving auto min main size? %s", + BoolToYesNo(isMainSizeAuto), BoolToYesNo(isMainMinSizeAuto)); nscoord resolvedMinSize; // (only set/used if isMainMinSizeAuto==true) bool minSizeNeedsToMeasureContent = false; // assume the best @@ -1725,7 +1739,7 @@ void nsFlexContainerFrame::ResolveAutoFlexBasisAndMinSize( contentSizeSuggestion, aItemReflowInput); } - FLEX_LOGV(" Content size suggestion: %d", contentSizeSuggestion); + FLEX_LOGV("Content size suggestion: %d", contentSizeSuggestion); resolvedMinSize = std::min(resolvedMinSize, contentSizeSuggestion); // Clamp the resolved min main size by the max main size if it's definite. @@ -1739,7 +1753,7 @@ void nsFlexContainerFrame::ResolveAutoFlexBasisAndMinSize( // nscoord_MAX sentinel value if we reach this point). resolvedMinSize = nscoord_MAX; } - FLEX_LOGV(" Resolved auto min main size: %d", resolvedMinSize); + FLEX_LOGV("Resolved auto min main size: %d", resolvedMinSize); if (resolvedMinSize == contentSizeSuggestion) { // When we are here, we've measured the item's content-based size, and @@ -1992,18 +2006,22 @@ const CachedBAxisMeasurement& nsFlexContainerFrame::MeasureBSizeForFlexItem( if (cachedData && cachedData->mBAxisMeasurement) { if (!aItem.Frame()->IsSubtreeDirty() && cachedData->mBAxisMeasurement->IsValidFor(aChildReflowInput)) { - FLEX_LOG("[perf] MeasureBSizeForFlexItem accepted cached value"); + FLEX_ITEM_LOG(aItem.Frame(), + "[perf] Accepted cached measurement: block-size %d", + cachedData->mBAxisMeasurement->BSize()); return *(cachedData->mBAxisMeasurement); } - FLEX_LOG("[perf] MeasureBSizeForFlexItem rejected cached value"); + FLEX_ITEM_LOG(aItem.Frame(), + "[perf] Rejected cached measurement: block-size %d", + cachedData->mBAxisMeasurement->BSize()); } else { - FLEX_LOG("[perf] MeasureBSizeForFlexItem didn't have a cached value"); + FLEX_ITEM_LOG(aItem.Frame(), "[perf] No cached measurement"); } // CachedFlexItemData is stored in item's writing mode, so we pass // aChildReflowInput into ReflowOutput's constructor. ReflowOutput childReflowOutput(aChildReflowInput); - nsReflowStatus childReflowStatus; + nsReflowStatus childStatus; const ReflowChildFlags flags = ReflowChildFlags::NoMoveFrame; const WritingMode outerWM = GetWritingMode(); @@ -2014,12 +2032,12 @@ const CachedBAxisMeasurement& nsFlexContainerFrame::MeasureBSizeForFlexItem( // unimportant. ReflowChild(aItem.Frame(), PresContext(), childReflowOutput, aChildReflowInput, outerWM, dummyPosition, dummyContainerSize, - flags, childReflowStatus); + flags, childStatus); aItem.SetHadMeasuringReflow(); // We always use unconstrained available block-size to measure flex items, // which means they should always complete. - MOZ_ASSERT(childReflowStatus.IsComplete(), + MOZ_ASSERT(childStatus.IsComplete(), "We gave flex item unconstrained available block-size, so it " "should be complete"); @@ -2055,7 +2073,7 @@ void nsFlexContainerFrame::MarkIntrinsicISizesDirty() { nscoord nsFlexContainerFrame::MeasureFlexItemContentBSize( FlexItem& aFlexItem, bool aForceBResizeForMeasuringReflow, const ReflowInput& aParentReflowInput) { - FLEX_LOG("Measuring flex item's content block-size"); + FLEX_ITEM_LOG(aFlexItem.Frame(), "Measuring item's content block-size"); // Set up a reflow input for measuring the flex item's content block-size: WritingMode wm = aFlexItem.Frame()->GetWritingMode(); @@ -2070,7 +2088,7 @@ nscoord nsFlexContainerFrame::MeasureFlexItemContentBSize( // block-size, because that would prevent us from measuring the content // block-size. sizeOverrides.mAspectRatio.emplace(AspectRatio()); - FLEX_LOGV(" Cross size override: %d", aFlexItem.CrossSize()); + FLEX_LOGV("Cross size override: %d", aFlexItem.CrossSize()); } sizeOverrides.mStyleBSize.emplace(StyleSize::Auto()); @@ -2209,7 +2227,7 @@ FlexItem::FlexItem(ReflowInput& aFlexItemReflowInput, float aFlexGrow, // (We'll resolve them later; until then, we want to treat them as 0-sized.) #ifdef DEBUG { - for (const auto side : AllLogicalSides()) { + for (const auto side : LogicalSides::All) { if (styleMargin->mMargin.Get(mCBWM, side).IsAuto()) { MOZ_ASSERT(GetMarginComponentForSide(side) == 0, "Someone else tried to resolve our auto margin"); @@ -2234,12 +2252,12 @@ FlexItem::FlexItem(ReflowInput& aFlexItemReflowInput, float aFlexGrow, // getting the physical side that corresponds to these axes' "logical // start" sides, and we compare those physical sides to find out if // they're the same vs. opposite. - mozilla::Side itemBlockStartSide = mWM.PhysicalSide(eLogicalSideBStart); + mozilla::Side itemBlockStartSide = mWM.PhysicalSide(LogicalSide::BStart); // (Note: this is *not* the "flex-start" side; rather, it's the *logical* // i.e. WM-relative block-start or inline-start side.) mozilla::Side containerStartSideInCrossAxis = mCBWM.PhysicalSide( - MakeLogicalSide(aAxisTracker.CrossAxis(), eLogicalEdgeStart)); + MakeLogicalSide(aAxisTracker.CrossAxis(), LogicalEdge::Start)); // We already know these two Sides (the item's block-start and the // container's 'logical start' side for its cross axis) are in the same @@ -2336,7 +2354,7 @@ nscoord FlexItem::BaselineOffsetFromOuterCrossEdge( // column-oriented flex container. We need to synthesize the item's baseline // from its border-box edge. const bool isMainAxisHorizontal = - mCBWM.PhysicalAxis(MainAxis()) == mozilla::eAxisHorizontal; + mCBWM.PhysicalAxis(MainAxis()) == PhysicalAxis::Horizontal; // When the main axis is horizontal, the synthesized baseline is the bottom // edge of the item's border-box. Otherwise, when the main axis is vertical, @@ -2363,7 +2381,7 @@ nscoord FlexItem::BaselineOffsetFromOuterCrossEdge( "Only expecting to be doing baseline computations when the " "cross axis is the block axis"); - mozilla::Side itemBlockStartSide = mWM.PhysicalSide(eLogicalSideBStart); + mozilla::Side itemBlockStartSide = mWM.PhysicalSide(LogicalSide::BStart); nscoord marginBStartToBaseline = ResolvedAscent(aUseFirstLineBaseline) + PhysicalMargin().Side(itemBlockStartSide); @@ -2430,7 +2448,7 @@ void FlexItem::ResolveFlexBaseSizeFromAspectRatio( uint32_t FlexItem::NumAutoMarginsInAxis(LogicalAxis aAxis) const { uint32_t numAutoMargins = 0; const auto& styleMargin = mFrame->StyleMargin()->mMargin; - for (const auto edge : {eLogicalEdgeStart, eLogicalEdgeEnd}) { + for (const auto edge : {LogicalEdge::Start, LogicalEdge::End}) { const auto side = MakeLogicalSide(aAxis, edge); if (styleMargin.Get(mCBWM, side).IsAuto()) { numAutoMargins++; @@ -2537,18 +2555,17 @@ static bool FrameHasRelativeBSizeDependency(nsIFrame* aFrame) { bool FlexItem::NeedsFinalReflow(const ReflowInput& aParentReflowInput) const { if (!StaticPrefs::layout_flexbox_item_final_reflow_optimization_enabled()) { - FLEX_LOG( - "[perf] Flex item %p needed a final reflow due to optimization being " - "disabled via the preference", - mFrame); + FLEX_ITEM_LOG(mFrame, + "[perf] Item needed a final reflow due to optimization being " + "disabled via the preference"); return true; } // NOTE: We can have continuations from an earlier constrained reflow. if (mFrame->GetPrevInFlow() || mFrame->GetNextInFlow()) { // This is an item has continuation(s). Reflow it. - FLEX_LOG("[frag] Flex item %p needed a final reflow due to continuation(s)", - mFrame); + FLEX_ITEM_LOG(mFrame, + "[frag] Item needed a final reflow due to continuation(s)"); return true; } @@ -2560,10 +2577,9 @@ bool FlexItem::NeedsFinalReflow(const ReflowInput& aParentReflowInput) const { // prevent triggering O(n^2) behavior when printing a deeply-nested flex // container. if (aParentReflowInput.IsInFragmentedContext()) { - FLEX_LOG( - "[frag] Flex item %p needed both a measuring reflow and a final " - "reflow due to being in a fragmented context.", - mFrame); + FLEX_ITEM_LOG(mFrame, + "[frag] Item needed both a measuring reflow and a final " + "reflow due to being in a fragmented context"); return true; } @@ -2579,10 +2595,9 @@ bool FlexItem::NeedsFinalReflow(const ReflowInput& aParentReflowInput) const { if (finalSize != mFrame->ContentSize(mWM)) { // The measuring reflow left the item with a different size than its // final flexed size. So, we need to reflow to give it the correct size. - FLEX_LOG( - "[perf] Flex item %p needed both a measuring reflow and a final " - "reflow due to measured size disagreeing with final size", - mFrame); + FLEX_ITEM_LOG(mFrame, + "[perf] Item needed both a measuring reflow and a final " + "reflow due to measured size disagreeing with final size"); return true; } @@ -2590,10 +2605,9 @@ bool FlexItem::NeedsFinalReflow(const ReflowInput& aParentReflowInput) const { // This item has descendants with relative BSizes who may care that its // size may now be considered "definite" in the final reflow (whereas it // was indefinite during the measuring reflow). - FLEX_LOG( - "[perf] Flex item %p needed both a measuring reflow and a final " - "reflow due to BSize potentially becoming definite", - mFrame); + FLEX_ITEM_LOG(mFrame, + "[perf] Item needed both a measuring reflow and a final " + "reflow due to BSize potentially becoming definite"); return true; } @@ -2621,10 +2635,9 @@ bool FlexItem::NeedsFinalReflow(const ReflowInput& aParentReflowInput) const { // definiteness. // Let's check for each condition that would still require us to reflow: if (mFrame->IsSubtreeDirty()) { - FLEX_LOG( - "[perf] Flex item %p needed a final reflow due to its subtree " - "being dirty", - mFrame); + FLEX_ITEM_LOG( + mFrame, + "[perf] Item needed a final reflow due to its subtree being dirty"); return true; } @@ -2634,19 +2647,17 @@ bool FlexItem::NeedsFinalReflow(const ReflowInput& aParentReflowInput) const { // Did we cache the metrics from its most recent "final reflow"? auto* cache = mFrame->GetProperty(CachedFlexItemData::Prop()); if (!cache || !cache->mFinalReflowMetrics) { - FLEX_LOG( - "[perf] Flex item %p needed a final reflow due to lacking a " - "cached mFinalReflowMetrics (maybe cache was cleared)", - mFrame); + FLEX_ITEM_LOG(mFrame, + "[perf] Item needed a final reflow due to lacking a cached " + "mFinalReflowMetrics (maybe cache was cleared)"); return true; } // Does the cached size match our current size? if (cache->mFinalReflowMetrics->Size() != finalSize) { - FLEX_LOG( - "[perf] Flex item %p needed a final reflow due to having a " - "different content box size vs. its most recent final reflow", - mFrame); + FLEX_ITEM_LOG(mFrame, + "[perf] Item needed a final reflow due to having a different " + "content box size vs. its most recent final reflow"); return true; } @@ -2659,10 +2670,9 @@ bool FlexItem::NeedsFinalReflow(const ReflowInput& aParentReflowInput) const { // reflow. if (cache->mFinalReflowMetrics->BorderPadding() != BorderPadding().ConvertTo(mWM, mCBWM)) { - FLEX_LOG( - "[perf] Flex item %p needed a final reflow due to having a " - "different border and padding vs. its most recent final reflow", - mFrame); + FLEX_ITEM_LOG(mFrame, + "[perf] Item needed a final reflow due to having a different " + "border and padding vs. its most recent final reflow"); return true; } @@ -2672,10 +2682,9 @@ bool FlexItem::NeedsFinalReflow(const ReflowInput& aParentReflowInput) const { if (cache->mFinalReflowMetrics->TreatBSizeAsIndefinite() != mTreatBSizeAsIndefinite && FrameHasRelativeBSizeDependency(mFrame)) { - FLEX_LOG( - "[perf] Flex item %p needed a final reflow due to having " - "its BSize change definiteness & having a rel-BSize child", - mFrame); + FLEX_ITEM_LOG(mFrame, + "[perf] Item needed a final reflow due to having its BSize " + "change definiteness & having a rel-BSize child"); return true; } @@ -2683,7 +2692,7 @@ bool FlexItem::NeedsFinalReflow(const ReflowInput& aParentReflowInput) const { // dirty, and our current conditions are sufficiently similar to the most // recent "final reflow" that it should have left our subtree in the correct // state.) - FLEX_LOG("[perf] Flex item %p didn't need a final reflow", mFrame); + FLEX_ITEM_LOG(mFrame, "[perf] Item didn't need a final reflow"); return false; } @@ -2699,12 +2708,12 @@ class MOZ_STACK_CLASS PositionTracker { inline LogicalSide StartSide() { return MakeLogicalSide( - mAxis, mIsAxisReversed ? eLogicalEdgeEnd : eLogicalEdgeStart); + mAxis, mIsAxisReversed ? LogicalEdge::End : LogicalEdge::Start); } inline LogicalSide EndSide() { return MakeLogicalSide( - mAxis, mIsAxisReversed ? eLogicalEdgeStart : eLogicalEdgeEnd); + mAxis, mIsAxisReversed ? LogicalEdge::Start : LogicalEdge::End); } // Advances our position across the start edge of the given margin, in the @@ -2757,7 +2766,7 @@ class MOZ_STACK_CLASS PositionTracker { const WritingMode mWM; // The axis along which we're moving. - const LogicalAxis mAxis = eLogicalAxisInline; + const LogicalAxis mAxis = LogicalAxis::Inline; // Is the axis along which we're moving reversed (e.g. LTR vs RTL) with // respect to the corresponding axis on the flex container's WM? @@ -3150,8 +3159,8 @@ void FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize, availableFreeSpace -= item.MainSize(); } - FLEX_LOG(" available free space: %" PRId64 "; flex items should \"%s\"", - availableFreeSpace.value, isUsingFlexGrow ? "grow" : "shrink"); + FLEX_LOGV("Available free space: %" PRId64 "; flex items should \"%s\"", + availableFreeSpace.value, isUsingFlexGrow ? "grow" : "shrink"); // The sign of our free space should agree with the type of flexing // (grow/shrink) that we're doing. Any disagreement should've made us use @@ -3276,7 +3285,7 @@ void FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize, } } - FLEX_LOG(" Distributing available space:"); + FLEX_LOGV("Distributing available space:"); // Since this loop only operates on unfrozen flex items, we can break as // soon as we have seen all of them. numUnfrozenItemsToBeSeen = NumItems() - mNumFrozenItems; @@ -3323,8 +3332,8 @@ void FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize, item.SetMainSize(item.MainSize() + nscoord(sizeDelta.ToMinMaxClamped())); - FLEX_LOG(" flex item %p receives %" PRId64 ", for a total of %d", - item.Frame(), sizeDelta.value, item.MainSize()); + FLEX_LOGV(" Flex item %p receives %" PRId64 ", for a total of %d", + item.Frame(), sizeDelta.value, item.MainSize()); } } @@ -3360,7 +3369,7 @@ void FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize, // Fix min/max violations: nscoord totalViolation = 0; // keeps track of adjustments for min/max - FLEX_LOG(" Checking for violations:"); + FLEX_LOGV("Checking for violations:"); // Since this loop only operates on unfrozen flex items, we can break as // soon as we have seen all of them. @@ -3392,7 +3401,7 @@ void FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize, FreezeOrRestoreEachFlexibleSize(totalViolation, iterationCounter + 1 == NumItems()); - FLEX_LOG(" Total violation: %d", totalViolation); + FLEX_LOGV("Total violation: %d", totalViolation); if (mNumFrozenItems == NumItems()) { break; @@ -3952,7 +3961,7 @@ void SingleLineCrossAxisPositionTracker::EnterAlignPackingSpace( if (alignSelf == StyleAlignFlags::SELF_START || alignSelf == StyleAlignFlags::SELF_END) { const LogicalAxis logCrossAxis = - aAxisTracker.IsRowOriented() ? eLogicalAxisBlock : eLogicalAxisInline; + aAxisTracker.IsRowOriented() ? LogicalAxis::Block : LogicalAxis::Inline; const WritingMode cWM = aAxisTracker.GetWritingMode(); const bool sameStart = cWM.ParallelAxisStartsOnSameSide(logCrossAxis, aItem.GetWritingMode()); @@ -4102,12 +4111,13 @@ FlexboxAxisTracker::FlexboxAxisTracker( LogicalSide FlexboxAxisTracker::MainAxisStartSide() const { return MakeLogicalSide( - MainAxis(), IsMainAxisReversed() ? eLogicalEdgeEnd : eLogicalEdgeStart); + MainAxis(), IsMainAxisReversed() ? LogicalEdge::End : LogicalEdge::Start); } LogicalSide FlexboxAxisTracker::CrossAxisStartSide() const { - return MakeLogicalSide( - CrossAxis(), IsCrossAxisReversed() ? eLogicalEdgeEnd : eLogicalEdgeStart); + return MakeLogicalSide(CrossAxis(), IsCrossAxisReversed() + ? LogicalEdge::End + : LogicalEdge::Start); } void nsFlexContainerFrame::GenerateFlexLines( @@ -4560,7 +4570,6 @@ void nsFlexContainerFrame::Reflow(nsPresContext* aPresContext, MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsFlexContainerFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aReflowOutput, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); MOZ_ASSERT(aPresContext == PresContext()); NS_WARNING_ASSERTION( @@ -4568,7 +4577,7 @@ void nsFlexContainerFrame::Reflow(nsPresContext* aPresContext, "Unconstrained inline size; this should only result from huge sizes " "(not intrinsic sizing w/ orthogonal flows)"); - FLEX_LOG("Reflow() for nsFlexContainerFrame %p", this); + FLEX_LOG("Reflowing flex container frame %p ...", this); if (IsFrameTreeTooDeep(aReflowInput, aReflowOutput, aStatus)) { return; @@ -4877,8 +4886,14 @@ void nsFlexContainerFrame::UnionInFlowChildOverflow( bool anyScrolledContentItem = false; // Union of normal-positioned margin boxes for all the items. nsRect itemMarginBoxes; - // Union of relative-positioned margin boxes for the relpos items only. - nsRect relPosItemMarginBoxes; + // Overflow areas containing the union of relative-positioned and + // stick-positioned margin boxes of relpos items. + // + // Note for sticky-positioned margin boxes, we only union it with the ink + // overflow to avoid circular dependencies with the scroll container. (The + // scroll position and the scroll container's size impact the sticky position, + // so we don't want the sticky position to impact them.) + OverflowAreas relPosItemMarginBoxes; const bool useMozBoxCollapseBehavior = StyleVisibility()->UseLegacyCollapseBehavior(); for (nsIFrame* f : mFrames) { @@ -4897,8 +4912,13 @@ void nsFlexContainerFrame::UnionInFlowChildOverflow( const nsRect marginRect = f->GetMarginRectRelativeToSelf(); itemMarginBoxes = itemMarginBoxes.Union(marginRect + f->GetNormalPosition()); - relPosItemMarginBoxes = - relPosItemMarginBoxes.Union(marginRect + f->GetPosition()); + if (f->IsRelativelyPositioned()) { + relPosItemMarginBoxes.UnionAllWith(marginRect + f->GetPosition()); + } else { + MOZ_ASSERT(f->IsStickyPositioned()); + relPosItemMarginBoxes.UnionWith( + OverflowAreas(marginRect + f->GetPosition(), nsRect())); + } } else { itemMarginBoxes = itemMarginBoxes.Union(f->GetMarginRect()); } @@ -4907,7 +4927,7 @@ void nsFlexContainerFrame::UnionInFlowChildOverflow( if (anyScrolledContentItem) { itemMarginBoxes.Inflate(GetUsedPadding()); aOverflowAreas.UnionAllWith(itemMarginBoxes); - aOverflowAreas.UnionAllWith(relPosItemMarginBoxes); + aOverflowAreas.UnionWith(relPosItemMarginBoxes); } } @@ -5276,8 +5296,8 @@ nsFlexContainerFrame::FlexLayoutResult nsFlexContainerFrame::DoFlexLayout( } else { sizeOverrides.mStyleBSize.emplace(item.StyleMainSize()); } - FLEX_LOG("Sizing flex item %p in cross axis", item.Frame()); - FLEX_LOGV(" Main size override: %d", item.MainSize()); + FLEX_ITEM_LOG(item.Frame(), "Sizing item in cross axis"); + FLEX_LOGV("Main size override: %d", item.MainSize()); const WritingMode wm = item.GetWritingMode(); LogicalSize availSize = aReflowInput.ComputedSize(wm); @@ -5507,14 +5527,19 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren( const bool isSingleLine = StyleFlexWrap::Nowrap == aReflowInput.mStylePosition->mFlexWrap; - - // FINAL REFLOW: Give each child frame another chance to reflow, now that - // we know its final size and position. const FlexLine& startmostLine = StartmostLine(aFlr.mLines, aAxisTracker); + const FlexLine& endmostLine = EndmostLine(aFlr.mLines, aAxisTracker); const FlexItem* startmostItem = startmostLine.IsEmpty() ? nullptr : &startmostLine.StartmostItem(aAxisTracker); + const FlexItem* endmostItem = + endmostLine.IsEmpty() ? nullptr : &endmostLine.EndmostItem(aAxisTracker); + bool endmostItemOrLineHasBreakAfter = false; + // If true, push all remaining flex items to the container's next-in-flow. + bool shouldPushRemainingItems = false; + + // FINAL REFLOW: Give each child frame another chance to reflow. const size_t numLines = aFlr.mLines.Length(); for (size_t lineIdx = 0; lineIdx < numLines; ++lineIdx) { // Iterate flex lines from the startmost to endmost (relative to flex @@ -5525,6 +5550,11 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren( MOZ_ASSERT(lineIdx != 0 || &line == &startmostLine, "Logic for finding startmost line should be consistent!"); + // These two variables can be set when we are a row-oriented flex container + // during fragmentation. + bool lineHasBreakBefore = false; + bool lineHasBreakAfter = false; + const size_t numItems = line.Items().Length(); for (size_t itemIdx = 0; itemIdx < numItems; ++itemIdx) { // Iterate flex items from the startmost to endmost (relative to flex @@ -5623,15 +5653,22 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren( // (i.e. its frame rect), instead of the container's content-box: framePos += containerContentBoxOrigin; - // Check if we actually need to reflow the item -- if the item's position - // is below the available space's block-end, push it to our next-in-flow; - // if it does need a reflow, and we already reflowed it with the right - // content-box size. - const bool childBPosExceedAvailableSpaceBEnd = - availableBSizeForItem != NS_UNCONSTRAINEDSIZE && - availableBSizeForItem <= 0; + // Check if we can skip reflowing the item because it will be pushed to + // our next-in-flow -- i.e. if there was a forced break before it, or its + // position is beyond the available space's block-end. bool itemInPushedItems = false; - if (childBPosExceedAvailableSpaceBEnd) { + if (shouldPushRemainingItems) { + FLEX_ITEM_LOG( + item.Frame(), + "[frag] Item needed to be pushed to container's next-in-flow due " + "to a forced break before it"); + pushedItems.Insert(item.Frame()); + itemInPushedItems = true; + } else if (availableBSizeForItem != NS_UNCONSTRAINEDSIZE && + availableBSizeForItem <= 0) { + // The item's position is beyond the available space, so we have to push + // it. + // // Note: Even if all of our items are beyond the available space & get // pushed here, we'll be guaranteed to place at least one of them (and // make progress) in one of the flex container's *next* fragment. It's @@ -5640,10 +5677,10 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren( // block-size and add it to // PerFragmentFlexData::mCumulativeContentBoxBSize even if we are not // laying out any child. - FLEX_LOG( - "[frag] Flex item %p needed to be pushed to container's " - "next-in-flow due to position below available space's block-end", - item.Frame()); + FLEX_ITEM_LOG( + item.Frame(), + "[frag] Item needed to be pushed to container's next-in-flow due " + "to being positioned beyond block-end edge of available space"); pushedItems.Insert(item.Frame()); itemInPushedItems = true; } else if (item.NeedsFinalReflow(aReflowInput)) { @@ -5654,17 +5691,50 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren( availableBSizeForItem) .ConvertTo(itemWM, flexWM); - const nsReflowStatus childReflowStatus = + const bool isAdjacentWithBStart = + framePos.B(flexWM) == containerContentBoxOrigin.B(flexWM); + const nsReflowStatus childStatus = ReflowFlexItem(aAxisTracker, aReflowInput, item, framePos, - availableSize, aContainerSize); + isAdjacentWithBStart, availableSize, aContainerSize); + + if (aReflowInput.IsInFragmentedContext()) { + const bool itemHasBreakBefore = + item.Frame()->ShouldBreakBefore(aReflowInput.mBreakType) || + childStatus.IsInlineBreakBefore(); + if (itemHasBreakBefore) { + if (aAxisTracker.IsRowOriented()) { + lineHasBreakBefore = true; + } else if (isSingleLine) { + if (&item == startmostItem) { + if (!GetPrevInFlow() && !aReflowInput.mFlags.mIsTopOfPage) { + // If we are first-in-flow and not at top-of-page, early + // return here to propagate forced break-before from the + // startmost item to the flex container. + nsReflowStatus childrenStatus; + childrenStatus.SetInlineLineBreakBeforeAndReset(); + return {0, childrenStatus}; + } + } else { + shouldPushRemainingItems = true; + } + } else { + // Bug 1806717: We haven't implemented fragmentation for + // multi-line column-oriented flex container, so we just ignore + // forced breaks for now. + } + } + } const bool shouldPushItem = [&]() { + if (shouldPushRemainingItems) { + return true; + } if (availableBSizeForItem == NS_UNCONSTRAINEDSIZE) { // If the available block-size is unconstrained, then we're not // fragmenting and we don't want to push the item. return false; } - if (framePos.B(flexWM) == containerContentBoxOrigin.B(flexWM)) { + if (isAdjacentWithBStart) { // The flex item is adjacent with block-start of the container's // content-box. Don't push it, or we'll trap in an infinite loop. return false; @@ -5680,19 +5750,41 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren( return true; }(); if (shouldPushItem) { - FLEX_LOG( - "[frag] Flex item %p needed to be pushed to container's " - "next-in-flow because its block-size is larger than the " - "available space", - item.Frame()); + FLEX_ITEM_LOG( + item.Frame(), + "[frag] Item needed to be pushed to container's next-in-flow " + "because it encounters a forced break before it, or its " + "block-size is larger than the available space"); pushedItems.Insert(item.Frame()); itemInPushedItems = true; - } else if (childReflowStatus.IsIncomplete()) { + } else if (childStatus.IsIncomplete()) { incompleteItems.Insert(item.Frame()); - } else if (childReflowStatus.IsOverflowIncomplete()) { + } else if (childStatus.IsOverflowIncomplete()) { overflowIncompleteItems.Insert(item.Frame()); } + + if (aReflowInput.IsInFragmentedContext()) { + const bool itemHasBreakAfter = + item.Frame()->ShouldBreakAfter(aReflowInput.mBreakType) || + childStatus.IsInlineBreakAfter(); + if (itemHasBreakAfter) { + if (aAxisTracker.IsRowOriented()) { + lineHasBreakAfter = true; + } else if (isSingleLine) { + shouldPushRemainingItems = true; + if (&item == endmostItem) { + endmostItemOrLineHasBreakAfter = true; + } + } else { + // Bug 1806717: We haven't implemented fragmentation for + // multi-line column-oriented flex container, so we just ignore + // forced breaks for now. + } + } + } } else { + // We already reflowed the item with the right content-box size, so we + // can simply move it into place. MoveFlexItemToFinalPosition(item, framePos, aContainerSize); } @@ -5780,6 +5872,37 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren( } } + if (aReflowInput.IsInFragmentedContext() && aAxisTracker.IsRowOriented()) { + // Propagate forced break values from the flex items to its flex line. + if (lineHasBreakBefore) { + if (&line == &startmostLine) { + if (!GetPrevInFlow() && !aReflowInput.mFlags.mIsTopOfPage) { + // If we are first-in-flow and not at top-of-page, early return here + // to propagate forced break-before from the startmost line to the + // flex container. + nsReflowStatus childrenStatus; + childrenStatus.SetInlineLineBreakBeforeAndReset(); + return {0, childrenStatus}; + } + } else { + // Current non-startmost line has forced break-before, so push all the + // items in this line. + for (const FlexItem& item : line.Items()) { + pushedItems.Insert(item.Frame()); + incompleteItems.Remove(item.Frame()); + overflowIncompleteItems.Remove(item.Frame()); + } + shouldPushRemainingItems = true; + } + } + if (lineHasBreakAfter) { + shouldPushRemainingItems = true; + if (&line == &endmostLine) { + endmostItemOrLineHasBreakAfter = true; + } + } + } + // Now we've finished processing all the items in the startmost line. // Determine the amount by which the startmost line's block-end edge has // shifted, so we can apply the same shift for the remaining lines. @@ -5801,6 +5924,8 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren( childrenStatus.SetIncomplete(); } else if (!overflowIncompleteItems.IsEmpty()) { childrenStatus.SetOverflowIncomplete(); + } else if (endmostItemOrLineHasBreakAfter) { + childrenStatus.SetInlineLineBreakAfter(); } PushIncompleteChildren(pushedItems, incompleteItems, overflowIncompleteItems); @@ -5945,6 +6070,14 @@ void nsFlexContainerFrame::PopulateReflowOutput( return; } + // Propagate forced break values from flex items or flex lines. + if (aChildrenStatus.IsInlineBreakBefore()) { + aStatus.SetInlineLineBreakBeforeAndReset(); + } + if (aChildrenStatus.IsInlineBreakAfter()) { + aStatus.SetInlineLineBreakAfter(); + } + // If we haven't established a baseline for the container yet, i.e. if we // don't have any flex item in the startmost flex line that participates in // baseline alignment, then use the startmost flex item to derive the @@ -6050,8 +6183,8 @@ void nsFlexContainerFrame::MoveFlexItemToFinalPosition( logicalOffsets, &pos, aContainerSize); } - FLEX_LOG("Moving flex item %p to its desired position %s", aItem.Frame(), - ToString(pos).c_str()); + FLEX_ITEM_LOG(aItem.Frame(), "Moving item to its desired position %s", + ToString(pos).c_str()); aItem.Frame()->SetPosition(outerWM, pos, aContainerSize); PositionFrameView(aItem.Frame()); PositionChildViews(aItem.Frame()); @@ -6060,8 +6193,9 @@ void nsFlexContainerFrame::MoveFlexItemToFinalPosition( nsReflowStatus nsFlexContainerFrame::ReflowFlexItem( const FlexboxAxisTracker& aAxisTracker, const ReflowInput& aReflowInput, const FlexItem& aItem, const LogicalPoint& aFramePos, - const LogicalSize& aAvailableSize, const nsSize& aContainerSize) { - FLEX_LOG("Doing final reflow for flex item %p", aItem.Frame()); + const bool aIsAdjacentWithBStart, const LogicalSize& aAvailableSize, + const nsSize& aContainerSize) { + FLEX_ITEM_LOG(aItem.Frame(), "Doing final reflow"); // Returns true if we should use 'auto' in block axis's StyleSizeOverrides to // allow fragmentation-imposed block-size growth. @@ -6131,15 +6265,15 @@ nsReflowStatus nsFlexContainerFrame::ReflowFlexItem( // Override flex item's main size. if (aItem.IsInlineAxisMainAxis()) { sizeOverrides.mStyleISize.emplace(aItem.StyleMainSize()); - FLEX_LOGV(" Main size (inline-size) override: %d", aItem.MainSize()); + FLEX_LOGV("Main size (inline-size) override: %d", aItem.MainSize()); } else { overrideBSizeWithAuto = ComputeBSizeOverrideWithAuto(); if (overrideBSizeWithAuto) { sizeOverrides.mStyleBSize.emplace(StyleSize::Auto()); - FLEX_LOGV(" Main size (block-size) override: Auto"); + FLEX_LOGV("Main size (block-size) override: Auto"); } else { sizeOverrides.mStyleBSize.emplace(aItem.StyleMainSize()); - FLEX_LOGV(" Main size (block-size) override: %d", aItem.MainSize()); + FLEX_LOGV("Main size (block-size) override: %d", aItem.MainSize()); } } @@ -6148,15 +6282,15 @@ nsReflowStatus nsFlexContainerFrame::ReflowFlexItem( if (aItem.IsStretched()) { if (aItem.IsInlineAxisCrossAxis()) { sizeOverrides.mStyleISize.emplace(aItem.StyleCrossSize()); - FLEX_LOGV(" Cross size (inline-size) override: %d", aItem.CrossSize()); + FLEX_LOGV("Cross size (inline-size) override: %d", aItem.CrossSize()); } else { overrideBSizeWithAuto = ComputeBSizeOverrideWithAuto(); if (overrideBSizeWithAuto) { sizeOverrides.mStyleBSize.emplace(StyleSize::Auto()); - FLEX_LOGV(" Cross size (block-size) override: Auto"); + FLEX_LOGV("Cross size (block-size) override: Auto"); } else { sizeOverrides.mStyleBSize.emplace(aItem.StyleCrossSize()); - FLEX_LOGV(" Cross size (block-size) override: %d", aItem.CrossSize()); + FLEX_LOGV("Cross size (block-size) override: %d", aItem.CrossSize()); } } } @@ -6199,21 +6333,28 @@ nsReflowStatus nsFlexContainerFrame::ReflowFlexItem( aItem.Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); } + if (!aIsAdjacentWithBStart) { + // mIsTopOfPage bit in childReflowInput is carried over from aReflowInput. + // However, if this item's position is not adjacent with the flex + // container's content-box block-start edge, we should clear it. + childReflowInput.mFlags.mIsTopOfPage = false; + } + // NOTE: Be very careful about doing anything else with childReflowInput // after this point, because some of its methods (e.g. SetComputedWidth) // internally call InitResizeFlags and stomp on mVResize & mHResize. - FLEX_LOG("Reflowing flex item %p at its desired position %s", aItem.Frame(), - ToString(aFramePos).c_str()); + FLEX_ITEM_LOG(aItem.Frame(), "Reflowing item at its desired position %s", + ToString(aFramePos).c_str()); // CachedFlexItemData is stored in item's writing mode, so we pass // aChildReflowInput into ReflowOutput's constructor. ReflowOutput childReflowOutput(childReflowInput); - nsReflowStatus childReflowStatus; + nsReflowStatus childStatus; WritingMode outerWM = aReflowInput.GetWritingMode(); ReflowChild(aItem.Frame(), PresContext(), childReflowOutput, childReflowInput, outerWM, aFramePos, aContainerSize, ReflowChildFlags::Default, - childReflowStatus); + childStatus); // XXXdholbert Perhaps we should call CheckForInterrupt here; see bug 1495532. @@ -6233,7 +6374,7 @@ nsReflowStatus nsFlexContainerFrame::ReflowFlexItem( aItem.Frame()->SetProperty(CachedFlexItemData::Prop(), cached); } - return childReflowStatus; + return childStatus; } void nsFlexContainerFrame::ReflowPlaceholders( @@ -6253,10 +6394,10 @@ void nsFlexContainerFrame::ReflowPlaceholders( // No need to set the -webkit-line-clamp related flags when reflowing // a placeholder. ReflowOutput childReflowOutput(outerWM); - nsReflowStatus childReflowStatus; + nsReflowStatus childStatus; ReflowChild(placeholder, PresContext(), childReflowOutput, childReflowInput, outerWM, aContentBoxOrigin, aContainerSize, - ReflowChildFlags::Default, childReflowStatus); + ReflowChildFlags::Default, childStatus); FinishReflowChild(placeholder, PresContext(), childReflowOutput, &childReflowInput, outerWM, aContentBoxOrigin, @@ -6332,7 +6473,6 @@ nscoord nsFlexContainerFrame::IntrinsicISize(gfxContext* aRenderingContext, /* virtual */ nscoord nsFlexContainerFrame::GetMinISize(gfxContext* aRenderingContext) { - DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize); if (mCachedMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) { if (Maybe<nscoord> containISize = ContainIntrinsicISize()) { mCachedMinISize = *containISize; @@ -6347,7 +6487,6 @@ nscoord nsFlexContainerFrame::GetMinISize(gfxContext* aRenderingContext) { /* virtual */ nscoord nsFlexContainerFrame::GetPrefISize(gfxContext* aRenderingContext) { - DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize); if (mCachedPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) { if (Maybe<nscoord> containISize = ContainIntrinsicISize()) { mCachedPrefISize = *containISize; diff --git a/layout/generic/nsFlexContainerFrame.h b/layout/generic/nsFlexContainerFrame.h index 30e440b401..2771098255 100644 --- a/layout/generic/nsFlexContainerFrame.h +++ b/layout/generic/nsFlexContainerFrame.h @@ -12,15 +12,9 @@ #include <tuple> #include "mozilla/dom/FlexBinding.h" -#include "mozilla/UniquePtr.h" #include "nsContainerFrame.h" #include "nsILineIterator.h" -namespace mozilla { -class LogicalPoint; -class PresShell; -} // namespace mozilla - nsContainerFrame* NS_NewFlexContainerFrame(mozilla::PresShell* aPresShell, mozilla::ComputedStyle* aStyle); @@ -610,6 +604,8 @@ class nsFlexContainerFrame final : public nsContainerFrame, * @param aItem The flex item to be reflowed. * @param aFramePos The position where the flex item's frame should * be placed. (pre-relative positioning) + * @param aIsAdjacentWithBStart True if aFramePos is adjacent with the flex + * container's content-box block-start edge. * @param aAvailableSize The available size to reflow the child frame (in the * child frame's writing-mode). * @param aContainerSize The flex container's size (required by some methods @@ -620,6 +616,7 @@ class nsFlexContainerFrame final : public nsContainerFrame, const ReflowInput& aReflowInput, const FlexItem& aItem, const mozilla::LogicalPoint& aFramePos, + const bool aIsAdjacentWithBStart, const mozilla::LogicalSize& aAvailableSize, const nsSize& aContainerSize); diff --git a/layout/generic/nsFloatManager.cpp b/layout/generic/nsFloatManager.cpp index b6612ca6f5..88a8b20b4c 100644 --- a/layout/generic/nsFloatManager.cpp +++ b/layout/generic/nsFloatManager.cpp @@ -2507,7 +2507,7 @@ nsFloatManager::ShapeInfo::CreateBasicShape(const StyleBasicShape& aBasicShape, case StyleBasicShape::Tag::Rect: return CreateInset(aBasicShape, aShapeMargin, aFrame, aShapeBoxRect, aWM, aContainerSize); - case StyleBasicShape::Tag::Path: + case StyleBasicShape::Tag::PathOrShape: MOZ_ASSERT_UNREACHABLE("Unsupported basic shape"); } return nullptr; @@ -2834,8 +2834,8 @@ nsFloatManager::ShapeInfo::ConvertToFloatLogical(const nscoord aRadii[8], // Get the physical side for line-left and line-right since border radii // are on the physical axis. - Side lineLeftSide = - aWM.PhysicalSide(aWM.LogicalSideForLineRelativeDir(eLineRelativeDirLeft)); + Side lineLeftSide = aWM.PhysicalSide( + aWM.LogicalSideForLineRelativeDir(LineRelativeDir::Left)); logicalRadii[eCornerTopLeftX] = aRadii[SideToHalfCorner(lineLeftSide, true, false)]; logicalRadii[eCornerTopLeftY] = @@ -2846,7 +2846,7 @@ nsFloatManager::ShapeInfo::ConvertToFloatLogical(const nscoord aRadii[8], aRadii[SideToHalfCorner(lineLeftSide, false, true)]; Side lineRightSide = aWM.PhysicalSide( - aWM.LogicalSideForLineRelativeDir(eLineRelativeDirRight)); + aWM.LogicalSideForLineRelativeDir(LineRelativeDir::Right)); logicalRadii[eCornerTopRightX] = aRadii[SideToHalfCorner(lineRightSide, false, false)]; logicalRadii[eCornerTopRightY] = diff --git a/layout/generic/nsFrameSelection.cpp b/layout/generic/nsFrameSelection.cpp index f1a8c7e27c..94cf368480 100644 --- a/layout/generic/nsFrameSelection.cpp +++ b/layout/generic/nsFrameSelection.cpp @@ -156,9 +156,10 @@ PeekOffsetStruct::PeekOffsetStruct(nsSelectionAmount aAmount, } // namespace mozilla -// Array which contains index of each SelecionType in Selection::mDOMSelections. -// For avoiding using if nor switch to retrieve the index, this needs to have -// -1 for SelectionTypes which won't be created its Selection instance. +// Array which contains index of each SelectionType in +// Selection::mDOMSelections. For avoiding using if nor switch to retrieve the +// index, this needs to have -1 for SelectionTypes which won't be created its +// Selection instance. static const int8_t kIndexOfSelections[] = { -1, // SelectionType::eInvalid -1, // SelectionType::eNone @@ -172,6 +173,7 @@ static const int8_t kIndexOfSelections[] = { 7, // SelectionType::eFind 8, // SelectionType::eURLSecondary 9, // SelectionType::eURLStrikeout + 10, // SelectionType::eTargetText -1, // SelectionType::eHighlight }; @@ -539,7 +541,11 @@ nsresult nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree( NS_ENSURE_STATE(mPresShell); RefPtr<PresShell> presShell = mPresShell; - nsIContent* anchorRoot = anchorContent->GetSelectionRootContent(presShell); + nsIContent* anchorRoot = + anchorContent + ->GetSelectionRootContent( + presShell, + StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() /* aAllowCrossShadowBoundary */); NS_ENSURE_TRUE(anchorRoot, NS_ERROR_UNEXPECTED); // @@ -549,7 +555,10 @@ nsresult nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree( nsCOMPtr<nsIContent> content = aFrame->GetContent(); if (content) { - nsIContent* contentRoot = content->GetSelectionRootContent(presShell); + nsIContent* contentRoot = + content->GetSelectionRootContent( + presShell, StaticPrefs:: + dom_shadowdom_selection_across_boundary_enabled() /* aAllowCrossShadowBoundary */); NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED); if (anchorRoot == contentRoot) { @@ -573,8 +582,9 @@ nsresult nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree( if (cursorFrame && cursorFrame->PresShell() == presShell) { nsCOMPtr<nsIContent> cursorContent = cursorFrame->GetContent(); NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE); - nsIContent* cursorContentRoot = - cursorContent->GetSelectionRootContent(presShell); + nsIContent* cursorContentRoot = cursorContent->GetSelectionRootContent( + presShell, StaticPrefs:: + dom_shadowdom_selection_across_boundary_enabled() /* aAllowCrossShadowBoundary */); NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED); if (cursorContentRoot == anchorRoot) { *aRetFrame = cursorFrame; @@ -1431,11 +1441,6 @@ nsresult nsFrameSelection::TakeFocus(nsIContent& aNewFocus, } } - // Don't notify selection listeners if batching is on: - if (IsBatching()) { - return NS_OK; - } - // Be aware, the Selection instance may be destroyed after this call. return NotifySelectionListeners(SelectionType::eNormal); } @@ -1493,11 +1498,11 @@ void nsFrameSelection::SetDragState(bool aState) { // Notify that reason is mouse up. SetChangeReasons(nsISelectionListener::MOUSEUP_REASON); - // flag is set to false in `NotifySelectionListeners`. + // flag is set to NotApplicable in `Selection::NotifySelectionListeners`. // since this function call is part of click event, this would immediately // reset the flag, rendering it useless. - AutoRestore<bool> restoreIsDoubleClickSelectionFlag( - mIsDoubleClickSelection); + AutoRestore<ClickSelectionType> restoreClickSelectionType( + mClickSelectionType); // Be aware, the Selection instance may be destroyed after this call. NotifySelectionListeners(SelectionType::eNormal); } diff --git a/layout/generic/nsFrameSelection.h b/layout/generic/nsFrameSelection.h index e8c55d53b5..3ba6fa72ca 100644 --- a/layout/generic/nsFrameSelection.h +++ b/layout/generic/nsFrameSelection.h @@ -200,6 +200,7 @@ class SelectionChangeEventDispatcher; namespace dom { class Highlight; class Selection; +enum class ClickSelectionType { NotApplicable, Double, Triple }; } // namespace dom /** @@ -259,14 +260,17 @@ class nsFrameSelection final { public: /** - * Sets flag to true if a selection is created by doubleclick or - * long tapping a word. + * Sets the type of the selection based on whether a selection is created + * by doubleclick, long tapping a word or tripleclick. * - * @param aIsDoubleClickSelection True if the selection is created by - * doubleclick or long tap over a word. + * @param aClickSelectionType ClickSelectionType::Double if the selection + * is created by doubleclick, + * ClickSelectionType::Triple if the selection + * is created by tripleclick. */ - void SetIsDoubleClickSelection(bool aIsDoubleClickSelection) { - mIsDoubleClickSelection = aIsDoubleClickSelection; + void SetClickSelectionType( + mozilla::dom::ClickSelectionType aClickSelectionType) { + mClickSelectionType = aClickSelectionType; } /** @@ -274,7 +278,14 @@ class nsFrameSelection final { * long tap over a word. */ [[nodiscard]] bool IsDoubleClickSelection() const { - return mIsDoubleClickSelection; + return mClickSelectionType == mozilla::dom::ClickSelectionType::Double; + } + + /** + * Returns true if the selection was created by triple click + */ + [[nodiscard]] bool IsTripleClickSelection() const { + return mClickSelectionType == mozilla::dom::ClickSelectionType::Triple; } /** @@ -1102,11 +1113,12 @@ class nsFrameSelection final { bool mDragState = false; // for drag purposes bool mAccessibleCaretEnabled = false; - // Records if a selection was created by doubleclicking a word. - // This information is needed later on to determine if a leading + // Records if a selection was created by doubleclicking or tripleclicking + // a word. This information is needed later on to determine if a leading // or trailing whitespace needs to be removed as well to achieve // native behaviour on macOS. - bool mIsDoubleClickSelection{false}; + mozilla::dom::ClickSelectionType mClickSelectionType = + mozilla::dom::ClickSelectionType::NotApplicable; }; /** diff --git a/layout/generic/nsFrameSetFrame.cpp b/layout/generic/nsFrameSetFrame.cpp index f292988c55..be561a6c32 100644 --- a/layout/generic/nsFrameSetFrame.cpp +++ b/layout/generic/nsFrameSetFrame.cpp @@ -760,7 +760,6 @@ void nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); mozilla::PresShell* presShell = aPresContext->PresShell(); @@ -1312,7 +1311,6 @@ void nsHTMLFramesetBorderFrame::Reflow(nsPresContext* aPresContext, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetBorderFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); // Override Reflow(), since we don't want to deal with what our diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 361847b753..97e6b614dc 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -170,7 +170,7 @@ class nsHTMLScrollFrame::ScrollEvent : public Runnable { class nsHTMLScrollFrame::ScrollEndEvent : public Runnable { public: NS_DECL_NSIRUNNABLE - explicit ScrollEndEvent(nsHTMLScrollFrame* aHelper); + explicit ScrollEndEvent(nsHTMLScrollFrame* aHelper, bool aDelayed); void Revoke() { mHelper = nullptr; } private: @@ -964,7 +964,7 @@ void nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowInput& aState, nsRect childScrollableOverflow = childOverflow.ScrollableOverflow(); const LogicalMargin inlinePadding = - padding.ApplySkipSides(LogicalSides(wm, eLogicalSideBitsBBoth)); + padding.ApplySkipSides(LogicalSides(wm, LogicalSides::BBoth)); childScrollableOverflow.Inflate(inlinePadding.GetPhysicalMargin(wm)); nsRect& so = aMetrics->ScrollableOverflow(); @@ -1265,17 +1265,7 @@ nsMargin nsHTMLScrollFrame::ComputeStableScrollbarGutter( // Legacy, this sucks! static bool IsMarqueeScrollbox(const nsIFrame& aScrollFrame) { - if (!aScrollFrame.GetContent()) { - return false; - } - if (MOZ_LIKELY(!aScrollFrame.GetContent()->HasBeenInUAWidget())) { - return false; - } - MOZ_ASSERT(aScrollFrame.GetParent() && - aScrollFrame.GetParent()->GetContent()); - return aScrollFrame.GetParent() && - HTMLMarqueeElement::FromNodeOrNull( - aScrollFrame.GetParent()->GetContent()); + return HTMLMarqueeElement::FromNodeOrNull(aScrollFrame.GetContent()); } /* virtual */ @@ -1290,7 +1280,6 @@ nscoord nsHTMLScrollFrame::GetMinISize(gfxContext* aRenderingContext) { return mScrolledFrame->GetMinISize(aRenderingContext); }(); - DISPLAY_MIN_INLINE_SIZE(this, result); return result + IntrinsicScrollbarGutterSizeAtInlineEdges(); } @@ -1300,7 +1289,6 @@ nscoord nsHTMLScrollFrame::GetPrefISize(gfxContext* aRenderingContext) { nscoord result = containISize ? *containISize : mScrolledFrame->GetPrefISize(aRenderingContext); - DISPLAY_PREF_INLINE_SIZE(this, result); return NSCoordSaturatingAdd(result, IntrinsicScrollbarGutterSizeAtInlineEdges()); } @@ -1497,7 +1485,6 @@ void nsHTMLScrollFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); HandleScrollbarStyleSwitching(); @@ -5224,15 +5211,19 @@ nsSize nsHTMLScrollFrame::GetPageScrollAmount() const { } nsSize lineScrollAmount = GetLineScrollAmount(); + const int32_t maxOverlapPercent = std::clamp( + StaticPrefs::toolkit_scrollbox_pagescroll_maxOverlapPercent(), 0, 80); + const int32_t maxOverlapLines = + std::max(StaticPrefs::toolkit_scrollbox_pagescroll_maxOverlapLines(), 0); - // The page increment is the size of the page, minus the smaller of - // 10% of the size or 2 lines. - return nsSize(effectiveScrollPortSize.width - - std::min(effectiveScrollPortSize.width / 10, - 2 * lineScrollAmount.width), - effectiveScrollPortSize.height - - std::min(effectiveScrollPortSize.height / 10, - 2 * lineScrollAmount.height)); + // The page increment is the size of the page, minus some overlap. + return nsSize( + effectiveScrollPortSize.width - + std::min(effectiveScrollPortSize.width * maxOverlapPercent / 100, + maxOverlapLines * lineScrollAmount.width), + effectiveScrollPortSize.height - + std::min(effectiveScrollPortSize.height * maxOverlapPercent / 100, + maxOverlapLines * lineScrollAmount.height)); } /** @@ -5429,23 +5420,31 @@ nsresult nsHTMLScrollFrame::FireScrollPortEvent() { return EventDispatcher::Dispatch(content, presContext, &event); } -void nsHTMLScrollFrame::PostScrollEndEvent() { +void nsHTMLScrollFrame::PostScrollEndEvent(bool aDelayed) { if (mScrollEndEvent) { return; } // The ScrollEndEvent constructor registers itself with the refresh driver. - mScrollEndEvent = new ScrollEndEvent(this); + mScrollEndEvent = new ScrollEndEvent(this, aDelayed); } void nsHTMLScrollFrame::FireScrollEndEvent() { - MOZ_ASSERT(GetContent()); - MOZ_ASSERT(mScrollEndEvent); + RefPtr<nsIContent> content = GetContent(); + MOZ_ASSERT(content); - RefPtr<nsPresContext> presContext = PresContext(); + MOZ_ASSERT(mScrollEndEvent); mScrollEndEvent->Revoke(); mScrollEndEvent = nullptr; + if (content->GetComposedDoc() && + content->GetComposedDoc()->EventHandlingSuppressed()) { + content->GetComposedDoc()->SetHasDelayedRefreshEvent(); + PostScrollEndEvent(/* aDelayed = */ true); + return; + } + + RefPtr<nsPresContext> presContext = PresContext(); nsEventStatus status = nsEventStatus_eIgnore; WidgetGUIEvent event(true, eScrollend, nullptr); event.mFlags.mBubbles = mIsRoot; @@ -5895,9 +5894,10 @@ nsHTMLScrollFrame::ScrollEvent::Run() { return NS_OK; } -nsHTMLScrollFrame::ScrollEndEvent::ScrollEndEvent(nsHTMLScrollFrame* aHelper) +nsHTMLScrollFrame::ScrollEndEvent::ScrollEndEvent(nsHTMLScrollFrame* aHelper, + bool aDelayed) : Runnable("nsHTMLScrollFrame::ScrollEndEvent"), mHelper(aHelper) { - mHelper->PresContext()->RefreshDriver()->PostScrollEvent(this); + mHelper->PresContext()->RefreshDriver()->PostScrollEvent(this, aDelayed); } MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index ce2a75a3df..9d619de4e5 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -500,7 +500,7 @@ class nsHTMLScrollFrame : public nsContainerFrame, OverflowState GetOverflowState() const; MOZ_CAN_RUN_SCRIPT nsresult FireScrollPortEvent(); - void PostScrollEndEvent(); + void PostScrollEndEvent(bool aDelayed = false); MOZ_CAN_RUN_SCRIPT void FireScrollEndEvent(); void PostOverflowEvent(); diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index 5eb9cde7da..48c70cd479 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 "nsHTMLButtonControlFrame.h" #include "nsGfxScrollFrame.h" #include "nsHashKeys.h" #include "nsIFrameInlines.h" // for nsIFrame::GetLogicalNormalPosition (don't remove) @@ -168,7 +169,7 @@ static nscoord ResolveToDefiniteSize(const StyleTrackBreadth& aBreadth, // block axis; e.g. for English horizontal-tb text, a traditional baseline // would be a y-axis measurement. But in some cases (e.g. orthogonal WMs), we // may need to synthesize a baseline in a child's inline axis, which is when -// this function might receive an aAxis of eLogicalAxisInline. In that case, we +// this function might receive an aAxis of LogicalAxis::Inline. In that case, we // assume that the writing mode's preference for central vs. alphabetic // baselines is irrelevant, since that's a choice about its block-axis // baselines, and we just unconditionally use the alphabetic baseline @@ -178,7 +179,7 @@ static nscoord SynthesizeBaselineFromBorderBox(BaselineSharingGroup aGroup, LogicalAxis aAxis, nscoord aBorderBoxSize) { const bool useAlphabeticBaseline = - (aAxis == eLogicalAxisInline) ? true : aWM.IsAlphabeticalBaseline(); + (aAxis == LogicalAxis::Inline) ? true : aWM.IsAlphabeticalBaseline(); if (aGroup == BaselineSharingGroup::First) { return useAlphabeticBaseline ? aBorderBoxSize : aBorderBoxSize / 2; @@ -218,7 +219,7 @@ struct RepeatTrackSizingInput { // We can use zero percentage basis since this is only called from // intrinsic sizing code. const nscoord percentageBasis = 0; - if (aAxis == eLogicalAxisInline) { + if (aAxis == LogicalAxis::Inline) { bp = std::max(padding.GetIStart(aWM).Resolve(percentageBasis), 0) + std::max(padding.GetIEnd(aWM).Resolve(percentageBasis), 0) + border.IStartEnd(aWM); @@ -234,17 +235,17 @@ struct RepeatTrackSizingInput { nscoord& size = mSize.Size(aAxis, aWM); nscoord& max = mMax.Size(aAxis, aWM); const auto& minCoord = - aAxis == eLogicalAxisInline ? pos->MinISize(aWM) : pos->MinBSize(aWM); + aAxis == LogicalAxis::Inline ? pos->MinISize(aWM) : pos->MinBSize(aWM); if (minCoord.ConvertsToLength()) { min = adjustForBoxSizing(minCoord.ToLength()); } const auto& maxCoord = - aAxis == eLogicalAxisInline ? pos->MaxISize(aWM) : pos->MaxBSize(aWM); + aAxis == LogicalAxis::Inline ? pos->MaxISize(aWM) : pos->MaxBSize(aWM); if (maxCoord.ConvertsToLength()) { max = std::max(min, adjustForBoxSizing(maxCoord.ToLength())); } const auto& sizeCoord = - aAxis == eLogicalAxisInline ? pos->ISize(aWM) : pos->BSize(aWM); + aAxis == LogicalAxis::Inline ? pos->ISize(aWM) : pos->BSize(aWM); if (sizeCoord.ConvertsToLength()) { size = Clamp(adjustForBoxSizing(sizeCoord.ToLength()), min, max); } @@ -576,10 +577,10 @@ struct nsGridContainerFrame::GridArea { : mCols(aCols), mRows(aRows) {} bool IsDefinite() const { return mCols.IsDefinite() && mRows.IsDefinite(); } LineRange& LineRangeForAxis(LogicalAxis aAxis) { - return aAxis == eLogicalAxisInline ? mCols : mRows; + return aAxis == LogicalAxis::Inline ? mCols : mRows; } const LineRange& LineRangeForAxis(LogicalAxis aAxis) const { - return aAxis == eLogicalAxisInline ? mCols : mRows; + return aAxis == LogicalAxis::Inline ? mCols : mRows; } LineRange mCols; LineRange mRows; @@ -665,12 +666,12 @@ struct nsGridContainerFrame::GridItemInfo { */ GridItemInfo Transpose() const { GridItemInfo info(mFrame, GridArea(mArea.mRows, mArea.mCols)); - info.mState[eLogicalAxisBlock] = mState[eLogicalAxisInline]; - info.mState[eLogicalAxisInline] = mState[eLogicalAxisBlock]; - info.mBaselineOffset[eLogicalAxisBlock] = - mBaselineOffset[eLogicalAxisInline]; - info.mBaselineOffset[eLogicalAxisInline] = - mBaselineOffset[eLogicalAxisBlock]; + info.mState[LogicalAxis::Block] = mState[LogicalAxis::Inline]; + info.mState[LogicalAxis::Inline] = mState[LogicalAxis::Block]; + info.mBaselineOffset[LogicalAxis::Block] = + mBaselineOffset[LogicalAxis::Inline]; + info.mBaselineOffset[LogicalAxis::Inline] = + mBaselineOffset[LogicalAxis::Block]; return info; } @@ -684,7 +685,7 @@ struct nsGridContainerFrame::GridItemInfo { // Is this item a subgrid in either axis? bool IsSubgrid() const { - return IsSubgrid(eLogicalAxisInline) || IsSubgrid(eLogicalAxisBlock); + return IsSubgrid(LogicalAxis::Inline) || IsSubgrid(LogicalAxis::Block); } // Return the (inner) grid container frame associated with this subgrid item. @@ -725,7 +726,7 @@ struct nsGridContainerFrame::GridItemInfo { bool ShouldApplyAutoMinSize(WritingMode aContainerWM, LogicalAxis aContainerAxis, nscoord aPercentageBasis) const { - const bool isInlineAxis = aContainerAxis == eLogicalAxisInline; + const bool isInlineAxis = aContainerAxis == LogicalAxis::Inline; const auto* pos = mFrame->IsTableWrapperFrame() ? mFrame->PrincipalChildList().FirstChild()->StylePosition() @@ -793,9 +794,9 @@ struct nsGridContainerFrame::GridItemInfo { return isContinuationA; } auto masonryA = a->mArea.mRows.mStart; - auto gridA = a->mState[eLogicalAxisInline] & StateBits::eAutoPlacement; + auto gridA = a->mState[LogicalAxis::Inline] & StateBits::eAutoPlacement; auto masonryB = b->mArea.mRows.mStart; - auto gridB = b->mState[eLogicalAxisInline] & StateBits::eAutoPlacement; + auto gridB = b->mState[LogicalAxis::Inline] & StateBits::eAutoPlacement; return (masonryA == 0 ? masonryB != 0 : (masonryB != 0 && gridA < gridB)) && !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW); } @@ -804,9 +805,9 @@ struct nsGridContainerFrame::GridItemInfo { MOZ_ASSERT(!a->mFrame->GetPrevInFlow() && !b->mFrame->GetPrevInFlow(), "fragmentation not supported in inline axis"); auto masonryA = a->mArea.mCols.mStart; - auto gridA = a->mState[eLogicalAxisBlock] & StateBits::eAutoPlacement; + auto gridA = a->mState[LogicalAxis::Block] & StateBits::eAutoPlacement; auto masonryB = b->mArea.mCols.mStart; - auto gridB = b->mState[eLogicalAxisBlock] & StateBits::eAutoPlacement; + auto gridB = b->mState[LogicalAxis::Block] & StateBits::eAutoPlacement; return (masonryA == 0 ? masonryB != 0 : (masonryB != 0 && gridA < gridB)) && !a->mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW); } @@ -847,20 +848,20 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ItemState) GridItemInfo::GridItemInfo(nsIFrame* aFrame, const GridArea& aArea) : mFrame(aFrame), mArea(aArea), mBaselineOffset{0, 0} { - mState[eLogicalAxisBlock] = + mState[LogicalAxis::Block] = StateBits(mArea.mRows.mStart == kAutoLine ? eAutoPlacement : 0); - mState[eLogicalAxisInline] = + mState[LogicalAxis::Inline] = StateBits(mArea.mCols.mStart == kAutoLine ? eAutoPlacement : 0); if (auto* gridFrame = GetGridContainerFrame(mFrame)) { auto parentWM = aFrame->GetParent()->GetWritingMode(); bool isOrthogonal = parentWM.IsOrthogonalTo(gridFrame->GetWritingMode()); if (gridFrame->IsColSubgrid()) { - mState[isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline] |= + mState[isOrthogonal ? LogicalAxis::Block : LogicalAxis::Inline] |= StateBits::eIsSubgrid; } if (gridFrame->IsRowSubgrid()) { - mState[isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock] |= + mState[isOrthogonal ? LogicalAxis::Inline : LogicalAxis::Block] |= StateBits::eIsSubgrid; } } @@ -884,7 +885,7 @@ void GridItemInfo::InhibitSubgrid(nsGridContainerFrame* aParent, MOZ_ASSERT(IsSubgrid(aAxis)); auto bit = NS_STATE_GRID_IS_COL_SUBGRID; if (aParent->GetWritingMode().IsOrthogonalTo(mFrame->GetWritingMode()) != - (aAxis == eLogicalAxisBlock)) { + (aAxis == LogicalAxis::Block)) { bit = NS_STATE_GRID_IS_ROW_SUBGRID; } MOZ_ASSERT(SubgridFrame()->HasAnyStateBits(bit)); @@ -894,16 +895,18 @@ void GridItemInfo::InhibitSubgrid(nsGridContainerFrame* aParent, void GridItemInfo::MaybeInhibitSubgridInMasonry(nsGridContainerFrame* aParent, uint32_t aGridAxisTrackCount) { - if (IsSubgrid(eLogicalAxisInline) && aParent->IsMasonry(eLogicalAxisBlock) && - mArea.mRows.mStart != 0 && mArea.mCols.Extent() != aGridAxisTrackCount && - (mState[eLogicalAxisInline] & eAutoPlacement)) { - InhibitSubgrid(aParent, eLogicalAxisInline); + if (IsSubgrid(LogicalAxis::Inline) && + aParent->IsMasonry(LogicalAxis::Block) && mArea.mRows.mStart != 0 && + mArea.mCols.Extent() != aGridAxisTrackCount && + (mState[LogicalAxis::Inline] & eAutoPlacement)) { + InhibitSubgrid(aParent, LogicalAxis::Inline); return; } - if (IsSubgrid(eLogicalAxisBlock) && aParent->IsMasonry(eLogicalAxisInline) && - mArea.mCols.mStart != 0 && mArea.mRows.Extent() != aGridAxisTrackCount && - (mState[eLogicalAxisBlock] & eAutoPlacement)) { - InhibitSubgrid(aParent, eLogicalAxisBlock); + if (IsSubgrid(LogicalAxis::Block) && + aParent->IsMasonry(LogicalAxis::Inline) && mArea.mCols.mStart != 0 && + mArea.mRows.Extent() != aGridAxisTrackCount && + (mState[LogicalAxis::Block] & eAutoPlacement)) { + InhibitSubgrid(aParent, LogicalAxis::Block); } } @@ -1061,9 +1064,9 @@ void nsGridContainerFrame::GridItemInfo::Dump() const { printf("\n"); }; printf("grid-row: %d %d\n", mArea.mRows.mStart, mArea.mRows.mEnd); - Dump1(" grid block-axis: ", eLogicalAxisBlock); + Dump1(" grid block-axis: ", LogicalAxis::Block); printf("grid-column: %d %d\n", mArea.mCols.mStart, mArea.mCols.mEnd); - Dump1(" grid inline-axis: ", eLogicalAxisInline); + Dump1(" grid inline-axis: ", LogicalAxis::Inline); } #endif @@ -1150,8 +1153,9 @@ struct nsGridContainerFrame::TrackSizingFunctions { parent->GetWritingMode().ParallelAxisStartsOnSameSide(parentAxis, grandParentWM); if (MOZ_UNLIKELY(!isSameDirInAxis)) { - auto end = parentAxis == eLogicalAxisBlock ? parentSubgrid->mGridRowEnd - : parentSubgrid->mGridColEnd; + auto end = parentAxis == LogicalAxis::Block + ? parentSubgrid->mGridRowEnd + : parentSubgrid->mGridColEnd; range.ReverseDirection(end); // range is now in the same direction as the grand-parent's axis } @@ -1166,7 +1170,7 @@ struct nsGridContainerFrame::TrackSizingFunctions { parent = grandParent; } const auto* pos = parent->StylePosition(); - const auto isInlineAxis = parentAxis == eLogicalAxisInline; + const auto isInlineAxis = parentAxis == LogicalAxis::Inline; const auto& szf = isInlineAxis ? pos->mGridTemplateRows : pos->mGridTemplateColumns; const auto& autoSizing = @@ -2707,7 +2711,7 @@ struct nsGridContainerFrame::Tracks { * gap before aRow to zero (and shift all rows after it by the removed gap). */ void BreakBeforeRow(uint32_t aRow) { - MOZ_ASSERT(mAxis == eLogicalAxisBlock, + MOZ_ASSERT(mAxis == LogicalAxis::Block, "Should only be fragmenting in the block axis (between rows)"); nscoord prevRowEndPos = 0; if (aRow != 0) { @@ -2728,7 +2732,7 @@ struct nsGridContainerFrame::Tracks { * Set the size of aRow to aSize and adjust the position of all rows after it. */ void ResizeRow(uint32_t aRow, nscoord aNewSize) { - MOZ_ASSERT(mAxis == eLogicalAxisBlock, + MOZ_ASSERT(mAxis == LogicalAxis::Block, "Should only be fragmenting in the block axis (between rows)"); MOZ_ASSERT(aNewSize >= 0); auto& sz = mSizes[aRow]; @@ -2775,7 +2779,7 @@ struct nsGridContainerFrame::Tracks { #ifdef DEBUG void nsGridContainerFrame::Tracks::Dump() const { printf("%zu %s %s ", mSizes.Length(), mIsMasonry ? "masonry" : "grid", - mAxis == eLogicalAxisBlock ? "rows" : "columns"); + mAxis == LogicalAxis::Block ? "rows" : "columns"); TrackSize::DumpStateBits(mStateUnion); printf("\n"); for (uint32_t i = 0, len = mSizes.Length(); i < len; ++i) { @@ -2805,8 +2809,8 @@ void nsGridContainerFrame::Tracks::Dump() const { */ struct nsGridContainerFrame::SharedGridData { SharedGridData() - : mCols(eLogicalAxisInline), - mRows(eLogicalAxisBlock), + : mCols(LogicalAxis::Inline), + mRows(LogicalAxis::Block), mGenerateComputedGridInfo(false) {} Tracks mCols; Tracks mRows; @@ -2884,7 +2888,7 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput { } } if (mStartRow == numRows || - aGridContainerFrame->IsMasonry(eLogicalAxisBlock)) { + aGridContainerFrame->IsMasonry(LogicalAxis::Block)) { // All of the grid's rows fit inside of previous grid-container fragments, // or it's a masonry axis. mFragBStart = aConsumedBSize; @@ -2904,8 +2908,8 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput { aGridContainerFrame->SetProperty(UsedTrackSizes::Prop(), prop); } prop->mCanResolveLineRangeSize = {true, true}; - prop->mSizes[eLogicalAxisInline].Assign(mCols.mSizes); - prop->mSizes[eLogicalAxisBlock].Assign(mRows.mSizes); + prop->mSizes[LogicalAxis::Inline].Assign(mCols.mSizes); + prop->mSizes[LogicalAxis::Block].Assign(mRows.mSizes); } // Copy item data from each child's first-in-flow data in mSharedGridData. @@ -2921,18 +2925,19 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput { mGridItems.AppendElement(GridItemInfo(child, itemInfo.mArea)); // Copy the item's baseline data so that the item's last fragment can // do 'last baseline' alignment if necessary. - item->mState[eLogicalAxisBlock] |= - itemInfo.mState[eLogicalAxisBlock] & ItemState::eAllBaselineBits; - item->mState[eLogicalAxisInline] |= - itemInfo.mState[eLogicalAxisInline] & ItemState::eAllBaselineBits; - item->mBaselineOffset[eLogicalAxisBlock] = - itemInfo.mBaselineOffset[eLogicalAxisBlock]; - item->mBaselineOffset[eLogicalAxisInline] = - itemInfo.mBaselineOffset[eLogicalAxisInline]; - item->mState[eLogicalAxisBlock] |= - itemInfo.mState[eLogicalAxisBlock] & ItemState::eAutoPlacement; - item->mState[eLogicalAxisInline] |= - itemInfo.mState[eLogicalAxisInline] & ItemState::eAutoPlacement; + item->mState[LogicalAxis::Block] |= + itemInfo.mState[LogicalAxis::Block] & ItemState::eAllBaselineBits; + item->mState[LogicalAxis::Inline] |= + itemInfo.mState[LogicalAxis::Inline] & + ItemState::eAllBaselineBits; + item->mBaselineOffset[LogicalAxis::Block] = + itemInfo.mBaselineOffset[LogicalAxis::Block]; + item->mBaselineOffset[LogicalAxis::Inline] = + itemInfo.mBaselineOffset[LogicalAxis::Inline]; + item->mState[LogicalAxis::Block] |= + itemInfo.mState[LogicalAxis::Block] & ItemState::eAutoPlacement; + item->mState[LogicalAxis::Inline] |= + itemInfo.mState[LogicalAxis::Inline] & ItemState::eAutoPlacement; break; } } @@ -2975,7 +2980,7 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput { /** * Return the percentage basis for a grid item in its writing-mode. - * If aAxis is eLogicalAxisInline then we return NS_UNCONSTRAINEDSIZE in + * If aAxis is LogicalAxis::Inline then we return NS_UNCONSTRAINEDSIZE in * both axes since we know all track sizes are indefinite at this point * (we calculate column sizes before row sizes). Otherwise, assert that * column sizes are known and calculate the size for aGridItem.mArea.mCols @@ -3218,10 +3223,10 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput { } Tracks& TracksFor(LogicalAxis aAxis) { - return aAxis == eLogicalAxisBlock ? mRows : mCols; + return aAxis == LogicalAxis::Block ? mRows : mCols; } const Tracks& TracksFor(LogicalAxis aAxis) const { - return aAxis == eLogicalAxisBlock ? mRows : mCols; + return aAxis == LogicalAxis::Block ? mRows : mCols; } CSSOrderAwareFrameIterator mIter; @@ -3275,13 +3280,13 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput { const nsStylePosition* aGridStyle, const WritingMode& aWM) : mIter(aFrame, FrameChildListID::Principal), mGridStyle(aGridStyle), - mCols(eLogicalAxisInline), - mRows(eLogicalAxisBlock), + mCols(LogicalAxis::Inline), + mRows(LogicalAxis::Block), mColFunctions(mGridStyle->mGridTemplateColumns, mGridStyle->mGridAutoColumns, - aFrame->IsSubgrid(eLogicalAxisInline)), + aFrame->IsSubgrid(LogicalAxis::Inline)), mRowFunctions(mGridStyle->mGridTemplateRows, mGridStyle->mGridAutoRows, - aFrame->IsSubgrid(eLogicalAxisBlock)), + aFrame->IsSubgrid(LogicalAxis::Block)), mReflowInput(aReflowInput), mRenderingContext(aRenderingContext), mFrame(aFrame), @@ -3299,8 +3304,8 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::GridReflowInput { mSkipSides = aFrame->PreReflowBlockLevelLogicalSkipSides(); mBorderPadding.ApplySkipSides(mSkipSides); } - mCols.mIsMasonry = aFrame->IsMasonry(eLogicalAxisInline); - mRows.mIsMasonry = aFrame->IsMasonry(eLogicalAxisBlock); + mCols.mIsMasonry = aFrame->IsMasonry(LogicalAxis::Inline); + mRows.mIsMasonry = aFrame->IsMasonry(LogicalAxis::Block); MOZ_ASSERT(!(mCols.mIsMasonry && mRows.mIsMasonry), "can't have masonry layout in both axes"); } @@ -3463,7 +3468,7 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::Grid { * @param aFromIndex the zero-based index to start counting from * @param aLineNameList the explicit named lines * @param aSide the axis+edge we're resolving names for (e.g. if we're - resolving a grid-row-start line, pass eLogicalSideBStart) + resolving a grid-row-start line, pass LogicalSide::BStart) * @param aExplicitGridEnd the last line in the explicit grid * @param aStyle the StylePosition() for the grid container * @return a definite line (1-based), clamped to @@ -3546,7 +3551,7 @@ struct MOZ_STACK_CLASS nsGridContainerFrame::Grid { if (!mParentGrid) { return nullptr; } - bool isRows = aIsOrthogonal == (aAxis == eLogicalAxisInline); + bool isRows = aIsOrthogonal == (aAxis == LogicalAxis::Inline); return isRows ? mParentGrid->mRowNameMap : mParentGrid->mColNameMap; } @@ -3684,38 +3689,56 @@ static Subgrid* SubgridComputeMarginBorderPadding( SizeComputationInput sz(subgridFrame, nullptr, cbWM, pmPercentageBasis); subgrid->mMarginBorderPadding = sz.ComputedLogicalMargin(cbWM) + sz.ComputedLogicalBorderPadding(cbWM); + if (aGridItem.mFrame == subgridFrame) { + return subgrid; + } - if (aGridItem.mFrame != subgridFrame) { - nsHTMLScrollFrame* scrollFrame = - do_QueryFrame(aGridItem.mFrame->GetScrollTargetFrame()); - if (scrollFrame) { - MOZ_ASSERT( - sz.ComputedLogicalMargin(cbWM) == LogicalMargin(cbWM) && - sz.ComputedLogicalBorder(cbWM) == LogicalMargin(cbWM), - "A scrolled inner frame should not have any margin or border!"); - - // Add the margin and border from the (outer) scroll frame. - SizeComputationInput szScrollFrame(aGridItem.mFrame, nullptr, cbWM, - pmPercentageBasis); - subgrid->mMarginBorderPadding += - szScrollFrame.ComputedLogicalMargin(cbWM) + - szScrollFrame.ComputedLogicalBorder(cbWM); - - nsMargin ssz = scrollFrame->IntrinsicScrollbarGutterSize(); + bool scroller = false; + nsIFrame* outerFrame = [&]() -> nsIFrame* { + if (nsHTMLScrollFrame* scrollFrame = + do_QueryFrame(aGridItem.mFrame->GetScrollTargetFrame())) { + scroller = true; + return scrollFrame; + } + if (nsHTMLButtonControlFrame* f = do_QueryFrame(aGridItem.mFrame)) { + return f; + } + return nullptr; + }(); + + if (outerFrame) { + MOZ_ASSERT(sz.ComputedLogicalMargin(cbWM) == LogicalMargin(cbWM) && + sz.ComputedLogicalBorder(cbWM) == LogicalMargin(cbWM), + "A scrolled inner frame / button content frame " + "should not have any margin or border / padding!"); + + // Add the margin and border from the (outer) frame. Padding is factored-in + // for scrollers already (except for the scrollbar gutter), but not for + // button-content. + SizeComputationInput szOuterFrame(outerFrame, nullptr, cbWM, + pmPercentageBasis); + subgrid->mMarginBorderPadding += szOuterFrame.ComputedLogicalMargin(cbWM) + + szOuterFrame.ComputedLogicalBorder(cbWM); + if (scroller) { + nsMargin ssz = static_cast<nsHTMLScrollFrame*>(outerFrame) + ->IntrinsicScrollbarGutterSize(); subgrid->mMarginBorderPadding += LogicalMargin(cbWM, ssz); + } else { + subgrid->mMarginBorderPadding += + szOuterFrame.ComputedLogicalPadding(cbWM); } + } - if (aGridItem.mFrame->IsFieldSetFrame()) { - const auto* f = static_cast<nsFieldSetFrame*>(aGridItem.mFrame); - const auto* inner = f->GetInner(); - auto wm = inner->GetWritingMode(); - LogicalPoint pos = inner->GetLogicalPosition(aGridItem.mFrame->GetSize()); - // The legend is always on the BStart side and it inflates the fieldset's - // "border area" size. The inner frame's b-start pos equals that size. - LogicalMargin offsets(wm, pos.B(wm), 0, 0, 0); - subgrid->mMarginBorderPadding += offsets.ConvertTo(cbWM, wm); - } + if (nsFieldSetFrame* f = do_QueryFrame(aGridItem.mFrame)) { + const auto* inner = f->GetInner(); + auto wm = inner->GetWritingMode(); + LogicalPoint pos = inner->GetLogicalPosition(aGridItem.mFrame->GetSize()); + // The legend is always on the BStart side and it inflates the fieldset's + // "border area" size. The inner frame's b-start pos equals that size. + LogicalMargin offsets(wm, pos.B(wm), 0, 0, 0); + subgrid->mMarginBorderPadding += offsets.ConvertTo(cbWM, wm); } + return subgrid; } @@ -3727,8 +3750,9 @@ static void CopyUsedTrackSizes(nsTArray<TrackSize>& aResult, LogicalAxis aSubgridAxis) { MOZ_ASSERT(aSubgridFrame->ParentGridContainerForSubgrid() == aUsedTrackSizesFrame); - aResult.SetLength(aSubgridAxis == eLogicalAxisInline ? aSubgrid->mGridColEnd - : aSubgrid->mGridRowEnd); + aResult.SetLength(aSubgridAxis == LogicalAxis::Inline + ? aSubgrid->mGridColEnd + : aSubgrid->mGridRowEnd); auto parentAxis = aSubgrid->mIsOrthogonal ? GetOrthogonalAxis(aSubgridAxis) : aSubgridAxis; const auto& parentSizes = aUsedTrackSizes->mSizes[parentAxis]; @@ -3740,7 +3764,7 @@ static void CopyUsedTrackSizes(nsTArray<TrackSize>& aResult, const auto cbwm = aUsedTrackSizesFrame->GetWritingMode(); const auto wm = aSubgridFrame->GetWritingMode(); // Recompute the MBP to resolve percentages against the resolved track sizes. - if (parentAxis == eLogicalAxisInline) { + if (parentAxis == LogicalAxis::Inline) { // Find the subgrid's grid item frame in its parent grid container. This // is usually the same as aSubgridFrame but it may also have a ScrollFrame, // FieldSetFrame etc. We just loop until we see the first ancestor @@ -3856,7 +3880,7 @@ void nsGridContainerFrame::UsedTrackSizes::ResolveSubgridTrackSizesForAxis( grid.mGridRowEnd = aSubgrid->mGridRowEnd; state.CalculateTrackSizesForAxis(aAxis, grid, aContentBoxSize, SizingConstraint::NoConstraint); - const auto& tracks = aAxis == eLogicalAxisInline ? state.mCols : state.mRows; + const auto& tracks = aAxis == LogicalAxis::Inline ? state.mCols : state.mRows; mSizes[aAxis].Assign(tracks.mSizes); mCanResolveLineRangeSize[aAxis] = tracks.mCanResolveLineRangeSize; MOZ_ASSERT(mCanResolveLineRangeSize[aAxis]); @@ -3865,11 +3889,11 @@ void nsGridContainerFrame::UsedTrackSizes::ResolveSubgridTrackSizesForAxis( void nsGridContainerFrame::GridReflowInput::CalculateTrackSizesForAxis( LogicalAxis aAxis, const Grid& aGrid, nscoord aContentBoxSize, SizingConstraint aConstraint) { - auto& tracks = aAxis == eLogicalAxisInline ? mCols : mRows; + auto& tracks = aAxis == LogicalAxis::Inline ? mCols : mRows; const auto& sizingFunctions = - aAxis == eLogicalAxisInline ? mColFunctions : mRowFunctions; - const auto& gapStyle = aAxis == eLogicalAxisInline ? mGridStyle->mColumnGap - : mGridStyle->mRowGap; + aAxis == LogicalAxis::Inline ? mColFunctions : mRowFunctions; + const auto& gapStyle = aAxis == LogicalAxis::Inline ? mGridStyle->mColumnGap + : mGridStyle->mRowGap; if (tracks.mIsMasonry) { // See comment on nsGridContainerFrame::MasonryLayout(). tracks.Initialize(sizingFunctions, gapStyle, 2, aContentBoxSize); @@ -3877,7 +3901,7 @@ void nsGridContainerFrame::GridReflowInput::CalculateTrackSizesForAxis( return; } uint32_t gridEnd = - aAxis == eLogicalAxisInline ? aGrid.mGridColEnd : aGrid.mGridRowEnd; + aAxis == LogicalAxis::Inline ? aGrid.mGridColEnd : aGrid.mGridRowEnd; Maybe<TrackSizingFunctions> fallbackTrackSizing; bool useParentGaps = false; @@ -3919,7 +3943,7 @@ void nsGridContainerFrame::GridReflowInput::CalculateTrackSizesForAxis( *this, mGridItems, fallbackTrackSizing ? *fallbackTrackSizing : sizingFunctions, aContentBoxSize, - aAxis == eLogicalAxisInline ? &GridArea::mCols : &GridArea::mRows, + aAxis == LogicalAxis::Inline ? &GridArea::mCols : &GridArea::mRows, aConstraint); if (hasSubgridItems && @@ -3976,9 +4000,9 @@ void nsGridContainerFrame::GridReflowInput::CalculateTrackSizesForAxis( void nsGridContainerFrame::GridReflowInput::CalculateTrackSizes( const Grid& aGrid, const LogicalSize& aContentBox, SizingConstraint aConstraint) { - CalculateTrackSizesForAxis(eLogicalAxisInline, aGrid, aContentBox.ISize(mWM), + CalculateTrackSizesForAxis(LogicalAxis::Inline, aGrid, aContentBox.ISize(mWM), aConstraint); - CalculateTrackSizesForAxis(eLogicalAxisBlock, aGrid, aContentBox.BSize(mWM), + CalculateTrackSizesForAxis(LogicalAxis::Block, aGrid, aContentBox.BSize(mWM), aConstraint); } @@ -3999,7 +4023,7 @@ static void AlignJustifySelf(StyleAlignFlags aAlignment, LogicalAxis aAxis, // Set the position (aPos) for the requested alignment. if (offset != 0) { WritingMode wm = aRI.GetWritingMode(); - nscoord& pos = aAxis == eLogicalAxisBlock ? aPos->B(wm) : aPos->I(wm); + nscoord& pos = aAxis == LogicalAxis::Block ? aPos->B(wm) : aPos->I(wm); pos += MOZ_LIKELY(aFlags & AlignJustifyFlags::SameSide) ? offset : -offset; } } @@ -4016,7 +4040,7 @@ static void AlignSelf(const nsGridContainerFrame::GridItemInfo& aGridItem, aAlignSelf &= ~StyleAlignFlags::FLAG_BITS; WritingMode childWM = aRI.GetWritingMode(); - if (aCBWM.ParallelAxisStartsOnSameSide(eLogicalAxisBlock, childWM)) { + if (aCBWM.ParallelAxisStartsOnSameSide(LogicalAxis::Block, childWM)) { flags |= AlignJustifyFlags::SameSide; } @@ -4032,12 +4056,12 @@ static void AlignSelf(const nsGridContainerFrame::GridItemInfo& aGridItem, nscoord baselineAdjust = 0; if (aAlignSelf == StyleAlignFlags::BASELINE || aAlignSelf == StyleAlignFlags::LAST_BASELINE) { - aAlignSelf = aGridItem.GetSelfBaseline(aAlignSelf, eLogicalAxisBlock, + aAlignSelf = aGridItem.GetSelfBaseline(aAlignSelf, LogicalAxis::Block, &baselineAdjust); } bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM); - LogicalAxis axis = isOrthogonal ? eLogicalAxisInline : eLogicalAxisBlock; + LogicalAxis axis = isOrthogonal ? LogicalAxis::Inline : LogicalAxis::Block; AlignJustifySelf(aAlignSelf, axis, flags, baselineAdjust, aCBSize, aRI, aSize, aPos); } @@ -4054,7 +4078,7 @@ static void JustifySelf(const nsGridContainerFrame::GridItemInfo& aGridItem, aJustifySelf &= ~StyleAlignFlags::FLAG_BITS; WritingMode childWM = aRI.GetWritingMode(); - if (aCBWM.ParallelAxisStartsOnSameSide(eLogicalAxisInline, childWM)) { + if (aCBWM.ParallelAxisStartsOnSameSide(LogicalAxis::Inline, childWM)) { flags |= AlignJustifyFlags::SameSide; } @@ -4073,12 +4097,12 @@ static void JustifySelf(const nsGridContainerFrame::GridItemInfo& aGridItem, aCBWM.IsBidiLTR() ? StyleAlignFlags::END : StyleAlignFlags::START; } else if (aJustifySelf == StyleAlignFlags::BASELINE || aJustifySelf == StyleAlignFlags::LAST_BASELINE) { - aJustifySelf = aGridItem.GetSelfBaseline(aJustifySelf, eLogicalAxisInline, + aJustifySelf = aGridItem.GetSelfBaseline(aJustifySelf, LogicalAxis::Inline, &baselineAdjust); } bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM); - LogicalAxis axis = isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline; + LogicalAxis axis = isOrthogonal ? LogicalAxis::Block : LogicalAxis::Inline; AlignJustifySelf(aJustifySelf, axis, flags, baselineAdjust, aCBSize, aRI, aSize, aPos); } @@ -4245,8 +4269,8 @@ void nsGridContainerFrame::InitImplicitNamedAreas( AddImplicitNamedAreas(aTemplate.AsSubgrid()->line_names.AsSpan()); } }; - Add(aStyle->mGridTemplateColumns, IsSubgrid(eLogicalAxisInline)); - Add(aStyle->mGridTemplateRows, IsSubgrid(eLogicalAxisBlock)); + Add(aStyle->mGridTemplateColumns, IsSubgrid(LogicalAxis::Inline)); + Add(aStyle->mGridTemplateRows, IsSubgrid(LogicalAxis::Block)); if (areas && areas->count() == 0) { RemoveProperty(ImplicitNamedAreasProperty()); } @@ -4293,7 +4317,7 @@ int32_t nsGridContainerFrame::Grid::ResolveLine( bool useStart = IsNameWithStartSuffix(aLine.LineName(), &index); if (useStart || IsNameWithEndSuffix(aLine.LineName(), &index)) { auto side = MakeLogicalSide( - GetAxis(aSide), useStart ? eLogicalEdgeStart : eLogicalEdgeEnd); + GetAxis(aSide), useStart ? LogicalEdge::Start : LogicalEdge::End); RefPtr<nsAtom> name = NS_Atomize(nsDependentSubstring( nsDependentAtomString(aLine.LineName()), 0, index)); aNameMap.FindNamedAreas(name, side, implicitLines); @@ -4353,7 +4377,7 @@ nsGridContainerFrame::Grid::ResolveLineRangeHelper( uint32_t from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0; auto end = ResolveLine(aEnd, aEnd.line_num, from, aNameMap, - MakeLogicalSide(aAxis, eLogicalEdgeEnd), + MakeLogicalSide(aAxis, LogicalEdge::End), aExplicitGridEnd, aStyle); int32_t span = aStart.line_num == 0 ? 1 : aStart.line_num; if (end <= 1) { @@ -4363,7 +4387,7 @@ nsGridContainerFrame::Grid::ResolveLineRangeHelper( return LinePair(start, end); } auto start = ResolveLine(aStart, -span, end, aNameMap, - MakeLogicalSide(aAxis, eLogicalEdgeStart), + MakeLogicalSide(aAxis, LogicalEdge::Start), aExplicitGridEnd, aStyle); return LinePair(start, end); } @@ -4387,7 +4411,7 @@ nsGridContainerFrame::Grid::ResolveLineRangeHelper( } else { uint32_t from = aStart.line_num < 0 ? aExplicitGridEnd + 1 : 0; start = ResolveLine(aStart, aStart.line_num, from, aNameMap, - MakeLogicalSide(aAxis, eLogicalEdgeStart), + MakeLogicalSide(aAxis, LogicalEdge::Start), aExplicitGridEnd, aStyle); if (aEnd.IsAuto()) { // A "definite line / auto" should resolve the auto to 'span 1'. @@ -4417,7 +4441,7 @@ nsGridContainerFrame::Grid::ResolveLineRangeHelper( from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0; } auto end = ResolveLine(aEnd, nth, from, aNameMap, - MakeLogicalSide(aAxis, eLogicalEdgeEnd), + MakeLogicalSide(aAxis, LogicalEdge::End), aExplicitGridEnd, aStyle); if (start == int32_t(kAutoLine)) { // auto / definite line @@ -4485,10 +4509,10 @@ nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceDefinite( const nsStylePosition* itemStyle = aChild->StylePosition(); return GridArea( ResolveLineRange(itemStyle->mGridColumnStart, itemStyle->mGridColumnEnd, - aColLineNameMap, eLogicalAxisInline, mExplicitGridColEnd, - aStyle), + aColLineNameMap, LogicalAxis::Inline, + mExplicitGridColEnd, aStyle), ResolveLineRange(itemStyle->mGridRowStart, itemStyle->mGridRowEnd, - aRowLineNameMap, eLogicalAxisBlock, mExplicitGridRowEnd, + aRowLineNameMap, LogicalAxis::Block, mExplicitGridRowEnd, aStyle)); } @@ -4503,7 +4527,7 @@ nsGridContainerFrame::Grid::ResolveAbsPosLineRange( } uint32_t from = aEnd.line_num < 0 ? aExplicitGridEnd + 1 : 0; int32_t end = ResolveLine(aEnd, aEnd.line_num, from, aNameMap, - MakeLogicalSide(aAxis, eLogicalEdgeEnd), + MakeLogicalSide(aAxis, LogicalEdge::End), aExplicitGridEnd, aStyle); if (aEnd.is_span) { ++end; @@ -4516,7 +4540,7 @@ nsGridContainerFrame::Grid::ResolveAbsPosLineRange( if (aEnd.IsAuto()) { uint32_t from = aStart.line_num < 0 ? aExplicitGridEnd + 1 : 0; int32_t start = ResolveLine(aStart, aStart.line_num, from, aNameMap, - MakeLogicalSide(aAxis, eLogicalEdgeStart), + MakeLogicalSide(aAxis, LogicalEdge::Start), aExplicitGridEnd, aStyle); if (aStart.is_span) { start = std::max(aGridEnd - start, aGridStart); @@ -4548,11 +4572,11 @@ nsGridContainerFrame::GridArea nsGridContainerFrame::Grid::PlaceAbsPos( int32_t gridRowStart = 1 - mExplicitGridOffsetRow; return GridArea(ResolveAbsPosLineRange( itemStyle->mGridColumnStart, itemStyle->mGridColumnEnd, - aColLineNameMap, eLogicalAxisInline, mExplicitGridColEnd, + aColLineNameMap, LogicalAxis::Inline, mExplicitGridColEnd, gridColStart, mGridColEnd, aStyle), ResolveAbsPosLineRange( itemStyle->mGridRowStart, itemStyle->mGridRowEnd, - aRowLineNameMap, eLogicalAxisBlock, mExplicitGridRowEnd, + aRowLineNameMap, LogicalAxis::Block, mExplicitGridRowEnd, gridRowStart, mGridRowEnd, aStyle)); } @@ -4736,10 +4760,10 @@ void nsGridContainerFrame::Grid::SubgridPlaceGridItems( MOZ_ASSERT(aGridItem.mArea.IsDefinite() || aGridItem.mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW), "the subgrid's lines should be resolved by now"); - if (aGridItem.IsSubgrid(eLogicalAxisInline)) { + if (aGridItem.IsSubgrid(LogicalAxis::Inline)) { aParentState.mFrame->AddStateBits(NS_STATE_GRID_HAS_COL_SUBGRID_ITEM); } - if (aGridItem.IsSubgrid(eLogicalAxisBlock)) { + if (aGridItem.IsSubgrid(LogicalAxis::Block)) { aParentState.mFrame->AddStateBits(NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM); } auto* childGrid = aGridItem.SubgridFrame(); @@ -4787,11 +4811,11 @@ void nsGridContainerFrame::Grid::SubgridPlaceGridItems( // computing them otherwise. RepeatTrackSizingInput repeatSizing(state.mWM); if (!childGrid->IsColSubgrid() && state.mColFunctions.mHasRepeatAuto) { - repeatSizing.InitFromStyle(eLogicalAxisInline, state.mWM, + repeatSizing.InitFromStyle(LogicalAxis::Inline, state.mWM, state.mFrame->Style()); } if (!childGrid->IsRowSubgrid() && state.mRowFunctions.mHasRepeatAuto) { - repeatSizing.InitFromStyle(eLogicalAxisBlock, state.mWM, + repeatSizing.InitFromStyle(LogicalAxis::Block, state.mWM, state.mFrame->Style()); } @@ -4812,8 +4836,8 @@ void nsGridContainerFrame::Grid::PlaceGridItems( if (aState.mFrame->HasSubgridItems() || aState.mFrame->IsSubgrid()) { if (auto* uts = aState.mFrame->GetUsedTrackSizes()) { uts->mCanResolveLineRangeSize = {false, false}; - uts->mSizes[eLogicalAxisInline].ClearAndRetainStorage(); - uts->mSizes[eLogicalAxisBlock].ClearAndRetainStorage(); + uts->mSizes[LogicalAxis::Inline].ClearAndRetainStorage(); + uts->mSizes[LogicalAxis::Block].ClearAndRetainStorage(); } } @@ -4847,11 +4871,11 @@ void nsGridContainerFrame::Grid::PlaceGridItems( uint32_t extent = subgridRange->Extent(); mExplicitGridColEnd = extent + 1; // the grid is 1-based at this point parentLineNameMap = - ParentLineMapForAxis(subgrid->mIsOrthogonal, eLogicalAxisInline); + ParentLineMapForAxis(subgrid->mIsOrthogonal, LogicalAxis::Inline); auto parentWM = aState.mFrame->ParentGridContainerForSubgrid()->GetWritingMode(); subgridAxisIsSameDirection = - aState.mWM.ParallelAxisStartsOnSameSide(eLogicalAxisInline, parentWM); + aState.mWM.ParallelAxisStartsOnSameSide(LogicalAxis::Inline, parentWM); } mGridColEnd = mExplicitGridColEnd; LineNameMap colLineNameMap(gridStyle, mAreas, aState.mColFunctions, @@ -4882,11 +4906,11 @@ void nsGridContainerFrame::Grid::PlaceGridItems( uint32_t extent = subgridRange->Extent(); mExplicitGridRowEnd = extent + 1; // the grid is 1-based at this point parentLineNameMap = - ParentLineMapForAxis(subgrid->mIsOrthogonal, eLogicalAxisBlock); + ParentLineMapForAxis(subgrid->mIsOrthogonal, LogicalAxis::Block); auto parentWM = aState.mFrame->ParentGridContainerForSubgrid()->GetWritingMode(); subgridAxisIsSameDirection = - aState.mWM.ParallelAxisStartsOnSameSide(eLogicalAxisBlock, parentWM); + aState.mWM.ParallelAxisStartsOnSameSide(LogicalAxis::Block, parentWM); } mGridRowEnd = mExplicitGridRowEnd; LineNameMap rowLineNameMap(gridStyle, mAreas, aState.mRowFunctions, @@ -4900,16 +4924,16 @@ void nsGridContainerFrame::Grid::PlaceGridItems( if (isSubgridOrItemInSubgrid) { const auto& area = aItem.mArea; if (area.mCols.mStart == 0) { - aItem.mState[eLogicalAxisInline] |= ItemState::eStartEdge; + aItem.mState[LogicalAxis::Inline] |= ItemState::eStartEdge; } if (area.mCols.mEnd == mGridColEnd) { - aItem.mState[eLogicalAxisInline] |= ItemState::eEndEdge; + aItem.mState[LogicalAxis::Inline] |= ItemState::eEndEdge; } if (area.mRows.mStart == 0) { - aItem.mState[eLogicalAxisBlock] |= ItemState::eStartEdge; + aItem.mState[LogicalAxis::Block] |= ItemState::eStartEdge; } if (area.mRows.mEnd == mGridRowEnd) { - aItem.mState[eLogicalAxisBlock] |= ItemState::eEndEdge; + aItem.mState[LogicalAxis::Block] |= ItemState::eEndEdge; } } }; @@ -4945,8 +4969,8 @@ void nsGridContainerFrame::Grid::PlaceGridItems( aState.mRowFunctions.mExplicitGridOffset = mExplicitGridOffsetRow; const int32_t offsetToColZero = int32_t(mExplicitGridOffsetCol) - 1; const int32_t offsetToRowZero = int32_t(mExplicitGridOffsetRow) - 1; - const bool isRowMasonry = aState.mFrame->IsMasonry(eLogicalAxisBlock); - const bool isColMasonry = aState.mFrame->IsMasonry(eLogicalAxisInline); + const bool isRowMasonry = aState.mFrame->IsMasonry(LogicalAxis::Block); + const bool isColMasonry = aState.mFrame->IsMasonry(LogicalAxis::Inline); const bool isMasonry = isColMasonry || isRowMasonry; mGridColEnd += offsetToColZero; mGridRowEnd += offsetToRowZero; @@ -5100,7 +5124,7 @@ void nsGridContainerFrame::Grid::PlaceGridItems( // Force all items into the 1st/2nd track and have span 1 in the masonry axis. // (See comment on nsGridContainerFrame::MasonryLayout().) if (isMasonry) { - auto masonryAxis = isRowMasonry ? eLogicalAxisBlock : eLogicalAxisInline; + auto masonryAxis = isRowMasonry ? LogicalAxis::Block : LogicalAxis::Inline; aState.mIter.Reset(); for (; !aState.mIter.AtEnd(); aState.mIter.Next()) { auto& item = aState.mGridItems[aState.mIter.ItemIndex()]; @@ -5168,16 +5192,16 @@ void nsGridContainerFrame::Grid::PlaceGridItems( // An abs.pos. subgrid with placement auto/1 or -1/auto technically // doesn't span any parent tracks. Inhibit subgridding in this case. - if (info->IsSubgrid(eLogicalAxisInline)) { + if (info->IsSubgrid(LogicalAxis::Inline)) { if (info->mArea.mCols.mStart == zeroOffsetGridColEnd.SavedValue() || info->mArea.mCols.mEnd == 0) { - info->InhibitSubgrid(aState.mFrame, eLogicalAxisInline); + info->InhibitSubgrid(aState.mFrame, LogicalAxis::Inline); } } - if (info->IsSubgrid(eLogicalAxisBlock)) { + if (info->IsSubgrid(LogicalAxis::Block)) { if (info->mArea.mRows.mStart == zeroOffsetGridRowEnd.SavedValue() || info->mArea.mRows.mEnd == 0) { - info->InhibitSubgrid(aState.mFrame, eLogicalAxisBlock); + info->InhibitSubgrid(aState.mFrame, LogicalAxis::Block); } } @@ -5219,18 +5243,18 @@ void nsGridContainerFrame::Grid::PlaceGridItems( // Adjust the line numbers in the grid areas. for (auto& item : aState.mGridItems) { if (numEmptyCols) { - item.AdjustForRemovedTracks(eLogicalAxisInline, *colAdjust); + item.AdjustForRemovedTracks(LogicalAxis::Inline, *colAdjust); } if (numEmptyRows) { - item.AdjustForRemovedTracks(eLogicalAxisBlock, *rowAdjust); + item.AdjustForRemovedTracks(LogicalAxis::Block, *rowAdjust); } } for (auto& item : aState.mAbsPosItems) { if (numEmptyCols) { - item.AdjustForRemovedTracks(eLogicalAxisInline, *colAdjust); + item.AdjustForRemovedTracks(LogicalAxis::Inline, *colAdjust); } if (numEmptyRows) { - item.AdjustForRemovedTracks(eLogicalAxisBlock, *rowAdjust); + item.AdjustForRemovedTracks(LogicalAxis::Block, *rowAdjust); } } // Adjust the grid size. @@ -5258,11 +5282,11 @@ void nsGridContainerFrame::Grid::PlaceGridItems( LineRange columnLines = ResolveLineRange(lineStartAndEnd, lineStartAndEnd, colLineNameMap, - eLogicalAxisInline, mExplicitGridColEnd, gridStyle); + LogicalAxis::Inline, mExplicitGridColEnd, gridStyle); LineRange rowLines = ResolveLineRange(lineStartAndEnd, lineStartAndEnd, rowLineNameMap, - eLogicalAxisBlock, mExplicitGridRowEnd, gridStyle); + LogicalAxis::Block, mExplicitGridRowEnd, gridStyle); // Put the resolved line indices back into the area structure. areaInfo.columns.start = columnLines.mStart + mExplicitGridOffsetCol; @@ -5322,7 +5346,7 @@ static nscoord MeasuringReflow(nsIFrame* aChild, ComputeSizeFlags csFlags = ComputeSizeFlag::IsGridMeasuringReflow; // Shrink-wrap grid items that will be aligned (rather than stretched) in // their own inline axis. - if (!parent->GridItemShouldStretch(aChild, eLogicalAxisInline)) { + if (!parent->GridItemShouldStretch(aChild, LogicalAxis::Inline)) { csFlags += ComputeSizeFlag::ShrinkWrap; } if (aAvailableSize.ISize(wm) == INFINITE_ISIZE_COORD) { @@ -5434,7 +5458,7 @@ static void PostReflowStretchChild( } ReflowInput ri(pc, aReflowInput, aChild, aAvailableSize, Some(aCBSize), {}, {}, csFlags); - if (aChildAxis == eLogicalAxisBlock) { + if (aChildAxis == LogicalAxis::Block) { ri.SetComputedBSize(ri.ApplyMinMaxBSize(aNewContentBoxSize)); } else { ri.SetComputedISize(ri.ApplyMinMaxISize(aNewContentBoxSize)); @@ -5542,18 +5566,18 @@ static nscoord ContentContribution( auto subgridAxis = aCBWM.IsOrthogonalTo(subgridFrame->GetWritingMode()) ? GetOrthogonalAxis(aAxis) : aAxis; - auto& gapStyle = subgridAxis == eLogicalAxisBlock + auto& gapStyle = subgridAxis == LogicalAxis::Block ? subgridFrame->StylePosition()->mRowGap : subgridFrame->StylePosition()->mColumnGap; if (!gapStyle.IsNormal()) { - auto subgridExtent = subgridAxis == eLogicalAxisBlock + auto subgridExtent = subgridAxis == LogicalAxis::Block ? subgrid->mGridRowEnd : subgrid->mGridColEnd; if (subgridExtent > 1) { nscoord subgridGap = nsLayoutUtils::ResolveGapToLength(gapStyle, NS_UNCONSTRAINEDSIZE); auto& tracks = - aAxis == eLogicalAxisBlock ? aState.mRows : aState.mCols; + aAxis == LogicalAxis::Block ? aState.mRows : aState.mCols; auto gapDelta = subgridGap - tracks.mGridGap; if (!itemEdgeBits) { extraMargin += gapDelta; @@ -5572,7 +5596,7 @@ static nscoord ContentContribution( auto childWM = child->GetWritingMode(); const bool isOrthogonal = childWM.IsOrthogonalTo(aCBWM); auto childAxis = isOrthogonal ? GetOrthogonalAxis(aAxis) : aAxis; - if (size == NS_INTRINSIC_ISIZE_UNKNOWN && childAxis == eLogicalAxisBlock) { + if (size == NS_INTRINSIC_ISIZE_UNKNOWN && childAxis == LogicalAxis::Block) { // We need to reflow the child to find its BSize contribution. // XXX this will give mostly correct results for now (until bug 1174569). nscoord availISize = INFINITE_ISIZE_COORD; @@ -5595,8 +5619,8 @@ static nscoord ContentContribution( } // The grid-item's inline-axis as expressed in the subgrid's WM. auto subgridAxis = childWM.IsOrthogonalTo(subgridFrame->GetWritingMode()) - ? eLogicalAxisBlock - : eLogicalAxisInline; + ? LogicalAxis::Block + : LogicalAxis::Inline; uts->ResolveTrackSizesForAxis(subgridFrame, subgridAxis, *aRC); if (uts->mCanResolveLineRangeSize[subgridAxis]) { auto* subgrid = @@ -5642,7 +5666,7 @@ static nscoord ContentContribution( } } } - if (isOrthogonal == (aAxis == eLogicalAxisInline)) { + if (isOrthogonal == (aAxis == LogicalAxis::Inline)) { bMinSizeClamp = aMinSizeClamp; } else { iMinSizeClamp = aMinSizeClamp; @@ -5737,9 +5761,10 @@ static nscoord MinSize(const GridItemInfo& aGridItem, PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis)); const nsStylePosition* stylePos = child->StylePosition(); StyleSize sizeStyle = - axis == eAxisHorizontal ? stylePos->mWidth : stylePos->mHeight; + axis == PhysicalAxis::Horizontal ? stylePos->mWidth : stylePos->mHeight; - auto ourInlineAxis = child->GetWritingMode().PhysicalAxis(eLogicalAxisInline); + auto ourInlineAxis = + child->GetWritingMode().PhysicalAxis(LogicalAxis::Inline); // max-content and min-content should behave as initial value in block axis. // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported // for block size dimension on sizing properties (e.g. height), so we @@ -5775,8 +5800,9 @@ static nscoord MinSize(const GridItemInfo& aGridItem, nsLayoutUtils::MinSizeContributionForAxis( axis, aRC, child, IntrinsicISizeType::MinISize, *aCache->mPercentageBasis); - const StyleSize& style = - axis == eAxisHorizontal ? stylePos->mMinWidth : stylePos->mMinHeight; + const StyleSize& style = axis == PhysicalAxis::Horizontal + ? stylePos->mMinWidth + : stylePos->mMinHeight; // max-content and min-content should behave as initial value in block axis. // FIXME: Bug 567039: moz-fit-content and -moz-available are not supported // for block size dimension on sizing properties (e.g. height), so we @@ -5992,7 +6018,7 @@ void nsGridContainerFrame::Tracks::InitializeItemBaselines( // against the physical block start side of the child to determine its // baseline sharing group. auto containerBlockStartSide = - containerWM.PhysicalSide(MakeLogicalSide(mAxis, eLogicalEdgeStart)); + containerWM.PhysicalSide(MakeLogicalSide(mAxis, LogicalEdge::Start)); for (GridItemInfo& gridItem : aGridItems) { if (gridItem.IsSubgrid(mAxis)) { @@ -6006,7 +6032,7 @@ void nsGridContainerFrame::Tracks::InitializeItemBaselines( const auto childWM = child->GetWritingMode(); const bool isOrthogonal = containerWM.IsOrthogonalTo(childWM); - const bool isInlineAxis = mAxis == eLogicalAxisInline; // i.e. columns + const bool isInlineAxis = mAxis == LogicalAxis::Inline; // i.e. columns // XXX update the line below to include orthogonal grid/table boxes // XXX since they have baselines in both dimensions. And flexbox with @@ -6099,7 +6125,7 @@ void nsGridContainerFrame::Tracks::InitializeItemBaselines( { auto childAxis = isOrthogonal ? GetOrthogonalAxis(mAxis) : mAxis; auto childBlockStartSide = childWM.PhysicalSide( - MakeLogicalSide(childAxis, eLogicalEdgeStart)); + MakeLogicalSide(childAxis, LogicalEdge::Start)); bool isFirstBaseline = (state & ItemState::eFirstBaseline) != 0; const bool containerAndChildHasEqualBaselineSide = containerBlockStartSide == childBlockStartSide; @@ -6281,7 +6307,7 @@ void nsGridContainerFrame::Tracks::InitializeItemBaselinesInMasonryAxis( auto state = ItemState(0); auto childWM = child->GetWritingMode(); const bool isOrthogonal = wm.IsOrthogonalTo(childWM); - const bool isInlineAxis = mAxis == eLogicalAxisInline; // i.e. columns + const bool isInlineAxis = mAxis == LogicalAxis::Inline; // i.e. columns // XXX update the line below to include orthogonal grid/table boxes // XXX since they have baselines in both dimensions. And flexbox with // XXX reversed main/cross axis? @@ -6449,10 +6475,10 @@ void nsGridContainerFrame::Tracks::AlignBaselineSubtree( const bool isFirstBaseline = state & ItemState::eFirstBaseline; if (isFirstBaseline) { baselineTrack = - mAxis == eLogicalAxisBlock ? area.mRows.mStart : area.mCols.mStart; + mAxis == LogicalAxis::Block ? area.mRows.mStart : area.mCols.mStart; } else { baselineTrack = - (mAxis == eLogicalAxisBlock ? area.mRows.mEnd : area.mCols.mEnd) - 1; + (mAxis == LogicalAxis::Block ? area.mRows.mEnd : area.mCols.mEnd) - 1; } const TrackSize& sz = mSizes[baselineTrack]; auto baselineGroup = isFirstBaseline ? BaselineSharingGroup::First @@ -6855,7 +6881,7 @@ float nsGridContainerFrame::Tracks::FindUsedFlexFraction( nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis, pb, IntrinsicISizeType::PrefISize); const LineRange& range = - mAxis == eLogicalAxisInline ? item.mArea.mCols : item.mArea.mRows; + mAxis == LogicalAxis::Inline ? item.mArea.mCols : item.mArea.mRows; MOZ_ASSERT(range.Extent() >= 1); const auto spannedGaps = range.Extent() - 1; if (spannedGaps > 0) { @@ -6898,10 +6924,10 @@ void nsGridContainerFrame::Tracks::StretchFlexibleTracks( nscoord maxSize = NS_UNCONSTRAINEDSIZE; if (aState.mReflowInput) { auto* ri = aState.mReflowInput; - minSize = mAxis == eLogicalAxisBlock ? ri->ComputedMinBSize() - : ri->ComputedMinISize(); - maxSize = mAxis == eLogicalAxisBlock ? ri->ComputedMaxBSize() - : ri->ComputedMaxISize(); + minSize = mAxis == LogicalAxis::Block ? ri->ComputedMinBSize() + : ri->ComputedMinISize(); + maxSize = mAxis == LogicalAxis::Block ? ri->ComputedMaxBSize() + : ri->ComputedMaxISize(); } Maybe<CopyableAutoTArray<TrackSize, 32>> origSizes; bool applyMinMax = (minSize != 0 || maxSize != NS_UNCONSTRAINEDSIZE) && @@ -6958,7 +6984,7 @@ void nsGridContainerFrame::Tracks::StretchFlexibleTracks( void nsGridContainerFrame::Tracks::AlignJustifyContent( const nsStylePosition* aStyle, StyleContentDistribution aAligmentStyleValue, WritingMode aWM, nscoord aContentBoxSize, bool aIsSubgriddedAxis) { - const bool isAlign = mAxis == eLogicalAxisBlock; + const bool isAlign = mAxis == LogicalAxis::Block; // Align-/justify-content doesn't apply in a subgridded axis. // Gap properties do apply though so we need to stretch/position the tracks // to center-align the gaps with the parent's gaps. @@ -7223,25 +7249,25 @@ LogicalSize nsGridContainerFrame::GridReflowInput::PercentageBasisFor( if (auto* uts = subgridFrame->GetUsedTrackSizes()) { auto subgridWM = subgridFrame->GetWritingMode(); LogicalSize cbSize(subgridWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - if (!subgridFrame->IsSubgrid(eLogicalAxisInline) && - uts->mCanResolveLineRangeSize[eLogicalAxisInline]) { + if (!subgridFrame->IsSubgrid(LogicalAxis::Inline) && + uts->mCanResolveLineRangeSize[LogicalAxis::Inline]) { // NOTE: At this point aGridItem.mArea is in this->mFrame coordinates // and thus may have been transposed. The range values in a non- // subgridded axis still has its original values in subgridFrame's // coordinates though. - auto rangeAxis = subgridWM.IsOrthogonalTo(mWM) ? eLogicalAxisBlock - : eLogicalAxisInline; + auto rangeAxis = subgridWM.IsOrthogonalTo(mWM) ? LogicalAxis::Block + : LogicalAxis::Inline; const auto& range = aGridItem.mArea.LineRangeForAxis(rangeAxis); cbSize.ISize(subgridWM) = - range.ToLength(uts->mSizes[eLogicalAxisInline]); + range.ToLength(uts->mSizes[LogicalAxis::Inline]); } - if (!subgridFrame->IsSubgrid(eLogicalAxisBlock) && - uts->mCanResolveLineRangeSize[eLogicalAxisBlock]) { - auto rangeAxis = subgridWM.IsOrthogonalTo(mWM) ? eLogicalAxisInline - : eLogicalAxisBlock; + if (!subgridFrame->IsSubgrid(LogicalAxis::Block) && + uts->mCanResolveLineRangeSize[LogicalAxis::Block]) { + auto rangeAxis = subgridWM.IsOrthogonalTo(mWM) ? LogicalAxis::Inline + : LogicalAxis::Block; const auto& range = aGridItem.mArea.LineRangeForAxis(rangeAxis); cbSize.BSize(subgridWM) = - range.ToLength(uts->mSizes[eLogicalAxisBlock]); + range.ToLength(uts->mSizes[LogicalAxis::Block]); } return cbSize.ConvertTo(wm, subgridWM); } @@ -7249,7 +7275,7 @@ LogicalSize nsGridContainerFrame::GridReflowInput::PercentageBasisFor( return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); } - if (aAxis == eLogicalAxisInline || !mCols.mCanResolveLineRangeSize) { + if (aAxis == LogicalAxis::Inline || !mCols.mCanResolveLineRangeSize) { return LogicalSize(wm, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); } // Note: for now, we only resolve transferred percentages to row sizing. @@ -7428,7 +7454,7 @@ void nsGridContainerFrame::GridReflowInput::AlignJustifyTracksInMasonryAxis( ContainingBlockFor(item->mArea).Size(wm).ConvertTo(childWM, wm); LogicalSize availableSize = cb; cb.Size(masonryChildAxis, childWM) = alignmentContainerSize; - availableSize.Size(eLogicalAxisBlock, childWM) = + availableSize.Size(LogicalAxis::Block, childWM) = NS_UNCONSTRAINEDSIZE; const auto& bp = child->GetLogicalUsedBorderAndPadding(childWM); newSize -= bp.StartEnd(masonryChildAxis, childWM); @@ -7626,9 +7652,9 @@ void nsGridContainerFrame::ReflowInFlowChild( aChild->RemoveProperty(aProp); } }; - SetProp(eLogicalAxisBlock, + SetProp(LogicalAxis::Block, isOrthogonal ? IBaselinePadProperty() : BBaselinePadProperty()); - SetProp(eLogicalAxisInline, + SetProp(LogicalAxis::Inline, isOrthogonal ? BBaselinePadProperty() : IBaselinePadProperty()); } else { // By convention, for frames that perform CSS Box Alignment, we position @@ -7654,9 +7680,9 @@ void nsGridContainerFrame::ReflowInFlowChild( ComputeSizeFlags csFlags; if (aGridItemInfo) { const auto childIAxisInWM = - isOrthogonal ? eLogicalAxisBlock : eLogicalAxisInline; + isOrthogonal ? LogicalAxis::Block : LogicalAxis::Inline; // Clamp during reflow if we're stretching in that axis. - if (GridItemShouldStretch(aChild, eLogicalAxisInline)) { + if (GridItemShouldStretch(aChild, LogicalAxis::Inline)) { if (aGridItemInfo->mState[childIAxisInWM] & ItemState::eClampMarginBoxMinSize) { csFlags += ComputeSizeFlag::IClampMarginBoxMinSize; @@ -7666,7 +7692,7 @@ void nsGridContainerFrame::ReflowInFlowChild( } const auto childBAxisInWM = GetOrthogonalAxis(childIAxisInWM); - if (GridItemShouldStretch(aChild, eLogicalAxisBlock) && + if (GridItemShouldStretch(aChild, LogicalAxis::Block) && aGridItemInfo->mState[childBAxisInWM] & ItemState::eClampMarginBoxMinSize) { csFlags += ComputeSizeFlag::BClampMarginBoxMinSize; @@ -7702,7 +7728,7 @@ void nsGridContainerFrame::ReflowInFlowChild( // nsBlockFrame::ComputeFinalSize the size. if (isConstrainedBSize && !wm.IsOrthogonalTo(childWM)) { const bool stretch = childRI.mStylePosition->BSize(childWM).IsAuto() && - GridItemShouldStretch(aChild, eLogicalAxisBlock); + GridItemShouldStretch(aChild, LogicalAxis::Block); if (stretch) { aChild->SetProperty(FragStretchBSizeProperty(), *aStretchBSize); } else { @@ -7750,7 +7776,7 @@ void nsGridContainerFrame::ReflowInFlowChild( ? StyleAlignFlags::SELF_START : StyleAlignFlags::SELF_END}; } - if (aAxis == eLogicalAxisBlock) { + if (aAxis == LogicalAxis::Block) { AlignSelf(*aGridItemInfo, align, aCBSize, wm, childRI, size, flags, &childPos); } else { @@ -7759,10 +7785,10 @@ void nsGridContainerFrame::ReflowInFlowChild( } }; if (aStatus.IsComplete()) { - applyItemSelfAlignment(eLogicalAxisBlock, + applyItemSelfAlignment(LogicalAxis::Block, cb.BSize(wm) - consumedGridAreaBSize); } - applyItemSelfAlignment(eLogicalAxisInline, cb.ISize(wm)); + applyItemSelfAlignment(LogicalAxis::Inline, cb.ISize(wm)); } // else, nsAbsoluteContainingBlock.cpp will handle align/justify-self. FinishReflowChild(aChild, pc, childSize, &childRI, childWM, childPos, @@ -8006,7 +8032,7 @@ nscoord nsGridContainerFrame::ReflowRowsInFragmentainer( const auto rowCount = aState.mRows.mSizes.Length(); nscoord masonryAxisGap; const auto wm = aState.mWM; - const bool isColMasonry = IsMasonry(eLogicalAxisInline); + const bool isColMasonry = IsMasonry(LogicalAxis::Inline); if (isColMasonry) { for (auto& sz : aState.mCols.mSizes) { sz.mPosition = 0; @@ -8174,15 +8200,15 @@ nscoord nsGridContainerFrame::ReflowRowsInFragmentainer( } if (isColMasonry) { auto childWM = child->GetWritingMode(); - auto childAxis = - !childWM.IsOrthogonalTo(wm) ? eLogicalAxisInline : eLogicalAxisBlock; + auto childAxis = !childWM.IsOrthogonalTo(wm) ? LogicalAxis::Inline + : LogicalAxis::Block; auto normalPos = child->GetLogicalNormalPosition(wm, aContainerSize); auto sz = - childAxis == eLogicalAxisBlock ? child->BSize() : child->ISize(); - auto pos = normalPos.Pos(eLogicalAxisInline, wm) + sz + + childAxis == LogicalAxis::Block ? child->BSize() : child->ISize(); + auto pos = normalPos.Pos(LogicalAxis::Inline, wm) + sz + child->GetLogicalUsedMargin(childWM).End(childAxis, childWM); masonryAxisPos.ref()[row] = - pos + masonryAxisGap - aContentArea.Start(eLogicalAxisInline, wm); + pos + masonryAxisGap - aContentArea.Start(LogicalAxis::Inline, wm); } } @@ -8281,7 +8307,7 @@ nscoord nsGridContainerFrame::MasonryLayout(GridReflowInput& aState, aState.mIter.Reset(CSSOrderAwareFrameIterator::ChildFilter::IncludeAll); size_t absposIndex = 0; const LogicalAxis masonryAxis = - IsMasonry(eLogicalAxisBlock) ? eLogicalAxisBlock : eLogicalAxisInline; + IsMasonry(LogicalAxis::Block) ? LogicalAxis::Block : LogicalAxis::Inline; const auto wm = aState.mWM; for (; !aState.mIter.AtEnd(); aState.mIter.Next()) { nsIFrame* child = *aState.mIter; @@ -8323,7 +8349,7 @@ nscoord nsGridContainerFrame::MasonryLayout(GridReflowInput& aState, const auto masonryAutoFlow = aState.mGridStyle->mMasonryAutoFlow; const bool definiteFirst = masonryAutoFlow.order == StyleMasonryItemOrder::DefiniteFirst; - if (masonryAxis == eLogicalAxisBlock) { + if (masonryAxis == LogicalAxis::Block) { std::stable_sort(sortedItems.begin(), sortedItems.end(), definiteFirst ? GridItemInfo::RowMasonryDefiniteFirst : GridItemInfo::RowMasonryOrdered); @@ -8366,8 +8392,8 @@ nscoord nsGridContainerFrame::MasonryLayout(GridReflowInput& aState, lastItems.SetLength(gridAxisTrackCount); PodZero(lastItems.Elements(), gridAxisTrackCount); const nscoord gap = nsLayoutUtils::ResolveGapToLength( - masonryAxis == eLogicalAxisBlock ? aState.mGridStyle->mRowGap - : aState.mGridStyle->mColumnGap, + masonryAxis == LogicalAxis::Block ? aState.mGridStyle->mRowGap + : aState.mGridStyle->mColumnGap, masonryTracks.mContentBoxSize); masonryTracks.mGridGap = gap; uint32_t cursor = 0; @@ -8495,7 +8521,7 @@ nscoord nsGridContainerFrame::MasonryLayout(GridReflowInput& aState, auto childAxis = !childWM.IsOrthogonalTo(wm) ? masonryAxis : gridAxis; auto normalPos = aChild->GetLogicalNormalPosition(wm, aContainerSize); auto sz = - childAxis == eLogicalAxisBlock ? aChild->BSize() : aChild->ISize(); + childAxis == LogicalAxis::Block ? aChild->BSize() : aChild->ISize(); return containerToMasonryBoxOffset + normalPos.Pos(masonryAxis, wm) + sz + aChild->GetLogicalUsedMargin(childWM).End(childAxis, childWM); }; @@ -8668,7 +8694,7 @@ nscoord nsGridContainerFrame::MasonryLayout(GridReflowInput& aState, consumeAllSpace ? toFragmentainerEnd : offsetToMarginBoxEnd(child); } else { LogicalSize percentBasis( - aState.PercentageBasisFor(eLogicalAxisInline, *item)); + aState.PercentageBasisFor(LogicalAxis::Inline, *item)); IntrinsicISizeType type = aConstraint == SizingConstraint::MaxContent ? IntrinsicISizeType::PrefISize : IntrinsicISizeType::MinISize; @@ -8775,19 +8801,19 @@ nscoord nsGridContainerFrame::ReflowChildren(GridReflowInput& aState, // so we let ReflowInFragmentainer() deal with grid-axis fragmentation // in the else-clause below. if (IsMasonry() && - !(IsMasonry(eLogicalAxisInline) && fragmentainer.isSome())) { + !(IsMasonry(LogicalAxis::Inline) && fragmentainer.isSome())) { aState.mInFragmentainer = fragmentainer.isSome(); nscoord sz = MasonryLayout( aState, aContentArea, SizingConstraint::NoConstraint, aDesiredSize, aStatus, fragmentainer.ptrOr(nullptr), aContainerSize); - if (IsMasonry(eLogicalAxisBlock)) { + if (IsMasonry(LogicalAxis::Block)) { bSize = aState.mReflowInput->ComputedBSize(); if (bSize == NS_UNCONSTRAINEDSIZE) { bSize = aState.mReflowInput->ApplyMinMaxBSize(sz); } } } else if (MOZ_UNLIKELY(fragmentainer.isSome())) { - if (IsMasonry(eLogicalAxisInline) && !GetPrevInFlow()) { + if (IsMasonry(LogicalAxis::Inline) && !GetPrevInFlow()) { // First we do an unconstrained reflow to resolve the item placement // which is then kept as-is in the constrained reflow below. MasonryLayout(aState, aContentArea, SizingConstraint::NoConstraint, @@ -8804,10 +8830,12 @@ nscoord nsGridContainerFrame::ReflowChildren(GridReflowInput& aState, if (!child->IsPlaceholderFrame()) { info = &aState.mGridItems[aState.mIter.ItemIndex()]; } + nsReflowStatus childStatus; ReflowInFlowChild(child, info, aContainerSize, Nothing(), nullptr, aState, - aContentArea, aDesiredSize, aStatus); - MOZ_ASSERT(aStatus.IsComplete(), + aContentArea, aDesiredSize, childStatus); + MOZ_ASSERT(childStatus.IsComplete(), "child should be complete in unconstrained reflow"); + aStatus.MergeCompletionStatusFrom(childStatus); } } @@ -8868,7 +8896,6 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) { @@ -8952,11 +8979,11 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, if (containBSize) { bSize = *containBSize; } else { - if (IsMasonry(eLogicalAxisBlock)) { + if (IsMasonry(LogicalAxis::Block)) { bSize = computedBSize; } else { const auto& rowSizes = gridReflowInput.mRows.mSizes; - if (MOZ_LIKELY(!IsSubgrid(eLogicalAxisBlock))) { + if (MOZ_LIKELY(!IsSubgrid(LogicalAxis::Block))) { // Note: we can't use GridLineEdge here since we haven't calculated // the rows' mPosition yet (happens in AlignJustifyContent below). for (const auto& sz : rowSizes) { @@ -9025,7 +9052,7 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, } // Save the final row sizes for use by subgrids, if needed. if (HasSubgridItems() || IsSubgrid()) { - StoreUsedTrackSizes(eLogicalAxisBlock, rowSizes); + StoreUsedTrackSizes(LogicalAxis::Block, rowSizes); } } @@ -9122,8 +9149,8 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, } // TODO: fix align-tracks alignment in fragments - if ((IsMasonry(eLogicalAxisBlock) && !prevInFlow) || - IsMasonry(eLogicalAxisInline)) { + if ((IsMasonry(LogicalAxis::Block) && !prevInFlow) || + IsMasonry(LogicalAxis::Inline)) { gridReflowInput.AlignJustifyTracksInMasonryAxis( contentArea.Size(wm), aDesiredSize.PhysicalSize()); } @@ -9221,7 +9248,7 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, // the ComputedGridTrackInfo and related properties const auto* subgrid = GetProperty(Subgrid::Prop()); - const auto* subgridColRange = subgrid && IsSubgrid(eLogicalAxisInline) + const auto* subgridColRange = subgrid && IsSubgrid(LogicalAxis::Inline) ? &subgrid->SubgridCols() : nullptr; @@ -9252,7 +9279,7 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, // taken by rvalue, but computing the size first prevents any changes in the // argument types of the constructor from breaking this. const uint32_t numColExplicitTracks = - IsSubgrid(eLogicalAxisInline) + IsSubgrid(LogicalAxis::Inline) ? colTrackSizes.Length() : gridReflowInput.mColFunctions.NumExplicitTracks(); ComputedGridTrackInfo* colInfo = new ComputedGridTrackInfo( @@ -9261,10 +9288,10 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, std::move(colTrackStates), std::move(colRemovedRepeatTracks), gridReflowInput.mColFunctions.mRepeatAutoStart, colLineNameMap.GetResolvedLineNamesForComputedGridTrackInfo(), - IsSubgrid(eLogicalAxisInline), IsMasonry(eLogicalAxisInline)); + IsSubgrid(LogicalAxis::Inline), IsMasonry(LogicalAxis::Inline)); SetProperty(GridColTrackInfo(), colInfo); - const auto* subgridRowRange = subgrid && IsSubgrid(eLogicalAxisBlock) + const auto* subgridRowRange = subgrid && IsSubgrid(LogicalAxis::Block) ? &subgrid->SubgridRows() : nullptr; LineNameMap rowLineNameMap( @@ -9294,7 +9321,7 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, // taken by rvalue, but computing the size first prevents any changes in the // argument types of the constructor from breaking this. const uint32_t numRowExplicitTracks = - IsSubgrid(eLogicalAxisBlock) + IsSubgrid(LogicalAxis::Block) ? rowTrackSizes.Length() : gridReflowInput.mRowFunctions.NumExplicitTracks(); // Row info has to accommodate fragmentation of the grid, which may happen @@ -9307,7 +9334,7 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext, std::move(rowRemovedRepeatTracks), gridReflowInput.mRowFunctions.mRepeatAutoStart, rowLineNameMap.GetResolvedLineNamesForComputedGridTrackInfo(), - IsSubgrid(eLogicalAxisBlock), IsMasonry(eLogicalAxisBlock)); + IsSubgrid(LogicalAxis::Block), IsMasonry(LogicalAxis::Block)); SetProperty(GridRowTrackInfo(), rowInfo); if (prevInFlow) { @@ -9613,16 +9640,16 @@ nscoord nsGridContainerFrame::IntrinsicISize(gfxContext* aRenderingContext, // They're only used for auto-repeat so we skip computing them otherwise. RepeatTrackSizingInput repeatSizing(state.mWM); if (!IsColSubgrid() && state.mColFunctions.mHasRepeatAuto) { - repeatSizing.InitFromStyle(eLogicalAxisInline, state.mWM, + repeatSizing.InitFromStyle(LogicalAxis::Inline, state.mWM, state.mFrame->Style()); } if ((!IsRowSubgrid() && state.mRowFunctions.mHasRepeatAuto && !(state.mGridStyle->mGridAutoFlow & StyleGridAutoFlow::ROW)) || - IsMasonry(eLogicalAxisInline)) { + IsMasonry(LogicalAxis::Inline)) { // Only 'grid-auto-flow:column' can create new implicit columns, so that's // the only case where our block-size can affect the number of columns. // Masonry layout always depends on how many rows we have though. - repeatSizing.InitFromStyle(eLogicalAxisBlock, state.mWM, + repeatSizing.InitFromStyle(LogicalAxis::Block, state.mWM, state.mFrame->Style()); } @@ -9640,13 +9667,13 @@ nscoord nsGridContainerFrame::IntrinsicISize(gfxContext* aRenderingContext, auto constraint = aType == IntrinsicISizeType::MinISize ? SizingConstraint::MinContent : SizingConstraint::MaxContent; - if (IsMasonry(eLogicalAxisInline)) { + if (IsMasonry(LogicalAxis::Inline)) { ReflowOutput desiredSize(state.mWM); nsSize containerSize; LogicalRect contentArea(state.mWM); nsReflowStatus status; state.mRows.mSizes.SetLength(grid.mGridRowEnd); - state.CalculateTrackSizesForAxis(eLogicalAxisInline, grid, + state.CalculateTrackSizesForAxis(LogicalAxis::Inline, grid, NS_UNCONSTRAINEDSIZE, constraint); return MasonryLayout(state, contentArea, constraint, desiredSize, status, nullptr, containerSize); @@ -9656,7 +9683,7 @@ nscoord nsGridContainerFrame::IntrinsicISize(gfxContext* aRenderingContext, return nscoord(0); } - state.CalculateTrackSizesForAxis(eLogicalAxisInline, grid, + state.CalculateTrackSizesForAxis(LogicalAxis::Inline, grid, NS_UNCONSTRAINEDSIZE, constraint); if (MOZ_LIKELY(!IsSubgrid())) { @@ -9672,7 +9699,6 @@ nscoord nsGridContainerFrame::GetMinISize(gfxContext* aRC) { return f->GetMinISize(aRC); } - DISPLAY_MIN_INLINE_SIZE(this, mCachedMinISize); if (mCachedMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) { Maybe<nscoord> containISize = ContainIntrinsicISize(); mCachedMinISize = containISize @@ -9688,7 +9714,6 @@ nscoord nsGridContainerFrame::GetPrefISize(gfxContext* aRC) { return f->GetPrefISize(aRC); } - DISPLAY_PREF_INLINE_SIZE(this, mCachedPrefISize); if (mCachedPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) { Maybe<nscoord> containISize = ContainIntrinsicISize(); mCachedPrefISize = containISize @@ -9770,7 +9795,7 @@ StyleAlignFlags nsGridContainerFrame::CSSAlignmentForAbsPosChild( "This method should only be called for abspos children"); StyleAlignFlags alignment = - (aLogicalAxis == eLogicalAxisInline) + (aLogicalAxis == LogicalAxis::Inline) ? aChildRI.mStylePosition->UsedJustifySelf(Style())._0 : aChildRI.mStylePosition->UsedAlignSelf(Style())._0; @@ -9792,7 +9817,7 @@ StyleAlignFlags nsGridContainerFrame::CSSAlignmentForAbsPosChild( alignment = StyleAlignFlags::END; } else if (alignment == StyleAlignFlags::LEFT || alignment == StyleAlignFlags::RIGHT) { - if (aLogicalAxis == eLogicalAxisInline) { + if (aLogicalAxis == LogicalAxis::Inline) { const bool isLeft = (alignment == StyleAlignFlags::LEFT); WritingMode wm = GetWritingMode(); alignment = (isLeft == wm.IsBidiLTR()) ? StyleAlignFlags::START @@ -9827,7 +9852,7 @@ nscoord nsGridContainerFrame::SynthesizeBaseline( nscoord start; nscoord size; - if (aAxis == eLogicalAxisBlock) { + if (aAxis == LogicalAxis::Block) { start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM); size = child->BSize(aCBWM); if (grid && aGridOrderItem.mIsInEdgeTrack) { @@ -9836,13 +9861,13 @@ nscoord nsGridContainerFrame::SynthesizeBaseline( } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack) { // This assertion is mostly for documentation purposes; it must hold, // given the checks in our 'if' statements. (We know aAxis is - // eLogicalAxisBlock, and isOrthogonal is false, which means childAxis - // must be eLogicalAxisBlock). If instead we got here with a childAxis of - // eLogicalAxisInline, then our call to + // LogicalAxis::Block, and isOrthogonal is false, which means childAxis + // must be LogicalAxis::Block). If instead we got here with a childAxis of + // LogicalAxis::Inline, then our call to // Baseline::SynthesizeBaselineFromBorderBox might incorrectly think // it makes sense to use a central baseline, in an axis where that // doesn't make sense. - MOZ_ASSERT(childAxis == eLogicalAxisBlock, "unexpected childAxis"); + MOZ_ASSERT(childAxis == LogicalAxis::Block, "unexpected childAxis"); baseline = child ->GetNaturalBaselineBOffset(childWM, aGroup, BaselineExportContext::Other) @@ -9893,8 +9918,8 @@ void nsGridContainerFrame::CalculateBaselines( } else if (firstBaseline == NS_INTRINSIC_ISIZE_UNKNOWN) { FindItemInGridOrderResult gridOrderFirstItem = FindFirstItemInGridOrder( *aIter, *aGridItems, - axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols, - axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows, + axis == LogicalAxis::Block ? &GridArea::mRows : &GridArea::mCols, + axis == LogicalAxis::Block ? &GridArea::mCols : &GridArea::mRows, aFragmentStartTrack); mBaseline[axis][BaselineSharingGroup::First] = SynthesizeBaseline( gridOrderFirstItem, axis, BaselineSharingGroup::First, aCBPhysicalSize, @@ -9929,8 +9954,8 @@ void nsGridContainerFrame::CalculateBaselines( iter.SetItemCount(aGridItems->Length()); FindItemInGridOrderResult gridOrderLastItem = FindLastItemInGridOrder( iter, *aGridItems, - axis == eLogicalAxisBlock ? &GridArea::mRows : &GridArea::mCols, - axis == eLogicalAxisBlock ? &GridArea::mCols : &GridArea::mRows, + axis == LogicalAxis::Block ? &GridArea::mRows : &GridArea::mCols, + axis == LogicalAxis::Block ? &GridArea::mCols : &GridArea::mRows, aFragmentStartTrack, aFirstExcludedTrack); mBaseline[axis][BaselineSharingGroup::Last] = SynthesizeBaseline(gridOrderLastItem, axis, BaselineSharingGroup::Last, @@ -10124,7 +10149,7 @@ bool nsGridContainerFrame::GridItemShouldStretch(const nsIFrame* aChild, } const auto* pos = aChild->StylePosition(); - const auto alignment = (aAxis == eLogicalAxisInline) == !isOrthogonal + const auto alignment = (aAxis == LogicalAxis::Inline) == !isOrthogonal ? pos->UsedJustifySelf(Style())._0 : pos->UsedAlignSelf(Style())._0; return alignment == StyleAlignFlags::NORMAL || diff --git a/layout/generic/nsGridContainerFrame.h b/layout/generic/nsGridContainerFrame.h index cf3a3b5776..1fa7bbe487 100644 --- a/layout/generic/nsGridContainerFrame.h +++ b/layout/generic/nsGridContainerFrame.h @@ -233,12 +233,12 @@ class nsGridContainerFrame final : public nsContainerFrame, /** Return true if this frame is subgridded in its aAxis. */ bool IsSubgrid(LogicalAxis aAxis) const { - return HasAnyStateBits(aAxis == mozilla::eLogicalAxisBlock + return HasAnyStateBits(aAxis == mozilla::LogicalAxis::Block ? NS_STATE_GRID_IS_ROW_SUBGRID : NS_STATE_GRID_IS_COL_SUBGRID); } - bool IsColSubgrid() const { return IsSubgrid(mozilla::eLogicalAxisInline); } - bool IsRowSubgrid() const { return IsSubgrid(mozilla::eLogicalAxisBlock); } + bool IsColSubgrid() const { return IsSubgrid(mozilla::LogicalAxis::Inline); } + bool IsRowSubgrid() const { return IsSubgrid(mozilla::LogicalAxis::Block); } /** Return true if this frame is subgridded in any axis. */ bool IsSubgrid() const { return HasAnyStateBits(NS_STATE_GRID_IS_ROW_SUBGRID | @@ -247,7 +247,7 @@ class nsGridContainerFrame final : public nsContainerFrame, /** Return true if this frame has an item that is subgridded in our aAxis. */ bool HasSubgridItems(LogicalAxis aAxis) const { - return HasAnyStateBits(aAxis == mozilla::eLogicalAxisBlock + return HasAnyStateBits(aAxis == mozilla::LogicalAxis::Block ? NS_STATE_GRID_HAS_ROW_SUBGRID_ITEM : NS_STATE_GRID_HAS_COL_SUBGRID_ITEM); } @@ -375,10 +375,10 @@ class nsGridContainerFrame final : public nsContainerFrame, mozilla::IntrinsicISizeType aConstraint); nscoord GetBBaseline(BaselineSharingGroup aBaselineGroup) const { - return mBaseline[mozilla::eLogicalAxisBlock][aBaselineGroup]; + return mBaseline[mozilla::LogicalAxis::Block][aBaselineGroup]; } nscoord GetIBaseline(BaselineSharingGroup aBaselineGroup) const { - return mBaseline[mozilla::eLogicalAxisInline][aBaselineGroup]; + return mBaseline[mozilla::LogicalAxis::Inline][aBaselineGroup]; } /** @@ -566,12 +566,8 @@ class nsGridContainerFrame final : public nsContainerFrame, if (aFrame->IsSubtreeDirty()) { return false; } - - if (!CanCacheMeasurement(aFrame, aCBSize)) { - return false; - } - - return mKey == Key(aFrame, aCBSize); + const mozilla::Maybe<Key> maybeKey = Key::TryHash(aFrame, aCBSize); + return maybeKey.isSome() && mKey == *maybeKey; } static bool CanCacheMeasurement(const nsIFrame* aFrame, @@ -583,55 +579,54 @@ class nsGridContainerFrame final : public nsContainerFrame, void Update(const nsIFrame* aFrame, const LogicalSize& aCBSize, const nscoord aBSize) { - MOZ_ASSERT(CanCacheMeasurement(aFrame, aCBSize)); - mKey.mHashKey = Key::GenerateHash(aFrame, aCBSize); + mKey.UpdateHash(aFrame, aCBSize); mBSize = aBSize; } private: - struct Key { + class Key { // mHashKey is generated by combining these 2 variables together // 1. The containing block size in the item's inline axis used // for measuring reflow // 2. The item's baseline padding property uint32_t mHashKey; + explicit Key(uint32_t aHashKey) : mHashKey(aHashKey) {} + + public: Key() = default; Key(const nsIFrame* aFrame, const LogicalSize& aCBSize) { - MOZ_ASSERT(CanHash(aFrame, aCBSize)); - mHashKey = GenerateHash(aFrame, aCBSize); + UpdateHash(aFrame, aCBSize); } void UpdateHash(const nsIFrame* aFrame, const LogicalSize& aCBSize) { - MOZ_ASSERT(CanHash(aFrame, aCBSize)); - mHashKey = GenerateHash(aFrame, aCBSize); + const mozilla::Maybe<Key> maybeKey = TryHash(aFrame, aCBSize); + MOZ_ASSERT(maybeKey.isSome()); + mHashKey = maybeKey->mHashKey; } - static uint32_t GenerateHash(const nsIFrame* aFrame, - const LogicalSize& aCBSize) { - MOZ_ASSERT(CanHash(aFrame, aCBSize)); - - nscoord gridAreaISize = aCBSize.ISize(aFrame->GetWritingMode()); - nscoord bBaselinePaddingProperty = + static mozilla::Maybe<Key> TryHash(const nsIFrame* aFrame, + const LogicalSize& aCBSize) { + const nscoord gridAreaISize = aCBSize.ISize(aFrame->GetWritingMode()); + const nscoord bBaselinePaddingProperty = abs(aFrame->GetProperty(nsIFrame::BBaselinePadProperty())); - uint_fast8_t bitsNeededForISize = mozilla::FloorLog2(gridAreaISize) + 1; - - return (gridAreaISize << (32 - bitsNeededForISize)) | - bBaselinePaddingProperty; + const uint_fast8_t bitsNeededForISize = + mozilla::FloorLog2(gridAreaISize) + 1; + + const uint_fast8_t bitsNeededForBBaselinePadding = + mozilla::FloorLog2(bBaselinePaddingProperty) + 1; + if (bitsNeededForISize + bitsNeededForBBaselinePadding > 32) { + return mozilla::Nothing(); + } + const uint32_t hashKey = (gridAreaISize << (32 - bitsNeededForISize)) | + bBaselinePaddingProperty; + return mozilla::Some(Key(hashKey)); } static bool CanHash(const nsIFrame* aFrame, const LogicalSize& aCBSize) { - uint_fast8_t bitsNeededForISize = - mozilla::FloorLog2(aCBSize.ISize(aFrame->GetWritingMode())) + 1; - - uint_fast8_t bitsNeededForBBaselinePadding = - mozilla::FloorLog2( - abs(aFrame->GetProperty(nsIFrame::BBaselinePadProperty()))) + - 1; - - return bitsNeededForISize + bitsNeededForBBaselinePadding <= 32; + return TryHash(aFrame, aCBSize).isSome(); } bool operator==(const Key& aOther) const { diff --git a/layout/generic/nsHTMLCanvasFrame.cpp b/layout/generic/nsHTMLCanvasFrame.cpp index 07aeabec07..4b646359e5 100644 --- a/layout/generic/nsHTMLCanvasFrame.cpp +++ b/layout/generic/nsHTMLCanvasFrame.cpp @@ -382,7 +382,6 @@ nscoord nsHTMLCanvasFrame::GetMinISize(gfxContext* aRenderingContext) { result = nsPresContext::CSSPixelsToAppUnits( vertical ? GetCanvasSize().height : GetCanvasSize().width); } - DISPLAY_MIN_INLINE_SIZE(this, result); return result; } @@ -398,7 +397,6 @@ nscoord nsHTMLCanvasFrame::GetPrefISize(gfxContext* aRenderingContext) { result = nsPresContext::CSSPixelsToAppUnits( vertical ? GetCanvasSize().height : GetCanvasSize().width); } - DISPLAY_PREF_INLINE_SIZE(this, result); return result; } @@ -438,7 +436,6 @@ void nsHTMLCanvasFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_FRAME_TRACE( NS_FRAME_TRACE_CALLS, diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index 479c26d18b..28c328c9e2 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -19,6 +19,7 @@ #include "mozilla/DebugOnly.h" #include "mozilla/DisplayPortUtils.h" #include "mozilla/EventForwards.h" +#include "mozilla/FocusModel.h" #include "mozilla/dom/CSSAnimation.h" #include "mozilla/dom/CSSTransition.h" #include "mozilla/dom/ContentVisibilityAutoStateChangeEvent.h" @@ -2017,6 +2018,12 @@ bool nsIFrame::GetShapeBoxBorderRadii(nscoord aRadii[8]) const { } } +nscoord nsIFrame::OneEmInAppUnits() const { + return StyleFont() + ->mFont.size.ScaledBy(nsLayoutUtils::FontSizeInflationFor(this)) + .ToAppUnits(); +} + ComputedStyle* nsIFrame::GetAdditionalComputedStyle(int32_t aIndex) const { MOZ_ASSERT(aIndex >= 0, "invalid index number"); return nullptr; @@ -2396,6 +2403,15 @@ already_AddRefed<ComputedStyle> nsIFrame::ComputeHighlightSelectionStyle( *element, PseudoStyleType::highlight, aHighlightName, Style()); } +already_AddRefed<ComputedStyle> nsIFrame::ComputeTargetTextStyle() const { + const Element* element = FindElementAncestorForMozSelection(GetContent()); + if (!element) { + return nullptr; + } + return PresContext()->StyleSet()->ProbePseudoElementStyle( + *element, PseudoStyleType::targetText, nullptr, Style()); +} + template <typename SizeOrMaxSize> static inline bool IsIntrinsicKeyword(const SizeOrMaxSize& aSize) { // All keywords other than auto/none/-moz-available depend on intrinsic sizes. @@ -4039,6 +4055,13 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, return; } + nsIFrame* child = aChild; + auto* placeholder = child->IsPlaceholderFrame() + ? static_cast<nsPlaceholderFrame*>(child) + : nullptr; + nsIFrame* childOrOutOfFlow = + placeholder ? placeholder->GetOutOfFlowFrame() : child; + // If we're generating a display list for printing, include Link items for // frames that correspond to HTML link elements so that we can have active // links in saved PDF output. Note that the state of "within a link" is @@ -4050,17 +4073,10 @@ void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, Maybe<nsDisplayListBuilder::Linkifier> linkifier; if (StaticPrefs::print_save_as_pdf_links_enabled() && aBuilder->IsForPrinting()) { - linkifier.emplace(aBuilder, aChild, aLists.Content()); - linkifier->MaybeAppendLink(aBuilder, aChild); + linkifier.emplace(aBuilder, childOrOutOfFlow, aLists.Content()); + linkifier->MaybeAppendLink(aBuilder, childOrOutOfFlow); } - nsIFrame* child = aChild; - auto* placeholder = child->IsPlaceholderFrame() - ? static_cast<nsPlaceholderFrame*>(child) - : nullptr; - nsIFrame* childOrOutOfFlow = - placeholder ? placeholder->GetOutOfFlowFrame() : child; - nsIFrame* parent = childOrOutOfFlow->GetParent(); const auto* parentDisplay = parent->StyleDisplay(); const auto overflowClipAxes = @@ -4913,12 +4929,14 @@ bool nsIFrame::MovingCaretToEventPointAllowedIfSecondaryButtonEvent( : aContentAtEventPoint.GetClosestNativeAnonymousSubtreeRoot()); if (Selection* selection = aFrameSelection.GetSelection(SelectionType::eNormal)) { - const bool selectionIsCollapsed = selection->IsCollapsed(); - // If right click in a selection range, we should not collapse selection. - if (!selectionIsCollapsed && - nsContentUtils::IsPointInSelection( - *selection, aContentAtEventPoint, - static_cast<uint32_t>(aOffsetAtEventPoint))) { + const bool selectionIsCollapsed = + selection->AreNormalAndCrossShadowBoundaryRangesCollapsed(); + // If right click in a selection range, we should not collapse + // selection. + if (!selectionIsCollapsed && nsContentUtils::IsPointInSelection( + *selection, aContentAtEventPoint, + static_cast<uint32_t>(aOffsetAtEventPoint), + true /* aAllowCrossShadowBoundary */)) { return false; } const bool wantToPreventMoveCaret = @@ -5120,7 +5138,9 @@ nsresult nsIFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack, return rv; } if (aAmountBack == eSelectWord) { - frameSelection->SetIsDoubleClickSelection(true); + frameSelection->SetClickSelectionType(ClickSelectionType::Double); + } else if (aAmountBack == eSelectParagraph) { + frameSelection->SetClickSelectionType(ClickSelectionType::Triple); } // maintain selection @@ -5989,18 +6009,10 @@ void nsIFrame::MarkSubtreeDirty() { } /* virtual */ -nscoord nsIFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result = 0; - DISPLAY_MIN_INLINE_SIZE(this, result); - return result; -} +nscoord nsIFrame::GetMinISize(gfxContext* aRenderingContext) { return 0; } /* virtual */ -nscoord nsIFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result = 0; - DISPLAY_PREF_INLINE_SIZE(this, result); - return result; -} +nscoord nsIFrame::GetPrefISize(gfxContext* aRenderingContext) { return 0; } /* virtual */ void nsIFrame::AddInlineMinISize(gfxContext* aRenderingContext, @@ -6319,14 +6331,12 @@ static MinMaxSize ComputeTransferredMinMaxInlineSize( if (aMinMaxBSize.mMinSize > 0) { transferredISize.mMinSize = aAspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, aWM, aMinMaxBSize.mMinSize, - aBoxSizingAdjustment); + LogicalAxis::Inline, aWM, aMinMaxBSize.mMinSize, aBoxSizingAdjustment); } if (aMinMaxBSize.mMaxSize != NS_UNCONSTRAINEDSIZE) { transferredISize.mMaxSize = aAspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, aWM, aMinMaxBSize.mMaxSize, - aBoxSizingAdjustment); + LogicalAxis::Inline, aWM, aMinMaxBSize.mMaxSize, aBoxSizingAdjustment); } // Minimum size wins over maximum size. @@ -6393,11 +6403,11 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize( // indicates which axis (in this frame's own WM) corresponds to its // flex container's main axis. LogicalAxis flexMainAxis = - eLogicalAxisInline; // (init to make valgrind happy) + LogicalAxis::Inline; // (init to make valgrind happy) if (isFlexItem) { flexMainAxis = nsFlexContainerFrame::IsItemInlineAxisMainAxis(this) - ? eLogicalAxisInline - : eLogicalAxisBlock; + ? LogicalAxis::Inline + : LogicalAxis::Block; } const bool isOrthogonal = aWM.IsOrthogonalTo(alignCB->GetWritingMode()); @@ -6426,8 +6436,8 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize( bool mayUseAspectRatio = aspectRatio && !isAutoBSize; if (!aFlags.contains(ComputeSizeFlag::ShrinkWrap) && !StyleMargin()->HasInlineAxisAuto(aWM) && - !alignCB->IsMasonry(isOrthogonal ? eLogicalAxisBlock - : eLogicalAxisInline)) { + !alignCB->IsMasonry(isOrthogonal ? LogicalAxis::Block + : LogicalAxis::Inline)) { auto inlineAxisAlignment = isOrthogonal ? StylePosition()->UsedAlignSelf(alignCB->Style())._0 : StylePosition()->UsedJustifySelf(alignCB->Style())._0; @@ -6451,7 +6461,7 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize( aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), styleBSize.AsLengthPercentage()); result.ISize(aWM) = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, aWM, bSize, boxSizingAdjust); + LogicalAxis::Inline, aWM, bSize, boxSizingAdjust); aspectRatioUsage = AspectRatioUsage::ToComputeISize; } @@ -6468,7 +6478,7 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize( aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), styleBSize.AsLengthPercentage()); result.ISize(aWM) = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, aWM, bSize, boxSizingAdjust); + LogicalAxis::Inline, aWM, bSize, boxSizingAdjust); aspectRatioUsage = AspectRatioUsage::ToComputeISize; } @@ -6523,7 +6533,7 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize( // flex container's main-axis. (Those properties get applied later in // the flexbox algorithm.) const bool isFlexItemInlineAxisMainAxis = - isFlexItem && flexMainAxis == eLogicalAxisInline; + isFlexItem && flexMainAxis == LogicalAxis::Inline; // Grid items that are subgridded in inline-axis also ignore their min & max // sizing properties in that axis. const bool shouldIgnoreMinMaxISize = @@ -6602,8 +6612,8 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize( } else if (MOZ_UNLIKELY(isGridItem) && styleBSize.IsAuto() && !aFlags.contains(ComputeSizeFlag::IsGridMeasuringReflow) && !IsTrueOverflowContainer() && - !alignCB->IsMasonry(isOrthogonal ? eLogicalAxisInline - : eLogicalAxisBlock)) { + !alignCB->IsMasonry(isOrthogonal ? LogicalAxis::Inline + : LogicalAxis::Block)) { auto cbSize = aCBSize.BSize(aWM); if (cbSize != NS_UNCONSTRAINEDSIZE) { // 'auto' block-size for grid-level box - fill the CB for 'stretch' / @@ -6628,8 +6638,7 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize( // https://drafts.csswg.org/css-grid/#grid-item-sizing if (!stretch && mayUseAspectRatio) { result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisBlock, aWM, result.ISize(aWM), - boxSizingAdjust); + LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust); MOZ_ASSERT(aspectRatioUsage == AspectRatioUsage::None); aspectRatioUsage = AspectRatioUsage::ToComputeBSize; } @@ -6651,8 +6660,7 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize( // applied (so aspectRatioUsage flag is set as expected). That's why we // apply aspect-ratio unconditionally for auto block size here. result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisBlock, aWM, result.ISize(aWM), - boxSizingAdjust); + LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust); MOZ_ASSERT(aspectRatioUsage == AspectRatioUsage::None); aspectRatioUsage = AspectRatioUsage::ToComputeBSize; } @@ -6662,7 +6670,7 @@ nsIFrame::SizeComputationResult nsIFrame::ComputeSize( // container's main-axis. (Those properties get applied later in the flexbox // algorithm.) const bool isFlexItemBlockAxisMainAxis = - isFlexItem && flexMainAxis == eLogicalAxisBlock; + isFlexItem && flexMainAxis == LogicalAxis::Block; // Grid items that are subgridded in block-axis also ignore their min & max // sizing properties in that axis. const bool shouldIgnoreMinMaxBSize = @@ -6789,7 +6797,7 @@ Maybe<nscoord> nsIFrame::ComputeInlineSizeFromAspectRatio( aCBSize.BSize(aWM), aContentEdgeToBoxSizing.BSize(aWM), styleBSize.AsLengthPercentage()); return Some(aspectRatio.ComputeRatioDependentSize( - LogicalAxis::eLogicalAxisInline, aWM, bSize, aContentEdgeToBoxSizing)); + LogicalAxis::Inline, aWM, bSize, aContentEdgeToBoxSizing)); } nsIFrame::ISizeComputationResult nsIFrame::ComputeISizeValue( @@ -7884,7 +7892,7 @@ nsRect nsIFrame::GetNormalRect() const { nsRect nsIFrame::GetBoundingClientRect() { return nsLayoutUtils::GetAllInFlowRectsUnion( this, nsLayoutUtils::GetContainingBlockForClientRect(this), - nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS); + nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms); } nsPoint nsIFrame::GetPositionIgnoringScrolling() const { @@ -7945,8 +7953,19 @@ OverflowAreas nsIFrame::GetActualAndNormalOverflowAreasRelativeToParent() } const OverflowAreas overflows = GetOverflowAreas(); - OverflowAreas actualAndNormalOverflows = overflows + GetPosition(); - actualAndNormalOverflows.UnionWith(overflows + GetNormalPosition()); + OverflowAreas actualAndNormalOverflows = overflows + GetNormalPosition(); + if (IsRelativelyPositioned()) { + actualAndNormalOverflows.UnionWith(overflows + GetPosition()); + } else { + // For sticky positioned elements, we only use the normal position for the + // scrollable overflow. This avoids circular dependencies between sticky + // positioned elements and their scroll container. (The scroll position and + // the scroll container's size impact the sticky position, so we don't want + // the sticky position to impact them.) + MOZ_ASSERT(IsStickyPositioned()); + actualAndNormalOverflows.UnionWith( + OverflowAreas(overflows.InkOverflow() + GetPosition(), nsRect())); + } return actualAndNormalOverflows; } @@ -8003,7 +8022,7 @@ bool nsIFrame::UpdateOverflow() { if (nsView* view = GetView()) { // Make sure the frame's view is properly sized. nsViewManager* vm = view->GetViewManager(); - vm->ResizeView(view, overflowAreas.InkOverflow(), true); + vm->ResizeView(view, overflowAreas.InkOverflow()); } return true; @@ -8560,6 +8579,12 @@ const nsFrameSelection* nsIFrame::GetConstFrameSelection() const { bool nsIFrame::IsFrameSelected() const { NS_ASSERTION(!GetContent() || GetContent()->IsMaybeSelected(), "use the public IsSelected() instead"); + if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { + if (const ShadowRoot* shadowRoot = + GetContent()->GetShadowRootForSelection()) { + return shadowRoot->IsSelected(0, shadowRoot->GetChildCount()); + } + } return GetContent()->IsSelected(0, GetContent()->GetChildCount()); } @@ -8983,6 +9008,13 @@ nsresult nsIFrame::PeekOffsetForParagraph(PeekOffsetStruct* aPos) { if (reachedLimit) { // no "stop frame" found aPos->mResultContent = frame->GetContent(); + if (ShadowRoot* shadowRoot = + aPos->mResultContent->GetShadowRootForSelection()) { + // Even if there's no children for this node, + // the elements inside the shadow root is still + // selectable + aPos->mResultContent = shadowRoot; + } if (aPos->mDirection == eDirPrevious) { aPos->mContentOffset = 0; } else if (aPos->mResultContent) { @@ -10698,21 +10730,27 @@ ComputedStyle* nsIFrame::DoGetParentComputedStyle( } void nsIFrame::GetLastLeaf(nsIFrame** aFrame) { - if (!aFrame || !*aFrame) return; - nsIFrame* child = *aFrame; - // if we are a block frame then go for the last line of 'this' - while (1) { - child = child->PrincipalChildList().FirstChild(); - if (!child) return; // nothing to do - nsIFrame* siblingFrame; - nsIContent* content; - // ignore anonymous elements, e.g. mozTableAdd* mozTableRemove* - // see bug 278197 comment #12 #13 for details - while ((siblingFrame = child->GetNextSibling()) && - (content = siblingFrame->GetContent()) && - !content->IsRootOfNativeAnonymousSubtree()) - child = siblingFrame; - *aFrame = child; + if (!aFrame || !*aFrame) { + return; + } + for (nsIFrame* maybeLastLeaf = (*aFrame)->PrincipalChildList().LastChild(); + maybeLastLeaf;) { + nsIFrame* lastChildNotInSubTree = nullptr; + for (nsIFrame* child = maybeLastLeaf; child; + child = child->GetPrevSibling()) { + nsIContent* content = child->GetContent(); + // ignore anonymous elements, e.g. mozTableAdd* mozTableRemove* + // see bug 278197 comment #12 #13 for details + if (content && !content->IsRootOfNativeAnonymousSubtree()) { + lastChildNotInSubTree = child; + break; + } + } + if (!lastChildNotInSubTree) { + return; + } + *aFrame = lastChildNotInSubTree; + maybeLastLeaf = lastChildNotInSubTree->PrincipalChildList().LastChild(); } } @@ -10771,7 +10809,7 @@ bool nsIFrame::IsFocusableDueToScrollFrame() { return true; } -Focusable nsIFrame::IsFocusable(bool aWithMouse, bool aCheckVisibility) { +Focusable nsIFrame::IsFocusable(IsFocusableFlags aFlags) { // cannot focus content in print preview mode. Only the root can be focused, // but that's handled elsewhere. if (PresContext()->Type() == nsPresContext::eContext_PrintPreview) { @@ -10782,7 +10820,8 @@ Focusable nsIFrame::IsFocusable(bool aWithMouse, bool aCheckVisibility) { return {}; } - if (aCheckVisibility && !IsVisibleConsideringAncestors()) { + if (!(aFlags & IsFocusableFlags::IgnoreVisibility) && + !IsVisibleConsideringAncestors()) { return {}; } @@ -10802,14 +10841,14 @@ Focusable nsIFrame::IsFocusable(bool aWithMouse, bool aCheckVisibility) { // As a legacy special-case, -moz-user-focus controls focusability and // tabability of XUL elements in some circumstances (which default to // -moz-user-focus: ignore). - auto focusability = xul->GetXULFocusability(aWithMouse); + auto focusability = xul->GetXULFocusability(aFlags); focusable.mFocusable = focusability.mForcedFocusable.valueOr(uf == StyleUserFocus::Normal); if (focusable) { focusable.mTabIndex = focusability.mForcedTabIndexIfFocusable.valueOr(0); } } else { - focusable = mContent->IsFocusableWithoutStyle(aWithMouse); + focusable = mContent->IsFocusableWithoutStyle(aFlags); } if (focusable) { @@ -10817,7 +10856,8 @@ Focusable nsIFrame::IsFocusable(bool aWithMouse, bool aCheckVisibility) { } // If we're focusing with the mouse we never focus scroll areas. - if (!aWithMouse && IsFocusableDueToScrollFrame()) { + if (!(aFlags & IsFocusableFlags::WithMouse) && + IsFocusableDueToScrollFrame()) { return {true, 0}; } @@ -10907,7 +10947,7 @@ void nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame, // Now that we've updated the style on aChildFrame, check whether it itself // has anon boxes to deal with. ServoRestyleState childrenState(*aChildFrame, aRestyleState, childHint, - ServoRestyleState::Type::InFlow); + ServoRestyleState::CanUseHandledHints::Yes); aChildFrame->UpdateStyleOfOwnedAnonBoxes(childrenState); // Assuming anon boxes don't have ::backdrop associated with them... if that @@ -11603,6 +11643,47 @@ bool nsIFrame::HasUnreflowedContainerQueryAncestor() const { return false; } +bool nsIFrame::ShouldBreakBefore( + const ReflowInput::BreakType aBreakType) const { + const auto* display = StyleDisplay(); + return ShouldBreakBetween(display, display->mBreakBefore, aBreakType); +} + +bool nsIFrame::ShouldBreakAfter(const ReflowInput::BreakType aBreakType) const { + const auto* display = StyleDisplay(); + return ShouldBreakBetween(display, display->mBreakAfter, aBreakType); +} + +bool nsIFrame::ShouldBreakBetween( + const nsStyleDisplay* aDisplay, const StyleBreakBetween aBreakBetween, + const ReflowInput::BreakType aBreakType) const { + const bool shouldBreakBetween = [&] { + switch (aBreakBetween) { + case StyleBreakBetween::Always: + return true; + case StyleBreakBetween::Auto: + case StyleBreakBetween::Avoid: + return false; + case StyleBreakBetween::Page: + case StyleBreakBetween::Left: + case StyleBreakBetween::Right: + return aBreakType == ReflowInput::BreakType::Page; + } + MOZ_ASSERT_UNREACHABLE("Unknown break-between value!"); + return false; + }(); + + if (!shouldBreakBetween) { + return false; + } + if (IsAbsolutelyPositioned(aDisplay)) { + // 'break-before' and 'break-after' properties does not apply to + // absolutely-positioned boxes. + return false; + } + return true; +} + #ifdef DEBUG static void GetTagName(nsIFrame* aFrame, nsIContent* aContent, int aResultSize, char* aResult) { @@ -11655,1037 +11736,6 @@ void nsIFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) { } } -// Start Display Reflow -DR_cookie::DR_cookie(nsPresContext* aPresContext, nsIFrame* aFrame, - const ReflowInput& aReflowInput, ReflowOutput& aMetrics, - nsReflowStatus& aStatus) - : mPresContext(aPresContext), - mFrame(aFrame), - mReflowInput(aReflowInput), - mMetrics(aMetrics), - mStatus(aStatus) { - MOZ_COUNT_CTOR(DR_cookie); - mValue = nsIFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowInput); -} - -DR_cookie::~DR_cookie() { - MOZ_COUNT_DTOR(DR_cookie); - nsIFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue); -} - -DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame) : mFrame(aFrame) { - MOZ_COUNT_CTOR(DR_layout_cookie); - mValue = nsIFrame::DisplayLayoutEnter(mFrame); -} - -DR_layout_cookie::~DR_layout_cookie() { - MOZ_COUNT_DTOR(DR_layout_cookie); - nsIFrame::DisplayLayoutExit(mFrame, mValue); -} - -DR_intrinsic_inline_size_cookie::DR_intrinsic_inline_size_cookie( - nsIFrame* aFrame, const char* aType, nscoord& aResult) - : mFrame(aFrame), mType(aType), mResult(aResult) { - MOZ_COUNT_CTOR(DR_intrinsic_inline_size_cookie); - mValue = nsIFrame::DisplayIntrinsicISizeEnter(mFrame, mType); -} - -DR_intrinsic_inline_size_cookie::~DR_intrinsic_inline_size_cookie() { - MOZ_COUNT_DTOR(DR_intrinsic_inline_size_cookie); - nsIFrame::DisplayIntrinsicISizeExit(mFrame, mType, mResult, mValue); -} - -DR_intrinsic_size_cookie::DR_intrinsic_size_cookie(nsIFrame* aFrame, - const char* aType, - nsSize& aResult) - : mFrame(aFrame), mType(aType), mResult(aResult) { - MOZ_COUNT_CTOR(DR_intrinsic_size_cookie); - mValue = nsIFrame::DisplayIntrinsicSizeEnter(mFrame, mType); -} - -DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie() { - MOZ_COUNT_DTOR(DR_intrinsic_size_cookie); - nsIFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue); -} - -DR_init_constraints_cookie::DR_init_constraints_cookie( - nsIFrame* aFrame, ReflowInput* aState, nscoord aCBWidth, nscoord aCBHeight, - const mozilla::Maybe<mozilla::LogicalMargin> aBorder, - const mozilla::Maybe<mozilla::LogicalMargin> aPadding) - : mFrame(aFrame), mState(aState) { - MOZ_COUNT_CTOR(DR_init_constraints_cookie); - nsMargin border; - if (aBorder) { - border = aBorder->GetPhysicalMargin(aFrame->GetWritingMode()); - } - nsMargin padding; - if (aPadding) { - padding = aPadding->GetPhysicalMargin(aFrame->GetWritingMode()); - } - mValue = ReflowInput::DisplayInitConstraintsEnter( - mFrame, mState, aCBWidth, aCBHeight, aBorder ? &border : nullptr, - aPadding ? &padding : nullptr); -} - -DR_init_constraints_cookie::~DR_init_constraints_cookie() { - MOZ_COUNT_DTOR(DR_init_constraints_cookie); - ReflowInput::DisplayInitConstraintsExit(mFrame, mState, mValue); -} - -DR_init_offsets_cookie::DR_init_offsets_cookie( - nsIFrame* aFrame, SizeComputationInput* aState, nscoord aPercentBasis, - WritingMode aCBWritingMode, - const mozilla::Maybe<mozilla::LogicalMargin> aBorder, - const mozilla::Maybe<mozilla::LogicalMargin> aPadding) - : mFrame(aFrame), mState(aState) { - MOZ_COUNT_CTOR(DR_init_offsets_cookie); - nsMargin border; - if (aBorder) { - border = aBorder->GetPhysicalMargin(aFrame->GetWritingMode()); - } - nsMargin padding; - if (aPadding) { - padding = aPadding->GetPhysicalMargin(aFrame->GetWritingMode()); - } - mValue = SizeComputationInput::DisplayInitOffsetsEnter( - mFrame, mState, aPercentBasis, aCBWritingMode, - aBorder ? &border : nullptr, aPadding ? &padding : nullptr); -} - -DR_init_offsets_cookie::~DR_init_offsets_cookie() { - MOZ_COUNT_DTOR(DR_init_offsets_cookie); - SizeComputationInput::DisplayInitOffsetsExit(mFrame, mState, mValue); -} - -struct DR_Rule; - -struct DR_FrameTypeInfo { - DR_FrameTypeInfo(LayoutFrameType aFrameType, const char* aFrameNameAbbrev, - const char* aFrameName); - ~DR_FrameTypeInfo(); - - LayoutFrameType mType; - char mNameAbbrev[16]; - char mName[32]; - nsTArray<DR_Rule*> mRules; - - private: - DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) = delete; -}; - -struct DR_FrameTreeNode; -struct DR_Rule; - -struct DR_State { - DR_State(); - ~DR_State(); - void Init(); - void AddFrameTypeInfo(LayoutFrameType aFrameType, - const char* aFrameNameAbbrev, const char* aFrameName); - DR_FrameTypeInfo* GetFrameTypeInfo(LayoutFrameType aFrameType); - DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName); - void InitFrameTypeTable(); - DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame, - const ReflowInput* aReflowInput); - void FindMatchingRule(DR_FrameTreeNode& aNode); - bool RuleMatches(DR_Rule& aRule, DR_FrameTreeNode& aNode); - bool GetToken(FILE* aFile, char* aBuf, size_t aBufSize); - DR_Rule* ParseRule(FILE* aFile); - void ParseRulesFile(); - void AddRule(nsTArray<DR_Rule*>& aRules, DR_Rule& aRule); - bool IsWhiteSpace(int c); - bool GetNumber(char* aBuf, int32_t& aNumber); - void PrettyUC(nscoord aSize, char* aBuf, int aBufSize); - void PrintMargin(const char* tag, const nsMargin* aMargin); - void DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent); - void DeleteTreeNode(DR_FrameTreeNode& aNode); - - bool mInited; - bool mActive; - int32_t mCount; - int32_t mAssert; - int32_t mIndent; - bool mIndentUndisplayedFrames; - bool mDisplayPixelErrors; - nsTArray<DR_Rule*> mWildRules; - nsTArray<DR_FrameTypeInfo> mFrameTypeTable; - // reflow specific state - nsTArray<DR_FrameTreeNode*> mFrameTreeLeaves; -}; - -static DR_State* DR_state; // the one and only DR_State - -struct DR_RulePart { - explicit DR_RulePart(LayoutFrameType aFrameType) - : mFrameType(aFrameType), mNext(0) {} - - void Destroy(); - - LayoutFrameType mFrameType; - DR_RulePart* mNext; -}; - -void DR_RulePart::Destroy() { - if (mNext) { - mNext->Destroy(); - } - delete this; -} - -struct DR_Rule { - DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) { - MOZ_COUNT_CTOR(DR_Rule); - } - ~DR_Rule() { - if (mTarget) mTarget->Destroy(); - MOZ_COUNT_DTOR(DR_Rule); - } - void AddPart(LayoutFrameType aFrameType); - - uint32_t mLength; - DR_RulePart* mTarget; - bool mDisplay; -}; - -void DR_Rule::AddPart(LayoutFrameType aFrameType) { - DR_RulePart* newPart = new DR_RulePart(aFrameType); - newPart->mNext = mTarget; - mTarget = newPart; - mLength++; -} - -DR_FrameTypeInfo::~DR_FrameTypeInfo() { - int32_t numElements; - numElements = mRules.Length(); - for (int32_t i = numElements - 1; i >= 0; i--) { - delete mRules.ElementAt(i); - } -} - -DR_FrameTypeInfo::DR_FrameTypeInfo(LayoutFrameType aFrameType, - const char* aFrameNameAbbrev, - const char* aFrameName) { - mType = aFrameType; - PL_strncpyz(mNameAbbrev, aFrameNameAbbrev, sizeof(mNameAbbrev)); - PL_strncpyz(mName, aFrameName, sizeof(mName)); -} - -struct DR_FrameTreeNode { - DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent) - : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0) { - MOZ_COUNT_CTOR(DR_FrameTreeNode); - } - - MOZ_COUNTED_DTOR(DR_FrameTreeNode) - - nsIFrame* mFrame; - DR_FrameTreeNode* mParent; - bool mDisplay; - uint32_t mIndent; -}; - -// DR_State implementation - -DR_State::DR_State() - : mInited(false), - mActive(false), - mCount(0), - mAssert(-1), - mIndent(0), - mIndentUndisplayedFrames(false), - mDisplayPixelErrors(false) { - MOZ_COUNT_CTOR(DR_State); -} - -void DR_State::Init() { - char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT"); - int32_t num; - if (env) { - if (GetNumber(env, num)) - mAssert = num; - else - printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env); - } - - env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START"); - if (env) { - if (GetNumber(env, num)) - mIndent = num; - else - printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env); - } - - env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES"); - if (env) { - if (GetNumber(env, num)) - mIndentUndisplayedFrames = num; - else - printf( - "GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s", - env); - } - - env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS"); - if (env) { - if (GetNumber(env, num)) - mDisplayPixelErrors = num; - else - printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s", - env); - } - - InitFrameTypeTable(); - ParseRulesFile(); - mInited = true; -} - -DR_State::~DR_State() { - MOZ_COUNT_DTOR(DR_State); - int32_t numElements, i; - numElements = mWildRules.Length(); - for (i = numElements - 1; i >= 0; i--) { - delete mWildRules.ElementAt(i); - } - numElements = mFrameTreeLeaves.Length(); - for (i = numElements - 1; i >= 0; i--) { - delete mFrameTreeLeaves.ElementAt(i); - } -} - -bool DR_State::GetNumber(char* aBuf, int32_t& aNumber) { - if (sscanf(aBuf, "%d", &aNumber) > 0) - return true; - else - return false; -} - -bool DR_State::IsWhiteSpace(int c) { - return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'); -} - -bool DR_State::GetToken(FILE* aFile, char* aBuf, size_t aBufSize) { - bool haveToken = false; - aBuf[0] = 0; - // get the 1st non whitespace char - int c = -1; - for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) { - } - - if (c > 0) { - haveToken = true; - aBuf[0] = c; - // get everything up to the next whitespace char - size_t cX; - for (cX = 1; cX + 1 < aBufSize; cX++) { - c = getc(aFile); - if (c < 0) { // EOF - ungetc(' ', aFile); - break; - } else { - if (IsWhiteSpace(c)) { - break; - } else { - aBuf[cX] = c; - } - } - } - aBuf[cX] = 0; - } - return haveToken; -} - -DR_Rule* DR_State::ParseRule(FILE* aFile) { - char buf[128]; - int32_t doDisplay; - DR_Rule* rule = nullptr; - while (GetToken(aFile, buf, sizeof(buf))) { - if (GetNumber(buf, doDisplay)) { - if (rule) { - rule->mDisplay = !!doDisplay; - break; - } else { - printf("unexpected token - %s \n", buf); - } - } else { - if (!rule) { - rule = new DR_Rule; - } - if (strcmp(buf, "*") == 0) { - rule->AddPart(LayoutFrameType::None); - } else { - DR_FrameTypeInfo* info = GetFrameTypeInfo(buf); - if (info) { - rule->AddPart(info->mType); - } else { - printf("invalid frame type - %s \n", buf); - } - } - } - } - return rule; -} - -void DR_State::AddRule(nsTArray<DR_Rule*>& aRules, DR_Rule& aRule) { - int32_t numRules = aRules.Length(); - for (int32_t ruleX = 0; ruleX < numRules; ruleX++) { - DR_Rule* rule = aRules.ElementAt(ruleX); - NS_ASSERTION(rule, "program error"); - if (aRule.mLength > rule->mLength) { - aRules.InsertElementAt(ruleX, &aRule); - return; - } - } - aRules.AppendElement(&aRule); -} - -static Maybe<bool> ShouldLogReflow(const char* processes) { - switch (processes[0]) { - case 'A': - case 'a': - return Some(true); - case 'P': - case 'p': - return Some(XRE_IsParentProcess()); - case 'C': - case 'c': - return Some(XRE_IsContentProcess()); - default: - return Nothing{}; - } -} - -void DR_State::ParseRulesFile() { - char* processes = PR_GetEnv("GECKO_DISPLAY_REFLOW_PROCESSES"); - if (processes) { - Maybe<bool> enableLog = ShouldLogReflow(processes); - if (enableLog.isNothing()) { - MOZ_CRASH("GECKO_DISPLAY_REFLOW_PROCESSES: [a]ll [p]arent [c]ontent"); - } else if (enableLog.value()) { - DR_Rule* rule = new DR_Rule; - rule->AddPart(LayoutFrameType::None); - rule->mDisplay = true; - AddRule(mWildRules, *rule); - mActive = true; - } - return; - } - - char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE"); - if (path) { - FILE* inFile = fopen(path, "r"); - if (!inFile) { - MOZ_CRASH( - "Failed to open the specified rules file; Try `--setpref " - "security.sandbox.content.level=2` if the sandbox is at cause"); - } - for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) { - if (rule->mTarget) { - LayoutFrameType fType = rule->mTarget->mFrameType; - if (fType != LayoutFrameType::None) { - DR_FrameTypeInfo* info = GetFrameTypeInfo(fType); - AddRule(info->mRules, *rule); - } else { - AddRule(mWildRules, *rule); - } - mActive = true; - } - } - - fclose(inFile); - } -} - -void DR_State::AddFrameTypeInfo(LayoutFrameType aFrameType, - const char* aFrameNameAbbrev, - const char* aFrameName) { - mFrameTypeTable.EmplaceBack(aFrameType, aFrameNameAbbrev, aFrameName); -} - -DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(LayoutFrameType aFrameType) { - int32_t numEntries = mFrameTypeTable.Length(); - NS_ASSERTION(numEntries != 0, "empty FrameTypeTable"); - for (int32_t i = 0; i < numEntries; i++) { - DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i); - if (info.mType == aFrameType) { - return &info; - } - } - return &mFrameTypeTable.ElementAt(numEntries - - 1); // return unknown frame type -} - -DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName) { - int32_t numEntries = mFrameTypeTable.Length(); - NS_ASSERTION(numEntries != 0, "empty FrameTypeTable"); - for (int32_t i = 0; i < numEntries; i++) { - DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i); - if ((strcmp(aFrameName, info.mName) == 0) || - (strcmp(aFrameName, info.mNameAbbrev) == 0)) { - return &info; - } - } - return &mFrameTypeTable.ElementAt(numEntries - - 1); // return unknown frame type -} - -void DR_State::InitFrameTypeTable() { - AddFrameTypeInfo(LayoutFrameType::Block, "block", "block"); - AddFrameTypeInfo(LayoutFrameType::Br, "br", "br"); - AddFrameTypeInfo(LayoutFrameType::ColorControl, "color", "colorControl"); - AddFrameTypeInfo(LayoutFrameType::GfxButtonControl, "button", - "gfxButtonControl"); - AddFrameTypeInfo(LayoutFrameType::HTMLButtonControl, "HTMLbutton", - "HTMLButtonControl"); - AddFrameTypeInfo(LayoutFrameType::HTMLCanvas, "HTMLCanvas", "HTMLCanvas"); - AddFrameTypeInfo(LayoutFrameType::SubDocument, "subdoc", "subDocument"); - AddFrameTypeInfo(LayoutFrameType::Image, "img", "image"); - AddFrameTypeInfo(LayoutFrameType::Inline, "inline", "inline"); - AddFrameTypeInfo(LayoutFrameType::Letter, "letter", "letter"); - AddFrameTypeInfo(LayoutFrameType::Line, "line", "line"); - AddFrameTypeInfo(LayoutFrameType::ListControl, "select", "select"); - AddFrameTypeInfo(LayoutFrameType::Page, "page", "page"); - AddFrameTypeInfo(LayoutFrameType::Placeholder, "place", "placeholder"); - AddFrameTypeInfo(LayoutFrameType::Canvas, "canvas", "canvas"); - AddFrameTypeInfo(LayoutFrameType::Scroll, "scroll", "scroll"); - AddFrameTypeInfo(LayoutFrameType::TableCell, "cell", "tableCell"); - AddFrameTypeInfo(LayoutFrameType::TableCol, "col", "tableCol"); - AddFrameTypeInfo(LayoutFrameType::TableColGroup, "colG", "tableColGroup"); - AddFrameTypeInfo(LayoutFrameType::Table, "tbl", "table"); - AddFrameTypeInfo(LayoutFrameType::TableWrapper, "tblW", "tableWrapper"); - AddFrameTypeInfo(LayoutFrameType::TableRowGroup, "rowG", "tableRowGroup"); - AddFrameTypeInfo(LayoutFrameType::TableRow, "row", "tableRow"); - AddFrameTypeInfo(LayoutFrameType::TextInput, "textCtl", "textInput"); - AddFrameTypeInfo(LayoutFrameType::Text, "text", "text"); - AddFrameTypeInfo(LayoutFrameType::Viewport, "VP", "viewport"); - AddFrameTypeInfo(LayoutFrameType::Slider, "Slider", "Slider"); - AddFrameTypeInfo(LayoutFrameType::None, "unknown", "unknown"); -} - -void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent) { - DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->Type()); - if (frameTypeInfo) { - for (int32_t i = 0; i < aIndent; i++) { - printf(" "); - } - if (!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) { - if (aFrame) { - nsAutoString name; - aFrame->GetFrameName(name); - printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(), - (void*)aFrame); - } else { - printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame); - } - } else { - printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame); - } - } -} - -bool DR_State::RuleMatches(DR_Rule& aRule, DR_FrameTreeNode& aNode) { - NS_ASSERTION(aRule.mTarget, "program error"); - - DR_RulePart* rulePart; - DR_FrameTreeNode* parentNode; - for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent; - rulePart && parentNode; - rulePart = rulePart->mNext, parentNode = parentNode->mParent) { - if (rulePart->mFrameType != LayoutFrameType::None) { - if (parentNode->mFrame) { - if (rulePart->mFrameType != parentNode->mFrame->Type()) { - return false; - } - } else - NS_ASSERTION(false, "program error"); - } - // else wild card match - } - return true; -} - -void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode) { - if (!aNode.mFrame) { - NS_ASSERTION(false, "invalid DR_FrameTreeNode \n"); - return; - } - - bool matchingRule = false; - - DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->Type()); - NS_ASSERTION(info, "program error"); - int32_t numRules = info->mRules.Length(); - for (int32_t ruleX = 0; ruleX < numRules; ruleX++) { - DR_Rule* rule = info->mRules.ElementAt(ruleX); - if (rule && RuleMatches(*rule, aNode)) { - aNode.mDisplay = rule->mDisplay; - matchingRule = true; - break; - } - } - if (!matchingRule) { - int32_t numWildRules = mWildRules.Length(); - for (int32_t ruleX = 0; ruleX < numWildRules; ruleX++) { - DR_Rule* rule = mWildRules.ElementAt(ruleX); - if (rule && RuleMatches(*rule, aNode)) { - aNode.mDisplay = rule->mDisplay; - break; - } - } - } -} - -DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame, - const ReflowInput* aReflowInput) { - // find the frame of the parent reflow input (usually just the parent of - // aFrame) - nsIFrame* parentFrame; - if (aReflowInput) { - const ReflowInput* parentRI = aReflowInput->mParentReflowInput; - parentFrame = (parentRI) ? parentRI->mFrame : nullptr; - } else { - parentFrame = aFrame->GetParent(); - } - - // find the parent tree node leaf - DR_FrameTreeNode* parentNode = nullptr; - - DR_FrameTreeNode* lastLeaf = nullptr; - if (mFrameTreeLeaves.Length()) - lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1); - if (lastLeaf) { - for (parentNode = lastLeaf; - parentNode && (parentNode->mFrame != parentFrame); - parentNode = parentNode->mParent) { - } - } - DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode); - FindMatchingRule(*newNode); - - newNode->mIndent = mIndent; - if (newNode->mDisplay || mIndentUndisplayedFrames) { - ++mIndent; - } - - if (lastLeaf && (lastLeaf == parentNode)) { - mFrameTreeLeaves.RemoveLastElement(); - } - mFrameTreeLeaves.AppendElement(newNode); - mCount++; - - return newNode; -} - -void DR_State::PrettyUC(nscoord aSize, char* aBuf, int aBufSize) { - if (NS_UNCONSTRAINEDSIZE == aSize) { - strcpy(aBuf, "UC"); - } else { - if ((nscoord)0xdeadbeefU == aSize) { - strcpy(aBuf, "deadbeef"); - } else { - snprintf(aBuf, aBufSize, "%d", aSize); - } - } -} - -void DR_State::PrintMargin(const char* tag, const nsMargin* aMargin) { - if (aMargin) { - char t[16], r[16], b[16], l[16]; - PrettyUC(aMargin->top, t, 16); - PrettyUC(aMargin->right, r, 16); - PrettyUC(aMargin->bottom, b, 16); - PrettyUC(aMargin->left, l, 16); - printf(" %s=%s,%s,%s,%s", tag, t, r, b, l); - } else { - // use %p here for consistency with other null-pointer printouts - printf(" %s=%p", tag, (void*)aMargin); - } -} - -void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode) { - mFrameTreeLeaves.RemoveElement(&aNode); - int32_t numLeaves = mFrameTreeLeaves.Length(); - if ((0 == numLeaves) || - (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) { - mFrameTreeLeaves.AppendElement(aNode.mParent); - } - - if (aNode.mDisplay || mIndentUndisplayedFrames) { - --mIndent; - } - // delete the tree node - delete &aNode; -} - -static void CheckPixelError(nscoord aSize, int32_t aPixelToTwips) { - if (NS_UNCONSTRAINEDSIZE != aSize) { - if ((aSize % aPixelToTwips) > 0) { - printf("VALUE %d is not a whole pixel \n", aSize); - } - } -} - -static void DisplayReflowEnterPrint(nsPresContext* aPresContext, - nsIFrame* aFrame, - const ReflowInput& aReflowInput, - DR_FrameTreeNode& aTreeNode, - bool aChanged) { - if (aTreeNode.mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent); - - char width[16]; - char height[16]; - - DR_state->PrettyUC(aReflowInput.AvailableWidth(), width, 16); - DR_state->PrettyUC(aReflowInput.AvailableHeight(), height, 16); - printf("Reflow a=%s,%s ", width, height); - - DR_state->PrettyUC(aReflowInput.ComputedWidth(), width, 16); - DR_state->PrettyUC(aReflowInput.ComputedHeight(), height, 16); - printf("c=%s,%s ", width, height); - - if (aFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY)) printf("dirty "); - - if (aFrame->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN)) - printf("dirty-children "); - - if (aReflowInput.mFlags.mSpecialBSizeReflow) printf("special-bsize "); - - if (aReflowInput.IsHResize()) printf("h-resize "); - - if (aReflowInput.IsVResize()) printf("v-resize "); - - nsIFrame* inFlow = aFrame->GetPrevInFlow(); - if (inFlow) { - printf("pif=%p ", (void*)inFlow); - } - inFlow = aFrame->GetNextInFlow(); - if (inFlow) { - printf("nif=%p ", (void*)inFlow); - } - if (aChanged) - printf("CHANGED \n"); - else - printf("cnt=%d \n", DR_state->mCount); - if (DR_state->mDisplayPixelErrors) { - int32_t d2a = aPresContext->AppUnitsPerDevPixel(); - CheckPixelError(aReflowInput.AvailableWidth(), d2a); - CheckPixelError(aReflowInput.AvailableHeight(), d2a); - CheckPixelError(aReflowInput.ComputedWidth(), d2a); - CheckPixelError(aReflowInput.ComputedHeight(), d2a); - } - } -} - -void* nsIFrame::DisplayReflowEnter(nsPresContext* aPresContext, - nsIFrame* aFrame, - const ReflowInput& aReflowInput) { - if (!DR_state->mInited) DR_state->Init(); - if (!DR_state->mActive) return nullptr; - - NS_ASSERTION(aFrame, "invalid call"); - - DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowInput); - if (treeNode) { - DisplayReflowEnterPrint(aPresContext, aFrame, aReflowInput, *treeNode, - false); - } - return treeNode; -} - -void* nsIFrame::DisplayLayoutEnter(nsIFrame* aFrame) { - if (!DR_state->mInited) DR_state->Init(); - if (!DR_state->mActive) return nullptr; - - NS_ASSERTION(aFrame, "invalid call"); - - DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); - if (treeNode && treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - printf("XULLayout\n"); - } - return treeNode; -} - -void* nsIFrame::DisplayIntrinsicISizeEnter(nsIFrame* aFrame, - const char* aType) { - if (!DR_state->mInited) DR_state->Init(); - if (!DR_state->mActive) return nullptr; - - NS_ASSERTION(aFrame, "invalid call"); - - DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); - if (treeNode && treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - printf("Get%sISize\n", aType); - } - return treeNode; -} - -void* nsIFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame, const char* aType) { - if (!DR_state->mInited) DR_state->Init(); - if (!DR_state->mActive) return nullptr; - - NS_ASSERTION(aFrame, "invalid call"); - - DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); - if (treeNode && treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - printf("Get%sSize\n", aType); - } - return treeNode; -} - -void nsIFrame::DisplayReflowExit(nsPresContext* aPresContext, nsIFrame* aFrame, - ReflowOutput& aMetrics, - const nsReflowStatus& aStatus, - void* aFrameTreeNode) { - if (!DR_state->mActive) return; - - NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call"); - if (!aFrameTreeNode) return; - - DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; - if (treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - - char width[16]; - char height[16]; - char x[16]; - char y[16]; - DR_state->PrettyUC(aMetrics.Width(), width, 16); - DR_state->PrettyUC(aMetrics.Height(), height, 16); - printf("Reflow d=%s,%s", width, height); - - if (!aStatus.IsEmpty()) { - printf(" status=%s", ToString(aStatus).c_str()); - } - if (aFrame->HasOverflowAreas()) { - DR_state->PrettyUC(aMetrics.InkOverflow().x, x, 16); - DR_state->PrettyUC(aMetrics.InkOverflow().y, y, 16); - DR_state->PrettyUC(aMetrics.InkOverflow().width, width, 16); - DR_state->PrettyUC(aMetrics.InkOverflow().height, height, 16); - printf(" vis-o=(%s,%s) %s x %s", x, y, width, height); - - nsRect storedOverflow = aFrame->InkOverflowRect(); - DR_state->PrettyUC(storedOverflow.x, x, 16); - DR_state->PrettyUC(storedOverflow.y, y, 16); - DR_state->PrettyUC(storedOverflow.width, width, 16); - DR_state->PrettyUC(storedOverflow.height, height, 16); - printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height); - - DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x, 16); - DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y, 16); - DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width, 16); - DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height, 16); - printf(" scr-o=(%s,%s) %s x %s", x, y, width, height); - - storedOverflow = aFrame->ScrollableOverflowRect(); - DR_state->PrettyUC(storedOverflow.x, x, 16); - DR_state->PrettyUC(storedOverflow.y, y, 16); - DR_state->PrettyUC(storedOverflow.width, width, 16); - DR_state->PrettyUC(storedOverflow.height, height, 16); - printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height); - } - printf("\n"); - if (DR_state->mDisplayPixelErrors) { - int32_t d2a = aPresContext->AppUnitsPerDevPixel(); - CheckPixelError(aMetrics.Width(), d2a); - CheckPixelError(aMetrics.Height(), d2a); - } - } - DR_state->DeleteTreeNode(*treeNode); -} - -void nsIFrame::DisplayLayoutExit(nsIFrame* aFrame, void* aFrameTreeNode) { - if (!DR_state->mActive) return; - - NS_ASSERTION(aFrame, "non-null frame required"); - if (!aFrameTreeNode) return; - - DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; - if (treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - nsRect rect = aFrame->GetRect(); - printf("XULLayout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height); - } - DR_state->DeleteTreeNode(*treeNode); -} - -void nsIFrame::DisplayIntrinsicISizeExit(nsIFrame* aFrame, const char* aType, - nscoord aResult, - void* aFrameTreeNode) { - if (!DR_state->mActive) return; - - NS_ASSERTION(aFrame, "non-null frame required"); - if (!aFrameTreeNode) return; - - DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; - if (treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - char iSize[16]; - DR_state->PrettyUC(aResult, iSize, 16); - printf("Get%sISize=%s\n", aType, iSize); - } - DR_state->DeleteTreeNode(*treeNode); -} - -void nsIFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame, const char* aType, - nsSize aResult, void* aFrameTreeNode) { - if (!DR_state->mActive) return; - - NS_ASSERTION(aFrame, "non-null frame required"); - if (!aFrameTreeNode) return; - - DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode; - if (treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - - char width[16]; - char height[16]; - DR_state->PrettyUC(aResult.width, width, 16); - DR_state->PrettyUC(aResult.height, height, 16); - printf("Get%sSize=%s,%s\n", aType, width, height); - } - DR_state->DeleteTreeNode(*treeNode); -} - -/* static */ -void nsIFrame::DisplayReflowStartup() { DR_state = new DR_State(); } - -/* static */ -void nsIFrame::DisplayReflowShutdown() { - delete DR_state; - DR_state = nullptr; -} - -void DR_cookie::Change() const { - DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue; - if (treeNode && treeNode->mDisplay) { - DisplayReflowEnterPrint(mPresContext, mFrame, mReflowInput, *treeNode, - true); - } -} - -/* static */ -void* ReflowInput::DisplayInitConstraintsEnter(nsIFrame* aFrame, - ReflowInput* aState, - nscoord aContainingBlockWidth, - nscoord aContainingBlockHeight, - const nsMargin* aBorder, - const nsMargin* aPadding) { - MOZ_ASSERT(aFrame, "non-null frame required"); - MOZ_ASSERT(aState, "non-null state required"); - - if (!DR_state->mInited) DR_state->Init(); - if (!DR_state->mActive) return nullptr; - - DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState); - if (treeNode && treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - - printf("InitConstraints parent=%p", (void*)aState->mParentReflowInput); - - char width[16]; - char height[16]; - - DR_state->PrettyUC(aContainingBlockWidth, width, 16); - DR_state->PrettyUC(aContainingBlockHeight, height, 16); - printf(" cb=%s,%s", width, height); - - DR_state->PrettyUC(aState->AvailableWidth(), width, 16); - DR_state->PrettyUC(aState->AvailableHeight(), height, 16); - printf(" as=%s,%s", width, height); - - DR_state->PrintMargin("b", aBorder); - DR_state->PrintMargin("p", aPadding); - putchar('\n'); - } - return treeNode; -} - -/* static */ -void ReflowInput::DisplayInitConstraintsExit(nsIFrame* aFrame, - ReflowInput* aState, - void* aValue) { - MOZ_ASSERT(aFrame, "non-null frame required"); - MOZ_ASSERT(aState, "non-null state required"); - - if (!DR_state->mActive) return; - if (!aValue) return; - - DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; - if (treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16]; - DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw, 16); - DR_state->PrettyUC(aState->ComputedWidth(), cw, 16); - DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw, 16); - DR_state->PrettyUC(aState->ComputedMinHeight(), cmih, 16); - DR_state->PrettyUC(aState->ComputedHeight(), ch, 16); - DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh, 16); - printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)", cmiw, cw, - cmxw, cmih, ch, cmxh); - const nsMargin m = aState->ComputedPhysicalOffsets(); - DR_state->PrintMargin("co", &m); - putchar('\n'); - } - DR_state->DeleteTreeNode(*treeNode); -} - -/* static */ -void* SizeComputationInput::DisplayInitOffsetsEnter( - nsIFrame* aFrame, SizeComputationInput* aState, nscoord aPercentBasis, - WritingMode aCBWritingMode, const nsMargin* aBorder, - const nsMargin* aPadding) { - MOZ_ASSERT(aFrame, "non-null frame required"); - MOZ_ASSERT(aState, "non-null state required"); - - if (!DR_state->mInited) DR_state->Init(); - if (!DR_state->mActive) return nullptr; - - // aState is not necessarily a ReflowInput - DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr); - if (treeNode && treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - - char pctBasisStr[16]; - DR_state->PrettyUC(aPercentBasis, pctBasisStr, 16); - printf("InitOffsets pct_basis=%s", pctBasisStr); - - DR_state->PrintMargin("b", aBorder); - DR_state->PrintMargin("p", aPadding); - putchar('\n'); - } - return treeNode; -} - -/* static */ -void SizeComputationInput::DisplayInitOffsetsExit(nsIFrame* aFrame, - SizeComputationInput* aState, - void* aValue) { - MOZ_ASSERT(aFrame, "non-null frame required"); - MOZ_ASSERT(aState, "non-null state required"); - - if (!DR_state->mActive) return; - if (!aValue) return; - - DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue; - if (treeNode->mDisplay) { - DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent); - printf("InitOffsets="); - const auto m = aState->ComputedPhysicalMargin(); - DR_state->PrintMargin("m", &m); - const auto p = aState->ComputedPhysicalPadding(); - DR_state->PrintMargin("p", &p); - const auto bp = aState->ComputedPhysicalBorderPadding(); - DR_state->PrintMargin("b+p", &bp); - putchar('\n'); - } - DR_state->DeleteTreeNode(*treeNode); -} - -// End Display Reflow - // Validation of SideIsVertical. # define CASE(side, result) \ static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong") diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 16f3d17d64..801e80c7e0 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -127,6 +127,7 @@ struct CharacterDataChangeInfo; namespace mozilla { enum class CaretAssociationHint; +enum class IsFocusableFlags : uint8_t; enum class PeekOffsetOption : uint16_t; enum class PseudoStyleType : uint8_t; enum class TableSelectionMode : uint32_t; @@ -937,6 +938,8 @@ class nsIFrame : public nsQueryFrame { already_AddRefed<ComputedStyle> ComputeHighlightSelectionStyle( nsAtom* aHighlightName); + already_AddRefed<ComputedStyle> ComputeTargetTextStyle() const; + /** * Accessor functions for geometric parent. */ @@ -1384,7 +1387,25 @@ class nsIFrame : public nsQueryFrame { bool HasUnreflowedContainerQueryAncestor() const; + // Return True if this frame has a forced break value before it. + // + // Note: this method only checks 'break-before' property on *this* frame, and + // it doesn't handle forced break value propagation from its first child. + // Callers should handle the propagation in reflow. + bool ShouldBreakBefore(const ReflowInput::BreakType aBreakType) const; + + // Return True if this frame has a forced break value after it. + // + // Note: this method only checks 'break-after' property on *this* frame, and + // it doesn't handle forced break value propagation from its last child. + // Callers should handle the propagation in reflow. + bool ShouldBreakAfter(const ReflowInput::BreakType aBreakType) const; + private: + bool ShouldBreakBetween(const nsStyleDisplay* aDisplay, + const mozilla::StyleBreakBetween aBreakBetween, + const ReflowInput::BreakType aBreakType) const; + // The value that the CSS page-name "auto" keyword resolves to for children // of this frame. // @@ -1586,6 +1607,11 @@ class nsIFrame : public nsQueryFrame { bool GetShapeBoxBorderRadii(nscoord aRadii[8]) const; /** + * Returns one em unit, adjusted for font inflation if needed, in app units. + */ + nscoord OneEmInAppUnits() const; + + /** * `GetNaturalBaselineBOffset`, but determines the baseline sharing group * through `GetDefaultBaselineSharingGroup` (If not specified), assuming line * layout context, and never fails, returning a synthesized baseline through @@ -3932,6 +3958,9 @@ class nsIFrame : public nsQueryFrame { public: // given a frame five me the first/last leaf available // XXX Robert O'Callahan wants to move these elsewhere + // FIXME: Only GetLastLeaf() never returns a leaf frame in native anonymous + // subtrees under aFrame. However, GetFirstLeaf() may return a leaf frame + // in a native anonymous subtree. static void GetLastLeaf(nsIFrame** aFrame); static void GetFirstLeaf(nsIFrame** aFrame); @@ -4359,13 +4388,10 @@ class nsIFrame : public nsQueryFrame { * Also, depending on the pref accessibility.tabfocus some widgets may be * focusable but removed from the tab order. This is the default on * Mac OS X, where fewer items are focusable. - * @param [in, optional] aWithMouse, is this focus query for mouse clicking - * @param [in, optional] aCheckVisibility, whether to treat an invisible - * frame as not focusable * @return whether the frame is focusable via mouse, kbd or script. */ - [[nodiscard]] Focusable IsFocusable(bool aWithMouse = false, - bool aCheckVisibility = true); + [[nodiscard]] Focusable IsFocusable( + mozilla::IsFocusableFlags = mozilla::IsFocusableFlags(0)); protected: // Helper for IsFocusable. @@ -5482,25 +5508,6 @@ class nsIFrame : public nsQueryFrame { // NS_FRAME_IS_DIRTY bit set static void VerifyDirtyBitSet(const nsFrameList& aFrameList); - // Display Reflow Debugging - static void* DisplayReflowEnter(nsPresContext* aPresContext, nsIFrame* aFrame, - const ReflowInput& aReflowInput); - static void* DisplayLayoutEnter(nsIFrame* aFrame); - static void* DisplayIntrinsicISizeEnter(nsIFrame* aFrame, const char* aType); - static void* DisplayIntrinsicSizeEnter(nsIFrame* aFrame, const char* aType); - static void DisplayReflowExit(nsPresContext* aPresContext, nsIFrame* aFrame, - ReflowOutput& aMetrics, - const nsReflowStatus& aStatus, - void* aFrameTreeNode); - static void DisplayLayoutExit(nsIFrame* aFrame, void* aFrameTreeNode); - static void DisplayIntrinsicISizeExit(nsIFrame* aFrame, const char* aType, - nscoord aResult, void* aFrameTreeNode); - static void DisplayIntrinsicSizeExit(nsIFrame* aFrame, const char* aType, - nsSize aResult, void* aFrameTreeNode); - - static void DisplayReflowStartup(); - static void DisplayReflowShutdown(); - static mozilla::LazyLogModule sFrameLogModule; #endif }; diff --git a/layout/generic/nsIFrameInlines.h b/layout/generic/nsIFrameInlines.h index e187f0dfb0..f53ba607c0 100644 --- a/layout/generic/nsIFrameInlines.h +++ b/layout/generic/nsIFrameInlines.h @@ -36,7 +36,7 @@ bool nsIFrame::IsFlexOrGridItem() const { bool nsIFrame::IsMasonry(mozilla::LogicalAxis aAxis) const { MOZ_DIAGNOSTIC_ASSERT(IsGridContainerFrame()); - return HasAnyStateBits(aAxis == mozilla::eLogicalAxisBlock + return HasAnyStateBits(aAxis == mozilla::LogicalAxis::Block ? NS_STATE_GRID_IS_ROW_MASONRY : NS_STATE_GRID_IS_COL_MASONRY); } diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index c1e69df6c9..c330f1ce6b 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -645,8 +645,9 @@ const StyleImage* nsImageFrame::GetImageFromStyle() const { nonAnonymousParent->GetContent()); styleContent = nonAnonymousParent->StyleContent(); } - MOZ_RELEASE_ASSERT(contentIndex < styleContent->ContentCount()); - auto& contentItem = styleContent->ContentAt(contentIndex); + auto items = styleContent->NonAltContentItems(); + MOZ_RELEASE_ASSERT(contentIndex < items.Length()); + const auto& contentItem = items[contentIndex]; MOZ_RELEASE_ASSERT(contentItem.IsImage()); return &contentItem.AsImage(); } @@ -1055,11 +1056,8 @@ bool nsImageFrame::ShouldCreateImageFrameForContentProperty( if (aElement.IsRootOfNativeAnonymousSubtree()) { return false; } - const auto& content = aStyle.StyleContent()->mContent; - if (!content.IsItems()) { - return false; - } - Span<const StyleContentItem> items = content.AsItems().AsSpan(); + Span<const StyleContentItem> items = + aStyle.StyleContent()->NonAltContentItems(); return items.Length() == 1 && items[0].IsImage(); } @@ -1476,8 +1474,6 @@ nscoord nsImageFrame::GetContinuationOffset() const { nscoord nsImageFrame::GetMinISize(gfxContext* aRenderingContext) { // XXX The caller doesn't account for constraints of the block-size, // min-block-size, and max-block-size properties. - DebugOnly<nscoord> result; - DISPLAY_MIN_INLINE_SIZE(this, result); EnsureIntrinsicSizeAndRatio(); const auto& iSize = GetWritingMode().IsVertical() ? mIntrinsicSize.height : mIntrinsicSize.width; @@ -1487,8 +1483,6 @@ nscoord nsImageFrame::GetMinISize(gfxContext* aRenderingContext) { nscoord nsImageFrame::GetPrefISize(gfxContext* aRenderingContext) { // XXX The caller doesn't account for constraints of the block-size, // min-block-size, and max-block-size properties. - DebugOnly<nscoord> result; - DISPLAY_PREF_INLINE_SIZE(this, result); EnsureIntrinsicSizeAndRatio(); const auto& iSize = GetWritingMode().IsVertical() ? mIntrinsicSize.height : mIntrinsicSize.width; @@ -1526,7 +1520,6 @@ void nsImageFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsImageFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_FRAME_TRACE( NS_FRAME_TRACE_CALLS, @@ -2862,10 +2855,10 @@ LogicalSides nsImageFrame::GetLogicalSkipSides() const { return skip; } if (GetPrevInFlow()) { - skip |= eLogicalSideBitsBStart; + skip += LogicalSide::BStart; } if (GetNextInFlow()) { - skip |= eLogicalSideBitsBEnd; + skip += LogicalSide::BEnd; } return skip; } diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp index 7a99ecbe5d..44fce51931 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -270,7 +270,6 @@ void nsInlineFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsInlineFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); if (!aReflowInput.mLineLayout) { @@ -798,7 +797,7 @@ LogicalSides nsInlineFrame::GetLogicalSkipSides() const { (prev && (prev->mRect.height || prev->mRect.width))) { // Prev continuation is not empty therefore we don't render our start // border edge. - skip |= eLogicalSideBitsIStart; + skip += LogicalSide::IStart; } else { // If the prev continuation is empty, then go ahead and let our start // edge border render. @@ -810,7 +809,7 @@ LogicalSides nsInlineFrame::GetLogicalSkipSides() const { (next && (next->mRect.height || next->mRect.width))) { // Next continuation is not empty therefore we don't render our end // border edge. - skip |= eLogicalSideBitsIEnd; + skip += LogicalSide::IEnd; } else { // If the next continuation is empty, then go ahead and let our end // edge border render. @@ -823,15 +822,15 @@ LogicalSides nsInlineFrame::GetLogicalSkipSides() const { // a split should skip the "start" side. But figuring out which part of // the split we are involves getting our first continuation, which might be // expensive. So don't bother if we already have the relevant bits set. - if (skip != LogicalSides(mWritingMode, eLogicalSideBitsIBoth)) { + if (skip != LogicalSides(mWritingMode, LogicalSides::IBoth)) { // We're missing one of the skip bits, so check whether we need to set it. // Only get the first continuation once, as an optimization. nsIFrame* firstContinuation = FirstContinuation(); if (firstContinuation->FrameIsNonLastInIBSplit()) { - skip |= eLogicalSideBitsIEnd; + skip += LogicalSide::IEnd; } if (firstContinuation->FrameIsNonFirstInIBSplit()) { - skip |= eLogicalSideBitsIStart; + skip += LogicalSide::IStart; } } } diff --git a/layout/generic/nsLeafFrame.cpp b/layout/generic/nsLeafFrame.cpp index 2688da3d4b..6125f6b800 100644 --- a/layout/generic/nsLeafFrame.cpp +++ b/layout/generic/nsLeafFrame.cpp @@ -24,18 +24,12 @@ void nsLeafFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, /* virtual */ nscoord nsLeafFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - result = GetIntrinsicISize(); - return result; + return GetIntrinsicISize(); } /* virtual */ nscoord nsLeafFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - result = GetIntrinsicISize(); - return result; + return GetIntrinsicISize(); } /* virtual */ diff --git a/layout/generic/nsLineBox.cpp b/layout/generic/nsLineBox.cpp index 18051aa69a..53a6914030 100644 --- a/layout/generic/nsLineBox.cpp +++ b/layout/generic/nsLineBox.cpp @@ -217,7 +217,7 @@ void nsLineBox::List(FILE* out, const char* aPrefix, IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean", IsImpactedByFloat() ? "impacted" : "not-impacted", IsLineWrapped() ? "wrapped" : "not-wrapped", - HasForcedLineBreak() ? "forced-break" : "no-break", + HasForcedLineBreakAfter() ? "forced-break-after" : "no-break", StyleClearToString(FloatClearTypeBefore()), StyleClearToString(FloatClearTypeAfter())); diff --git a/layout/generic/nsLineBox.h b/layout/generic/nsLineBox.h index d46cf9604a..e35f36a481 100644 --- a/layout/generic/nsLineBox.h +++ b/layout/generic/nsLineBox.h @@ -238,42 +238,40 @@ class nsLineBox final : public nsLineLink { } } - // mHasForcedLineBreak bit & mFloatClearType value - // Break information is applied *before* the line if the line is a block, - // or *after* the line if the line is an inline. - bool HasForcedLineBreak() const { return mFlags.mHasForcedLineBreak; } + // mHasForcedLineBreakAfter bit & mFloatClearType value void ClearForcedLineBreak() { - mFlags.mHasForcedLineBreak = false; + mFlags.mHasForcedLineBreakAfter = false; mFlags.mFloatClearType = mozilla::StyleClear::None; } - bool HasForcedLineBreakBefore() const { - return IsBlock() && HasForcedLineBreak(); + bool HasFloatClearTypeBefore() const { + return FloatClearTypeBefore() != mozilla::StyleClear::None; } - void SetForcedLineBreakBefore(mozilla::StyleClear aClearType) { - MOZ_ASSERT(IsBlock(), "Only blocks have break-before"); + void SetFloatClearTypeBefore(mozilla::StyleClear aClearType) { + MOZ_ASSERT(IsBlock(), "Only block lines have break-before status!"); MOZ_ASSERT(aClearType != mozilla::StyleClear::None, "Only StyleClear:Left/Right/Both are allowed before a line"); - mFlags.mHasForcedLineBreak = true; mFlags.mFloatClearType = aClearType; } mozilla::StyleClear FloatClearTypeBefore() const { - return IsBlock() ? FloatClearType() : mozilla::StyleClear::None; + return IsBlock() ? mFlags.mFloatClearType : mozilla::StyleClear::None; } bool HasForcedLineBreakAfter() const { - return IsInline() && HasForcedLineBreak(); + MOZ_ASSERT(IsInline() || !mFlags.mHasForcedLineBreakAfter, + "A block line shouldn't set mHasForcedLineBreakAfter bit!"); + return IsInline() && mFlags.mHasForcedLineBreakAfter; } void SetForcedLineBreakAfter(mozilla::StyleClear aClearType) { - MOZ_ASSERT(IsInline(), "Only inlines have break-after"); - mFlags.mHasForcedLineBreak = true; + MOZ_ASSERT(IsInline(), "Only inline lines have break-after status!"); + mFlags.mHasForcedLineBreakAfter = true; mFlags.mFloatClearType = aClearType; } bool HasFloatClearTypeAfter() const { - return IsInline() && FloatClearType() != mozilla::StyleClear::None; + return FloatClearTypeAfter() != mozilla::StyleClear::None; } mozilla::StyleClear FloatClearTypeAfter() const { - return IsInline() ? FloatClearType() : mozilla::StyleClear::None; + return IsInline() ? mFlags.mFloatClearType : mozilla::StyleClear::None; } // mCarriedOutBEndMargin value @@ -316,7 +314,7 @@ class nsLineBox final : public nsLineLink { nsRect InkOverflowRect() const { return GetOverflowArea(mozilla::OverflowType::Ink); } - nsRect ScrollableOverflowRect() { + nsRect ScrollableOverflowRect() const { return GetOverflowArea(mozilla::OverflowType::Scrollable); } @@ -520,11 +518,15 @@ class nsLineBox final : public nsLineLink { // Has this line moved to a different fragment of the block since // the last time it was reflowed? bool mMovedFragments : 1; - // mHasForcedLineBreak indicates that this line has either a break-before or - // a break-after. - bool mHasForcedLineBreak : 1; - // mFloatClearType indicates that there's a float clearance before or after - // this line. + // mHasForcedLineBreakAfter indicates that this *inline* line has a + // break-after status due to a float clearance or ending with <br>. A block + // line shouldn't set this bit. + // + // Note: This bit is unrelated to CSS break-after property because it is all + // about line break-after for inline-level boxes. + bool mHasForcedLineBreakAfter : 1; + // mFloatClearType indicates that there's a float clearance before a block + // line, or after an inline line. mozilla::StyleClear mFloatClearType; }; @@ -570,8 +572,6 @@ class nsLineBox final : public nsLineLink { FlagBits mFlags; }; - mozilla::StyleClear FloatClearType() const { return mFlags.mFloatClearType; }; - union { ExtraData* mData; ExtraBlockData* mBlockData; diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index ab6924faa4..a452568f57 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -396,8 +396,10 @@ nsLineLayout::PerSpanData* nsLineLayout::NewPerSpanData() { psd->mFrame = nullptr; psd->mFirstFrame = nullptr; psd->mLastFrame = nullptr; + psd->mReflowInput = nullptr; psd->mContainsFloat = false; psd->mHasNonemptyContent = false; + psd->mBaseline = nullptr; #ifdef DEBUG outerLineLayout->mSpansAllocated++; @@ -1713,10 +1715,10 @@ void nsLineLayout::AdjustLeadings(nsIFrame* spanFrame, PerSpanData* psd, if (aStyleText->HasEffectiveTextEmphasis()) { nscoord bsize = GetBSizeOfEmphasisMarks(spanFrame, aInflation); LogicalSide side = aStyleText->TextEmphasisSide(mRootSpan->mWritingMode); - if (side == eLogicalSideBStart) { + if (side == LogicalSide::BStart) { requiredStartLeading += bsize; } else { - MOZ_ASSERT(side == eLogicalSideBEnd, + MOZ_ASSERT(side == LogicalSide::BEnd, "emphasis marks must be in block axis"); requiredEndLeading += bsize; } @@ -2341,7 +2343,7 @@ void nsLineLayout::VerticalAlignFrames(PerSpanData* psd) { delta = emphasisHeight; } LogicalSide side = mStyleText->TextEmphasisSide(lineWM); - if (side == eLogicalSideBStart) { + if (side == LogicalSide::BStart) { blockStart -= delta; } else { blockEnd += delta; diff --git a/layout/generic/nsPageContentFrame.cpp b/layout/generic/nsPageContentFrame.cpp index 11e634894d..1c82ca1523 100644 --- a/layout/generic/nsPageContentFrame.cpp +++ b/layout/generic/nsPageContentFrame.cpp @@ -35,7 +35,6 @@ void nsPageContentFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsPageContentFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aReflowOutput, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); MOZ_ASSERT(mPD, "Need a pointer to nsSharedPageData before reflow starts"); diff --git a/layout/generic/nsPageFrame.cpp b/layout/generic/nsPageFrame.cpp index fb9cfa2e81..de573962af 100644 --- a/layout/generic/nsPageFrame.cpp +++ b/layout/generic/nsPageFrame.cpp @@ -9,6 +9,7 @@ #include "mozilla/AppUnits.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs_layout.h" +#include "mozilla/StaticPrefs_print.h" #include "mozilla/gfx/2D.h" #include "mozilla/intl/Segmenter.h" #include "gfxContext.h" @@ -213,7 +214,6 @@ void nsPageFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsPageFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aReflowOutput, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); MOZ_ASSERT(mPD, "Need a pointer to nsSharedPageData before reflow starts"); @@ -540,6 +540,69 @@ static std::tuple<uint32_t, uint32_t> GetRowAndColFromIdx(uint32_t aIdxOnSheet, return {aIdxOnSheet / aNumCols, aIdxOnSheet % aNumCols}; } +// The minimum ratio for which we will center the page on the sheet when using +// auto-detect logic. +// Note that this ratio is of the content's size to the sheet size scaled to be +// in content space, and so the actual ratio will always be from 0.0 to 1.0, +// with this marking the smallest ratio we consider a near-miss. +// The ratio of A4 on Letter is 0.915034. A threshold of 0.9 will ensure that +// A4 on Letter works, as well as other near-misses. +// +// The ratio is computed as so: +// scale = min(1, sheetHeight / pageHeight, sheetWidth / pageWidth) +// +// Where pageSize is pageWidth or pageHeight, and sheetSize is sheetWidth or +// sheetHeight, respectively: +// scaledPageSize = pageSize * scale +// ratio = scaledPageSize / sheetSize +// +// A4 (210mm x 297mm) on US Letter (215.9mm x 279.4mm) is derived as so: +// scale = min(1, 215.9 / 210, 279.4 / 297) = 0.9407407407.. +// +// Using the widths: +// scaledPageSize = (210 * 0.940741) = 197.556 +// ratio = 197.556 / 215.9 = 0.915034 +// +// See nsPageFrame::ComputeSinglePPSPageSizeScale for scale calculation, and +// OffsetToCenterPage for ratio calculation. +constexpr float kCenterPageRatioThreshold = 0.9f; + +// Numeric values for the pref "print.center_page_on_sheet" +enum { + kPrintCenterPageOnSheetNever = 0, + kPrintCenterPageOnSheetAlways = 1, + kPrintCenterPageOnSheetAuto = 2 +}; + +// Returns an offset to center the page on the sheet, with a given scale. +// When no centering can/should happen, this will avoid extra calculations and +// return 0.0f. +// This takes into account the value of the pref "print.center_page_on_sheet". +static float OffsetToCenterPage(nscoord aContentSize, nscoord aSheetSize, + float aScale, float aAppUnitsPerPixel) { + MOZ_ASSERT(aScale <= 1.0f && aScale > 0.0f, + "Scale must be in the range (0,1]"); + const unsigned centerPagePref = StaticPrefs::print_center_page_on_sheet(); + if (centerPagePref == kPrintCenterPageOnSheetNever) { + return 0.0f; + } + + // Determine the ratio of scaled page to the sheet size. + const float sheetSize = + NSAppUnitsToFloatPixels(aSheetSize, aAppUnitsPerPixel); + const float scaledContentSize = + NSAppUnitsToFloatPixels(aContentSize, aAppUnitsPerPixel) * aScale; + const float ratio = scaledContentSize / sheetSize; + + // If the ratio is within the threshold, or the pref indicates we should + // always center the page, return half the difference to form the offset. + if (centerPagePref == kPrintCenterPageOnSheetAlways || + ratio >= kCenterPageRatioThreshold) { + return (sheetSize - scaledContentSize) * 0.5f; + } + return 0.0f; +} + // Helper for BuildDisplayList: static gfx::Matrix4x4 ComputePagesPerSheetAndPageSizeTransform( const nsIFrame* aFrame, float aAppUnitsPerPixel) { @@ -561,8 +624,8 @@ static gfx::Matrix4x4 ComputePagesPerSheetAndPageSizeTransform( gfx::Matrix4x4 transform; if (ppsInfo->mNumPages == 1) { + const nsSize sheetSize = sheetFrame->GetSizeForChildren(); if (rotation != 0.0) { - const nsSize sheetSize = sheetFrame->GetSizeForChildren(); const bool sheetIsPortrait = sheetSize.width < sheetSize.height; const bool rotatingClockwise = rotation > 0.0; @@ -584,7 +647,21 @@ static gfx::Matrix4x4 ComputePagesPerSheetAndPageSizeTransform( NSAppUnitsToFloatPixels(-y, aAppUnitsPerPixel), 0); } - float scale = pageFrame->ComputeSinglePPSPageSizeScale(contentPageSize); + // If the difference in horizontal size, after scaling, is relatively small + // then center the page on the sheet. + const float scale = + pageFrame->ComputeSinglePPSPageSizeScale(contentPageSize); + const float centeringOffset = OffsetToCenterPage( + contentPageSize.width, sheetSize.width, scale, aAppUnitsPerPixel); + + // Only bother with the translation if it is at least one pixel. + // It's possible for a mismatch in the paper size reported by the print + // server and the paper size from Gecko to lead to small offsets, or + // even (in combination with floating point error) a very small negative + // offset. Do not apply an offset in those cases. + if (centeringOffset >= 1.0f) { + transform.PreTranslate(centeringOffset, 0, 0); + } transform.PreScale(scale, scale, 1); return transform; } @@ -937,7 +1014,6 @@ void nsPageBreakFrame::Reflow(nsPresContext* aPresContext, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsPageBreakFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aReflowOutput, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); // Override reflow, since we don't want to deal with what our diff --git a/layout/generic/nsPageSequenceFrame.cpp b/layout/generic/nsPageSequenceFrame.cpp index a3348433cb..cd3c2cd6a5 100644 --- a/layout/generic/nsPageSequenceFrame.cpp +++ b/layout/generic/nsPageSequenceFrame.cpp @@ -266,7 +266,6 @@ void nsPageSequenceFrame::Reflow(nsPresContext* aPresContext, MOZ_ASSERT(aPresContext->IsRootPaginatedDocument(), "A Page Sequence is only for real pages"); DO_GLOBAL_REFLOW_COUNT("nsPageSequenceFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aReflowOutput, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_FRAME_TRACE_REFLOW_IN("nsPageSequenceFrame::Reflow"); diff --git a/layout/generic/nsPlaceholderFrame.cpp b/layout/generic/nsPlaceholderFrame.cpp index f6b4d53193..aad2c816e5 100644 --- a/layout/generic/nsPlaceholderFrame.cpp +++ b/layout/generic/nsPlaceholderFrame.cpp @@ -124,7 +124,6 @@ void nsPlaceholderFrame::Reflow(nsPresContext* aPresContext, MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); aDesiredSize.ClearSize(); } diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp index 6e29152bc9..50b08b6f5b 100644 --- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -275,7 +275,6 @@ void nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); if (!aReflowInput.mLineLayout) { diff --git a/layout/generic/nsRubyFrame.cpp b/layout/generic/nsRubyFrame.cpp index d7da5bc321..fc74ce8184 100644 --- a/layout/generic/nsRubyFrame.cpp +++ b/layout/generic/nsRubyFrame.cpp @@ -94,7 +94,6 @@ void nsRubyFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsRubyFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); if (!aReflowInput.mLineLayout) { @@ -301,25 +300,25 @@ void nsRubyFrame::ReflowSegment(nsPresContext* aPresContext, Maybe<LineRelativeDir> lineSide; switch (textContainer->StyleText()->mRubyPosition) { case StyleRubyPosition::Over: - lineSide.emplace(eLineRelativeDirOver); + lineSide.emplace(LineRelativeDir::Over); break; case StyleRubyPosition::Under: - lineSide.emplace(eLineRelativeDirUnder); + lineSide.emplace(LineRelativeDir::Under); break; case StyleRubyPosition::AlternateOver: if (lastLineSide.isSome() && - lastLineSide.value() == eLineRelativeDirOver) { - lineSide.emplace(eLineRelativeDirUnder); + lastLineSide.value() == LineRelativeDir::Over) { + lineSide.emplace(LineRelativeDir::Under); } else { - lineSide.emplace(eLineRelativeDirOver); + lineSide.emplace(LineRelativeDir::Over); } break; case StyleRubyPosition::AlternateUnder: if (lastLineSide.isSome() && - lastLineSide.value() == eLineRelativeDirUnder) { - lineSide.emplace(eLineRelativeDirOver); + lastLineSide.value() == LineRelativeDir::Under) { + lineSide.emplace(LineRelativeDir::Over); } else { - lineSide.emplace(eLineRelativeDirUnder); + lineSide.emplace(LineRelativeDir::Under); } break; default: @@ -334,7 +333,7 @@ void nsRubyFrame::ReflowSegment(nsPresContext* aPresContext, lineWM.LogicalSideForLineRelativeDir(lineSide.value()); if (StaticPrefs::layout_css_ruby_intercharacter_enabled() && rtcWM.IsVerticalRL() && - lineWM.GetInlineDir() == WritingMode::eInlineLTR) { + lineWM.GetInlineDir() == WritingMode::InlineDir::LTR) { // Inter-character ruby annotations are only supported for vertical-rl // in ltr horizontal writing. Fall back to non-inter-character behavior // otherwise. @@ -345,11 +344,11 @@ void nsRubyFrame::ReflowSegment(nsPresContext* aPresContext, : 0); position = offsetRect.Origin(lineWM) + offset; aReflowInput.mLineLayout->AdvanceICoord(size.ISize(lineWM)); - } else if (logicalSide == eLogicalSideBStart) { + } else if (logicalSide == LogicalSide::BStart) { offsetRect.BStart(lineWM) -= size.BSize(lineWM); offsetRect.BSize(lineWM) += size.BSize(lineWM); position = offsetRect.Origin(lineWM); - } else if (logicalSide == eLogicalSideBEnd) { + } else if (logicalSide == LogicalSide::BEnd) { position = offsetRect.Origin(lineWM) + LogicalPoint(lineWM, 0, offsetRect.BSize(lineWM)); offsetRect.BSize(lineWM) += size.BSize(lineWM); diff --git a/layout/generic/nsRubyTextContainerFrame.cpp b/layout/generic/nsRubyTextContainerFrame.cpp index 855d7c1825..8494b36a28 100644 --- a/layout/generic/nsRubyTextContainerFrame.cpp +++ b/layout/generic/nsRubyTextContainerFrame.cpp @@ -105,7 +105,6 @@ void nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsRubyTextContainerFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); // Although a ruby text container may have continuations, returning diff --git a/layout/generic/nsSplittableFrame.cpp b/layout/generic/nsSplittableFrame.cpp index 4054e45f8c..abc3b5bf6e 100644 --- a/layout/generic/nsSplittableFrame.cpp +++ b/layout/generic/nsSplittableFrame.cpp @@ -316,7 +316,7 @@ LogicalSides nsSplittableFrame::GetBlockLevelLogicalSkipSides( bool aAfterReflow) const { LogicalSides skip(mWritingMode); if (MOZ_UNLIKELY(IsTrueOverflowContainer())) { - skip |= eLogicalSideBitsBBoth; + skip += LogicalSides(mWritingMode, LogicalSides::BBoth); return skip; } @@ -326,19 +326,19 @@ LogicalSides nsSplittableFrame::GetBlockLevelLogicalSkipSides( } if (GetPrevContinuation()) { - skip |= eLogicalSideBitsBStart; + skip += LogicalSide::BStart; } // Always skip block-end side if we have a *later* sibling across column-span // split. if (HasColumnSpanSiblings()) { - skip |= eLogicalSideBitsBEnd; + skip += LogicalSide::BEnd; } if (aAfterReflow) { nsIFrame* nif = GetNextContinuation(); if (nif && !nif->IsTrueOverflowContainer()) { - skip |= eLogicalSideBitsBEnd; + skip += LogicalSide::BEnd; } } diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index bfbd7763a9..731d31a16f 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -322,30 +322,29 @@ void nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, return; } - nsFrameLoader* frameLoader = FrameLoader(); - bool isRemoteFrame = frameLoader && frameLoader->IsRemoteFrame(); - - // If we are pointer-events:none then we don't need to HitTest background const bool pointerEventsNone = Style()->PointerEvents() == StylePointerEvents::None; - if (!aBuilder->IsForEventDelivery() || !pointerEventsNone) { - nsDisplayListCollection decorations(aBuilder); - DisplayBorderBackgroundOutline(aBuilder, decorations); - if (isRemoteFrame) { - // Wrap background colors of <iframe>s with remote subdocuments in their - // own layer so we generate a ColorLayer. This is helpful for optimizing - // compositing; we can skip compositing the ColorLayer when the - // remote content is opaque. - WrapBackgroundColorInOwnLayer(aBuilder, this, - decorations.BorderBackground()); - } - decorations.MoveTo(aLists); - } - if (aBuilder->IsForEventDelivery() && pointerEventsNone) { + // If we are pointer-events:none then we don't need to HitTest background or + // anything else. return; } + nsFrameLoader* frameLoader = FrameLoader(); + const bool isRemoteFrame = frameLoader && frameLoader->IsRemoteFrame(); + + nsDisplayListCollection decorations(aBuilder); + DisplayBorderBackgroundOutline(aBuilder, decorations); + if (isRemoteFrame) { + // Wrap background colors of <iframe>s with remote subdocuments in their + // own layer so we generate a ColorLayer. This is helpful for optimizing + // compositing; we can skip compositing the ColorLayer when the + // remote content is opaque. + WrapBackgroundColorInOwnLayer(aBuilder, this, + decorations.BorderBackground()); + } + decorations.MoveTo(aLists); + if (HidesContent()) { return; } @@ -554,39 +553,18 @@ nsresult nsSubDocumentFrame::GetFrameName(nsAString& aResult) const { /* virtual */ nscoord nsSubDocumentFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - - nsCOMPtr<nsIObjectLoadingContent> iolc = do_QueryInterface(mContent); - auto olc = static_cast<nsObjectLoadingContent*>(iolc.get()); - - if (olc && olc->GetSubdocumentIntrinsicSize()) { - // The subdocument is an SVG document, so technically we should call - // SVGOuterSVGFrame::GetMinISize() on its root frame. That method always - // returns 0, though, so we can just do that & don't need to bother with - // the cross-doc communication. - result = 0; - } else { - result = GetIntrinsicISize(); - } - - return result; + return GetIntrinsicISize(); } /* virtual */ nscoord nsSubDocumentFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - // If the subdocument is an SVG document, then in theory we want to return // the same thing that SVGOuterSVGFrame::GetPrefISize does. That method // has some special handling of percentage values to avoid unhelpful zero // sizing in the presence of orthogonal writing modes. We don't bother // with that for SVG documents in <embed> and <object>, since that special // handling doesn't look up across document boundaries anyway. - result = GetIntrinsicISize(); - - return result; + return GetIntrinsicISize(); } /* virtual */ @@ -676,7 +654,6 @@ void nsSubDocumentFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_FRAME_TRACE( NS_FRAME_TRACE_CALLS, @@ -713,7 +690,7 @@ void nsSubDocumentFrame::Reflow(nsPresContext* aPresContext, nsViewManager* vm = mInnerView->GetViewManager(); vm->MoveViewTo(mInnerView, destRect.x, destRect.y); - vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size()), true); + vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), destRect.Size())); } aDesiredSize.SetOverflowAreasToDesiredBounds(); diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 9afefc5c28..0ad527e842 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -1929,8 +1929,8 @@ bool BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, // Map inline-end and inline-start to physical sides for checking presence // of non-zero margin/border/padding. - Side side1 = wm.PhysicalSide(eLogicalSideIEnd); - Side side2 = wm.PhysicalSide(eLogicalSideIStart); + Side side1 = wm.PhysicalSide(LogicalSide::IEnd); + Side side2 = wm.PhysicalSide(LogicalSide::IStart); // If the frames have an embedding level that is opposite to the writing // mode, we need to swap which sides we're checking. if (aFrame1->GetEmbeddingLevel().IsRTL() == wm.IsBidiLTR()) { @@ -3611,6 +3611,20 @@ void nsTextFrame::PropertyProvider::GetSpacing(Range aRange, !(mTextRun->GetFlags2() & nsTextFrameUtils::Flags::HasTab)); } +static bool CanAddSpacingBefore(const gfxTextRun* aTextRun, uint32_t aOffset, + bool aNewlineIsSignificant) { + const auto* g = aTextRun->GetCharacterGlyphs(); + MOZ_ASSERT(aOffset < aTextRun->GetLength()); + if (aNewlineIsSignificant && g[aOffset].CharIsNewline()) { + return false; + } + if (!aOffset) { + return true; + } + return g[aOffset].IsClusterStart() && g[aOffset].IsLigatureGroupStart() && + !g[aOffset - 1].CharIsFormattingControl() && !g[aOffset].CharIsTab(); +} + static bool CanAddSpacingAfter(const gfxTextRun* aTextRun, uint32_t aOffset, bool aNewlineIsSignificant) { const auto* g = aTextRun->GetCharacterGlyphs(); @@ -3683,14 +3697,45 @@ void nsTextFrame::PropertyProvider::GetSpacingInternal(Range aRange, nsSkipCharsRunIterator run( start, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aRange.Length()); bool newlineIsSignificant = mTextStyle->NewlineIsSignificant(mFrame); + // Which letter-spacing model are we using? + // 0 - Gecko legacy model, spacing added to trailing side of letter + // 1 - WebKit/Blink-compatible, spacing added to right-hand side + // 2 - Symmetrical spacing, half added to each side + gfxFloat before, after; + switch (StaticPrefs::layout_css_letter_spacing_model()) { + default: // use Gecko legacy behavior if pref value is unknown + case 0: + before = 0.0; + after = mLetterSpacing; + break; + case 1: + if (mTextRun->IsRightToLeft()) { + before = mLetterSpacing; + after = 0.0; + } else { + before = 0.0; + after = mLetterSpacing; + } + break; + case 2: + before = mLetterSpacing / 2.0; + after = mLetterSpacing - before; + break; + } while (run.NextRun()) { uint32_t runOffsetInSubstring = run.GetSkippedOffset() - aRange.start; gfxSkipCharsIterator iter = run.GetPos(); for (int32_t i = 0; i < run.GetRunLength(); ++i) { - if (CanAddSpacingAfter(mTextRun, run.GetSkippedOffset() + i, + if (before != 0.0 && + CanAddSpacingBefore(mTextRun, run.GetSkippedOffset() + i, + newlineIsSignificant)) { + aSpacing[runOffsetInSubstring + i].mBefore += before; + } + if (after != 0.0 && + CanAddSpacingAfter(mTextRun, run.GetSkippedOffset() + i, newlineIsSignificant)) { // End of a cluster, not in a ligature: put letter-spacing after it - aSpacing[runOffsetInSubstring + i].mAfter += mLetterSpacing; + aSpacing[runOffsetInSubstring + i].mAfter += after; } if (IsCSSWordSpacingSpace(mFrag, i + run.GetOriginalOffset(), mFrame, mTextStyle)) { @@ -5050,25 +5095,25 @@ nsRect nsTextFrame::UpdateTextEmphasis(WritingMode aWM, : do_AddRef(aProvider.GetFontMetrics()); // When the writing mode is vertical-lr the line is inverted, and thus // the ascent and descent are swapped. - nscoord absOffset = (side == eLogicalSideBStart) != aWM.IsLineInverted() + nscoord absOffset = (side == LogicalSide::BStart) != aWM.IsLineInverted() ? baseFontMetrics->MaxAscent() + fm->MaxDescent() : baseFontMetrics->MaxDescent() + fm->MaxAscent(); RubyBlockLeadings leadings; if (nsRubyFrame* ruby = FindFurthestInlineRubyAncestor(this)) { leadings = ruby->GetBlockLeadings(); } - if (side == eLogicalSideBStart) { + if (side == LogicalSide::BStart) { info->baselineOffset = -absOffset - leadings.mStart; overflowRect.BStart(aWM) = -overflowRect.BSize(aWM) - leadings.mStart; } else { - MOZ_ASSERT(side == eLogicalSideBEnd); + MOZ_ASSERT(side == LogicalSide::BEnd); info->baselineOffset = absOffset + leadings.mEnd; overflowRect.BStart(aWM) = frameSize.BSize(aWM) + leadings.mEnd; } // If text combined, fix the gap between the text frame and its parent. if (isTextCombined) { nscoord gap = (baseFontMetrics->MaxHeight() - frameSize.BSize(aWM)) / 2; - overflowRect.BStart(aWM) += gap * (side == eLogicalSideBStart ? -1 : 1); + overflowRect.BStart(aWM) += gap * (side == LogicalSide::BStart ? -1 : 1); } SetProperty(EmphasisMarkProperty(), info); @@ -5676,6 +5721,10 @@ bool nsTextFrame::GetSelectionTextColors(SelectionType aSelectionType, aHighlightName, aBackground); return hasForeground || hasBackground; } + case SelectionType::eTargetText: { + aTextPaintStyle.GetTargetTextColors(aForeground, aBackground); + return true; + } case SelectionType::eURLSecondary: aTextPaintStyle.GetURLSecondaryColor(aForeground); *aBackground = NS_RGBA(0, 0, 0, 0); @@ -9216,7 +9265,6 @@ void nsTextFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTextFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); InvalidateSelectionState(); diff --git a/layout/generic/nsTextPaintStyle.cpp b/layout/generic/nsTextPaintStyle.cpp index e97a059b07..f29fa753d0 100644 --- a/layout/generic/nsTextPaintStyle.cpp +++ b/layout/generic/nsTextPaintStyle.cpp @@ -213,6 +213,24 @@ void nsTextPaintStyle::GetHighlightColors(nscolor* aForeColor, *aBackColor = NS_TRANSPARENT; } +void nsTextPaintStyle::GetTargetTextColors(nscolor* aForeColor, + nscolor* aBackColor) { + NS_ASSERTION(aForeColor, "aForeColor is null"); + NS_ASSERTION(aBackColor, "aBackColor is null"); + const RefPtr<const ComputedStyle> targetTextStyle = + mFrame->ComputeTargetTextStyle(); + if (targetTextStyle) { + *aForeColor = targetTextStyle->GetVisitedDependentColor( + &nsStyleText::mWebkitTextFillColor); + *aBackColor = targetTextStyle->GetVisitedDependentColor( + &nsStyleBackground::mBackgroundColor); + return; + } + // XXX(:jjaschke): Before shipping this feature, a sensible set of colors must + // be set (Bug 1867940). + // in the meantime, use the colors of find selection. + GetHighlightColors(aForeColor, aBackColor); +} bool nsTextPaintStyle::GetCustomHighlightTextColor(nsAtom* aHighlightName, nscolor* aForeColor) { NS_ASSERTION(aForeColor, "aForeColor is null"); diff --git a/layout/generic/nsTextPaintStyle.h b/layout/generic/nsTextPaintStyle.h index adf28fdad8..94cd3cf322 100644 --- a/layout/generic/nsTextPaintStyle.h +++ b/layout/generic/nsTextPaintStyle.h @@ -65,6 +65,7 @@ class MOZ_STACK_CLASS nsTextPaintStyle { */ bool GetSelectionColors(nscolor* aForeColor, nscolor* aBackColor); void GetHighlightColors(nscolor* aForeColor, nscolor* aBackColor); + void GetTargetTextColors(nscolor* aForeColor, nscolor* aBackColor); // Computes colors for custom highlights. // Returns false if there are no rules associated with `aHighlightName`. bool GetCustomHighlightTextColor(nsAtom* aHighlightName, nscolor* aForeColor); diff --git a/layout/generic/nsTextRunTransformations.cpp b/layout/generic/nsTextRunTransformations.cpp index d18a7ec293..de9fd31fda 100644 --- a/layout/generic/nsTextRunTransformations.cpp +++ b/layout/generic/nsTextRunTransformations.cpp @@ -80,6 +80,26 @@ bool nsTransformedTextRun::SetPotentialLineBreaks(Range aRange, return changed; } +void nsTransformedTextRun::SetEmergencyWrapPositions() { + // This parallels part of what gfxShapedText::SetupClusterBoundaries() does + // for normal textruns. + bool prevWasHyphen = false; + for (uint32_t pos : IntegerRange(mString.Length())) { + const char16_t ch = mString[pos]; + if (prevWasHyphen) { + if (nsContentUtils::IsAlphanumeric(ch)) { + mCharacterGlyphs[pos].SetCanBreakBefore( + CompressedGlyph::FLAG_BREAK_TYPE_EMERGENCY_WRAP); + } + prevWasHyphen = false; + } + if (nsContentUtils::IsHyphen(ch) && pos && + nsContentUtils::IsAlphanumeric(mString[pos - 1])) { + prevWasHyphen = true; + } + } +} + size_t nsTransformedTextRun::SizeOfExcludingThis( mozilla::MallocSizeOf aMallocSizeOf) { size_t total = gfxTextRun::SizeOfExcludingThis(aMallocSizeOf); diff --git a/layout/generic/nsTextRunTransformations.h b/layout/generic/nsTextRunTransformations.h index 915211c6af..6bcf26a8c0 100644 --- a/layout/generic/nsTextRunTransformations.h +++ b/layout/generic/nsTextRunTransformations.h @@ -202,7 +202,10 @@ class nsTransformedTextRun final : public gfxTextRun { mOwnsFactory(aOwnsFactory), mNeedsRebuild(true) { mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1); + SetEmergencyWrapPositions(); } + + void SetEmergencyWrapPositions(); }; /** diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp index d5bcc45969..e003058c3e 100644 --- a/layout/generic/nsVideoFrame.cpp +++ b/layout/generic/nsVideoFrame.cpp @@ -226,7 +226,6 @@ void nsVideoFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_FRAME_TRACE( NS_FRAME_TRACE_CALLS, @@ -375,14 +374,9 @@ nsIFrame::SizeComputationResult nsVideoFrame::ComputeSize( } nscoord nsVideoFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - // Bind the result variable to a RAII-based debug object - the variable - // therefore must match the function's return value. - DISPLAY_MIN_INLINE_SIZE(this, result); // This call handles size-containment nsSize size = GetIntrinsicSize().ToSize().valueOr(nsSize()); - result = GetWritingMode().IsVertical() ? size.height : size.width; - return result; + return GetWritingMode().IsVertical() ? size.height : size.width; } nscoord nsVideoFrame::GetPrefISize(gfxContext* aRenderingContext) { diff --git a/layout/generic/test/mochitest.toml b/layout/generic/test/mochitest.toml index 5c06c90eb7..dcaac3de7a 100644 --- a/layout/generic/test/mochitest.toml +++ b/layout/generic/test/mochitest.toml @@ -12,6 +12,7 @@ support-files = [ "file_SlowTallImage.sjs", "bug1174521.html", "!/gfx/layers/apz/test/mochitest/apz_test_utils.js", + "selection_cross_shadow_boundary_helper.js", ] ["test_bug240933.html"] @@ -265,6 +266,42 @@ support-files = [ ["test_selection_changes_with_middle_mouse_button.html"] +["test_selection_cross_shadow_boundary_1_backward_click.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_1_backward_drag.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_1_forward_click.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_1_forward_drag.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_2_backward_click.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_2_backward_drag.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_2_forward_click.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_2_forward_drag.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_multi_ranges_forward_drag.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_multi_ranges_forward_click.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_multi_ranges_backward_drag.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + +["test_selection_cross_shadow_boundary_multi_ranges_backward_click.html"] +skip-if = ["release_or_beta"] # requires Selection.getComposedRanges to be enabled (Nightly only) + ["test_selection_doubleclick.html"] ["test_selection_expanding.html"] diff --git a/layout/generic/test/selection_cross_shadow_boundary_helper.js b/layout/generic/test/selection_cross_shadow_boundary_helper.js new file mode 100644 index 0000000000..b2a596a27f --- /dev/null +++ b/layout/generic/test/selection_cross_shadow_boundary_helper.js @@ -0,0 +1,28 @@ +// Helper file for test_selection_cross_shadow_boundary_* related tests + +function drag( + fromTarget, + fromX, + fromY, + toTarget, + toX, + toY, + withAccelKey = false +) { + synthesizeMouse(fromTarget, fromX, fromY, { + type: "mousemove", + accelKey: withAccelKey, + }); + synthesizeMouse(fromTarget, fromX, fromY, { + type: "mousedown", + accelKey: withAccelKey, + }); + synthesizeMouse(toTarget, toX, toY, { + type: "mousemove", + accelKey: withAccelKey, + }); + synthesizeMouse(toTarget, toX, toY, { + type: "mouseup", + accelKey: withAccelKey, + }); +} diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_1_backward_click.html b/layout/generic/test/test_selection_cross_shadow_boundary_1_backward_click.html new file mode 100644 index 0000000000..ee724b344c --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_1_backward_click.html @@ -0,0 +1,36 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); +function run() { + document.getElementById("host").attachShadow({ mode: "open" }).innerHTML = "<span>InnerText</span>"; + + const inner = host.shadowRoot.firstChild; + const rect = inner.getBoundingClientRect(); + + // Click the bottom right of "InnerText" + synthesizeMouse(inner, rect.width, rect.height, { type: "mousedown"}); + synthesizeMouse(inner, rect.width, rect.height, { type: "mouseup" }); + + // Click the top left of "OuterText" + synthesizeMouse(document.getElementById("outer"), 0, 0, { type: "mousedown" , shiftKey: true}); + synthesizeMouse(document.getElementById("outer"), 0, 0, { type: "mouseup" , shiftKey: true}); + + // Above two clicks should select both "OuterText" and "InnerText" + const sel = document.getSelection().getComposedRanges(host.shadowRoot)[0]; + + // backward selection + is(sel.endContainer, inner.firstChild, "endContainer is the InnerText"); + is(sel.endOffset, 9, "endOffset ends at the last character"); + is(sel.startContainer, outer.firstChild, "startContainer is the OuterText"); + is(sel.startOffset, 0, "startOffset starts at the first character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer">OuterText</span> + <div id="host"></div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_1_backward_drag.html b/layout/generic/test/test_selection_cross_shadow_boundary_1_backward_drag.html new file mode 100644 index 0000000000..f1737eaefa --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_1_backward_drag.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/layout/generic/test/selection_cross_shadow_boundary_helper.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); + +function run() { + document.getElementById("host").attachShadow({ mode: "open" }).innerHTML = "<span>InnerText</span>"; + + const inner = host.shadowRoot.firstChild; + const rect = inner.getBoundingClientRect(); + + // Drag from the bottom right of InnerText to the + // top left of OuterText. + drag( + inner, + rect.width, + rect.height, + document.getElementById("outer"), + 0, + 0); + + // Above drag selects both "OuterText" and "InnerText" + const sel = document.getSelection().getComposedRanges(host.shadowRoot)[0]; + + // backward selection + is(sel.endContainer, inner.firstChild, "endContainer is the InnerText"); + is(sel.endOffset, 9, "endOffset ends at the last character"); + is(sel.startContainer, outer.firstChild, "startContainer is the OuterText"); + is(sel.startOffset, 0, "startOffset starts at the first character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer">OuterText</span> + <div id="host"></div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_1_forward_click.html b/layout/generic/test/test_selection_cross_shadow_boundary_1_forward_click.html new file mode 100644 index 0000000000..dcf4f9ad16 --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_1_forward_click.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); +function run() { + document.getElementById("host").attachShadow({ mode: "open" }).innerHTML = "<span>InnerText</span>"; + + // Click the top left of "OuterText" + synthesizeMouse(document.getElementById("outer"), 0, 0, { type: "mousedown" }); + synthesizeMouse(document.getElementById("outer"), 0, 0, { type: "mouseup" }); + + // Click the bottom right of "InnerText" + const inner = host.shadowRoot.firstChild; + const rect = inner.getBoundingClientRect(); + synthesizeMouse(inner, rect.width, rect.height, { type: "mousedown", shiftKey: true}); + synthesizeMouse(inner, rect.width, rect.height, { type: "mouseup" , shiftKey: true}); + + // Above two clicks should select both "OuterText" and "InnerText" + const sel = document.getSelection().getComposedRanges(host.shadowRoot)[0]; + + // forward selection + is(sel.startContainer, outer.firstChild, "startContainer is the OuterText"); + is(sel.startOffset, 0, "startOffset starts at the first character"); + is(sel.endContainer, inner.firstChild, "endContainer is the InnerText"); + is(sel.endOffset, 9, "endOffset ends at the last character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer">OuterText</span> + <div id="host"></div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_1_forward_drag.html b/layout/generic/test/test_selection_cross_shadow_boundary_1_forward_drag.html new file mode 100644 index 0000000000..6a351ff16b --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_1_forward_drag.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/layout/generic/test/selection_cross_shadow_boundary_helper.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); +function run() { + document.getElementById("host").attachShadow({ mode: "open" }).innerHTML = "<span>InnerText</span>"; + + const inner = host.shadowRoot.firstChild; + const rect = inner.getBoundingClientRect(); + + // drag from the top left of OuterText + // to the bottom right of InnerText + drag( + document.getElementById("outer"), + 0, + 0, + inner, + rect.width, + rect.height); + + // Above drag selects should select both "OuterText" and "InnerText" + const sel = document.getSelection().getComposedRanges(host.shadowRoot)[0]; + + // forward selection + is(sel.startContainer, outer.firstChild, "startContainer is the OuterText"); + is(sel.startOffset, 0, "startOffset starts at the first character"); + is(sel.endContainer, inner.firstChild, "endContainer is the InnerText"); + is(sel.endOffset, 9, "endOffset ends at the last character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer">OuterText</span> + <div id="host"></div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_2_backward_click.html b/layout/generic/test/test_selection_cross_shadow_boundary_2_backward_click.html new file mode 100644 index 0000000000..8df867a0a5 --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_2_backward_click.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/layout/generic/test/selection_cross_shadow_boundary_helper.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); + +function run() { + const root1 = document.getElementById("host1").attachShadow({ mode: "open" }); + root1.innerHTML = "InnerText1"; + + const root2 = document.getElementById("host2").attachShadow({ mode: "open" }); + root2.innerHTML = "<span>InnerText2</span>"; + + + + const inner2 = root2.firstChild; + const rect = inner2.getBoundingClientRect(); + + // Click the bottom right of "InnerText2" + synthesizeMouse(inner2, rect.width, rect.height, { type: "mousedown" }); + synthesizeMouse(inner2, rect.width, rect.height, { type: "mouseup" }); + + // Click the top left of "OuterText1" + synthesizeMouse(document.getElementById("outer1"), 0, 0, { type: "mousedown", shiftKey: true}); + synthesizeMouse(document.getElementById("outer1"), 0, 0, { type: "mouseup" , shiftKey: true}); + + // Above clicks selects should select + // "OuterText1", "OuterText2", "InnerText1" and "InnerText2". + const sel = document.getSelection().getComposedRanges(root2)[0]; + + // backward selection + is(sel.endContainer, inner2.firstChild, "endContainer is the InnerText2"); + is(sel.endOffset, 10, "endOffset ends at the last character"); + is(sel.startContainer, outer1.firstChild, "startContainer is the OuterText1"); + is(sel.startOffset, 0, "startOffset starts at the first character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer1">OuterText1</span> + <div id="host1"></div> + <span id="outer2">OuterText2</span> + <div id="host2"></div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_2_backward_drag.html b/layout/generic/test/test_selection_cross_shadow_boundary_2_backward_drag.html new file mode 100644 index 0000000000..f79512d35e --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_2_backward_drag.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/layout/generic/test/selection_cross_shadow_boundary_helper.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); + +function run() { + const root1 = document.getElementById("host1").attachShadow({ mode: "open" }); + root1.innerHTML = "InnerText1"; + + const root2 = document.getElementById("host2").attachShadow({ mode: "open" }); + root2.innerHTML = "<span>InnerText2</span>"; + + const inner2 = root2.firstChild; + const rect = inner2.getBoundingClientRect(); + + const outer1 = document.getElementById("outer1"); + // Drag from the bottom right of InnerText2 to + // the top left of OuterText1. + drag( + inner2, + rect.width, + rect.height, + outer1, + 0, + 0); + + // Above drag should selects + // "OuterText1", "OuterText2", "InnerText1" and "InnerText2". + const sel = document.getSelection().getComposedRanges(root2)[0]; + + // backward selection + is(sel.endContainer, inner2.firstChild, "endContainer is the InnerText2"); + is(sel.endOffset, 10, "endOffset ends at the last character"); + is(sel.startContainer, outer1.firstChild, "startContainer is the OuterText1"); + is(sel.startOffset, 0, "startOffset starts at the first character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer1">OuterText1</span> + <div id="host1"></div> + <span id="outer2">OuterText2</span> + <div id="host2"></div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_2_forward_click.html b/layout/generic/test/test_selection_cross_shadow_boundary_2_forward_click.html new file mode 100644 index 0000000000..2ca2eaeda9 --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_2_forward_click.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/layout/generic/test/selection_cross_shadow_boundary_helper.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); + +function run() { + const root1 = document.getElementById("host1").attachShadow({ mode: "open" }); + root1.innerHTML = "InnerText1"; + + const root2 = document.getElementById("host2").attachShadow({ mode: "open" }); + root2.innerHTML = "<span>InnerText2</span>"; + + const outer1 = document.getElementById("outer1"); + // Click the top left of "OuterText1" + synthesizeMouse(outer1, 0, 0, { type: "mousedown" }); + synthesizeMouse(outer1, 0, 0, { type: "mouseup" }); + + const inner2 = root2.firstChild; + const rect = inner2.getBoundingClientRect(); + + // Click the bottom right of "InnerText2" + synthesizeMouse(inner2, rect.width, rect.height, { type: "mousedown", shiftKey: true}); + synthesizeMouse(inner2, rect.width, rect.height, { type: "mouseup" , shiftKey: true}); + + // Above clicks should select + // "OuterText1", "OuterText2", "InnerText1" and "InnerText2". + const sel = document.getSelection().getComposedRanges(root2)[0]; + + // forward selection + is(sel.startContainer, outer1.firstChild, "startContainer is the OuterText1"); + is(sel.startOffset, 0, "startOffset starts at the first character"); + is(sel.endContainer, inner2.firstChild, "endContainer is the InnerText2"); + is(sel.endOffset, 10, "endOffset ends at the last character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer1">OuterText1</span> + <div id="host1"></div> + <span id="outer2">OuterText2</span> + <div id="host2"></div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_2_forward_drag.html b/layout/generic/test/test_selection_cross_shadow_boundary_2_forward_drag.html new file mode 100644 index 0000000000..b92108de95 --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_2_forward_drag.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/layout/generic/test/selection_cross_shadow_boundary_helper.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); + +function run() { + const root1 = document.getElementById("host1").attachShadow({ mode: "open" }); + root1.innerHTML = "InnerText1"; + + const root2 = document.getElementById("host2").attachShadow({ mode: "open" }); + root2.innerHTML = "<span>InnerText2</span>"; + + const inner2 = root2.firstChild; + const rect = inner2.getBoundingClientRect(); + + const outer1 = document.getElementById("outer1"); + // Drag from the top lef of OuterText1 to the + // bottom right of InnerText2. + drag( + outer1, + 0, + 0, + inner2, + rect.width, + rect.height); + + // Above drag should select + // "OuterText1", "OuterText2", "InnerText1" and "InnerText2". + const sel = document.getSelection().getComposedRanges(root2)[0]; + + // forward selection + is(sel.startContainer, outer1.firstChild, "startContainer is the OuterText1"); + is(sel.startOffset, 0, "startOffset starts at the first character"); + is(sel.endContainer, inner2.firstChild, "endContainer is the InnerText2"); + is(sel.endOffset, 10, "endOffset ends at the last character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer1">OuterText1</span> + <div id="host1"></div> + <span id="outer2">OuterText2</span> + <div id="host2"></div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_backward_click.html b/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_backward_click.html new file mode 100644 index 0000000000..a2a0d0e2be --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_backward_click.html @@ -0,0 +1,61 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/layout/generic/test/selection_cross_shadow_boundary_helper.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); +function run() { + const inner1 = host1.shadowRoot.querySelector("span"); + const rect1 = inner1.getBoundingClientRect(); + + // Click the bottom right of "InnerText1" + synthesizeMouse(inner1, rect1.width - 1, rect1.height - 1, { type: "mousedown" }); + synthesizeMouse(inner1, rect1.width - 1, rect1.height - 1, { type: "mouseup" }); + + // Click the top left of "OuterText1" + synthesizeMouse(document.getElementById("outer1"), 0, 0, { type: "mousedown", shiftKey: true }); + synthesizeMouse(document.getElementById("outer1"), 0, 0, { type: "mouseup", shiftKey: true }); + + const inner2 = host2.shadowRoot.querySelector("span"); + const rect2 = inner2.getBoundingClientRect(); + + // Click the bottom right of "InnerText2" with accelKey + synthesizeMouse(inner2, rect2.width, rect2.height, { type: "mousedown", accelKey: true}); + synthesizeMouse(inner2, rect2.width, rect2.height, { type: "mouseup" , accelKey: true}); + + // Click the top left of "OuterText2" + synthesizeMouse(document.getElementById("outer2"), 1, 1, { type: "mousedown", shiftKey: true}); + synthesizeMouse(document.getElementById("outer2"), 1, 1, { type: "mouseup", shiftKey: true}); + + const ranges = document.getSelection().getComposedRanges(host1.shadowRoot, host2.shadowRoot); + is(ranges.length, 2, "Above two drag selection should produce two ranges"); + + is(ranges[0].startContainer, outer1.firstChild, "startContainer is the OuterText1"); + is(ranges[0].startOffset, 0, "startOffset starts at the first character"); + is(ranges[0].endContainer, inner1.firstChild, "endContainer is the InnerText1"); + is(ranges[0].endOffset, 10, "endOffset ends at the last character"); + + is(ranges[1].startContainer, outer2.firstChild, "startContainer is the OuterText2"); + is(ranges[1].startOffset, 0, "startOffset starts at the first character"); + is(ranges[1].endContainer, inner2.firstChild, "endContainer is the InnerText2"); + is(ranges[1].endOffset, 10, "endOffset ends at the last character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer1">OuterText1</span> + <div id="host1"> + <template shadowrootmode="open"> + <span>InnerText1</span> + </template> + </div> + <br> + <span id="outer2">OuterText2</span> + <div id="host2"> + <template shadowrootmode="open"> + <span>InnerText2</span> + </template> + </div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_backward_drag.html b/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_backward_drag.html new file mode 100644 index 0000000000..317ee4f973 --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_backward_drag.html @@ -0,0 +1,65 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/layout/generic/test/selection_cross_shadow_boundary_helper.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); +function run() { + const inner1 = host1.shadowRoot.querySelector("span"); + const rect1 = inner1.getBoundingClientRect(); + + // drag from the bottom right of InnerText1 + // to the top left of OuterText1 + drag( + inner1, + rect1.width - 1, + rect1.height - 1, + document.getElementById("outer1"), + 1, + 1); + + const inner2 = host2.shadowRoot.querySelector("span"); + const rect2 = inner2.getBoundingClientRect(); + // drag from the bottom right of InnerText2 + // to the top left of OuterText2 + drag( + inner2, + rect2.width, + rect2.height, + document.getElementById("outer2"), + 1, + 1, + true /* accelKey */); + + const ranges = document.getSelection().getComposedRanges(host1.shadowRoot, host2.shadowRoot); + is(ranges.length, 2, "Above two drag selection should produce two ranges"); + + is(ranges[0].startContainer, outer1.firstChild, "startContainer is the OuterText1"); + is(ranges[0].startOffset, 0, "startOffset starts at the first character"); + is(ranges[0].endContainer, inner1.firstChild, "endContainer is the InnerText1"); + is(ranges[0].endOffset, 10, "endOffset ends at the last character"); + + is(ranges[1].startContainer, outer2.firstChild, "startContainer is the OuterText2"); + is(ranges[1].startOffset, 0, "startOffset starts at the first character"); + is(ranges[1].endContainer, inner2.firstChild, "endContainer is the InnerText2"); + is(ranges[1].endOffset, 10, "endOffset ends at the last character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer1">OuterText1</span> + <div id="host1"> + <template shadowrootmode="open"> + <span>InnerText1</span> + </template> + </div> + <br> + <span id="outer2">OuterText2</span> + <div id="host2"> + <template shadowrootmode="open"> + <span>InnerText2</span> + </template> + </div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_forward_click.html b/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_forward_click.html new file mode 100644 index 0000000000..931c01fca0 --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_forward_click.html @@ -0,0 +1,59 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/layout/generic/test/selection_cross_shadow_boundary_helper.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); +function run() { + // Click the top left of "OuterText1" + synthesizeMouse(document.getElementById("outer1"), 1, 1, { type: "mousedown" }); + synthesizeMouse(document.getElementById("outer1"), 1, 1, { type: "mouseup" }); + + const inner1 = host1.shadowRoot.querySelector("span"); + const rect1 = inner1.getBoundingClientRect(); + // Click the bottom right of "InnerText1" + synthesizeMouse(inner1, rect1.width - 1, rect1.height - 1, { type: "mousedown", shiftKey: true}); + synthesizeMouse(inner1, rect1.width - 1, rect1.height - 1, { type: "mouseup" , shiftKey: true}); + + // Click the top left of "OuterText2" with accelKey + synthesizeMouse(document.getElementById("outer2"), 1, 1, { type: "mousedown", accelKey: true}); + synthesizeMouse(document.getElementById("outer2"), 1, 1, { type: "mouseup", accelKey: true}); + + const inner2 = host2.shadowRoot.querySelector("span"); + const rect2 = inner2.getBoundingClientRect(); + // Click the bottom right of "InnerText2" + synthesizeMouse(inner2, rect2.width, rect2.height, { type: "mousedown", shiftKey: true}); + synthesizeMouse(inner2, rect2.width, rect2.height, { type: "mouseup" , shiftKey: true}); + + const ranges = document.getSelection().getComposedRanges(host1.shadowRoot, host2.shadowRoot); + is(ranges.length, 2, "Above two drag selection should produce two ranges"); + + is(ranges[0].startContainer, outer1.firstChild, "startContainer is the OuterText1"); + is(ranges[0].startOffset, 0, "startOffset starts at the first character"); + is(ranges[0].endContainer, inner1.firstChild, "endContainer is the InnerText1"); + is(ranges[0].endOffset, 10, "endOffset ends at the last character"); + + is(ranges[1].startContainer, outer2.firstChild, "startContainer is the OuterText2"); + is(ranges[1].startOffset, 0, "startOffset starts at the first character"); + is(ranges[1].endContainer, inner2.firstChild, "endContainer is the InnerText2"); + is(ranges[1].endOffset, 10, "endOffset ends at the last character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer1">OuterText1</span> + <div id="host1"> + <template shadowrootmode="open"> + <span>InnerText1</span> + </template> + </div> + <br> + <span id="outer2">OuterText2</span> + <div id="host2"> + <template shadowrootmode="open"> + <span>InnerText2</span> + </template> + </div> +</body> diff --git a/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_forward_drag.html b/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_forward_drag.html new file mode 100644 index 0000000000..b45e588685 --- /dev/null +++ b/layout/generic/test/test_selection_cross_shadow_boundary_multi_ranges_forward_drag.html @@ -0,0 +1,65 @@ +<!DOCTYPE HTML> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="application/javascript" src="/tests/layout/generic/test/selection_cross_shadow_boundary_helper.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script> +SimpleTest.waitForExplicitFinish(); +function run() { + const inner1 = host1.shadowRoot.querySelector("span"); + const rect1 = inner1.getBoundingClientRect(); + + // drag from the top left of OuterText1 + // to the bottom right of InnerText1 + drag( + document.getElementById("outer1"), + 1, + 1, + inner1, + rect1.width - 1, + rect1.height - 1); + + const inner2 = host2.shadowRoot.querySelector("span"); + const rect2 = inner2.getBoundingClientRect(); + // drag from the top left of OuterText2 + // to the bottom right of InnerText2 + drag( + document.getElementById("outer2"), + 1, + 1, + inner2, + rect2.width, + rect2.height, + true /* accelKey */); + + const ranges = document.getSelection().getComposedRanges(host1.shadowRoot, host2.shadowRoot); + is(ranges.length, 2, "Above two drag selection should produce two ranges"); + + is(ranges[0].startContainer, outer1.firstChild, "startContainer is the OuterText1"); + is(ranges[0].startOffset, 0, "startOffset starts at the first character"); + is(ranges[0].endContainer, inner1.firstChild, "endContainer is the InnerText1"); + is(ranges[0].endOffset, 10, "endOffset ends at the last character"); + + is(ranges[1].startContainer, outer2.firstChild, "startContainer is the OuterText2"); + is(ranges[1].startOffset, 0, "startOffset starts at the first character"); + is(ranges[1].endContainer, inner2.firstChild, "endContainer is the InnerText2"); + is(ranges[1].endOffset, 10, "endOffset ends at the last character"); + + SimpleTest.finish(); +} +</script> +<body onload="SimpleTest.waitForFocus(run);"> + <span id="outer1">OuterText1</span> + <div id="host1"> + <template shadowrootmode="open"> + <span>InnerText1</span> + </template> + </div> + <br> + <span id="outer2">OuterText2</span> + <div id="host2"> + <template shadowrootmode="open"> + <span>InnerText2</span> + </template> + </div> +</body> diff --git a/layout/inspector/InspectorCSSParser.cpp b/layout/inspector/InspectorCSSParser.cpp new file mode 100644 index 0000000000..bc9cbf1487 --- /dev/null +++ b/layout/inspector/InspectorCSSParser.cpp @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/InspectorCSSParser.h" + +#include "mozilla/ServoBindings.h" +#include "mozilla/ServoStyleConsts.h" +#include "mozilla/UniquePtr.h" + +namespace mozilla::dom { + +InspectorCSSParser::InspectorCSSParser(const nsACString& aText) + : mInput(aText) { + mParserState = Servo_CSSParser_create(&mInput); +} + +UniquePtr<InspectorCSSParser> InspectorCSSParser::Constructor( + const GlobalObject& aGlobal, const nsACString& aText) { + return MakeUnique<InspectorCSSParser>(aText); +} + +InspectorCSSParser::~InspectorCSSParser() { + Servo_CSSParser_destroy(mParserState); + mParserState = nullptr; +} + +uint32_t InspectorCSSParser::LineNumber() const { return mLineNumber; } + +uint32_t InspectorCSSParser::ColumnNumber() const { + // mColumnNumber is 1-based, but consumers expect 0-based. + return mColumnNumber - 1; +} + +void InspectorCSSParser::NextToken(Nullable<InspectorCSSToken>& aResult) { + StyleCSSToken cssToken; + if (!Servo_CSSParser_NextToken(&mInput, mParserState, &cssToken)) { + aResult.SetNull(); + + mLineNumber = Servo_CSSParser_GetCurrentLine(mParserState); + mColumnNumber = Servo_CSSParser_GetCurrentColumn(mParserState); + + return; + } + + InspectorCSSToken& inspectorCssToken = aResult.SetValue(); + inspectorCssToken.mText.Append(cssToken.text); + inspectorCssToken.mTokenType.Append(cssToken.token_type); + if (cssToken.has_value) { + inspectorCssToken.mValue.Append(cssToken.value); + } else { + inspectorCssToken.mValue.SetIsVoid(true); + } + if (cssToken.has_unit) { + inspectorCssToken.mUnit.Append(cssToken.unit); + } else { + inspectorCssToken.mUnit.SetIsVoid(true); + } + if (cssToken.has_number) { + // Reduce precision to avoid floating point inprecision + inspectorCssToken.mNumber = round(cssToken.number * 100) / 100.0; + } + + mLineNumber = cssToken.line; + mColumnNumber = cssToken.column; +} + +} // namespace mozilla::dom diff --git a/layout/inspector/InspectorCSSParser.h b/layout/inspector/InspectorCSSParser.h new file mode 100644 index 0000000000..e11bc9a898 --- /dev/null +++ b/layout/inspector/InspectorCSSParser.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef InspectorCSSParser_h___ +#define InspectorCSSParser_h___ + +#include "mozilla/dom/InspectorUtilsBinding.h" +#include "mozilla/dom/NonRefcountedDOMObject.h" + +namespace mozilla { + +class StyleParserState; + +namespace dom { + +class InspectorCSSParser final : public NonRefcountedDOMObject { + public: + explicit InspectorCSSParser(const nsACString&); + // The WebIDL constructor. + static UniquePtr<InspectorCSSParser> Constructor(const GlobalObject& aGlobal, + const nsACString& aText); + + ~InspectorCSSParser(); + + bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, + JS::MutableHandle<JSObject*> aReflector) { + return InspectorCSSParser_Binding::Wrap(aCx, this, aGivenProto, aReflector); + } + + uint32_t LineNumber() const; + uint32_t ColumnNumber() const; + void NextToken(Nullable<InspectorCSSToken>& aResult); + + private: + const nsCString mInput; + StyleParserState* mParserState; + uint32_t mLineNumber = 0; + uint32_t mColumnNumber = 0; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* InspectorCSSParser_h___ */ diff --git a/layout/inspector/InspectorUtils.cpp b/layout/inspector/InspectorUtils.cpp index f1886a0dd0..d174c16424 100644 --- a/layout/inspector/InspectorUtils.cpp +++ b/layout/inspector/InspectorUtils.cpp @@ -415,6 +415,7 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList, case StyleCssRuleType::Media: case StyleCssRuleType::Supports: case StyleCssRuleType::LayerBlock: + case StyleCssRuleType::Property: case StyleCssRuleType::Container: { Unused << aResult.AppendElement(OwningNonNull(*rule), fallible); break; @@ -425,7 +426,6 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList, case StyleCssRuleType::LayerStatement: case StyleCssRuleType::FontFace: case StyleCssRuleType::Page: - case StyleCssRuleType::Property: case StyleCssRuleType::Keyframes: case StyleCssRuleType::Keyframe: case StyleCssRuleType::Margin: @@ -433,6 +433,8 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList, case StyleCssRuleType::CounterStyle: case StyleCssRuleType::FontFeatureValues: case StyleCssRuleType::FontPaletteValues: + case StyleCssRuleType::Scope: + case StyleCssRuleType::StartingStyle: break; } @@ -1000,22 +1002,19 @@ void InspectorUtils::GetCSSRegisteredProperties( } /* static */ -void InspectorUtils::GetRuleBodyTextOffsets( - GlobalObject&, const nsACString& aInitialText, - Nullable<InspectorGetRuleBodyTextResult>& aResult) { - uint32_t resultStartOffset; - uint32_t resultEndOffset; - - if (!Servo_GetRuleBodyTextOffsets(&aInitialText, &resultStartOffset, - &resultEndOffset)) { - aResult.SetNull(); - return; - } - - InspectorGetRuleBodyTextResult& offsets = aResult.SetValue(); - offsets.mStartOffset = resultStartOffset; - offsets.mEndOffset = resultEndOffset; +void InspectorUtils::GetRuleBodyText(GlobalObject&, + const nsACString& aInitialText, + nsACString& aBodyText) { + Servo_GetRuleBodyText(&aInitialText, &aBodyText); } +/* static */ +void InspectorUtils::ReplaceBlockRuleBodyTextInStylesheet( + GlobalObject&, const nsACString& aStyleSheetText, uint32_t aLine, + uint32_t aColumn, const nsACString& aNewBodyText, + nsACString& aNewStyleSheetText) { + Servo_ReplaceBlockRuleBodyTextInStylesheetText( + &aStyleSheetText, aLine, aColumn, &aNewBodyText, &aNewStyleSheetText); +} } // namespace dom } // namespace mozilla diff --git a/layout/inspector/InspectorUtils.h b/layout/inspector/InspectorUtils.h index f375e51472..9d64823fc4 100644 --- a/layout/inspector/InspectorUtils.h +++ b/layout/inspector/InspectorUtils.h @@ -270,11 +270,18 @@ class InspectorUtils { nsTArray<InspectorCSSPropertyDefinition>& aResult); /** - * Get the rule body text start and end offsets within aInitialText + * Get the rule body text within aInitialText */ - static void GetRuleBodyTextOffsets( - GlobalObject&, const nsACString& aInitialText, - Nullable<InspectorGetRuleBodyTextResult>& aResult); + static void GetRuleBodyText(GlobalObject&, const nsACString& aInitialText, + nsACString& aBodyText); + + /** + * Replace the rule body text in aStyleSheetText at passed line and column + */ + static void ReplaceBlockRuleBodyTextInStylesheet( + GlobalObject&, const nsACString& aStyleSheetText, uint32_t aLine, + uint32_t aColumn, const nsACString& aNewBodyText, + nsACString& aNewStyleSheetText); }; } // namespace mozilla::dom diff --git a/layout/inspector/ServoStyleRuleMap.cpp b/layout/inspector/ServoStyleRuleMap.cpp index b67d0c98fc..188c4f5329 100644 --- a/layout/inspector/ServoStyleRuleMap.cpp +++ b/layout/inspector/ServoStyleRuleMap.cpp @@ -86,7 +86,9 @@ void ServoStyleRuleMap::RuleRemoved(StyleSheet& aStyleSheet, case StyleCssRuleType::Supports: case StyleCssRuleType::LayerBlock: case StyleCssRuleType::Container: - case StyleCssRuleType::Document: { + case StyleCssRuleType::Document: + case StyleCssRuleType::Scope: + case StyleCssRuleType::StartingStyle: { // See the comment in SheetRemoved. mTable.Clear(); break; @@ -124,7 +126,9 @@ void ServoStyleRuleMap::FillTableFromRule(css::Rule& aRule) { case StyleCssRuleType::Media: case StyleCssRuleType::Supports: case StyleCssRuleType::Container: - case StyleCssRuleType::Document: { + case StyleCssRuleType::Document: + case StyleCssRuleType::Scope: + case StyleCssRuleType::StartingStyle: { auto& rule = static_cast<css::GroupRule&>(aRule); FillTableFromRuleList(*rule.CssRules()); break; diff --git a/layout/inspector/moz.build b/layout/inspector/moz.build index 45d31eb090..9a6634204c 100644 --- a/layout/inspector/moz.build +++ b/layout/inspector/moz.build @@ -19,6 +19,7 @@ EXPORTS.mozilla += [ ] EXPORTS.mozilla.dom += [ + "InspectorCSSParser.h", "InspectorFontFace.h", "InspectorUtils.h", ] @@ -26,6 +27,7 @@ EXPORTS.mozilla.dom += [ UNIFIED_SOURCES += [ "inDeepTreeWalker.cpp", "inLayoutUtils.cpp", + "InspectorCSSParser.cpp", "InspectorFontFace.cpp", "InspectorUtils.cpp", "ServoStyleRuleMap.cpp", diff --git a/layout/inspector/tests/chrome/chrome.toml b/layout/inspector/tests/chrome/chrome.toml index 193be351cb..4326401918 100644 --- a/layout/inspector/tests/chrome/chrome.toml +++ b/layout/inspector/tests/chrome/chrome.toml @@ -14,6 +14,8 @@ support-files = ["test_bug708874.css"] ["test_bug727834.xhtml"] support-files = ["test_bug727834.css"] +["test_CSSStyleRule_querySelectorAll.html"] + ["test_fontFaceGeneric.xhtml"] ["test_fontFaceRanges.xhtml"] diff --git a/layout/inspector/tests/chrome/test_CSSStyleRule_querySelectorAll.html b/layout/inspector/tests/chrome/test_CSSStyleRule_querySelectorAll.html new file mode 100644 index 0000000000..dcddc5744b --- /dev/null +++ b/layout/inspector/tests/chrome/test_CSSStyleRule_querySelectorAll.html @@ -0,0 +1,121 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>Test CSSStyleRule::QuerySelectorAll</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + <style> + .test-simple { + } + .test-nested-parent { + .test-nested-child { + .test-nested-and-non-nested { + } + } + } + .test-nested-and-non-nested { + } + .test-no-match { + } + </style> + <script> + SimpleTest.waitForExplicitFinish(); + addLoadEvent(doTest); + + function doTest() { + let { cssRules } = document.styleSheets[1]; + + info("Testing simple case"); + let rule = cssRules[0]; + let result = rule.querySelectorAll(document); + is(result.length, 2, `2 elements are matching "${rule.selectorText}"`); + is( + result[0].id, + "a", + `Got expected id for first element matching "${rule.selectorText}"` + ); + is( + result[1].id, + "b", + `Got expected id for second element matching "${rule.selectorText}"` + ); + + info("Testing nested rule"); + rule = cssRules[1].cssRules[0]; + result = rule.querySelectorAll(document); + is(result.length, 1, `1 element is matching "${rule.selectorText}"`); + is( + result[0].id, + "d", + `Got expected id for element matching "${rule.selectorText}"` + ); + + info("Testing multi-level deep nested rule"); + rule = cssRules[1].cssRules[0].cssRules[0]; + result = rule.querySelectorAll(document); + // Check that we're not retrieving `f`, as the rule selectorText is `.test-nested-and-non-nested`, + // but it is nested inside `.test-nested-child`. + is(result.length, 1, `1 element is matching "${rule.selectorText}"`); + is( + result[0].id, + "e", + `Got expected id for element matching "${rule.selectorText}"` + ); + + info( + "Testing rule matching multiple elements with the same class, some nested, some not" + ); + rule = cssRules[2]; + result = rule.querySelectorAll(document); + is(result.length, 2, `2 elements are matching "${rule.selectorText}"`); + is( + result[0].id, + "e", + `Got expected id for first element matching "${rule.selectorText}"` + ); + is( + result[1].id, + "f", + `Got expected id for second element matching "${rule.selectorText}"` + ); + + info("Testing that search results are limited by the passed root node"); + rule = cssRules[2]; + result = rule.querySelectorAll(document.querySelector("#c")); + is( + result.length, + 1, + `An element is matching "${rule.selectorText}" in #c` + ); + is( + result[0].id, + "e", + `Got expected id for element matching "${rule.selectorText}"` + ); + + info("Testing rule with no matching elements"); + rule = cssRules[3]; + result = rule.querySelectorAll(document); + is(result.length, 0, `No elements matching "${rule.selectorText}"`); + + SimpleTest.finish(); + } + </script> + </head> + <body> + <h1>Test CSSStyleRule::QuerySelectorAll</h1> + <p id="display"></p> + <div id="content" style="display: none"> + <div id="a" class="test-simple"></div> + <div id="b" class="test-simple"></div> + <div id="c" class="test-nested-parent"> + <span id="d" class="test-nested-child"> + <b id="e" class="test-nested-and-non-nested"></b> + </span> + </div> + <b id="f" class="test-nested-and-non-nested"></b> + </div> + <pre id="test"></pre> + </body> +</html> diff --git a/layout/inspector/tests/mochitest.toml b/layout/inspector/tests/mochitest.toml index e51941752b..b45742b727 100644 --- a/layout/inspector/tests/mochitest.toml +++ b/layout/inspector/tests/mochitest.toml @@ -1,6 +1,7 @@ [DEFAULT] prefs = [ "layout.css.basic-shape-rect.enabled=true", + "layout.css.basic-shape-shape.enabled=true", "layout.css.basic-shape-xywh.enabled=true", "layout.css.properties-and-values.enabled=true", "layout.css.transition-behavior.enabled=true", @@ -78,6 +79,8 @@ skip-if = ["os == 'android'"] ["test_parseStyleSheet_nested.html"] +["test_replaceBlockRuleBodyTextInStylesheet.html"] + ["test_rgba_to_color_name.html"] ["test_selectormatcheselement.html"] diff --git a/layout/inspector/tests/test_bug877690.html b/layout/inspector/tests/test_bug877690.html index 9e5efca0d3..668d18200e 100644 --- a/layout/inspector/tests/test_bug877690.html +++ b/layout/inspector/tests/test_bug877690.html @@ -217,10 +217,13 @@ function do_test() { ok(testValues(values, expected), "property box-shadow's values"); // Regression test for bug 1255379. + var shapeFunction = [ "close", "evenodd", "nonzero", "by", "to", "cw", "ccw", + "small", "large" ]; var expected = [ "inherit", "initial", "unset", "revert", "revert-layer", "none", "url", "polygon", "circle", "ellipse", "inset", - "path", "rect", "xywh", "fill-box", "stroke-box", "view-box", - "margin-box", "border-box", "padding-box", "content-box" ]; + "path", "rect", "xywh", "fill-box", "stroke-box", + "view-box", "margin-box", "border-box", "padding-box", + "content-box", ...shapeFunction ]; var values = InspectorUtils.getCSSValuesForProperty("clip-path"); ok(testValues(values, expected), "property clip-path's values"); diff --git a/layout/inspector/tests/test_getCSSPseudoElementNames.html b/layout/inspector/tests/test_getCSSPseudoElementNames.html index 255f58b889..356d4a89bc 100644 --- a/layout/inspector/tests/test_getCSSPseudoElementNames.html +++ b/layout/inspector/tests/test_getCSSPseudoElementNames.html @@ -22,6 +22,7 @@ "::highlight", "::placeholder", "::selection", + "::target-text", "::-moz-color-swatch", "::-moz-focus-inner", "::-moz-meter-bar", diff --git a/layout/inspector/tests/test_replaceBlockRuleBodyTextInStylesheet.html b/layout/inspector/tests/test_replaceBlockRuleBodyTextInStylesheet.html new file mode 100644 index 0000000000..e6385978ba --- /dev/null +++ b/layout/inspector/tests/test_replaceBlockRuleBodyTextInStylesheet.html @@ -0,0 +1,209 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test InspectorUtils::replaceBlockRuleBodyTextInStylesheet</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css"/> + <style> +#test-simple { + color: #f0c; +} +#test-unicode,[data-unicode="🦄👨👩👧👦"]::after { + content: /* test comment */ "👨👩👧👦🦄"; + outline: 2px solid salmon; +} +#test-empty {} /* 🛠️⚒️🛠️ */ #test-same-line { font-size: 3em; } +#test-nested-parent { + color: tomato; + #test-nested-child { + background: gold; + } +}#test-after-closing-bracket{--modified:false} + </style> + <script>SimpleTest.waitForExplicitFinish();</script> + <script defer> + const InspectorUtils = SpecialPowers.InspectorUtils; + let stylesheet = document.styleSheets[1]; + let authoredStyleSheetText = document.querySelector("style").textContent; + + const existingRulesAuthoredText = [ +`#test-simple { + color: #f0c; +}`, +`#test-unicode,[data-unicode="🦄👨👩👧👦"]::after { + content: /* test comment */ "👨👩👧👦🦄"; + outline: 2px solid salmon; +}`, +`#test-empty {}`, +`#test-same-line { font-size: 3em; }`, +`#test-nested-parent { + color: tomato; + #test-nested-child { + background: gold; + } +}`, +`#test-nested-child { + background: gold; + }`, +`#test-after-closing-bracket{--modified:false}`, +]; + + const replaceBlockRuleBodyTextInStylesheet = (rule, newBodyText) => { + return InspectorUtils.replaceBlockRuleBodyTextInStylesheet( + authoredStyleSheetText, + InspectorUtils.getRelativeRuleLine(rule), + InspectorUtils.getRuleColumn(rule), + newBodyText + )}; + + info("Check a simple case"); + let newBodyText = `border-color: cyan;`; + is( + replaceBlockRuleBodyTextInStylesheet(stylesheet.cssRules[0], newBodyText), + authoredStyleSheetText.replace( + existingRulesAuthoredText[0], + `#test-simple {${newBodyText}}`, + ), + "Got the expected result for #test-simple" + ); + + info("Check that the rule body can be emptied"); + is( + replaceBlockRuleBodyTextInStylesheet(stylesheet.cssRules[0], ""), + authoredStyleSheetText.replace( + existingRulesAuthoredText[0], + `#test-simple {}`, + ), + "Successfuly removed rule content for #test-simple" + ); + + info("Check that it can handle unicode characters"); + newBodyText = `content: "o 🦊 o";`; + is( + replaceBlockRuleBodyTextInStylesheet(stylesheet.cssRules[1], newBodyText), + authoredStyleSheetText.replace(existingRulesAuthoredText[1], + `#test-unicode,[data-unicode="🦄👨👩👧👦"]::after {${newBodyText}}`, + ), + "Got the expected result for #test-unicode" + ); + + info("Check that it can replace content of an empty rule"); + newBodyText = `font-family: "Zilla;"`; + is( + replaceBlockRuleBodyTextInStylesheet(stylesheet.cssRules[2], newBodyText), + authoredStyleSheetText.replace( + existingRulesAuthoredText[2], + `#test-empty {${newBodyText}}`, + ), + "Got the expected result for #test-empty" + ); + + info("Check that it can handle a rule on a same line as another rule"); + newBodyText = `color: pink;`; + is( + replaceBlockRuleBodyTextInStylesheet(stylesheet.cssRules[3], newBodyText), + authoredStyleSheetText.replace( + existingRulesAuthoredText[3], + `#test-same-line {${newBodyText}}`, + ), + "Got the expected result for #test-same-line" + ); + + info("Check that it can handle a rule with a child rule"); + newBodyText = `background: silver; + & > span { + color: white; + }`; + is( + replaceBlockRuleBodyTextInStylesheet(stylesheet.cssRules[4], newBodyText), + authoredStyleSheetText.replace( + existingRulesAuthoredText[4], + `#test-nested-parent {${newBodyText}}`, + ), + "Got the expected result for #test-nested-parent" + ); + + info("Check that it can handle a nested rule"); + newBodyText = `color: white;height: 100%;`; + is( + replaceBlockRuleBodyTextInStylesheet(stylesheet.cssRules[4].cssRules[0], newBodyText), + authoredStyleSheetText.replace( + existingRulesAuthoredText[5], + `#test-nested-child {${newBodyText}}`, + ), + "Got the expected result for #test-nested-child" + ); + + // Covering fix for Bug 1890775 + info("Check that it can handle rules whose declaration is directly after the } of the previous rule, without spaces"); + isnot( + InspectorUtils.getRelativeRuleLine(stylesheet.cssRules[5]), + 1, + "The rule should not be on the first line of the stylesheet to check the issue it covers" + ); + newBodyText = `--modified:true`; + is( + replaceBlockRuleBodyTextInStylesheet(stylesheet.cssRules[5], newBodyText), + authoredStyleSheetText.replace( + existingRulesAuthoredText[6], + `#test-after-closing-bracket{${newBodyText}}`, + ), + "Got the expected result for #test-after-closing-bracket" + ); + + info("Checking fix for files with crlf EOL sequence"); + let styleEl = document.createElement("style"); + let ruleText = `#test-after-closing-bracket-crlf{--modified-crlf:false}` + authoredStyleSheetText = `\r\nhtml{}${ruleText}`; + styleEl.append(document.createTextNode(authoredStyleSheetText)); + document.head.append(styleEl); + stylesheet = document.styleSheets[2]; + isnot( + InspectorUtils.getRelativeRuleLine(stylesheet.cssRules[1]), + 1, + "The rule should not be on the first line of the stylesheet to check the issue it covers" + ); + newBodyText = `--modified-crlf:true`; + is( + replaceBlockRuleBodyTextInStylesheet(stylesheet.cssRules[1], newBodyText), + authoredStyleSheetText.replace( + ruleText, + `#test-after-closing-bracket-crlf{${newBodyText}}`, + ), + "Got the expected result for #test-after-closing-bracket-crlf" + ); + + info("Checking fix for files with cr EOL sequence"); + ruleText = `#test-after-closing-bracket-cr{--modified-cr:false}` + authoredStyleSheetText = `\rhtml{}${ruleText}`; + styleEl.innerText = ""; + styleEl.append(document.createTextNode(authoredStyleSheetText)); + isnot( + InspectorUtils.getRelativeRuleLine(stylesheet.cssRules[1]), + 1, + "The rule should not be on the first line of the stylesheet to check the issue it covers" + ); + newBodyText = `--modified-cr:true`; + is( + replaceBlockRuleBodyTextInStylesheet(stylesheet.cssRules[1], newBodyText), + authoredStyleSheetText.replace( + ruleText, + `#test-after-closing-bracket-cr{${newBodyText}}`, + ), + "Got the expected result for #test-after-closing-bracket-cr" + ); + + SimpleTest.finish(); + </script> +</head> +<body> +<h1>Test InspectorUtils::replaceBlockRuleBodyTextInStylesheet</h1> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> +</html> diff --git a/layout/mathml/nsMathMLContainerFrame.cpp b/layout/mathml/nsMathMLContainerFrame.cpp index b63792c288..fc334d1cae 100644 --- a/layout/mathml/nsMathMLContainerFrame.cpp +++ b/layout/mathml/nsMathMLContainerFrame.cpp @@ -829,39 +829,33 @@ static nscoord AddInterFrameSpacingToSize(ReflowOutput& aDesiredSize, /* virtual */ void nsMathMLContainerFrame::MarkIntrinsicISizesDirty() { - mIntrinsicWidth = NS_INTRINSIC_ISIZE_UNKNOWN; + mIntrinsicISize = NS_INTRINSIC_ISIZE_UNKNOWN; nsContainerFrame::MarkIntrinsicISizesDirty(); } -void nsMathMLContainerFrame::UpdateIntrinsicWidth( +void nsMathMLContainerFrame::UpdateIntrinsicISize( gfxContext* aRenderingContext) { - if (mIntrinsicWidth == NS_INTRINSIC_ISIZE_UNKNOWN) { + if (mIntrinsicISize == NS_INTRINSIC_ISIZE_UNKNOWN) { ReflowOutput desiredSize(GetWritingMode()); GetIntrinsicISizeMetrics(aRenderingContext, desiredSize); // Include the additional width added by FixInterFrameSpacing to ensure // consistent width calculations. AddInterFrameSpacingToSize(desiredSize, this); - mIntrinsicWidth = desiredSize.ISize(GetWritingMode()); + mIntrinsicISize = desiredSize.ISize(GetWritingMode()); } } /* virtual */ nscoord nsMathMLContainerFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - UpdateIntrinsicWidth(aRenderingContext); - result = mIntrinsicWidth; - return result; + UpdateIntrinsicISize(aRenderingContext); + return mIntrinsicISize; } /* virtual */ nscoord nsMathMLContainerFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - UpdateIntrinsicWidth(aRenderingContext); - result = mIntrinsicWidth; - return result; + UpdateIntrinsicISize(aRenderingContext); + return mIntrinsicISize; } /* virtual */ diff --git a/layout/mathml/nsMathMLContainerFrame.h b/layout/mathml/nsMathMLContainerFrame.h index 805d7e03ca..24bc86c338 100644 --- a/layout/mathml/nsMathMLContainerFrame.h +++ b/layout/mathml/nsMathMLContainerFrame.h @@ -37,9 +37,7 @@ class nsMathMLContainerFrame : public nsContainerFrame, public nsMathMLFrame { public: nsMathMLContainerFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, ClassID aID) - : nsContainerFrame(aStyle, aPresContext, aID), - mIntrinsicWidth(NS_INTRINSIC_ISIZE_UNKNOWN), - mBlockStartAscent(0) {} + : nsContainerFrame(aStyle, aPresContext, aID) {} NS_DECL_QUERYFRAME_TARGET(nsMathMLContainerFrame) NS_DECL_QUERYFRAME @@ -341,13 +339,13 @@ class nsMathMLContainerFrame : public nsContainerFrame, public nsMathMLFrame { static void DidReflowChildren(nsIFrame* aFirst, nsIFrame* aStop = nullptr); /** - * Recompute mIntrinsicWidth if it's not already up to date. + * Recompute mIntrinsicISize if it's not already up to date. */ - void UpdateIntrinsicWidth(gfxContext* aRenderingContext); + void UpdateIntrinsicISize(gfxContext* aRenderingContext); - nscoord mIntrinsicWidth; + nscoord mIntrinsicISize = NS_INTRINSIC_ISIZE_UNKNOWN; - nscoord mBlockStartAscent; + nscoord mBlockStartAscent = 0; private: class RowChildFrameIterator; @@ -429,8 +427,7 @@ class nsMathMLmathBlockFrame final : public nsBlockFrame { protected: explicit nsMathMLmathBlockFrame(ComputedStyle* aStyle, nsPresContext* aPresContext) - : nsBlockFrame(aStyle, aPresContext, kClassID) { - } + : nsBlockFrame(aStyle, aPresContext, kClassID) {} virtual ~nsMathMLmathBlockFrame() = default; }; diff --git a/layout/mathml/nsMathMLmoFrame.cpp b/layout/mathml/nsMathMLmoFrame.cpp index e0d68234ae..e27709c0b0 100644 --- a/layout/mathml/nsMathMLmoFrame.cpp +++ b/layout/mathml/nsMathMLmoFrame.cpp @@ -8,6 +8,7 @@ #include "gfxContext.h" #include "mozilla/PresShell.h" +#include "mozilla/StaticPrefs_mathml.h" #include "nsCSSValue.h" #include "nsLayoutUtils.h" #include "nsPresContext.h" @@ -145,15 +146,17 @@ void nsMathMLmoFrame::ProcessTextData() { mFlags |= allFlags & NS_MATHML_OPERATOR_ACCENT; mFlags |= allFlags & NS_MATHML_OPERATOR_MOVABLELIMITS; - // see if this is an operator that should be centered to cater for - // fonts that are not math-aware - if (1 == length) { - if ((ch == '+') || (ch == '=') || (ch == '*') || - (ch == 0x2212) || // − - (ch == 0x2264) || // ≤ - (ch == 0x2265) || // ≥ - (ch == 0x00D7)) { // × - mFlags |= NS_MATHML_OPERATOR_CENTERED; + if (!StaticPrefs::mathml_centered_operators_disabled()) { + // see if this is an operator that should be centered to cater for + // fonts that are not math-aware + if (1 == length) { + if ((ch == '+') || (ch == '=') || (ch == '*') || + (ch == 0x2212) || // − + (ch == 0x2264) || // ≤ + (ch == 0x2265) || // ≥ + (ch == 0x00D7)) { // × + mFlags |= NS_MATHML_OPERATOR_CENTERED; + } } } @@ -593,9 +596,15 @@ nsMathMLmoFrame::Stretch(DrawTarget* aDrawTarget, // get the leading to be left at the top and the bottom of the stretched char // this seems more reliable than using fm->GetLeading() on suspicious fonts - nscoord em; - GetEmHeight(fm, em); - nscoord leading = NSToCoordRound(0.2f * em); + const nscoord leading = [&fm] { + if (StaticPrefs:: + mathml_top_bottom_spacing_for_stretchy_operators_disabled()) { + return 0; + } + nscoord em; + GetEmHeight(fm, em); + return NSToCoordRound(0.2f * (float)em); + }(); // Operators that are stretchy, or those that are to be centered // to cater for fonts that are not math-aware, are handled by the MathMLChar diff --git a/layout/mathml/nsMathMLmtableFrame.h b/layout/mathml/nsMathMLmtableFrame.h index 1a801de8fc..dee6c5ba7a 100644 --- a/layout/mathml/nsMathMLmtableFrame.h +++ b/layout/mathml/nsMathMLmtableFrame.h @@ -222,7 +222,8 @@ class nsMathMLmtdFrame final : public nsTableCellFrame { mozilla::nsDisplayListBuilder* aBuilder, const mozilla::nsDisplayListSet& aLists) override; - LogicalMargin GetBorderWidth(WritingMode aWM) const override; + mozilla::LogicalMargin GetBorderWidth( + mozilla::WritingMode aWM) const override; nsMargin GetBorderOverflow() override; diff --git a/layout/painting/RetainedDisplayListBuilder.cpp b/layout/painting/RetainedDisplayListBuilder.cpp index ca8f37a252..18e9612ff2 100644 --- a/layout/painting/RetainedDisplayListBuilder.cpp +++ b/layout/painting/RetainedDisplayListBuilder.cpp @@ -1313,23 +1313,6 @@ bool RetainedDisplayListBuilder::ShouldBuildPartial( return true; } -void RetainedDisplayListBuilder::InvalidateCaretFramesIfNeeded() { - if (mPreviousCaret == mBuilder.GetCaretFrame()) { - // The current caret frame is the same as the previous one. - return; - } - - if (mPreviousCaret) { - mPreviousCaret->MarkNeedsDisplayItemRebuild(); - } - - if (mBuilder.GetCaretFrame()) { - mBuilder.GetCaretFrame()->MarkNeedsDisplayItemRebuild(); - } - - mPreviousCaret = mBuilder.GetCaretFrame(); -} - class AutoClearFramePropsArray { public: explicit AutoClearFramePropsArray(size_t aCapacity) : mFrames(aCapacity) {} @@ -1585,7 +1568,7 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate( MarkFramesWithItemsAndImagesModified(&mList); } - InvalidateCaretFramesIfNeeded(); + mBuilder.InvalidateCaretFramesIfNeeded(); // We set the override dirty regions during ComputeRebuildRegion or in // DisplayPortUtils::InvalidateForDisplayPortChange. The display port change diff --git a/layout/painting/RetainedDisplayListBuilder.h b/layout/painting/RetainedDisplayListBuilder.h index e9f5b56d1b..e255c4d3aa 100644 --- a/layout/painting/RetainedDisplayListBuilder.h +++ b/layout/painting/RetainedDisplayListBuilder.h @@ -200,11 +200,6 @@ class RetainedDisplayListBuilder { void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem); /** - * Invalidates the current and previous caret frame if they have changed. - */ - void InvalidateCaretFramesIfNeeded(); - - /** * A simple early exit heuristic to avoid slow partial display list rebuilds. * Returns true if a partial display list build should be attempted. */ @@ -272,7 +267,6 @@ class RetainedDisplayListBuilder { nsDisplayListBuilder mBuilder; RetainedDisplayList mList; - WeakFrame mPreviousCaret; RetainedDisplayListMetrics mMetrics; RetainedDisplayListData mData; }; diff --git a/layout/painting/crashtests/1862277-1.html b/layout/painting/crashtests/1862277-1.html index 820bade2fd..f732bb71b6 100644 --- a/layout/painting/crashtests/1862277-1.html +++ b/layout/painting/crashtests/1862277-1.html @@ -15,11 +15,7 @@ that is affected by the clip path a couple times. The retained display list won' container, but the modified display list will have the nsDisplayMask, this will confuse merging when the same item appears inside two different containers. -Note that we set widget.windows.window_occlusion_tracking.enabled=false for this test because -crashtests leave windows open and occlud the crashtest window, which means the refresh driver doesn't -run, which means the requestAnimationFrame don't run. Bug 1864255 tracks fixing this. - -Note that we image.decode-sync.enabled=false for this test because sync decoding triggers extra +Note that we image.testing.decode-sync.enabled=false for this test because sync decoding triggers extra invalidation which "fixes" this bug before it gets a chance to appear. Bug 1866411 tracks this. --> diff --git a/layout/painting/crashtests/1870415-1.html b/layout/painting/crashtests/1870415-1.html new file mode 100644 index 0000000000..ef379dad4d --- /dev/null +++ b/layout/painting/crashtests/1870415-1.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html class="reftest-wait" > + +<style> +HTML { + border: lch(53% 16 none) solid 472288419.0851412mm; + background-blend-mode: difference +} +* { + overflow-y: hidden; + position: sticky; + content-visibility: auto; + background: url(#x) local, scroll repeat-x bottom / contain, left 3% center / contain scroll padding-box, image-set('\E0FAEt\1BA3F\E0379o8' type('c\1D242+\6F0\A8E6**=\D\A\11D5D\1F9D4*\A0\FFF9 2\1F500\6F9\26F1C\2F220 0\D\1FE4C\2064*|\301\62DC\1DD1\101FD 3\643X\E00E6\E0998\2B4C7Y\6F0+*=\23DD6 A>\1615A\101FD\E0C32\1DE4\2F51D\A34A\1D908\1D244\1F9FB*"\135F-=%Z\1D172\1BF1C\C7D\A16B\669\FFFF\A21F\161BF\E0E63\3099\1BBDB\1924\E006C 4\20F9D-\101FD\E3F7\D\3099\2F97E\B>>=5\669\2998A\E0A33\E0450\1D1AC\2FDC2\2FE9E\D\1F973\E0918>\E0493\346\209BC\1D244 c4\1F469\669\E0563\D\Al3\2027\66A') 249dppx, '\E0E1B\E0500\2CC7\E020A+\FDDE\66A 0\A 5\200E\2FD7A/*\1F82 91\2F69F\D\1A7F\FE20(\D-\2F9B0\202A,\3099\FFF9\2FFEA 99G\FE22\11A03 e\16D58]7\1B7D8y\ED25\E08AB\D\A\7F2\235FE\1DBB1\3000\1B71\2029\E08A1vH|=\B751\D\0\66A' type('\23A\2F300\BCC\B48E\1D187 7\E0969\98D3\BBC5\133DF?[\2F36E\1F252\E04CE>>=\2F94D.\660\1D243\E029A 3\27048L\2BF98\2FF8B\7F1\66A\1D168 9\2826\101FD\390\1B07D 29\669\7E5DZ\2029\6F9\6F0\11F0D\27DA2\2F7EB\29A96\E05A5sA\D;\253CB\660\166D6\E0C9C*=\2F046\A682\2044\660o\296A9p\2011{\202D\202F\9\2F8BD C\\#\2A9A7\645') 75dpcm, '\301\4430\301\2F40D\1D943\16DFC 8R\12ED6\1FC45\2426\2FEA8 0<<=\29E7D\2AC8E\D\2028\66A\1D244\1125A\16FE7\D\D\16ED4\66B\1D0BA|=\FB2C\E0C77\BF-\2F93B\23A\A\D^=s\F6D5\E02EB\E008D\36A\0\\\1B885\205F/=\E0FD5\3000#8]n\6F9s\2F9B7\1A7F\B^\1D574!U4\2F645\E0A65\2060B;\FDFA\D>>=\1648A\D3EC\24A09\E05AE\E02FF\A0\E078E{\E0135\2FB3F\1D16E f\1D1AA\4FFB\3A7A\16943\A\D\E032B' 11dppx type('\489\5932\E0A6B\6F9\1F7A0\66A\1F182\205F\D\A\7F3\1DD7\16110\1D11B\1BADC\72E5\12CDB\E034E\25E85\8\669\AD\3000 9')), border-box repeat-x; + rotate: 2116778118.5570207grad 32 20208 64 +} +</style> +<script> +window.addEventListener("load", () => { + let a = document.createElementNS("http://www.w3.org/1999/xhtml", "form") + document.documentElement.appendChild(a) + let b = document.createElementNS("http://www.w3.org/1999/xhtml", "p") + let c = document.createElementNS("http://www.w3.org/1999/xhtml", "span") + let d = document.createElementNS("http://www.w3.org/1999/xhtml", "bdo") + d.contentEditable = true + d.setAttribute("autofocus", true) + c.appendChild(d) + b.appendChild(c) + a.appendChild(b) + setTimeout(finish, 1000); +}); +function finish() { + document.documentElement.className = ""; +} +</script> +</html> diff --git a/layout/painting/crashtests/crashtests.list b/layout/painting/crashtests/crashtests.list index af2df7830c..b51fa60007 100644 --- a/layout/painting/crashtests/crashtests.list +++ b/layout/painting/crashtests/crashtests.list @@ -8,7 +8,7 @@ load 1418722-1.html load 1419917.html load 1425271-1.html load 1428906-1.html -pref(widget.windows.window_occlusion_tracking.enabled,false) load 1430589-1.html # Bug 1819154 +load 1430589-1.html load 1454105-1.html load 1455944-1.html load 1458145.html @@ -18,7 +18,7 @@ load 1469472.html load 1477831-1.html load 1504033.html load 1514544-1.html -asserts(0-1) asserts-if(Android,0-2) load 1547420-1.html +asserts(0-10) load 1547420-1.html # bug 1524064 load 1549909.html load 1551389-1.html asserts(0-2) load 1555819-1.html @@ -31,4 +31,5 @@ load 1714584-1.html load 1763006-1.html load 1819957-1.html load 1851726-1.html -pref(widget.windows.window_occlusion_tracking.enabled,false) pref(image.decode-sync.enabled,false) load 1862277-1.html +pref(image.testing.decode-sync.enabled,false) load 1862277-1.html +needs-focus pref(ui.caretBlinkTime,200) pref(image.testing.decode-sync.enabled,false) asserts(0-2) load 1870415-1.html diff --git a/layout/painting/nsCSSRenderingGradients.cpp b/layout/painting/nsCSSRenderingGradients.cpp index 2fb2f0b024..db30d4fefc 100644 --- a/layout/painting/nsCSSRenderingGradients.cpp +++ b/layout/painting/nsCSSRenderingGradients.cpp @@ -1195,15 +1195,16 @@ class MOZ_STACK_CLASS WrColorStopInterpolator WrColorStopInterpolator( const nsTArray<ColorStop>& aStops, const StyleColorInterpolationMethod& aStyleColorInterpolationMethod, - float aOpacity, nsTArray<wr::GradientStop>& aResult) - : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod), + float aOpacity, nsTArray<wr::GradientStop>& aResult, bool aExtendLastStop) + : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod, + aExtendLastStop), mResult(aResult), mOpacity(aOpacity), mOutputStop(0) {} void CreateStops() { mResult.SetLengthAndRetainStorage(0); - // we always emit at least two stops (start and end) for each input stop, + // We always emit at least two stops (start and end) for each input stop, // which avoids ambiguity with incomplete oklch/lch/hsv/hsb color stops for // the last stop pair, where the last color stop can't be interpreted on its // own because it actually depends on the previous stop. @@ -1257,11 +1258,26 @@ void nsCSSGradientRenderer::BuildWebRenderParameters( // * https://bugzilla.mozilla.org/show_bug.cgi?id=1248178 StyleColorInterpolationMethod styleColorInterpolationMethod = mGradient->ColorInterpolationMethod(); - if (mStops.Length() >= 2 && - (styleColorInterpolationMethod.space != StyleColorSpace::Srgb || - gfxPlatform::GetCMSMode() == CMSMode::All)) { + // For colorspaces supported by WebRender (Srgb, Hsl, Hwb) we technically do + // not need to add extra stops, but the only one of those colorspaces that + // appears frequently is Srgb, and Srgb still needs extra stops if CMS is + // enabled. Hsl/Hwb need extra stops if StyleHueInterpolationMethod is not + // Shorter, or if CMS is enabled. + // + // It's probably best to keep this logic as simple as possible, see + // https://bugzilla.mozilla.org/show_bug.cgi?id=1885716 for an example of + // what can happen if we try to be clever here. + if (styleColorInterpolationMethod.space != StyleColorSpace::Srgb || + gfxPlatform::GetCMSMode() == CMSMode::All) { + // For the specific case of longer hue interpolation on a CSS non-repeating + // gradient, we have to pretend there is another stop at position=1.0 that + // duplicates the last stop, this is probably only used for things like a + // color wheel. No such problem for SVG as it doesn't have that complexity. + bool extendLastStop = aMode == wr::ExtendMode::Clamp && + styleColorInterpolationMethod.hue == + StyleHueInterpolationMethod::Longer; WrColorStopInterpolator interpolator(mStops, styleColorInterpolationMethod, - aOpacity, aStops); + aOpacity, aStops, extendLastStop); interpolator.CreateStops(); } else { aStops.SetLength(mStops.Length()); diff --git a/layout/painting/nsCSSRenderingGradients.h b/layout/painting/nsCSSRenderingGradients.h index 549cd8c4b6..68ecab07ae 100644 --- a/layout/painting/nsCSSRenderingGradients.h +++ b/layout/painting/nsCSSRenderingGradients.h @@ -44,23 +44,38 @@ class MOZ_STACK_CLASS ColorStopInterpolator { public: ColorStopInterpolator( const nsTArray<ColorStop>& aStops, - const StyleColorInterpolationMethod& aStyleColorInterpolationMethod) + const StyleColorInterpolationMethod& aStyleColorInterpolationMethod, + bool aExtendLastStop) : mStyleColorInterpolationMethod(aStyleColorInterpolationMethod), - mStops(aStops) {} + mStops(aStops), + mExtendLastStop(aExtendLastStop) {} void CreateStops() { - for (uint32_t i = 0; i < mStops.Length() - 1; i++) { + // This loop intentionally iterates the last stop if extending. + uint32_t iterStops = mStops.Length() - (mExtendLastStop ? 0 : 1); + for (uint32_t i = 0; i < iterStops; i++) { + auto nextindex = i + 1 < mStops.Length() ? i + 1 : i; const auto& start = mStops[i]; - const auto& end = mStops[i + 1]; + const auto& end = mStops[nextindex]; + float startPosition = start.mPosition; + float endPosition = end.mPosition; + // For CSS non-repeating gradients with longer hue specified, we have to + // pretend there is a stop beyond the last stop. This is never the case + // on SVG gradients as they only use shorter hue. + // + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1885716 for more info. + if (i == mStops.Length() - 1 && mExtendLastStop) { + endPosition = 1.0f; + } uint32_t extraStops = - (uint32_t)(floor(end.mPosition * kFullRangeExtraStops) - - floor(start.mPosition * kFullRangeExtraStops)); + (uint32_t)(floor(endPosition * kFullRangeExtraStops) - + floor(startPosition * kFullRangeExtraStops)); extraStops = clamped(extraStops, 1U, kFullRangeExtraStops); float step = 1.0f / (float)extraStops; for (uint32_t extraStop = 0; extraStop <= extraStops; extraStop++) { auto progress = (float)extraStop * step; auto position = - start.mPosition + progress * (end.mPosition - start.mPosition); + startPosition + progress * (endPosition - startPosition); StyleAbsoluteColor color = Servo_InterpolateColor(mStyleColorInterpolationMethod, &end.mColor, &start.mColor, progress); @@ -73,11 +88,15 @@ class MOZ_STACK_CLASS ColorStopInterpolator { protected: StyleColorInterpolationMethod mStyleColorInterpolationMethod; const nsTArray<ColorStop>& mStops; + // This indicates that we want to extend the endPosition on the last stop, + // which only matters if this is a CSS non-repeating gradient with + // StyleHueInterpolationMethod::Longer (only valid for hsl/hwb/lch/oklch). + bool mExtendLastStop; - // this could be made tunable, but at 1.0/128 the error is largely + // This could be made tunable, but at 1.0/128 the error is largely // irrelevant, as WebRender re-encodes it to 128 pairs of stops. // - // note that we don't attempt to place the positions of these stops + // Note that we don't attempt to place the positions of these stops // precisely at intervals, we just add this many extra stops across the // range where it is convenient. inline static const uint32_t kFullRangeExtraStops = 128; diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 78388d2185..0007f86c5e 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -383,6 +383,36 @@ nsDisplayWrapList* nsDisplayListBuilder::MergeItems( return merged; } +// FIXME(emilio): This whole business should ideally not be needed at all, but +// there are a variety of hard-to-deal-with caret invalidation issues, like +// bug 1888583, and caret changes are relatively uncommon, enough that it +// probably isn't worth chasing all them down. +void nsDisplayListBuilder::InvalidateCaretFramesIfNeeded() { + if (mPaintedCarets.IsEmpty()) { + return; + } + size_t i = mPaintedCarets.Length(); + while (i--) { + nsCaret* caret = mPaintedCarets[i]; + nsIFrame* oldCaret = caret->GetLastPaintedFrame(); + nsRect caretRect; + nsIFrame* currentCaret = caret->GetPaintGeometry(&caretRect); + if (oldCaret == currentCaret) { + // Keep tracking this caret, it hasn't changed. + continue; + } + if (oldCaret) { + oldCaret->MarkNeedsDisplayItemRebuild(); + } + if (currentCaret) { + currentCaret->MarkNeedsDisplayItemRebuild(); + } + // If / when we paint this caret, we'll track it again. + caret->SetLastPaintedFrame(nullptr); + mPaintedCarets.RemoveElementAt(i); + } +} + void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter:: SetCurrentActiveScrolledRoot( const ActiveScrolledRoot* aActiveScrolledRoot) { @@ -650,7 +680,6 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, mCurrentContainerASR(nullptr), mCurrentFrame(aReferenceFrame), mCurrentReferenceFrame(aReferenceFrame), - mCaretFrame(nullptr), mScrollInfoItemsForHoisting(nullptr), mFirstClipChainToDestroy(nullptr), mTableBackgroundSet(nullptr), @@ -718,21 +747,6 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc(); } -static PresShell* GetFocusedPresShell() { - nsPIDOMWindowOuter* focusedWnd = - nsFocusManager::GetFocusManager()->GetFocusedWindow(); - if (!focusedWnd) { - return nullptr; - } - - nsCOMPtr<nsIDocShell> focusedDocShell = focusedWnd->GetDocShell(); - if (!focusedDocShell) { - return nullptr; - } - - return focusedDocShell->GetPresShell(); -} - void nsDisplayListBuilder::BeginFrame() { nsCSSRendering::BeginFrameTreesLocked(); @@ -742,26 +756,6 @@ void nsDisplayListBuilder::BeginFrame() { mInTransform = false; mInFilter = false; mSyncDecodeImages = false; - - if (!mBuildCaret) { - return; - } - - RefPtr<PresShell> presShell = GetFocusedPresShell(); - if (presShell) { - RefPtr<nsCaret> caret = presShell->GetCaret(); - mCaretFrame = caret->GetPaintGeometry(&mCaretRect); - - // The focused pres shell may not be in the document that we're - // painting, or be in a popup. Check if the display root for - // the caret matches the display root that we're painting, and - // only use it if it matches. - if (mCaretFrame && - nsLayoutUtils::GetDisplayRootFrame(mCaretFrame) != - nsLayoutUtils::GetDisplayRootFrame(mReferenceFrame)) { - mCaretFrame = nullptr; - } - } } void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser* aBrowser, @@ -801,7 +795,6 @@ void nsDisplayListBuilder::EndFrame() { FreeClipChains(); FreeTemporaryItems(); nsCSSRendering::EndFrameTreesLocked(); - mCaretFrame = nullptr; } void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame, @@ -1141,12 +1134,32 @@ void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame, return; } - // Caret frames add visual area to their frame, but we don't update the - // overflow area. Use flags to make sure we build display items for that frame - // instead. - if (mCaretFrame && mCaretFrame->PresShell() == state->mPresShell) { - MarkFrameForDisplay(mCaretFrame, aReferenceFrame); - } + state->mCaretFrame = [&]() -> nsIFrame* { + RefPtr<nsCaret> caret = state->mPresShell->GetCaret(); + nsIFrame* currentCaret = caret->GetPaintGeometry(&mCaretRect); + if (!currentCaret) { + return nullptr; + } + + // Check if the display root for the caret matches the display root that + // we're painting, and only use it if it matches. Likely we only need this + // for carets inside popups. + if (nsLayoutUtils::GetDisplayRootFrame(currentCaret) != + nsLayoutUtils::GetDisplayRootFrame(aReferenceFrame)) { + return nullptr; + } + + // Caret frames add visual area to their frame, but we don't update the + // overflow area. Use flags to make sure we build display items for that + // frame instead. + MOZ_ASSERT(currentCaret->PresShell() == state->mPresShell); + MarkFrameForDisplay(currentCaret, aReferenceFrame); + caret->SetLastPaintedFrame(currentCaret); + if (!mPaintedCarets.Contains(caret)) { + mPaintedCarets.AppendElement(std::move(caret)); + } + return currentCaret; + }(); } // A non-blank paint is a paint that does not just contain the canvas @@ -2534,9 +2547,7 @@ struct ZSortItem { }; struct ZOrderComparator { - bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const { - // Note that we can't just take the difference of the two - // z-indices here, because that might overflow a 32-bit int. + bool LessThan(const ZSortItem& aLeft, const ZSortItem& aRight) const { return aLeft.zIndex < aRight.zIndex; } }; @@ -2549,7 +2560,7 @@ struct ContentComparator { explicit ContentComparator(nsIContent* aCommonAncestor) : mCommonAncestor(aCommonAncestor) {} - bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const { + bool LessThan(nsDisplayItem* aLeft, nsDisplayItem* aRight) const { // It's possible that the nsIContent for aItem1 or aItem2 is in a // subdocument of commonAncestor, because display items for subdocuments // have been mixed into the same list. Ensure that we're looking at content @@ -2562,7 +2573,8 @@ struct ContentComparator { // Something weird going on return true; } - return nsContentUtils::CompareTreePosition<TreeKind::Flat>( + return content1 != content2 && + nsContentUtils::CompareTreePosition<TreeKind::Flat>( content1, content2, mCommonAncestor) < 0; } }; @@ -4134,8 +4146,7 @@ bool nsDisplayCaret::CreateWebRenderCommands( nscolor caretColor; nsIFrame* frame = mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor); - MOZ_ASSERT(frame == mFrame, "We're referring different frame"); - if (!frame) { + if (NS_WARN_IF(!frame) || NS_WARN_IF(frame != mFrame)) { return true; } @@ -5196,7 +5207,7 @@ bool nsDisplayOwnLayer::CreateWebRenderCommands( Maybe<wr::WrAnimationProperty> prop; bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() && (IsScrollThumbLayer() || IsZoomingLayer() || - ShouldGetFixedOrStickyAnimationId() || + ShouldGetFixedAnimationId() || (IsRootScrollbarContainer() && HasDynamicToolbar())); if (needsProp) { @@ -5223,7 +5234,7 @@ bool nsDisplayOwnLayer::CreateWebRenderCommands( params.prim_flags |= wr::PrimitiveFlags::IS_SCROLLBAR_CONTAINER; } if (IsZoomingLayer() || - (ShouldGetFixedOrStickyAnimationId() || + (ShouldGetFixedAnimationId() || (IsRootScrollbarContainer() && HasDynamicToolbar()))) { params.is_2d_scale_translation = true; params.should_snap = true; @@ -5239,9 +5250,8 @@ bool nsDisplayOwnLayer::CreateWebRenderCommands( bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData, WebRenderLayerScrollData* aLayerData) { - bool isRelevantToApz = - (IsScrollThumbLayer() || IsScrollbarContainer() || IsZoomingLayer() || - ShouldGetFixedOrStickyAnimationId()); + bool isRelevantToApz = (IsScrollThumbLayer() || IsScrollbarContainer() || + IsZoomingLayer() || ShouldGetFixedAnimationId()); if (!isRelevantToApz) { return false; @@ -5256,16 +5266,11 @@ bool nsDisplayOwnLayer::UpdateScrollData(WebRenderScrollData* aData, return true; } - if (IsFixedPositionLayer() && ShouldGetFixedOrStickyAnimationId()) { + if (IsFixedPositionLayer() && ShouldGetFixedAnimationId()) { aLayerData->SetFixedPositionAnimationId(mWrAnimationId); return true; } - if (IsStickyPositionLayer() && ShouldGetFixedOrStickyAnimationId()) { - aLayerData->SetStickyPositionAnimationId(mWrAnimationId); - return true; - } - MOZ_ASSERT(IsScrollbarContainer() || IsScrollThumbLayer()); aLayerData->SetScrollbarData(mScrollbarData); @@ -5447,7 +5452,7 @@ bool nsDisplayFixedPosition::UpdateScrollData( return true; } -bool nsDisplayFixedPosition::ShouldGetFixedOrStickyAnimationId() { +bool nsDisplayFixedPosition::ShouldGetFixedAnimationId() { #if defined(MOZ_WIDGET_ANDROID) return mFrame->PresContext()->IsRootContentDocumentCrossProcess() && nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) == @@ -5511,7 +5516,8 @@ nsDisplayStickyPosition::nsDisplayStickyPosition( : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot), mContainerASR(aContainerASR), mClippedToDisplayPort(aClippedToDisplayPort), - mShouldFlatten(false) { + mShouldFlatten(false), + mWrStickyAnimationId(0) { MOZ_COUNT_CTOR(nsDisplayStickyPosition); } @@ -5727,12 +5733,27 @@ bool nsDisplayStickyPosition::CreateWebRenderCommands( wr::LayoutVector2D applied = { NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel), NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)}; + bool needsProp = ShouldGetStickyAnimationId(); + Maybe<wr::WrAnimationProperty> prop; + auto spatialKey = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(), + wr::SpatialKeyKind::Sticky); + if (needsProp) { + RefPtr<WebRenderAPZAnimationData> animationData = + aManager->CommandBuilder() + .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>( + this); + mWrStickyAnimationId = animationData->GetAnimationId(); + + prop.emplace(); + prop->id = mWrStickyAnimationId; + prop->key = spatialKey; + prop->effect_type = wr::WrAnimationType::Transform; + } wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame( wr::ToLayoutRect(bounds), topMargin.ptrOr(nullptr), rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr), - leftMargin.ptrOr(nullptr), vBounds, hBounds, applied, - wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(), - wr::SpatialKeyKind::Sticky)); + leftMargin.ptrOr(nullptr), vBounds, hBounds, applied, spatialKey, + prop.ptrOr(nullptr)); saccHelper.emplace(aBuilder, spatialId); aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId); @@ -5801,6 +5822,10 @@ bool nsDisplayStickyPosition::UpdateScrollData( ->GetContent()); aLayerData->SetStickyPositionScrollContainerId(scrollId); } + + if (ShouldGetStickyAnimationId()) { + aLayerData->SetStickyPositionAnimationId(mWrStickyAnimationId); + } } // Return true if either there is a dynamic toolbar affecting this sticky // item or the OwnLayer base implementation returns true for some other @@ -5810,21 +5835,8 @@ bool nsDisplayStickyPosition::UpdateScrollData( return ret; } -bool nsDisplayStickyPosition::ShouldGetFixedOrStickyAnimationId() { -#if defined(MOZ_WIDGET_ANDROID) - if (HasDynamicToolbar()) { // also implies being in the cross-process RCD - StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer(); - if (stickyScrollContainer) { - ScrollableLayerGuid::ViewID scrollId = - nsLayoutUtils::FindOrCreateIDFor(stickyScrollContainer->ScrollFrame() - ->GetScrolledFrame() - ->GetContent()); - return nsLayoutUtils::ScrollIdForRootScrollFrame(mFrame->PresContext()) == - scrollId; - } - } -#endif - return false; +bool nsDisplayStickyPosition::ShouldGetStickyAnimationId() const { + return HasDynamicToolbar(); // also implies being in the cross-process RCD } nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer( diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 5064677cc7..9862415f61 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -659,7 +659,7 @@ class nsDisplayListBuilder { * Get the frame that the caret is supposed to draw in. * If the caret is currently invisible, this will be null. */ - nsIFrame* GetCaretFrame() { return mCaretFrame; } + nsIFrame* GetCaretFrame() { return CurrentPresShellState()->mCaretFrame; } /** * Get the rectangle we're supposed to draw the caret into. */ @@ -852,9 +852,9 @@ class nsDisplayListBuilder { /** * Notifies the builder that a particular themed widget exists * at the given rectangle within the currently built display list. - * For certain appearance values (currently only StyleAppearance::Toolbar and - * StyleAppearance::WindowTitlebar) this gets called during every display list - * construction, for every themed widget of the right type within the + * For certain appearance values (currently only + * StyleAppearance::MozWindowTitlebar) this gets called during every display + * list construction, for every themed widget of the right type within the * display list, except for themed widgets which are transformed or have * effects applied to them (e.g. CSS opacity or filters). * @@ -904,6 +904,11 @@ class nsDisplayListBuilder { const dom::EffectsInfo& aUpdate); /** + * Invalidates the caret frames from previous paints, if they have changed. + */ + void InvalidateCaretFramesIfNeeded(); + + /** * Allocate memory in our arena. It will only be freed when this display list * builder is destroyed. This memory holds nsDisplayItems and * DisplayItemClipChain objects. @@ -1729,6 +1734,7 @@ class nsDisplayListBuilder { bool mInsidePointerEventsNoneDoc; bool mTouchEventPrefEnabledDoc; nsIFrame* mPresShellIgnoreScrollFrame; + nsIFrame* mCaretFrame = nullptr; }; PresShellState* CurrentPresShellState() { @@ -1763,7 +1769,6 @@ class nsDisplayListBuilder { // The reference frame for mCurrentFrame. const nsIFrame* mCurrentReferenceFrame; - nsIFrame* mCaretFrame; // A temporary list that we append scroll info items to while building // display items for the contents of frames with SVG effects. // Only non-null when ShouldBuildScrollInfoItemsForHoisting() is true. @@ -1808,6 +1813,9 @@ class nsDisplayListBuilder { // Stores reusable items collected during display list preprocessing. nsTHashSet<nsDisplayItem*> mReuseableItems; + // Tracked carets used for retained display list. + AutoTArray<RefPtr<nsCaret>, 1> mPaintedCarets; + // Tracked regions used for retained display list. WeakFrameRegion mRetainedWindowDraggingRegion; WeakFrameRegion mRetainedWindowNoDraggingRegion; @@ -3195,12 +3203,13 @@ class nsDisplayList { // array of 20 items should be able to avoid a lot of dynamic allocations // here. AutoTArray<Item, 20> items; + // Ensure we need just one alloc otherwise, no-op if enough. + items.SetCapacity(Length()); for (nsDisplayItem* item : TakeItems()) { items.AppendElement(Item(item)); } - - std::stable_sort(items.begin(), items.end(), aComparator); + items.StableSort(aComparator); for (Item& item : items) { AppendToTop(item); @@ -3208,6 +3217,7 @@ class nsDisplayList { } nsDisplayList TakeItems() { + // This std::move makes this a defined empty list, see assignment operator. nsDisplayList list = std::move(*this); #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED list.mAllowNonEmptyDestruction = true; @@ -5465,7 +5475,7 @@ class nsDisplayOwnLayer : public nsDisplayWrapList { bool IsFixedPositionLayer() const; bool IsStickyPositionLayer() const; bool HasDynamicToolbar() const; - virtual bool ShouldGetFixedOrStickyAnimationId() { return false; } + virtual bool ShouldGetFixedAnimationId() { return false; } bool CreatesStackingContextHelper() override { return true; } @@ -5481,6 +5491,16 @@ class nsDisplayOwnLayer : public nsDisplayWrapList { */ layers::ScrollbarData mScrollbarData; bool mForceActive; + + // Used for APZ to animate this layer for purposes such as + // pinch-zooming or scrollbar thumb movement. Note that setting this + // creates a WebRender ReferenceFrame spatial node, and should only + // be used for display items that establish a Gecko reference frame + // as well (or leaf items like scrollbar thumb nodes where it does not + // matter). + // FIXME: This is currently also used for adjusting position:fixed items + // for dynamic toolbar movement. This may be a problem as position:fixed + // items do not establish Gecko reference frames. uint64_t mWrAnimationId; }; @@ -5540,7 +5560,8 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer { : nsDisplayOwnLayer(aBuilder, aOther), mContainerASR(aOther.mContainerASR), mClippedToDisplayPort(aOther.mClippedToDisplayPort), - mShouldFlatten(false) { + mShouldFlatten(false), + mWrStickyAnimationId(0) { MOZ_COUNT_CTOR(nsDisplayStickyPosition); } @@ -5565,7 +5586,6 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer { bool UpdateScrollData(layers::WebRenderScrollData* aData, layers::WebRenderLayerScrollData* aLayerData) override; - bool ShouldGetFixedOrStickyAnimationId() override; const ActiveScrolledRoot* GetContainerASR() const { return mContainerASR; } @@ -5581,6 +5601,8 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer { return mShouldFlatten; } + bool ShouldGetStickyAnimationId() const; + private: NS_DISPLAY_ALLOW_CLONING() @@ -5610,6 +5632,13 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer { // True if this item should be flattened away. bool mShouldFlatten; + + // Used for APZ to animate the sticky element in the compositor + // for purposes such as dynamic toolbar movement and (in the future) + // overscroll-related adjustment. Unlike nsDisplayOwnLayer::mWrAnimationId, + // this does not create a WebRender ReferenceFrame, which is important + // because sticky elements do not establish Gecko reference frames either. + uint64_t mWrStickyAnimationId; }; class nsDisplayFixedPosition : public nsDisplayOwnLayer { @@ -5651,7 +5680,7 @@ class nsDisplayFixedPosition : public nsDisplayOwnLayer { nsDisplayListBuilder* aDisplayListBuilder) override; bool UpdateScrollData(layers::WebRenderScrollData* aData, layers::WebRenderLayerScrollData* aLayerData) override; - bool ShouldGetFixedOrStickyAnimationId() override; + bool ShouldGetFixedAnimationId() override; void WriteDebugInfo(std::stringstream& aStream) override; protected: diff --git a/layout/printing/crashtests/1758199-1.html b/layout/printing/crashtests/1758199-1.html index 3f7c9227a4..67ee0a73a2 100644 --- a/layout/printing/crashtests/1758199-1.html +++ b/layout/printing/crashtests/1758199-1.html @@ -1,54 +1 @@ -<html class="reftest-wait"> -<script> -let pp; -let documentElements = []; -documentElements.push(document.documentElement); - -window.onload = () => { - documentElements.push(document.documentElement); - - let o = document.getElementById('a') - o.parentNode.appendChild(o) - pp = SpecialPowers.wrap(self).printPreview(); - pp?.print() - window.requestIdleCallback(() => { - documentElements.push(document.documentElement); - - document.write(''); - - setTimeout(finish, 100); - }); -} - -function finish() { - - // The printPreview call above actually opens two print preview windows - // because the <embed src='#'> below causes a second one to open. At least - // we close the one window we can access, not sure if there is a way to get - // ahold of the other window to close it. So this test leaves a window open - // after it finishes. - try { pp.close(); } catch (e) {} - - if (document.documentElement) { - try { document.documentElement.className = ""; } catch (e) {} - } - - // The documentElement that the reftest harness looks at to determine if the - // test is done is not what document.documentElement points to when this code - // is run. So we save all the document.documentElement's we encounter while - // running this test and clear all of their class names. - for (let de of documentElements) { - if (de) { - try { - de.className = ""; - } catch (e) {} - } - } -} -</script> -<style> -:first-of-type { padding-block-start: 99% } -</style> -<mark id='a'> -<embed src='#'> -</html> +<!-- file is now located at testing/crashtest/final/dom/base/crashtests/1758199-1.html --> diff --git a/layout/printing/crashtests/crashtests.list b/layout/printing/crashtests/crashtests.list index e2c57b282c..ec64509ddb 100644 --- a/layout/printing/crashtests/crashtests.list +++ b/layout/printing/crashtests/crashtests.list @@ -6,7 +6,7 @@ skip-if(Android) load 1662259.html skip-if(Android) pref(dom.window.sizeToContent.enabled,true) load 1663722.html skip-if(Android) load 1671503.html load 1748277.html # Bug 1751260 -skip-if(Android) load 1758199-1.html # printPreview doesn't work on android +# load 1758199-1.html # this test is run at the very end in testing/crashtest/final/crashtests.list load 1804571.html load 1804798.html load 1804794.html diff --git a/layout/reftests/async-scrolling/reftest.list b/layout/reftests/async-scrolling/reftest.list index d74a1544b2..421129645a 100644 --- a/layout/reftests/async-scrolling/reftest.list +++ b/layout/reftests/async-scrolling/reftest.list @@ -12,7 +12,7 @@ skip-if(useDrawSnapshot) fuzzy-if(gtkWidget,0-1,0-87) fuzzy-if(!gtkWidget,0-1,0- skip-if(useDrawSnapshot) == bg-fixed-child-no-culling-1.html bg-fixed-child-no-culling-1-ref.html skip-if(useDrawSnapshot) == bg-fixed-child-no-culling-2.html bg-fixed-child-no-culling-2-ref.html skip-if(useDrawSnapshot) == bg-fixed-child-no-culling-3.html bg-fixed-child-no-culling-3-ref.html -fuzzy-if(Android,0-2,0-4000) fuzzy-if(cocoaWidget,0-2,0-179524) fuzzy-if(winWidget,0-1,0-74590) fuzzy-if(gtkWidget&&layersGPUAccelerated,0-1,0-3528) skip-if(useDrawSnapshot) fuzzy-if(geckoview,0-1,0-74590) == bg-fixed-transformed-image.html bg-fixed-transformed-image-ref.html +fuzzy-if(Android,0-2,0-4000) fuzzy-if(cocoaWidget,0-2,0-179524) fuzzy-if(winWidget,0-1,0-74590) fuzzy-if(gtkWidget,0-1,0-3528) skip-if(useDrawSnapshot) fuzzy-if(geckoview,0-1,0-74590) == bg-fixed-transformed-image.html bg-fixed-transformed-image-ref.html skip-if(useDrawSnapshot) == contain-paint-scrollable-frame-1.html contain-paint-scrollable-frame-1-ref.html skip-if(useDrawSnapshot) == element-1.html element-1-ref.html pref(layers.force-active,true) skip-if(useDrawSnapshot) == iframe-1.html iframe-1-ref.html @@ -68,12 +68,12 @@ fuzzy-if(Android,0-13,0-465) fuzzy-if(gtkWidget,17-28,24-60) fuzzy-if(cocoaWidge fuzzy-if(Android,0-13,0-465) fuzzy-if(gtkWidget,17-29,24-60) fuzzy-if(cocoaWidget,15-19,40-75) skip-if(useDrawSnapshot) == fixed-pos-scrolled-clip-4.html fixed-pos-scrolled-clip-4-ref.html # Bug 1604338 skip-if(useDrawSnapshot) == fixed-pos-scrolled-clip-5.html fixed-pos-scrolled-clip-5-ref.html skip-if(useDrawSnapshot) == position-sticky-bug1434250.html position-sticky-bug1434250-ref.html -fuzzy-if(Android,0-12,0-11) fuzzy-if(gtkWidget,16-25,12-32) fuzzy-if(cocoaWidget,13-16,20-44) skip-if(useDrawSnapshot) == position-sticky-scrolled-clip-1.html position-sticky-scrolled-clip-1-ref.html # Bug 1604338 +fuzzy-if(Android,0-12,0-45) fuzzy-if(gtkWidget,16-25,12-32) fuzzy-if(cocoaWidget,13-16,20-44) skip-if(useDrawSnapshot) == position-sticky-scrolled-clip-1.html position-sticky-scrolled-clip-1-ref.html # Bug 1604338 fuzzy-if(Android,0-6,0-4) skip == position-sticky-scrolled-clip-2.html position-sticky-scrolled-clip-2-ref.html # bug ?????? - incorrectly applying clip to sticky contents fuzzy-if(Android,0-8,0-27) fuzzy-if(cocoaWidget,9-11,20-44) skip-if(useDrawSnapshot) == curtain-effect-1.html curtain-effect-1-ref.html fuzzy-if(Android,0-7,0-9) fuzzy-if(gtkWidget,10-15,12-32) fuzzy-if(cocoaWidget,5-9,20-42) skip-if(useDrawSnapshot) == transformed-1.html transformed-1-ref.html # Bug 1604338 fuzzy-if(Android,2-7,1-12) fuzzy-if(gtkWidget,3-5,12-28) fuzzy-if(cocoaWidget,5-6,18-38) skip-if(useDrawSnapshot) fuzzy-if(swgl&&cocoaWidget&&isDebugBuild,0-6,0-38) == position-sticky-transformed-in-scrollframe-1.html position-sticky-transformed-in-scrollframe-1-ref.html # Bug 1604338 -fuzzy-if(Android,3-3,1-470) fuzzy-if(Android&&swgl&&isDebugBuild&&/^aarch64-gcc3/.test(xulRuntime.XPCOMABI),3-3,457-457) fuzzy-if(gtkWidget,13-20,12-32) fuzzy-if(cocoaWidget,12-16,20-44) skip-if(useDrawSnapshot) == position-sticky-transformed-in-scrollframe-2.html position-sticky-transformed-in-scrollframe-2-ref.html # Bug 1604338 +fuzzy-if(Android,3-3,1-470) fuzzy-if(gtkWidget,13-20,12-32) fuzzy-if(cocoaWidget,12-16,20-44) skip-if(useDrawSnapshot) == position-sticky-transformed-in-scrollframe-2.html position-sticky-transformed-in-scrollframe-2-ref.html # Bug 1604338 fuzzy-if(Android,12-13,4-31) fuzzy-if(gtkWidget,16-27,14-32) fuzzy-if(cocoaWidget,13-16,20-44) skip-if(useDrawSnapshot) == position-sticky-in-transformed-scrollframe-1.html position-sticky-in-transformed-scrollframe-ref.html # Bug 1604338 fuzzy-if(Android,12-13,4-31) fuzzy-if(gtkWidget,16-27,14-32) fuzzy-if(cocoaWidget,13-16,20-44) skip-if(useDrawSnapshot) == position-sticky-in-transformed-scrollframe-2.html position-sticky-in-transformed-scrollframe-ref.html # Bug 1604338 @@ -106,9 +106,9 @@ skip-if(useDrawSnapshot||Android) pref(apz.overscroll.enabled,false) pref(apz.ov # for this test, apz.allow_zooming is needed to ensure we take the containerless scrolling codepath that creates # an async zoom container (since we are testing a regression in that codepath) skip-if(!Android) pref(apz.allow_zooming,true) test-pref(apz.fixed-margin-override.enabled,true) test-pref(apz.fixed-margin-override.bottom,50) == dynamic-toolbar-fixed-bottom-1.html dynamic-toolbar-fixed-bottom-1-ref.html -skip-if(!Android) pref(apz.allow_zooming,true) test-pref(apz.fixed-margin-override.enabled,true) test-pref(apz.fixed-margin-override.bottom,50) == dynamic-toolbar-sticky-bottom-1.html dynamic-toolbar-sticky-bottom-1-ref.html skip-if(!Android) pref(apz.allow_zooming,true) test-pref(apz.fixed-margin-override.enabled,true) test-pref(apz.fixed-margin-override.top,50) == dynamic-toolbar-fixed-top-1.html dynamic-toolbar-fixed-top-1-ref.html -skip-if(!Android) pref(apz.allow_zooming,true) test-pref(apz.fixed-margin-override.enabled,true) test-pref(apz.fixed-margin-override.top,50) == dynamic-toolbar-sticky-top-1.html dynamic-toolbar-sticky-top-1-ref.html # bug 1627326 for geckoview&&!webrender +pref(apz.allow_zooming,true) test-pref(apz.fixed-margin-override.enabled,true) test-pref(apz.fixed-margin-override.bottom,50) == dynamic-toolbar-sticky-bottom-1.html dynamic-toolbar-sticky-bottom-1-ref.html +pref(apz.allow_zooming,true) test-pref(apz.fixed-margin-override.enabled,true) test-pref(apz.fixed-margin-override.top,50) == dynamic-toolbar-sticky-top-1.html dynamic-toolbar-sticky-top-1-ref.html # The next block of tests are based on a single test page which has three # sticky items (one sticky to the top, one sticky to the bottom, and one sticky @@ -144,7 +144,7 @@ defaults # at the top. Only the -5 and -6 scroll offsets are impacted, so the # reference pages for first 4 scroll offsets are the same as the baseline # case. -defaults skip-if(!geckoview) pref(apz.allow_zooming,true) test-pref(apz.fixed-margin-override.enabled,true) test-pref(apz.fixed-margin-override.top,50) fuzzy(0-1,0-120000) +defaults pref(apz.allow_zooming,true) test-pref(apz.fixed-margin-override.enabled,true) test-pref(apz.fixed-margin-override.top,50) fuzzy(0-1,0-120000) == dynamic-toolbar-sticky-1a.html dynamic-toolbar-sticky-1-ref.html == dynamic-toolbar-sticky-1b.html dynamic-toolbar-sticky-1-ref.html == dynamic-toolbar-sticky-1c.html dynamic-toolbar-sticky-1-ref.html @@ -161,17 +161,17 @@ defaults skip-if(!geckoview) pref(apz.allow_zooming,true) test-pref(apz.fixed-ma == dynamic-toolbar-sticky-5b.html dynamic-toolbar-sticky-5-ref-t.html == dynamic-toolbar-sticky-5c.html dynamic-toolbar-sticky-5-ref-t.html == dynamic-toolbar-sticky-6a.html dynamic-toolbar-sticky-6-ref-t.html -== dynamic-toolbar-sticky-6b.html dynamic-toolbar-sticky-6-ref-t.html # bug 1630274 for non-WR + == dynamic-toolbar-sticky-6b.html dynamic-toolbar-sticky-6-ref-t.html == dynamic-toolbar-sticky-6c.html dynamic-toolbar-sticky-6-ref-t.html defaults # Same as above block of tests, except with the dynamic toolbar margin # at the bottom of the page. This time the -1 and -2 scroll offsets are # impacted. -defaults skip-if(!geckoview) pref(apz.allow_zooming,true) test-pref(apz.fixed-margin-override.enabled,true) test-pref(apz.fixed-margin-override.bottom,50) fuzzy(0-1,0-120000) +defaults pref(apz.allow_zooming,true) test-pref(apz.fixed-margin-override.enabled,true) test-pref(apz.fixed-margin-override.bottom,50) fuzzy(0-1,0-120000) == dynamic-toolbar-sticky-1a.html dynamic-toolbar-sticky-1-ref-b.html == dynamic-toolbar-sticky-1b.html dynamic-toolbar-sticky-1-ref-b.html -== dynamic-toolbar-sticky-1c.html dynamic-toolbar-sticky-1-ref-b.html # bug 1630274 for non-WR + == dynamic-toolbar-sticky-1c.html dynamic-toolbar-sticky-1-ref-b.html == dynamic-toolbar-sticky-2a.html dynamic-toolbar-sticky-2-ref-b.html == dynamic-toolbar-sticky-2b.html dynamic-toolbar-sticky-2-ref-b.html == dynamic-toolbar-sticky-2c.html dynamic-toolbar-sticky-2-ref-b.html diff --git a/layout/reftests/bidi/dirAuto/reftest.list b/layout/reftests/bidi/dirAuto/reftest.list index f47c635124..5fc24b2a2b 100644 --- a/layout/reftests/bidi/dirAuto/reftest.list +++ b/layout/reftests/bidi/dirAuto/reftest.list @@ -42,7 +42,7 @@ == dynamicDirAuto-setLTR-InvalidDir5.html dynamicDirAuto-refLTR-LTR.html == dynamicDirAuto-setLTR-InvalidDir6.html dynamicDirAuto-refLTR-LTR.html == dynamicDirAuto-setLTR-InvalidDir7.html dynamicDirAuto-refLTR-LTR.html -== dynamicDirAuto-setLTR-InvalidDir7.html dynamicDirAuto-refLTR-LTR.html +== dynamicDirAuto-setLTR-InvalidDir8.html dynamicDirAuto-refLTR-LTR.html == dynamicDirAuto-setRTL-Auto1.html dynamicDirAuto-refRTL-RTL.html fuzzy-if(Android,0-1,0-1) == dynamicDirAuto-setRTL-Auto2.html dynamicDirAuto-refRTL-RTL.html # android fuzz: bug 1461573 == dynamicDirAuto-setRTL-Auto3.html dynamicDirAuto-refRTL-RTL.html diff --git a/layout/reftests/bugs/1888941-text-transform-emergency-wrap-ref.html b/layout/reftests/bugs/1888941-text-transform-emergency-wrap-ref.html new file mode 100644 index 0000000000..1ae7db9050 --- /dev/null +++ b/layout/reftests/bugs/1888941-text-transform-emergency-wrap-ref.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<div>12345-<br>67890-<br>98765-<br>43210</div> diff --git a/layout/reftests/bugs/1888941-text-transform-emergency-wrap.html b/layout/reftests/bugs/1888941-text-transform-emergency-wrap.html new file mode 100644 index 0000000000..9e7c49cf57 --- /dev/null +++ b/layout/reftests/bugs/1888941-text-transform-emergency-wrap.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<div style="width: 1px; text-transform: uppercase">12345-67890-98765-43210</div> diff --git a/layout/reftests/bugs/307102-2-ref.html b/layout/reftests/bugs/307102-2-ref.html deleted file mode 100644 index 8b72bc0383..0000000000 --- a/layout/reftests/bugs/307102-2-ref.html +++ /dev/null @@ -1,8 +0,0 @@ -<!DOCTYPE html> -<html> - <body> - <div style="position: absolute; width: 100px;"> - <input style="position: absolute; top: 0; left: 0;"> - </div> - </body> -</html> diff --git a/layout/reftests/bugs/307102-2.html b/layout/reftests/bugs/307102-2.html deleted file mode 100644 index cfeb6658a9..0000000000 --- a/layout/reftests/bugs/307102-2.html +++ /dev/null @@ -1,8 +0,0 @@ -<!DOCTYPE html> -<html> - <body> - <div style="position: absolute; width: 100px;"> - <input style="position: absolute; top: 0; left: 0; right: 0"> - </div> - </body> -</html> diff --git a/layout/reftests/bugs/385870-1-ref.html b/layout/reftests/bugs/385870-1-ref.html deleted file mode 100644 index fcebd34fb2..0000000000 --- a/layout/reftests/bugs/385870-1-ref.html +++ /dev/null @@ -1,51 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<title></title> -<style> -#red { - position: absolute; - background-color: blue; - top: 10px; - left: 10px; -} - -#green { - position: absolute; - background-color: blue; - top: 10px; - width: 80px; - right: 10px; -} - -#blue { - position: absolute; - background-color: blue; - left: 10px; - height: 80px; - bottom: 10px; -} - -#yellow { - position: absolute; - background-color: blue; - right: 10px; - width: 80px; - height: 80px; - bottom: 10px; -} - -</style> -<script> - -</script> -</head> -<body> - -<textarea id=red></textarea> -<textarea id=green></textarea> -<textarea id=blue></textarea> -<textarea id=yellow></textarea> - -</body> -</html> diff --git a/layout/reftests/bugs/385870-1.html b/layout/reftests/bugs/385870-1.html deleted file mode 100644 index 4d292e1c20..0000000000 --- a/layout/reftests/bugs/385870-1.html +++ /dev/null @@ -1,55 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<title></title> -<style> -#red { - position: absolute; - background-color: blue; - top: 10px; - left: 10px; - right: 100px; - bottom: 100px; -} - -#green { - position: absolute; - background-color: blue; - top: 10px; - width: 80px; - right: 10px; - bottom: 100px; -} - -#blue { - position: absolute; - background-color: blue; - left: 10px; - height: 80px; - right: 100px; - bottom: 10px; -} - -#yellow { - position: absolute; - background-color: blue; - right: 10px; - width: 80px; - height: 80px; - bottom: 10px; -} - -</style> -<script> - -</script> -</head> -<body> - -<textarea id=red></textarea> -<textarea id=green></textarea> -<textarea id=blue></textarea> -<textarea id=yellow></textarea> - -</body> -</html> diff --git a/layout/reftests/bugs/385870-2-ref.html b/layout/reftests/bugs/385870-2-ref.html deleted file mode 100644 index eebef3805e..0000000000 --- a/layout/reftests/bugs/385870-2-ref.html +++ /dev/null @@ -1,51 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<title></title> -<style> -#red { - position: absolute; - background-color: blue; - top: 10px; - left: 10px; -} - -#green { - position: absolute; - background-color: blue; - top: 10px; - width: 80px; - right: 10px; -} - -#blue { - position: absolute; - background-color: blue; - left: 10px; - height: 80px; - bottom: 10px; -} - -#yellow { - position: absolute; - background-color: blue; - right: 10px; - width: 80px; - height: 80px; - bottom: 10px; -} - -</style> -<script> - -</script> -</head> -<body> - -<input id=red> -<input id=green> -<input id=blue> -<input id=yellow> - -</body> -</html> diff --git a/layout/reftests/bugs/385870-2.html b/layout/reftests/bugs/385870-2.html deleted file mode 100644 index c641abb796..0000000000 --- a/layout/reftests/bugs/385870-2.html +++ /dev/null @@ -1,55 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<title></title> -<style> -#red { - position: absolute; - background-color: blue; - top: 10px; - left: 10px; - right: 100px; - bottom: 100px; -} - -#green { - position: absolute; - background-color: blue; - top: 10px; - width: 80px; - right: 10px; - bottom: 100px; -} - -#blue { - position: absolute; - background-color: blue; - left: 10px; - height: 80px; - right: 100px; - bottom: 10px; -} - -#yellow { - position: absolute; - background-color: blue; - right: 10px; - width: 80px; - height: 80px; - bottom: 10px; -} - -</style> -<script> - -</script> -</head> -<body> - -<input id=red> -<input id=green> -<input id=blue> -<input id=yellow> - -</body> -</html> diff --git a/layout/reftests/bugs/404553-1-ref.html b/layout/reftests/bugs/404553-1-ref.html index cfa0dc8147..ac35a9e4b5 100644 --- a/layout/reftests/bugs/404553-1-ref.html +++ b/layout/reftests/bugs/404553-1-ref.html @@ -1 +1 @@ -<div style="background-color: lime; height: 50px;"><div style="background: green; width: 50px; margin-left: auto; margin-right: 0;"> </div></div>
\ No newline at end of file +<div style="background-color: lime; height: 50px;"><div style="background: green; width: 50px;"> </div></div> diff --git a/layout/reftests/bugs/404553-1.html b/layout/reftests/bugs/404553-1.html index 692c63c67c..ab63f1396e 100644 --- a/layout/reftests/bugs/404553-1.html +++ b/layout/reftests/bugs/404553-1.html @@ -1 +1 @@ -<table><marquee behavior="alternate" scrollamount="0" style="background-color: lime; height: 50px;"><div style="background: green; width: 50px"> </div></marquee><span><title>
\ No newline at end of file +<table><marquee behavior="alternate" scrollamount="0" style="background-color: lime; height: 50px;"><div style="background: green; width: 50px"> </div></marquee><span><title> diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index d85d5e7310..84f4ecca9f 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -342,8 +342,7 @@ skip-if(useDrawSnapshot) == 306660-2.html 306660-2-ref.html == 306660-3.html 306660-3-ref.html == 307076-1.html 307076-1-ref.html == 307102-1.html 307102-1-ref.html -== 307102-2.html 307102-2-ref.html -== 307102-3.html 307102-3-ref.html +random == 307102-3.html 307102-3-ref.html == 307102-4.html 307102-4-ref.html == 308406-1.html 308406-1-ref.html == 308406-2.html 308406-2-ref.html @@ -725,8 +724,6 @@ fuzzy(0-2,0-5) == 381507-1.html 381507-1-ref.html fuzzy(0-1,0-600) == 385823-2a.html 385823-2-ref.html fails == 385823-2b.html 385823-2-ref.html fuzzy(0-1,0-600) == 385823-2c.html 385823-2-ref.html -fuzzy(0-2,0-11) == 385870-1.html 385870-1-ref.html -fuzzy(0-2,0-3) == 385870-2.html 385870-2-ref.html == 386014-1a.html 386014-1-ref.html == 386014-1b.html 386014-1-ref.html == 386014-1c.html 386014-1-ref.html @@ -922,7 +919,7 @@ fuzzy-if(winWidget,0-123,0-1600) fuzzy-if(swgl,0-1,0-39) == 409659-1a.html 40965 != 409659-1c.html 409659-1-ref.html fuzzy-if(winWidget,0-123,0-1900) fuzzy-if(swgl,0-1,0-39) == 409659-1d.html 409659-1-ref.html # Bug 1128229 == 410621-1.html 410621-1-ref.html -== 411059-1.html 411059-1-ref.html +pref(layout.css.letter-spacing.model,0) == 411059-1.html 411059-1-ref.html fuzzy-if(winWidget,46-129,652-770) == 411334-1.xml 411334-1-ref.xml == 411585-1.html 411585-1-ref.html == 411585-2.html 411585-2-ref.html @@ -1123,7 +1120,7 @@ fuzzy(0-1,0-3280) == 438987-2c.html 438987-2-ref.html != about:blank 438987-2-ref.html # check that backgrounds work at all == 439004-1.html 439004-1-ref.html == 439639-1.html 439639-1-ref.html -fuzzy-if(gtkWidget,0-255,0-6) == 439910.html 439910-ref.html +pref(layout.css.letter-spacing.model,0) fuzzy-if(gtkWidget,0-255,0-6) == 439910.html 439910-ref.html fuzzy(0-1,0-1) fuzzy-if(Android,0-1,0-3) == 440112.html 440112-ref.html == 440149-1.html 440149-1-ref.html == 441259-1.html 441259-1-ref.html @@ -1347,7 +1344,7 @@ pref(browser.display.focus_ring_width,1) == 491180-2.html 491180-2-ref.html fuzzy(0-6,0-97456) == 501627-1.html 501627-1-ref.html # Bug 1481664 == 502288-1.html 502288-1-ref.html fuzzy-if(gtkWidget,0-1,0-2) == 502447-1.html 502447-1-ref.html #Bug 1315834 -== 502795-1.html 502795-1-ref.html +pref(layout.css.letter-spacing.model,0) == 502795-1.html 502795-1-ref.html == 502942-1.html 502942-1-ref.html == 503364-1a.html 503364-1-ref.html == 503364-1b.html 503364-1-ref.html @@ -1370,8 +1367,8 @@ fuzzy(0-1,0-1200) == 512410.html 512410-ref.html == 512631-1.html 512631-1-ref.html fuzzy(0-1,0-4) == 513153-1a.html 513153-1-ref.html fuzzy(0-1,0-4) == 513153-1b.html 513153-1-ref.html -pref(widget.non-native-theme.webrender,true) == 513153-2a.html 513153-2-ref.html # appleSilicon: bug 1724583 -fuzzy-if(cocoaWidget,0-112,0-108) == 513153-2b.html 513153-2-ref.html # only fuzzy when widget.non-native-theme.webrender=false, snapping difference +== 513153-2a.html 513153-2-ref.html # appleSilicon: bug 1724583 +== 513153-2b.html 513153-2-ref.html == chrome://reftest/content/bugs/513318-1.xhtml chrome://reftest/content/bugs/513318-1-ref.xhtml != chrome://reftest/content/bugs/513318-2.xhtml chrome://reftest/content/bugs/513318-2-ref.xhtml == 514917-1.html 514917-1-ref.html @@ -2165,3 +2162,4 @@ fuzzy-if(!useDrawSnapshot,18-19,294-322) == 1840747-1.html about:blank # here. That's tracked in bug 1840747. fuzzy-if(!useDrawSnapshot&&!swgl,254-255,110-121) fuzzy-if(useDrawSnapshot,18-18,93-93) fuzzy-if(swgl,19-19,58-58) == 1841355-1.html about:blank skip-if(((AddressSanitizer||ThreadSanitizer)&>kWidget)||(isDebugBuild&&Android)) fuzzy(0-123,0-1425) == 1878294-1.html 1878294-1-ref.html +== 1888941-text-transform-emergency-wrap.html 1888941-text-transform-emergency-wrap-ref.html diff --git a/layout/reftests/counters/counter-hebrew-reference.html b/layout/reftests/counters/counter-hebrew-reference.html index f2c1326056..0d08f6d49b 100644 --- a/layout/reftests/counters/counter-hebrew-reference.html +++ b/layout/reftests/counters/counter-hebrew-reference.html @@ -55,10 +55,7 @@ <span>ט׳</span> <span>ט׳תתקצט</span> <span>תתקצט׳תתקצט</span> - <style> - #c18:before { counter-set: c 18; content: counter(c, hebrew); } - </style> - <span id="c18"></span> + <span>1000000</span> </div> </body> diff --git a/layout/reftests/counters/counters-hebrew-reference.html b/layout/reftests/counters/counters-hebrew-reference.html index 7e725760e8..9ae6b5e011 100644 --- a/layout/reftests/counters/counters-hebrew-reference.html +++ b/layout/reftests/counters/counters-hebrew-reference.html @@ -12,54 +12,51 @@ <p></p> <div> <span>א.א</span> - <span>ב</span> - <span>ג</span> - <span>ד</span> - <span>ה</span> - <span>ו</span> - <span>ז</span> - <span>ח</span> - <span>ט</span> - <span>י</span> - <span>יא</span> - <span>יב</span> - <span>יג</span> - <span>יד</span> - <span>טו</span> - <span>טז</span> - <span>יז</span> - <span>יז.כ</span> - <span>יז.ל</span> - <span>יז.מ</span> - <span>יז.נ</span> - <span>יז.ס</span> - <span>יז.ע</span> - <span>יז.פ</span> - <span>יז.צ</span> - <span>יז.ק</span> - <span>יז.ר</span> - <span>יז.ש</span> - <span>יז.ת</span> - <span>יז.תק</span> - <span>יז.תר</span> - <span>יז.תש</span> - <span>יז.תת</span> - <span>יז.תתק</span> - <span>יז.א׳</span> - <span>יז.ב׳</span> - <span>יז.ג׳</span> - <span>יז.ד׳</span> - <span>יז.ה׳</span> - <span>יז.ו׳</span> - <span>יז.ז׳</span> - <span>יז.ח׳</span> - <span>יז.ט׳</span> - <span>יז.ט׳תתקצט</span> - <span>יז.תתקצט׳תתקצט</span> - <style> - #c18:before { counter-set: c 18; content: counter(c, hebrew); } - </style> - <span id="c18"></span> + <span>א.ב</span> + <span>א.ג</span> + <span>א.ד</span> + <span>א.ה</span> + <span>א.ו</span> + <span>א.ז</span> + <span>א.ח</span> + <span>א.ט</span> + <span>א.י</span> + <span>א.יא</span> + <span>א.יב</span> + <span>א.יג</span> + <span>א.יד</span> + <span>א.טו</span> + <span>א.טז</span> + <span>א.יז</span> + <span>א.כ</span> + <span>א.ל</span> + <span>א.מ</span> + <span>א.נ</span> + <span>א.ס</span> + <span>א.ע</span> + <span>א.פ</span> + <span>א.צ</span> + <span>א.ק</span> + <span>א.ר</span> + <span>א.ש</span> + <span>א.ת</span> + <span>א.תק</span> + <span>א.תר</span> + <span>א.תש</span> + <span>א.תת</span> + <span>א.תתק</span> + <span>א.א׳</span> + <span>א.ב׳</span> + <span>א.ג׳</span> + <span>א.ד׳</span> + <span>א.ה׳</span> + <span>א.ו׳</span> + <span>א.ז׳</span> + <span>א.ח׳</span> + <span>א.ט׳</span> + <span>א.ט׳תתקצט</span> + <span>א.תתקצט׳תתקצט</span> + <span>א.1000000</span> </div> </body> diff --git a/layout/reftests/counters/t1202-counter-06-b-reference.html b/layout/reftests/counters/t1202-counter-06-b-reference.html index 78c9300a8b..0fdc2905e2 100644 --- a/layout/reftests/counters/t1202-counter-06-b-reference.html +++ b/layout/reftests/counters/t1202-counter-06-b-reference.html @@ -24,8 +24,8 @@ <span>11</span> <span>12</span> <span>99</span> - <span>13</span> - <span>14</span> + <span>100</span> + <span>101</span> </div> </body> diff --git a/layout/reftests/counters/t1202-counter-07-b-reference.html b/layout/reftests/counters/t1202-counter-07-b-reference.html index b0da1fa597..f6429c211a 100644 --- a/layout/reftests/counters/t1202-counter-07-b-reference.html +++ b/layout/reftests/counters/t1202-counter-07-b-reference.html @@ -23,12 +23,12 @@ <span>xi</span> <span>xii</span> <span>xlix</span> - <span>xiii</span> + <span>l</span> <span>ccclxxxix</span> - <span>xiv</span> + <span>cccxc</span> <span>mmmcdlxxxix</span> - <span>xv</span> - <span>xvi</span> + <span>mmmcdxc</span> + <span>mmmcdxci</span> </div> </body> diff --git a/layout/reftests/counters/t1202-counter-08-b-reference.html b/layout/reftests/counters/t1202-counter-08-b-reference.html index 40a8bf8d53..16be49dcbc 100644 --- a/layout/reftests/counters/t1202-counter-08-b-reference.html +++ b/layout/reftests/counters/t1202-counter-08-b-reference.html @@ -23,12 +23,12 @@ <span>XI</span> <span>XII</span> <span>XLIX</span> - <span>XIII</span> + <span>L</span> <span>CCCLXXXIX</span> - <span>XIV</span> + <span>CCCXC</span> <span>MMMCDLXXXIX</span> - <span>XV</span> - <span>XVI</span> + <span>MMMCDXC</span> + <span>MMMCDXCI</span> </div> </body> diff --git a/layout/reftests/counters/t1202-counters-06-b-reference.html b/layout/reftests/counters/t1202-counters-06-b-reference.html index 7b3af2409f..b2b5ca1a07 100644 --- a/layout/reftests/counters/t1202-counters-06-b-reference.html +++ b/layout/reftests/counters/t1202-counters-06-b-reference.html @@ -13,20 +13,20 @@ <div> <span>01.01</span> - <span>02</span> - <span>03</span> - <span>04</span> - <span>05</span> - <span>06</span> - <span>07</span> - <span>08</span> - <span>09</span> - <span>10</span> - <span>11</span> - <span>12</span> - <span>12.99</span> - <span>13</span> - <span>14</span> + <span>01.02</span> + <span>01.03</span> + <span>01.04</span> + <span>01.05</span> + <span>01.06</span> + <span>01.07</span> + <span>01.08</span> + <span>01.09</span> + <span>01.10</span> + <span>01.11</span> + <span>01.12</span> + <span>01.99</span> + <span>01.100</span> + <span>01.101</span> </div> </body> diff --git a/layout/reftests/counters/t1202-counters-07-b-reference.html b/layout/reftests/counters/t1202-counters-07-b-reference.html index 49713c7344..ef08cfbefc 100644 --- a/layout/reftests/counters/t1202-counters-07-b-reference.html +++ b/layout/reftests/counters/t1202-counters-07-b-reference.html @@ -13,24 +13,24 @@ <div> <span>i.i</span> - <span>ii</span> - <span>iii</span> - <span>iv</span> - <span>v</span> - <span>vi</span> - <span>vii</span> - <span>viii</span> - <span>ix</span> - <span>x</span> - <span>xi</span> - <span>xii</span> - <span>xii.xlix</span> - <span>xiii</span> - <span>xiii.ccclxxxix</span> - <span>xiv</span> - <span>xiv.mmmcdlxxxix</span> - <span>xv</span> - <span>xvi</span> + <span>i.ii</span> + <span>i.iii</span> + <span>i.iv</span> + <span>i.v</span> + <span>i.vi</span> + <span>i.vii</span> + <span>i.viii</span> + <span>i.ix</span> + <span>i.x</span> + <span>i.xi</span> + <span>i.xii</span> + <span>i.xlix</span> + <span>i.l</span> + <span>i.ccclxxxix</span> + <span>i.cccxc</span> + <span>i.mmmcdlxxxix</span> + <span>i.mmmcdxc</span> + <span>i.mmmcdxci</span> </div> </body> diff --git a/layout/reftests/counters/t1202-counters-08-b-reference.html b/layout/reftests/counters/t1202-counters-08-b-reference.html index 92a6429164..7838d10fbb 100644 --- a/layout/reftests/counters/t1202-counters-08-b-reference.html +++ b/layout/reftests/counters/t1202-counters-08-b-reference.html @@ -13,24 +13,24 @@ <div> <span>I.I</span> - <span>II</span> - <span>III</span> - <span>IV</span> - <span>V</span> - <span>VI</span> - <span>VII</span> - <span>VIII</span> - <span>IX</span> - <span>X</span> - <span>XI</span> - <span>XII</span> - <span>XII.XLIX</span> - <span>XIII</span> - <span>XIII.CCCLXXXIX</span> - <span>XIV</span> - <span>XIV.MMMCDLXXXIX</span> - <span>XV</span> - <span>XVI</span> + <span>I.II</span> + <span>I.III</span> + <span>I.IV</span> + <span>I.V</span> + <span>I.VI</span> + <span>I.VII</span> + <span>I.VIII</span> + <span>I.IX</span> + <span>I.X</span> + <span>I.XI</span> + <span>I.XII</span> + <span>I.XLIX</span> + <span>I.L</span> + <span>I.CCCLXXXIX</span> + <span>I.CCCXC</span> + <span>I.MMMCDLXXXIX</span> + <span>I.MMMCDXC</span> + <span>I.MMMCDXCI</span> </div> </body> diff --git a/layout/reftests/counters/t1202-counters-09-b-reference.html b/layout/reftests/counters/t1202-counters-09-b-reference.html index 928e440ae1..0511df55e4 100644 --- a/layout/reftests/counters/t1202-counters-09-b-reference.html +++ b/layout/reftests/counters/t1202-counters-09-b-reference.html @@ -13,45 +13,45 @@ <div> <span>ა.ა</span> - <span>ბ</span> - <span>გ</span> - <span>დ</span> - <span>ე</span> - <span>ვ</span> - <span>ზ</span> - <span>ჱ</span> - <span>თ</span> - <span>ი</span> - <span>ია</span> - <span>იბ</span> - <span>იბ.კ</span> - <span>იბ.ლ</span> - <span>იბ.მ</span> - <span>იბ.ნ</span> - <span>იბ.ჲ</span> - <span>იბ.ო</span> - <span>იბ.პ</span> - <span>იბ.ჟ</span> - <span>იბ.რ</span> - <span>იბ.ს</span> - <span>იბ.ტ</span> - <span>იბ.ჳ</span> - <span>იბ.ფ</span> - <span>იბ.ქ</span> - <span>იბ.ღ</span> - <span>იბ.ყ</span> - <span>იბ.შ</span> - <span>იბ.ჩ</span> - <span>იბ.ც</span> - <span>იბ.ძ</span> - <span>იბ.წ</span> - <span>იბ.ჭ</span> - <span>იბ.ხ</span> - <span>იბ.ჴ</span> - <span>იბ.ჯ</span> - <span>იბ.ჰ</span> - <span>იბ.ჵ</span> - <span>იბ.ჵჰშჟთ</span> + <span>ა.ბ</span> + <span>ა.გ</span> + <span>ა.დ</span> + <span>ა.ე</span> + <span>ა.ვ</span> + <span>ა.ზ</span> + <span>ა.ჱ</span> + <span>ა.თ</span> + <span>ა.ი</span> + <span>ა.ია</span> + <span>ა.იბ</span> + <span>ა.კ</span> + <span>ა.ლ</span> + <span>ა.მ</span> + <span>ა.ნ</span> + <span>ა.ჲ</span> + <span>ა.ო</span> + <span>ა.პ</span> + <span>ა.ჟ</span> + <span>ა.რ</span> + <span>ა.ს</span> + <span>ა.ტ</span> + <span>ა.ჳ</span> + <span>ა.ფ</span> + <span>ა.ქ</span> + <span>ა.ღ</span> + <span>ა.ყ</span> + <span>ა.შ</span> + <span>ა.ჩ</span> + <span>ა.ც</span> + <span>ა.ძ</span> + <span>ა.წ</span> + <span>ა.ჭ</span> + <span>ა.ხ</span> + <span>ა.ჴ</span> + <span>ა.ჯ</span> + <span>ა.ჰ</span> + <span>ა.ჵ</span> + <span>ა.ჵჰშჟთ</span> </div> </body> diff --git a/layout/reftests/counters/t1202-counters-10-b-reference.html b/layout/reftests/counters/t1202-counters-10-b-reference.html index f3a3fa6dfa..af593dab5c 100644 --- a/layout/reftests/counters/t1202-counters-10-b-reference.html +++ b/layout/reftests/counters/t1202-counters-10-b-reference.html @@ -13,44 +13,44 @@ <div> <span>Ա.Ա</span> - <span>Բ</span> - <span>Գ</span> - <span>Դ</span> - <span>Ե</span> - <span>Զ</span> - <span>Է</span> - <span>Ը</span> - <span>Թ</span> - <span>Ժ</span> - <span>ԺԱ</span> - <span>ԺԲ</span> - <span>ԺԲ.Ի</span> - <span>ԺԲ.Լ</span> - <span>ԺԲ.Խ</span> - <span>ԺԲ.Ծ</span> - <span>ԺԲ.Կ</span> - <span>ԺԲ.Հ</span> - <span>ԺԲ.Ձ</span> - <span>ԺԲ.Ղ</span> - <span>ԺԲ.Ճ</span> - <span>ԺԲ.Մ</span> - <span>ԺԲ.Յ</span> - <span>ԺԲ.Ն</span> - <span>ԺԲ.Շ</span> - <span>ԺԲ.Ո</span> - <span>ԺԲ.Չ</span> - <span>ԺԲ.Պ</span> - <span>ԺԲ.Ջ</span> - <span>ԺԲ.Ռ</span> - <span>ԺԲ.Ս</span> - <span>ԺԲ.Վ</span> - <span>ԺԲ.Տ</span> - <span>ԺԲ.Ր</span> - <span>ԺԲ.Ց</span> - <span>ԺԲ.Ւ</span> - <span>ԺԲ.Փ</span> - <span>ԺԲ.Ք</span> - <span>ԺԲ.ՔՋՂԹ</span> + <span>Ա.Բ</span> + <span>Ա.Գ</span> + <span>Ա.Դ</span> + <span>Ա.Ե</span> + <span>Ա.Զ</span> + <span>Ա.Է</span> + <span>Ա.Ը</span> + <span>Ա.Թ</span> + <span>Ա.Ժ</span> + <span>Ա.ԺԱ</span> + <span>Ա.ԺԲ</span> + <span>Ա.Ի</span> + <span>Ա.Լ</span> + <span>Ա.Խ</span> + <span>Ա.Ծ</span> + <span>Ա.Կ</span> + <span>Ա.Հ</span> + <span>Ա.Ձ</span> + <span>Ա.Ղ</span> + <span>Ա.Ճ</span> + <span>Ա.Մ</span> + <span>Ա.Յ</span> + <span>Ա.Ն</span> + <span>Ա.Շ</span> + <span>Ա.Ո</span> + <span>Ա.Չ</span> + <span>Ա.Պ</span> + <span>Ա.Ջ</span> + <span>Ա.Ռ</span> + <span>Ա.Ս</span> + <span>Ա.Վ</span> + <span>Ա.Տ</span> + <span>Ա.Ր</span> + <span>Ա.Ց</span> + <span>Ա.Ւ</span> + <span>Ա.Փ</span> + <span>Ա.Ք</span> + <span>Ա.ՔՋՂԹ</span> </div> </body> diff --git a/layout/reftests/counters/t1204-reset-00-c-o-reference.html b/layout/reftests/counters/t1204-reset-00-c-o-reference.html index 3be209b7e8..e9afee0b67 100644 --- a/layout/reftests/counters/t1204-reset-00-c-o-reference.html +++ b/layout/reftests/counters/t1204-reset-00-c-o-reference.html @@ -9,7 +9,7 @@ </head> <body> - <div id="reference"><span>1-</span><span>2-</span><span>3-</span></div> + <div id="reference"><span>1-</span><span>1.1-</span><span>1.2-</span></div> </body> </html> diff --git a/layout/reftests/counters/t120401-scope-01-c-reference.html b/layout/reftests/counters/t120401-scope-01-c-reference.html index 021366aaed..c7488ce4b3 100644 --- a/layout/reftests/counters/t120401-scope-01-c-reference.html +++ b/layout/reftests/counters/t120401-scope-01-c-reference.html @@ -10,7 +10,7 @@ <body> - <div><span>B</span><span>1</span><span>-</span><span>B</span><span>2</span><span>-</span><span>B</span><span>2.1</span><span>-</span><span>B</span><span>2.2</span><span>-</span><span>A</span><span>2.3</span><span>-</span><span>B</span><span>2.4</span><span>-</span><span>A</span><span>2.5</span><span>-</span><span>A</span><span>2.6</span><span>-</span><span>B</span><span>3</span><span>-</span><span>B</span><span>4</span><span>-</span><span>A</span><span>5</span><span>-</span><span>A</span><span>6</span><span>-</span><span>A</span><span>7</span><span>-</span><span>A</span><span>8</span><span>-</span></div> + <div><span>B</span><span>1</span><span>-</span><span>B</span><span>2</span><span>-</span><span>B</span><span>2.1</span><span>-</span><span>B</span><span>2.2</span><span>-</span><span>A</span><span>2.3</span><span>-</span><span>B</span><span>2.4</span><span>-</span><span>A</span><span>2.5</span><span>-</span><span>A</span><span>2.6</span><span>-</span><span>B</span><span>2.7</span><span>-</span><span>B</span><span>2.8</span><span>-</span><span>A</span><span>2.9</span><span>-</span><span>A</span><span>2.10</span><span>-</span><span>A</span><span>2.11</span><span>-</span><span>A</span><span>3</span><span>-</span></div> </body> </html> diff --git a/layout/reftests/counters/t120401-scope-02-c-reference.html b/layout/reftests/counters/t120401-scope-02-c-reference.html index 41fc79875a..ff2281a8cf 100644 --- a/layout/reftests/counters/t120401-scope-02-c-reference.html +++ b/layout/reftests/counters/t120401-scope-02-c-reference.html @@ -10,7 +10,7 @@ <body> - <div><span>B</span><span>1</span><span>-</span><span>B</span><span>1.1</span><span>-</span><span>B</span><span>2</span><span>-</span><span>A</span><span>3</span><span>-</span><span>A</span><span>4</span><span>-</span><span>A</span><span>5</span><span>-</span></div> + <div><span>B</span><span>1</span><span>-</span><span>B</span><span>1.1</span><span>-</span><span>B</span><span>1.2</span><span>-</span><span>A</span><span>1.3</span><span>-</span><span>A</span><span>1.4</span><span>-</span><span>A</span><span>2</span><span>-</span></div> </body> </html> diff --git a/layout/reftests/css-gradients/reftest.list b/layout/reftests/css-gradients/reftest.list index 1574996fa3..0803f7ec35 100644 --- a/layout/reftests/css-gradients/reftest.list +++ b/layout/reftests/css-gradients/reftest.list @@ -47,8 +47,8 @@ fuzzy-if(Android,0-8,0-771) == radial-shape-farthest-corner-1a.html radial-shape fails-if(gtkWidget) fuzzy(0-2,0-500) == radial-shape-farthest-corner-1b.html radial-shape-farthest-corner-1-ref.html fuzzy(0-2,0-15000) fuzzy-if(Android,0-17,0-13320) == radial-shape-farthest-side-1a.html radial-shape-farthest-side-1-ref.html fuzzy(0-2,0-15000) fuzzy-if(Android,0-17,0-13320) == radial-shape-farthest-side-1b.html radial-shape-farthest-side-1-ref.html -== radial-size-1a.html radial-size-1-ref.html -== radial-size-1b.html radial-size-1-ref.html +fuzzy(0-1,0-100) == radial-size-1a.html radial-size-1-ref.html +fuzzy(0-1,0-100) == radial-size-1b.html radial-size-1-ref.html fuzzy-if(Android,0-4,0-248) == radial-zero-length-1a.html radial-zero-length-1-ref.html fuzzy-if(Android,0-4,0-248) == radial-zero-length-1b.html radial-zero-length-1-ref.html fuzzy-if(Android,0-4,0-248) == radial-zero-length-1c.html radial-zero-length-1-ref.html diff --git a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-001-ref.html b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-001-ref.html index a157cf074a..20c088ab02 100644 --- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-001-ref.html +++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-001-ref.html @@ -51,7 +51,7 @@ for (var i = 0; i < coltest.length; ++i) { img.setAttribute("src","support/lime-24x2.png"); img.setAttribute("style",coltest[i] + "; min-width:"+ results[i]); grid.appendChild(img); - document.body.appendChild(document.createTextNode("24x2.png -- "+coltest[i]+'\n')); + document.body.appendChild(document.createTextNode(`Test ${i+1}: 24x2.png -- ${coltest[i]}`)); document.body.appendChild(document.createElement('br')); document.body.appendChild(grid); document.body.appendChild(document.createElement('br')); diff --git a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-001.html b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-001.html index 93a251be43..9565253b50 100644 --- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-001.html +++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-001.html @@ -52,7 +52,7 @@ for (var i = 0; i < coltest.length; ++i) { img.setAttribute("src","support/lime-24x2.png"); img.setAttribute("style",coltest[i]); grid.appendChild(img); - document.body.appendChild(document.createTextNode("24x2.png -- "+coltest[i]+'\n')); + document.body.appendChild(document.createTextNode(`Test ${i+1}: 24x2.png -- ${coltest[i]}`)); document.body.appendChild(document.createElement('br')); document.body.appendChild(grid); document.body.appendChild(document.createElement('br')); diff --git a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-002-ref.html b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-002-ref.html index 183f00e24f..2b09eb11ee 100644 --- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-002-ref.html +++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-002-ref.html @@ -55,7 +55,7 @@ for (var i = 0; i < coltest.length; ++i) { img.setAttribute("src","support/lime-24x2.png"); img.setAttribute("style",coltest[i] + "; width:"+ item_width[i]); grid.appendChild(img); - document.body.appendChild(document.createTextNode("24x2.png -- "+coltest[i]+'\n')); + document.body.appendChild(document.createTextNode(`Test ${i+1}: 24x2.png -- ${coltest[i]}`)); document.body.appendChild(document.createElement('br')); document.body.appendChild(grid); document.body.appendChild(document.createElement('br')); diff --git a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-002.html b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-002.html index 8809d5b06e..ee69018a93 100644 --- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-002.html +++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-002.html @@ -53,7 +53,7 @@ for (var i = 0; i < coltest.length; ++i) { img.setAttribute("src","support/lime-24x2.png"); img.setAttribute("style",coltest[i]); grid.appendChild(img); - document.body.appendChild(document.createTextNode("24x2.png -- "+coltest[i]+'\n')); + document.body.appendChild(document.createTextNode(`Test ${i+1}: 24x2.png -- ${coltest[i]}`)); document.body.appendChild(document.createElement('br')); document.body.appendChild(grid); document.body.appendChild(document.createElement('br')); diff --git a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-003-ref.html b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-003-ref.html index 9ec975d62d..e051d4c3ef 100644 --- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-003-ref.html +++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-003-ref.html @@ -52,7 +52,7 @@ for (var i = 0; i < rowtest.length; ++i) { img.setAttribute("src","support/lime-2x24.png"); img.setAttribute("style",rowtest[i] + "; min-height:"+ results[i]); grid.appendChild(img); - document.body.appendChild(document.createTextNode("2x24.png -- "+rowtest[i]+'\n')); + document.body.appendChild(document.createTextNode(`Test ${i+1}: 24x2.png -- ${rowtest[i]}`)); document.body.appendChild(document.createElement('br')); document.body.appendChild(grid); document.body.appendChild(document.createElement('br')); diff --git a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-003.html b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-003.html index ae34753eba..659204c992 100644 --- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-003.html +++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-003.html @@ -51,7 +51,7 @@ for (var i = 0; i < rowtest.length; ++i) { img.setAttribute("src","support/lime-2x24.png"); img.setAttribute("style",rowtest[i]); grid.appendChild(img); - document.body.appendChild(document.createTextNode("2x24.png -- "+rowtest[i]+'\n')); + document.body.appendChild(document.createTextNode(`Test ${i+1}: 24x2.png -- ${rowtest[i]}`)); document.body.appendChild(document.createElement('br')); document.body.appendChild(grid); document.body.appendChild(document.createElement('br')); diff --git a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004-ref.html b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004-ref.html index 6533c97b67..c1022e53dd 100644 --- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004-ref.html +++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004-ref.html @@ -53,7 +53,7 @@ for (var i = 0; i < rowtest.length; ++i) { img.setAttribute("src","support/lime-2x24.png"); img.setAttribute("style",rowtest[i] + "; max-height:auto; height:"+ item_height[i]); grid.appendChild(img); - document.body.appendChild(document.createTextNode("2x24.png -- "+rowtest[i]+'\n')); + document.body.appendChild(document.createTextNode(`Test ${i+1}: 24x2.png -- ${rowtest[i]}`)); document.body.appendChild(document.createElement('br')); document.body.appendChild(grid); document.body.appendChild(document.createElement('br')); diff --git a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004.html b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004.html index c2b650525c..dab114939b 100644 --- a/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004.html +++ b/layout/reftests/css-grid/grid-auto-min-sizing-min-content-min-size-004.html @@ -52,7 +52,7 @@ for (var i = 0; i < rowtest.length; ++i) { img.setAttribute("src","support/lime-2x24.png"); img.setAttribute("style",rowtest[i]); grid.appendChild(img); - document.body.appendChild(document.createTextNode("2x24.png -- "+rowtest[i]+'\n')); + document.body.appendChild(document.createTextNode(`Test ${i+1}: 24x2.png -- ${rowtest[i]}`)); document.body.appendChild(document.createElement('br')); document.body.appendChild(grid); document.body.appendChild(document.createElement('br')); diff --git a/layout/reftests/dom/reftest.list b/layout/reftests/dom/reftest.list index 64ac914010..897af84d11 100644 --- a/layout/reftests/dom/reftest.list +++ b/layout/reftests/dom/reftest.list @@ -28,7 +28,7 @@ # a range insert and an append == insertmultiplemultiple-2.html insertmultiplemultiple-ref.html # multiple range inserts and an append -== insertmultiplemultiple-2.html insertmultiplemultiple-ref.html +== insertmultiplemultiple-3.html insertmultiplemultiple-ref.html # testing bindings that have multiple insertion points == multipleinsertionpoints-ref2-shadow.xhtml multipleinsertionpoints-ref.xhtml diff --git a/layout/reftests/flexbox/reftest.list b/layout/reftests/flexbox/reftest.list index dbd6c705cd..b96dbf4fce 100644 --- a/layout/reftests/flexbox/reftest.list +++ b/layout/reftests/flexbox/reftest.list @@ -75,7 +75,6 @@ fuzzy(0-3,0-10) == flexbox-dyn-insertAroundSpan-3.xhtml flexbox-dyn-insertAround == flexbox-position-absolute-2.xhtml flexbox-position-absolute-2-ref.xhtml == flexbox-position-absolute-3.xhtml flexbox-position-absolute-3-ref.xhtml == flexbox-position-absolute-4.xhtml flexbox-position-absolute-4-ref.xhtml -== flexbox-position-fixed-3.xhtml flexbox-position-fixed-3-ref.xhtml fuzzy-if(Android,0-16,0-400) == flexbox-position-fixed-1.xhtml flexbox-position-fixed-1-ref.xhtml fuzzy-if(Android,0-16,0-400) == flexbox-position-fixed-2.xhtml flexbox-position-fixed-2-ref.xhtml == flexbox-position-fixed-3.xhtml flexbox-position-fixed-3-ref.xhtml diff --git a/layout/reftests/forms/input/file/reftest.list b/layout/reftests/forms/input/file/reftest.list index d8276cb941..f16315f5a7 100644 --- a/layout/reftests/forms/input/file/reftest.list +++ b/layout/reftests/forms/input/file/reftest.list @@ -5,7 +5,7 @@ fuzzy(0-1,0-10) == background.html chrome://reftest/content/forms/input/file/bac fuzzy-if(gtkWidget,0-1,0-10) == style.html chrome://reftest/content/forms/input/file/style-ref.xhtml != width-clip.html width-clip-ref.html == color-inherit.html color-inherit-ref.html -pref(widget.non-native-theme.webrender,true) fuzzy(0-1,0-5) fuzzy-if(cocoaWidget,0-46,0-134) == dynamic-max-width.html dynamic-max-width-ref.html # bug 1496542 for webrender, bug 1724582 for appleSilicon +fuzzy(0-1,0-5) fuzzy-if(cocoaWidget,0-46,0-134) == dynamic-max-width.html dynamic-max-width-ref.html # bug 1496542 for webrender, bug 1724582 for appleSilicon == label-min-inline-size.html label-min-inline-size-ref.html == css-overflow.html css-overflow-ref.html == css-display.html css-display-ref.html diff --git a/layout/reftests/forms/input/range/1887539-ref.html b/layout/reftests/forms/input/range/1887539-ref.html new file mode 100644 index 0000000000..b539a1b5c2 --- /dev/null +++ b/layout/reftests/forms/input/range/1887539-ref.html @@ -0,0 +1,30 @@ +<!doctype html> +<style> +#scrubber { + height: 100px; + width: 100%; + appearance: none; + background-color: black; + box-sizing: border-box; + padding: 6px 2px; + margin: 0; + + &::-moz-range-thumb { + border-radius: 14px; + background-color: #BFBFC9; + width: 8px; + height: 8px; + border: 3px solid white; + padding: 0; + } + + &::-moz-range-track { + background-color: #969696; + } + + &::-moz-range-progress { + background-color: white; + } +} +</style> +<input type="range" id="scrubber" value="0.5" min="0" max="1" step=".001"> diff --git a/layout/reftests/forms/input/range/1887539.html b/layout/reftests/forms/input/range/1887539.html new file mode 100644 index 0000000000..9472af203e --- /dev/null +++ b/layout/reftests/forms/input/range/1887539.html @@ -0,0 +1,35 @@ +<!doctype html> +<style> +#scrubber { + height: 100px; + width: 100%; + appearance: none; + background-color: black; + box-sizing: border-box; + padding: 6px 2px; + margin: 0; + + &::-moz-range-thumb { + border-radius: 14px; + background-color: #BFBFC9; + width: 8px; + height: 8px; + border: 3px solid white; + padding: 0; + } + + &::-moz-range-track { + background-color: #969696; + } + + &::-moz-range-progress { + background-color: white; + } +} +</style> +<input type="range" id="scrubber" value="0" min="0" max="1" step=".001"> +<script> +let scrubber = document.getElementById('scrubber'); +scrubber.getBoundingClientRect(); +scrubber.value = 0.5; +</script> diff --git a/layout/reftests/forms/input/range/reftest.list b/layout/reftests/forms/input/range/reftest.list index 181097dc15..f98f345b95 100644 --- a/layout/reftests/forms/input/range/reftest.list +++ b/layout/reftests/forms/input/range/reftest.list @@ -59,3 +59,5 @@ skip-if(Android) == range-border-background.html range-border-background-ref.htm fails-if(Android) fuzzy(0-2,0-80) == auto-size.html auto-size-ref.html # Snapping, bug 1621141 == range-track-bg.html range-track-bg-ref.html != track-default-rendering.html track-default-rendering-ref.html + +== 1887539.html 1887539-ref.html diff --git a/layout/reftests/forms/input/text/autofill-author-background.html b/layout/reftests/forms/input/text/autofill-author-background.html index 691adaa1ff..73eb71ea7c 100644 --- a/layout/reftests/forms/input/text/autofill-author-background.html +++ b/layout/reftests/forms/input/text/autofill-author-background.html @@ -2,8 +2,8 @@ <input type=text style="background-color: red; background-image: linear-gradient(red, blue);"> <script> let input = SpecialPowers.wrap(document.querySelector("input")); - SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutofillField(input); - input.getBoundingClientRect(); // previewValue setter depends on the reframe posted by markAsAutofillField() having being processed... + SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutoCompletableField(input); + input.getBoundingClientRect(); // previewValue setter depends on the reframe posted by markAsAutoCompletableField() having being processed... input.previewValue = "Autofill"; SpecialPowers.wrap(window).windowUtils.addManuallyManagedState(input, "-moz-autofill-preview"); </script> diff --git a/layout/reftests/forms/input/text/autofill-blank.html b/layout/reftests/forms/input/text/autofill-blank.html index 966d314038..17919875ee 100644 --- a/layout/reftests/forms/input/text/autofill-blank.html +++ b/layout/reftests/forms/input/text/autofill-blank.html @@ -2,7 +2,7 @@ <input type=text> <script> let input = SpecialPowers.wrap(document.querySelector("input")); - SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutofillField(input); + SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutoCompletableField(input); input.getBoundingClientRect(); SpecialPowers.wrap(window).windowUtils.addManuallyManagedState(input, "autofill"); </script> diff --git a/layout/reftests/forms/input/text/autofill-prefilled-value.html b/layout/reftests/forms/input/text/autofill-prefilled-value.html index 42924ac531..07b4ce5310 100644 --- a/layout/reftests/forms/input/text/autofill-prefilled-value.html +++ b/layout/reftests/forms/input/text/autofill-prefilled-value.html @@ -2,8 +2,8 @@ <input type=text value="JOHN DOE"> <script> let input = SpecialPowers.wrap(document.querySelector("input")); - SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutofillField(input); - input.getBoundingClientRect(); // previewValue setter depends on the reframe posted by markAsAutofillField() having being processed... + SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutoCompletableField(input); + input.getBoundingClientRect(); // previewValue setter depends on the reframe posted by markAsAutoCompletableField() having being processed... input.previewValue = "Autofill"; SpecialPowers.wrap(window).windowUtils.addManuallyManagedState(input, "-moz-autofill-preview"); </script> diff --git a/layout/reftests/forms/input/text/autofill-preview-blank.html b/layout/reftests/forms/input/text/autofill-preview-blank.html index a235b7430f..e441b80700 100644 --- a/layout/reftests/forms/input/text/autofill-preview-blank.html +++ b/layout/reftests/forms/input/text/autofill-preview-blank.html @@ -2,7 +2,7 @@ <input type=text> <script> let input = SpecialPowers.wrap(document.querySelector("input")); - SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutofillField(input); + SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutoCompletableField(input); input.getBoundingClientRect(); SpecialPowers.wrap(window).windowUtils.addManuallyManagedState(input, "-moz-autofill-preview"); </script> diff --git a/layout/reftests/forms/input/text/autofill-preview-line-height.html b/layout/reftests/forms/input/text/autofill-preview-line-height.html index 7ce1cadb2e..40b63e9ede 100644 --- a/layout/reftests/forms/input/text/autofill-preview-line-height.html +++ b/layout/reftests/forms/input/text/autofill-preview-line-height.html @@ -5,8 +5,8 @@ <input value="Autofill"> <script> let input = SpecialPowers.wrap(document.querySelector("input")); - SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutofillField(input); - input.getBoundingClientRect(); // previewValue setter depends on the reframe posted by markAsAutofillField() having being processed... + SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutoCompletableField(input); + input.getBoundingClientRect(); // previewValue setter depends on the reframe posted by markAsAutoCompletableField() having being processed... input.previewValue = "Autofill"; SpecialPowers.wrap(window).windowUtils.addManuallyManagedState(input, "-moz-autofill-preview"); </script> diff --git a/layout/reftests/forms/input/text/autofill-preview.html b/layout/reftests/forms/input/text/autofill-preview.html index 1382d29abd..540c07423f 100644 --- a/layout/reftests/forms/input/text/autofill-preview.html +++ b/layout/reftests/forms/input/text/autofill-preview.html @@ -2,8 +2,8 @@ <input type=text> <script> let input = SpecialPowers.wrap(document.querySelector("input")); - SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutofillField(input); - input.getBoundingClientRect(); // previewValue setter depends on the reframe posted by markAsAutofillField() having being processed... + SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutoCompletableField(input); + input.getBoundingClientRect(); // previewValue setter depends on the reframe posted by markAsAutoCompletableField() having being processed... input.previewValue = "Autofill"; SpecialPowers.wrap(window).windowUtils.addManuallyManagedState(input, "-moz-autofill-preview"); </script> diff --git a/layout/reftests/forms/input/text/autofill.html b/layout/reftests/forms/input/text/autofill.html index ccb2b15b1b..0d9c6a0588 100644 --- a/layout/reftests/forms/input/text/autofill.html +++ b/layout/reftests/forms/input/text/autofill.html @@ -2,8 +2,8 @@ <input type=text> <script> let input = SpecialPowers.wrap(document.querySelector("input")); - SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutofillField(input); - input.getBoundingClientRect(); // previewValue setter depends on the reframe posted by markAsAutofillField() having being processed... + SpecialPowers.Cc["@mozilla.org/satchel/form-fill-controller;1"].getService(SpecialPowers.Ci.nsIFormFillController).markAsAutoCompletableField(input); + input.getBoundingClientRect(); // previewValue setter depends on the reframe posted by markAsAutoCompletableField() having being processed... input.previewValue = "Autofill"; SpecialPowers.wrap(window).windowUtils.addManuallyManagedState(input, "autofill"); </script> diff --git a/layout/reftests/forms/input/text/reftest.list b/layout/reftests/forms/input/text/reftest.list index 6c649a5196..e80b881246 100644 --- a/layout/reftests/forms/input/text/reftest.list +++ b/layout/reftests/forms/input/text/reftest.list @@ -22,5 +22,7 @@ fuzzy(0-1,0-500) needs-focus == select.html select-ref.html == pseudo-class-lock.html pseudo-class-lock-ref.html +pref(layout.forms.reveal-password-button.enabled,true) != suppress-password-button.html suppress-password-button-notref.html + needs-focus == focus-on-anchor.html#anchor focus-on-anchor-ref.html needs-focus == select-overflow.html select-overflow-ref.html diff --git a/layout/reftests/forms/input/text/suppress-password-button-notref.html b/layout/reftests/forms/input/text/suppress-password-button-notref.html new file mode 100644 index 0000000000..c0d6fc108a --- /dev/null +++ b/layout/reftests/forms/input/text/suppress-password-button-notref.html @@ -0,0 +1 @@ +<input type="password" value="abc"> diff --git a/layout/reftests/forms/input/text/suppress-password-button.html b/layout/reftests/forms/input/text/suppress-password-button.html new file mode 100644 index 0000000000..53398e20df --- /dev/null +++ b/layout/reftests/forms/input/text/suppress-password-button.html @@ -0,0 +1 @@ +<input type="password" value="abc" style="appearance: textfield"> diff --git a/layout/reftests/forms/textarea/in-ltr-doc-scrollbar.html b/layout/reftests/forms/textarea/in-ltr-doc-scrollbar.html index e6b14358ec..c0b294ce73 100644 --- a/layout/reftests/forms/textarea/in-ltr-doc-scrollbar.html +++ b/layout/reftests/forms/textarea/in-ltr-doc-scrollbar.html @@ -5,6 +5,9 @@ <title></title> </head> <body> - <textarea cols=20 rows=4 style="overflow: scroll; resize: none"></textarea> + <textarea cols=20 rows=4 + style="overflow: scroll; + resize: none; + scrollbar-color: pink blue"></textarea> </body> </html> diff --git a/layout/reftests/forms/textarea/in-rtl-doc-scrollbar.html b/layout/reftests/forms/textarea/in-rtl-doc-scrollbar.html index 8c915b5ee9..5700b37fc2 100644 --- a/layout/reftests/forms/textarea/in-rtl-doc-scrollbar.html +++ b/layout/reftests/forms/textarea/in-rtl-doc-scrollbar.html @@ -5,6 +5,10 @@ <title></title> </head> <body> - <textarea cols=20 rows=4 style="float: left; overflow: scroll; resize: none"></textarea> + <textarea cols=20 rows=4 + style="float: left; + overflow: scroll; + resize: none; + scrollbar-color: pink blue"></textarea> </body> </html> diff --git a/layout/reftests/forms/textarea/ltr-scrollbar.html b/layout/reftests/forms/textarea/ltr-scrollbar.html index 927fbede66..04384ff9d6 100644 --- a/layout/reftests/forms/textarea/ltr-scrollbar.html +++ b/layout/reftests/forms/textarea/ltr-scrollbar.html @@ -5,6 +5,9 @@ <title></title> </head> <body> - <textarea dir="ltr" cols=20 rows=4 style="overflow: scroll; resize: none"></textarea> + <textarea dir="ltr" cols=20 rows=4 + style="overflow: scroll; + resize: none; + scrollbar-color: pink blue"></textarea> </body> </html> diff --git a/layout/reftests/forms/textarea/rtl-scrollbar.html b/layout/reftests/forms/textarea/rtl-scrollbar.html index 2770dc6941..63462f1ce8 100644 --- a/layout/reftests/forms/textarea/rtl-scrollbar.html +++ b/layout/reftests/forms/textarea/rtl-scrollbar.html @@ -5,6 +5,9 @@ <title></title> </head> <body> - <textarea dir="rtl" cols=20 rows=4 style="overflow: scroll; resize: none"></textarea> + <textarea dir="rtl" cols=20 rows=4 + style="overflow: scroll; + resize: none; + scrollbar-color: pink blue"></textarea> </body> </html> diff --git a/layout/reftests/marquee/336736-1a-ref.html b/layout/reftests/marquee/336736-1a-ref.html new file mode 100644 index 0000000000..690992b8a5 --- /dev/null +++ b/layout/reftests/marquee/336736-1a-ref.html @@ -0,0 +1,5 @@ +<html> +<body dir="rtl"> +<div style="background: green; width: 50px"> </div> +</body> +</html> diff --git a/layout/reftests/marquee/336736-1-ref.html b/layout/reftests/marquee/336736-1b-ref.html index 116e5ade28..116e5ade28 100644 --- a/layout/reftests/marquee/336736-1-ref.html +++ b/layout/reftests/marquee/336736-1b-ref.html diff --git a/layout/reftests/marquee/reftest.list b/layout/reftests/marquee/reftest.list index ac6772f6c9..c7d2fafd30 100644 --- a/layout/reftests/marquee/reftest.list +++ b/layout/reftests/marquee/reftest.list @@ -1,6 +1,6 @@ == 166591-dynamic-1.html 166591-dynamic-1-ref.html -fuzzy-if(Android,0-8,0-50) == 336736-1a.html 336736-1-ref.html -fuzzy-if(Android,0-8,0-50) == 336736-1b.html 336736-1-ref.html +fuzzy-if(Android,0-8,0-50) == 336736-1a.html 336736-1a-ref.html +fuzzy-if(Android,0-8,0-50) == 336736-1b.html 336736-1b-ref.html == 406073-1.html 406073-1-ref.html == 407016-2.html 407016-2-ref.html fuzzy-if(Android,0-8,0-220) == 413027-4.html 413027-4-ref.html diff --git a/layout/reftests/moz.build b/layout/reftests/moz.build index 14b30d92a3..261ea6db09 100644 --- a/layout/reftests/moz.build +++ b/layout/reftests/moz.build @@ -171,8 +171,6 @@ with Files("native-theme/**"): BUG_COMPONENT = ("Core", "Layout") with Files("object/**"): BUG_COMPONENT = ("Core", "DOM: Core & HTML") -with Files("ogg-video/**"): - BUG_COMPONENT = ("Core", "Audio/Video") with Files("outline/**"): BUG_COMPONENT = ("Core", "Layout") with Files("pagination/**"): diff --git a/layout/reftests/ogg-video/444-1-ref.html b/layout/reftests/ogg-video/444-1-ref.html deleted file mode 100644 index 08e4df28ee..0000000000 --- a/layout/reftests/ogg-video/444-1-ref.html +++ /dev/null @@ -1,20 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<body> -<video id="v1" style="position:absolute; left:0; top:0"></video> -<!-- hide bottom of video --> -<div style="position:absolute; top:120px; left:0; right:0; bottom:0; background:black"></div> -<script> -function doTest() { - // Set source now so that the loadeddata event can't fire before - // this function runs. - v1.src = "seek420.ogv"; - v1.play(); - v1.addEventListener("loadeddata", function() { - setTimeout(function() { - document.documentElement.removeAttribute('class'); - }, 50); - }); -} -document.addEventListener("MozReftestInvalidate", doTest); -</script> diff --git a/layout/reftests/ogg-video/444-1.html b/layout/reftests/ogg-video/444-1.html deleted file mode 100644 index 02e7b3ddb8..0000000000 --- a/layout/reftests/ogg-video/444-1.html +++ /dev/null @@ -1,20 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<body> -<video id="v1" style="position:absolute; left:0; top:0"></video> -<!-- hide bottom of video --> -<div style="position:absolute; top:120px; left:0; right:0; bottom:0; background:black"></div> -<script> -function doTest() { - // Set source now so that the loadeddata event can't fire before - // this function runs. - v1.src = "seek444.ogv"; - v1.play(); - v1.addEventListener("loadeddata", function() { - setTimeout(function() { - document.documentElement.removeAttribute('class'); - }, 50); - }); -} -document.addEventListener("MozReftestInvalidate", doTest); -</script> diff --git a/layout/reftests/ogg-video/aspect-ratio-1-ref.html b/layout/reftests/ogg-video/aspect-ratio-1-ref.html deleted file mode 100644 index 93391ed83c..0000000000 --- a/layout/reftests/ogg-video/aspect-ratio-1-ref.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<div style="width:140px; height:100px; position:relative; left:100px; top:100px; background:black;"></div> -</body> -</html> diff --git a/layout/reftests/ogg-video/aspect-ratio-1a.xhtml b/layout/reftests/ogg-video/aspect-ratio-1a.xhtml deleted file mode 100644 index c70d3248f5..0000000000 --- a/layout/reftests/ogg-video/aspect-ratio-1a.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<video src="black140x100.ogv" style="width:340px; height:100px; position:relative; top:100px;"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/aspect-ratio-1b.xhtml b/layout/reftests/ogg-video/aspect-ratio-1b.xhtml deleted file mode 100644 index b59352b516..0000000000 --- a/layout/reftests/ogg-video/aspect-ratio-1b.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<video src="black140x100.ogv" style="width:140px; height:300px; position:relative; left:100px;"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/aspect-ratio-2-ref.html b/layout/reftests/ogg-video/aspect-ratio-2-ref.html deleted file mode 100644 index 4f78ab4d2f..0000000000 --- a/layout/reftests/ogg-video/aspect-ratio-2-ref.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<div style="width:280px; height:200px; position:relative; left:200px; top:200px; background:black;"></div> -</body> -</html> diff --git a/layout/reftests/ogg-video/aspect-ratio-2a.xhtml b/layout/reftests/ogg-video/aspect-ratio-2a.xhtml deleted file mode 100644 index f708ec90af..0000000000 --- a/layout/reftests/ogg-video/aspect-ratio-2a.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<video src="black140x100.ogv" style="width:680px; height:200px; position:relative; top:200px;"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/aspect-ratio-2b.xhtml b/layout/reftests/ogg-video/aspect-ratio-2b.xhtml deleted file mode 100644 index e7469031a8..0000000000 --- a/layout/reftests/ogg-video/aspect-ratio-2b.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<video src="black140x100.ogv" style="width:280px; height:600px; position:relative; left:200px;"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/aspect-ratio-3-ref.xhtml b/layout/reftests/ogg-video/aspect-ratio-3-ref.xhtml deleted file mode 100644 index 73662654f8..0000000000 --- a/layout/reftests/ogg-video/aspect-ratio-3-ref.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<video src="black140x100.ogv" style="width:280px; height:200px;"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/aspect-ratio-3a.xhtml b/layout/reftests/ogg-video/aspect-ratio-3a.xhtml deleted file mode 100644 index 3c253e8f81..0000000000 --- a/layout/reftests/ogg-video/aspect-ratio-3a.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<video src="black140x100.ogv" width="280"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/aspect-ratio-3b.xhtml b/layout/reftests/ogg-video/aspect-ratio-3b.xhtml deleted file mode 100644 index cc6ec4c750..0000000000 --- a/layout/reftests/ogg-video/aspect-ratio-3b.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<video src="black140x100.ogv" height="200"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/basic-1-ref.html b/layout/reftests/ogg-video/basic-1-ref.html deleted file mode 100644 index aca3dcb9ca..0000000000 --- a/layout/reftests/ogg-video/basic-1-ref.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<div style="width:140px; height:100px; background:black;"></div> -</body> -</html> diff --git a/layout/reftests/ogg-video/basic-1.xhtml b/layout/reftests/ogg-video/basic-1.xhtml deleted file mode 100644 index 4899dc29f7..0000000000 --- a/layout/reftests/ogg-video/basic-1.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<video src="black140x100.ogv"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/black100x100-aspect3to2.ogv b/layout/reftests/ogg-video/black100x100-aspect3to2.ogv Binary files differdeleted file mode 100644 index 81fe51ffb3..0000000000 --- a/layout/reftests/ogg-video/black100x100-aspect3to2.ogv +++ /dev/null diff --git a/layout/reftests/ogg-video/black140x100.ogv b/layout/reftests/ogg-video/black140x100.ogv Binary files differdeleted file mode 100644 index ab146ebe28..0000000000 --- a/layout/reftests/ogg-video/black140x100.ogv +++ /dev/null diff --git a/layout/reftests/ogg-video/black29x19offset.ogv b/layout/reftests/ogg-video/black29x19offset.ogv Binary files differdeleted file mode 100644 index b515ebd1f3..0000000000 --- a/layout/reftests/ogg-video/black29x19offset.ogv +++ /dev/null diff --git a/layout/reftests/ogg-video/blue140x100.png b/layout/reftests/ogg-video/blue140x100.png Binary files differdeleted file mode 100644 index f4c3973fcc..0000000000 --- a/layout/reftests/ogg-video/blue140x100.png +++ /dev/null diff --git a/layout/reftests/ogg-video/blue250x200.png b/layout/reftests/ogg-video/blue250x200.png Binary files differdeleted file mode 100644 index 5eb0b52511..0000000000 --- a/layout/reftests/ogg-video/blue250x200.png +++ /dev/null diff --git a/layout/reftests/ogg-video/canvas-1a.xhtml b/layout/reftests/ogg-video/canvas-1a.xhtml deleted file mode 100644 index 9f115e785f..0000000000 --- a/layout/reftests/ogg-video/canvas-1a.xhtml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1" class="reftest-wait"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body> -<canvas id="canvas" width="200" height="200"></canvas> -<script> -var video = document.createElement("video"); -video.src = "black140x100.ogv"; -video.load(); -function draw() { - var canvas = document.getElementById("canvas"); - var ctx = canvas.getContext("2d"); - try { - ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight); - } catch (e) { - } - document.documentElement.removeAttribute("class"); -} -video.addEventListener("loadeddata", draw, false); -</script> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/canvas-1b.xhtml b/layout/reftests/ogg-video/canvas-1b.xhtml deleted file mode 100644 index 4524593e9e..0000000000 --- a/layout/reftests/ogg-video/canvas-1b.xhtml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body> -<canvas id="canvas" width="200" height="200"></canvas> -<script> -function draw() { - var video = document.getElementById("video"); - var canvas = document.getElementById("canvas"); - var ctx = canvas.getContext("2d"); - try { - ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight); - } catch (e) { - } - document.documentElement.removeAttribute("class"); -} -</script> -<video id="video" src="black140x100.ogv" onloadeddata="draw()" style="opacity:0"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/clipping-1-ref.html b/layout/reftests/ogg-video/clipping-1-ref.html deleted file mode 100644 index 67782811c5..0000000000 --- a/layout/reftests/ogg-video/clipping-1-ref.html +++ /dev/null @@ -1,9 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white"> -<div style="position:absolute; left:0; top:0; width:200px; height:600px;"> - <video src="black140x100.ogv" style="width:400px; margin-left:-100px;"></video> -</div> -<div style="position:absolute; left:200px; top:0; background:white; width:200px; height:600px;"></div> -</body> -</html> diff --git a/layout/reftests/ogg-video/clipping-1a.html b/layout/reftests/ogg-video/clipping-1a.html deleted file mode 100644 index cfae72cedd..0000000000 --- a/layout/reftests/ogg-video/clipping-1a.html +++ /dev/null @@ -1,8 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white"> -<div style="overflow:hidden; position:absolute; left:0; top:0; width:200px; height:600px;"> - <video src="black140x100.ogv" style="width:400px; margin-left:-100px;"></video> -</div> -</body> -</html> diff --git a/layout/reftests/ogg-video/empty-1-ref.html b/layout/reftests/ogg-video/empty-1-ref.html deleted file mode 100644 index fcc7693202..0000000000 --- a/layout/reftests/ogg-video/empty-1-ref.html +++ /dev/null @@ -1,5 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -</body> -</html> diff --git a/layout/reftests/ogg-video/empty-1a.html b/layout/reftests/ogg-video/empty-1a.html deleted file mode 100644 index fc1421a810..0000000000 --- a/layout/reftests/ogg-video/empty-1a.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<video src="black140x100.ogv" style="width:0"></video> -</body> -</html> diff --git a/layout/reftests/ogg-video/empty-1b.html b/layout/reftests/ogg-video/empty-1b.html deleted file mode 100644 index 9dd17dbbaa..0000000000 --- a/layout/reftests/ogg-video/empty-1b.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<video src="black140x100.ogv" style="height:0"></video> -</body> -</html> diff --git a/layout/reftests/ogg-video/encoded-aspect-ratio-1-ref.html b/layout/reftests/ogg-video/encoded-aspect-ratio-1-ref.html deleted file mode 100644 index d9c0054eb5..0000000000 --- a/layout/reftests/ogg-video/encoded-aspect-ratio-1-ref.html +++ /dev/null @@ -1,19 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<div style="background-color: black; width: 150px; height: 100px; position: absolute; left: 10px; top: 10px;"></div> - -<!-- Left side vertical. --> -<div style="position: absolute; left: 9px; top: 9px; width: 2px; height: 102px; z-index: 2; background: red;"></div> - -<!-- Right side vertical. --> -<div style="position: absolute; left: 159px; top: 9px; width: 2px; height: 102px; z-index: 2; background: red;"></div> - -<!-- Top horizontal bar. --> -<div style="position: absolute; left: 9px; top: 9px; width: 152px; height: 2px; z-index: 2; background: red;"></div> - -<!-- Bottom horizontal bar. --> -<div style="position: absolute; left: 9px; top: 109px; width: 152px; height: 2px; z-index: 2; background: red;"></div> - -</body> -</html> diff --git a/layout/reftests/ogg-video/encoded-aspect-ratio-1.html b/layout/reftests/ogg-video/encoded-aspect-ratio-1.html deleted file mode 100644 index 238fdbfe8d..0000000000 --- a/layout/reftests/ogg-video/encoded-aspect-ratio-1.html +++ /dev/null @@ -1,29 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<!-- - Test if video displays correctly with a 3:2 aspect ratio. - It should display at w=150 h=100. - On some Linux systems, the scaling to preserve the aspect ratio can sample - the pixels outside visible region. This results in a thin grey line down - some sides of the scaled picture. We add red bars over the edges to - overwrite such happenings, to make the test reliable. ---> -<video src="black100x100-aspect3to2.ogv" - style="position:absolute; left: 10px; top: 10px; z-index: 1;"> -</video> - -<!-- Left side vertical. --> -<div style="position: absolute; left: 9px; top: 9px; width: 2px; height: 102px; z-index: 2; background: red;"></div> - -<!-- Right side vertical. --> -<div style="position: absolute; left: 159px; top: 9px; width: 2px; height: 102px; z-index: 2; background: red;"></div> - -<!-- Top horizontal bar. --> -<div style="position: absolute; left: 9px; top: 9px; width: 152px; height: 2px; z-index: 2; background: red;"></div> - -<!-- Bottom horizontal bar. --> -<div style="position: absolute; left: 9px; top: 109px; width: 152px; height: 2px; z-index: 2; background: red;"></div> - -</body> -</html> diff --git a/layout/reftests/ogg-video/green70x30.png b/layout/reftests/ogg-video/green70x30.png Binary files differdeleted file mode 100644 index b2bf32762d..0000000000 --- a/layout/reftests/ogg-video/green70x30.png +++ /dev/null diff --git a/layout/reftests/ogg-video/object-aspect-ratio-1a.xhtml b/layout/reftests/ogg-video/object-aspect-ratio-1a.xhtml deleted file mode 100644 index 29fd619fd7..0000000000 --- a/layout/reftests/ogg-video/object-aspect-ratio-1a.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<object data="black140x100.ogv" style="width:340px; height:100px; position:relative; top:100px;"></object> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/object-aspect-ratio-1b.xhtml b/layout/reftests/ogg-video/object-aspect-ratio-1b.xhtml deleted file mode 100644 index ea1f02ff74..0000000000 --- a/layout/reftests/ogg-video/object-aspect-ratio-1b.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<object data="black140x100.ogv" style="width:140px; height:300px; position:relative; left:100px;"></object> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/object-aspect-ratio-2a.xhtml b/layout/reftests/ogg-video/object-aspect-ratio-2a.xhtml deleted file mode 100644 index 36b4cc0c1e..0000000000 --- a/layout/reftests/ogg-video/object-aspect-ratio-2a.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<object data="black140x100.ogv" style="width:680px; height:200px; position:relative; top:200px;"></object> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/object-aspect-ratio-2b.xhtml b/layout/reftests/ogg-video/object-aspect-ratio-2b.xhtml deleted file mode 100644 index d557921d2f..0000000000 --- a/layout/reftests/ogg-video/object-aspect-ratio-2b.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<object data="black140x100.ogv" style="width:280px; height:600px; position:relative; left:200px;"></object> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/offset-1-ref.html b/layout/reftests/ogg-video/offset-1-ref.html deleted file mode 100644 index 61a1dc83a7..0000000000 --- a/layout/reftests/ogg-video/offset-1-ref.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<div style="width:29px; height:29px; background:black;"></div> -</body> -</html> diff --git a/layout/reftests/ogg-video/offset-1.xhtml b/layout/reftests/ogg-video/offset-1.xhtml deleted file mode 100644 index 3504734907..0000000000 --- a/layout/reftests/ogg-video/offset-1.xhtml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject filter="url(../filters.svg#ThresholdRGB)" x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white;"> -<video src="black29x19offset.ogv"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/ogg-video/poster-1.html b/layout/reftests/ogg-video/poster-1.html deleted file mode 100644 index 15e3cb5462..0000000000 --- a/layout/reftests/ogg-video/poster-1.html +++ /dev/null @@ -1,7 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<!-- Ensure video element displays at poster size when video's intrinsic size isn't available. --> -<video preload="none" src="black140x100.ogv" poster="blue250x200.png"></video> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-10.html b/layout/reftests/ogg-video/poster-10.html deleted file mode 100644 index 546b96a46f..0000000000 --- a/layout/reftests/ogg-video/poster-10.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<!-- Test: Create video, add poster, load. Should display poster. --> -<script> -function runTest() { - var v = document.createElement('video'); - v.addEventListener('loadeddata', function(){setTimeout(function(){document.documentElement.className = '';}, 0);}); - v.id = 'v'; - v.src = "black140x100.ogv"; - v.poster = "blue250x200.png"; - v.preload = "auto"; - document.body.appendChild(v); -} - -</script> -<body style="background:white;" onload="runTest();"> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-11.html b/layout/reftests/ogg-video/poster-11.html deleted file mode 100644 index a2f186a4bb..0000000000 --- a/layout/reftests/ogg-video/poster-11.html +++ /dev/null @@ -1,29 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<!-- Test: Create video, load. Add poster frame, load again, poster should show. --> -<script> -function runTest() { - var v = document.createElement('video'); - - var endTest = function() { - setTimeout(function(){document.documentElement.className = '';}, 0); - }; - - var addPoster = function() { - v.removeEventListener('loadeddata', addPoster); - v.poster = "blue140x100.png"; - v.addEventListener('loadeddata', endTest); - v.load(); - }; - - v.addEventListener('loadeddata', addPoster); - v.id = 'v'; - v.src = "black140x100.ogv"; - v.preload = "auto"; - document.body.appendChild(v); -} - -</script> -<body style="background:white;" onload="runTest();"> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-12.html b/layout/reftests/ogg-video/poster-12.html deleted file mode 100644 index 49b6dbfebf..0000000000 --- a/layout/reftests/ogg-video/poster-12.html +++ /dev/null @@ -1,38 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<!-- Test: Create video, load, play. Add poster frame, load again, poster should show. --> -<script> -function runTest() { - var v = document.createElement('video'); - - var endTest = function() { - setTimeout(function(){document.documentElement.className = '';}, 0); - }; - - var play = - function() { - v.removeEventListener('loadeddata', play); - v.play(); - } - - var addPoster = function() { - v.removeEventListener('playing', addPoster); - v.poster = "blue140x100.png"; - v.addEventListener('loadeddata', endTest); - v.load(); - }; - - v.addEventListener('loadeddata', - play); - v.addEventListener('playing', - addPoster); - v.id = 'v'; - v.src = "black140x100.ogv"; - v.preload = "auto"; - document.body.appendChild(v); -} - -</script> -<body style="background:white;" onload="runTest();"> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-13.html b/layout/reftests/ogg-video/poster-13.html deleted file mode 100644 index 79c3b8c582..0000000000 --- a/layout/reftests/ogg-video/poster-13.html +++ /dev/null @@ -1,8 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<!-- Test that poster is resized, maintaining its aspect ratio. --> -<video src="black140x100.ogv" poster="blue250x200.png" style="width: 288px; height: 216px;"></video> - -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-15.html b/layout/reftests/ogg-video/poster-15.html deleted file mode 100644 index 01c25b660e..0000000000 --- a/layout/reftests/ogg-video/poster-15.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- Test that poster is correctly laid out inside borders. Also test that - poster frames smaller than video don't have the video frame drawn behind - them etc. --> -<body style="background:white;"> -<video src="black140x100.ogv" - poster="green70x30.png" - preload="none" - style="border: solid blue 2px;"> -</video> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-2.html b/layout/reftests/ogg-video/poster-2.html deleted file mode 100644 index 28b38f398c..0000000000 --- a/layout/reftests/ogg-video/poster-2.html +++ /dev/null @@ -1,7 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<!-- Test if poster frame with invalid poster displays video frame. --> -<video src="black140x100.ogv" poster="not-a-valid-file"></video> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-3.html b/layout/reftests/ogg-video/poster-3.html deleted file mode 100644 index ae12c1b65d..0000000000 --- a/layout/reftests/ogg-video/poster-3.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<body style="background:white;"> -<!-- Test if poster hides after playing with autoplay. --> -<video src="black140x100.ogv" - poster="blue250x200.png" - preload="auto" - autoplay - onplaying="setTimeout(function(){document.documentElement.className = '';},0);"></video> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-4.html b/layout/reftests/ogg-video/poster-4.html deleted file mode 100644 index 85453638c8..0000000000 --- a/layout/reftests/ogg-video/poster-4.html +++ /dev/null @@ -1,14 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<body style="background:white;" - onload="setTimeout(function(){document.documentElement.className = '';}, 0);"> -<!-- Test if we show video frame after removing valid poster. --> -<video src="black140x100.ogv" - preload="auto" - id="v" - poster="blue250x200.png"></video> -<script type="text/javascript"> - document.getElementById('v').poster = ''; -</script> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-5.html b/layout/reftests/ogg-video/poster-5.html deleted file mode 100644 index 94049c92d7..0000000000 --- a/layout/reftests/ogg-video/poster-5.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<body style="background:white;"> -<!-- Test to ensure that changing the poster after video has painted its first - frame doesn't render the poster. The video frame should not change to the - poster, since it's already painted its first video frame. --> -<video src="black140x100.ogv" - preload="auto" - id="v" - autoplay - onended="document.getElementById('v').poster = 'blue250x200.png'; setTimeout(function(){document.documentElement.className = '';},0);"></video> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-6.html b/layout/reftests/ogg-video/poster-6.html deleted file mode 100644 index c9ae5b88f5..0000000000 --- a/layout/reftests/ogg-video/poster-6.html +++ /dev/null @@ -1,12 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<body style="background:white;"> -<!-- Test that poster frame should hide after completing a seek. --> -<video src="black140x100.ogv" - preload="auto" - id="v" - onloadeddata="var v = document.getElementById('v'); v.currentTime = v.duration;" - onseeked="setTimeout(function(){document.documentElement.className = '';}, 0);" - poster="blue250x200.png"></video> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-7.html b/layout/reftests/ogg-video/poster-7.html deleted file mode 100644 index 4c3e002d37..0000000000 --- a/layout/reftests/ogg-video/poster-7.html +++ /dev/null @@ -1,14 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<body style="background:white;" - onload="setTimeout(function(){document.documentElement.className = '';}, 0);"> -<!-- Test that poster frame changes when you change the poster attribute. --> -<video src="black140x100.ogv" - preload="none" - id="v" - poster="blue250x200.png"></video> -<script type="text/javascript"> - document.getElementById('v').poster = 'red140x100.png'; -</script> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-8.html b/layout/reftests/ogg-video/poster-8.html deleted file mode 100644 index c9e1fa37a0..0000000000 --- a/layout/reftests/ogg-video/poster-8.html +++ /dev/null @@ -1,12 +0,0 @@ -<!DOCTYPE HTML> -<html class="reftest-wait"> -<body style="background:white;"> -<!-- Test if poster hides after playing with play() call. --> -<video src="black140x100.ogv" - poster="blue250x200.png" - id="v" - preload="auto" - onloadeddata="document.getElementById('v').play();" - onplaying="setTimeout(function(){document.documentElement.className = '';}, 0);"></video> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-ref-black140x100.html b/layout/reftests/ogg-video/poster-ref-black140x100.html deleted file mode 100644 index 98f1a4cba7..0000000000 --- a/layout/reftests/ogg-video/poster-ref-black140x100.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<video src="black140x100.ogv" preload="auto"></video> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-ref-blue125x100.html b/layout/reftests/ogg-video/poster-ref-blue125x100.html deleted file mode 100644 index 90795ae911..0000000000 --- a/layout/reftests/ogg-video/poster-ref-blue125x100.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<img src="blue140x100.png" alt="poster" style="position: absolute; height: 100px; width: 125px; left: 16px"> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-ref-blue140x100.html b/layout/reftests/ogg-video/poster-ref-blue140x100.html deleted file mode 100644 index 66540b8e85..0000000000 --- a/layout/reftests/ogg-video/poster-ref-blue140x100.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<img src="blue140x100.png" alt="poster"> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-ref-blue250x200.html b/layout/reftests/ogg-video/poster-ref-blue250x200.html deleted file mode 100644 index 050d53dfb3..0000000000 --- a/layout/reftests/ogg-video/poster-ref-blue250x200.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<img src="blue250x200.png" alt="poster"> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-ref-blue400x300.html b/layout/reftests/ogg-video/poster-ref-blue400x300.html deleted file mode 100644 index 8c32ff715d..0000000000 --- a/layout/reftests/ogg-video/poster-ref-blue400x300.html +++ /dev/null @@ -1,8 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<img src="blue250x200.png" style="position: relative; width: 270px; height: 216px; left: 9px;"> -</body> -</html> - - diff --git a/layout/reftests/ogg-video/poster-ref-green70x30.html b/layout/reftests/ogg-video/poster-ref-green70x30.html deleted file mode 100644 index f979220a6a..0000000000 --- a/layout/reftests/ogg-video/poster-ref-green70x30.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<img src="green70x30.png" alt="poster" style="border: solid blue 2px;"> -</body> -</html> diff --git a/layout/reftests/ogg-video/poster-ref-red140x100.html b/layout/reftests/ogg-video/poster-ref-red140x100.html deleted file mode 100644 index 3ac08d29e2..0000000000 --- a/layout/reftests/ogg-video/poster-ref-red140x100.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white;"> -<img src="red140x100.png" alt="poster"> -</body> -</html> diff --git a/layout/reftests/ogg-video/red140x100.png b/layout/reftests/ogg-video/red140x100.png Binary files differdeleted file mode 100644 index 20250771f3..0000000000 --- a/layout/reftests/ogg-video/red140x100.png +++ /dev/null diff --git a/layout/reftests/ogg-video/red160x120.png b/layout/reftests/ogg-video/red160x120.png Binary files differdeleted file mode 100644 index 21737a0e8e..0000000000 --- a/layout/reftests/ogg-video/red160x120.png +++ /dev/null diff --git a/layout/reftests/ogg-video/reftest.list b/layout/reftests/ogg-video/reftest.list deleted file mode 100644 index 2c9a96f8ee..0000000000 --- a/layout/reftests/ogg-video/reftest.list +++ /dev/null @@ -1,35 +0,0 @@ -# NOTE: bug 1084564 covers "fails"/"skip" annotations for android below: -fuzzy(0-255,0-5000) == 444-1.html 444-1-ref.html -fails-if(Android) == aspect-ratio-1a.xhtml aspect-ratio-1-ref.html -fails-if(Android) == aspect-ratio-1b.xhtml aspect-ratio-1-ref.html -fails-if(Android) skip-if(gtkWidget) == aspect-ratio-2a.xhtml aspect-ratio-2-ref.html -fails-if(Android) skip-if(gtkWidget) == aspect-ratio-2b.xhtml aspect-ratio-2-ref.html -== aspect-ratio-3a.xhtml aspect-ratio-3-ref.xhtml -== aspect-ratio-3b.xhtml aspect-ratio-3-ref.xhtml -fails-if(Android) random == encoded-aspect-ratio-1.html encoded-aspect-ratio-1-ref.html -fails-if(Android) == basic-1.xhtml basic-1-ref.html -== canvas-1a.xhtml basic-1-ref.html -fails-if(Android) == canvas-1b.xhtml basic-1-ref.html -== clipping-1a.html clipping-1-ref.html -== empty-1a.html empty-1-ref.html -== empty-1b.html empty-1-ref.html -#these is skipped because we hang on the htmlparser tests when this is ran -random == object-aspect-ratio-1a.xhtml aspect-ratio-1-ref.html -random == object-aspect-ratio-1b.xhtml aspect-ratio-1-ref.html -== offset-1.xhtml offset-1-ref.html -random == object-aspect-ratio-2a.xhtml aspect-ratio-2-ref.html -random == object-aspect-ratio-2b.xhtml aspect-ratio-2-ref.html -fuzzy-if(winWidget,0-1,0-56000) fuzzy-if(cocoaWidget,0-1,0-56000) == zoomed-1.xhtml zoomed-1-ref.html # bug 778995 for fuzzy -== poster-1.html poster-ref-blue250x200.html -== poster-2.html poster-ref-black140x100.html -== poster-3.html poster-ref-black140x100.html -== poster-4.html poster-ref-black140x100.html -== poster-5.html poster-ref-black140x100.html -== poster-6.html poster-ref-black140x100.html -== poster-7.html poster-ref-red140x100.html -== poster-8.html poster-ref-black140x100.html -random == poster-10.html poster-ref-blue125x100.html -random == poster-11.html poster-ref-blue140x100.html -random == poster-12.html poster-ref-blue140x100.html -== poster-13.html poster-ref-blue400x300.html -== poster-15.html poster-ref-green70x30.html diff --git a/layout/reftests/ogg-video/seek420.ogv b/layout/reftests/ogg-video/seek420.ogv Binary files differdeleted file mode 100644 index 2221cfef42..0000000000 --- a/layout/reftests/ogg-video/seek420.ogv +++ /dev/null diff --git a/layout/reftests/ogg-video/seek444.ogv b/layout/reftests/ogg-video/seek444.ogv Binary files differdeleted file mode 100644 index a2453afeae..0000000000 --- a/layout/reftests/ogg-video/seek444.ogv +++ /dev/null diff --git a/layout/reftests/ogg-video/zoomed-1-ref.html b/layout/reftests/ogg-video/zoomed-1-ref.html deleted file mode 100644 index 01929db7e7..0000000000 --- a/layout/reftests/ogg-video/zoomed-1-ref.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE HTML> -<html> -<body style="background:white; margin:0;"> -<div style="width:280px; height:200px; background:#010101;"></div> -</body> -</html> diff --git a/layout/reftests/ogg-video/zoomed-1.xhtml b/layout/reftests/ogg-video/zoomed-1.xhtml deleted file mode 100644 index e68e1290f4..0000000000 --- a/layout/reftests/ogg-video/zoomed-1.xhtml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.1" reftest-zoom="2" - width="50%" height="50%"> -<!-- use an empty g to force filters.svg to load before onload --> -<use xlink:href="../filters.svg#empty" /> -<foreignObject x="0" y="0" height="100%" width="100%"> -<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml"> -<body style="background:white; margin:0"> -<video src="black140x100.ogv"></video> -</body> -</html> -</foreignObject> -</svg> diff --git a/layout/reftests/position-sticky/reftest.list b/layout/reftests/position-sticky/reftest.list index bd9aadc1d0..d6ed9617fe 100644 --- a/layout/reftests/position-sticky/reftest.list +++ b/layout/reftests/position-sticky/reftest.list @@ -51,5 +51,5 @@ fuzzy(0-1,0-220) == block-in-inline-3.html block-in-inline-3-ref.html == iframe-1.html iframe-1-ref.html == transformed-1.html transformed-1-ref.html fuzzy-if(Android,0-8,0-9) fuzzy-if(gtkWidget,10-17,12-32) fuzzy-if(cocoaWidget,7-8,18-42) skip-if(useDrawSnapshot) fails-if(useDrawSnapshot) == transformed-2.html transformed-2-ref.html # Bug 1604644 -skip-if(useDrawSnapshot) fuzzy-if(Android,0-14,0-11) fuzzy-if(gtkWidget,19-30,12-32) fuzzy-if(cocoaWidget,13-16,20-44) fails-if(useDrawSnapshot) == nested-sticky-1.html nested-sticky-1-ref.html # Bug 1604644 -skip-if(useDrawSnapshot) fuzzy-if(Android,0-14,0-11) fuzzy-if(gtkWidget,19-30,12-32) fuzzy-if(cocoaWidget,13-16,20-44) fails-if(useDrawSnapshot) == nested-sticky-2.html nested-sticky-2-ref.html # Bug 1604644 +skip-if(useDrawSnapshot) fuzzy-if(Android,0-14,0-17) fuzzy-if(gtkWidget,19-30,12-32) fuzzy-if(cocoaWidget,13-16,20-44) fails-if(useDrawSnapshot) == nested-sticky-1.html nested-sticky-1-ref.html # Bug 1604644 +skip-if(useDrawSnapshot) fuzzy-if(Android,0-14,0-96) fuzzy-if(gtkWidget,19-30,12-32) fuzzy-if(cocoaWidget,13-16,20-44) fails-if(useDrawSnapshot) == nested-sticky-2.html nested-sticky-2-ref.html # Bug 1604644 diff --git a/layout/reftests/printing/1900028-text-mask-pdf-ref.html b/layout/reftests/printing/1900028-text-mask-pdf-ref.html new file mode 100644 index 0000000000..5cd8dc37d3 --- /dev/null +++ b/layout/reftests/printing/1900028-text-mask-pdf-ref.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<head> + <meta charset="utf-8"> + <link href="print.css" rel="stylesheet"> + <style> + .test { + font-size: 50px; + height: 2em; + } + </style> +</head> +<p>Some text</p> +<p class=test><!-- This will not be visible in the print output, + unless backgrounds are explicitly enabled. --></p> +<p>More text after the test</p> diff --git a/layout/reftests/printing/1900028-text-mask-pdf.html b/layout/reftests/printing/1900028-text-mask-pdf.html new file mode 100644 index 0000000000..3e32d98c16 --- /dev/null +++ b/layout/reftests/printing/1900028-text-mask-pdf.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<head> + <meta charset="utf-8"> + <link href="print.css" rel="stylesheet"> + <style> + .test { + color: transparent; + font-size: 50px; + background: blue; + background-clip: text; + height: 2em; + } + </style> +</head> +<p>Some text</p> +<p class=test>background-clip:text</p> +<p>More text after the test</p> diff --git a/layout/reftests/printing/reftest.list b/layout/reftests/printing/reftest.list index eb571f067a..3e474ae47a 100644 --- a/layout/reftests/printing/reftest.list +++ b/layout/reftests/printing/reftest.list @@ -19,3 +19,4 @@ fails print test-unexpected-text.html test-unexpected-text-noref.html fails print test-missing-text.html test-missing-text-noref.html test-pref(print.print_in_color,false) fails print test-color-text-01.html test-color-text-01.html print testcase-1696844.html testcase-1696844.html +print 1900028-text-mask-pdf.html 1900028-text-mask-pdf-ref.html diff --git a/layout/reftests/reftest-sanity/reftest.list b/layout/reftests/reftest-sanity/reftest.list index 52b6982aac..df657fd683 100644 --- a/layout/reftests/reftest-sanity/reftest.list +++ b/layout/reftests/reftest-sanity/reftest.list @@ -46,11 +46,6 @@ fails-if(geckoview&&device) == filter-2.xhtml filter-2-ref.xhtml == invalidation.html about:blank fails-if(useDrawSnapshot) == zoom-invalidation.html zoom-invalidation-ref.html # bug 773482 -# test that xulRuntime.OS works -fails-if(xulRuntime.OS!="Linux"&&!Android) == data:text/html,<body>Linux data:text/html,<script>document.write(navigator.platform.substr(0,5))</script> -fails-if(xulRuntime.OS!="WINNT") == data:text/html,<body>Win data:text/html,<script>document.write(navigator.platform.substr(0,3))</script> -fails-if(xulRuntime.OS!="Darwin") == data:text/html,<body>Mac data:text/html,<script>document.write(navigator.platform.substr(0,3))</script> - # test parsing of asserts() expressions asserts(0) load about:blank asserts(0-5) load about:blank diff --git a/layout/reftests/reftest.list b/layout/reftests/reftest.list index 9587c70798..c8911b2f20 100644 --- a/layout/reftests/reftest.list +++ b/layout/reftests/reftest.list @@ -18,7 +18,8 @@ include position-relative/reftest.list # apng-mime include apng-mime/reftest.list -skip-if(unsupportedWithDrawSnapshot) include async-scrolling/reftest.list +# unsupported with draw snapshot +skip-if(useDrawSnapshot) include async-scrolling/reftest.list # backgrounds/ include backgrounds/reftest.list @@ -273,9 +274,6 @@ include outline/reftest.list # object/ include object/reftest.list -# ogg-video/ -skip include ogg-video/reftest.list - # webm-video/ skip include webm-video/reftest.list @@ -363,9 +361,6 @@ include text-svgglyphs/reftest.list # text-transform/ include text-transform/reftest.list -# theme (osx) -include ../../toolkit/themes/osx/reftests/reftest.list - include ../../toolkit/content/tests/reftests/reftest.list # transform/ @@ -434,7 +429,8 @@ include invalidation/reftest.list include ../../dom/encoding/test/reftest/reftest.list # APZ/async positioning tests -skip-if(unsupportedWithDrawSnapshot) include ../../gfx/layers/apz/test/reftest/reftest.list +# unsupported with draw snapshot +skip-if(useDrawSnapshot) include ../../gfx/layers/apz/test/reftest/reftest.list # Display list building include display-list/reftest.list diff --git a/layout/reftests/scrolling/reftest.list b/layout/reftests/scrolling/reftest.list index 159f480416..d88bc2e038 100644 --- a/layout/reftests/scrolling/reftest.list +++ b/layout/reftests/scrolling/reftest.list @@ -34,10 +34,10 @@ fuzzy-if(Android,0-5,0-20000) == uncovering-2.html uncovering-2-ref.html == less-than-scrollbar-height.html less-than-scrollbar-height-ref.html == huge-horizontal-overflow.html huge-horizontal-overflow-ref.html == huge-vertical-overflow.html huge-vertical-overflow-ref.html -pref(apz.allow_zooming,true) fuzzy-if(gtkWidget,0-1,0-80) fuzzy-if(winWidget,0-4,0-170) fuzzy-if(winWidget&&fission,0-96,0-1109) == iframe-scrolling-attr-1.html iframe-scrolling-attr-ref.html # fission: Bug 1717856 -pref(apz.allow_zooming,true) fuzzy-if(gtkWidget,0-1,0-80) fuzzy-if(winWidget,0-4,0-170) fuzzy-if(winWidget&&fission,0-96,0-1109) == iframe-scrolling-attr-2.html iframe-scrolling-attr-ref.html # fission: Bug 1717856 -pref(apz.allow_zooming,true) fuzzy(0-1,0-2) fuzzy-if(geckoview,0-1,0-15) fuzzy-if(gtkWidget,0-1,0-48) fuzzy-if(winWidget,0-4,0-108) fuzzy-if(winWidget&&fission,0-92,0-1280) == frame-scrolling-attr-1.html frame-scrolling-attr-ref.html -pref(apz.allow_zooming,true) fuzzy(0-1,0-2) fuzzy-if(geckoview,0-1,0-88) fuzzy-if(gtkWidget,0-1,0-48) fuzzy-if(winWidget,0-4,0-108) fuzzy-if(winWidget&&fission,0-92,0-1920) == frame-scrolling-attr-2.html frame-scrolling-attr-ref.html +pref(apz.allow_zooming,true) fuzzy-if(gtkWidget,0-1,0-80) fuzzy-if(winWidget,0-4,0-170) fuzzy-if(winWidget,0-96,0-1109) == iframe-scrolling-attr-1.html iframe-scrolling-attr-ref.html # win/fission: Bug 1717856 +pref(apz.allow_zooming,true) fuzzy-if(gtkWidget,0-1,0-80) fuzzy-if(winWidget,0-4,0-170) fuzzy-if(winWidget,0-96,0-1109) == iframe-scrolling-attr-2.html iframe-scrolling-attr-ref.html # win/fission: Bug 1717856 +pref(apz.allow_zooming,true) fuzzy(0-1,0-2) fuzzy-if(geckoview,0-1,0-15) fuzzy-if(gtkWidget,0-1,0-48) fuzzy-if(winWidget,0-4,0-108) fuzzy-if(winWidget,0-92,0-1280) == frame-scrolling-attr-1.html frame-scrolling-attr-ref.html +pref(apz.allow_zooming,true) fuzzy(0-1,0-2) fuzzy-if(geckoview,0-1,0-88) fuzzy-if(gtkWidget,0-1,0-48) fuzzy-if(winWidget,0-4,0-108) fuzzy-if(winWidget,0-92,0-1920) == frame-scrolling-attr-2.html frame-scrolling-attr-ref.html == move-item.html move-item-ref.html # bug 1125750 == fractional-scroll-area.html?top=-0.4&outerBottom=100&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200 == fractional-scroll-area.html?top=0.4&outerBottom=100&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200 diff --git a/layout/reftests/svg/1630900-1-ref.html b/layout/reftests/svg/1630900-1-ref.html new file mode 100644 index 0000000000..aa471c548b --- /dev/null +++ b/layout/reftests/svg/1630900-1-ref.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> + <head> + <style> + + .loader { + height: 480px; + } + + rect { + fill: #000; + opacity: 0.5; + } + + </style> + </head> + <body> + <svg + class="loader" + x="0px" + y="0px" + viewBox="0 0 100 100" + > + <rect x="0" y="0" width="100" height="100"/> + </svg> + </body> +</html> diff --git a/layout/reftests/svg/1630900-1.html b/layout/reftests/svg/1630900-1.html new file mode 100644 index 0000000000..ce86050ee1 --- /dev/null +++ b/layout/reftests/svg/1630900-1.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> + <head> + <style> + + .loader { + height: 480px; + } + + rect { + fill: #000; + opacity: 0.5; + } + .anim { + animation: fade 10.5s infinite; + } + + @keyframes fade { + 0% { + opacity: 0.5; + } + 50% { + opacity: 0.51; + } + 100% { + opacity: 0.5; + } + } + + </style> + </head> + <body> + <svg + class="loader" + x="0px" + y="0px" + viewBox="0 0 100 100" + > + <rect class="anim" x="0" y="0" width="100" height="100"/> + </svg> + </body> +</html> diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index 5f1da97375..27f00455f5 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -634,3 +634,7 @@ skip-if(Android) skip-if(cocoaWidget) skip-if(winWidget) == transform-animation- fuzzy(0-20,0-110) == 1792313.svg 1792313-ref.svg pref(svg.use-element.recursive-clone-limit.enabled,1) != about:blank explosive-use.svg + +# do not increase fuzz significantly, test was designed to be within 1 color unit +fuzzy(0-1,0-230400) == 1630900-1.html 1630900-1-ref.html +# do not increase fuzz significantly, test was designed to be within 1 color unit diff --git a/layout/reftests/svg/smil/anim-feComponentTransfer-01.svg b/layout/reftests/svg/smil/anim-feComponentTransfer-01.svg index 69f879d804..a2f686dc99 100644 --- a/layout/reftests/svg/smil/anim-feComponentTransfer-01.svg +++ b/layout/reftests/svg/smil/anim-feComponentTransfer-01.svg @@ -4,8 +4,7 @@ --> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(1, true)"> + class="reftest-wait"> <title>Test animation of the "intercept" attribute of the "feComponentTransfer" element</title> <script xlink:href="smil-util.js" type="text/javascript"/> <filter id="flood_filter" x="0%" y="0%" width="100%" height="100%"> @@ -29,4 +28,11 @@ </filter> <rect width="100%" height="100%" fill="red"/> <rect width="100%" height="100%" fill="red" filter="url(#flood_filter)"/> + + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(1, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-feComposite-operator-01.svg b/layout/reftests/svg/smil/anim-feComposite-operator-01.svg index 0223cea931..bc93b4a7f7 100644 --- a/layout/reftests/svg/smil/anim-feComposite-operator-01.svg +++ b/layout/reftests/svg/smil/anim-feComposite-operator-01.svg @@ -1,7 +1,6 @@ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(1, true)"> + class="reftest-wait"> <title>Test animation of the "operator" enum attribute on the "feComposite" element</title> <script xlink:href="smil-util.js" type="text/javascript"/> <filter id="composite_filter_1" x="-100%" y="0%" width="200%" height="100%"> @@ -35,4 +34,11 @@ <!-- 50% through discrete animation simple duration - test animation affects the element now --> <rect y="100" width="100" height="100" fill="red"/> <rect y="100" width="100" height="100" fill="red" filter="url(#composite_filter_2)"/> + + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(1, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-feConvolveMatrix-order-01.svg b/layout/reftests/svg/smil/anim-feConvolveMatrix-order-01.svg index d24eb3b8d7..9a98a57c93 100644 --- a/layout/reftests/svg/smil/anim-feConvolveMatrix-order-01.svg +++ b/layout/reftests/svg/smil/anim-feConvolveMatrix-order-01.svg @@ -4,8 +4,7 @@ --> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(2, true)"> + class="reftest-wait"> <title>Testcase for animation of the "order" attribute of the "feConvolveMatrix" element</title> <script xlink:href="smil-util.js" type="text/javascript"/> <defs> @@ -50,4 +49,11 @@ <rect x="10" y="10" width="50" height="100" fill="orange"/> <rect x="60" y="10" width="50" height="100" fill="blue"/> </g> + + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(2, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-feDistantLight-01.svg b/layout/reftests/svg/smil/anim-feDistantLight-01.svg index 42221cdb48..2edcb4ab54 100644 --- a/layout/reftests/svg/smil/anim-feDistantLight-01.svg +++ b/layout/reftests/svg/smil/anim-feDistantLight-01.svg @@ -4,8 +4,7 @@ --> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(1, true)"> + class="reftest-wait"> <title>Testcase for animation of the "elevation" attribute of the "feDistantLight" element</title> <script xlink:href="smil-util.js" type="text/javascript"/> <defs> @@ -22,4 +21,11 @@ </filter> </defs> <path d="M0,0 h100 v100 h-100 z" filter="url(#f)"/> + + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(1, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-feFuncR-tableValues-01.svg b/layout/reftests/svg/smil/anim-feFuncR-tableValues-01.svg index 512e1ab074..2b51091862 100644 --- a/layout/reftests/svg/smil/anim-feFuncR-tableValues-01.svg +++ b/layout/reftests/svg/smil/anim-feFuncR-tableValues-01.svg @@ -1,7 +1,6 @@ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(1, true)"> + class="reftest-wait"> <title>Test animation of the <number-list> attribute on the 'text' element</title> <script xlink:href="smil-util.js" type="text/javascript"/> @@ -98,4 +97,10 @@ <rect x="20" y="140" width="256" height="20" fill="url(#gradient)" filter="url(#f_calcMode_discrete)"/> + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(1, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-feGaussianBlur-01.svg b/layout/reftests/svg/smil/anim-feGaussianBlur-01.svg index 40f804d74c..f80f46075a 100644 --- a/layout/reftests/svg/smil/anim-feGaussianBlur-01.svg +++ b/layout/reftests/svg/smil/anim-feGaussianBlur-01.svg @@ -4,8 +4,7 @@ --> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(1.9999, true)"> + class="reftest-wait"> <title>Test animation of the "stdDeviation" <number-optional-number> attribute on "feGaussianBlur" elements</title> <script xlink:href="smil-util.js" type="text/javascript"/> <filter id="filter" x="0" y="0" width="1" height="1"> @@ -22,4 +21,11 @@ <circle fill="red" cx="100" cy="100" r="98" transform="translate(50, 0)" filter="url(#filter)"/> </g> <circle fill="lime" cx="200" cy="100" r="100"/> + + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(1.9999, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-feOffset-01.svg b/layout/reftests/svg/smil/anim-feOffset-01.svg index 30c8795670..e2f3055af8 100644 --- a/layout/reftests/svg/smil/anim-feOffset-01.svg +++ b/layout/reftests/svg/smil/anim-feOffset-01.svg @@ -1,7 +1,6 @@ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(1, true)"> + class="reftest-wait"> <title>Test animation of the "dx" and "dy" attributes on the "feOffset" element</title> <script xlink:href="smil-util.js" type="text/javascript"/> <filter id="offset_filter_1" x="0%" y="0%" width="300%" height="100%"> @@ -31,4 +30,11 @@ <!-- test 100% completed animation --> <rect y="100" width="100" height="100" fill="red"/> <rect width="100" height="100" fill="lime" filter="url(#offset_filter_2)"/> + + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(1, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-feSpotLight-01.svg b/layout/reftests/svg/smil/anim-feSpotLight-01.svg index 011a9ecff9..4169c3bb36 100644 --- a/layout/reftests/svg/smil/anim-feSpotLight-01.svg +++ b/layout/reftests/svg/smil/anim-feSpotLight-01.svg @@ -4,8 +4,7 @@ --> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(1, true)"> + class="reftest-wait"> <title>Testcase for animation of the "elevation" attribute of the "feSpotLight" element</title> <script xlink:href="smil-util.js" type="text/javascript"/> <defs> @@ -23,4 +22,11 @@ </filter> </defs> <path d="M0,0 h100 v100 h-100 z" filter="url(#f)"/> + + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(1, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-feTurbulence-numOctaves-01.svg b/layout/reftests/svg/smil/anim-feTurbulence-numOctaves-01.svg index e48a6bd55e..9589ea38b1 100644 --- a/layout/reftests/svg/smil/anim-feTurbulence-numOctaves-01.svg +++ b/layout/reftests/svg/smil/anim-feTurbulence-numOctaves-01.svg @@ -1,7 +1,6 @@ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(5, true)"> + class="reftest-wait"> <title>Test animation of the "numOctaves" <integer> attribute on the "feTurbulence" element</title> <script xlink:href="smil-util.js" type="text/javascript"/> @@ -214,4 +213,10 @@ </filter> <rect x="20" y="100" width="20" height="20" filter="url(#filter_12)"/> + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(5, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-feTurbulence-numOctaves-02.svg b/layout/reftests/svg/smil/anim-feTurbulence-numOctaves-02.svg index ffade083b0..2e9f2329dc 100644 --- a/layout/reftests/svg/smil/anim-feTurbulence-numOctaves-02.svg +++ b/layout/reftests/svg/smil/anim-feTurbulence-numOctaves-02.svg @@ -1,7 +1,6 @@ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(3, true)"> + class="reftest-wait"> <title>Test animation of the "numOctaves" <integer> attribute on the "feTurbulence" element</title> <script xlink:href="smil-util.js" type="text/javascript"/> @@ -199,4 +198,11 @@ </filter> <rect x="20" y="60" width="20" height="20" filter="url(#filter_12)"/> + + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(3, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-filter-filterUnits-01.svg b/layout/reftests/svg/smil/anim-filter-filterUnits-01.svg index 641f711931..4d508c38f4 100644 --- a/layout/reftests/svg/smil/anim-filter-filterUnits-01.svg +++ b/layout/reftests/svg/smil/anim-filter-filterUnits-01.svg @@ -1,7 +1,6 @@ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(1, true)"> + class="reftest-wait"> <title>Test animation of the "filterUnits" enum attributes of the "filter" element</title> <script xlink:href="smil-util.js" type="text/javascript"/> <filter id="flood_filter_1" filterUnits="userSpaceOnUse" @@ -39,4 +38,11 @@ <svg y="50" height="50"> <rect width="50%" height="100%" fill="red" filter="url(#flood_filter_2)"/> </svg> + + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(1, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/smil/anim-filter-href-01.svg b/layout/reftests/svg/smil/anim-filter-href-01.svg index fa259de4ee..39f7ae7dcf 100644 --- a/layout/reftests/svg/smil/anim-filter-href-01.svg +++ b/layout/reftests/svg/smil/anim-filter-href-01.svg @@ -4,8 +4,7 @@ --> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - class="reftest-wait" - onload="setTimeAndSnapshot(1, true)"> + class="reftest-wait"> <title>Test animation of the "in" and "result" <string> attributes on "filter" elements</title> <script xlink:href="smil-util.js" type="text/javascript"/> @@ -31,4 +30,10 @@ <rect width="100%" height="100%" fill="red"/> <rect width="100%" height="100%" fill="red" filter="url(#filt)"/> + <script type="text/javascript"> + function doTest() { + setTimeAndSnapshot(1, true); + } + window.addEventListener("MozReftestInvalidate", doTest, false); + </script> </svg> diff --git a/layout/reftests/svg/text/reftest.list b/layout/reftests/svg/text/reftest.list index 14b3c68b34..0b749fe20a 100644 --- a/layout/reftests/svg/text/reftest.list +++ b/layout/reftests/svg/text/reftest.list @@ -209,8 +209,8 @@ needs-focus == multiple-chunks-selection.svg multiple-chunks-selection-ref.svg fuzzy(0-1,0-200) needs-focus == textpath-selection.svg textpath-selection-ref.svg # letter-spacing and word-spacing -== simple-letter-spacing.svg simple-letter-spacing-ref.svg +pref(layout.css.letter-spacing.model,0) == simple-letter-spacing.svg simple-letter-spacing-ref.svg == simple-word-spacing.svg simple-word-spacing-ref.svg -== multiple-chunks-letter-spacing.svg multiple-chunks-letter-spacing-ref.svg +pref(layout.css.letter-spacing.model,0) == multiple-chunks-letter-spacing.svg multiple-chunks-letter-spacing-ref.svg == tspan-shaping.svg tspan-shaping-ref.svg diff --git a/layout/reftests/text/reftest.list b/layout/reftests/text/reftest.list index 1402cc7ad7..5c3aba84f4 100644 --- a/layout/reftests/text/reftest.list +++ b/layout/reftests/text/reftest.list @@ -146,8 +146,8 @@ random-if(!winWidget) == arial-bold-lam-alef-1.html arial-bold-lam-alef-1-ref.ht == 1320665-cmap-format-13.html 1320665-cmap-format-13-ref.html # see bug 1320665 comments 8-9 == 1331339-script-extensions-shaping-1.html 1331339-script-extensions-shaping-1-ref.html skip-if(!cocoaWidget) != 1349308-1.html 1349308-notref.html # macOS-specific test for -apple-system glyph metrics -fuzzy-if(Android,0-128,0-233) == 1463020-letter-spacing-text-transform-1.html 1463020-letter-spacing-text-transform-1-ref.html -fails-if(Android) == 1463020-letter-spacing-text-transform-2.html 1463020-letter-spacing-text-transform-2-ref.html # missing font coverage on Android +pref(layout.css.letter-spacing.model,0) fuzzy-if(Android,0-128,0-233) == 1463020-letter-spacing-text-transform-1.html 1463020-letter-spacing-text-transform-1-ref.html +pref(layout.css.letter-spacing.model,0) fails-if(Android) == 1463020-letter-spacing-text-transform-2.html 1463020-letter-spacing-text-transform-2-ref.html # missing font coverage on Android pref(intl.icu4x.segmenter.enabled,false) == 1507661-spurious-hyphenation-after-explicit.html 1507661-spurious-hyphenation-after-explicit-ref.html fuzzy-if(useDrawSnapshot,255-255,50-50) == 1522857-1.html 1522857-1-ref.html # antialiasing fuzz in non-webrender cases != 1637405-pua-shaping-1.html 1637405-pua-shaping-1-notref.html diff --git a/layout/reftests/transform-3d/reftest.list b/layout/reftests/transform-3d/reftest.list index affea05d0b..1f865ac3a7 100644 --- a/layout/reftests/transform-3d/reftest.list +++ b/layout/reftests/transform-3d/reftest.list @@ -10,7 +10,6 @@ fuzzy(0-1,0-6) == rotatey-1a.html rotatey-1-ref.html # perspective should only apply to child elements == rotatex-perspective-1c.html rotatex-1-ref.html == rotatex-perspective-3a.html rotatex-perspective-3-ref.html -== scalez-1a.html scalez-1-ref.html fuzzy(0-16,0-346) fuzzy-if(cocoaWidget,0-200,0-310) fuzzy-if(winWidget,0-175,0-250) == preserve3d-1a.html preserve3d-1-ref.html == preserve3d-1b.html about:blank == preserve3d-clipped.html about:blank diff --git a/layout/style/CSSKeyframeRule.cpp b/layout/style/CSSKeyframeRule.cpp index e6ae2bada0..b70c6543b0 100644 --- a/layout/style/CSSKeyframeRule.cpp +++ b/layout/style/CSSKeyframeRule.cpp @@ -16,7 +16,7 @@ namespace mozilla::dom { // CSSKeyframeDeclaration // -class CSSKeyframeDeclaration : public nsDOMCSSDeclaration { +class CSSKeyframeDeclaration final : public nsDOMCSSDeclaration { public: explicit CSSKeyframeDeclaration(CSSKeyframeRule* aRule) : mRule(aRule) { mDecls = @@ -63,7 +63,6 @@ class CSSKeyframeDeclaration : public nsDOMCSSDeclaration { nsIPrincipal* aSubjectPrincipal) const final { return GetParsingEnvironmentForRule(mRule, StyleCssRuleType::Keyframe); } - Document* DocToUpdate() final { return nullptr; } nsINode* GetAssociatedNode() const final { return mRule ? mRule->GetAssociatedDocumentOrShadowRoot() : nullptr; diff --git a/layout/style/CSSMarginRule.cpp b/layout/style/CSSMarginRule.cpp new file mode 100644 index 0000000000..64a706666c --- /dev/null +++ b/layout/style/CSSMarginRule.cpp @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/CSSMarginRule.h" +#include "mozilla/dom/CSSMarginRuleBinding.h" + +#include "mozilla/DeclarationBlock.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +// -- CSSMarginRuleDeclaration --------------------------------------- + +CSSMarginRuleDeclaration::CSSMarginRuleDeclaration( + already_AddRefed<StyleLockedDeclarationBlock> aDecls) + : mDecls(new DeclarationBlock(std::move(aDecls))) { + mDecls->SetOwningRule(Rule()); +} + +CSSMarginRuleDeclaration::~CSSMarginRuleDeclaration() { + mDecls->SetOwningRule(nullptr); +} + +// QueryInterface implementation for CSSMarginRuleDeclaration +NS_INTERFACE_MAP_BEGIN(CSSMarginRuleDeclaration) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + // We forward the cycle collection interfaces to Rule(), which is + // never null (in fact, we're part of that object!) + if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) || + aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) { + return Rule()->QueryInterface(aIID, aInstancePtr); + } +NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) + +NS_IMPL_ADDREF_USING_AGGREGATOR(CSSMarginRuleDeclaration, Rule()) +NS_IMPL_RELEASE_USING_AGGREGATOR(CSSMarginRuleDeclaration, Rule()) + +/* nsDOMCSSDeclaration implementation */ +css::Rule* CSSMarginRuleDeclaration::GetParentRule() { return Rule(); } + +nsINode* CSSMarginRuleDeclaration::GetAssociatedNode() const { + return Rule()->GetAssociatedDocumentOrShadowRoot(); +} + +nsISupports* CSSMarginRuleDeclaration::GetParentObject() const { + return Rule()->GetParentObject(); +} + +DeclarationBlock* CSSMarginRuleDeclaration::GetOrCreateCSSDeclaration( + Operation aOperation, DeclarationBlock** aCreated) { + if (aOperation != Operation::Read) { + if (StyleSheet* sheet = Rule()->GetStyleSheet()) { + sheet->WillDirty(); + } + } + return mDecls; +} + +void CSSMarginRuleDeclaration::SetRawAfterClone( + RefPtr<StyleLockedDeclarationBlock> aDeclarationBlock) { + mDecls->SetOwningRule(nullptr); + mDecls = new DeclarationBlock(aDeclarationBlock.forget()); + mDecls->SetOwningRule(Rule()); +} + +nsresult CSSMarginRuleDeclaration::SetCSSDeclaration( + DeclarationBlock* aDecl, MutationClosureData* aClosureData) { + MOZ_ASSERT(aDecl, "must be non-null"); + CSSMarginRule* rule = Rule(); + + if (aDecl != mDecls) { + mDecls->SetOwningRule(nullptr); + RefPtr<DeclarationBlock> decls = aDecl; + // TODO alaskanemily: bug 1890418 for implementing this and margin-rule + // style properties in general. + // Servo_MarginRule_SetStyle(rule->Raw(), decls->Raw()); + mDecls = std::move(decls); + mDecls->SetOwningRule(rule); + } + + return NS_OK; +} + +nsDOMCSSDeclaration::ParsingEnvironment +CSSMarginRuleDeclaration::GetParsingEnvironment( + nsIPrincipal* aSubjectPrincipal) const { + return GetParsingEnvironmentForRule(Rule(), StyleCssRuleType::Margin); +} + +// -- CSSMarginRule -------------------------------------------------- + +CSSMarginRule::CSSMarginRule(RefPtr<StyleMarginRule> aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)), + mDecls(Servo_MarginRule_GetStyle(mRawRule).Consume()) {} + +NS_IMPL_ADDREF_INHERITED(CSSMarginRule, css::Rule) +NS_IMPL_RELEASE_INHERITED(CSSMarginRule, css::Rule) + +// QueryInterface implementation for MarginRule +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSMarginRule) +NS_INTERFACE_MAP_END_INHERITING(css::Rule) + +NS_IMPL_CYCLE_COLLECTION_CLASS(CSSMarginRule) + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CSSMarginRule, css::Rule) + // Keep this in sync with IsCCLeaf. + + // Trace the wrapper for our declaration. This just expands out + // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use + // directly because the wrapper is on the declaration, not on us. + tmp->mDecls.TraceWrapper(aCallbacks, aClosure); +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSMarginRule) + // Keep this in sync with IsCCLeaf. + + // Unlink the wrapper for our declaration. + // + // Note that this has to happen before unlinking css::Rule. + tmp->UnlinkDeclarationWrapper(tmp->mDecls); + tmp->mDecls.mDecls->SetOwningRule(nullptr); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(css::Rule) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSMarginRule, css::Rule) + // Keep this in sync with IsCCLeaf. +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +bool CSSMarginRule::IsCCLeaf() const { + if (!Rule::IsCCLeaf()) { + return false; + } + + return !mDecls.PreservingWrapper(); +} + +void CSSMarginRule::SetRawAfterClone(RefPtr<StyleMarginRule> aRaw) { + mRawRule = std::move(aRaw); + mDecls.SetRawAfterClone(Servo_MarginRule_GetStyle(mRawRule.get()).Consume()); +} + +// WebIDL interfaces +StyleCssRuleType CSSMarginRule::Type() const { + return StyleCssRuleType::Margin; +} + +// CSSRule implementation + +void CSSMarginRule::GetCssText(nsACString& aCssText) const { + Servo_MarginRule_GetCssText(mRawRule, &aCssText); +} + +void CSSMarginRule::GetName(nsACString& aRuleName) const { + Servo_MarginRule_GetName(mRawRule, &aRuleName); +} + +size_t CSSMarginRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + // TODO Implement this! + return aMallocSizeOf(this); +} + +#ifdef DEBUG +void CSSMarginRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_MarginRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +JSObject* CSSMarginRule::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return CSSMarginRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSMarginRule.h b/layout/style/CSSMarginRule.h new file mode 100644 index 0000000000..0ad5f60dbd --- /dev/null +++ b/layout/style/CSSMarginRule.h @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_CSSMarginRule_h +#define mozilla_dom_CSSMarginRule_h + +#include "mozilla/css/Rule.h" +#include "mozilla/ServoBindingTypes.h" + +#include "nsDOMCSSDeclaration.h" +#include "nsICSSDeclaration.h" + +namespace mozilla { +class DeclarationBlock; + +namespace dom { +class CSSMarginRule; + +class CSSMarginRuleDeclaration final : public nsDOMCSSDeclaration { + public: + NS_DECL_ISUPPORTS_INHERITED + + css::Rule* GetParentRule() final; + nsINode* GetAssociatedNode() const final; + nsISupports* GetParentObject() const final; + + protected: + DeclarationBlock* GetOrCreateCSSDeclaration( + Operation aOperation, DeclarationBlock** aCreated) final; + nsresult SetCSSDeclaration(DeclarationBlock* aDecl, + MutationClosureData* aClosureData) final; + Document* DocToUpdate() final { return nullptr; } + nsDOMCSSDeclaration::ParsingEnvironment GetParsingEnvironment( + nsIPrincipal* aSubjectPrincipal) const final; + + private: + // For accessing the constructor. + friend class CSSMarginRule; + + explicit CSSMarginRuleDeclaration( + already_AddRefed<StyleLockedDeclarationBlock> aDecls); + void SetRawAfterClone(RefPtr<StyleLockedDeclarationBlock>); + + ~CSSMarginRuleDeclaration(); + + inline CSSMarginRule* Rule(); + inline const CSSMarginRule* Rule() const; + + RefPtr<DeclarationBlock> mDecls; +}; + +class CSSMarginRule final : public css::Rule { + public: + CSSMarginRule(RefPtr<StyleMarginRule> aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(CSSMarginRule, + css::Rule) + + bool IsCCLeaf() const final; + + StyleMarginRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr<StyleMarginRule>); + + // WebIDL interfaces + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + nsICSSDeclaration* Style() { return &mDecls; } + + void GetName(nsACString& aRuleName) const; + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const final; + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final; + + private: + ~CSSMarginRule() = default; + + // For computing the offset of mDecls. + friend class CSSMarginRuleDeclaration; + + RefPtr<StyleMarginRule> mRawRule; + CSSMarginRuleDeclaration mDecls; +}; + +CSSMarginRule* CSSMarginRuleDeclaration::Rule() { + return reinterpret_cast<CSSMarginRule*>(reinterpret_cast<uint8_t*>(this) - + offsetof(CSSMarginRule, mDecls)); +} + +const CSSMarginRule* CSSMarginRuleDeclaration::Rule() const { + return reinterpret_cast<const CSSMarginRule*>( + reinterpret_cast<const uint8_t*>(this) - offsetof(CSSMarginRule, mDecls)); +} + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_CSSMarginRule_h diff --git a/layout/style/CSSPageRule.cpp b/layout/style/CSSPageRule.cpp index 04d064b362..fb7dcbc38b 100644 --- a/layout/style/CSSPageRule.cpp +++ b/layout/style/CSSPageRule.cpp @@ -94,20 +94,20 @@ CSSPageRuleDeclaration::GetParsingEnvironment( CSSPageRule::CSSPageRule(RefPtr<StyleLockedPageRule> aRawRule, StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn) - : css::Rule(aSheet, aParentRule, aLine, aColumn), + : css::GroupRule(aSheet, aParentRule, aLine, aColumn), mRawRule(std::move(aRawRule)), mDecls(Servo_PageRule_GetStyle(mRawRule).Consume()) {} -NS_IMPL_ADDREF_INHERITED(CSSPageRule, css::Rule) -NS_IMPL_RELEASE_INHERITED(CSSPageRule, css::Rule) +NS_IMPL_ADDREF_INHERITED(CSSPageRule, css::GroupRule) +NS_IMPL_RELEASE_INHERITED(CSSPageRule, css::GroupRule) // QueryInterface implementation for PageRule NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSPageRule) -NS_INTERFACE_MAP_END_INHERITING(css::Rule) +NS_INTERFACE_MAP_END_INHERITING(css::GroupRule) NS_IMPL_CYCLE_COLLECTION_CLASS(CSSPageRule) -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CSSPageRule, css::Rule) +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CSSPageRule, css::GroupRule) // Keep this in sync with IsCCLeaf. // Trace the wrapper for our declaration. This just expands out @@ -124,14 +124,14 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSPageRule) // Note that this has to happen before unlinking css::Rule. tmp->UnlinkDeclarationWrapper(tmp->mDecls); tmp->mDecls.mDecls->SetOwningRule(nullptr); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(css::Rule) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(css::GroupRule) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSPageRule, css::Rule) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSPageRule, css::GroupRule) // Keep this in sync with IsCCLeaf. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END bool CSSPageRule::IsCCLeaf() const { - if (!Rule::IsCCLeaf()) { + if (!GroupRule::IsCCLeaf()) { return false; } @@ -147,7 +147,7 @@ StyleCssRuleType CSSPageRule::Type() const { return StyleCssRuleType::Page; } size_t CSSPageRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { // TODO Implement this! - return aMallocSizeOf(this); + return GroupRule::SizeOfExcludingThis(aMallocSizeOf) + aMallocSizeOf(this); } #ifdef DEBUG @@ -188,6 +188,10 @@ void CSSPageRule::SetSelectorText(const nsACString& aSelectorText) { } } +already_AddRefed<StyleLockedCssRules> CSSPageRule::GetOrCreateRawRules() { + return Servo_PageRule_GetRules(mRawRule).Consume(); +} + nsICSSDeclaration* CSSPageRule::Style() { return &mDecls; } JSObject* CSSPageRule::WrapObject(JSContext* aCx, diff --git a/layout/style/CSSPageRule.h b/layout/style/CSSPageRule.h index 705d526bdf..b133111244 100644 --- a/layout/style/CSSPageRule.h +++ b/layout/style/CSSPageRule.h @@ -7,7 +7,7 @@ #ifndef mozilla_dom_CSSPageRule_h #define mozilla_dom_CSSPageRule_h -#include "mozilla/css/Rule.h" +#include "mozilla/css/GroupRule.h" #include "mozilla/ServoBindingTypes.h" #include "nsDOMCSSDeclaration.h" @@ -33,7 +33,6 @@ class CSSPageRuleDeclaration final : public nsDOMCSSDeclaration { Operation aOperation, DeclarationBlock** aCreated) final; nsresult SetCSSDeclaration(DeclarationBlock* aDecl, MutationClosureData* aClosureData) final; - Document* DocToUpdate() final { return nullptr; } nsDOMCSSDeclaration::ParsingEnvironment GetParsingEnvironment( nsIPrincipal* aSubjectPrincipal) const final; @@ -53,13 +52,14 @@ class CSSPageRuleDeclaration final : public nsDOMCSSDeclaration { RefPtr<DeclarationBlock> mDecls; }; -class CSSPageRule final : public css::Rule { +class CSSPageRule final : public css::GroupRule { public: CSSPageRule(RefPtr<StyleLockedPageRule> aRawRule, StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn); NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(CSSPageRule, css::Rule) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(CSSPageRule, + css::GroupRule) bool IsCCLeaf() const final; @@ -76,6 +76,8 @@ class CSSPageRule final : public css::Rule { size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const final; + already_AddRefed<StyleLockedCssRules> GetOrCreateRawRules() final; + #ifdef DEBUG void List(FILE* out = stdout, int32_t aIndent = 0) const final; #endif diff --git a/layout/style/CSSScopeRule.cpp b/layout/style/CSSScopeRule.cpp new file mode 100644 index 0000000000..18047a582b --- /dev/null +++ b/layout/style/CSSScopeRule.cpp @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/CSSScopeRule.h" +#include "mozilla/dom/CSSScopeRuleBinding.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +CSSScopeRule::CSSScopeRule(RefPtr<StyleScopeRule> aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, + uint32_t aColumn) + : css::GroupRule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + +NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(CSSScopeRule, css::GroupRule) + +// QueryInterface implementation for SupportsRule + +#ifdef DEBUG +void CSSScopeRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_ScopeRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +StyleCssRuleType CSSScopeRule::Type() const { return StyleCssRuleType::Scope; } + +already_AddRefed<StyleLockedCssRules> CSSScopeRule::GetOrCreateRawRules() { + return Servo_ScopeRule_GetRules(mRawRule).Consume(); +} + +void CSSScopeRule::SetRawAfterClone(RefPtr<StyleScopeRule> aRaw) { + mRawRule = std::move(aRaw); + css::GroupRule::DidSetRawAfterClone(); +} + +void CSSScopeRule::GetCssText(nsACString& aCssText) const { + Servo_ScopeRule_GetCssText(mRawRule.get(), &aCssText); +} + +void CSSScopeRule::GetStart(nsACString& aStart) const { + Servo_ScopeRule_GetStart(mRawRule.get(), &aStart); +} + +void CSSScopeRule::GetEnd(nsACString& aEnd) const { + Servo_ScopeRule_GetEnd(mRawRule.get(), &aEnd); +} + +size_t CSSScopeRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + return aMallocSizeOf(this); +} + +JSObject* CSSScopeRule::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return CSSScopeRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSScopeRule.h b/layout/style/CSSScopeRule.h new file mode 100644 index 0000000000..2cb1fa7e40 --- /dev/null +++ b/layout/style/CSSScopeRule.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CSSScopeRule_h___ +#define CSSScopeRule_h___ + +#include "mozilla/css/GroupRule.h" +#include "mozilla/ServoBindingTypes.h" + +namespace mozilla::dom { + +class CSSScopeRule final : public css::GroupRule { + public: + CSSScopeRule(RefPtr<StyleScopeRule> aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + StyleScopeRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr<StyleScopeRule>); + + already_AddRefed<StyleLockedCssRules> GetOrCreateRawRules() final; + + // WebIDL interface + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + + void GetStart(nsACString& aStart) const; + void GetEnd(nsACString& aEnd) const; + + size_t SizeOfIncludingThis(MallocSizeOf) const override; + JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override; + + private: + ~CSSScopeRule() = default; + + RefPtr<StyleScopeRule> mRawRule; +}; + +} // namespace mozilla::dom + +#endif diff --git a/layout/style/CSSStartingStyleRule.cpp b/layout/style/CSSStartingStyleRule.cpp new file mode 100644 index 0000000000..5c5ec311c9 --- /dev/null +++ b/layout/style/CSSStartingStyleRule.cpp @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/CSSStartingStyleRule.h" +#include "mozilla/dom/CSSStartingStyleRuleBinding.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(CSSStartingStyleRule, + css::GroupRule) + +// QueryInterface implementation for SupportsRule + +#ifdef DEBUG +void CSSStartingStyleRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_StartingStyleRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +StyleCssRuleType CSSStartingStyleRule::Type() const { + return StyleCssRuleType::StartingStyle; +} + +already_AddRefed<StyleLockedCssRules> +CSSStartingStyleRule::GetOrCreateRawRules() { + return Servo_StartingStyleRule_GetRules(mRawRule).Consume(); +} + +void CSSStartingStyleRule::GetCssText(nsACString& aCssText) const { + Servo_StartingStyleRule_GetCssText(mRawRule.get(), &aCssText); +} + +JSObject* CSSStartingStyleRule::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return CSSStartingStyleRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSStartingStyleRule.h b/layout/style/CSSStartingStyleRule.h new file mode 100644 index 0000000000..9ecccf505c --- /dev/null +++ b/layout/style/CSSStartingStyleRule.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef CSSStaringStyleRule_h___ +#define CSSStaringStyleRule_h___ + +#include "mozilla/css/GroupRule.h" +#include "mozilla/ServoBindingTypes.h" + +namespace mozilla::dom { + +class CSSStartingStyleRule final : public css::GroupRule { + public: + CSSStartingStyleRule(RefPtr<StyleStartingStyleRule> aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::GroupRule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + + NS_DECL_ISUPPORTS_INHERITED + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + StyleStartingStyleRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr<StyleStartingStyleRule> aRaw) { + mRawRule = std::move(aRaw); + css::GroupRule::DidSetRawAfterClone(); + } + + already_AddRefed<StyleLockedCssRules> GetOrCreateRawRules() final; + + // WebIDL interface + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); + } + JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override; + + private: + ~CSSStartingStyleRule() = default; + + RefPtr<StyleStartingStyleRule> mRawRule; +}; + +} // namespace mozilla::dom + +#endif diff --git a/layout/style/CSSStyleRule.cpp b/layout/style/CSSStyleRule.cpp index 5cdb47fddb..5a3dbfad01 100644 --- a/layout/style/CSSStyleRule.cpp +++ b/layout/style/CSSStyleRule.cpp @@ -101,8 +101,6 @@ nsresult CSSStyleRuleDeclaration::SetCSSDeclaration( return NS_OK; } -Document* CSSStyleRuleDeclaration::DocToUpdate() { return nullptr; } - nsDOMCSSDeclaration::ParsingEnvironment CSSStyleRuleDeclaration::GetParsingEnvironment( nsIPrincipal* aSubjectPrincipal) const { @@ -314,6 +312,18 @@ void CSSStyleRule::GetSelectorWarnings( } } +already_AddRefed<nsINodeList> CSSStyleRule::QuerySelectorAll(nsINode& aRoot) { + AutoTArray<const StyleLockedStyleRule*, 8> rules; + CollectStyleRules(*this, /* aDesugared = */ true, rules); + StyleSelectorList* list = Servo_StyleRule_GetSelectorList(&rules); + + RefPtr<nsSimpleContentList> contentList = new nsSimpleContentList(&aRoot); + Servo_SelectorList_QueryAll(&aRoot, list, contentList.get(), + /* useInvalidation */ false); + Servo_SelectorList_Drop(list); + return contentList.forget(); +} + /* virtual */ JSObject* CSSStyleRule::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { diff --git a/layout/style/CSSStyleRule.h b/layout/style/CSSStyleRule.h index 05eaae5c10..9d8a1478e6 100644 --- a/layout/style/CSSStyleRule.h +++ b/layout/style/CSSStyleRule.h @@ -36,7 +36,6 @@ class CSSStyleRuleDeclaration final : public nsDOMCSSDeclaration { Operation aOperation, mozilla::DeclarationBlock** aCreated) final; nsresult SetCSSDeclaration(DeclarationBlock* aDecl, MutationClosureData* aClosureData) final; - Document* DocToUpdate() final; ParsingEnvironment GetParsingEnvironment( nsIPrincipal* aSubjectPrincipal) const final; @@ -76,6 +75,7 @@ class CSSStyleRule final : public css::GroupRule, public SupportsWeakPtr { bool aRelevantLinkVisited); NotNull<DeclarationBlock*> GetDeclarationBlock() const; void GetSelectorWarnings(nsTArray<SelectorWarning>& aResult) const; + already_AddRefed<nsINodeList> QuerySelectorAll(nsINode& aRoot); // WebIDL interface StyleCssRuleType Type() const final; diff --git a/layout/style/FontFaceImpl.cpp b/layout/style/FontFaceImpl.cpp index 9d1f7c8c5b..5650b62afd 100644 --- a/layout/style/FontFaceImpl.cpp +++ b/layout/style/FontFaceImpl.cpp @@ -675,25 +675,40 @@ bool FontFaceImpl::IsInFontFaceSet(FontFaceSetImpl* aFontFaceSet) const { void FontFaceImpl::AddFontFaceSet(FontFaceSetImpl* aFontFaceSet) { MOZ_ASSERT(!IsInFontFaceSet(aFontFaceSet)); - if (mFontFaceSet == aFontFaceSet) { - mInFontFaceSet = true; + auto doAddFontFaceSet = [&]() { + if (mFontFaceSet == aFontFaceSet) { + mInFontFaceSet = true; + } else { + mOtherFontFaceSets.AppendElement(aFontFaceSet); + } + }; + + if (mUserFontEntry) { + AutoWriteLock lock(mUserFontEntry->Lock()); + doAddFontFaceSet(); } else { - mOtherFontFaceSets.AppendElement(aFontFaceSet); + doAddFontFaceSet(); } } void FontFaceImpl::RemoveFontFaceSet(FontFaceSetImpl* aFontFaceSet) { MOZ_ASSERT(IsInFontFaceSet(aFontFaceSet)); - if (mFontFaceSet == aFontFaceSet) { - mInFontFaceSet = false; - } else { - mOtherFontFaceSets.RemoveElement(aFontFaceSet); - } + auto doRemoveFontFaceSet = [&]() { + if (mFontFaceSet == aFontFaceSet) { + mInFontFaceSet = false; + } else { + mOtherFontFaceSets.RemoveElement(aFontFaceSet); + } + }; - // The caller should be holding a strong reference to the FontFaceSetImpl. if (mUserFontEntry) { - mUserFontEntry->CheckUserFontSet(); + AutoWriteLock lock(mUserFontEntry->Lock()); + doRemoveFontFaceSet(); + // The caller should be holding a strong reference to the FontFaceSetImpl. + mUserFontEntry->CheckUserFontSetLocked(); + } else { + doRemoveFontFaceSet(); } } @@ -736,7 +751,7 @@ void FontFaceImpl::Entry::SetLoadState(UserFontLoadState aLoadState) { nsTArray<RefPtr<FontFaceImpl>> fontFaces; { - MutexAutoLock lock(mMutex); + AutoReadLock lock(mLock); fontFaces.SetCapacity(mFontFaces.Length()); for (FontFaceImpl* f : mFontFaces) { fontFaces.AppendElement(f); @@ -758,7 +773,7 @@ void FontFaceImpl::Entry::SetLoadState(UserFontLoadState aLoadState) { /* virtual */ void FontFaceImpl::Entry::GetUserFontSets( nsTArray<RefPtr<gfxUserFontSet>>& aResult) { - MutexAutoLock lock(mMutex); + AutoReadLock lock(mLock); aResult.Clear(); @@ -783,7 +798,7 @@ void FontFaceImpl::Entry::GetUserFontSets( /* virtual */ already_AddRefed<gfxUserFontSet> FontFaceImpl::Entry::GetUserFontSet() const { - MutexAutoLock lock(mMutex); + AutoReadLock lock(mLock); if (mFontSet) { return do_AddRef(mFontSet); } @@ -816,7 +831,7 @@ void FontFaceImpl::Entry::CheckUserFontSetLocked() { } void FontFaceImpl::Entry::FindFontFaceOwners(nsTHashSet<FontFace*>& aOwners) { - MutexAutoLock lock(mMutex); + AutoReadLock lock(mLock); for (FontFaceImpl* f : mFontFaces) { if (FontFace* owner = f->GetOwner()) { aOwners.Insert(owner); @@ -825,13 +840,13 @@ void FontFaceImpl::Entry::FindFontFaceOwners(nsTHashSet<FontFace*>& aOwners) { } void FontFaceImpl::Entry::AddFontFace(FontFaceImpl* aFontFace) { - MutexAutoLock lock(mMutex); + AutoWriteLock lock(mLock); mFontFaces.AppendElement(aFontFace); CheckUserFontSetLocked(); } void FontFaceImpl::Entry::RemoveFontFace(FontFaceImpl* aFontFace) { - MutexAutoLock lock(mMutex); + AutoWriteLock lock(mLock); mFontFaces.RemoveElement(aFontFace); CheckUserFontSetLocked(); } diff --git a/layout/style/FontFaceImpl.h b/layout/style/FontFaceImpl.h index 70c06609e9..7f1279a248 100644 --- a/layout/style/FontFaceImpl.h +++ b/layout/style/FontFaceImpl.h @@ -10,7 +10,7 @@ #include "mozilla/dom/FontFaceBinding.h" #include "mozilla/FontPropertyTypes.h" #include "mozilla/Maybe.h" -#include "mozilla/Mutex.h" +#include "mozilla/RWLock.h" #include "mozilla/ServoStyleConsts.h" #include "gfxUserFontSet.h" #include "nsCSSPropertyID.h" @@ -50,21 +50,15 @@ class FontFaceImpl final { Entry(gfxUserFontSet* aFontSet, nsTArray<gfxFontFaceSrc>&& aFontFaceSrcList, gfxUserFontAttributes&& aAttr) : gfxUserFontEntry(std::move(aFontFaceSrcList), std::move(aAttr)), - mMutex("FontFaceImpl::Entry::mMutex"), mFontSet(aFontSet) {} void SetLoadState(UserFontLoadState aLoadState) override; void GetUserFontSets(nsTArray<RefPtr<gfxUserFontSet>>& aResult) override; already_AddRefed<gfxUserFontSet> GetUserFontSet() const override; - void CheckUserFontSet() { - MutexAutoLock lock(mMutex); - CheckUserFontSetLocked(); - } - #ifdef DEBUG bool HasUserFontSet(gfxUserFontSet* aFontSet) const { - MutexAutoLock lock(mMutex); + AutoReadLock lock(mLock); return mFontSet == aFontSet; } #endif @@ -73,19 +67,19 @@ class FontFaceImpl final { void RemoveFontFace(FontFaceImpl* aOwner); void FindFontFaceOwners(nsTHashSet<FontFace*>& aOwners); - protected: - void CheckUserFontSetLocked() MOZ_REQUIRES(mMutex); + RWLock& Lock() const MOZ_RETURN_CAPABILITY(mLock) { return mLock; } - mutable Mutex mMutex; + protected: + void CheckUserFontSetLocked() MOZ_REQUIRES(mLock); // Font set which owns this entry; - gfxUserFontSet* MOZ_NON_OWNING_REF mFontSet MOZ_GUARDED_BY(mMutex); + gfxUserFontSet* MOZ_NON_OWNING_REF mFontSet MOZ_GUARDED_BY(mLock); // The FontFace objects that use this user font entry. We need to store // an array of these, not just a single pointer, since the user font // cache can return the same entry for different FontFaces that have // the same descriptor values and come from the same origin. - AutoTArray<FontFaceImpl*, 1> mFontFaces MOZ_GUARDED_BY(mMutex); + AutoTArray<FontFaceImpl*, 1> mFontFaces MOZ_GUARDED_BY(mLock); }; #ifdef DEBUG diff --git a/layout/style/FontFaceSetDocumentImpl.cpp b/layout/style/FontFaceSetDocumentImpl.cpp index 33f7404c7c..cd1d5959d2 100644 --- a/layout/style/FontFaceSetDocumentImpl.cpp +++ b/layout/style/FontFaceSetDocumentImpl.cpp @@ -496,7 +496,7 @@ bool FontFaceSetDocumentImpl::UpdateRules( } if (modified) { - IncrementGeneration(true); + IncrementGenerationLocked(true); mHasLoadingFontFacesIsDirty = true; CheckLoadingStarted(); CheckLoadingFinished(); diff --git a/layout/style/FontFaceSetImpl.cpp b/layout/style/FontFaceSetImpl.cpp index 7383842a41..5dabf25d38 100644 --- a/layout/style/FontFaceSetImpl.cpp +++ b/layout/style/FontFaceSetImpl.cpp @@ -70,8 +70,7 @@ using namespace mozilla::dom; NS_IMPL_ISUPPORTS0(FontFaceSetImpl) FontFaceSetImpl::FontFaceSetImpl(FontFaceSet* aOwner) - : mMutex("mozilla::dom::FontFaceSetImpl"), - mOwner(aOwner), + : mOwner(aOwner), mStatus(FontFaceSetLoadStatus::Loaded), mNonRuleFacesDirty(false), mHasLoadingFontFaces(false), @@ -891,7 +890,7 @@ void FontFaceSetImpl::OnLoadingFinished() { void FontFaceSetImpl::RefreshStandardFontLoadPrincipal() { RecursiveMutexAutoLock lock(mMutex); mAllowedFontLoads.Clear(); - IncrementGeneration(false); + IncrementGenerationLocked(false); } // -- gfxUserFontSet diff --git a/layout/style/FontFaceSetImpl.h b/layout/style/FontFaceSetImpl.h index 6f245c6599..dd412dd721 100644 --- a/layout/style/FontFaceSetImpl.h +++ b/layout/style/FontFaceSetImpl.h @@ -92,9 +92,10 @@ class FontFaceSetImpl : public nsISupports, public gfxUserFontSet { virtual void DispatchToOwningThread(const char* aName, std::function<void()>&& aFunc) = 0; - // Called by nsFontFaceLoader when the loader has completed normally. + // Called by nsFontFaceLoader when the loader has completed normally, + // or by gfxUserFontSet if it cancels the loader. // It's removed from the mLoaders set. - virtual void RemoveLoader(nsFontFaceLoader* aLoader); + void RemoveLoader(nsFontFaceLoader* aLoader) override; virtual bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules) { MOZ_ASSERT_UNREACHABLE("Not implemented!"); @@ -247,8 +248,6 @@ class FontFaceSetImpl : public nsISupports, public gfxUserFontSet { virtual TimeStamp GetNavigationStartTimeStamp() = 0; - mutable RecursiveMutex mMutex; - FontFaceSet* MOZ_NON_OWNING_REF mOwner MOZ_GUARDED_BY(mMutex); // The document's node principal, which is the principal font loads for diff --git a/layout/style/FontFaceSetWorkerImpl.cpp b/layout/style/FontFaceSetWorkerImpl.cpp index 015c9e5b18..cd11e1f584 100644 --- a/layout/style/FontFaceSetWorkerImpl.cpp +++ b/layout/style/FontFaceSetWorkerImpl.cpp @@ -177,11 +177,11 @@ void FontFaceSetWorkerImpl::DispatchToOwningThread( return; } - class FontFaceSetWorkerRunnable final : public WorkerRunnable { + class FontFaceSetWorkerRunnable final : public WorkerThreadRunnable { public: FontFaceSetWorkerRunnable(WorkerPrivate* aWorkerPrivate, std::function<void()>&& aFunc) - : WorkerRunnable(aWorkerPrivate, "FontFaceSetWorkerRunnable"), + : WorkerThreadRunnable("FontFaceSetWorkerRunnable"), mFunc(std::move(aFunc)) {} bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { @@ -195,7 +195,7 @@ void FontFaceSetWorkerImpl::DispatchToOwningThread( RefPtr<FontFaceSetWorkerRunnable> runnable = new FontFaceSetWorkerRunnable(workerPrivate, std::move(aFunc)); - runnable->Dispatch(); + runnable->Dispatch(workerPrivate); } uint64_t FontFaceSetWorkerImpl::GetInnerWindowID() { @@ -227,7 +227,7 @@ void FontFaceSetWorkerImpl::FlushUserFontSet() { } if (modified) { - IncrementGeneration(true); + IncrementGenerationLocked(true); mHasLoadingFontFacesIsDirty = true; CheckLoadingStarted(); CheckLoadingFinished(); diff --git a/layout/style/GeckoBindings.cpp b/layout/style/GeckoBindings.cpp index dc43e9cf6a..17ba5c952e 100644 --- a/layout/style/GeckoBindings.cpp +++ b/layout/style/GeckoBindings.cpp @@ -988,34 +988,6 @@ const AnonymousCounterStyle* Gecko_CounterStyle_GetAnonymous( return aPtr->AsAnonymous(); } -void Gecko_EnsureTArrayCapacity(void* aArray, size_t aCapacity, - size_t aElemSize) { - auto base = - reinterpret_cast<nsTArray_base<nsTArrayInfallibleAllocator, - nsTArray_RelocateUsingMemutils>*>(aArray); - - base->EnsureCapacity<nsTArrayInfallibleAllocator>(aCapacity, aElemSize); -} - -void Gecko_ClearPODTArray(void* aArray, size_t aElementSize, - size_t aElementAlign) { - auto base = - reinterpret_cast<nsTArray_base<nsTArrayInfallibleAllocator, - nsTArray_RelocateUsingMemutils>*>(aArray); - - base->template ShiftData<nsTArrayInfallibleAllocator>( - 0, base->Length(), 0, aElementSize, aElementAlign); -} - -void Gecko_ResizeTArrayForStrings(nsTArray<nsString>* aArray, - uint32_t aLength) { - aArray->SetLength(aLength); -} - -void Gecko_ResizeAtomArray(nsTArray<RefPtr<nsAtom>>* aArray, uint32_t aLength) { - aArray->SetLength(aLength); -} - void Gecko_EnsureImageLayersLength(nsStyleImageLayers* aLayers, size_t aLen, nsStyleImageLayers::LayerType aLayerType) { size_t oldLength = aLayers->mLayers.Length(); @@ -1135,16 +1107,6 @@ Keyframe* Gecko_GetOrCreateFinalKeyframe( KeyframeInsertPosition::LastForOffset); } -PropertyValuePair* Gecko_AppendPropertyValuePair( - nsTArray<PropertyValuePair>* aProperties, - const mozilla::AnimatedPropertyID* aProperty) { - MOZ_ASSERT(aProperties); - MOZ_ASSERT( - aProperty->IsCustom() || - !nsCSSProps::PropHasFlags(aProperty->mID, CSSPropFlags::IsLogical)); - return aProperties->AppendElement(PropertyValuePair{*aProperty}); -} - void Gecko_GetComputedURLSpec(const StyleComputedUrl* aURL, nsCString* aOut) { MOZ_ASSERT(aURL); MOZ_ASSERT(aOut); diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h index 7bb839ae18..8ba7fcc5c6 100644 --- a/layout/style/GeckoBindings.h +++ b/layout/style/GeckoBindings.h @@ -375,20 +375,6 @@ const mozilla::ServoElementSnapshot* Gecko_GetElementSnapshot( // Have we seen this pointer before? bool Gecko_HaveSeenPtr(mozilla::SeenPtrs* table, const void* ptr); -// `array` must be an nsTArray -// If changing this signature, please update the -// friend function declaration in nsTArray.h -void Gecko_EnsureTArrayCapacity(void* array, size_t capacity, size_t elem_size); - -// Same here, `array` must be an nsTArray<T>, for some T. -// -// Important note: Only valid for POD types, since destructors won't be run -// otherwise. This is ensured with rust traits for the relevant structs. -void Gecko_ClearPODTArray(void* array, size_t elem_size, size_t elem_align); - -void Gecko_ResizeTArrayForStrings(nsTArray<nsString>* array, uint32_t length); -void Gecko_ResizeAtomArray(nsTArray<RefPtr<nsAtom>>* array, uint32_t length); - void Gecko_EnsureImageLayersLength(nsStyleImageLayers* layers, size_t len, nsStyleImageLayers::LayerType layer_type); @@ -438,13 +424,6 @@ mozilla::Keyframe* Gecko_GetOrCreateFinalKeyframe( const mozilla::StyleComputedTimingFunction* timingFunction, const mozilla::dom::CompositeOperationOrAuto composition); -// Appends and returns a new PropertyValuePair to |aProperties| initialized with -// its mProperty member set to |aProperty| and all other members initialized to -// their default values. -mozilla::PropertyValuePair* Gecko_AppendPropertyValuePair( - nsTArray<mozilla::PropertyValuePair>*, - const mozilla::AnimatedPropertyID* aProperty); - void Gecko_ResetFilters(nsStyleEffects* effects, size_t new_len); void Gecko_CopyFiltersFrom(nsStyleEffects* aSrc, nsStyleEffects* aDest); diff --git a/layout/style/GenerateServoCSSPropList.py b/layout/style/GenerateServoCSSPropList.py index 0e9678ac5c..4dba1f0a8f 100644 --- a/layout/style/GenerateServoCSSPropList.py +++ b/layout/style/GenerateServoCSSPropList.py @@ -30,7 +30,7 @@ def generate_data(output, template): ) # Add all relevant files into the dependencies of the generated file. - DEP_EXTS = [".py", ".rs", ".zip"] + DEP_EXTS = [".py", ".rs"] deps = set() for path, dirs, files in os.walk(SERVO_PROP_BASE): for file in files: diff --git a/layout/style/ImageLoader.cpp b/layout/style/ImageLoader.cpp index 2ae1822b02..1a59a5bcf3 100644 --- a/layout/style/ImageLoader.cpp +++ b/layout/style/ImageLoader.cpp @@ -565,6 +565,16 @@ static void InvalidateImages(nsIFrame* aFrame, imgIRequest* aRequest, } } } +#ifdef XP_MACOSX + else if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) { + // On macOS popups are painted with fallback rendering so they don't have + // webrender user data to tell us if the frame was painted last time, so we + // just have to invalidate always. Bug 1754796 tracks making popups on macOS + // use webrender. (On other platforms tooltips type popups are still + // rendered with fallback, but we don't expect them to have images.) + invalidateFrame = true; + } +#endif // Update ancestor rendering observers (-moz-element etc) // diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 1ea37b094f..3133dd8631 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -18,12 +18,11 @@ #include "mozilla/AutoRestore.h" #include "mozilla/LoadInfo.h" #include "mozilla/Logging.h" +#include "mozilla/glean/GleanMetrics.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PreloadHashKey.h" #include "mozilla/ResultExtensions.h" -#include "mozilla/SchedulerGroup.h" #include "mozilla/URLPreloader.h" -#include "nsIChildChannel.h" #include "nsIPrincipal.h" #include "nsISupportsPriority.h" #include "nsITimedChannel.h" @@ -47,8 +46,6 @@ #include "nsMimeTypes.h" #include "nsICSSLoaderObserver.h" #include "nsThreadUtils.h" -#include "nsGkAtoms.h" -#include "nsIThreadInternal.h" #include "nsINetworkPredictor.h" #include "nsQueryActor.h" #include "nsStringStream.h" @@ -62,13 +59,10 @@ #include "mozilla/StyleSheet.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/ConsoleReportCollector.h" -#include "mozilla/ServoUtils.h" #include "mozilla/css/StreamLoader.h" #include "mozilla/SharedStyleSheetCache.h" #include "mozilla/StaticPrefs_layout.h" #include "mozilla/StaticPrefs_network.h" -#include "mozilla/StaticPrefs_dom.h" -#include "mozilla/StaticPrefs_network.h" #include "mozilla/Try.h" #include "ReferrerInfo.h" @@ -417,6 +411,23 @@ SheetLoadData::~SheetLoadData() { "dropping the load"); } +void SheetLoadData::StartLoading() { + MOZ_ASSERT(!mIsLoading, "Already loading? How?"); + mIsLoading = true; + mLoadStart = TimeStamp::Now(); +} + +void SheetLoadData::SetLoadCompleted() { + MOZ_ASSERT(mIsLoading, "Not loading?"); + MOZ_ASSERT(!mLoadStart.IsNull()); + mIsLoading = false; + // Belts and suspenders just in case. + if (MOZ_LIKELY(!mLoadStart.IsNull())) { + glean::performance_pageload::async_sheet_load.AccumulateRawDuration( + TimeStamp::Now() - mLoadStart); + } +} + RefPtr<StyleSheet> SheetLoadData::ValueForCache() const { // We need to clone the sheet on insertion to the cache because otherwise the // stylesheets can keep full windows alive via either their JS wrapper, or via diff --git a/layout/style/PreferenceSheet.cpp b/layout/style/PreferenceSheet.cpp index a206209885..8b0179a029 100644 --- a/layout/style/PreferenceSheet.cpp +++ b/layout/style/PreferenceSheet.cpp @@ -11,7 +11,6 @@ #include "mozilla/Encoding.h" #include "mozilla/Preferences.h" #include "mozilla/StaticPrefs_browser.h" -#include "mozilla/StaticPrefs_devtools.h" #include "mozilla/StaticPrefs_layout.h" #include "mozilla/StaticPrefs_widget.h" #include "mozilla/StaticPrefs_ui.h" @@ -205,7 +204,7 @@ void PreferenceSheet::Prefs::Load(bool aIsChrome) { // as those are the colors exposed to the user in the colors dialog. mMustUseLightColorSet = mUsePrefColors && !mUseDocumentColors; #ifdef XP_WIN - if (mUseAccessibilityTheme) { + if (mUseAccessibilityTheme && (mIsChrome || !mUseDocumentColors)) { // Windows overrides the light colors with the HCM colors when HCM is // active, so make sure to always use the light system colors in that case, // and also make sure that we always use the light color set for the same diff --git a/layout/style/Rule.cpp b/layout/style/Rule.cpp index 0a7de42789..deaa46576c 100644 --- a/layout/style/Rule.cpp +++ b/layout/style/Rule.cpp @@ -95,9 +95,10 @@ Rule* Rule::GetParentRule() const { return mParentRule; } #ifdef DEBUG void Rule::AssertParentRuleType() { - // Would be nice to check that this->Type() is KEYFRAME_RULE when - // mParentRule->Tye() is KEYFRAMES_RULE, but we can't call + // Would be nice to check that this->Type() is StyleCssRuleType::Keyframe + // when mParentRule->Tye() is StyleCssRuleType::Keyframes, but we can't call // this->Type() here since it's virtual. + // Same for StyleCssRuleType::Margin and StyleCssRuleType::Page. if (mParentRule) { auto type = mParentRule->Type(); MOZ_ASSERT(type == StyleCssRuleType::Media || @@ -106,7 +107,10 @@ void Rule::AssertParentRuleType() { type == StyleCssRuleType::Supports || type == StyleCssRuleType::Keyframes || type == StyleCssRuleType::LayerBlock || - type == StyleCssRuleType::Container); + type == StyleCssRuleType::Container || + type == StyleCssRuleType::Scope || + type == StyleCssRuleType::StartingStyle || + type == StyleCssRuleType::Page); } } #endif diff --git a/layout/style/ServoBindingTypes.h b/layout/style/ServoBindingTypes.h index fe4f96cf0b..e2443ef526 100644 --- a/layout/style/ServoBindingTypes.h +++ b/layout/style/ServoBindingTypes.h @@ -123,12 +123,15 @@ UNLOCKED_RULE_TYPE(Property) UNLOCKED_RULE_TYPE(LayerBlock) UNLOCKED_RULE_TYPE(LayerStatement) UNLOCKED_RULE_TYPE(Namespace) +UNLOCKED_RULE_TYPE(Margin) UNLOCKED_RULE_TYPE(Container) UNLOCKED_RULE_TYPE(Media) UNLOCKED_RULE_TYPE(Supports) UNLOCKED_RULE_TYPE(Document) UNLOCKED_RULE_TYPE(FontFeatureValues) UNLOCKED_RULE_TYPE(FontPaletteValues) +UNLOCKED_RULE_TYPE(Scope) +UNLOCKED_RULE_TYPE(StartingStyle) SERVO_ARC_TYPE(AnimationValue, mozilla::StyleAnimationValue) SERVO_ARC_TYPE(ComputedStyle, mozilla::ComputedStyle) diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 6f19006512..2c0abbe0db 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -77,7 +77,8 @@ BASIC_RULE_FUNCS_LOCKED(Keyframes) GROUP_RULE_FUNCS_UNLOCKED(Media) GROUP_RULE_FUNCS_UNLOCKED(Document) BASIC_RULE_FUNCS_UNLOCKED(Namespace) -BASIC_RULE_FUNCS_LOCKED(Page) +BASIC_RULE_FUNCS_UNLOCKED(Margin) +GROUP_RULE_FUNCS_LOCKED(Page) BASIC_RULE_FUNCS_UNLOCKED(Property) GROUP_RULE_FUNCS_UNLOCKED(Supports) GROUP_RULE_FUNCS_UNLOCKED(LayerBlock) @@ -87,6 +88,8 @@ BASIC_RULE_FUNCS_UNLOCKED(FontPaletteValues) BASIC_RULE_FUNCS_LOCKED(FontFace) BASIC_RULE_FUNCS_LOCKED(CounterStyle) GROUP_RULE_FUNCS_UNLOCKED(Container) +GROUP_RULE_FUNCS_UNLOCKED(Scope) +GROUP_RULE_FUNCS_UNLOCKED(StartingStyle) #undef GROUP_RULE_FUNCS_LOCKED #undef GROUP_RULE_FUNCS_UNLOCKED diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml index 20e1ca4116..833fbd0272 100644 --- a/layout/style/ServoBindings.toml +++ b/layout/style/ServoBindings.toml @@ -54,6 +54,7 @@ hide-types = [ "mozilla::StyleTimingFunction.*", # https://github.com/rust-lang/rust-bindgen/issues/1559 "mozilla::StyleGeneric.*", + "nsTArray_.*", ".*ErrorResult.*", ] bitfield-enums = [ @@ -290,7 +291,6 @@ allowlist-types = [ "nsStyleUI", "nsStyleVisibility", "nsStyleXUL", - "nsTArrayHeader", "mozilla::UniquePtr", "mozilla::DeclarationBlock", "mozilla::DefaultDelete", @@ -403,6 +403,7 @@ cbindgen-types = [ { gecko = "StyleOffsetRotate", servo = "crate::values::computed::motion::OffsetRotate" }, { gecko = "StylePathCommand", servo = "crate::values::specified::svg_path::PathCommand" }, { gecko = "StyleRayFunction", servo = "crate::values::computed::motion::RayFunction" }, + { gecko = "StyleParserState", servo = "cssparser::ParserState" }, { gecko = "StyleUnicodeRange", servo = "cssparser::UnicodeRange" }, { gecko = "StyleOverflowWrap", servo = "crate::values::computed::OverflowWrap" }, { gecko = "StyleWordBreak", servo = "crate::values::computed::WordBreak" }, @@ -515,6 +516,9 @@ cbindgen-types = [ { gecko = "StyleBasicShape", servo = "crate::values::computed::basic_shape::BasicShape" }, { gecko = "StyleGenericInsetRect", servo = "crate::values::generics::basic_shape::InsetRect" }, { gecko = "StyleInsetRect", servo = "crate::values::computed::basic_shape::InsetRect" }, + { gecko = "StyleShape", servo = "crate::values::computed::basic_shape::Shape" }, + { gecko = "StyleShapeCommand", servo = "crate::values::computed::basic_shape::ShapeCommand" }, + { gecko = "StyleGenericShapeCommand", servo = "crate::values::generics::basic_shape::ShapeCommand" }, { gecko = "StyleArcSlice", servo = "style_traits::arc_slice::ArcSlice" }, { gecko = "StyleForgottenArcSlicePtr", servo = "style_traits::arc_slice::ForgottenArcSlicePtr" }, { gecko = "StyleOwnedSlice", servo = "style_traits::owned_slice::OwnedSlice" }, @@ -628,6 +632,8 @@ mapped-generic-types = [ { generic = false, gecko = "nsAString", servo = "nsstring::nsAString" }, { generic = false, gecko = "nsCString", servo = "nsstring::nsCString" }, { generic = false, gecko = "nsString", servo = "nsstring::nsString" }, + { generic = true, gecko = "nsTArray", servo = "thin_vec::ThinVec" }, + { generic = true, gecko = "CopyableTArray", servo = "thin_vec::ThinVec" }, ] allowlist-functions = ["Servo_.*", "Gecko_.*"] diff --git a/layout/style/ServoCSSRuleList.cpp b/layout/style/ServoCSSRuleList.cpp index 6fcdfdd4b5..b7172879de 100644 --- a/layout/style/ServoCSSRuleList.cpp +++ b/layout/style/ServoCSSRuleList.cpp @@ -17,11 +17,14 @@ #include "mozilla/dom/CSSLayerStatementRule.h" #include "mozilla/dom/CSSKeyframesRule.h" #include "mozilla/dom/CSSContainerRule.h" +#include "mozilla/dom/CSSMarginRule.h" #include "mozilla/dom/CSSMediaRule.h" #include "mozilla/dom/CSSMozDocumentRule.h" #include "mozilla/dom/CSSNamespaceRule.h" #include "mozilla/dom/CSSPageRule.h" #include "mozilla/dom/CSSPropertyRule.h" +#include "mozilla/dom/CSSScopeRule.h" +#include "mozilla/dom/CSSStartingStyleRule.h" #include "mozilla/dom/CSSStyleRule.h" #include "mozilla/dom/CSSSupportsRule.h" #include "mozilla/IntegerRange.h" @@ -86,6 +89,7 @@ css::Rule* ServoCSSRuleList::GetRule(uint32_t aIndex) { CASE_RULE_LOCKED(Keyframes, Keyframes) CASE_RULE_UNLOCKED(Media, Media) CASE_RULE_UNLOCKED(Namespace, Namespace) + CASE_RULE_UNLOCKED(Margin, Margin) CASE_RULE_LOCKED(Page, Page) CASE_RULE_UNLOCKED(Property, Property) CASE_RULE_UNLOCKED(Supports, Supports) @@ -98,15 +102,14 @@ css::Rule* ServoCSSRuleList::GetRule(uint32_t aIndex) { CASE_RULE_UNLOCKED(LayerBlock, LayerBlock) CASE_RULE_UNLOCKED(LayerStatement, LayerStatement) CASE_RULE_UNLOCKED(Container, Container) + CASE_RULE_UNLOCKED(Scope, Scope) + CASE_RULE_UNLOCKED(StartingStyle, StartingStyle) #undef CASE_RULE_LOCKED #undef CASE_RULE_UNLOCKED #undef CASE_RULE_WITH_PREFIX case StyleCssRuleType::Keyframe: MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here"); return nullptr; - case StyleCssRuleType::Margin: - // Margin rules not implemented yet, see bug 1864737 - return nullptr; } rule = CastToUint(ruleObj.forget().take()); mRules[aIndex] = rule; @@ -208,12 +211,21 @@ nsresult ServoCSSRuleList::InsertRule(const nsACString& aRule, } StyleCssRuleType type; uint32_t containingTypes = 0; + Maybe<StyleCssRuleType> parseRelativeRuleType; for (css::Rule* rule = mParentRule; rule; rule = rule->GetParentRule()) { - containingTypes |= (1 << uint32_t(rule->Type())); + const auto ruleType = rule->Type(); + containingTypes |= (1 << uint32_t(ruleType)); + if (parseRelativeRuleType.isNothing() && + (ruleType == StyleCssRuleType::Style || + ruleType == StyleCssRuleType::Scope)) { + // Only the closest applicable type to this rule matters. + parseRelativeRuleType = Some(ruleType); + } } nsresult rv = Servo_CssRules_InsertRule( mRawRules, mStyleSheet->RawContents(), &aRule, aIndex, containingTypes, - loader, allowImportRules, mStyleSheet, &type); + parseRelativeRuleType.ptrOr(nullptr), loader, allowImportRules, + mStyleSheet, &type); NS_ENSURE_SUCCESS(rv, rv); mRules.InsertElementAt(aIndex, uintptr_t(type)); return rv; @@ -264,6 +276,7 @@ void ServoCSSRuleList::SetRawContents(RefPtr<StyleLockedCssRules> aNewRules, RULE_CASE_LOCKED(Keyframes, Keyframes) RULE_CASE_UNLOCKED(Media, Media) RULE_CASE_UNLOCKED(Namespace, Namespace) + RULE_CASE_UNLOCKED(Margin, Margin) RULE_CASE_LOCKED(Page, Page) RULE_CASE_UNLOCKED(Property, Property) RULE_CASE_UNLOCKED(Supports, Supports) @@ -276,12 +289,11 @@ void ServoCSSRuleList::SetRawContents(RefPtr<StyleLockedCssRules> aNewRules, RULE_CASE_UNLOCKED(LayerBlock, LayerBlock) RULE_CASE_UNLOCKED(LayerStatement, LayerStatement) RULE_CASE_UNLOCKED(Container, Container) + RULE_CASE_UNLOCKED(Scope, Scope) + RULE_CASE_UNLOCKED(StartingStyle, StartingStyle) case StyleCssRuleType::Keyframe: MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here"); break; - case StyleCssRuleType::Margin: - // Margin rules not implemented yet, see bug 1864737 - break; } #undef RULE_CASE_WITH_PREFIX #undef RULE_CASE_LOCKED diff --git a/layout/style/ServoElementSnapshot.cpp b/layout/style/ServoElementSnapshot.cpp index e0ef2849f9..a5251d9349 100644 --- a/layout/style/ServoElementSnapshot.cpp +++ b/layout/style/ServoElementSnapshot.cpp @@ -82,4 +82,11 @@ void ServoElementSnapshot::AddAttrs(const Element& aElement, } } +void ServoElementSnapshot::AddCustomStates(Element& aElement) { + if (mContains & Flags::CustomState) { + return; + } + mCustomStates = aElement.EnsureCustomStates().Clone(); + mContains |= Flags::CustomState; +} } // namespace mozilla diff --git a/layout/style/ServoElementSnapshot.h b/layout/style/ServoElementSnapshot.h index b702975c4b..5a86b4c13d 100644 --- a/layout/style/ServoElementSnapshot.h +++ b/layout/style/ServoElementSnapshot.h @@ -33,6 +33,7 @@ enum class ServoElementSnapshotFlags : uint8_t { Id = 1 << 2, MaybeClass = 1 << 3, OtherPseudoClassState = 1 << 4, + CustomState = 1 << 5, }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoElementSnapshotFlags) @@ -89,6 +90,11 @@ class ServoElementSnapshot { void AddAttrs(const Element&, int32_t aNameSpaceID, nsAtom* aAttribute); /** + * Captures the given element custom states. + */ + void AddCustomStates(Element&); + + /** * Captures some other pseudo-class matching state not included in * ElementState. */ @@ -155,6 +161,7 @@ class ServoElementSnapshot { // snapshots. nsTArray<AttrArray::InternalAttr> mAttrs; nsTArray<RefPtr<nsAtom>> mChangedAttrNames; + nsTArray<RefPtr<nsAtom>> mCustomStates; nsAttrValue mClass; ServoStateType mState; Flags mContains; diff --git a/layout/style/ServoLockedArcTypeList.h b/layout/style/ServoLockedArcTypeList.h index 2d356aabf9..70adc7bd8e 100644 --- a/layout/style/ServoLockedArcTypeList.h +++ b/layout/style/ServoLockedArcTypeList.h @@ -19,6 +19,7 @@ SERVO_LOCKED_ARC_TYPE(StyleRule) SERVO_LOCKED_ARC_TYPE(ImportRule) SERVO_LOCKED_ARC_TYPE(Keyframe) SERVO_LOCKED_ARC_TYPE(KeyframesRule) +SERVO_LOCKED_ARC_TYPE(MarginList) SERVO_LOCKED_ARC_TYPE(MediaList) SERVO_LOCKED_ARC_TYPE(PageRule) SERVO_LOCKED_ARC_TYPE(FontFaceRule) diff --git a/layout/style/ServoStyleConstsForwards.h b/layout/style/ServoStyleConstsForwards.h index 65128e1377..cd2958618f 100644 --- a/layout/style/ServoStyleConstsForwards.h +++ b/layout/style/ServoStyleConstsForwards.h @@ -83,12 +83,13 @@ struct PropertyStyleAnimationValuePair; using ComputedKeyframeValues = nsTArray<PropertyStyleAnimationValuePair>; class ComputedStyle; -enum LogicalAxis : uint8_t; +enum class LogicalAxis : uint8_t; class SeenPtrs; class SharedFontList; class StyleSheet; class WritingMode; class ServoElementSnapshotTable; +class StyleParserState; template <typename T> struct StyleForgottenArcSlicePtr; @@ -100,7 +101,7 @@ struct ComputedTiming; struct URLExtraData; enum HalfCorner : uint8_t; -enum LogicalSide : uint8_t; +enum class LogicalSide : uint8_t; enum class PseudoStyleType : uint8_t; enum class OriginFlags : uint8_t; enum class UseBoxSizing : uint8_t; diff --git a/layout/style/ServoStyleConstsInlines.h b/layout/style/ServoStyleConstsInlines.h index 1985e8fbeb..50893f5112 100644 --- a/layout/style/ServoStyleConstsInlines.h +++ b/layout/style/ServoStyleConstsInlines.h @@ -45,6 +45,7 @@ template struct StyleStrong<StyleLockedKeyframesRule>; template struct StyleStrong<StyleMediaRule>; template struct StyleStrong<StyleDocumentRule>; template struct StyleStrong<StyleNamespaceRule>; +template struct StyleStrong<StyleMarginRule>; template struct StyleStrong<StyleLockedPageRule>; template struct StyleStrong<StylePropertyRule>; template struct StyleStrong<StyleSupportsRule>; @@ -53,6 +54,8 @@ template struct StyleStrong<StyleFontPaletteValuesRule>; template struct StyleStrong<StyleLockedFontFaceRule>; template struct StyleStrong<StyleLockedCounterStyleRule>; template struct StyleStrong<StyleContainerRule>; +template struct StyleStrong<StyleScopeRule>; +template struct StyleStrong<StyleStartingStyleRule>; template <typename T> inline void StyleOwnedSlice<T>::Clear() { @@ -697,9 +700,12 @@ CSSCoord StyleCalcLengthPercentage::ResolveToCSSPixels(CSSCoord aBasis) const { return Servo_ResolveCalcLengthPercentage(this, aBasis); } -nscoord StyleCalcLengthPercentage::Resolve(nscoord aBasis) const { - return detail::DefaultLengthToAppUnits( - ResolveToCSSPixels(CSSPixel::FromAppUnits(aBasis))); +template <typename Rounder> +nscoord StyleCalcLengthPercentage::Resolve(nscoord aBasis, + Rounder aRounder) const { + static_assert(std::is_same_v<decltype(aRounder(1.0f)), nscoord>); + CSSCoord result = ResolveToCSSPixels(CSSPixel::FromAppUnits(aBasis)); + return aRounder(result * AppUnitsPerCSSPixel()); } template <> @@ -724,11 +730,10 @@ CSSCoord LengthPercentage::ResolveToCSSPixelsWith(T aPercentageGetter) const { return ResolveToCSSPixels(aPercentageGetter()); } -template <typename T, typename PercentRounder> -nscoord LengthPercentage::Resolve(T aPercentageGetter, - PercentRounder aPercentRounder) const { +template <typename T, typename Rounder> +nscoord LengthPercentage::Resolve(T aPercentageGetter, Rounder aRounder) const { static_assert(std::is_same_v<decltype(aPercentageGetter()), nscoord>); - static_assert(std::is_same_v<decltype(aPercentRounder(1.0f)), nscoord>); + static_assert(std::is_same_v<decltype(aRounder(1.0f)), nscoord>); if (ConvertsToLength()) { return ToLength(); } @@ -737,9 +742,9 @@ nscoord LengthPercentage::Resolve(T aPercentageGetter, } nscoord basis = aPercentageGetter(); if (IsPercentage()) { - return aPercentRounder(basis * AsPercentage()._0); + return aRounder(basis * AsPercentage()._0); } - return AsCalc().Resolve(basis); + return AsCalc().Resolve(basis, aRounder); } nscoord LengthPercentage::Resolve(nscoord aPercentageBasis) const { @@ -752,11 +757,10 @@ nscoord LengthPercentage::Resolve(T aPercentageGetter) const { return Resolve(aPercentageGetter, detail::DefaultPercentLengthToAppUnits); } -template <typename PercentRounder> +template <typename Rounder> nscoord LengthPercentage::Resolve(nscoord aPercentageBasis, - PercentRounder aPercentRounder) const { - return Resolve([aPercentageBasis] { return aPercentageBasis; }, - aPercentRounder); + Rounder aRounder) const { + return Resolve([aPercentageBasis] { return aPercentageBasis; }, aRounder); } void LengthPercentage::ScaleLengthsBy(float aScale) { @@ -1201,6 +1205,20 @@ inline nsRect StyleZoom::Unzoom(const nsRect& aValue) const { UnzoomCoord(aValue.Width()), UnzoomCoord(aValue.Height())); } +template <> +inline gfx::Point StyleCoordinatePair<StyleCSSFloat>::ToGfxPoint( + const CSSSize* aBasis) const { + return gfx::Point(x, y); +} + +template <> +inline gfx::Point StyleCoordinatePair<LengthPercentage>::ToGfxPoint( + const CSSSize* aBasis) const { + MOZ_ASSERT(aBasis); + return gfx::Point(x.ResolveToCSSPixels(aBasis->Width()), + y.ResolveToCSSPixels(aBasis->Height())); +} + } // namespace mozilla #endif diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index cfc38849b8..22359fa82d 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -34,6 +34,7 @@ #include "mozilla/dom/CSSContainerRule.h" #include "mozilla/dom/CSSLayerBlockRule.h" #include "mozilla/dom/CSSLayerStatementRule.h" +#include "mozilla/dom/CSSMarginRule.h" #include "mozilla/dom/CSSMediaRule.h" #include "mozilla/dom/CSSMozDocumentRule.h" #include "mozilla/dom/CSSKeyframesRule.h" @@ -41,7 +42,9 @@ #include "mozilla/dom/CSSNamespaceRule.h" #include "mozilla/dom/CSSPageRule.h" #include "mozilla/dom/CSSPropertyRule.h" +#include "mozilla/dom/CSSScopeRule.h" #include "mozilla/dom/CSSSupportsRule.h" +#include "mozilla/dom/CSSStartingStyleRule.h" #include "mozilla/dom/FontFaceSet.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/ElementInlines.h" @@ -764,7 +767,7 @@ bool ServoStyleSet::GeneratedContentPseudoExists( if (!aPseudoStyle.StyleContent()->mContent.IsItems()) { return false; } - MOZ_ASSERT(aPseudoStyle.StyleContent()->ContentCount() > 0, + MOZ_ASSERT(!aPseudoStyle.StyleContent()->NonAltContentItems().IsEmpty(), "IsItems() implies we have at least one item"); // display:none is equivalent to not having a pseudo at all. if (aPseudoStyle.StyleDisplay()->mDisplay == StyleDisplay::None) { @@ -992,6 +995,7 @@ void ServoStyleSet::RuleChangedInternal(StyleSheet& aSheet, css::Rule& aRule, CASE_FOR(Import, Import) CASE_FOR(Media, Media) CASE_FOR(Keyframes, Keyframes) + CASE_FOR(Margin, Margin) CASE_FOR(FontFeatureValues, FontFeatureValues) CASE_FOR(FontPaletteValues, FontPaletteValues) CASE_FOR(FontFace, FontFace) @@ -1002,6 +1006,8 @@ void ServoStyleSet::RuleChangedInternal(StyleSheet& aSheet, css::Rule& aRule, CASE_FOR(LayerBlock, LayerBlock) CASE_FOR(LayerStatement, LayerStatement) CASE_FOR(Container, Container) + CASE_FOR(Scope, Scope) + CASE_FOR(StartingStyle, StartingStyle) // @namespace can only be inserted / removed when there are only other // @namespace and @import rules, and can't be mutated. case StyleCssRuleType::Namespace: @@ -1010,9 +1016,6 @@ void ServoStyleSet::RuleChangedInternal(StyleSheet& aSheet, css::Rule& aRule, // FIXME: We should probably just forward to the parent @keyframes rule? I // think that'd do the right thing, but meanwhile... return MarkOriginsDirty(ToOriginFlags(aSheet.GetOrigin())); - case StyleCssRuleType::Margin: - // Margin rules not implemented yet, see bug 1864737 - break; } #undef CASE_FOR @@ -1394,6 +1397,13 @@ void ServoStyleSet::MaybeInvalidateRelativeSelectorClassDependency( mRawData.get(), &aElement, &aSnapshots); } +void ServoStyleSet::MaybeInvalidateRelativeSelectorCustomStateDependency( + const Element& aElement, nsAtom* state, + const ServoElementSnapshotTable& aSnapshots) { + Servo_StyleSet_MaybeInvalidateRelativeSelectorCustomStateDependency( + mRawData.get(), &aElement, state, &aSnapshots); +} + void ServoStyleSet::MaybeInvalidateRelativeSelectorAttributeDependency( const Element& aElement, nsAtom* aAttribute, const ServoElementSnapshotTable& aSnapshots) { @@ -1465,6 +1475,12 @@ bool ServoStyleSet::HasNthOfStateDependency(const Element& aElement, aState.GetInternalValue()); } +bool ServoStyleSet::HasNthOfCustomStateDependency(const Element& aElement, + nsAtom* aState) const { + return Servo_StyleSet_HasNthOfCustomStateDependency(mRawData.get(), &aElement, + aState); +} + void ServoStyleSet::RestyleSiblingsForNthOf(const Element& aElement, uint32_t aFlags) const { Servo_StyleSet_RestyleSiblingsForNthOf(&aElement, aFlags); diff --git a/layout/style/ServoStyleSet.h b/layout/style/ServoStyleSet.h index eee6cba0f7..f3159c97ec 100644 --- a/layout/style/ServoStyleSet.h +++ b/layout/style/ServoStyleSet.h @@ -477,6 +477,14 @@ class ServoStyleSet { const dom::Element&, const ServoElementSnapshotTable& aSnapshots); /** + * Maybe invalidate if a modification to a Custom State might require us to + * restyle the relative selector it refers to. + */ + void MaybeInvalidateRelativeSelectorCustomStateDependency( + const dom::Element&, nsAtom* state, + const ServoElementSnapshotTable& aSnapshots); + + /** * Maybe invalidate if a modification to an ID might require us to restyle * the relative selector it refers to. */ @@ -550,6 +558,12 @@ class ServoStyleSet { bool HasNthOfStateDependency(const dom::Element&, dom::ElementState) const; /** + * Returns true if a change in Custom State on an element might require + * us to restyle the element's siblings. + */ + bool HasNthOfCustomStateDependency(const dom::Element&, nsAtom*) const; + + /** * Restyle this element's siblings in order to propagate any potential change * in :nth-child(of) styling. */ diff --git a/layout/style/SharedSubResourceCache.h b/layout/style/SharedSubResourceCache.h index 32919b8863..896eacbcb5 100644 --- a/layout/style/SharedSubResourceCache.h +++ b/layout/style/SharedSubResourceCache.h @@ -58,6 +58,9 @@ struct SharedSubResourceCacheLoadingValueBase { virtual void SetLoadCompleted() = 0; virtual void Cancel() = 0; + // Return the next sub-resource which has the same key. + Derived* GetNextSubResource() { return mNext; } + ~SharedSubResourceCacheLoadingValueBase() { // Do this iteratively to avoid blowing up the stack. RefPtr<Derived> next = std::move(mNext); diff --git a/layout/style/SheetLoadData.h b/layout/style/SheetLoadData.h index 6621af35bd..2f829d746e 100644 --- a/layout/style/SheetLoadData.h +++ b/layout/style/SheetLoadData.h @@ -239,6 +239,9 @@ class SheetLoadData final // listening for the load. bool mIntentionallyDropped = false; + // The start timestamp for the load. + TimeStamp mLoadStart; + const bool mRecordErrors; bool ShouldDefer() const { return mWasAlternate || !mMediaMatched; } @@ -269,8 +272,9 @@ class SheetLoadData final bool IsLoading() const override { return mIsLoading; } bool IsCancelled() const override { return mIsCancelled; } - void StartLoading() override { mIsLoading = true; } - void SetLoadCompleted() override { mIsLoading = false; } + void StartLoading() override; + void SetLoadCompleted() override; + void Cancel() override { mIsCancelled = true; } private: diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list index d54f053f1b..2568a6e036 100644 --- a/layout/style/crashtests/crashtests.list +++ b/layout/style/crashtests/crashtests.list @@ -166,7 +166,7 @@ load 1290994-4.html load 1314531.html load 1315889-1.html load 1315894-1.html -pref(widget.windows.window_occlusion_tracking.enabled,false) skip-if(wayland) load 1319072-1.html # Bug 1819154, wayland: bug 1856389 +load 1319072-1.html HTTP load 1320423-1.html load 1321357-1.html load 1328535-1.html @@ -174,8 +174,8 @@ load 1331272.html load 1332550.html HTTP load 1333001-1.html load 1340248.html -skip-if(wayland) load 1340344.html # wayland: bug 1856389 -skip-if(wayland) load 1342316-1.html # wayland: bug 1856389 +load 1340344.html +load 1342316-1.html load 1344210.html load 1353312.html load 1356601-1.html @@ -185,19 +185,19 @@ load 1374175-1.html load 1375812-1.html load 1377053-1.html load 1377256-1.html -skip-if(wayland) load 1378064-1.html # wayland: bug 1856389 +load 1378064-1.html load 1378814.html load 1380800.html load link-transition-before.html -skip-if(wayland) load 1381420-1.html # wayland: bug 1856389 +load 1381420-1.html load 1381682.html load 1382672.html load 1382710.html pref(dom.animations-api.compositing.enabled,true) load 1383493-1.html -skip-if(wayland) load 1383001.html # wayland: bug 1856389 +load 1383001.html load 1383001-2.html load 1383319.html -skip-if(wayland) load 1383589-1.html # wayland: bug 1856389 +load 1383589-1.html load 1383975.html load border-image-visited-link.html load content-only-on-link-before.html @@ -206,7 +206,7 @@ load font-face-truncated-src.html load large_border_image_width.html load link-transition-before.html load long-url-list-stack-overflow.html #Bug 1525117 -skip-if(wayland) load scale-on-block-continuation.html # wayland: bug 1856389 +load scale-on-block-continuation.html skip-if(AddressSanitizer) skip-if(ThreadSanitizer) load 1383981.html # Very sensitive to stack size. skip-if(AddressSanitizer) skip-if(ThreadSanitizer) load 1383981-2.html skip-if(AddressSanitizer) skip-if(ThreadSanitizer) load 1383981-3.html @@ -239,7 +239,7 @@ load 1400936-1.html load 1400936-2.html load 1401256.html load 1401706.html -skip-if(wayland) load 1401801.html # wayland: bug 1856389 +load 1401801.html load 1401825.html load 1402218-1.html load 1402366.html @@ -270,8 +270,8 @@ load 1413288.html load 1413361.html load 1413670.html load 1415353.html -skip-if(wayland) load 1418059.html # wayland: bug 1856389 -skip-if(wayland) load 1418867.html # wayland: bug 1856389 +load 1418059.html +load 1418867.html load 1419554.html load 1426312.html load 1439793.html diff --git a/layout/style/moz.build b/layout/style/moz.build index a14ab6a7ac..ce9ee36cea 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -56,6 +56,7 @@ EXPORTS += [ "nsCSSValue.h", "nsDOMCSSAttrDeclaration.h", "nsDOMCSSDeclaration.h", + "nsFontFaceLoader.h", "nsICSSDeclaration.h", "nsICSSLoaderObserver.h", "nsStyleAutoArray.h", @@ -137,12 +138,15 @@ EXPORTS.mozilla.dom += [ "CSSKeyframesRule.h", "CSSLayerBlockRule.h", "CSSLayerStatementRule.h", + "CSSMarginRule.h", "CSSMediaRule.h", "CSSMozDocumentRule.h", "CSSNamespaceRule.h", "CSSPageRule.h", "CSSPropertyRule.h", "CSSRuleList.h", + "CSSScopeRule.h", + "CSSStartingStyleRule.h", "CSSStyleRule.h", "CSSSupportsRule.h", "CSSValue.h", @@ -188,12 +192,15 @@ UNIFIED_SOURCES += [ "CSSKeyframesRule.cpp", "CSSLayerBlockRule.cpp", "CSSLayerStatementRule.cpp", + "CSSMarginRule.cpp", "CSSMediaRule.cpp", "CSSMozDocumentRule.cpp", "CSSNamespaceRule.cpp", "CSSPageRule.cpp", "CSSPropertyRule.cpp", "CSSRuleList.cpp", + "CSSScopeRule.cpp", + "CSSStartingStyleRule.cpp", "CSSStyleRule.cpp", "CSSSupportsRule.cpp", "DeclarationBlock.cpp", diff --git a/layout/style/nsCSSPseudoElementList.h b/layout/style/nsCSSPseudoElementList.h index 9514dd6c4b..7492523c58 100644 --- a/layout/style/nsCSSPseudoElementList.h +++ b/layout/style/nsCSSPseudoElementList.h @@ -51,6 +51,7 @@ CSS_PSEUDO_ELEMENT(highlight, ":highlight", 0) CSS_PSEUDO_ELEMENT(selection, ":selection", CSS_PSEUDO_ELEMENT_CONTAINS_ELEMENTS) +CSS_PSEUDO_ELEMENT(targetText, ":target-text", 0) // XXXbz should we really allow random content to style these? Maybe // use our flags to prevent that? CSS_PSEUDO_ELEMENT(mozFocusInner, ":-moz-focus-inner", 0) diff --git a/layout/style/nsCSSPseudoElements.h b/layout/style/nsCSSPseudoElements.h index 9866fbfb7e..baeee9d4de 100644 --- a/layout/style/nsCSSPseudoElements.h +++ b/layout/style/nsCSSPseudoElements.h @@ -130,6 +130,8 @@ class nsCSSPseudoElements { switch (aType) { case Type::highlight: return mozilla::StaticPrefs::dom_customHighlightAPI_enabled(); + case Type::targetText: + return mozilla::StaticPrefs::dom_text_fragments_enabled(); case Type::sliderTrack: case Type::sliderThumb: case Type::sliderFill: diff --git a/layout/style/nsCSSValue.h b/layout/style/nsCSSValue.h index bc8914bbc1..540ac91a49 100644 --- a/layout/style/nsCSSValue.h +++ b/layout/style/nsCSSValue.h @@ -55,14 +55,16 @@ enum nsCSSUnit : uint32_t { // different behavior than percent) // Font relative measure - eCSSUnit_EM = 800, // == current font size - eCSSUnit_XHeight = 801, // distance from top of lower case x to - // baseline - eCSSUnit_Char = 802, // number of characters, used for width with - // monospace font - eCSSUnit_RootEM = 803, // == root element font size - eCSSUnit_Ideographic = 804, // == CJK water ideograph width - eCSSUnit_CapHeight = 805, // == Capital letter height + eCSSUnit_EM = 800, // == current font size + eCSSUnit_XHeight = 801, // distance from top of lower case x to + // baseline + eCSSUnit_Char = 802, // number of characters, used for width with + // monospace font + eCSSUnit_RootEM = 803, // == root element font size + eCSSUnit_Ideographic = 804, // == CJK water ideograph width + eCSSUnit_CapHeight = 805, // == Capital letter height + eCSSUnit_LineHeight = 806, // == Line height + eCSSUnit_RootLineHeight = 807, // == Root line height // Screen relative measure eCSSUnit_Point = 900, // 4/3 of a CSS pixel diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index d40d4bc801..876f32cd7a 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -1358,11 +1358,28 @@ already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::MatrixToCSSValue( /* Create a value to hold our result. */ RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - val->SetString(resultString); return val.forget(); } +already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::AppUnitsToCSSValue( + nscoord aAppUnits) { + return PixelsToCSSValue(CSSPixel::FromAppUnits(aAppUnits)); +} + +already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::PixelsToCSSValue( + float aPixels) { + RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; + SetValueToPixels(val, aPixels); + return val.forget(); +} + +void nsComputedDOMStyle::SetValueToPixels(nsROCSSPrimitiveValue* aValue, + float aPixels) { + MOZ_ASSERT(mComputedStyle); + aValue->SetPixels(mComputedStyle->EffectiveZoom().Unzoom(aPixels)); +} + already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMozOsxFontSmoothing() { if (nsContentUtils::ShouldResistFingerprinting( mPresShell->GetPresContext()->GetDocShell(), @@ -1614,9 +1631,7 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows( // Add any leading implicit tracks. if (serializeImplicit) { for (uint32_t i = 0; i < numLeadingImplicitTracks; ++i) { - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - val->SetAppUnits(trackSizes[i]); - valueList->AppendCSSValue(val.forget()); + valueList->AppendCSSValue(AppUnitsToCSSValue(trackSizes[i])); } } @@ -1652,8 +1667,7 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows( for (uint32_t i = 0; i < repeatStart; i++) { AppendGridLineNames(valueList, aTrackInfo.mResolvedLineNames[i]); RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - val->SetAppUnits(*trackSizeIter++); - valueList->AppendCSSValue(val.forget()); + valueList->AppendCSSValue(AppUnitsToCSSValue(*trackSizeIter++)); } auto lineNameIter = aTrackInfo.mResolvedLineNames.cbegin() + repeatStart; // Write the track names at the start of the repeat, including the names @@ -1666,9 +1680,7 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows( // track). const nscoord firstRepeatTrackSize = (!aTrackInfo.mRemovedRepeatTracks[0]) ? *trackSizeIter++ : 0; - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - val->SetAppUnits(firstRepeatTrackSize); - valueList->AppendCSSValue(val.forget()); + valueList->AppendCSSValue(AppUnitsToCSSValue(firstRepeatTrackSize)); } // Write the line names and track sizes inside the repeat, checking for // removed tracks (size 0). @@ -1688,9 +1700,7 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows( trackSizeIter != explicitTrackSizeEnd); const nscoord repeatTrackSize = (!aTrackInfo.mRemovedRepeatTracks[i]) ? *trackSizeIter++ : 0; - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - val->SetAppUnits(repeatTrackSize); - valueList->AppendCSSValue(val.forget()); + valueList->AppendCSSValue(AppUnitsToCSSValue(repeatTrackSize)); } // The resolved line names include a single repetition of the auto-repeat // line names. Skip over those. @@ -1698,9 +1708,7 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows( // Write out any more tracks after the repeat. while (trackSizeIter != explicitTrackSizeEnd) { AppendGridLineNames(valueList, *lineNameIter++); - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - val->SetAppUnits(*trackSizeIter++); - valueList->AppendCSSValue(val.forget()); + valueList->AppendCSSValue(AppUnitsToCSSValue(*trackSizeIter++)); } // Write the final trailing line name. AppendGridLineNames(valueList, *lineNameIter++); @@ -1711,18 +1719,15 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows( if (i == numExplicitTracks) { break; } - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - val->SetAppUnits(trackSizes[i + numLeadingImplicitTracks]); - valueList->AppendCSSValue(val.forget()); + valueList->AppendCSSValue( + AppUnitsToCSSValue(trackSizes[i + numLeadingImplicitTracks])); } } // Add any trailing implicit tracks. if (serializeImplicit) { for (uint32_t i = numLeadingImplicitTracks + numExplicitTracks; i < numSizes; ++i) { - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - val->SetAppUnits(trackSizes[i]); - valueList->AppendCSSValue(val.forget()); + valueList->AppendCSSValue(AppUnitsToCSSValue(trackSizes[i])); } } @@ -1787,15 +1792,9 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingRight() { already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderSpacing() { RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false); - RefPtr<nsROCSSPrimitiveValue> xSpacing = new nsROCSSPrimitiveValue; - RefPtr<nsROCSSPrimitiveValue> ySpacing = new nsROCSSPrimitiveValue; - const nsStyleTableBorder* border = StyleTableBorder(); - xSpacing->SetAppUnits(border->mBorderSpacingCol); - ySpacing->SetAppUnits(border->mBorderSpacingRow); - - valueList->AppendCSSValue(xSpacing.forget()); - valueList->AppendCSSValue(ySpacing.forget()); + valueList->AppendCSSValue(AppUnitsToCSSValue(border->mBorderSpacingCol)); + valueList->AppendCSSValue(AppUnitsToCSSValue(border->mBorderSpacingRow)); return valueList.forget(); } @@ -1833,32 +1832,26 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginRight() { } already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetHeight() { - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - if (mInnerFrame && !IsNonReplacedInline(mInnerFrame)) { AssertFlushedPendingReflows(); - nsMargin adjustedValues = GetAdjustedValuesForBoxSizing(); - val->SetAppUnits(mInnerFrame->GetContentRect().height + - adjustedValues.TopBottom()); - } else { - SetValueToSize(val, StylePosition()->mHeight); + const nsMargin adjustedValues = GetAdjustedValuesForBoxSizing(); + return AppUnitsToCSSValue(mInnerFrame->GetContentRect().height + + adjustedValues.TopBottom()); } - + RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; + SetValueToSize(val, StylePosition()->mHeight); return val.forget(); } already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetWidth() { - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - if (mInnerFrame && !IsNonReplacedInline(mInnerFrame)) { AssertFlushedPendingReflows(); nsMargin adjustedValues = GetAdjustedValuesForBoxSizing(); - val->SetAppUnits(mInnerFrame->GetContentRect().width + - adjustedValues.LeftRight()); - } else { - SetValueToSize(val, StylePosition()->mWidth); + return AppUnitsToCSSValue(mInnerFrame->GetContentRect().width + + adjustedValues.LeftRight()); } - + RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; + SetValueToSize(val, StylePosition()->mWidth); return val.forget(); } @@ -1895,7 +1888,8 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinHeight() { RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; StyleSize minHeight = StylePosition()->mMinHeight; - if (minHeight.IsAuto() && !ShouldHonorMinSizeAutoInAxis(eAxisVertical)) { + if (minHeight.IsAuto() && + !ShouldHonorMinSizeAutoInAxis(PhysicalAxis::Vertical)) { minHeight = StyleSize::LengthPercentage(LengthPercentage::Zero()); } @@ -1908,7 +1902,8 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinWidth() { StyleSize minWidth = StylePosition()->mMinWidth; - if (minWidth.IsAuto() && !ShouldHonorMinSizeAutoInAxis(eAxisHorizontal)) { + if (minWidth.IsAuto() && + !ShouldHonorMinSizeAutoInAxis(PhysicalAxis::Horizontal)) { minWidth = StyleSize::LengthPercentage(LengthPercentage::Zero()); } @@ -1968,14 +1963,13 @@ static_assert(eSideTop == 0 && eSideRight == 1 && eSideBottom == 2 && already_AddRefed<CSSValue> nsComputedDOMStyle::GetNonStaticPositionOffset( mozilla::Side aSide, bool aResolveAuto, PercentageBaseGetter aWidthGetter, PercentageBaseGetter aHeightGetter) { - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - const nsStylePosition* positionData = StylePosition(); int32_t sign = 1; LengthPercentageOrAuto coord = positionData->mOffset.Get(aSide); if (coord.IsAuto()) { if (!aResolveAuto) { + RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; val->SetString("auto"); return val.forget(); } @@ -1983,14 +1977,12 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetNonStaticPositionOffset( sign = -1; } if (!coord.IsLengthPercentage()) { - val->SetPixels(0.0f); - return val.forget(); + return PixelsToCSSValue(0.0f); } - auto& lp = coord.AsLengthPercentage(); + const auto& lp = coord.AsLengthPercentage(); if (lp.ConvertsToLength()) { - val->SetPixels(sign * lp.ToLengthInCSSPixels()); - return val.forget(); + return PixelsToCSSValue(sign * lp.ToLengthInCSSPixels()); } PercentageBaseGetter baseGetter = (aSide == eSideLeft || aSide == eSideRight) @@ -1998,12 +1990,10 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetNonStaticPositionOffset( : aHeightGetter; nscoord percentageBase; if (!(this->*baseGetter)(percentageBase)) { - val->SetPixels(0.0f); - return val.forget(); + return PixelsToCSSValue(0.0f); } nscoord result = lp.Resolve(percentageBase); - val->SetAppUnits(sign * result); - return val.forget(); + return AppUnitsToCSSValue(sign * result); } already_AddRefed<CSSValue> nsComputedDOMStyle::GetAbsoluteOffset( @@ -2013,9 +2003,7 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetAbsoluteOffset( const auto& oppositeCoord = offset.Get(NS_OPPOSITE_SIDE(aSide)); if (coord.IsAuto() || oppositeCoord.IsAuto()) { - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - val->SetAppUnits(GetUsedAbsoluteOffset(aSide)); - return val.forget(); + return AppUnitsToCSSValue(GetUsedAbsoluteOffset(aSide)); } return GetNonStaticPositionOffset( @@ -2096,23 +2084,18 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetStaticOffset( already_AddRefed<CSSValue> nsComputedDOMStyle::GetPaddingWidthFor( mozilla::Side aSide) { - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - - auto& padding = StylePadding()->mPadding.Get(aSide); + const auto& padding = StylePadding()->mPadding.Get(aSide); if (!mInnerFrame || !PaddingNeedsUsedValue(padding, *mComputedStyle)) { + RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; SetValueToLengthPercentage(val, padding, true); - } else { - AssertFlushedPendingReflows(); - val->SetAppUnits(mInnerFrame->GetUsedPadding().Side(aSide)); + return val.forget(); } - - return val.forget(); + AssertFlushedPendingReflows(); + return AppUnitsToCSSValue(mInnerFrame->GetUsedPadding().Side(aSide)); } already_AddRefed<CSSValue> nsComputedDOMStyle::GetBorderWidthFor( mozilla::Side aSide) { - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - nscoord width; if (mInnerFrame && mComputedStyle->StyleDisplay()->HasAppearance()) { AssertFlushedPendingReflows(); @@ -2120,29 +2103,23 @@ already_AddRefed<CSSValue> nsComputedDOMStyle::GetBorderWidthFor( } else { width = StyleBorder()->GetComputedBorderWidth(aSide); } - val->SetAppUnits(width); - - return val.forget(); + return AppUnitsToCSSValue(width); } already_AddRefed<CSSValue> nsComputedDOMStyle::GetMarginFor(Side aSide) { - RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; - - auto& margin = StyleMargin()->mMargin.Get(aSide); + const auto& margin = StyleMargin()->mMargin.Get(aSide); if (!mInnerFrame || margin.ConvertsToLength()) { + RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue; SetValueToLengthPercentageOrAuto(val, margin, false); - } else { - AssertFlushedPendingReflows(); - - // For tables, GetUsedMargin always returns an empty margin, so we - // should read the margin from the table wrapper frame instead. - val->SetAppUnits(mOuterFrame->GetUsedMargin().Side(aSide)); - NS_ASSERTION(mOuterFrame == mInnerFrame || - mInnerFrame->GetUsedMargin() == nsMargin(0, 0, 0, 0), - "Inner tables must have zero margins"); + return val.forget(); } - - return val.forget(); + AssertFlushedPendingReflows(); + // For tables, GetUsedMargin always returns an empty margin, so we + // should read the margin from the table wrapper frame instead. + NS_ASSERTION( + mOuterFrame == mInnerFrame || mInnerFrame->GetUsedMargin() == nsMargin(), + "Inner tables must have zero margins"); + return AppUnitsToCSSValue(mOuterFrame->GetUsedMargin().Side(aSide)); } static void SetValueToExtremumLength(nsROCSSPrimitiveValue* aValue, @@ -2223,7 +2200,7 @@ void nsComputedDOMStyle::SetValueToLengthPercentage( if (aClampNegativeCalc) { length = std::max(float(length), 0.0f); } - return aValue->SetPixels(length); + return SetValueToPixels(aValue, length); } if (aLength.ConvertsToPercentage()) { float result = aLength.ToPercentage(); diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 4f135a3aca..14dc400418 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -133,7 +133,7 @@ class nsComputedDOMStyle final : public nsDOMCSSDeclaration, Operation aOperation, mozilla::DeclarationBlock** aCreated) final; virtual nsresult SetCSSDeclaration(mozilla::DeclarationBlock*, mozilla::MutationClosureData*) override; - virtual mozilla::dom::Document* DocToUpdate() override; + virtual mozilla::dom::Document* DocToUpdate() final; nsDOMCSSDeclaration::ParsingEnvironment GetParsingEnvironment( nsIPrincipal* aSubjectPrincipal) const final; @@ -148,6 +148,10 @@ class nsComputedDOMStyle final : public nsDOMCSSDeclaration, NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED private: + already_AddRefed<nsROCSSPrimitiveValue> AppUnitsToCSSValue(nscoord); + already_AddRefed<nsROCSSPrimitiveValue> PixelsToCSSValue(float); + void SetValueToPixels(nsROCSSPrimitiveValue*, float); + void GetPropertyValue(const nsCSSPropertyID aPropID, const nsACString& aMaybeCustomPropertyNme, nsACString& aValue); diff --git a/layout/style/nsDOMCSSAttrDeclaration.h b/layout/style/nsDOMCSSAttrDeclaration.h index ebae280bf0..b2e9154a2d 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.h +++ b/layout/style/nsDOMCSSAttrDeclaration.h @@ -83,7 +83,7 @@ class nsDOMCSSAttributeDeclaration final : public nsDOMCSSDeclaration { nsresult SetCSSDeclaration( mozilla::DeclarationBlock* aDecl, mozilla::MutationClosureData* aClosureData) override; - mozilla::dom::Document* DocToUpdate() override; + mozilla::dom::Document* DocToUpdate() final; RefPtr<Element> mElement; diff --git a/layout/style/nsDOMCSSDeclaration.h b/layout/style/nsDOMCSSDeclaration.h index 28810c280a..66134982a0 100644 --- a/layout/style/nsDOMCSSDeclaration.h +++ b/layout/style/nsDOMCSSDeclaration.h @@ -138,7 +138,7 @@ class nsDOMCSSDeclaration : public nsICSSDeclaration { // Document that we must call BeginUpdate/EndUpdate on around the // calls to SetCSSDeclaration and the style rule mutation that leads // to it. - virtual mozilla::dom::Document* DocToUpdate() = 0; + virtual mozilla::dom::Document* DocToUpdate() { return nullptr; } // mUrlExtraData returns URL data for parsing url values in // CSS. Returns nullptr on failure. If mUrlExtraData is nullptr, diff --git a/layout/style/nsMediaFeatures.cpp b/layout/style/nsMediaFeatures.cpp index f888c127d4..cc86d1abf6 100644 --- a/layout/style/nsMediaFeatures.cpp +++ b/layout/style/nsMediaFeatures.cpp @@ -336,18 +336,19 @@ StyleDynamicRange Gecko_MediaFeatures_DynamicRange(const Document* aDocument) { StyleDynamicRange Gecko_MediaFeatures_VideoDynamicRange( const Document* aDocument) { - if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSVideoDynamicRange)) { + if (aDocument->ShouldResistFingerprinting(RFPTarget::CSSVideoDynamicRange) || + !StaticPrefs::layout_css_video_dynamic_range_allows_high()) { return StyleDynamicRange::Standard; } // video-dynamic-range: high has 3 requirements: // 1) high peak brightness // 2) high contrast ratio // 3) color depth > 24 - // We check the color depth requirement before asking the LookAndFeel - // if it is HDR capable. + + // As a proxy for those requirements, return 'High' if the screen associated + // with the device context claims to be HDR capable. if (nsDeviceContext* dx = GetDeviceContextFor(aDocument)) { - if (dx->GetDepth() > 24 && - LookAndFeel::GetInt(LookAndFeel::IntID::VideoDynamicRange)) { + if (dx->GetScreenIsHDR()) { return StyleDynamicRange::High; } } diff --git a/layout/style/nsROCSSPrimitiveValue.cpp b/layout/style/nsROCSSPrimitiveValue.cpp index 84911d209b..3b31a14dd2 100644 --- a/layout/style/nsROCSSPrimitiveValue.cpp +++ b/layout/style/nsROCSSPrimitiveValue.cpp @@ -102,20 +102,12 @@ void nsROCSSPrimitiveValue::SetDegree(float aValue) { mType = CSS_DEG; } -void nsROCSSPrimitiveValue::SetAppUnits(nscoord aValue) { - SetPixels(nsPresContext::AppUnitsToFloatCSSPixels(aValue)); -} - void nsROCSSPrimitiveValue::SetPixels(float aValue) { Reset(); mValue.mFloat = aValue; mType = CSS_PX; } -void nsROCSSPrimitiveValue::SetAppUnits(float aValue) { - SetAppUnits(NSToCoordRound(aValue)); -} - void nsROCSSPrimitiveValue::SetString(const nsACString& aString) { Reset(); mValue.mString = ToNewUnicode(aString, mozilla::fallible); diff --git a/layout/style/nsROCSSPrimitiveValue.h b/layout/style/nsROCSSPrimitiveValue.h index d782a23469..79e6fd6a10 100644 --- a/layout/style/nsROCSSPrimitiveValue.h +++ b/layout/style/nsROCSSPrimitiveValue.h @@ -48,8 +48,6 @@ class nsROCSSPrimitiveValue final : public mozilla::dom::CSSValue { void SetPercent(float aValue); void SetDegree(float aValue); void SetPixels(float aValue); - void SetAppUnits(nscoord aValue); - void SetAppUnits(float aValue); void SetString(const nsACString& aString); void SetString(const nsAString& aString); diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 39f5b1a760..94d120e378 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -2237,6 +2237,7 @@ static bool AppearanceValueAffectsFrames(StyleAppearance aAppearance, // We need to reframe since this affects the spinbox creation in // nsNumber/SearchControlFrame::CreateAnonymousContent. return aDefaultAppearance == StyleAppearance::NumberInput || + aDefaultAppearance == StyleAppearance::PasswordInput || aDefaultAppearance == StyleAppearance::Searchfield; case StyleAppearance::Menulist: // This affects the menulist button creation. @@ -2697,12 +2698,11 @@ void nsStyleContent::TriggerImageLoads(Document& aDoc, } Span<const StyleContentItem> oldItems; - if (aOld && aOld->mContent.IsItems()) { - oldItems = aOld->mContent.AsItems().AsSpan(); + if (aOld) { + oldItems = aOld->NonAltContentItems(); } - auto items = mContent.AsItems().AsSpan(); - + auto items = NonAltContentItems(); for (size_t i = 0; i < items.Length(); ++i) { const auto& item = items[i]; if (!item.IsImage()) { @@ -3536,7 +3536,7 @@ static nscoord Resolve(const StyleContainIntrinsicSize& aSize, } MOZ_ASSERT(aSize.HasAuto()); if (const auto* element = Element::FromNodeOrNull(aFrame.GetContent())) { - Maybe<float> lastSize = aAxis == eLogicalAxisBlock + Maybe<float> lastSize = aAxis == LogicalAxis::Block ? element->GetLastRememberedBSize() : element->GetLastRememberedISize(); if (lastSize && aFrame.HidesContent()) { @@ -3556,7 +3556,7 @@ Maybe<nscoord> ContainSizeAxes::ContainIntrinsicBSize( } const StyleContainIntrinsicSize& bSize = aFrame.StylePosition()->ContainIntrinsicBSize(aFrame.GetWritingMode()); - return Some(Resolve(bSize, aNoneValue, aFrame, eLogicalAxisBlock)); + return Some(Resolve(bSize, aNoneValue, aFrame, LogicalAxis::Block)); } Maybe<nscoord> ContainSizeAxes::ContainIntrinsicISize( @@ -3566,7 +3566,7 @@ Maybe<nscoord> ContainSizeAxes::ContainIntrinsicISize( } const StyleContainIntrinsicSize& iSize = aFrame.StylePosition()->ContainIntrinsicISize(aFrame.GetWritingMode()); - return Some(Resolve(iSize, aNoneValue, aFrame, eLogicalAxisInline)); + return Some(Resolve(iSize, aNoneValue, aFrame, LogicalAxis::Inline)); } nsSize ContainSizeAxes::ContainSize(const nsSize& aUncontainedSize, diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 8835934eaf..efca723852 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1356,17 +1356,11 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay { return mDefaultAppearance; case mozilla::StyleAppearance::Textfield: // `appearance: textfield` should behave like `auto` on all elements - // except <input type=search> elements, which we identify using the - // internal -moz-default-appearance property. (In the browser chrome - // we have some other elements that set `-moz-default-appearance: - // searchfield`, but not in content documents.) - if (mDefaultAppearance == mozilla::StyleAppearance::Searchfield) { - return mAppearance; - } - // We also need to support `appearance: textfield` on <input - // type=number>, since that is the only way in Gecko to disable the - // spinners. - if (mDefaultAppearance == mozilla::StyleAppearance::NumberInput) { + // except <input type=search/number/password> elements, which we + // identify using the internal -moz-default-appearance property. + if (mDefaultAppearance == mozilla::StyleAppearance::Searchfield || + mDefaultAppearance == mozilla::StyleAppearance::NumberInput || + mDefaultAppearance == mozilla::StyleAppearance::PasswordInput) { return mAppearance; } return mDefaultAppearance; @@ -1533,8 +1527,10 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay { } } + // These two methods are deprecated since they do not differentiate paginated + // context and multi-column context. Use nsIFrame::ShouldBreakBefore() / + // nsIFrame::ShouldBreakAfter() instead. bool BreakBefore() const { return ShouldBreak(mBreakBefore); } - bool BreakAfter() const { return ShouldBreak(mBreakAfter); } // These are defined in nsStyleStructInlines.h. @@ -1607,12 +1603,22 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleContent { using CounterPair = mozilla::StyleGenericCounterPair<int32_t>; - size_t ContentCount() const { - return mContent.IsItems() ? mContent.AsItems().Length() : 0; + /// Returns the content items that aren't alternative content. + mozilla::Span<const mozilla::StyleContentItem> NonAltContentItems() const { + if (!mContent.IsItems()) { + return {}; + } + const auto& items = mContent.AsItems(); + return mozilla::Span(items.items).To(items.alt_start); } - const mozilla::StyleContentItem& ContentAt(size_t aIndex) const { - return mContent.AsItems().AsSpan()[aIndex]; + /// Returns the content items that /are/ alternative content. + mozilla::Span<const mozilla::StyleContentItem> AltContentItems() const { + if (!mContent.IsItems()) { + return {}; + } + const auto& items = mContent.AsItems(); + return mozilla::Span(items.items).From(items.alt_start); } mozilla::StyleContent mContent; @@ -2058,35 +2064,4 @@ struct UniquePtr_Simple { STATIC_ASSERT_TYPE_LAYOUTS_MATCH(mozilla::UniquePtr<int>, UniquePtr_Simple<int>); -/** - * <div rustbindgen replaces="nsTArray"></div> - */ -template <typename T> -class nsTArray_Simple { - protected: - T* mBuffer; - - public: - ~nsTArray_Simple() { - // The existence of a user-provided, and therefore non-trivial, destructor - // here prevents bindgen from deriving the Clone trait via a simple memory - // copy. - } -}; - -/** - * <div rustbindgen replaces="CopyableTArray"></div> - */ -template <typename T> -class CopyableTArray_Simple : public nsTArray_Simple<T> {}; - -STATIC_ASSERT_TYPE_LAYOUTS_MATCH(nsTArray<nsStyleImageLayers::Layer>, - nsTArray_Simple<nsStyleImageLayers::Layer>); -STATIC_ASSERT_TYPE_LAYOUTS_MATCH(nsTArray<mozilla::StyleTransition>, - nsTArray_Simple<mozilla::StyleTransition>); -STATIC_ASSERT_TYPE_LAYOUTS_MATCH(nsTArray<mozilla::StyleAnimation>, - nsTArray_Simple<mozilla::StyleAnimation>); -STATIC_ASSERT_TYPE_LAYOUTS_MATCH(nsTArray<mozilla::StyleViewTimeline>, - nsTArray_Simple<mozilla::StyleViewTimeline>); - #endif /* nsStyleStruct_h___ */ diff --git a/layout/style/res/forms.css b/layout/style/res/forms.css index 587534e9f3..7a5c25fc6e 100644 --- a/layout/style/res/forms.css +++ b/layout/style/res/forms.css @@ -73,6 +73,8 @@ fieldset { label { cursor: default; + /* If you add declarations here, consider whether the select > label and file + * input label need them as well. */ } /* Default inputs, text inputs, and selects */ @@ -190,6 +192,10 @@ input::-moz-text-control-preview { line-height: -moz-block-height !important; } +input[type=password] { + -moz-default-appearance: password-input; +} + input[type=password]::-moz-text-control-editing-root, input[type=password]::-moz-text-control-preview { /* @@ -281,6 +287,7 @@ select > button { select > label { display: inline-block; overflow: clip; + cursor: unset; } option[label]::before { @@ -627,10 +634,7 @@ input[type=file] > label { min-inline-size: 12em; text-align: match-parent; - color: unset; - font-size: unset; - letter-spacing: unset; - + cursor: unset; user-select: none; unicode-bidi: plaintext; } @@ -838,7 +842,6 @@ input[type=range]::slider-thumb { } input[type=number] { - appearance: auto; -moz-default-appearance: number-input; } diff --git a/layout/style/res/html.css b/layout/style/res/html.css index ff58ecd4d1..769ebece45 100644 --- a/layout/style/res/html.css +++ b/layout/style/res/html.css @@ -136,7 +136,7 @@ body { margin: 8px; } -p, dl, multicol { +p, dl { display: block; margin-block-start: 1em; margin-block-end: 1em; @@ -765,7 +765,7 @@ video > .caption-box { * The pseudo element won't inherit CSS styles from its direct parent, `::cue` * would actually inherit styles from video because it's video's pseudo element. * Therefore, we have to explicitly set some styles which are already defined - * in its parent element in vtt.jsm. + * in its parent element in vtt.sys.mjs. */ ::cue { color: rgba(255, 255, 255, 1); @@ -816,15 +816,21 @@ dialog::backdrop { background: rgba(0, 0, 0, 0.1); } +/* https://html.spec.whatwg.org/#the-marquee-element-2 */ marquee { - inline-size: -moz-available; display: inline-block; + text-align: initial; + overflow: hidden !important; + + /* See https://github.com/whatwg/html/issues/10249 */ + inline-size: -moz-available; vertical-align: text-bottom; - text-align: start; + white-space: nowrap; } marquee:is([direction="up"], [direction="down"]) { block-size: 200px; + white-space: unset; } /* Ruby */ diff --git a/layout/style/res/quirk.css b/layout/style/res/quirk.css index 6e74839ee3..0379033515 100644 --- a/layout/style/res/quirk.css +++ b/layout/style/res/quirk.css @@ -59,7 +59,7 @@ table { * selectors will be hashed in the selector maps and things will be much more * efficient. */ -:is(body, td, th) > :is(p, dl, multicol, blockquote, h1, h2, h3, h4, h5, h6, listing, plaintext, xmp, pre, ul, menu, dir, ol):-moz-first-node { +:is(body, td, th) > :is(p, dl, blockquote, h1, h2, h3, h4, h5, h6, listing, plaintext, xmp, pre, ul, menu, dir, ol):-moz-first-node { margin-block-start: 0; } @@ -71,11 +71,11 @@ td > p:-moz-last-node, th > p:-moz-last-node { * collapse the bottom or top margins of empty elements * - see bug 97361 */ -:is(body, td, th) > :is(p, dl, multicol, blockquote, h1, h2, h3, h4, h5, h6, listing, plaintext, xmp, pre, ul, menu, dir, ol):-moz-only-whitespace:-moz-first-node { +:is(body, td, th) > :is(p, dl, blockquote, h1, h2, h3, h4, h5, h6, listing, plaintext, xmp, pre, ul, menu, dir, ol):-moz-only-whitespace:-moz-first-node { margin-block-end: 0; } -:is(td, th) > :is(p, dl, multicol, blockquote, h1, h2, h3, h4, h5, h6, listing, plaintext, xmp, pre, ul, menu, dir, ol):-moz-only-whitespace:-moz-last-node { +:is(td, th) > :is(p, dl, blockquote, h1, h2, h3, h4, h5, h6, listing, plaintext, xmp, pre, ul, menu, dir, ol):-moz-only-whitespace:-moz-last-node { margin-block-start: 0; } diff --git a/layout/style/test/chrome/chrome-only-media-queries.js b/layout/style/test/chrome/chrome-only-media-queries.js index aaf313a526..d17976149b 100644 --- a/layout/style/test/chrome/chrome-only-media-queries.js +++ b/layout/style/test/chrome/chrome-only-media-queries.js @@ -31,4 +31,5 @@ const CHROME_ONLY_QUERIES = [ "(-moz-gtk-theme-family: adwaita)", "(-moz-gtk-theme-family: breeze)", "(-moz-gtk-theme-family: yaru)", + "(forced-colors: requested)", ]; diff --git a/layout/style/test/mochitest.toml b/layout/style/test/mochitest.toml index 54ad9736f2..cc5f26708f 100644 --- a/layout/style/test/mochitest.toml +++ b/layout/style/test/mochitest.toml @@ -6,16 +6,13 @@ prefs = [ "gfx.font_loader.delay=0", "layout.css.container-queries.enabled=true", "layout.css.individual-transform.enabled=true", - "layout.css.motion-path-ray.enabled=true", - "layout.css.motion-path-basic-shapes.enabled=true", - "layout.css.motion-path-coord-box.enabled=true", - "layout.css.motion-path-offset-position.enabled=true", "layout.css.motion-path-url.enabled=true", "layout.css.backdrop-filter.enabled=true", "layout.css.fit-content-function.enabled=true", "layout.css.scroll-driven-animations.enabled=true", "layout.css.animation-composition.enabled=true", "layout.css.basic-shape-rect.enabled=true", + "layout.css.basic-shape-shape.enabled=true", "layout.css.basic-shape-xywh.enabled=true", "layout.css.transform-box-content-stroke.enabled=true", "layout.css.transition-behavior.enabled=true", diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 2e8b4c71a3..302bd48b42 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -1035,6 +1035,36 @@ if (IsCSSPropertyPrefEnabled("layout.css.basic-shape-rect.enabled")) { ); } +var basicShapeShapeValues = []; +var basicShapeShapeValuesWithFillRule = []; +if (IsCSSPropertyPrefEnabled("layout.css.basic-shape-shape.enabled")) { + basicShapeShapeValuesWithFillRule.push( + "shape(evenodd from 0px 0px, line to 10px 10px)", + "shape(nonzero from 0px 0px, line to 10px 10px)" + ); + + basicShapeShapeValues.push( + "shape(from 0px 0%, line to 10px 10%)", + "shape(from 10px 10px, move by 10px 5px, line by 20px 40%, close)", + "shape(from 10px 10px, hline by 10px, vline to 5rem)", + "shape(from 10px 10px, vline by 5%, hline to 1vw)", + "shape(from 10px 10px, curve to 50px 20px via 10rem 1%)", + "shape(from 10px 10px, smooth to 50px 20px via 10rem 1%)", + "shape(from 10% 1rem, arc to 50px 1pt of 20% cw large rotate 25deg)" + ); + + // It's fine to include this for properties which don't support shape(), + // e.g. shape-outside, because they must reject these values. + basicShapeInvalidValues.push( + "shape()", + "shape(evenodd, from 0px 0px)", + "shape(from 0px 0px line to 10px 10px)", + "shape(from 0px 0px)", + "shape(close)", + "shape(nonzero, close)" + ); +} + if (/* mozGradientsEnabled */ true) { // Maybe one day :( // Extend gradient lists with valid/invalid moz-prefixed expressions: @@ -5352,7 +5382,6 @@ var gCSSProperties = { "counter(\\()", "counters(a\\+b, '.')", "counter(\\}, upper-alpha)", - "-moz-alt-content", "counter(foo, symbols('*'))", "counter(foo, symbols(numeric '0' '1'))", "counters(foo, '.', symbols('*'))", @@ -5370,6 +5399,7 @@ var gCSSProperties = { "attr(-2)", "counter(2)", "counters(-2, '.')", + "-moz-alt-content", "-moz-alt-content 'foo'", "'foo' -moz-alt-content", "counter(one, two, three) 'foo'", @@ -6841,6 +6871,130 @@ var gCSSProperties = { "left 10px top", ], }, + offset: { + domProp: "offset", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ + "offset-path", + "offset-distance", + "offset-rotate", + "offset-anchor", + "offset-position", + ], + initial_values: ["none"], + other_values: [ + "none 30deg reverse", + "none 50px reverse 30deg", + "none calc(10px + 20%) auto", + "none reverse", + "none / left center", + "path('M 0 0 H 1') -200% auto", + "path('M 0 0 H 1') -200%", + "path('M 0 0 H 1') 50px", + "path('M 0 0 H 1') auto", + "path('M 0 0 H 1') reverse 30deg 50px", + "path('M 0 0 H 1')", + "path('m 20 0 h 100') -7rad 8px / auto", + "path('m 0 30 v 100') -7rad 8px / left top", + "path('m 0 0 h 100') -7rad 8px", + "path('M 0 0 H 100') 100px 0deg", + "top right / top left", + "top right ray(45deg closest-side)", + "50% 50% ray(0rad farthest-side)", + ], + invalid_values: [ + "100px 0deg path('m 0 0 h 100')", + "30deg", + "auto 30deg 100px", + "auto / none", + "none /", + "none / 100px 20px 30deg", + "path('M 20 30 A 60 70 80') bottom", + "path('M 20 30 A 60 70 80') bottom top", + "path('M 20 30 A 60 70 80') 100px 200px", + "path('M 20 30 A 60 70 80') reverse auto", + "path('M 20 30 A 60 70 80') reverse 10px 30deg", + "path('M 20 30 A 60 70 80') /", + ], + }, + "offset-anchor": { + domProp: "offsetAnchor", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: ["auto"], + other_values: [ + "left bottom", + "center center", + "calc(20% + 10px) center", + "right 30em", + "10px 20%", + "left -10px top -20%", + "right 10% bottom 20em", + ], + invalid_values: ["none", "10deg", "left 10% top"], + }, + "offset-distance": { + domProp: "offsetDistance", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: ["0"], + other_values: ["10px", "10%", "190%", "-280%", "calc(30px + 40%)"], + invalid_values: ["none", "45deg"], + }, + "offset-path": { + domProp: "offsetPath", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: ["none"], + other_values: [ + "ray(0deg)", + "ray(45deg closest-side)", + "ray(0rad farthest-side)", + "ray(0.5turn closest-corner contain)", + "ray(200grad farthest-corner)", + "ray(sides 180deg)", + "ray(contain farthest-side 180deg)", + "ray(calc(180deg - 45deg) farthest-side)", + "ray(0deg at center center)", + "ray(at 10% 10% 1rad)", + ] + .concat(pathValues.other_values) + .concat(basicShapeOtherValues) + .concat(basicShapeXywhRectValues) + .concat(basicShapeShapeValues), + invalid_values: [ + "path('')", + "ray(closest-side)", + "ray(0deg, closest-side)", + "ray(contain 0deg closest-side contain)", + ].concat(pathValues.invalid_values), + }, + "offset-position": { + domProp: "offsetPosition", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: ["normal"], + other_values: [ + "auto", + "left bottom", + "center center", + "calc(20% + 10px) center", + "right 30em", + "10px 20%", + "left -10px top -20%", + "right 10% bottom 20em", + ], + invalid_values: ["none", "10deg", "left 10% top"], + }, + "offset-rotate": { + domProp: "offsetRotate", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: ["auto"], + other_values: ["reverse", "0deg", "0rad reverse", "-45deg", "5turn auto"], + invalid_values: ["none", "10px", "reverse 0deg reverse", "reverse auto"], + }, opacity: { domProp: "opacity", inherited: false, @@ -8827,7 +8981,9 @@ var gCSSProperties = { .concat(basicShapeSVGBoxValues) .concat(basicShapeOtherValues) .concat(basicShapeOtherValuesWithFillRule) - .concat(basicShapeXywhRectValues), + .concat(basicShapeXywhRectValues) + .concat(basicShapeShapeValues) + .concat(basicShapeShapeValuesWithFillRule), invalid_values: [ "path(nonzero)", "path(abs, 'M 10 10 L 10 10 z')", @@ -13409,158 +13565,10 @@ gCSSProperties["scrollbar-width"] = { invalid_values: ["1px"], }; -gCSSProperties["offset"] = { - domProp: "offset", - inherited: false, - type: CSS_TYPE_TRUE_SHORTHAND, - subproperties: [ - "offset-path", - "offset-distance", - "offset-rotate", - "offset-anchor", - ], - initial_values: ["none"], - other_values: [ - "none 30deg reverse", - "none 50px reverse 30deg", - "none calc(10px + 20%) auto", - "none reverse", - "none / left center", - "path('M 0 0 H 1') -200% auto", - "path('M 0 0 H 1') -200%", - "path('M 0 0 H 1') 50px", - "path('M 0 0 H 1') auto", - "path('M 0 0 H 1') reverse 30deg 50px", - "path('M 0 0 H 1')", - "path('m 20 0 h 100') -7rad 8px / auto", - "path('m 0 30 v 100') -7rad 8px / left top", - "path('m 0 0 h 100') -7rad 8px", - "path('M 0 0 H 100') 100px 0deg", - ], - invalid_values: [ - "100px 0deg path('m 0 0 h 100')", - "30deg", - "auto 30deg 100px", - "auto / none", - "none /", - "none / 100px 20px 30deg", - "path('M 20 30 A 60 70 80') bottom", - "path('M 20 30 A 60 70 80') bottom top", - "path('M 20 30 A 60 70 80') 100px 200px", - "path('M 20 30 A 60 70 80') reverse auto", - "path('M 20 30 A 60 70 80') reverse 10px 30deg", - "path('M 20 30 A 60 70 80') /", - ], -}; - -gCSSProperties["offset-path"] = { - domProp: "offsetPath", - inherited: false, - type: CSS_TYPE_LONGHAND, - initial_values: ["none"], - other_values: [...pathValues.other_values], - invalid_values: ["path('')"].concat(pathValues.invalid_values), -}; - -if (IsCSSPropertyPrefEnabled("layout.css.motion-path-ray.enabled")) { - gCSSProperties["offset-path"]["other_values"].push( - "ray(0deg)", - "ray(45deg closest-side)", - "ray(0rad farthest-side)", - "ray(0.5turn closest-corner contain)", - "ray(200grad farthest-corner)", - "ray(sides 180deg)", - "ray(contain farthest-side 180deg)", - "ray(calc(180deg - 45deg) farthest-side)", - "ray(0deg at center center)", - "ray(at 10% 10% 1rad)" - ); - - gCSSProperties["offset-path"]["invalid_values"].push( - "ray(closest-side)", - "ray(0deg, closest-side)", - "ray(contain 0deg closest-side contain)" - ); -} - -if (IsCSSPropertyPrefEnabled("layout.css.motion-path-basic-shapes.enabled")) { - gCSSProperties["offset-path"]["other_values"].push( - ...basicShapeOtherValues, - ...basicShapeXywhRectValues - ); -} - if (IsCSSPropertyPrefEnabled("layout.css.motion-path-url.enabled")) { gCSSProperties["offset-path"]["other_values"].push("url(#svgPath)"); } -gCSSProperties["offset-distance"] = { - domProp: "offsetDistance", - inherited: false, - type: CSS_TYPE_LONGHAND, - initial_values: ["0"], - other_values: ["10px", "10%", "190%", "-280%", "calc(30px + 40%)"], - invalid_values: ["none", "45deg"], -}; - -gCSSProperties["offset-rotate"] = { - domProp: "offsetRotate", - inherited: false, - type: CSS_TYPE_LONGHAND, - initial_values: ["auto"], - other_values: ["reverse", "0deg", "0rad reverse", "-45deg", "5turn auto"], - invalid_values: ["none", "10px", "reverse 0deg reverse", "reverse auto"], -}; - -gCSSProperties["offset-anchor"] = { - domProp: "offsetAnchor", - inherited: false, - type: CSS_TYPE_LONGHAND, - initial_values: ["auto"], - other_values: [ - "left bottom", - "center center", - "calc(20% + 10px) center", - "right 30em", - "10px 20%", - "left -10px top -20%", - "right 10% bottom 20em", - ], - invalid_values: ["none", "10deg", "left 10% top"], -}; - -if ( - IsCSSPropertyPrefEnabled("layout.css.motion-path-offset-position.enabled") -) { - gCSSProperties["offset"]["subproperties"].push("offset-position"); - gCSSProperties["offset"]["other_values"].push("top right / top left"); - - if (IsCSSPropertyPrefEnabled("layout.css.motion-path-ray.enabled")) { - gCSSProperties["offset"]["other_values"].push( - "top right ray(45deg closest-side)", - "50% 50% ray(0rad farthest-side)" - ); - } - - gCSSProperties["offset-position"] = { - domProp: "offsetPosition", - inherited: false, - type: CSS_TYPE_LONGHAND, - initial_values: ["normal"], - other_values: [ - "auto", - "left bottom", - "center center", - "calc(20% + 10px) center", - "right 30em", - "10px 20%", - "left -10px top -20%", - "right 10% bottom 20em", - ], - invalid_values: ["none", "10deg", "left 10% top"], - }; -} - { let linear_function_other_values = [ "linear(0, 1)", diff --git a/layout/style/test/test_hover_quirk.html b/layout/style/test/test_hover_quirk.html index 61e19f2a60..7a78185178 100644 --- a/layout/style/test/test_hover_quirk.html +++ b/layout/style/test/test_hover_quirk.html @@ -54,6 +54,18 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=783213 #dynamic-test:hover > * { background: rgb(0, 255, 0); } + + #dynamic-test-2 :is(button,input,a){ + background-color:yellow !important; + } + + #dynamic-test-2 :is(button,input,a):hover{ + background-color:lime !important; + } + + #dynamic-test-2 :is(button,input):focus{ + background-color:skyblue !important; + } </style> <script src="/tests/SimpleTest/SimpleTest.js"></script> <script src="/tests/SimpleTest/EventUtils.js"></script> @@ -97,6 +109,21 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=783213 is(getComputedStyle(document.getElementById('should-be-green-on-hover')).backgroundColor, "rgb(0, 255, 0)", "Dynamic change should invalidate properly"); + + synthesizeMouseAtCenter(document.getElementById('button'), {type: "mousemove"}); + is(getComputedStyle(document.getElementById('button')).backgroundColor, + "rgb(0, 255, 0)", + "Button hover should be green"); + + synthesizeMouseAtCenter(document.getElementById('input'), {type: "mousemove"}); + is(getComputedStyle(document.getElementById('input')).backgroundColor, + "rgb(0, 255, 0)", + "Input hover should be green"); + + synthesizeMouseAtCenter(document.getElementById('link-2'), {type: "mousemove"}); + is(getComputedStyle(document.getElementById('link-2')).backgroundColor, + "rgb(0, 255, 0)", + "Link hover should be green"); SimpleTest.finish(); }); </script> @@ -113,6 +140,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=783213 <a id="link" href="#">Link<span class="child"></span></a><br> <div id="div" class="parent">Div <span><span class="child"></span></span></div><br> </div> + <div id="dynamic-test-2"> + <button id="button">Button</button> + <input id="input" value="Input"> + <a id="link-2"href="">Link</a> + </div> <pre id="test"></pre> </body> </html> diff --git a/layout/style/test/test_non_content_accessible_pseudos.html b/layout/style/test/test_non_content_accessible_pseudos.html index 81493b1a4a..5f2d15d6a2 100644 --- a/layout/style/test/test_non_content_accessible_pseudos.html +++ b/layout/style/test/test_non_content_accessible_pseudos.html @@ -30,7 +30,6 @@ const NON_CONTENT_ACCESIBLE_PSEUDOS = [ ":-moz-dir-attr-rtl", ":-moz-dir-attr-like-auto", ":-moz-autofill-preview", - ":-moz-lwtheme", ":-moz-is-html", ":-moz-locale-dir(rtl)", ":-moz-locale-dir(ltr)", diff --git a/layout/style/test/test_selectors.html b/layout/style/test/test_selectors.html index d688139d3c..684e9d6524 100644 --- a/layout/style/test/test_selectors.html +++ b/layout/style/test/test_selectors.html @@ -1077,7 +1077,6 @@ function runTests() { test_balanced_unparseable(":dir"); // Test chrome-only -moz-lwtheme - test_balanced_unparseable(":-moz-lwtheme"); test_balanced_unparseable(":-moz-broken"); test_balanced_unparseable(":-moz-tree-row(selected)"); diff --git a/layout/style/test/test_style_struct_copy_constructors.html b/layout/style/test/test_style_struct_copy_constructors.html index 95f727a58d..bce5a4e32c 100644 --- a/layout/style/test/test_style_struct_copy_constructors.html +++ b/layout/style/test/test_style_struct_copy_constructors.html @@ -64,6 +64,12 @@ for (var prop in gCSSProperties) { } /** Test using inheritance **/ + +// TODO(bug 1887221): Zoom right now doesn't apply to explicitly inherited +// values, so remove it to get consistent results. +gRule1.style.removeProperty("zoom"); +gRule2.style.removeProperty("zoom"); + for (var prop in gCSSProperties) { var info = gCSSProperties[prop]; if (info.inherited && !("subproperties" in info)) { @@ -79,14 +85,6 @@ for (var prop in gCSSProperties) { } } -for (var prop in gCSSProperties) { - var info = gCSSProperties[prop]; - if (!("subproperties" in info)) { - gRule1.style.removeProperty(prop); - gRule2.style.removeProperty(prop); - } -} - </script> </pre> </body> diff --git a/layout/svg/CSSClipPathInstance.cpp b/layout/svg/CSSClipPathInstance.cpp index 77bde2bb54..a8e0306d3e 100644 --- a/layout/svg/CSSClipPathInstance.cpp +++ b/layout/svg/CSSClipPathInstance.cpp @@ -66,7 +66,7 @@ bool CSSClipPathInstance::HitTestBasicShapeOrPathClip(nsIFrame* aFrame, RefPtr<Path> path = instance.CreateClipPath( drawTarget, SVGUtils::GetCSSPxToDevPxMatrix(aFrame)); float pixelRatio = float(AppUnitsPerCSSPixel()) / - aFrame->PresContext()->AppUnitsPerDevPixel(); + float(aFrame->PresContext()->AppUnitsPerDevPixel()); return path && path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix()); } @@ -121,8 +121,10 @@ already_AddRefed<Path> CSSClipPathInstance::CreateClipPath( return CreateClipPathPolygon(aDrawTarget, r); case StyleBasicShape::Tag::Rect: return CreateClipPathInset(aDrawTarget, r); - case StyleBasicShape::Tag::Path: - return CreateClipPathPath(aDrawTarget, r); + case StyleBasicShape::Tag::PathOrShape: + return basicShape.AsPathOrShape().IsPath() + ? CreateClipPathPath(aDrawTarget, r) + : CreateClipPathShape(aDrawTarget, r); default: MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type"); } @@ -175,18 +177,41 @@ already_AddRefed<Path> CSSClipPathInstance::CreateClipPathInset( already_AddRefed<Path> CSSClipPathInstance::CreateClipPathPath( DrawTarget* aDrawTarget, const nsRect& aRefBox) { - const auto& path = mClipPathStyle.AsShape()._0->AsPath(); + const auto& path = mClipPathStyle.AsShape()._0->AsPathOrShape().AsPath(); RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder( path.fill == StyleFillRule::Nonzero ? FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD); - nscoord appUnitsPerDevPixel = + const nscoord appUnitsPerDevPixel = mTargetFrame->PresContext()->AppUnitsPerDevPixel(); - float scale = float(AppUnitsPerCSSPixel()) / appUnitsPerDevPixel; - Point offset = Point(aRefBox.x, aRefBox.y) / appUnitsPerDevPixel; - + const Point offset = + LayoutDevicePoint::FromAppUnits(aRefBox.TopLeft(), appUnitsPerDevPixel) + .ToUnknownPoint(); + const float scale = mTargetFrame->Style()->EffectiveZoom().Zoom( + float(AppUnitsPerCSSPixel()) / float(appUnitsPerDevPixel)); return SVGPathData::BuildPath(path.path._0.AsSpan(), builder, - StyleStrokeLinecap::Butt, 0.0, offset, scale); + StyleStrokeLinecap::Butt, 0.0, {}, offset, + scale); +} + +already_AddRefed<Path> CSSClipPathInstance::CreateClipPathShape( + DrawTarget* aDrawTarget, const nsRect& aRefBox) { + const auto& shape = mClipPathStyle.AsShape()._0->AsPathOrShape().AsShape(); + + RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder( + shape.fill == StyleFillRule::Nonzero ? FillRule::FILL_WINDING + : FillRule::FILL_EVEN_ODD); + const nscoord appUnitsPerDevPixel = + mTargetFrame->PresContext()->AppUnitsPerDevPixel(); + const CSSSize basis = CSSSize::FromAppUnits(aRefBox.Size()); + const Point offset = + LayoutDevicePoint::FromAppUnits(aRefBox.TopLeft(), appUnitsPerDevPixel) + .ToUnknownPoint(); + const float scale = mTargetFrame->Style()->EffectiveZoom().Zoom( + float(AppUnitsPerCSSPixel()) / float(appUnitsPerDevPixel)); + return SVGPathData::BuildPath(shape.commands.AsSpan(), builder, + StyleStrokeLinecap::Butt, 0.0, basis, offset, + scale); } } // namespace mozilla diff --git a/layout/svg/CSSClipPathInstance.h b/layout/svg/CSSClipPathInstance.h index 2d1e16c62f..2cec8c125f 100644 --- a/layout/svg/CSSClipPathInstance.h +++ b/layout/svg/CSSClipPathInstance.h @@ -58,6 +58,9 @@ class MOZ_STACK_CLASS CSSClipPathInstance { already_AddRefed<Path> CreateClipPathPath(DrawTarget* aDrawTarget, const nsRect& aRefBox); + already_AddRefed<Path> CreateClipPathShape(DrawTarget* aDrawTarget, + const nsRect& aRefBox); + /** * The frame for the element that is currently being clipped. */ diff --git a/layout/svg/SVGAFrame.cpp b/layout/svg/SVGAFrame.cpp index 02ff68d7e7..3d09cdc3f5 100644 --- a/layout/svg/SVGAFrame.cpp +++ b/layout/svg/SVGAFrame.cpp @@ -82,20 +82,6 @@ nsresult SVGAFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, NotifySVGChanged(TRANSFORM_CHANGED); } - // Currently our SMIL implementation does not modify the DOM attributes. Once - // we implement the SVG 2 SMIL behaviour this can be removed - // SVGAElement::SetAttr/UnsetAttr's ResetLinkState() call will be sufficient. - if (aModType == dom::MutationEvent_Binding::SMIL && - aAttribute == nsGkAtoms::href && - (aNameSpaceID == kNameSpaceID_None || - aNameSpaceID == kNameSpaceID_XLink)) { - auto* content = static_cast<dom::SVGAElement*>(GetContent()); - - // SMIL may change whether an <a> element is a link, in which case we will - // need to update the link state. - content->ResetLinkState(true, content->ElementHasHref()); - } - return NS_OK; } diff --git a/layout/svg/SVGContextPaint.cpp b/layout/svg/SVGContextPaint.cpp index 0d7a610df9..e135ea2db3 100644 --- a/layout/svg/SVGContextPaint.cpp +++ b/layout/svg/SVGContextPaint.cpp @@ -80,7 +80,7 @@ bool SVGContextPaint::IsAllowedForImageFromURI(nsIURI* aURI) { // Only allowed for extensions that have the // internal:svgContextPropertiesAllowed permission (added internally from // to Mozilla-owned extensions, see `isMozillaExtension` function - // defined in Extension.jsm for the exact criteria). + // defined in Extension.sys.mjs for the exact criteria). return addonPolicy->HasPermission( nsGkAtoms::svgContextPropertiesAllowedPermission); } diff --git a/layout/svg/SVGFEImageFrame.cpp b/layout/svg/SVGFEImageFrame.cpp index 50fb42cd68..d89ed566f1 100644 --- a/layout/svg/SVGFEImageFrame.cpp +++ b/layout/svg/SVGFEImageFrame.cpp @@ -121,24 +121,6 @@ nsresult SVGFEImageFrame::AttributeChanged(int32_t aNameSpaceID, SVGObserverUtils::InvalidateRenderingObservers(GetParent()); } - // Currently our SMIL implementation does not modify the DOM attributes. Once - // we implement the SVG 2 SMIL behaviour this can be removed - // SVGFEImageElement::AfterSetAttr's implementation will be sufficient. - if (aModType == MutationEvent_Binding::SMIL && - aAttribute == nsGkAtoms::href && - (aNameSpaceID == kNameSpaceID_XLink || - aNameSpaceID == kNameSpaceID_None)) { - bool hrefIsSet = - element->mStringAttributes[SVGFEImageElement::HREF].IsExplicitlySet() || - element->mStringAttributes[SVGFEImageElement::XLINK_HREF] - .IsExplicitlySet(); - if (hrefIsSet) { - element->LoadSVGImage(true, true); - } else { - element->CancelImageRequests(true); - } - } - return nsIFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); } diff --git a/layout/svg/SVGGeometryFrame.cpp b/layout/svg/SVGGeometryFrame.cpp index 3d6d6aef7e..0e00d60a31 100644 --- a/layout/svg/SVGGeometryFrame.cpp +++ b/layout/svg/SVGGeometryFrame.cpp @@ -114,6 +114,7 @@ void SVGGeometryFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) { if (element->IsGeometryChangedViaCSS(*Style(), *aOldComputedStyle)) { element->ClearAnyCachedPath(); + SVGObserverUtils::InvalidateRenderingObservers(this); } } @@ -772,7 +773,12 @@ bool SVGGeometryFrame::CreateWebRenderCommands( // At the moment this code path doesn't support strokes so it fine to // combine the rectangle's opacity (which has to be applied on the result) // of (filling + stroking) with the fill opacity. - float elemOpacity = StyleEffects()->mOpacity; + + float elemOpacity = 1.0f; + if (SVGUtils::CanOptimizeOpacity(this)) { + elemOpacity = StyleEffects()->mOpacity; + } + float fillOpacity = SVGUtils::GetOpacity(style->mFillOpacity, contextPaint); float opacity = elemOpacity * fillOpacity; diff --git a/layout/svg/SVGGradientFrame.cpp b/layout/svg/SVGGradientFrame.cpp index 92558cefcd..be6f395cda 100644 --- a/layout/svg/SVGGradientFrame.cpp +++ b/layout/svg/SVGGradientFrame.cpp @@ -244,8 +244,10 @@ class MOZ_STACK_CLASS SVGColorStopInterpolator public: SVGColorStopInterpolator( gfxPattern* aGradient, const nsTArray<ColorStop>& aStops, - const StyleColorInterpolationMethod& aStyleColorInterpolationMethod) - : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod), + const StyleColorInterpolationMethod& aStyleColorInterpolationMethod, + bool aExtendLastStop) + : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod, + aExtendLastStop), mGradient(aGradient) {} void CreateStop(float aPosition, DeviceColor aColor) { @@ -327,7 +329,8 @@ already_AddRefed<gfxPattern> SVGGradientFrame::GetPaintServerPattern( if (StyleSVG()->mColorInterpolation == StyleColorInterpolation::Linearrgb) { static constexpr auto interpolationMethod = StyleColorInterpolationMethod{ StyleColorSpace::SrgbLinear, StyleHueInterpolationMethod::Shorter}; - SVGColorStopInterpolator interpolator(gradient, stops, interpolationMethod); + SVGColorStopInterpolator interpolator(gradient, stops, interpolationMethod, + false); interpolator.CreateStops(); } else { // setup standard sRGB stops diff --git a/layout/svg/SVGImageFrame.cpp b/layout/svg/SVGImageFrame.cpp index 242ee6e25e..35693e87c0 100644 --- a/layout/svg/SVGImageFrame.cpp +++ b/layout/svg/SVGImageFrame.cpp @@ -191,26 +191,6 @@ nsresult SVGImageFrame::AttributeChanged(int32_t aNameSpaceID, } } - // Currently our SMIL implementation does not modify the DOM attributes. Once - // we implement the SVG 2 SMIL behaviour this can be removed - // SVGImageElement::AfterSetAttr's implementation will be sufficient. - if (aModType == MutationEvent_Binding::SMIL && - aAttribute == nsGkAtoms::href && - (aNameSpaceID == kNameSpaceID_XLink || - aNameSpaceID == kNameSpaceID_None)) { - SVGImageElement* element = static_cast<SVGImageElement*>(GetContent()); - - bool hrefIsSet = - element->mStringAttributes[SVGImageElement::HREF].IsExplicitlySet() || - element->mStringAttributes[SVGImageElement::XLINK_HREF] - .IsExplicitlySet(); - if (hrefIsSet) { - element->LoadSVGImage(true, true); - } else { - element->CancelImageRequests(true); - } - } - return NS_OK; } diff --git a/layout/svg/SVGOuterSVGFrame.cpp b/layout/svg/SVGOuterSVGFrame.cpp index ea0879c0c2..00d7663901 100644 --- a/layout/svg/SVGOuterSVGFrame.cpp +++ b/layout/svg/SVGOuterSVGFrame.cpp @@ -126,20 +126,14 @@ NS_QUERYFRAME_TAIL_INHERITING(SVGDisplayContainerFrame) /* virtual */ nscoord SVGOuterSVGFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result; - DISPLAY_MIN_INLINE_SIZE(this, result); - - // If this ever changes to return something other than zero, then - // nsSubDocumentFrame::GetMinISize will also need to change. - result = nscoord(0); - - return result; + auto size = GetIntrinsicSize(); + const auto& iSize = GetWritingMode().IsVertical() ? size.height : size.width; + return iSize.valueOr(0); } /* virtual */ nscoord SVGOuterSVGFrame::GetPrefISize(gfxContext* aRenderingContext) { nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); SVGSVGElement* svg = static_cast<SVGSVGElement*>(GetContent()); WritingMode wm = GetWritingMode(); @@ -325,7 +319,6 @@ void SVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("SVGOuterSVGFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_FRAME_TRACE( NS_FRAME_TRACE_CALLS, diff --git a/layout/svg/SVGUseFrame.cpp b/layout/svg/SVGUseFrame.cpp index c655f7b24f..e356895208 100644 --- a/layout/svg/SVGUseFrame.cpp +++ b/layout/svg/SVGUseFrame.cpp @@ -42,19 +42,6 @@ void SVGUseFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, SVGGFrame::Init(aContent, aParent, aPrevInFlow); } -nsresult SVGUseFrame::AttributeChanged(int32_t aNamespaceID, nsAtom* aAttribute, - int32_t aModType) { - // Currently our SMIL implementation does not modify the DOM attributes. Once - // we implement the SVG 2 SMIL behaviour this can be removed - // SVGUseElement::AfterSetAttr's implementation will be sufficient. - if (aModType == MutationEvent_Binding::SMIL) { - auto* content = SVGUseElement::FromNode(GetContent()); - content->ProcessAttributeChange(aNamespaceID, aAttribute); - } - - return SVGGFrame::AttributeChanged(aNamespaceID, aAttribute, aModType); -} - void SVGUseFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) { SVGGFrame::DidSetComputedStyle(aOldComputedStyle); diff --git a/layout/svg/SVGUseFrame.h b/layout/svg/SVGUseFrame.h index b72d29247a..a528522991 100644 --- a/layout/svg/SVGUseFrame.h +++ b/layout/svg/SVGUseFrame.h @@ -41,8 +41,6 @@ class SVGUseFrame final : public SVGGFrame { void DimensionAttributeChanged(bool aHadValidDimensions, bool aAttributeIsUsed); - nsresult AttributeChanged(int32_t aNamespaceID, nsAtom* aAttribute, - int32_t aModType) override; void DidSetComputedStyle(ComputedStyle* aOldComputedStyle) override; #ifdef DEBUG_FRAME_DUMP diff --git a/layout/svg/SVGUtils.cpp b/layout/svg/SVGUtils.cpp index 2967bac780..7c0d864b20 100644 --- a/layout/svg/SVGUtils.cpp +++ b/layout/svg/SVGUtils.cpp @@ -1080,8 +1080,8 @@ bool SVGUtils::GetNonScalingStrokeTransform(const nsIFrame* aFrame, MOZ_ASSERT(aFrame->GetContent()->IsSVGElement(), "should be an SVG element"); - *aUserToOuterSVG = ThebesMatrix(SVGContentUtils::GetCTM( - static_cast<SVGElement*>(aFrame->GetContent()), true)); + *aUserToOuterSVG = ThebesMatrix( + SVGContentUtils::GetCTM(static_cast<SVGElement*>(aFrame->GetContent()))); return aUserToOuterSVG->HasNonTranslation(); } diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index 0d38fed5ab..690faec32e 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -223,8 +223,8 @@ load 1467552-1.html load 1474982.html load conditional-outer-svg-nondirty-reflow-assert.xhtml load extref-test-1.xhtml -skip-if(wayland) pref(widget.windows.window_occlusion_tracking.enabled,false) load blob-merging-and-retained-display-list.html # Bug 1819154, wayland: Bug 1857256 -skip-if(wayland) load empty-blob-merging.html # wayland: Bug 1857256 +load blob-merging-and-retained-display-list.html +load empty-blob-merging.html load grouping-empty-bounds.html load 1480275.html load 1480224.html @@ -243,7 +243,7 @@ load 1539318-1.svg load 1548985-1.html load 1548985-2.svg load 1555851.html -skip-if(wayland) pref(widget.windows.window_occlusion_tracking.enabled,false) load invalidation-of-opacity-0.html # Bug 1819154, wayland: Bug 1857256 +load invalidation-of-opacity-0.html load 1563779.html load 1600855.html load 1601824.html diff --git a/layout/tables/BasicTableLayoutStrategy.cpp b/layout/tables/BasicTableLayoutStrategy.cpp index 5cb890c234..b6fe51db59 100644 --- a/layout/tables/BasicTableLayoutStrategy.cpp +++ b/layout/tables/BasicTableLayoutStrategy.cpp @@ -37,7 +37,6 @@ BasicTableLayoutStrategy::~BasicTableLayoutStrategy() = default; /* virtual */ nscoord BasicTableLayoutStrategy::GetMinISize(gfxContext* aRenderingContext) { - DISPLAY_MIN_INLINE_SIZE(mTableFrame, mMinISize); if (mMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) { ComputeIntrinsicISizes(aRenderingContext); } @@ -47,7 +46,6 @@ nscoord BasicTableLayoutStrategy::GetMinISize(gfxContext* aRenderingContext) { /* virtual */ nscoord BasicTableLayoutStrategy::GetPrefISize(gfxContext* aRenderingContext, bool aComputingSize) { - DISPLAY_PREF_INLINE_SIZE(mTableFrame, mPrefISize); NS_ASSERTION((mPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) == (mPrefISizePctExpand == NS_INTRINSIC_ISIZE_UNKNOWN), "dirtyness out of sync"); @@ -97,18 +95,10 @@ static CellISizeInfo GetISizeInfo(gfxContext* aRenderingContext, // XXX Should we ignore percentage padding? nsIFrame::IntrinsicSizeOffsetData offsets = aFrame->IntrinsicISizeOffsets(); - - // In quirks mode, table cell isize should be content-box, - // but bsize should be border box. - // Because of this historic anomaly, we do not use quirk.css. - // (We can't specify one value of box-sizing for isize and another - // for bsize). - // For this reason, we also do not use box-sizing for just one of - // them, as this may be confusing. - if (isQuirks || stylePos->mBoxSizing == StyleBoxSizing::Content) { + if (stylePos->mBoxSizing == StyleBoxSizing::Content) { boxSizingToBorderEdge = offsets.padding + offsets.border; } else { - // StyleBoxSizing::Border and standards-mode + // StyleBoxSizing::Border minCoord += offsets.padding + offsets.border; prefCoord += offsets.padding + offsets.border; } diff --git a/layout/tables/FixedTableLayoutStrategy.cpp b/layout/tables/FixedTableLayoutStrategy.cpp index 8d74e3ba12..5eda3b001e 100644 --- a/layout/tables/FixedTableLayoutStrategy.cpp +++ b/layout/tables/FixedTableLayoutStrategy.cpp @@ -32,7 +32,6 @@ FixedTableLayoutStrategy::~FixedTableLayoutStrategy() = default; /* virtual */ nscoord FixedTableLayoutStrategy::GetMinISize(gfxContext* aRenderingContext) { - DISPLAY_MIN_INLINE_SIZE(mTableFrame, mMinISize); if (mMinISize != NS_INTRINSIC_ISIZE_UNKNOWN) { return mMinISize; } @@ -119,9 +118,7 @@ nscoord FixedTableLayoutStrategy::GetPrefISize(gfxContext* aRenderingContext, // algorithm to find the narrowest inline size that would hold all of // those intrinsic inline sizes), but it wouldn't be compatible with // other browsers. - nscoord result = nscoord_MAX; - DISPLAY_PREF_INLINE_SIZE(mTableFrame, result); - return result; + return nscoord_MAX; } /* virtual */ diff --git a/layout/tables/celldata.h b/layout/tables/celldata.h index 681f0e9f28..22cdb4217d 100644 --- a/layout/tables/celldata.h +++ b/layout/tables/celldata.h @@ -347,7 +347,7 @@ inline BCData::BCData() { SetBStartStart(true); SetIStartStart(true); mIStartSize = mCornerSubSize = mBStartSize = 0; - mCornerSide = mozilla::eLogicalSideBStart; + mCornerSide = static_cast<uint8_t>(mozilla::LogicalSide::BStart); mCornerBevel = false; } @@ -393,7 +393,7 @@ inline BCPixelSize BCData::GetCorner(mozilla::LogicalSide& aOwnerSide, inline void BCData::SetCorner(BCPixelSize aSubSize, mozilla::LogicalSide aOwnerSide, bool aBevel) { mCornerSubSize = aSubSize; - mCornerSide = aOwnerSide; + mCornerSide = static_cast<uint8_t>(aOwnerSide); mCornerBevel = aBevel; } diff --git a/layout/tables/nsCellMap.cpp b/layout/tables/nsCellMap.cpp index 7c29baa5b0..60496d3ae0 100644 --- a/layout/tables/nsCellMap.cpp +++ b/layout/tables/nsCellMap.cpp @@ -678,7 +678,8 @@ void nsTableCellMap::Dump(char* aString) const { printf("l=%d%X%d ", int32_t(size), owner, segStart); } else { size = cd.GetCorner(side, bevel); - printf("c=%d%X%d ", int32_t(size), side, bevel); + printf("c=%d%hhX%d ", int32_t(size), static_cast<uint8_t>(side), + bevel); } } BCData& cd = mBCInfo->mBEndIEndCorner; @@ -690,7 +691,7 @@ void nsTableCellMap::Dump(char* aString) const { printf("l=%d%X%d ", int32_t(size), owner, segStart); } else { size = cd.GetCorner(side, bevel); - printf("c=%d%X%d ", int32_t(size), side, bevel); + printf("c=%d%hhX%d ", int32_t(size), static_cast<uint8_t>(side), bevel); } } printf("\n"); @@ -818,7 +819,7 @@ bool nsTableCellMap::RowHasSpanningCells(int32_t aRowIndex, return false; } -// FIXME: The only value callers pass for aSide is eLogicalSideBEnd. +// FIXME: The only value callers pass for aSide is LogicalSide::BEnd. // Consider removing support for the other three values. void nsTableCellMap::ResetBStartStart(LogicalSide aSide, nsCellMap& aCellMap, uint32_t aRowGroupStart, @@ -829,16 +830,16 @@ void nsTableCellMap::ResetBStartStart(LogicalSide aSide, nsCellMap& aCellMap, BCData* bcData = nullptr; switch (aSide) { - case eLogicalSideBEnd: + case LogicalSide::BEnd: aRowIndex++; [[fallthrough]]; - case eLogicalSideBStart: + case LogicalSide::BStart: cellData = (BCCellData*)aCellMap.GetDataAt(aRowIndex - aRowGroupStart, aColIndex); if (cellData) { bcData = &cellData->mData; } else { - NS_ASSERTION(aSide == eLogicalSideBEnd, "program error"); + NS_ASSERTION(aSide == LogicalSide::BEnd, "program error"); // try the next row group nsCellMap* cellMap = aCellMap.GetNextSibling(); if (cellMap) { @@ -851,16 +852,16 @@ void nsTableCellMap::ResetBStartStart(LogicalSide aSide, nsCellMap& aCellMap, } } break; - case eLogicalSideIEnd: + case LogicalSide::IEnd: aColIndex++; [[fallthrough]]; - case eLogicalSideIStart: + case LogicalSide::IStart: cellData = (BCCellData*)aCellMap.GetDataAt(aRowIndex - aRowGroupStart, aColIndex); if (cellData) { bcData = &cellData->mData; } else { - NS_ASSERTION(aSide == eLogicalSideIEnd, "program error"); + NS_ASSERTION(aSide == LogicalSide::IEnd, "program error"); bcData = GetIEndMostBorder(aRowIndex); } break; @@ -890,11 +891,11 @@ void nsTableCellMap::SetBCBorderEdge(LogicalSide aSide, nsCellMap& aCellMap, bool changed; switch (aSide) { - case eLogicalSideBEnd: + case LogicalSide::BEnd: rgYPos++; yPos++; [[fallthrough]]; - case eLogicalSideBStart: + case LogicalSide::BStart: lastIndex = xPos + aLength - 1; for (xIndex = xPos; xIndex <= lastIndex; xIndex++) { changed = aChanged && (xIndex == xPos); @@ -908,7 +909,7 @@ void nsTableCellMap::SetBCBorderEdge(LogicalSide aSide, nsCellMap& aCellMap, false, 0, damageArea); if (!cellData) ABORT0(); } else { - NS_ASSERTION(aSide == eLogicalSideBEnd, "program error"); + NS_ASSERTION(aSide == LogicalSide::BEnd, "program error"); // try the next non empty row group nsCellMap* cellMap = aCellMap.GetNextSibling(); while (cellMap && (0 == cellMap->GetRowCount())) { @@ -935,10 +936,10 @@ void nsTableCellMap::SetBCBorderEdge(LogicalSide aSide, nsCellMap& aCellMap, NS_ERROR("Cellmap: BStart edge not found"); } break; - case eLogicalSideIEnd: + case LogicalSide::IEnd: xPos++; [[fallthrough]]; - case eLogicalSideIStart: + case LogicalSide::IStart: // since bStart, bEnd borders were set, there should already be a cellData // entry lastIndex = rgYPos + aLength - 1; @@ -948,7 +949,7 @@ void nsTableCellMap::SetBCBorderEdge(LogicalSide aSide, nsCellMap& aCellMap, if (cellData) { cellData->mData.SetIStartEdge(aOwner, aSize, changed); } else { - NS_ASSERTION(aSide == eLogicalSideIEnd, "program error"); + NS_ASSERTION(aSide == LogicalSide::IEnd, "program error"); BCData* bcData = GetIEndMostBorder(yIndex + aCellMapStart); if (bcData) { bcData->SetIStartEdge(aOwner, aSize, changed); @@ -2225,7 +2226,8 @@ void nsCellMap::Dump(bool aIsBorderCollapse) const { printf("l=%d%d%d ", int32_t(size), owner, segStart); } else { size = cd->mData.GetCorner(side, bevel); - printf("c=%d%d%d ", int32_t(size), side, bevel); + printf("c=%d%hhu%d ", int32_t(size), static_cast<uint8_t>(side), + bevel); } } } diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index 6eb1c3146a..86ce48d47b 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -48,9 +48,6 @@ nsTableCellFrame::nsTableCellFrame(ComputedStyle* aStyle, nsTableFrame* aTableFrame, ClassID aID) : nsContainerFrame(aStyle, aTableFrame->PresContext(), aID), mDesiredSize(aTableFrame->GetWritingMode()) { - mColIndex = 0; - mPriorAvailISize = 0; - SetContentEmpty(false); } @@ -381,10 +378,10 @@ LogicalSides nsTableCellFrame::GetLogicalSkipSides() const { } if (GetPrevInFlow()) { - skip |= eLogicalSideBitsBStart; + skip += LogicalSide::BStart; } if (GetNextInFlow()) { - skip |= eLogicalSideBitsBEnd; + skip += LogicalSide::BEnd; } return skip; } @@ -392,9 +389,14 @@ LogicalSides nsTableCellFrame::GetLogicalSkipSides() const { /* virtual */ nsMargin nsTableCellFrame::GetBorderOverflow() { return nsMargin(0, 0, 0, 0); } -// Align the cell's child frame within the cell +void nsTableCellFrame::BlockDirAlignChild( + WritingMode aWM, nscoord aMaxAscent, + ForceAlignTopForTableCell aForceAlignTop) { + MOZ_ASSERT(aForceAlignTop != ForceAlignTopForTableCell::Yes || + PresContext()->IsPaginated(), + "We shouldn't force table-cells to do 'vertical-align:top' if " + "we're not in printing!"); -void nsTableCellFrame::BlockDirAlignChild(WritingMode aWM, nscoord aMaxAscent) { /* It's the 'border-collapse' on the table that matters */ const LogicalMargin border = GetLogicalUsedBorder(GetWritingMode()) .ApplySkipSides(GetLogicalSkipSides()) @@ -413,8 +415,11 @@ void nsTableCellFrame::BlockDirAlignChild(WritingMode aWM, nscoord aMaxAscent) { nscoord childBSize = kidRect.BSize(aWM); // Vertically align the child + const auto verticalAlign = aForceAlignTop == ForceAlignTopForTableCell::Yes + ? StyleVerticalAlignKeyword::Top + : GetVerticalAlign(); nscoord kidBStart = 0; - switch (GetVerticalAlign()) { + switch (verticalAlign) { case StyleVerticalAlignKeyword::Baseline: if (!GetContentEmpty()) { // Align the baselines of the child frame with the baselines of @@ -590,26 +595,18 @@ nsIScrollableFrame* nsTableCellFrame::GetScrollTargetFrame() const { /* virtual */ nscoord nsTableCellFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord result = 0; - DISPLAY_MIN_INLINE_SIZE(this, result); - nsIFrame* inner = mFrames.FirstChild(); - result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, - IntrinsicISizeType::MinISize, - nsLayoutUtils::IGNORE_PADDING); - return result; + return nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, + IntrinsicISizeType::MinISize, + nsLayoutUtils::IGNORE_PADDING); } /* virtual */ nscoord nsTableCellFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord result = 0; - DISPLAY_PREF_INLINE_SIZE(this, result); - nsIFrame* inner = mFrames.FirstChild(); - result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, - IntrinsicISizeType::PrefISize, - nsLayoutUtils::IGNORE_PADDING); - return result; + return nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, + IntrinsicISizeType::PrefISize, + nsLayoutUtils::IGNORE_PADDING); } /* virtual */ nsIFrame::IntrinsicSizeOffsetData @@ -675,7 +672,6 @@ void nsTableCellFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); if (aReflowInput.mFlags.mSpecialBSizeReflow) { @@ -708,14 +704,12 @@ void nsTableCellFrame::Reflow(nsPresContext* aPresContext, if (aReflowInput.mFlags.mSpecialBSizeReflow) { const_cast<ReflowInput&>(aReflowInput) .SetComputedBSize(BSize(wm) - bp.BStartEnd(wm)); - DISPLAY_REFLOW_CHANGE(); } else { const nscoord computedUnpaginatedBSize = CalcUnpaginatedBSize(*this, *tableFrame, bp.BStartEnd(wm)); if (computedUnpaginatedBSize > 0) { const_cast<ReflowInput&>(aReflowInput) .SetComputedBSize(computedUnpaginatedBSize); - DISPLAY_REFLOW_CHANGE(); } } } @@ -977,11 +971,11 @@ LogicalMargin nsBCTableCellFrame::GetBorderWidth(WritingMode aWM) const { BCPixelSize nsBCTableCellFrame::GetBorderWidth(LogicalSide aSide) const { switch (aSide) { - case eLogicalSideBStart: + case LogicalSide::BStart: return BC_BORDER_END_HALF(mBStartBorder); - case eLogicalSideIEnd: + case LogicalSide::IEnd: return BC_BORDER_START_HALF(mIEndBorder); - case eLogicalSideBEnd: + case LogicalSide::BEnd: return BC_BORDER_START_HALF(mBEndBorder); default: return BC_BORDER_END_HALF(mIStartBorder); @@ -990,13 +984,13 @@ BCPixelSize nsBCTableCellFrame::GetBorderWidth(LogicalSide aSide) const { void nsBCTableCellFrame::SetBorderWidth(LogicalSide aSide, BCPixelSize aValue) { switch (aSide) { - case eLogicalSideBStart: + case LogicalSide::BStart: mBStartBorder = aValue; break; - case eLogicalSideIEnd: + case LogicalSide::IEnd: mIEndBorder = aValue; break; - case eLogicalSideBEnd: + case LogicalSide::BEnd: mBEndBorder = aValue; break; default: diff --git a/layout/tables/nsTableCellFrame.h b/layout/tables/nsTableCellFrame.h index f9b6f3ccac..ae1a881819 100644 --- a/layout/tables/nsTableCellFrame.h +++ b/layout/tables/nsTableCellFrame.h @@ -34,9 +34,6 @@ class PresShell; class nsTableCellFrame : public nsContainerFrame, public nsITableCellLayout, public nsIPercentBSizeObserver { - typedef mozilla::gfx::DrawTarget DrawTarget; - typedef mozilla::image::ImgDrawResult ImgDrawResult; - friend nsTableCellFrame* NS_NewTableCellFrame(mozilla::PresShell* aPresShell, ComputedStyle* aStyle, nsTableFrame* aTableFrame); @@ -44,11 +41,6 @@ class nsTableCellFrame : public nsContainerFrame, nsTableCellFrame(ComputedStyle* aStyle, nsTableFrame* aTableFrame) : nsTableCellFrame(aStyle, aTableFrame, kClassID) {} - protected: - typedef mozilla::WritingMode WritingMode; - typedef mozilla::LogicalSide LogicalSide; - typedef mozilla::LogicalMargin LogicalMargin; - public: NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS(nsTableCellFrame) @@ -122,7 +114,9 @@ class nsTableCellFrame : public nsContainerFrame, nsresult GetFrameName(nsAString& aResult) const override; #endif - void BlockDirAlignChild(mozilla::WritingMode aWM, nscoord aMaxAscent); + // Align the cell's child frame within the cell. + void BlockDirAlignChild(mozilla::WritingMode aWM, nscoord aMaxAscent, + mozilla::ForceAlignTopForTableCell aForceAlignTop); /* * Get the value of vertical-align adjusted for CSS 2's rules for a @@ -193,20 +187,24 @@ class nsTableCellFrame : public nsContainerFrame, void SetColIndex(int32_t aColIndex); - /** return the available isize given to this frame during its last reflow */ - inline nscoord GetPriorAvailISize(); - - /** set the available isize given to this frame during its last reflow */ - inline void SetPriorAvailISize(nscoord aPriorAvailISize); - - /** return the desired size returned by this frame during its last reflow */ - inline mozilla::LogicalSize GetDesiredSize(); + // Get or set the available isize given to this frame during its last reflow. + nscoord GetPriorAvailISize() const { return mPriorAvailISize; } + void SetPriorAvailISize(nscoord aPriorAvailISize) { + mPriorAvailISize = aPriorAvailISize; + } - /** set the desired size returned by this frame during its last reflow */ - inline void SetDesiredSize(const ReflowOutput& aDesiredSize); + // Get or set the desired size returned by this frame during its last reflow. + mozilla::LogicalSize GetDesiredSize() const { return mDesiredSize; } + void SetDesiredSize(const ReflowOutput& aDesiredSize) { + mDesiredSize = aDesiredSize.Size(GetWritingMode()); + } - bool GetContentEmpty() const; - void SetContentEmpty(bool aContentEmpty); + bool GetContentEmpty() const { + return HasAnyStateBits(NS_TABLE_CELL_CONTENT_EMPTY); + } + void SetContentEmpty(bool aContentEmpty) { + AddOrRemoveStateBits(NS_TABLE_CELL_CONTENT_EMPTY, aContentEmpty); + } nsTableCellFrame* GetNextCell() const { nsIFrame* sibling = GetNextSibling(); @@ -216,7 +214,7 @@ class nsTableCellFrame : public nsContainerFrame, return static_cast<nsTableCellFrame*>(sibling); } - virtual LogicalMargin GetBorderWidth(WritingMode aWM) const; + virtual mozilla::LogicalMargin GetBorderWidth(mozilla::WritingMode aWM) const; void DecorateForSelection(DrawTarget* aDrawTarget, nsPoint aPt); @@ -251,44 +249,17 @@ class nsTableCellFrame : public nsContainerFrame, friend class nsTableRowFrame; - uint32_t mColIndex; // the starting column for this cell - - nscoord mPriorAvailISize; // the avail isize during the last reflow - mozilla::LogicalSize mDesiredSize; // the last desired inline and block size -}; - -inline nscoord nsTableCellFrame::GetPriorAvailISize() { - return mPriorAvailISize; -} - -inline void nsTableCellFrame::SetPriorAvailISize(nscoord aPriorAvailISize) { - mPriorAvailISize = aPriorAvailISize; -} - -inline mozilla::LogicalSize nsTableCellFrame::GetDesiredSize() { - return mDesiredSize; -} - -inline void nsTableCellFrame::SetDesiredSize(const ReflowOutput& aDesiredSize) { - mDesiredSize = aDesiredSize.Size(GetWritingMode()); -} + // The starting column for this cell + uint32_t mColIndex = 0; -inline bool nsTableCellFrame::GetContentEmpty() const { - return HasAnyStateBits(NS_TABLE_CELL_CONTENT_EMPTY); -} + // The avail isize during the last reflow + nscoord mPriorAvailISize = 0; -inline void nsTableCellFrame::SetContentEmpty(bool aContentEmpty) { - if (aContentEmpty) { - AddStateBits(NS_TABLE_CELL_CONTENT_EMPTY); - } else { - RemoveStateBits(NS_TABLE_CELL_CONTENT_EMPTY); - } -} + // The last desired inline and block size + mozilla::LogicalSize mDesiredSize; +}; -// nsBCTableCellFrame class nsBCTableCellFrame final : public nsTableCellFrame { - typedef mozilla::image::ImgDrawResult ImgDrawResult; - public: NS_DECL_FRAMEARENA_HELPERS(nsBCTableCellFrame) @@ -299,13 +270,14 @@ class nsBCTableCellFrame final : public nsTableCellFrame { nsMargin GetUsedBorder() const override; // Get the *inner half of the border only*, in twips. - LogicalMargin GetBorderWidth(WritingMode aWM) const override; + mozilla::LogicalMargin GetBorderWidth( + mozilla::WritingMode aWM) const override; // Get the *inner half of the border only*, in pixels. - BCPixelSize GetBorderWidth(LogicalSide aSide) const; + BCPixelSize GetBorderWidth(mozilla::LogicalSide aSide) const; // Set the full (both halves) width of the border - void SetBorderWidth(LogicalSide aSide, BCPixelSize aPixelValue); + void SetBorderWidth(mozilla::LogicalSide aSide, BCPixelSize aPixelValue); nsMargin GetBorderOverflow() override; diff --git a/layout/tables/nsTableColFrame.cpp b/layout/tables/nsTableColFrame.cpp index 16eab74cab..8031d4c47c 100644 --- a/layout/tables/nsTableColFrame.cpp +++ b/layout/tables/nsTableColFrame.cpp @@ -82,7 +82,6 @@ void nsTableColFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableColFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); aDesiredSize.ClearSize(); const nsStyleVisibility* colVis = StyleVisibility(); diff --git a/layout/tables/nsTableColGroupFrame.cpp b/layout/tables/nsTableColGroupFrame.cpp index 54fe53a5c4..965960a58d 100644 --- a/layout/tables/nsTableColGroupFrame.cpp +++ b/layout/tables/nsTableColGroupFrame.cpp @@ -315,10 +315,10 @@ nsIFrame::LogicalSides nsTableColGroupFrame::GetLogicalSkipSides() const { } if (GetPrevInFlow()) { - skip |= eLogicalSideBitsBStart; + skip += LogicalSide::BStart; } if (GetNextInFlow()) { - skip |= eLogicalSideBitsBEnd; + skip += LogicalSide::BEnd; } return skip; } @@ -329,7 +329,6 @@ void nsTableColGroupFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); NS_ASSERTION(nullptr != mContent, "bad state -- null content for frame"); diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index e7fd7340bf..a2f8b2b625 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1235,10 +1235,10 @@ LogicalSides nsTableFrame::GetLogicalSkipSides() const { // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto // account for pagination if (GetPrevInFlow()) { - skip |= eLogicalSideBitsBStart; + skip += LogicalSide::BStart; } if (GetNextInFlow()) { - skip |= eLogicalSideBitsBEnd; + skip += LogicalSide::BEnd; } return skip; } @@ -1615,7 +1615,6 @@ void nsTableFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_OUT_OF_FLOW), "The nsTableWrapperFrame should be the out-of-flow if needed"); @@ -2393,8 +2392,11 @@ nsMargin nsTableFrame::GetUsedMargin() const { return nsMargin(0, 0, 0, 0); } -// TODO(TYLin): Should this property only be set on the first-in-flow of -// nsTableFrame? +// TODO(TYLin, dshin): This ideally should be set only in first-in-flow. +// However, the current implementation of border-collapsed table does not +// handle continuation gracefully. One concrete issue is shown in bug 1881157 +// comment 3. It is also unclear if the damage area, current included in this +// property, should be stored separately per-continuation. NS_DECLARE_FRAME_PROPERTY_DELETABLE(TableBCDataProperty, TableBCData) TableBCData* nsTableFrame::GetTableBCData() const { @@ -3800,24 +3802,29 @@ class BCMapCellIterator; /***************************************************************** * BCMapCellInfo - * This structure stores information about the cellmap and all involved - * table related frames that are used during the computation of winning borders - * in CalcBCBorders so that they do need to be looked up again and again when - * iterating over the cells. + * This structure stores information during the computation of winning borders + * in CalcBCBorders, so that they don't need to be looked up repeatedly. ****************************************************************/ -struct BCMapCellInfo { +struct BCMapCellInfo final { explicit BCMapCellInfo(nsTableFrame* aTableFrame); void ResetCellInfo(); void SetInfo(nsTableRowFrame* aNewRow, int32_t aColIndex, BCCellData* aCellData, BCMapCellIterator* aIter, nsCellMap* aCellMap = nullptr); - // functions to set the border widths on the table related frames, where the - // knowledge about the current position in the table is used. - void SetTableBStartBorderWidth(BCPixelSize aWidth); - void SetTableIStartBorderWidth(int32_t aRowB, BCPixelSize aWidth); - void SetTableIEndBorderWidth(int32_t aRowB, BCPixelSize aWidth); - void SetTableBEndBorderWidth(BCPixelSize aWidth); + // Functions to (re)set the border widths on the table related cell frames, + // where the knowledge about the current position in the table is used. + // For most "normal" cells that have row/colspan of 1, these functions + // are called once at most during the reflow, setting the value as given + // (Discarding the value from the previous reflow, which is now irrelevant). + // However, for cells spanning multiple rows/coluns, the maximum border + // width seen is stored. This is controlled by calling the reset functions + // before the cell's border is computed the first time. + void ResetIStartBorderWidths(); + void ResetIEndBorderWidths(); + void ResetBStartBorderWidths(); + void ResetBEndBorderWidths(); + void SetIStartBorderWidths(BCPixelSize aWidth); void SetIEndBorderWidths(BCPixelSize aWidth); void SetBStartBorderWidths(BCPixelSize aWidth); @@ -3845,12 +3852,12 @@ struct BCMapCellInfo { int32_t GetCellEndRowIndex() const; int32_t GetCellEndColIndex() const; - // storage of table information + // Storage of table information required to compute individual cell + // information. nsTableFrame* mTableFrame; nsTableFrame* mTableFirstInFlow; int32_t mNumTableRows; int32_t mNumTableCols; - TableBCData* mTableBCData; WritingMode mTableWM; // a cell can only belong to one rowgroup @@ -3893,7 +3900,6 @@ BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame) mTableFirstInFlow(static_cast<nsTableFrame*>(aTableFrame->FirstInFlow())), mNumTableRows(aTableFrame->GetRowCount()), mNumTableCols(aTableFrame->GetColCount()), - mTableBCData(mTableFirstInFlow->GetTableBCData()), mTableWM(aTableFrame->Style()), mCurrentRowFrame(nullptr), mCurrentColGroupFrame(nullptr), @@ -3922,6 +3928,36 @@ inline int32_t BCMapCellInfo::GetCellEndColIndex() const { return mColIndex + mColSpan - 1; } +static TableBCData* GetTableBCData(nsTableFrame* aTableFrame) { + auto* firstInFlow = static_cast<nsTableFrame*>(aTableFrame->FirstInFlow()); + return firstInFlow->GetTableBCData(); +} + +/***************************************************************** + * BCMapTableInfo + * This structure stores controls border information global to the + * table computed during the border-collapsed border calcuation. + ****************************************************************/ +struct BCMapTableInfo final { + explicit BCMapTableInfo(nsTableFrame* aTableFrame) + : mTableBCData{GetTableBCData(aTableFrame)} {} + + void ResetTableIStartBorderWidth() { mTableBCData->mIStartBorderWidth = 0; } + + void ResetTableIEndBorderWidth() { mTableBCData->mIEndBorderWidth = 0; } + + void ResetTableBStartBorderWidth() { mTableBCData->mBStartBorderWidth = 0; } + + void ResetTableBEndBorderWidth() { mTableBCData->mBEndBorderWidth = 0; } + + void SetTableIStartBorderWidth(int32_t aRowB, BCPixelSize aWidth); + void SetTableIEndBorderWidth(int32_t aRowB, BCPixelSize aWidth); + void SetTableBStartBorderWidth(BCPixelSize aWidth); + void SetTableBEndBorderWidth(BCPixelSize aWidth); + + TableBCData* mTableBCData; +}; + class BCMapCellIterator { public: BCMapCellIterator(nsTableFrame* aTableFrame, const TableArea& aDamageArea); @@ -4537,7 +4573,7 @@ struct BCCornerInfo { ownerColor = 0; ownerWidth = subWidth = ownerElem = subSide = subElem = hasDashDot = numSegs = bevel = 0; - ownerSide = eLogicalSideBStart; + ownerSide = static_cast<uint16_t>(LogicalSide::BStart); ownerStyle = BORDER_STYLE_UNSET; subStyle = StyleBorderStyle::Solid; } @@ -4552,7 +4588,7 @@ struct BCCornerInfo { // border perpendicular to ownerSide StyleBorderStyle subStyle; // border style of subElem StyleBorderStyle ownerStyle; // border style of ownerElem - uint16_t ownerSide : 2; // LogicalSide (e.g eLogicalSideBStart, etc) of the + uint16_t ownerSide : 2; // LogicalSide (e.g LogicalSide::BStart, etc) of the // border owning the corner relative to the corner uint16_t ownerElem : 4; // elem type (e.g. eTable, eGroup, etc) owning the corner @@ -4575,7 +4611,7 @@ void BCCornerInfo::Set(mozilla::LogicalSide aSide, BCCellBorder aBorder) { ownerStyle = aBorder.style; ownerWidth = aBorder.width; ownerColor = aBorder.color; - ownerSide = aSide; + ownerSide = static_cast<uint16_t>(aSide); hasDashDot = 0; numSegs = 0; if (aBorder.width > 0) { @@ -4586,7 +4622,8 @@ void BCCornerInfo::Set(mozilla::LogicalSide aSide, BCCellBorder aBorder) { bevel = 0; subWidth = 0; // the following will get set later - subSide = IsInline(aSide) ? eLogicalSideBStart : eLogicalSideIStart; + subSide = static_cast<uint16_t>(IsInline(aSide) ? LogicalSide::BStart + : LogicalSide::IStart); subElem = eTableOwner; subStyle = StyleBorderStyle::Solid; } @@ -4631,16 +4668,16 @@ void BCCornerInfo::Update(mozilla::LogicalSide aSide, BCCellBorder aBorder) { subStyle = tempBorder.style; subWidth = tempBorder.width; if (!firstWins) { - subSide = aSide; + subSide = static_cast<uint16_t>(aSide); } } } else { // input args are dominant - ownerSide = aSide; + ownerSide = static_cast<uint16_t>(aSide); if (::Perpendicular(oldSide, LogicalSide(ownerSide))) { subElem = oldBorder.owner; subStyle = oldBorder.style; subWidth = oldBorder.width; - subSide = oldSide; + subSide = static_cast<uint16_t>(oldSide); } } if (aBorder.width > 0) { @@ -4858,13 +4895,8 @@ void nsTableFrame::ExpandBCDamageArea(TableArea& aArea) const { #define ADJACENT true #define INLINE_DIR true -void BCMapCellInfo::SetTableBStartBorderWidth(BCPixelSize aWidth) { - mTableBCData->mBStartBorderWidth = - std::max(mTableBCData->mBStartBorderWidth, aWidth); -} - -void BCMapCellInfo::SetTableIStartBorderWidth(int32_t aRowB, - BCPixelSize aWidth) { +void BCMapTableInfo::SetTableIStartBorderWidth(int32_t aRowB, + BCPixelSize aWidth) { // update the iStart first cell border if (aRowB == 0) { mTableBCData->mIStartCellBorderWidth = aWidth; @@ -4873,7 +4905,8 @@ void BCMapCellInfo::SetTableIStartBorderWidth(int32_t aRowB, std::max(mTableBCData->mIStartBorderWidth, aWidth); } -void BCMapCellInfo::SetTableIEndBorderWidth(int32_t aRowB, BCPixelSize aWidth) { +void BCMapTableInfo::SetTableIEndBorderWidth(int32_t aRowB, + BCPixelSize aWidth) { // update the iEnd first cell border if (aRowB == 0) { mTableBCData->mIEndCellBorderWidth = aWidth; @@ -4882,38 +4915,83 @@ void BCMapCellInfo::SetTableIEndBorderWidth(int32_t aRowB, BCPixelSize aWidth) { std::max(mTableBCData->mIEndBorderWidth, aWidth); } -void BCMapCellInfo::SetIEndBorderWidths(BCPixelSize aWidth) { - // update the borders of the cells and cols affected +void BCMapTableInfo::SetTableBStartBorderWidth(BCPixelSize aWidth) { + mTableBCData->mBStartBorderWidth = + std::max(mTableBCData->mBStartBorderWidth, aWidth); +} + +void BCMapTableInfo::SetTableBEndBorderWidth(BCPixelSize aWidth) { + mTableBCData->mBEndBorderWidth = + std::max(mTableBCData->mBEndBorderWidth, aWidth); +} + +void BCMapCellInfo::ResetIStartBorderWidths() { if (mCell) { - mCell->SetBorderWidth( - eLogicalSideIEnd, - std::max(aWidth, mCell->GetBorderWidth(eLogicalSideIEnd))); + mCell->SetBorderWidth(LogicalSide::IStart, 0); + } + if (mStartCol) { + mStartCol->SetIStartBorderWidth(0); + } +} + +void BCMapCellInfo::ResetIEndBorderWidths() { + if (mCell) { + mCell->SetBorderWidth(LogicalSide::IEnd, 0); } if (mEndCol) { - BCPixelSize half = BC_BORDER_START_HALF(aWidth); - mEndCol->SetIEndBorderWidth(std::max(half, mEndCol->GetIEndBorderWidth())); + mEndCol->SetIEndBorderWidth(0); } } -void BCMapCellInfo::SetBEndBorderWidths(BCPixelSize aWidth) { - // update the borders of the affected cells and rows +void BCMapCellInfo::ResetBStartBorderWidths() { if (mCell) { - mCell->SetBorderWidth( - eLogicalSideBEnd, - std::max(aWidth, mCell->GetBorderWidth(eLogicalSideBEnd))); + mCell->SetBorderWidth(LogicalSide::BStart, 0); + } + if (mStartRow) { + mStartRow->SetBStartBCBorderWidth(0); + } +} + +void BCMapCellInfo::ResetBEndBorderWidths() { + if (mCell) { + mCell->SetBorderWidth(LogicalSide::BEnd, 0); } if (mEndRow) { + mEndRow->SetBEndBCBorderWidth(0); + } +} + +void BCMapCellInfo::SetIStartBorderWidths(BCPixelSize aWidth) { + if (mCell) { + mCell->SetBorderWidth( + LogicalSide::IStart, + std::max(aWidth, mCell->GetBorderWidth(LogicalSide::IStart))); + } + if (mStartCol) { + BCPixelSize half = BC_BORDER_END_HALF(aWidth); + mStartCol->SetIStartBorderWidth( + std::max(half, mStartCol->GetIStartBorderWidth())); + } +} + +void BCMapCellInfo::SetIEndBorderWidths(BCPixelSize aWidth) { + // update the borders of the cells and cols affected + if (mCell) { + mCell->SetBorderWidth( + LogicalSide::IEnd, + std::max(aWidth, mCell->GetBorderWidth(LogicalSide::IEnd))); + } + if (mEndCol) { BCPixelSize half = BC_BORDER_START_HALF(aWidth); - mEndRow->SetBEndBCBorderWidth( - std::max(half, mEndRow->GetBEndBCBorderWidth())); + mEndCol->SetIEndBorderWidth(std::max(half, mEndCol->GetIEndBorderWidth())); } } void BCMapCellInfo::SetBStartBorderWidths(BCPixelSize aWidth) { if (mCell) { mCell->SetBorderWidth( - eLogicalSideBStart, - std::max(aWidth, mCell->GetBorderWidth(eLogicalSideBStart))); + LogicalSide::BStart, + std::max(aWidth, mCell->GetBorderWidth(LogicalSide::BStart))); } if (mStartRow) { BCPixelSize half = BC_BORDER_END_HALF(aWidth); @@ -4922,24 +5000,20 @@ void BCMapCellInfo::SetBStartBorderWidths(BCPixelSize aWidth) { } } -void BCMapCellInfo::SetIStartBorderWidths(BCPixelSize aWidth) { +void BCMapCellInfo::SetBEndBorderWidths(BCPixelSize aWidth) { + // update the borders of the affected cells and rows if (mCell) { mCell->SetBorderWidth( - eLogicalSideIStart, - std::max(aWidth, mCell->GetBorderWidth(eLogicalSideIStart))); + LogicalSide::BEnd, + std::max(aWidth, mCell->GetBorderWidth(LogicalSide::BEnd))); } - if (mStartCol) { - BCPixelSize half = BC_BORDER_END_HALF(aWidth); - mStartCol->SetIStartBorderWidth( - std::max(half, mStartCol->GetIStartBorderWidth())); + if (mEndRow) { + BCPixelSize half = BC_BORDER_START_HALF(aWidth); + mEndRow->SetBEndBCBorderWidth( + std::max(half, mEndRow->GetBEndBCBorderWidth())); } } -void BCMapCellInfo::SetTableBEndBorderWidth(BCPixelSize aWidth) { - mTableBCData->mBEndBorderWidth = - std::max(mTableBCData->mBEndBorderWidth, aWidth); -} - void BCMapCellInfo::SetColumn(int32_t aColX) { mCurrentColFrame = mTableFirstInFlow->GetColFrame(aColX); mCurrentColGroupFrame = @@ -4957,46 +5031,46 @@ void BCMapCellInfo::IncrementRow(bool aResetToBStartRowOfCell) { BCCellBorder BCMapCellInfo::GetBStartEdgeBorder() { return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, mRowGroup, mStartRow, mCell, mTableWM, - eLogicalSideBStart, !ADJACENT); + LogicalSide::BStart, !ADJACENT); } BCCellBorder BCMapCellInfo::GetBEndEdgeBorder() { return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, - mRowGroup, mEndRow, mCell, mTableWM, eLogicalSideBEnd, + mRowGroup, mEndRow, mCell, mTableWM, LogicalSide::BEnd, ADJACENT); } BCCellBorder BCMapCellInfo::GetIStartEdgeBorder() { return CompareBorders(mTableFrame, mColGroup, mStartCol, mRowGroup, - mCurrentRowFrame, mCell, mTableWM, eLogicalSideIStart, + mCurrentRowFrame, mCell, mTableWM, LogicalSide::IStart, !ADJACENT); } BCCellBorder BCMapCellInfo::GetIEndEdgeBorder() { return CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup, - mCurrentRowFrame, mCell, mTableWM, eLogicalSideIEnd, + mCurrentRowFrame, mCell, mTableWM, LogicalSide::IEnd, ADJACENT); } BCCellBorder BCMapCellInfo::GetIEndInternalBorder() { const nsIFrame* cg = mCgAtEnd ? mColGroup : nullptr; return CompareBorders(nullptr, cg, mEndCol, nullptr, nullptr, mCell, mTableWM, - eLogicalSideIEnd, ADJACENT); + LogicalSide::IEnd, ADJACENT); } BCCellBorder BCMapCellInfo::GetIStartInternalBorder() { const nsIFrame* cg = mCgAtStart ? mColGroup : nullptr; return CompareBorders(nullptr, cg, mStartCol, nullptr, nullptr, mCell, - mTableWM, eLogicalSideIStart, !ADJACENT); + mTableWM, LogicalSide::IStart, !ADJACENT); } BCCellBorder BCMapCellInfo::GetBEndInternalBorder() { const nsIFrame* rg = mRgAtEnd ? mRowGroup : nullptr; return CompareBorders(nullptr, nullptr, nullptr, rg, mEndRow, mCell, mTableWM, - eLogicalSideBEnd, ADJACENT); + LogicalSide::BEnd, ADJACENT); } BCCellBorder BCMapCellInfo::GetBStartInternalBorder() { const nsIFrame* rg = mRgAtStart ? mRowGroup : nullptr; return CompareBorders(nullptr, nullptr, nullptr, rg, mStartRow, mCell, - mTableWM, eLogicalSideBStart, !ADJACENT); + mTableWM, LogicalSide::BStart, !ADJACENT); } // Calculate border information for border-collapsed tables. @@ -5115,6 +5189,10 @@ void nsTableFrame::CalcBCBorders() { if (!lastBEndBorders.borders) ABORT0(); BCMapCellInfo info(this); + // TODO(dshin): This is basically propData, except it uses first-in-flow's + // data. Consult the definition of `TableBCDataProperty` regarding + // using the first-in-flow only. + BCMapTableInfo tableInfo(this); // Block-start corners of the cell being traversed, indexed by columns. BCCorners bStartCorners(damageArea.ColCount() + 1, damageArea.StartCol()); @@ -5164,10 +5242,12 @@ void nsTableFrame::CalcBCBorders() { // table, row group, row if the border is at the bStart of the table, // otherwise it was processed in a previous row if (0 == info.mRowIndex) { - if (!tableBorderReset[eLogicalSideBStart]) { - propData->mBStartBorderWidth = 0; - tableBorderReset[eLogicalSideBStart] = true; + uint8_t idxBStart = static_cast<uint8_t>(LogicalSide::BStart); + if (!tableBorderReset[idxBStart]) { + tableInfo.ResetTableBStartBorderWidth(); + tableBorderReset[idxBStart] = true; } + bool reset = false; for (int32_t colIdx = info.mColIndex; colIdx <= info.GetCellEndColIndex(); colIdx++) { info.SetColumn(colIdx); @@ -5175,9 +5255,9 @@ void nsTableFrame::CalcBCBorders() { BCCornerInfo& bStartIStartCorner = bStartCorners[colIdx]; // Mark inline-end direction border from this corner. if (0 == colIdx) { - bStartIStartCorner.Set(eLogicalSideIEnd, currentBorder); + bStartIStartCorner.Set(LogicalSide::IEnd, currentBorder); } else { - bStartIStartCorner.Update(eLogicalSideIEnd, currentBorder); + bStartIStartCorner.Update(LogicalSide::IEnd, currentBorder); tableCellMap->SetBCBorderCorner( LogicalCorner::BStartIStart, *iter.mCellMap, 0, 0, colIdx, LogicalSide(bStartIStartCorner.ownerSide), @@ -5186,7 +5266,7 @@ void nsTableFrame::CalcBCBorders() { // Above, we set the corner `colIndex` column as having a border towards // inline-end, heading towards the next column. Vice versa is also true, // where the next column has a border heading towards this column. - bStartCorners[colIdx + 1].Set(eLogicalSideIStart, currentBorder); + bStartCorners[colIdx + 1].Set(LogicalSide::IStart, currentBorder); MOZ_ASSERT(firstRowBStartEdgeBorder, "Inline start border tracking not set?"); // update firstRowBStartEdgeBorder and see if a new segment starts @@ -5196,13 +5276,17 @@ void nsTableFrame::CalcBCBorders() { firstRowBStartEdgeBorder.ref()) : true; // store the border segment in the cell map - tableCellMap->SetBCBorderEdge(eLogicalSideBStart, *iter.mCellMap, 0, 0, + tableCellMap->SetBCBorderEdge(LogicalSide::BStart, *iter.mCellMap, 0, 0, colIdx, 1, currentBorder.owner, currentBorder.width, startSeg); // Set border width at block-start (table-wide and for the cell), but // only if it's the largest we've encountered. - info.SetTableBStartBorderWidth(currentBorder.width); + tableInfo.SetTableBStartBorderWidth(currentBorder.width); + if (!reset) { + info.ResetBStartBorderWidths(); + reset = true; + } info.SetBStartBorderWidths(currentBorder.width); } } else { @@ -5225,34 +5309,40 @@ void nsTableFrame::CalcBCBorders() { // table, col group, col if the border is at the iStart of the table, // otherwise it was processed in a previous col if (0 == info.mColIndex) { - if (!tableBorderReset[eLogicalSideIStart]) { - propData->mIStartBorderWidth = 0; - tableBorderReset[eLogicalSideIStart] = true; + uint8_t idxIStart = static_cast<uint8_t>(LogicalSide::IStart); + if (!tableBorderReset[idxIStart]) { + tableInfo.ResetTableIStartBorderWidth(); + tableBorderReset[idxIStart] = true; } info.mCurrentRowFrame = nullptr; + bool reset = false; for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex(); rowB++) { info.IncrementRow(rowB == info.mRowIndex); BCCellBorder currentBorder = info.GetIStartEdgeBorder(); BCCornerInfo& bStartIStartCorner = (0 == rowB) ? bStartCorners[0] : bEndCorners[0]; - bStartIStartCorner.Update(eLogicalSideBEnd, currentBorder); + bStartIStartCorner.Update(LogicalSide::BEnd, currentBorder); tableCellMap->SetBCBorderCorner( LogicalCorner::BStartIStart, *iter.mCellMap, iter.mRowGroupStart, rowB, 0, LogicalSide(bStartIStartCorner.ownerSide), bStartIStartCorner.subWidth, bStartIStartCorner.bevel); - bEndCorners[0].Set(eLogicalSideBStart, currentBorder); + bEndCorners[0].Set(LogicalSide::BStart, currentBorder); // update lastBlockDirBorders and see if a new segment starts bool startSeg = SetBorder(currentBorder, lastBlockDirBorders[0]); // store the border segment in the cell map - tableCellMap->SetBCBorderEdge(eLogicalSideIStart, *iter.mCellMap, + tableCellMap->SetBCBorderEdge(LogicalSide::IStart, *iter.mCellMap, iter.mRowGroupStart, rowB, info.mColIndex, 1, currentBorder.owner, currentBorder.width, startSeg); // Set border width at inline-start (table-wide and for the cell), but // only if it's the largest we've encountered. - info.SetTableIStartBorderWidth(rowB, currentBorder.width); + tableInfo.SetTableIStartBorderWidth(rowB, currentBorder.width); + if (!reset) { + info.ResetIStartBorderWidths(); + reset = true; + } info.SetIStartBorderWidths(currentBorder.width); } } @@ -5261,11 +5351,13 @@ void nsTableFrame::CalcBCBorders() { // cells and the table, row group, row if (info.mNumTableCols == info.GetCellEndColIndex() + 1) { // touches iEnd edge of table - if (!tableBorderReset[eLogicalSideIEnd]) { - propData->mIEndBorderWidth = 0; - tableBorderReset[eLogicalSideIEnd] = true; + uint8_t idxIEnd = static_cast<uint8_t>(LogicalSide::IEnd); + if (!tableBorderReset[idxIEnd]) { + tableInfo.ResetTableIEndBorderWidth(); + tableBorderReset[idxIEnd] = true; } info.mCurrentRowFrame = nullptr; + bool reset = false; for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex(); rowB++) { info.IncrementRow(rowB == info.mRowIndex); @@ -5275,7 +5367,7 @@ void nsTableFrame::CalcBCBorders() { BCCornerInfo& bStartIEndCorner = (0 == rowB) ? bStartCorners[info.GetCellEndColIndex() + 1] : bEndCorners[info.GetCellEndColIndex() + 1]; - bStartIEndCorner.Update(eLogicalSideBEnd, currentBorder); + bStartIEndCorner.Update(LogicalSide::BEnd, currentBorder); tableCellMap->SetBCBorderCorner( LogicalCorner::BStartIEnd, *iter.mCellMap, iter.mRowGroupStart, rowB, info.GetCellEndColIndex(), @@ -5283,7 +5375,7 @@ void nsTableFrame::CalcBCBorders() { bStartIEndCorner.bevel); BCCornerInfo& bEndIEndCorner = bEndCorners[info.GetCellEndColIndex() + 1]; - bEndIEndCorner.Set(eLogicalSideBStart, currentBorder); + bEndIEndCorner.Set(LogicalSide::BStart, currentBorder); tableCellMap->SetBCBorderCorner( LogicalCorner::BEndIEnd, *iter.mCellMap, iter.mRowGroupStart, rowB, info.GetCellEndColIndex(), LogicalSide(bEndIEndCorner.ownerSide), @@ -5293,12 +5385,16 @@ void nsTableFrame::CalcBCBorders() { currentBorder, lastBlockDirBorders[info.GetCellEndColIndex() + 1]); // store the border segment in the cell map and update cellBorders tableCellMap->SetBCBorderEdge( - eLogicalSideIEnd, *iter.mCellMap, iter.mRowGroupStart, rowB, + LogicalSide::IEnd, *iter.mCellMap, iter.mRowGroupStart, rowB, info.GetCellEndColIndex(), 1, currentBorder.owner, currentBorder.width, startSeg); // Set border width at inline-end (table-wide and for the cell), but // only if it's the largest we've encountered. - info.SetTableIEndBorderWidth(rowB, currentBorder.width); + tableInfo.SetTableIEndBorderWidth(rowB, currentBorder.width); + if (!reset) { + info.ResetIEndBorderWidths(); + reset = true; + } info.SetIEndBorderWidths(currentBorder.width); } } else { @@ -5306,6 +5402,7 @@ void nsTableFrame::CalcBCBorders() { int32_t segLength = 0; BCMapCellInfo ajaInfo(this); BCMapCellInfo priorAjaInfo(this); + bool reset = false; for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex(); rowB += segLength) { // Grab the cell adjacent to our inline-end. @@ -5325,9 +5422,14 @@ void nsTableFrame::CalcBCBorders() { if (info.GetCellEndColIndex() < damageArea.EndCol() && rowB >= damageArea.StartRow() && rowB < damageArea.EndRow()) { tableCellMap->SetBCBorderEdge( - eLogicalSideIEnd, *iter.mCellMap, iter.mRowGroupStart, rowB, + LogicalSide::IEnd, *iter.mCellMap, iter.mRowGroupStart, rowB, info.GetCellEndColIndex(), segLength, currentBorder.owner, currentBorder.width, startSeg); + if (!reset) { + info.ResetIEndBorderWidths(); + ajaInfo.ResetIStartBorderWidths(); + reset = true; + } info.SetIEndBorderWidths(currentBorder.width); ajaInfo.SetIStartBorderWidths(currentBorder.width); } @@ -5346,7 +5448,7 @@ void nsTableFrame::CalcBCBorders() { ? &bStartCorners[info.GetCellEndColIndex() + 1] : &bEndCorners[info.GetCellEndColIndex() + 1]; // From previous row. - bStartIEndCorner->Update(eLogicalSideBEnd, currentBorder); + bStartIEndCorner->Update(LogicalSide::BEnd, currentBorder); // If this is a rowspan, need to consider if this "corner" is generating // an inline segment for the adjacent cell. e.g. // @@ -5360,7 +5462,7 @@ void nsTableFrame::CalcBCBorders() { BCCellBorder adjacentBorder = ajaInfo.GetBStartInternalBorder(); currentBorder = CompareBorders(!CELL_CORNER, currentBorder, adjacentBorder, INLINE_DIR); - bStartIEndCorner->Update(eLogicalSideIEnd, currentBorder); + bStartIEndCorner->Update(LogicalSide::IEnd, currentBorder); } // Check that the spanned area is inside of the invalidation area if (info.GetCellEndColIndex() < damageArea.EndCol() && @@ -5387,7 +5489,7 @@ void nsTableFrame::CalcBCBorders() { BCCornerInfo& bEndIEndCorner = (hitsSpanOnIEnd) ? bStartCorners[info.GetCellEndColIndex() + 1] : bEndCorners[info.GetCellEndColIndex() + 1]; - bEndIEndCorner.Set(eLogicalSideBStart, currentBorder); + bEndIEndCorner.Set(LogicalSide::BStart, currentBorder); priorAjaInfo = ajaInfo; } } @@ -5400,23 +5502,25 @@ void nsTableFrame::CalcBCBorders() { // cells and the table, row group, row if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) { // touches bEnd edge of table - if (!tableBorderReset[eLogicalSideBEnd]) { - propData->mBEndBorderWidth = 0; - tableBorderReset[eLogicalSideBEnd] = true; + uint8_t idxBEnd = static_cast<uint8_t>(LogicalSide::BEnd); + if (!tableBorderReset[idxBEnd]) { + tableInfo.ResetTableBEndBorderWidth(); + tableBorderReset[idxBEnd] = true; } + bool reset = false; for (int32_t colIdx = info.mColIndex; colIdx <= info.GetCellEndColIndex(); colIdx++) { info.SetColumn(colIdx); BCCellBorder currentBorder = info.GetBEndEdgeBorder(); BCCornerInfo& bEndIStartCorner = bEndCorners[colIdx]; - bEndIStartCorner.Update(eLogicalSideIEnd, currentBorder); + bEndIStartCorner.Update(LogicalSide::IEnd, currentBorder); tableCellMap->SetBCBorderCorner( LogicalCorner::BEndIStart, *iter.mCellMap, iter.mRowGroupStart, info.GetCellEndRowIndex(), colIdx, LogicalSide(bEndIStartCorner.ownerSide), bEndIStartCorner.subWidth, bEndIStartCorner.bevel); BCCornerInfo& bEndIEndCorner = bEndCorners[colIdx + 1]; - bEndIEndCorner.Update(eLogicalSideIStart, currentBorder); + bEndIEndCorner.Update(LogicalSide::IStart, currentBorder); // Store the block-end inline-end corner if it also is the block-end // inline-end of the overall table. if (info.mNumTableCols == colIdx + 1) { @@ -5439,7 +5543,7 @@ void nsTableFrame::CalcBCBorders() { } // store the border segment in the cell map and update cellBorders tableCellMap->SetBCBorderEdge( - eLogicalSideBEnd, *iter.mCellMap, iter.mRowGroupStart, + LogicalSide::BEnd, *iter.mCellMap, iter.mRowGroupStart, info.GetCellEndRowIndex(), colIdx, 1, currentBorder.owner, currentBorder.width, startSeg); // update lastBEndBorders @@ -5449,12 +5553,17 @@ void nsTableFrame::CalcBCBorders() { // Set border width at block-end (table-wide and for the cell), but // only if it's the largest we've encountered. + if (!reset) { + info.ResetBEndBorderWidths(); + reset = true; + } info.SetBEndBorderWidths(currentBorder.width); - info.SetTableBEndBorderWidth(currentBorder.width); + tableInfo.SetTableBEndBorderWidth(currentBorder.width); } } else { int32_t segLength = 0; BCMapCellInfo ajaInfo(this); + bool reset = false; for (int32_t colIdx = info.mColIndex; colIdx <= info.GetCellEndColIndex(); colIdx += segLength) { // Grab the cell adjacent to our block-end. @@ -5486,12 +5595,12 @@ void nsTableFrame::CalcBCBorders() { } else if (prevRowIndex < info.GetCellEndRowIndex() + 1) { // spans below the cell to the iStart side bStartCorners[colIdx] = bEndIStartCorner; - bEndIStartCorner.Set(eLogicalSideIEnd, currentBorder); + bEndIStartCorner.Set(LogicalSide::IEnd, currentBorder); update = false; } } if (update) { - bEndIStartCorner.Update(eLogicalSideIEnd, currentBorder); + bEndIStartCorner.Update(LogicalSide::IEnd, currentBorder); } // Check that the spanned area is inside of the invalidation area if (info.GetCellEndRowIndex() < damageArea.EndRow() && @@ -5506,7 +5615,7 @@ void nsTableFrame::CalcBCBorders() { // Propagate this segment down the colspan for (int32_t c = colIdx + 1; c < colIdx + segLength; c++) { BCCornerInfo& corner = bEndCorners[c]; - corner.Set(eLogicalSideIEnd, currentBorder); + corner.Set(LogicalSide::IEnd, currentBorder); tableCellMap->SetBCBorderCorner( LogicalCorner::BEndIStart, *iter.mCellMap, iter.mRowGroupStart, info.GetCellEndRowIndex(), c, LogicalSide(corner.ownerSide), @@ -5533,15 +5642,21 @@ void nsTableFrame::CalcBCBorders() { if (info.GetCellEndRowIndex() < damageArea.EndRow() && colIdx >= damageArea.StartCol() && colIdx < damageArea.EndCol()) { tableCellMap->SetBCBorderEdge( - eLogicalSideBEnd, *iter.mCellMap, iter.mRowGroupStart, + LogicalSide::BEnd, *iter.mCellMap, iter.mRowGroupStart, info.GetCellEndRowIndex(), colIdx, segLength, currentBorder.owner, currentBorder.width, startSeg); + + if (!reset) { + info.ResetBEndBorderWidths(); + ajaInfo.ResetBStartBorderWidths(); + reset = true; + } info.SetBEndBorderWidths(currentBorder.width); ajaInfo.SetBStartBorderWidths(currentBorder.width); } // update bEnd-iEnd corner BCCornerInfo& bEndIEndCorner = bEndCorners[colIdx + segLength]; - bEndIEndCorner.Update(eLogicalSideIStart, currentBorder); + bEndIEndCorner.Update(LogicalSide::IStart, currentBorder); } } // o------o------o @@ -5570,7 +5685,7 @@ void nsTableFrame::CalcBCBorders() { // new segment if (iter.mCellMap) { tableCellMap->ResetBStartStart( - eLogicalSideBEnd, *iter.mCellMap, iter.mRowGroupStart, + LogicalSide::BEnd, *iter.mCellMap, iter.mRowGroupStart, info.GetCellEndRowIndex(), nextColIndex); } } @@ -6248,7 +6363,7 @@ static nscoord CalcVerCornerOffset(nsPresContext* aPresContext, offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; } else { offset = - (eLogicalSideBStart == aCornerOwnerSide) ? smallHalf : -largeHalf; + (LogicalSide::BStart == aCornerOwnerSide) ? smallHalf : -largeHalf; } } else { DivideBCBorderSize(aHorWidth, smallHalf, largeHalf); @@ -6283,7 +6398,7 @@ static nscoord CalcHorCornerOffset(nsPresContext* aPresContext, offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; } else { offset = - (eLogicalSideIStart == aCornerOwnerSide) ? smallHalf : -largeHalf; + (LogicalSide::IStart == aCornerOwnerSide) ? smallHalf : -largeHalf; } } else { DivideBCBorderSize(aVerWidth, smallHalf, largeHalf); @@ -6305,7 +6420,7 @@ BCBlockDirSeg::BCBlockDirSeg() mCol = nullptr; mFirstCell = mLastCell = mAjaCell = nullptr; mOffsetI = mOffsetB = mLength = mWidth = mBStartBevelOffset = 0; - mBStartBevelSide = eLogicalSideBStart; + mBStartBevelSide = LogicalSide::BStart; mOwner = eCellOwner; } @@ -6322,7 +6437,7 @@ void BCBlockDirSeg::Start(BCPaintBorderIterator& aIter, BCPixelSize aBlockSegISize, BCPixelSize aInlineSegBSize, Maybe<nscoord> aEmptyRowEndBSize) { - LogicalSide ownerSide = eLogicalSideBStart; + LogicalSide ownerSide = LogicalSide::BStart; bool bevel = false; nscoord cornerSubWidth = @@ -6339,7 +6454,7 @@ void BCBlockDirSeg::Start(BCPaintBorderIterator& aIter, bStartBevel ? presContext->DevPixelsToAppUnits(maxInlineSegBSize) : 0; // XXX this assumes that only corners where 2 segments join can be beveled mBStartBevelSide = - (aInlineSegBSize > 0) ? eLogicalSideIEnd : eLogicalSideIStart; + (aInlineSegBSize > 0) ? LogicalSide::IEnd : LogicalSide::IStart; if (aEmptyRowEndBSize && *aEmptyRowEndBSize < offset) { // This segment is starting from an empty row. This will require the the // starting segment to overlap with the previously drawn segment, unless the @@ -6390,7 +6505,7 @@ void BCBlockDirSeg::Initialize(BCPaintBorderIterator& aIter) { */ void BCBlockDirSeg::GetBEndCorner(BCPaintBorderIterator& aIter, BCPixelSize aInlineSegBSize) { - LogicalSide ownerSide = eLogicalSideBStart; + LogicalSide ownerSide = LogicalSide::BStart; nscoord cornerSubWidth = 0; bool bevel = false; if (aIter.mBCData) { @@ -6410,7 +6525,7 @@ Maybe<BCBorderParameters> BCBlockDirSeg::BuildBorderParameters( // get the border style, color and paint the segment LogicalSide side = - aIter.IsDamageAreaIEndMost() ? eLogicalSideIEnd : eLogicalSideIStart; + aIter.IsDamageAreaIEndMost() ? LogicalSide::IEnd : LogicalSide::IStart; int32_t relColIndex = aIter.GetRelativeColIndex(); nsTableColFrame* col = mCol; if (!col) ABORT1(Nothing()); @@ -6430,7 +6545,7 @@ Maybe<BCBorderParameters> BCBlockDirSeg::BuildBorderParameters( owner = aIter.mTable; break; case eAjaColGroupOwner: - side = eLogicalSideIEnd; + side = LogicalSide::IEnd; if (!aIter.IsTableIEndMost() && (relColIndex > 0)) { col = aIter.mBlockDirInfo[relColIndex - 1].mCol; } @@ -6441,7 +6556,7 @@ Maybe<BCBorderParameters> BCBlockDirSeg::BuildBorderParameters( } break; case eAjaColOwner: - side = eLogicalSideIEnd; + side = LogicalSide::IEnd; if (!aIter.IsTableIEndMost() && (relColIndex > 0)) { col = aIter.mBlockDirInfo[relColIndex - 1].mCol; } @@ -6466,7 +6581,7 @@ Maybe<BCBorderParameters> BCBlockDirSeg::BuildBorderParameters( owner = mFirstRow; break; case eAjaCellOwner: - side = eLogicalSideIEnd; + side = LogicalSide::IEnd; cell = mAjaCell; [[fallthrough]]; case eCellOwner: @@ -6487,7 +6602,7 @@ Maybe<BCBorderParameters> BCBlockDirSeg::BuildBorderParameters( (mIsBEndBevel) ? presContext->DevPixelsToAppUnits(mBEndInlineSegBSize) : 0; LogicalSide bEndBevelSide = - (aInlineSegBSize > 0) ? eLogicalSideIEnd : eLogicalSideIStart; + (aInlineSegBSize > 0) ? LogicalSide::IEnd : LogicalSide::IStart; // Convert logical to physical sides/coordinates for DrawTableBorderSegment. @@ -6733,11 +6848,11 @@ void BCBlockDirSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter) { BCInlineDirSeg::BCInlineDirSeg() : mIsIEndBevel(false), mIEndBevelOffset(0), - mIEndBevelSide(eLogicalSideBStart), + mIEndBevelSide(LogicalSide::BStart), mEndOffset(0), mOwner(eTableOwner) { mOffsetI = mOffsetB = mLength = mWidth = mIStartBevelOffset = 0; - mIStartBevelSide = eLogicalSideBStart; + mIStartBevelSide = LogicalSide::BStart; mFirstCell = mAjaCell = nullptr; } @@ -6751,7 +6866,7 @@ void BCInlineDirSeg::Start(BCPaintBorderIterator& aIter, BCBorderOwner aBorderOwner, BCPixelSize aBEndBlockSegISize, BCPixelSize aInlineSegBSize) { - LogicalSide cornerOwnerSide = eLogicalSideBStart; + LogicalSide cornerOwnerSide = LogicalSide::BStart; bool bevel = false; mOwner = aBorderOwner; @@ -6769,7 +6884,7 @@ void BCInlineDirSeg::Start(BCPaintBorderIterator& aIter, (iStartBevel && (aInlineSegBSize > 0)) ? maxBlockSegISize : 0; // XXX this assumes that only corners where 2 segments join can be beveled mIStartBevelSide = - (aBEndBlockSegISize > 0) ? eLogicalSideBEnd : eLogicalSideBStart; + (aBEndBlockSegISize > 0) ? LogicalSide::BEnd : LogicalSide::BStart; mOffsetI += offset; mLength = -offset; mWidth = aInlineSegBSize; @@ -6787,7 +6902,7 @@ void BCInlineDirSeg::Start(BCPaintBorderIterator& aIter, */ void BCInlineDirSeg::GetIEndCorner(BCPaintBorderIterator& aIter, BCPixelSize aIStartSegISize) { - LogicalSide ownerSide = eLogicalSideBStart; + LogicalSide ownerSide = LogicalSide::BStart; nscoord cornerSubWidth = 0; bool bevel = false; if (aIter.mBCData) { @@ -6805,7 +6920,7 @@ void BCInlineDirSeg::GetIEndCorner(BCPaintBorderIterator& aIter, mIEndBevelOffset = (mIsIEndBevel) ? presContext->DevPixelsToAppUnits(verWidth) : 0; mIEndBevelSide = - (aIStartSegISize > 0) ? eLogicalSideBEnd : eLogicalSideBStart; + (aIStartSegISize > 0) ? LogicalSide::BEnd : LogicalSide::BStart; } Maybe<BCBorderParameters> BCInlineDirSeg::BuildBorderParameters( @@ -6814,7 +6929,7 @@ Maybe<BCBorderParameters> BCInlineDirSeg::BuildBorderParameters( // get the border style, color and paint the segment LogicalSide side = - aIter.IsDamageAreaBEndMost() ? eLogicalSideBEnd : eLogicalSideBStart; + aIter.IsDamageAreaBEndMost() ? LogicalSide::BEnd : LogicalSide::BStart; nsIFrame* rg = aIter.mRg; if (!rg) ABORT1(Nothing()); nsIFrame* row = aIter.mRow; @@ -6855,21 +6970,21 @@ Maybe<BCBorderParameters> BCInlineDirSeg::BuildBorderParameters( owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1); break; case eAjaRowGroupOwner: - side = eLogicalSideBEnd; + side = LogicalSide::BEnd; rg = (aIter.IsTableBEndMost()) ? aIter.mRg : aIter.mPrevRg; [[fallthrough]]; case eRowGroupOwner: owner = rg; break; case eAjaRowOwner: - side = eLogicalSideBEnd; + side = LogicalSide::BEnd; row = (aIter.IsTableBEndMost()) ? aIter.mRow : aIter.mPrevRow; [[fallthrough]]; case eRowOwner: owner = row; break; case eAjaCellOwner: - side = eLogicalSideBEnd; + side = LogicalSide::BEnd; // if this is null due to the damage area origin-y > 0, then the border // won't show up anyway cell = mAjaCell; @@ -6974,14 +7089,14 @@ void BCPaintBorderIterator::StoreColumnWidth(int32_t aIndex) { * Determine if a block-dir segment owns the corner */ bool BCPaintBorderIterator::BlockDirSegmentOwnsCorner() { - LogicalSide cornerOwnerSide = eLogicalSideBStart; + LogicalSide cornerOwnerSide = LogicalSide::BStart; bool bevel = false; if (mBCData) { mBCData->GetCorner(cornerOwnerSide, bevel); } // unitialized ownerside, bevel - return (eLogicalSideBStart == cornerOwnerSide) || - (eLogicalSideBEnd == cornerOwnerSide); + return (LogicalSide::BStart == cornerOwnerSide) || + (LogicalSide::BEnd == cornerOwnerSide); } /** diff --git a/layout/tables/nsTableRowFrame.cpp b/layout/tables/nsTableRowFrame.cpp index 1190561ee8..2447ceea15 100644 --- a/layout/tables/nsTableRowFrame.cpp +++ b/layout/tables/nsTableRowFrame.cpp @@ -305,7 +305,7 @@ static nscoord GetBSizeOfRowsSpannedBelowFirst( /** * Post-reflow hook. This is where the table row does its post-processing */ -void nsTableRowFrame::DidResize() { +void nsTableRowFrame::DidResize(ForceAlignTopForTableCell aForceAlignTop) { // Resize and re-align the cell frames based on our row bsize nsTableFrame* tableFrame = GetTableFrame(); @@ -369,7 +369,7 @@ void nsTableRowFrame::DidResize() { // realign cell content based on the new bsize. We might be able to // skip this if the bsize didn't change... maybe. Hard to tell. - cellFrame->BlockDirAlignChild(wm, mMaxCellAscent); + cellFrame->BlockDirAlignChild(wm, mMaxCellAscent, aForceAlignTop); // Always store the overflow, even if the height didn't change, since // we'll lose part of our overflow area otherwise. @@ -561,10 +561,10 @@ LogicalSides nsTableRowFrame::GetLogicalSkipSides() const { } if (GetPrevInFlow()) { - skip |= eLogicalSideBitsBStart; + skip += LogicalSide::BStart; } if (GetNextInFlow()) { - skip |= eLogicalSideBitsBEnd; + skip += LogicalSide::BEnd; } return skip; } @@ -581,11 +581,8 @@ nscoord nsTableRowFrame::CalcCellActualBSize(nsTableCellFrame* aCellFrame, const auto& bsizeStyleCoord = position->BSize(aWM); if (bsizeStyleCoord.ConvertsToLength()) { - // In quirks mode, table cell isize should be content-box, but bsize - // should be border-box. - // Because of this historic anomaly, we do not use quirk.css - // (since we can't specify one value of box-sizing for isize and another - // for bsize) + // In quirks mode, table cell bsize should always be border-box. + // https://quirks.spec.whatwg.org/#the-table-cell-height-box-sizing-quirk specifiedBSize = bsizeStyleCoord.ToLength(); if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks && position->mBoxSizing == StyleBoxSizing::Content) { @@ -956,7 +953,6 @@ void nsTableRowFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); WritingMode wm = aReflowInput.GetWritingMode(); @@ -999,17 +995,14 @@ void nsTableRowFrame::Reflow(nsPresContext* aPresContext, PushDirtyBitToAbsoluteFrames(); } -/** - * This function is called by the row group frame's SplitRowGroup() code when - * pushing a row frame that has cell frames that span into it. The cell frame - * should be reflowed with the specified height - */ nscoord nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext, const ReflowInput& aReflowInput, bool aIsTopOfPage, nsTableCellFrame* aCellFrame, nscoord aAvailableBSize, nsReflowStatus& aStatus) { + MOZ_ASSERT(aPresContext->IsPaginated(), + "ReflowCellFrame currently supports only paged media!"); MOZ_ASSERT(aAvailableBSize != NS_UNCONSTRAINEDSIZE, "Why split cell frame if available bsize is unconstrained?"); WritingMode wm = aReflowInput.GetWritingMode(); @@ -1049,7 +1042,8 @@ nscoord nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext, // XXX What happens if this cell has 'vertical-align: baseline' ? // XXX Why is it assumed that the cell's ascent hasn't changed ? if (isCompleteAndNotTruncated) { - aCellFrame->BlockDirAlignChild(wm, mMaxCellAscent); + aCellFrame->BlockDirAlignChild(wm, mMaxCellAscent, + ForceAlignTopForTableCell::Yes); } nsTableFrame::InvalidateTableFrame( diff --git a/layout/tables/nsTableRowFrame.h b/layout/tables/nsTableRowFrame.h index 1d68a534c3..e24be1ea23 100644 --- a/layout/tables/nsTableRowFrame.h +++ b/layout/tables/nsTableRowFrame.h @@ -15,6 +15,11 @@ class nsTableCellFrame; namespace mozilla { class PresShell; struct TableCellReflowInput; + +// Yes if table-cells should use 'vertical-align:top' in +// nsTableCellFrame::BlockDirAlignChild(). This is a hack to workaround our +// current table row group fragmentation to avoid data loss. +enum class ForceAlignTopForTableCell : uint8_t { No, Yes }; } // namespace mozilla /** @@ -104,7 +109,8 @@ class nsTableRowFrame : public nsContainerFrame { const ReflowInput& aReflowInput, nsReflowStatus& aStatus) override; - void DidResize(); + void DidResize(mozilla::ForceAlignTopForTableCell aForceAlignTop = + mozilla::ForceAlignTopForTableCell::No); #ifdef DEBUG_FRAME_DUMP nsresult GetFrameName(nsAString& aResult) const override; @@ -145,7 +151,11 @@ class nsTableRowFrame : public nsContainerFrame { // See nsTableFrame.h void AddDeletedRowIndex(); - /** used by row group frame code */ + /** + * This function is called by the row group frame's SplitRowGroup() code when + * pushing a row frame that has cell frames that span into it. The cell frame + * should be reflowed with the specified available block-size. + */ nscoord ReflowCellFrame(nsPresContext* aPresContext, const ReflowInput& aReflowInput, bool aIsTopOfPage, nsTableCellFrame* aCellFrame, nscoord aAvailableBSize, diff --git a/layout/tables/nsTableRowGroupFrame.cpp b/layout/tables/nsTableRowGroupFrame.cpp index f8dac61386..45cca8c8fa 100644 --- a/layout/tables/nsTableRowGroupFrame.cpp +++ b/layout/tables/nsTableRowGroupFrame.cpp @@ -254,10 +254,10 @@ LogicalSides nsTableRowGroupFrame::GetLogicalSkipSides() const { } if (GetPrevInFlow()) { - skip |= eLogicalSideBitsBStart; + skip += LogicalSide::BStart; } if (GetNextInFlow()) { - skip |= eLogicalSideBitsBEnd; + skip += LogicalSide::BEnd; } return skip; } @@ -1122,7 +1122,7 @@ void nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext, FinishReflowChild(rowFrame, aPresContext, rowMetrics, &rowReflowInput, wm, dummyPos, dummyContainerSize, ReflowChildFlags::NoMoveFrame); - rowFrame->DidResize(); + rowFrame->DidResize(ForceAlignTopForTableCell::Yes); if (!aRowForcedPageBreak && !aStatus.IsFullyComplete() && ShouldAvoidBreakInside(aReflowInput)) { @@ -1324,7 +1324,6 @@ void nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableRowGroupFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); // Row geometry may be going to change so we need to invalidate any row diff --git a/layout/tables/nsTableWrapperFrame.cpp b/layout/tables/nsTableWrapperFrame.cpp index a3e957c4fd..aa898ddf6f 100644 --- a/layout/tables/nsTableWrapperFrame.cpp +++ b/layout/tables/nsTableWrapperFrame.cpp @@ -242,7 +242,6 @@ ComputedStyle* nsTableWrapperFrame::GetParentComputedStyle( nscoord nsTableWrapperFrame::GetMinISize(gfxContext* aRenderingContext) { nscoord iSize = nsLayoutUtils::IntrinsicForContainer( aRenderingContext, InnerTableFrame(), IntrinsicISizeType::MinISize); - DISPLAY_MIN_INLINE_SIZE(this, iSize); if (mCaptionFrames.NotEmpty()) { nscoord capISize = nsLayoutUtils::IntrinsicForContainer( aRenderingContext, mCaptionFrames.FirstChild(), @@ -256,10 +255,7 @@ nscoord nsTableWrapperFrame::GetMinISize(gfxContext* aRenderingContext) { /* virtual */ nscoord nsTableWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) { - nscoord maxISize; - DISPLAY_PREF_INLINE_SIZE(this, maxISize); - - maxISize = nsLayoutUtils::IntrinsicForContainer( + nscoord maxISize = nsLayoutUtils::IntrinsicForContainer( aRenderingContext, InnerTableFrame(), IntrinsicISizeType::PrefISize); if (mCaptionFrames.NotEmpty()) { // Don't let the caption's pref isize expand the table's pref isize. @@ -547,7 +543,7 @@ ComputeSizeFlags nsTableWrapperFrame::CreateComputeSizeFlagsForChild() const { // Shrink-wrap child frames by default, except if we're a stretched grid item. if (MOZ_UNLIKELY(IsGridItem())) { auto* gridContainer = static_cast<nsGridContainerFrame*>(GetParent()); - if (gridContainer->GridItemShouldStretch(this, eLogicalAxisInline)) { + if (gridContainer->GridItemShouldStretch(this, LogicalAxis::Inline)) { return {}; } } @@ -691,7 +687,6 @@ void nsTableWrapperFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsTableWrapperFrame"); - DISPLAY_REFLOW(aPresContext, this, aOuterRI, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); // Initialize out parameters diff --git a/layout/tools/reftest/jar.mn b/layout/tools/reftest/jar.mn index b879ac1646..4fc1b7e10f 100644 --- a/layout/tools/reftest/jar.mn +++ b/layout/tools/reftest/jar.mn @@ -25,7 +25,6 @@ reftest.jar: content/xul (../../reftests/xul/*) content/xul/reftest (../../xul/reftest/*) content/toolkit/reftests (../../../toolkit/content/tests/reftests/*) - content/osx-theme (../../../toolkit/themes/osx/reftests/*) content/reftest.xhtml (reftest.xhtml) # Crash tests diff --git a/layout/tools/reftest/mach_commands.py b/layout/tools/reftest/mach_commands.py index 8cb1164332..dd4c8ea63a 100644 --- a/layout/tools/reftest/mach_commands.py +++ b/layout/tools/reftest/mach_commands.py @@ -160,10 +160,6 @@ class ReftestRunner(MozbuildObject): args.e10s = False print("using e10s=False for non-geckoview app") - # Disable fission until geckoview supports fission by default. - # Need fission on Android? Use '--setpref fission.autostart=true' - args.disableFission = True - # A symlink and some path manipulations are required so that test # manifests can be found both locally and remotely (via a url) # using the same relative path. diff --git a/layout/tools/reftest/manifest.sys.mjs b/layout/tools/reftest/manifest.sys.mjs index 4be0afde57..09ccefd9f7 100644 --- a/layout/tools/reftest/manifest.sys.mjs +++ b/layout/tools/reftest/manifest.sys.mjs @@ -155,6 +155,7 @@ function ReadManifest(aURL, aFilter, aManifestID) { var origLength = items.length; items = defaults.concat(items); + var modifiers = [...items]; while ( items[0].match( /^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|test-pref|ref-pref|fuzzy|chaos-mode|wr-capture|wr-capture-ref|noautofuzz)/ @@ -492,6 +493,7 @@ function ReadManifest(aURL, aFilter, aManifestID) { chaosMode, wrCapture, noAutoFuzz, + modifiers, }, aFilter, aManifestID @@ -572,6 +574,7 @@ function ReadManifest(aURL, aFilter, aManifestID) { chaosMode, wrCapture, noAutoFuzz, + modifiers, }, aFilter, aManifestID @@ -612,22 +615,7 @@ function BuildConditionSandbox(aURL) { sandbox.isDebugBuild = g.debug.isDebugBuild; sandbox.isCoverageBuild = g.isCoverageBuild; - sandbox.xulRuntime = Cu.cloneInto( - { - widgetToolkit: Services.appinfo.widgetToolkit, - OS: Services.appinfo.OS, - XPCOMABI: Services.appinfo.XPCOMABI, - }, - sandbox - ); - - sandbox.smallScreen = false; - if ( - g.containingWindow.innerWidth < 800 || - g.containingWindow.innerHeight < 1000 - ) { - sandbox.smallScreen = true; - } + sandbox.xulRuntime = {}; var gfxInfo = NS_GFXINFO_CONTRACTID in Cc && @@ -640,57 +628,24 @@ function BuildConditionSandbox(aURL) { }; try { - sandbox.d2d = readGfxInfo(gfxInfo, "D2DEnabled"); - sandbox.dwrite = readGfxInfo(gfxInfo, "DWriteEnabled"); - sandbox.embeddedInFirefoxReality = readGfxInfo( - gfxInfo, - "EmbeddedInFirefoxReality" - ); - } catch (e) { - sandbox.d2d = false; - sandbox.dwrite = false; - sandbox.embeddedInFirefoxReality = false; - } - - var canvasBackend = readGfxInfo(gfxInfo, "AzureCanvasBackend"); - var contentBackend = readGfxInfo(gfxInfo, "AzureContentBackend"); - - sandbox.gpuProcess = gfxInfo.usingGPUProcess; - sandbox.azureCairo = canvasBackend == "cairo"; - sandbox.azureSkia = canvasBackend == "skia"; - sandbox.skiaContent = contentBackend == "skia"; - sandbox.azureSkiaGL = false; - // true if we are using the same Azure backend for rendering canvas and content - sandbox.contentSameGfxBackendAsCanvas = - contentBackend == canvasBackend || - (contentBackend == "none" && canvasBackend == "cairo"); - - try { var windowProtocol = readGfxInfo(gfxInfo, "windowProtocol"); sandbox.wayland = windowProtocol == "wayland"; } catch (e) { sandbox.wayland = false; } - sandbox.remoteCanvas = - Services.prefs.getBoolPref("gfx.canvas.remote") && - sandbox.d2d && - sandbox.gpuProcess; + sandbox.mozinfo = Services.prefs.getStringPref("sandbox.mozinfo", null); + sandbox.os_version = sandbox.mozinfo.os_version; - sandbox.layersGPUAccelerated = g.windowUtils.layerManagerType != "Basic"; sandbox.d3d11 = g.windowUtils.layerManagerType == "Direct3D 11"; - sandbox.d3d9 = g.windowUtils.layerManagerType == "Direct3D 9"; - sandbox.layersOpenGL = g.windowUtils.layerManagerType == "OpenGL"; sandbox.swgl = g.windowUtils.layerManagerType.startsWith( "WebRender (Software" ); - sandbox.layersOMTC = !!g.windowUtils.layerManagerRemote; // Shortcuts for widget toolkits. sandbox.Android = Services.appinfo.OS == "Android"; sandbox.cocoaWidget = Services.appinfo.widgetToolkit == "cocoa"; sandbox.gtkWidget = Services.appinfo.widgetToolkit == "gtk"; - sandbox.qtWidget = Services.appinfo.widgetToolkit == "qt"; sandbox.winWidget = Services.appinfo.widgetToolkit == "windows"; sandbox.is64Bit = Services.appinfo.is64Bit; @@ -698,27 +653,11 @@ function BuildConditionSandbox(aURL) { // Use this to annotate reftests that fail in drawSnapshot, but // the reason hasn't been investigated (or fixed) yet. sandbox.useDrawSnapshot = g.useDrawSnapshot; - // Use this to annotate reftests that use functionality - // that isn't available to drawSnapshot (like any sort of - // compositor feature such as async scrolling). - sandbox.unsupportedWithDrawSnapshot = g.useDrawSnapshot; - - sandbox.retainedDisplayList = - Services.prefs.getBoolPref("layout.display-list.retain") && - !sandbox.useDrawSnapshot; - - // Needed to specifically test the new and old behavior. This will eventually be removed. - sandbox.retainedDisplayListNew = - sandbox.retainedDisplayList && - Services.prefs.getBoolPref("layout.display-list.retain.sc"); // GeckoView is currently uniquely identified by "android + e10s" but // we might want to make this condition more precise in the future. sandbox.geckoview = sandbox.Android && g.browserIsRemote; - // Scrollbars that are semi-transparent. See bug 1169666. - sandbox.transparentScrollbars = Services.appinfo.widgetToolkit == "gtk"; - if (sandbox.Android) { sandbox.AndroidVersion = Services.sysinfo.getPropertyAsInt32("version"); @@ -731,35 +670,15 @@ function BuildConditionSandbox(aURL) { // Some reftests need extra fuzz on the Android 13 Pixel 5 devices. sandbox.Android13 = sandbox.AndroidVersion == "33"; - sandbox.MinGW = - sandbox.winWidget && Services.sysinfo.getPropertyAsBool("isMinGW"); - sandbox.AddressSanitizer = AppConstants.ASAN; sandbox.ThreadSanitizer = AppConstants.TSAN; sandbox.webrtc = AppConstants.MOZ_WEBRTC; - sandbox.jxl = AppConstants.MOZ_JXL; - - sandbox.compareRetainedDisplayLists = g.compareRetainedDisplayLists; sandbox.release_or_beta = AppConstants.RELEASE_OR_BETA; var hh = Cc[NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX + "http"].getService( Ci.nsIHttpProtocolHandler ); - var httpProps = [ - "userAgent", - "appName", - "appVersion", - "vendor", - "vendorSub", - "product", - "productSub", - "oscpu", - "language", - "misc", - ]; - sandbox.http = new sandbox.Object(); - httpProps.forEach(x => (sandbox.http[x] = hh[x])); // Set OSX to be the Mac OS X version, as an integer, or undefined // for other platforms. The integer is formed by 100 times the @@ -776,11 +695,6 @@ function BuildConditionSandbox(aURL) { false ); - sandbox.gpuProcessForceEnabled = Services.prefs.getBoolPref( - "layers.gpu-process.force-enabled", - false - ); - sandbox.prefs = Cu.cloneInto( { getBoolPref(p) { @@ -794,28 +708,11 @@ function BuildConditionSandbox(aURL) { { cloneFunctions: true } ); - // Tests shouldn't care about this except for when they need to - // crash the content process - sandbox.browserIsRemote = g.browserIsRemote; - sandbox.browserIsFission = g.browserIsFission; - - try { - sandbox.asyncPan = - g.containingWindow.docShell.asyncPanZoomEnabled && - !sandbox.useDrawSnapshot; - } catch (e) { - sandbox.asyncPan = false; - } - - // Graphics features - sandbox.usesRepeatResampling = sandbox.d2d; - // Running in a test-verify session? sandbox.verify = Services.prefs.getBoolPref("reftest.verify", false); // Running with a variant enabled? sandbox.fission = Services.appinfo.fissionAutostart; - sandbox.serviceWorkerE10s = true; if (!g.dumpedConditionSandbox) { g.logger.info( diff --git a/layout/tools/reftest/reftest.sys.mjs b/layout/tools/reftest/reftest.sys.mjs index 1040470967..12bdab98f9 100644 --- a/layout/tools/reftest/reftest.sys.mjs +++ b/layout/tools/reftest/reftest.sys.mjs @@ -1423,7 +1423,9 @@ function RecordResult(testRunTime, errorMsg, typeSpecificResults) { // branch, 'equal' must be false so let's assert that to guard // against logic errors. if (equal) { - throw new Error("Logic error in reftest.jsm fuzzy test handling!"); + throw new Error( + "Logic error in reftest.sys.mjs fuzzy test handling!" + ); } output = { s: ["PASS", "FAIL"], n: "UnexpectedPass" }; } else { @@ -1510,6 +1512,8 @@ function RecordResult(testRunTime, errorMsg, typeSpecificResults) { extra.image1 = image1; } } + extra.modifiers = g.urls[0].modifiers; + logger.testStatus( g.urls[0].identifier, message, diff --git a/layout/tools/reftest/reftestcommandline.py b/layout/tools/reftest/reftestcommandline.py index e2f0baff8c..13154b2456 100644 --- a/layout/tools/reftest/reftestcommandline.py +++ b/layout/tools/reftest/reftestcommandline.py @@ -478,11 +478,6 @@ class DesktopArgumentsParser(ReftestArgumentsParser): help="run tests in parallel if possible", ) - def _prefs_gpu(self): - if mozinfo.os != "win": - return ["layers.acceleration.force-enabled=true"] - return [] - def validate(self, options, reftest): super(DesktopArgumentsParser, self).validate(options, reftest) diff --git a/layout/tools/reftest/runreftest.py b/layout/tools/reftest/runreftest.py index cf314c99b5..cac1f7dd3c 100644 --- a/layout/tools/reftest/runreftest.py +++ b/layout/tools/reftest/runreftest.py @@ -302,6 +302,7 @@ class RefTest(object): self.outputHandler = None self.testDumpFile = os.path.join(tempfile.gettempdir(), "reftests.json") self.currentManifest = "No test started" + self.gtkTheme = self.getGtkTheme() self.run_by_manifest = True if suite in ("crashtest", "jstestbrowser"): @@ -328,6 +329,17 @@ class RefTest(object): "reftest harness", options, {"tbpl": sys.stdout}, fmt_options ) + def getGtkTheme(self): + if not platform.system() == "Linux": + return "" + + theme_cmd = "gsettings get org.gnome.desktop.interface gtk-theme" + theme = subprocess.check_output(theme_cmd, shell=True, universal_newlines=True) + if theme: + theme = theme.strip("\n") + theme = theme.strip("'") + return theme.strip() + def getFullPath(self, path): "Get an absolute path relative to self.oldcwd." return os.path.normpath(os.path.join(self.oldcwd, os.path.expanduser(path))) @@ -357,14 +369,14 @@ class RefTest(object): locations.add_host(server, scheme="http", port=port) locations.add_host(server, scheme="https", port=port) - sandbox_whitelist_paths = options.sandboxReadWhitelist + sandbox_allowlist_paths = options.sandboxReadWhitelist if platform.system() == "Linux" or platform.system() in ( "Windows", "Microsoft", ): # Trailing slashes are needed to indicate directories on Linux and Windows - sandbox_whitelist_paths = map( - lambda p: os.path.join(p, ""), sandbox_whitelist_paths + sandbox_allowlist_paths = map( + lambda p: os.path.join(p, ""), sandbox_allowlist_paths ) addons = [] @@ -390,7 +402,7 @@ class RefTest(object): kwargs = { "addons": addons, "locations": locations, - "whitelistpaths": sandbox_whitelist_paths, + "allowlistpaths": sandbox_allowlist_paths, } if profile_to_clone: profile = mozprofile.Profile.clone(profile_to_clone, **kwargs) @@ -462,6 +474,9 @@ class RefTest(object): # config specific flags prefs["sandbox.apple_silicon"] = mozinfo.info.get("apple_silicon", False) + prefs["sandbox.mozinfo"] = json.dumps(mozinfo.info) + prefs["sandbox.os_version"] = mozinfo.info.get("os_version", "") + # Set tests to run or manifests to parse. if tests: testlist = os.path.join(profile.profile, "reftests.json") @@ -482,22 +497,6 @@ class RefTest(object): if options.thisChunk: prefs["reftest.thisChunk"] = options.thisChunk - # Bug 1262954: For winXP + e10s disable acceleration - if ( - platform.system() in ("Windows", "Microsoft") - and "5.1" in platform.version() - and options.e10s - ): - prefs["layers.acceleration.disabled"] = True - - # Bug 1300355: Disable canvas cache for win7 as it uses - # too much memory and causes OOMs. - if ( - platform.system() in ("Windows", "Microsoft") - and "6.1" in platform.version() - ): - prefs["reftest.nocache"] = True - if options.marionette: # options.marionette can specify host:port port = options.marionette.split(":")[1] @@ -552,6 +551,9 @@ class RefTest(object): browserEnv["TZ"] = "PST8PDT" browserEnv["LC_ALL"] = "en_US.UTF-8" + # This should help with consistency + browserEnv["GTK_THEME"] = "Adwaita" + for v in options.environment: ix = v.find("=") if ix <= 0: @@ -1113,6 +1115,13 @@ class RefTest(object): overall = 0 status = -1 for manifest, tests in tests_by_manifest.items(): + if self.getGtkTheme() != self.gtkTheme: + self.log.error( + "Theme (%s) has changed to (%s), terminating job as this is unstable" + % (self.gtkTheme, self.getGtkTheme()) + ) + return 1 + self.log.info("Running tests in {}".format(manifest)) self.currentManifest = manifest status = run(tests=tests) diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp index 8ebb8b01d5..17993c8466 100644 --- a/layout/xul/nsMenuPopupFrame.cpp +++ b/layout/xul/nsMenuPopupFrame.cpp @@ -491,19 +491,13 @@ void nsMenuPopupFrame::TweakMinPrefISize(nscoord& aSize) { } nscoord nsMenuPopupFrame::GetMinISize(gfxContext* aRC) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - - result = nsBlockFrame::GetMinISize(aRC); + nscoord result = nsBlockFrame::GetMinISize(aRC); TweakMinPrefISize(result); return result; } nscoord nsMenuPopupFrame::GetPrefISize(gfxContext* aRC) { - nscoord result; - DISPLAY_PREF_INLINE_SIZE(this, result); - - result = nsBlockFrame::GetPrefISize(aRC); + nscoord result = nsBlockFrame::GetPrefISize(aRC); TweakMinPrefISize(result); return result; } @@ -514,7 +508,6 @@ void nsMenuPopupFrame::Reflow(nsPresContext* aPresContext, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsMenuPopupFrame"); - DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); const auto wm = GetWritingMode(); @@ -2186,6 +2179,12 @@ nsMargin nsMenuPopupFrame::GetMargin() const { margin.left += auOffset.x; margin.right += auOffset.x; } + if (mPopupType == PopupType::Tooltip && !IsAnchored()) { + const auto auOffset = + CSSPixel::ToAppUnits(LookAndFeel::TooltipOffsetVertical()); + margin.top += auOffset; + margin.bottom += auOffset; + } return margin; } @@ -2246,7 +2245,8 @@ void nsMenuPopupFrame::MoveTo(const CSSPoint& aPos, bool aUpdateAttrs, void nsMenuPopupFrame::MoveToAnchor(nsIContent* aAnchorContent, const nsAString& aPosition, int32_t aXPos, int32_t aYPos, bool aAttributesOverride) { - NS_ASSERTION(IsVisible(), "popup must be visible to move it"); + NS_ASSERTION(IsVisibleOrShowing(), + "popup must be visible or showing to move it"); nsPopupState oldstate = mPopupState; InitializePopup(aAnchorContent, mTriggerContent, aPosition, aXPos, aYPos, diff --git a/layout/xul/nsXULPopupManager.cpp b/layout/xul/nsXULPopupManager.cpp index 332d60d363..a6dbeadca7 100644 --- a/layout/xul/nsXULPopupManager.cpp +++ b/layout/xul/nsXULPopupManager.cpp @@ -49,6 +49,7 @@ #include "mozilla/dom/XULMenuElement.h" #include "mozilla/dom/XULMenuBarElement.h" #include "mozilla/dom/XULPopupElement.h" +#include "mozilla/AutoRestore.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventStateManager.h" #include "mozilla/LookAndFeel.h" diff --git a/layout/xul/tree/crashtests/crashtests.list b/layout/xul/tree/crashtests/crashtests.list index 0b79916cd2..1644c61bf2 100644 --- a/layout/xul/tree/crashtests/crashtests.list +++ b/layout/xul/tree/crashtests/crashtests.list @@ -13,5 +13,5 @@ load 399715-1.xhtml load chrome://reftest/content/crashtests/layout/xul/tree/crashtests/414170-1.xhtml load 479931-1.xhtml load 585815.html -skip-if(wayland) pref(widget.windows.window_occlusion_tracking.enabled,false) load 601427.html # Bug 1819154, wayland: Bug 1857258 +load 601427.html load chrome://reftest/content/crashtests/layout/xul/tree/crashtests/730441-3.xhtml diff --git a/layout/xul/tree/nsTreeBodyFrame.cpp b/layout/xul/tree/nsTreeBodyFrame.cpp index 5ccc1468cc..a7eba30f8b 100644 --- a/layout/xul/tree/nsTreeBodyFrame.cpp +++ b/layout/xul/tree/nsTreeBodyFrame.cpp @@ -47,13 +47,11 @@ #include "nsContainerFrame.h" #include "nsView.h" #include "nsViewManager.h" -#include "nsVariant.h" #include "nsWidgetsCID.h" #include "nsIFrameInlines.h" #include "nsTreeContentView.h" #include "nsTreeUtils.h" #include "nsStyleConsts.h" -#include "nsITheme.h" #include "imgIRequest.h" #include "imgIContainer.h" #include "mozilla/dom/NodeInfo.h" @@ -1711,115 +1709,133 @@ void nsTreeBodyFrame::PrefillPropertyArray(int32_t aRowIndex, mScratchArray.Clear(); // focus - if (mFocused) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::focus); - else - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::blur); + if (mFocused) { + mScratchArray.AppendElement(nsGkAtoms::focus); + } else { + mScratchArray.AppendElement(nsGkAtoms::blur); + } // sort bool sorted = false; mView->IsSorted(&sorted); - if (sorted) mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::sorted); + if (sorted) { + mScratchArray.AppendElement(nsGkAtoms::sorted); + } // drag session - if (mSlots && mSlots->mIsDragging) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::dragSession); + if (mSlots && mSlots->mIsDragging) { + mScratchArray.AppendElement(nsGkAtoms::dragSession); + } if (aRowIndex != -1) { - if (aRowIndex == mMouseOverRow) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::hover); + if (aRowIndex == mMouseOverRow) { + mScratchArray.AppendElement(nsGkAtoms::hover); + } nsCOMPtr<nsITreeSelection> selection = GetSelection(); if (selection) { // selected bool isSelected; selection->IsSelected(aRowIndex, &isSelected); - if (isSelected) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::selected); + if (isSelected) { + mScratchArray.AppendElement(nsGkAtoms::selected); + } // current int32_t currentIndex; selection->GetCurrentIndex(¤tIndex); - if (aRowIndex == currentIndex) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::current); + if (aRowIndex == currentIndex) { + mScratchArray.AppendElement(nsGkAtoms::current); + } } // container or leaf bool isContainer = false; mView->IsContainer(aRowIndex, &isContainer); if (isContainer) { - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::container); + mScratchArray.AppendElement(nsGkAtoms::container); // open or closed bool isOpen = false; mView->IsContainerOpen(aRowIndex, &isOpen); - if (isOpen) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::open); - else - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::closed); + if (isOpen) { + mScratchArray.AppendElement(nsGkAtoms::open); + } else { + mScratchArray.AppendElement(nsGkAtoms::closed); + } } else { - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::leaf); + mScratchArray.AppendElement(nsGkAtoms::leaf); } // drop orientation if (mSlots && mSlots->mDropAllowed && mSlots->mDropRow == aRowIndex) { - if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::dropBefore); - else if (mSlots->mDropOrient == nsITreeView::DROP_ON) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::dropOn); - else if (mSlots->mDropOrient == nsITreeView::DROP_AFTER) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::dropAfter); + if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) { + mScratchArray.AppendElement(nsGkAtoms::dropBefore); + } else if (mSlots->mDropOrient == nsITreeView::DROP_ON) { + mScratchArray.AppendElement(nsGkAtoms::dropOn); + } else if (mSlots->mDropOrient == nsITreeView::DROP_AFTER) { + mScratchArray.AppendElement(nsGkAtoms::dropAfter); + } } // odd or even - if (aRowIndex % 2) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::odd); - else - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::even); + if (aRowIndex % 2) { + mScratchArray.AppendElement(nsGkAtoms::odd); + } else { + mScratchArray.AppendElement(nsGkAtoms::even); + } XULTreeElement* tree = GetBaseElement(); if (tree && tree->HasAttr(nsGkAtoms::editing)) { - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::editing); + mScratchArray.AppendElement(nsGkAtoms::editing); } // multiple columns - if (mColumns->GetColumnAt(1)) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::multicol); + if (mColumns->GetColumnAt(1)) { + mScratchArray.AppendElement(nsGkAtoms::multicol); + } } if (aCol) { mScratchArray.AppendElement(aCol->GetAtom()); - if (aCol->IsPrimary()) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::primary); + if (aCol->IsPrimary()) { + mScratchArray.AppendElement(nsGkAtoms::primary); + } if (aCol->GetType() == TreeColumn_Binding::TYPE_CHECKBOX) { - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::checkbox); + mScratchArray.AppendElement(nsGkAtoms::checkbox); if (aRowIndex != -1) { nsAutoString value; mView->GetCellValue(aRowIndex, aCol, value); - if (value.EqualsLiteral("true")) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::checked); + if (value.EqualsLiteral("true")) { + mScratchArray.AppendElement(nsGkAtoms::checked); + } } } + if (aCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ordinal, + u"1"_ns, eIgnoreCase)) { + mScratchArray.AppendElement(nsGkAtoms::firstColumn); + } + // Read special properties from attributes on the column content node if (aCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::insertbefore, - nsGkAtoms::_true, eCaseMatters)) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::insertbefore); + nsGkAtoms::_true, eCaseMatters)) { + mScratchArray.AppendElement(nsGkAtoms::insertbefore); + } if (aCol->mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::insertafter, - nsGkAtoms::_true, eCaseMatters)) - mScratchArray.AppendElement((nsStaticAtom*)nsGkAtoms::insertafter); + nsGkAtoms::_true, eCaseMatters)) { + mScratchArray.AppendElement(nsGkAtoms::insertafter); + } } } -nsITheme* nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex, - nsTreeColumn* aColumn, - nsRect& aImageRect, - nsRect& aTwistyRect, - nsPresContext* aPresContext, - ComputedStyle* aTwistyContext) { +void nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex, nsTreeColumn* aColumn, + nsRect& aImageRect, nsRect& aTwistyRect, + nsPresContext* aPresContext, + ComputedStyle* aTwistyContext) { // The twisty rect extends all the way to the end of the cell. This is // incorrect. We need to determine the twisty rect's true width. This is // done by examining the ComputedStyle for a width first. If it has one, we @@ -1828,40 +1844,14 @@ nsITheme* nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex, // a -moz-appearance involved, adjust the rect by the minimum widget size // provided by the theme implementation. aImageRect = GetImageSize(aRowIndex, aColumn, true, aTwistyContext); - if (aImageRect.height > aTwistyRect.height) + if (aImageRect.height > aTwistyRect.height) { aImageRect.height = aTwistyRect.height; - if (aImageRect.width > aTwistyRect.width) + } + if (aImageRect.width > aTwistyRect.width) { aImageRect.width = aTwistyRect.width; - else + } else { aTwistyRect.width = aImageRect.width; - - bool useTheme = false; - nsITheme* theme = nullptr; - StyleAppearance appearance = - aTwistyContext->StyleDisplay()->EffectiveAppearance(); - if (appearance != StyleAppearance::None) { - theme = aPresContext->Theme(); - if (theme->ThemeSupportsWidget(aPresContext, nullptr, appearance)) - useTheme = true; - } - - if (useTheme) { - LayoutDeviceIntSize minTwistySizePx = - theme->GetMinimumWidgetSize(aPresContext, this, appearance); - - // GMWS() returns size in pixels, we need to convert it back to app units - nsSize minTwistySize; - minTwistySize.width = - aPresContext->DevPixelsToAppUnits(minTwistySizePx.width); - minTwistySize.height = - aPresContext->DevPixelsToAppUnits(minTwistySizePx.height); - - if (aTwistyRect.width < minTwistySize.width) { - aTwistyRect.width = minTwistySize.width; - } } - - return useTheme ? theme : nullptr; } nsresult nsTreeBodyFrame::GetImage(int32_t aRowIndex, nsTreeColumn* aCol, @@ -2693,21 +2683,8 @@ ImgDrawResult nsTreeBodyFrame::PaintRow(int32_t aRowIndex, ImgDrawResult result = ImgDrawResult::SUCCESS; // Paint our borders and background for our row rect. - nsITheme* theme = nullptr; - auto appearance = rowContext->StyleDisplay()->EffectiveAppearance(); - if (appearance != StyleAppearance::None) { - theme = aPresContext->Theme(); - } - - if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, appearance)) { - nsRect dirty; - dirty.IntersectRect(rowRect, aDirtyRect); - theme->DrawWidgetBackground(&aRenderingContext, this, appearance, rowRect, - dirty); - } else { - result &= PaintBackgroundLayer(rowContext, aPresContext, aRenderingContext, - rowRect, aDirtyRect); - } + result &= PaintBackgroundLayer(rowContext, aPresContext, aRenderingContext, + rowRect, aDirtyRect); // Adjust the rect for its border and padding. nsRect originalRowRect = rowRect; @@ -2820,54 +2797,32 @@ ImgDrawResult nsTreeBodyFrame::PaintSeparator(int32_t aRowIndex, // Resolve style for the separator. ComputedStyle* separatorContext = GetPseudoComputedStyle(nsCSSAnonBoxes::mozTreeSeparator()); - bool useTheme = false; - nsITheme* theme = nullptr; - StyleAppearance appearance = - separatorContext->StyleDisplay()->EffectiveAppearance(); - if (appearance != StyleAppearance::None) { - theme = aPresContext->Theme(); - if (theme->ThemeSupportsWidget(aPresContext, nullptr, appearance)) - useTheme = true; - } - ImgDrawResult result = ImgDrawResult::SUCCESS; + const nsStylePosition* stylePosition = separatorContext->StylePosition(); - // use -moz-appearance if provided. - if (useTheme) { - nsRect dirty; - dirty.IntersectRect(aSeparatorRect, aDirtyRect); - theme->DrawWidgetBackground(&aRenderingContext, this, appearance, - aSeparatorRect, dirty); + // Obtain the height for the separator or use the default value. + nscoord height; + if (stylePosition->mHeight.ConvertsToLength()) { + height = stylePosition->mHeight.ToLength(); } else { - const nsStylePosition* stylePosition = separatorContext->StylePosition(); - - // Obtain the height for the separator or use the default value. - nscoord height; - if (stylePosition->mHeight.ConvertsToLength()) { - height = stylePosition->mHeight.ToLength(); - } else { - // Use default height 2px. - height = nsPresContext::CSSPixelsToAppUnits(2); - } - - // Obtain the margins for the separator and then deflate our rect by that - // amount. The separator is assumed to be contained within the deflated - // rect. - nsRect separatorRect(aSeparatorRect.x, aSeparatorRect.y, - aSeparatorRect.width, height); - nsMargin separatorMargin; - separatorContext->StyleMargin()->GetMargin(separatorMargin); - separatorRect.Deflate(separatorMargin); + // Use default height 2px. + height = nsPresContext::CSSPixelsToAppUnits(2); + } - // Center the separator. - separatorRect.y += (aSeparatorRect.height - height) / 2; + // Obtain the margins for the separator and then deflate our rect by that + // amount. The separator is assumed to be contained within the deflated + // rect. + nsRect separatorRect(aSeparatorRect.x, aSeparatorRect.y, aSeparatorRect.width, + height); + nsMargin separatorMargin; + separatorContext->StyleMargin()->GetMargin(separatorMargin); + separatorRect.Deflate(separatorMargin); - result &= - PaintBackgroundLayer(separatorContext, aPresContext, aRenderingContext, - separatorRect, aDirtyRect); - } + // Center the separator. + separatorRect.y += (aSeparatorRect.height - height) / 2; - return result; + return PaintBackgroundLayer(separatorContext, aPresContext, aRenderingContext, + separatorRect, aDirtyRect); } ImgDrawResult nsTreeBodyFrame::PaintCell( @@ -3084,68 +3039,57 @@ ImgDrawResult nsTreeBodyFrame::PaintTwisty( twistyRect.Deflate(twistyMargin); nsRect imageSize; - nsITheme* theme = GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, - aPresContext, twistyContext); + GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, aPresContext, + twistyContext); // Subtract out the remaining width. This is done even when we don't actually // paint a twisty in this cell, so that cells in different rows still line up. nsRect copyRect(twistyRect); copyRect.Inflate(twistyMargin); aRemainingWidth -= copyRect.width; - if (!isRTL) aCurrX += copyRect.width; + if (!isRTL) { + aCurrX += copyRect.width; + } - ImgDrawResult result = ImgDrawResult::SUCCESS; + auto result = ImgDrawResult::SUCCESS; + if (!shouldPaint) { + return result; + } + // Paint our borders and background for our image rect. + result &= PaintBackgroundLayer(twistyContext, aPresContext, aRenderingContext, + twistyRect, aDirtyRect); - if (shouldPaint) { - // Paint our borders and background for our image rect. - result &= PaintBackgroundLayer(twistyContext, aPresContext, - aRenderingContext, twistyRect, aDirtyRect); - - if (theme) { - if (isRTL) twistyRect.x = rightEdge - twistyRect.width; - // yeah, I know it says we're drawing a background, but a twisty is really - // a fg object since it doesn't have anything that gecko would want to - // draw over it. Besides, we have to prevent imagelib from drawing it. - nsRect dirty; - dirty.IntersectRect(twistyRect, aDirtyRect); - theme->DrawWidgetBackground( - &aRenderingContext, this, - twistyContext->StyleDisplay()->EffectiveAppearance(), twistyRect, - dirty); - } else { - // Time to paint the twisty. - // Adjust the rect for its border and padding. - nsMargin bp(0, 0, 0, 0); - GetBorderPadding(twistyContext, bp); - twistyRect.Deflate(bp); - if (isRTL) twistyRect.x = rightEdge - twistyRect.width; - imageSize.Deflate(bp); - - // Get the image for drawing. - nsCOMPtr<imgIContainer> image; - GetImage(aRowIndex, aColumn, true, twistyContext, getter_AddRefs(image)); - if (image) { - nsPoint anchorPoint = twistyRect.TopLeft(); - - // Center the image. XXX Obey vertical-align style prop? - if (imageSize.height < twistyRect.height) { - anchorPoint.y += (twistyRect.height - imageSize.height) / 2; - } + // Time to paint the twisty. + // Adjust the rect for its border and padding. + nsMargin bp; + GetBorderPadding(twistyContext, bp); + twistyRect.Deflate(bp); + if (isRTL) twistyRect.x = rightEdge - twistyRect.width; + imageSize.Deflate(bp); - // Apply context paint if applicable - SVGImageContext svgContext; - SVGImageContext::MaybeStoreContextPaint(svgContext, *aPresContext, - *twistyContext, image); + // Get the image for drawing. + nsCOMPtr<imgIContainer> image; + GetImage(aRowIndex, aColumn, true, twistyContext, getter_AddRefs(image)); + if (!image) { + return result; + } + nsPoint anchorPoint = twistyRect.TopLeft(); - // Paint the image. - result &= nsLayoutUtils::DrawSingleUnscaledImage( - aRenderingContext, aPresContext, image, SamplingFilter::POINT, - anchorPoint, &aDirtyRect, svgContext, imgIContainer::FLAG_NONE, - &imageSize); - } - } + // Center the image. XXX Obey vertical-align style prop? + if (imageSize.height < twistyRect.height) { + anchorPoint.y += (twistyRect.height - imageSize.height) / 2; } + // Apply context paint if applicable + SVGImageContext svgContext; + SVGImageContext::MaybeStoreContextPaint(svgContext, *aPresContext, + *twistyContext, image); + + // Paint the image. + result &= nsLayoutUtils::DrawSingleUnscaledImage( + aRenderingContext, aPresContext, image, SamplingFilter::POINT, + anchorPoint, &aDirtyRect, svgContext, imgIContainer::FLAG_NONE, + &imageSize); return result; } @@ -3895,7 +3839,7 @@ void nsTreeBodyFrame::RemoveImageCacheEntry(int32_t aRowIndex, nsTreeColumn* aCol) { nsAutoString imageSrc; nsCOMPtr<nsITreeView> view = GetExistingView(); - if (NS_FAILED(view->GetImageSrc(aRowIndex, aCol, imageSrc))) { + if (!view || NS_FAILED(view->GetImageSrc(aRowIndex, aCol, imageSrc))) { return; } nsTreeImageCacheEntry entry; diff --git a/layout/xul/tree/nsTreeBodyFrame.h b/layout/xul/tree/nsTreeBodyFrame.h index dd38644ad9..d7a9498ced 100644 --- a/layout/xul/tree/nsTreeBodyFrame.h +++ b/layout/xul/tree/nsTreeBodyFrame.h @@ -295,10 +295,10 @@ class nsTreeBodyFrame final : public mozilla::SimpleXULLeafFrame, nsCSSAnonBoxPseudoStaticAtom** aChildElt); // Retrieve the area for the twisty for a cell. - nsITheme* GetTwistyRect(int32_t aRowIndex, nsTreeColumn* aColumn, - nsRect& aImageRect, nsRect& aTwistyRect, - nsPresContext* aPresContext, - ComputedStyle* aTwistyContext); + void GetTwistyRect(int32_t aRowIndex, nsTreeColumn* aColumn, + nsRect& aImageRect, nsRect& aTwistyRect, + nsPresContext* aPresContext, + ComputedStyle* aTwistyContext); // Fetch an image from the image cache. nsresult GetImage(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext, |