diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
commit | 8dd16259287f58f9273002717ec4d27e97127719 (patch) | |
tree | 3863e62a53829a84037444beab3abd4ed9dfc7d0 /layout | |
parent | Releasing progress-linux version 126.0.1-1~progress7.99u1. (diff) | |
download | firefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz firefox-8dd16259287f58f9273002717ec4d27e97127719.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout')
175 files changed, 3373 insertions, 1366 deletions
diff --git a/layout/base/AccessibleCaretManager.cpp b/layout/base/AccessibleCaretManager.cpp index 99155133cd..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" @@ -879,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(); 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 7674abb3d0..cd41da3730 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -3931,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) { @@ -6566,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) { @@ -11260,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 } @@ -11849,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()) { @@ -12102,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/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 81ffebf89a..e00493467f 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -3246,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()) { @@ -3271,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) { 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 3b1ed1e3d4..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 @@ -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 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 c0f35c278d..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); } @@ -5283,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); diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 51d5fe32d0..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); /** diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 8ae0e001b8..bbd43a830e 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -2426,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) { @@ -2436,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) { 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 429ad335cc..3f84a97250 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -900,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; @@ -2131,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 || @@ -3498,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. @@ -3510,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), @@ -3523,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; @@ -3568,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) {} @@ -3609,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); } @@ -3618,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); } @@ -3649,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 @@ -4610,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) { @@ -4641,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); @@ -5048,10 +5063,11 @@ 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(LogicalAxis::Inline); // According to the spec, max-content and min-content should behave as the @@ -5077,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()) { @@ -8896,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); } } @@ -9223,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 diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 135a9ad9ab..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 }; /** diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 7885d6b8db..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); } @@ -2228,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; } 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/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/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/nsRangeFrame.h b/layout/forms/nsRangeFrame.h index 10d0fc0095..206103ea44 100644 --- a/layout/forms/nsRangeFrame.h +++ b/layout/forms/nsRangeFrame.h @@ -105,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; diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index 32e817be8a..3c44038d89 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -417,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, 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/CSSAlignUtils.cpp b/layout/generic/CSSAlignUtils.cpp index 7dd8999563..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); diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp index c642c77b2c..bdfd39aff1 100644 --- a/layout/generic/ReflowInput.cpp +++ b/layout/generic/ReflowInput.cpp @@ -953,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 @@ -1123,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 = @@ -1228,9 +1217,9 @@ 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; } @@ -2549,9 +2538,9 @@ 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; @@ -2742,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; } @@ -2771,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(); } @@ -2781,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(); } @@ -2839,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"); @@ -2850,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; } @@ -2861,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 ba49edcaf6..7bf7c4fc73 100644 --- a/layout/generic/ReflowInput.h +++ b/layout/generic/ReflowInput.h @@ -741,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; 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/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/WritingModes.h b/layout/generic/WritingModes.h index 0fcf47f1d0..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 @@ -50,7 +48,7 @@ enum class LogicalAxis : uint8_t { Block, Inline, }; -enum LogicalEdge { eLogicalEdgeStart = 0x0, eLogicalEdgeEnd = 0x1 }; +enum class LogicalEdge : uint8_t { Start, End }; enum class LogicalSide : uint8_t { BStart, @@ -59,11 +57,6 @@ enum class LogicalSide : uint8_t { IEnd, }; -constexpr auto AllLogicalSides() { - return mozilla::MakeInclusiveEnumeratedRange(LogicalSide::BStart, - LogicalSide::IEnd); -} - enum class LogicalCorner : uint8_t { BStartIStart, BStartIEnd, @@ -72,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 { @@ -104,38 +97,30 @@ inline LogicalAxis GetAxis(LogicalSide aSide) { } 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((uint8_t(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 << static_cast<uint8_t>(LogicalSide::BStart), - eLogicalSideBitsBEnd = 1 << static_cast<uint8_t>(LogicalSide::BEnd), - eLogicalSideBitsIEnd = 1 << static_cast<uint8_t>(LogicalSide::IEnd), - eLogicalSideBitsIStart = 1 << static_cast<uint8_t>(LogicalSide::IStart), - eLogicalSideBitsBBoth = eLogicalSideBitsBStart | eLogicalSideBitsBEnd, - eLogicalSideBitsIBoth = eLogicalSideBitsIStart | eLogicalSideBitsIEnd, - eLogicalSideBitsAll = eLogicalSideBitsBBoth | eLogicalSideBitsIBoth -}; - -enum LineRelativeDir { - eLineRelativeDirOver = static_cast<uint8_t>(LogicalSide::BStart), - eLineRelativeDirUnder = static_cast<uint8_t>(LogicalSide::BEnd), - eLineRelativeDirLeft = static_cast<uint8_t>(LogicalSide::IStart), - eLineRelativeDirRight = static_cast<uint8_t>(LogicalSide::IEnd) +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) }; /** @@ -155,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 - }; - - /** - * 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 + 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 }; /** - * 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; } /** @@ -216,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. @@ -242,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 @@ -339,8 +311,9 @@ class WritingMode { uint8_t(StyleWritingModeProperty::VerticalRl) == 1 && uint8_t(StyleWritingModeProperty::VerticalLr) == 3 && uint8_t(LogicalAxis::Block) == 0 && - uint8_t(LogicalAxis::Inline) == 1 && eAxisVertical == 0 && - eAxisHorizontal == 1, + 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 ^ uint8_t(aAxis)) & 0x1); @@ -376,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 { @@ -413,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)]; } /** @@ -428,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; @@ -490,12 +463,12 @@ class WritingMode { }; // 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]; } @@ -580,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. @@ -588,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"); @@ -611,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); } /** @@ -624,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) { @@ -818,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); @@ -834,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); @@ -857,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, @@ -1107,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()); @@ -1165,7 +1140,7 @@ struct LogicalSides final { #ifdef DEBUG WritingMode mWritingMode; #endif - uint8_t mBits; + EnumSet<LogicalSide> mSides; }; /** diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 2aea090a6f..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 diff --git a/layout/generic/nsAbsoluteContainingBlock.cpp b/layout/generic/nsAbsoluteContainingBlock.cpp index 4f561688bf..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; } diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index 4582c29e0b..4b314bfcaf 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -1186,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; } @@ -7979,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 b4b108905f..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()) { diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp index a9d3e28138..d78a5cb992 100644 --- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -570,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; diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index 2c9e707830..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); } } 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 28d1b08a30..c08950121a 100644 --- a/layout/generic/nsFirstLetterFrame.cpp +++ b/layout/generic/nsFirstLetterFrame.cpp @@ -447,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 66f1e79609..4604106a4a 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -312,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 @@ -2021,7 +2021,7 @@ const CachedBAxisMeasurement& nsFlexContainerFrame::MeasureBSizeForFlexItem( // 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(); @@ -2032,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"); @@ -2227,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"); @@ -2257,7 +2257,7 @@ FlexItem::FlexItem(ReflowInput& aFlexItemReflowInput, float aFlexGrow, // (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 @@ -2354,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, @@ -2448,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++; @@ -2708,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 @@ -4111,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( @@ -4885,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) { @@ -4905,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()); } @@ -4915,7 +4927,7 @@ void nsFlexContainerFrame::UnionInFlowChildOverflow( if (anyScrolledContentItem) { itemMarginBoxes.Inflate(GetUsedPadding()); aOverflowAreas.UnionAllWith(itemMarginBoxes); - aOverflowAreas.UnionAllWith(relPosItemMarginBoxes); + aOverflowAreas.UnionWith(relPosItemMarginBoxes); } } @@ -5515,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 @@ -5533,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 @@ -5631,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 @@ -5662,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; @@ -5691,15 +5753,38 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren( FLEX_ITEM_LOG( item.Frame(), "[frag] Item needed to be pushed to container's next-in-flow " - "because its block-size is larger than the available space"); + "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); } @@ -5787,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. @@ -5808,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); @@ -5952,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 @@ -6067,7 +6193,8 @@ void nsFlexContainerFrame::MoveFlexItemToFinalPosition( nsReflowStatus nsFlexContainerFrame::ReflowFlexItem( const FlexboxAxisTracker& aAxisTracker, const ReflowInput& aReflowInput, const FlexItem& aItem, const LogicalPoint& aFramePos, - const LogicalSize& aAvailableSize, const nsSize& aContainerSize) { + 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 @@ -6206,6 +6333,13 @@ 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. @@ -6216,11 +6350,11 @@ nsReflowStatus nsFlexContainerFrame::ReflowFlexItem( // 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. @@ -6240,7 +6374,7 @@ nsReflowStatus nsFlexContainerFrame::ReflowFlexItem( aItem.Frame()->SetProperty(CachedFlexItemData::Prop(), cached); } - return childReflowStatus; + return childStatus; } void nsFlexContainerFrame::ReflowPlaceholders( @@ -6260,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, diff --git a/layout/generic/nsFlexContainerFrame.h b/layout/generic/nsFlexContainerFrame.h index 14f91ae064..2771098255 100644 --- a/layout/generic/nsFlexContainerFrame.h +++ b/layout/generic/nsFlexContainerFrame.h @@ -604,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 @@ -614,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 e1866b35c1..88a8b20b4c 100644 --- a/layout/generic/nsFloatManager.cpp +++ b/layout/generic/nsFloatManager.cpp @@ -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 ca4e3d647e..94cf368480 100644 --- a/layout/generic/nsFrameSelection.cpp +++ b/layout/generic/nsFrameSelection.cpp @@ -1441,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); } diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 2b0c53efe1..97e6b614dc 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -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(); @@ -5211,15 +5211,19 @@ nsSize nsHTMLScrollFrame::GetPageScrollAmount() const { } nsSize lineScrollAmount = GetLineScrollAmount(); - - // 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)); + 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 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)); } /** diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index bb6e2150ce..48c70cd479 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -4317,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); @@ -4377,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) { @@ -4387,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); } @@ -4411,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'. @@ -4441,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 @@ -4527,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; @@ -4540,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); @@ -5761,7 +5761,7 @@ 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(LogicalAxis::Inline); @@ -5800,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 @@ -6017,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)) { @@ -6124,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; diff --git a/layout/generic/nsGridContainerFrame.h b/layout/generic/nsGridContainerFrame.h index cb3eef68c3..1fa7bbe487 100644 --- a/layout/generic/nsGridContainerFrame.h +++ b/layout/generic/nsGridContainerFrame.h @@ -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/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index 27f09b84f6..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" @@ -4928,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 = @@ -7889,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 { @@ -7950,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; } @@ -8008,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; @@ -10795,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) { @@ -10806,7 +10820,8 @@ Focusable nsIFrame::IsFocusable(bool aWithMouse, bool aCheckVisibility) { return {}; } - if (aCheckVisibility && !IsVisibleConsideringAncestors()) { + if (!(aFlags & IsFocusableFlags::IgnoreVisibility) && + !IsVisibleConsideringAncestors()) { return {}; } @@ -10826,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) { @@ -10841,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}; } @@ -11627,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) { diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 6e55ebe1c7..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; @@ -1386,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. // @@ -4369,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. diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 91d8430b60..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(); } @@ -2857,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 b481c71827..44fce51931 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -797,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. @@ -809,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. @@ -822,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/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index be6f64f1fe..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++; diff --git a/layout/generic/nsRubyFrame.cpp b/layout/generic/nsRubyFrame.cpp index d6119f44fb..fc74ce8184 100644 --- a/layout/generic/nsRubyFrame.cpp +++ b/layout/generic/nsRubyFrame.cpp @@ -300,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: @@ -333,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. 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 69b4042033..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,22 +553,7 @@ nsresult nsSubDocumentFrame::GetFrameName(nsAString& aResult) const { /* virtual */ nscoord nsSubDocumentFrame::GetMinISize(gfxContext* aRenderingContext) { - nscoord 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 */ @@ -706,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/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/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/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 954e2de6e2..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-5) 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/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 2c2b7cb04c..0007f86c5e 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -5207,7 +5207,7 @@ bool nsDisplayOwnLayer::CreateWebRenderCommands( Maybe<wr::WrAnimationProperty> prop; bool needsProp = aManager->LayerManager()->AsyncPanZoomEnabled() && (IsScrollThumbLayer() || IsZoomingLayer() || - ShouldGetFixedOrStickyAnimationId() || + ShouldGetFixedAnimationId() || (IsRootScrollbarContainer() && HasDynamicToolbar())); if (needsProp) { @@ -5234,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; @@ -5250,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; @@ -5267,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); @@ -5458,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()) == @@ -5522,7 +5516,8 @@ nsDisplayStickyPosition::nsDisplayStickyPosition( : nsDisplayOwnLayer(aBuilder, aFrame, aList, aActiveScrolledRoot), mContainerASR(aContainerASR), mClippedToDisplayPort(aClippedToDisplayPort), - mShouldFlatten(false) { + mShouldFlatten(false), + mWrStickyAnimationId(0) { MOZ_COUNT_CTOR(nsDisplayStickyPosition); } @@ -5738,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); @@ -5812,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 @@ -5821,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 acda6ea863..9862415f61 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -5475,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; } @@ -5491,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; }; @@ -5550,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); } @@ -5575,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; } @@ -5591,6 +5601,8 @@ class nsDisplayStickyPosition : public nsDisplayOwnLayer { return mShouldFlatten; } + bool ShouldGetStickyAnimationId() const; + private: NS_DISPLAY_ALLOW_CLONING() @@ -5620,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 { @@ -5661,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/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 28e96a51da..84f4ecca9f 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -919,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 @@ -1120,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 @@ -1344,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 @@ -1367,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 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/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/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/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 97f04542ff..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 @@ -360,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/ @@ -431,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/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.h b/layout/style/CSSPageRule.h index 904f94d2c0..b133111244 100644 --- a/layout/style/CSSPageRule.h +++ b/layout/style/CSSPageRule.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; 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/FontFaceSetWorkerImpl.cpp b/layout/style/FontFaceSetWorkerImpl.cpp index 7fbfbf0d95..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() { 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/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 eb752b2789..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" diff --git a/layout/style/Rule.cpp b/layout/style/Rule.cpp index 4b0f783bd9..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 || @@ -108,7 +109,8 @@ void Rule::AssertParentRuleType() { type == StyleCssRuleType::LayerBlock || type == StyleCssRuleType::Container || type == StyleCssRuleType::Scope || - type == StyleCssRuleType::StartingStyle); + type == StyleCssRuleType::StartingStyle || + type == StyleCssRuleType::Page); } } #endif diff --git a/layout/style/ServoBindingTypes.h b/layout/style/ServoBindingTypes.h index b081a4981b..e2443ef526 100644 --- a/layout/style/ServoBindingTypes.h +++ b/layout/style/ServoBindingTypes.h @@ -123,6 +123,7 @@ 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) diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index 52538cafb8..2c0abbe0db 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -77,6 +77,7 @@ BASIC_RULE_FUNCS_LOCKED(Keyframes) GROUP_RULE_FUNCS_UNLOCKED(Media) GROUP_RULE_FUNCS_UNLOCKED(Document) BASIC_RULE_FUNCS_UNLOCKED(Namespace) +BASIC_RULE_FUNCS_UNLOCKED(Margin) GROUP_RULE_FUNCS_LOCKED(Page) BASIC_RULE_FUNCS_UNLOCKED(Property) GROUP_RULE_FUNCS_UNLOCKED(Supports) diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml index 67fd902e2b..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" }, @@ -631,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 133a962256..b7172879de 100644 --- a/layout/style/ServoCSSRuleList.cpp +++ b/layout/style/ServoCSSRuleList.cpp @@ -17,6 +17,7 @@ #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" @@ -88,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) @@ -108,9 +110,6 @@ css::Rule* ServoCSSRuleList::GetRule(uint32_t aIndex) { 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; @@ -277,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) @@ -294,9 +294,6 @@ void ServoCSSRuleList::SetRawContents(RefPtr<StyleLockedCssRules> aNewRules, 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/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 5bf87d8330..cd2958618f 100644 --- a/layout/style/ServoStyleConstsForwards.h +++ b/layout/style/ServoStyleConstsForwards.h @@ -89,6 +89,7 @@ class SharedFontList; class StyleSheet; class WritingMode; class ServoElementSnapshotTable; +class StyleParserState; template <typename T> struct StyleForgottenArcSlicePtr; diff --git a/layout/style/ServoStyleConstsInlines.h b/layout/style/ServoStyleConstsInlines.h index 5a86e4ccd0..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>; diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index 91231af2b0..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" @@ -766,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) { @@ -994,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) @@ -1014,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 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 b77fa34983..ce9ee36cea 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -138,6 +138,7 @@ EXPORTS.mozilla.dom += [ "CSSKeyframesRule.h", "CSSLayerBlockRule.h", "CSSLayerStatementRule.h", + "CSSMarginRule.h", "CSSMediaRule.h", "CSSMozDocumentRule.h", "CSSNamespaceRule.h", @@ -191,6 +192,7 @@ UNIFIED_SOURCES += [ "CSSKeyframesRule.cpp", "CSSLayerBlockRule.cpp", "CSSLayerStatementRule.cpp", + "CSSMarginRule.cpp", "CSSMediaRule.cpp", "CSSMozDocumentRule.cpp", "CSSNamespaceRule.cpp", 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 5b71ec54bf..876f32cd7a 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -1888,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()); } @@ -1901,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()); } diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index d1f3458030..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; 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 fbdde4102d..cc86d1abf6 100644 --- a/layout/style/nsMediaFeatures.cpp +++ b/layout/style/nsMediaFeatures.cpp @@ -349,7 +349,6 @@ StyleDynamicRange Gecko_MediaFeatures_VideoDynamicRange( // with the device context claims to be HDR capable. if (nsDeviceContext* dx = GetDeviceContextFor(aDocument)) { if (dx->GetScreenIsHDR()) { - // bjw return StyleDynamicRange::High; } } diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index e46566d471..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()) { 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 044d460ad4..7a5c25fc6e 100644 --- a/layout/style/res/forms.css +++ b/layout/style/res/forms.css @@ -192,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 { /* @@ -631,10 +635,6 @@ input[type=file] > label { text-align: match-parent; cursor: unset; - color: unset; - font-size: unset; - letter-spacing: unset; - user-select: none; unicode-bidi: plaintext; } @@ -842,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 18dd1c4855..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; 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/property_database.js b/layout/style/test/property_database.js index c63d65926e..302bd48b42 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -5382,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('*'))", @@ -5400,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'", 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/SVGOuterSVGFrame.cpp b/layout/svg/SVGOuterSVGFrame.cpp index d88327f059..00d7663901 100644 --- a/layout/svg/SVGOuterSVGFrame.cpp +++ b/layout/svg/SVGOuterSVGFrame.cpp @@ -126,9 +126,9 @@ NS_QUERYFRAME_TAIL_INHERITING(SVGDisplayContainerFrame) /* virtual */ nscoord SVGOuterSVGFrame::GetMinISize(gfxContext* aRenderingContext) { - // If this ever changes to return something other than zero, then - // nsSubDocumentFrame::GetMinISize will also need to change. - return 0; + auto size = GetIntrinsicSize(); + const auto& iSize = GetWritingMode().IsVertical() ? size.height : size.width; + return iSize.valueOr(0); } /* virtual */ 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/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index fdfa5e305e..86ce48d47b 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -378,10 +378,10 @@ LogicalSides nsTableCellFrame::GetLogicalSkipSides() const { } if (GetPrevInFlow()) { - skip |= eLogicalSideBitsBStart; + skip += LogicalSide::BStart; } if (GetNextInFlow()) { - skip |= eLogicalSideBitsBEnd; + skip += LogicalSide::BEnd; } return skip; } diff --git a/layout/tables/nsTableColGroupFrame.cpp b/layout/tables/nsTableColGroupFrame.cpp index bf0200e27c..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; } diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 84bdbc48af..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; } @@ -2392,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 { @@ -3799,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); @@ -3844,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 @@ -3892,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), @@ -3921,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); @@ -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,30 +4915,75 @@ 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( - LogicalSide::IEnd, - std::max(aWidth, mCell->GetBorderWidth(LogicalSide::IEnd))); + 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( - LogicalSide::BEnd, - std::max(aWidth, mCell->GetBorderWidth(LogicalSide::BEnd))); + 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())); } } @@ -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( - LogicalSide::IStart, - std::max(aWidth, mCell->GetBorderWidth(LogicalSide::IStart))); + 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 = @@ -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()); @@ -5166,9 +5244,10 @@ void nsTableFrame::CalcBCBorders() { if (0 == info.mRowIndex) { uint8_t idxBStart = static_cast<uint8_t>(LogicalSide::BStart); if (!tableBorderReset[idxBStart]) { - propData->mBStartBorderWidth = 0; + tableInfo.ResetTableBStartBorderWidth(); tableBorderReset[idxBStart] = true; } + bool reset = false; for (int32_t colIdx = info.mColIndex; colIdx <= info.GetCellEndColIndex(); colIdx++) { info.SetColumn(colIdx); @@ -5203,7 +5282,11 @@ void nsTableFrame::CalcBCBorders() { // 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 { @@ -5228,10 +5311,11 @@ void nsTableFrame::CalcBCBorders() { if (0 == info.mColIndex) { uint8_t idxIStart = static_cast<uint8_t>(LogicalSide::IStart); if (!tableBorderReset[idxIStart]) { - propData->mIStartBorderWidth = 0; + 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); @@ -5254,7 +5338,11 @@ void nsTableFrame::CalcBCBorders() { 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); } } @@ -5265,10 +5353,11 @@ void nsTableFrame::CalcBCBorders() { // touches iEnd edge of table uint8_t idxIEnd = static_cast<uint8_t>(LogicalSide::IEnd); if (!tableBorderReset[idxIEnd]) { - propData->mIEndBorderWidth = 0; + 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); @@ -5301,7 +5390,11 @@ void nsTableFrame::CalcBCBorders() { 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 { @@ -5309,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. @@ -5331,6 +5425,11 @@ void nsTableFrame::CalcBCBorders() { 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); } @@ -5405,9 +5504,10 @@ void nsTableFrame::CalcBCBorders() { // touches bEnd edge of table uint8_t idxBEnd = static_cast<uint8_t>(LogicalSide::BEnd); if (!tableBorderReset[idxBEnd]) { - propData->mBEndBorderWidth = 0; + tableInfo.ResetTableBEndBorderWidth(); tableBorderReset[idxBEnd] = true; } + bool reset = false; for (int32_t colIdx = info.mColIndex; colIdx <= info.GetCellEndColIndex(); colIdx++) { info.SetColumn(colIdx); @@ -5453,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. @@ -5540,6 +5645,12 @@ void nsTableFrame::CalcBCBorders() { 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); } diff --git a/layout/tables/nsTableRowFrame.cpp b/layout/tables/nsTableRowFrame.cpp index a6f4647429..2447ceea15 100644 --- a/layout/tables/nsTableRowFrame.cpp +++ b/layout/tables/nsTableRowFrame.cpp @@ -561,10 +561,10 @@ LogicalSides nsTableRowFrame::GetLogicalSkipSides() const { } if (GetPrevInFlow()) { - skip |= eLogicalSideBitsBStart; + skip += LogicalSide::BStart; } if (GetNextInFlow()) { - skip |= eLogicalSideBitsBEnd; + skip += LogicalSide::BEnd; } return skip; } diff --git a/layout/tables/nsTableRowGroupFrame.cpp b/layout/tables/nsTableRowGroupFrame.cpp index 9ae8a59a90..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; } 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/manifest.sys.mjs b/layout/tools/reftest/manifest.sys.mjs index a4a8ed126f..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,60 +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; @@ -701,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"); @@ -734,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 @@ -779,11 +695,6 @@ function BuildConditionSandbox(aURL) { false ); - sandbox.gpuProcessForceEnabled = Services.prefs.getBoolPref( - "layers.gpu-process.force-enabled", - false - ); - sandbox.prefs = Cu.cloneInto( { getBoolPref(p) { @@ -797,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 0f103c5816..12bdab98f9 100644 --- a/layout/tools/reftest/reftest.sys.mjs +++ b/layout/tools/reftest/reftest.sys.mjs @@ -1512,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 e97a6de5d7..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) @@ -539,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: @@ -1100,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/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 f585009600..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; } 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, |