diff options
Diffstat (limited to 'layout/base')
24 files changed, 619 insertions, 170 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,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"/> +</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);} , |