diff --git a/dom/base/DOMIntersectionObserver.cpp b/dom/base/DOMIntersectionObserver.cpp index 6bf6339bcbcc..6f801bd5d3eb 100644 --- a/dom/base/DOMIntersectionObserver.cpp +++ b/dom/base/DOMIntersectionObserver.cpp @@ -14,6 +14,7 @@ #include "nsRefreshDriver.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPrefs_layout.h" #include "mozilla/ServoBindings.h" #include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/BrowsingContext.h" @@ -150,6 +151,20 @@ static void LazyLoadCallback( } } +static void ContentVisibilityCallback( + const Sequence>& aEntries) { + for (const auto& entry : aEntries) { + entry->Target()->SetVisibleForContentVisibility(entry->IsIntersecting()); + + if (RefPtr doc = entry->Target()->GetComposedDoc()) { + if (RefPtr presShell = doc->GetPresShell()) { + presShell->ScheduleContentRelevancyUpdate( + ContentRelevancyReason::Visible); + } + } + } +} + static LengthPercentage PrefMargin(float aValue, bool aIsPercentage) { return aIsPercentage ? LengthPercentage::FromPercentage(aValue / 100.0f) : LengthPercentage::FromPixels(aValue); @@ -182,6 +197,25 @@ DOMIntersectionObserver::CreateLazyLoadObserver(Document& aDocument) { return observer.forget(); } +already_AddRefed +DOMIntersectionObserver::CreateContentVisibilityObserver(Document& aDocument) { + RefPtr observer = + new DOMIntersectionObserver(aDocument, ContentVisibilityCallback); + + observer->mThresholds.AppendElement(0.0f); + + auto margin = LengthPercentage::FromPercentage( + StaticPrefs::layout_css_content_visibility_relevant_content_margin() / + 100.0f); + + observer->mRootMargin.Get(eSideTop) = margin; + observer->mRootMargin.Get(eSideRight) = margin; + observer->mRootMargin.Get(eSideBottom) = margin; + observer->mRootMargin.Get(eSideLeft) = margin; + + return observer.forget(); +} + bool DOMIntersectionObserver::SetRootMargin(const nsACString& aString) { return Servo_IntersectionObserverRootMargin_Parse(&aString, &mRootMargin); } diff --git a/dom/base/DOMIntersectionObserver.h b/dom/base/DOMIntersectionObserver.h index ce985543af90..144e0ac4fea6 100644 --- a/dom/base/DOMIntersectionObserver.h +++ b/dom/base/DOMIntersectionObserver.h @@ -163,6 +163,9 @@ class DOMIntersectionObserver final : public nsISupports, static already_AddRefed CreateLazyLoadObserverViewport(Document&); + static already_AddRefed + CreateContentVisibilityObserver(Document&); + protected: void Connect(); void QueueIntersectionObserverEntry(Element* aTarget, diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 7fcc9d34175b..fbca5e2e281f 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -2510,6 +2510,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadImageObserver) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadImageObserverViewport) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastRememberedSizeObserver) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentVisibilityObserver) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise) @@ -2628,6 +2629,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadImageObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadImageObserverViewport) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mContentVisibilityObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLastRememberedSizeObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet) NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle) @@ -15767,6 +15769,24 @@ DOMIntersectionObserver& Document::EnsureLazyLoadImageObserver() { return *mLazyLoadImageObserver; } +DOMIntersectionObserver& Document::EnsureContentVisibilityObserver() { + if (!mContentVisibilityObserver) { + mContentVisibilityObserver = + DOMIntersectionObserver::CreateContentVisibilityObserver(*this); + } + return *mContentVisibilityObserver; +} + +void Document::ObserveForContentVisibility(Element& aElement) { + EnsureContentVisibilityObserver().Observe(aElement); +} + +void Document::UnobserveForContentVisibility(Element& aElement) { + if (mContentVisibilityObserver) { + mContentVisibilityObserver->Unobserve(aElement); + } +} + ResizeObserver& Document::EnsureLastRememberedSizeObserver() { if (!mLastRememberedSizeObserver) { mLastRememberedSizeObserver = diff --git a/dom/base/Document.h b/dom/base/Document.h index d45ec95b66bd..abca2a83e8cc 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -3768,6 +3768,10 @@ class Document : public nsINode, } DOMIntersectionObserver& EnsureLazyLoadImageObserver(); + DOMIntersectionObserver& EnsureContentVisibilityObserver(); + void ObserveForContentVisibility(Element&); + void UnobserveForContentVisibility(Element&); + ResizeObserver* GetLastRememberedSizeObserver() { return mLastRememberedSizeObserver; } @@ -5137,6 +5141,10 @@ class Document : public nsINode, // Used to measure how effective the lazyload thresholds are. RefPtr mLazyLoadImageObserverViewport; + // Used for detecting when `content-visibility: auto` elements are near + // or far from the viewport. + RefPtr mContentVisibilityObserver; + // ResizeObserver for storing and removing the last remembered size. // @see {@link https://drafts.csswg.org/css-sizing-4/#last-remembered} RefPtr mLastRememberedSizeObserver; diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 54b2d30eebbb..8de3a71a4c4c 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -724,7 +724,8 @@ bool Element::CheckVisibility(const CheckVisibilityOptions& aOptions) { return false; } - if (f->IsHiddenByContentVisibilityOnAnyAncestor()) { + if (f->IsHiddenByContentVisibilityOnAnyAncestor( + nsIFrame::IncludeContentVisibility::Hidden)) { // 2. If a shadow-including ancestor of this has content-visibility: hidden, // return false. return false; diff --git a/dom/base/Element.h b/dom/base/Element.h index d935d990af61..f146aa674f93 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -1333,6 +1333,29 @@ class Element : public FragmentOrElement { return GetLastRememberedISize().isSome(); } + const Maybe GetContentRelevancy() const { + const auto* slots = GetExistingExtendedDOMSlots(); + return slots ? slots->mContentRelevancy : Nothing(); + } + void SetContentRelevancy(ContentRelevancy relevancy) { + ExtendedDOMSlots()->mContentRelevancy = Some(relevancy); + } + + const Maybe GetVisibleForContentVisibility() const { + const auto* slots = GetExistingExtendedDOMSlots(); + return slots ? slots->mVisibleForContentVisibility : Nothing(); + } + void SetVisibleForContentVisibility(bool visible) { + ExtendedDOMSlots()->mVisibleForContentVisibility = Some(visible); + } + + void ClearContentRelevancy() { + if (auto* slots = GetExistingExtendedDOMSlots()) { + slots->mContentRelevancy.reset(); + slots->mVisibleForContentVisibility.reset(); + } + } + // https://drafts.csswg.org/cssom-view-1/#dom-element-checkvisibility MOZ_CAN_RUN_SCRIPT bool CheckVisibility(const CheckVisibilityOptions&); diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index 4e1ce720ca93..222b755ae20d 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -14,6 +14,7 @@ #define FragmentOrElement_h___ #include "mozilla/Attributes.h" +#include "mozilla/EnumSet.h" #include "mozilla/MemoryReporting.h" #include "mozilla/UniquePtr.h" #include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_* @@ -33,6 +34,8 @@ class nsIURI; namespace mozilla { class DeclarationBlock; +enum class ContentRelevancyReason; +using ContentRelevancy = EnumSet; namespace dom { struct CustomElementData; class Element; @@ -198,6 +201,18 @@ class FragmentOrElement : public nsIContent { */ Maybe mLastRememberedBSize; Maybe mLastRememberedISize; + + /** + * Whether the content of this element is relevant for the purposes + * of `content-visibility: auto. + */ + Maybe mContentRelevancy; + + /** + * Whether the content of this element is considered visible for + * the purposes of `content-visibility: auto. + */ + Maybe mVisibleForContentVisibility; }; class nsDOMSlots : public nsIContent::nsContentSlots { diff --git a/dom/base/Selection.cpp b/dom/base/Selection.cpp index 5ff74d1a174c..45ac9935de22 100644 --- a/dom/base/Selection.cpp +++ b/dom/base/Selection.cpp @@ -9,6 +9,8 @@ */ #include "mozilla/dom/Selection.h" + +#include "LayoutConstants.h" #include "mozilla/intl/BidiEmbeddingLevel.h" #include "mozilla/AccessibleCaretEventHub.h" @@ -3200,6 +3202,7 @@ void Selection::NotifySelectionListeners() { PresShell* presShell = GetPresShell(); if (presShell) { doc = presShell->GetDocument(); + presShell->ScheduleContentRelevancyUpdate(ContentRelevancyReason::Selected); } // We've notified all selection listeners even when some of them are removed diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index 0280ffc5fe69..f29523824dee 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -8,6 +8,7 @@ #include "nsFocusManager.h" +#include "LayoutConstants.h" #include "ChildIterator.h" #include "nsIInterfaceRequestorUtils.h" #include "nsGkAtoms.h" @@ -2882,6 +2883,9 @@ void nsFocusManager::FireFocusOrBlurEvent(EventMessage aEventMessage, } #endif + aPresShell->ScheduleContentRelevancyUpdate( + ContentRelevancyReason::FocusInSubtree); + nsContentUtils::AddScriptRunner( new FocusBlurEvent(aTarget, aEventMessage, aPresShell->GetPresContext(), aWindowRaised, aIsRefocus, aRelatedTarget)); diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h index e58a9e4548f9..60bdd2bd738e 100644 --- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -353,6 +353,18 @@ class nsRange final : public mozilla::dom::AbstractRange, */ MOZ_CAN_RUN_SCRIPT void NotifySelectionListenersAfterRangeSet(); + /** + * For a range for which IsInSelection() is true, return the closest common + * inclusive ancestor + * (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor) + * for the range, which we had to compute when the common ancestor changed or + * IsInSelection became true, so we could register with it. That is, it's a + * faster version of GetClosestCommonInclusiveAncestor that only works for + * ranges in a Selection. The method will assert and the behavior is undefined + * if called on a range where IsInSelection() is false. + */ + nsINode* GetRegisteredClosestCommonInclusiveAncestor(); + protected: /** * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor @@ -386,18 +398,6 @@ class nsRange final : public mozilla::dom::AbstractRange, const mozilla::RangeBoundaryBase& aEndBoundary, nsINode* aRootNode, bool aNotInsertedYet = false); - /** - * For a range for which IsInSelection() is true, return the closest common - * inclusive ancestor - * (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor) - * for the range, which we had to compute when the common ancestor changed or - * IsInSelection became true, so we could register with it. That is, it's a - * faster version of GetClosestCommonInclusiveAncestor that only works for - * ranges in a Selection. The method will assert and the behavior is undefined - * if called on a range where IsInSelection() is false. - */ - nsINode* GetRegisteredClosestCommonInclusiveAncestor(); - // Assume that this is guaranteed that this is held by the caller when // this is used. (Note that we cannot use AutoRestore for mCalledByJS // due to a bit field.) diff --git a/layout/base/LayoutConstants.h b/layout/base/LayoutConstants.h index bcdb51baea17..f441159fe3f2 100644 --- a/layout/base/LayoutConstants.h +++ b/layout/base/LayoutConstants.h @@ -92,6 +92,21 @@ inline constexpr nsSize kFallbackIntrinsicSize(kFallbackIntrinsicWidth, */ enum class IntrinsicISizeType { MinISize, PrefISize }; +enum class ContentRelevancyReason { + // If the content of this Frame is on screen or nearly on screen. + Visible, + + // If this Frame's element is a descendant of a top layer element. + DescendantOfTopLayerElement, + + // If this Frame's element has focus in its subtree. + FocusInSubtree, + + // If this Frame's content is part of a selection. + Selected, +}; +using ContentRelevancy = EnumSet; + } // namespace mozilla #endif // LayoutConstants_h___ diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index e2f7204beb14..79d48700e683 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -2278,6 +2278,8 @@ void PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) { mPendingScrollAnchorAdjustment.Remove(scrollableFrame); mPendingScrollResnap.Remove(scrollableFrame); } + + mContentVisibilityAutoFrames.Remove(aFrame); } } @@ -3661,6 +3663,18 @@ nsresult PresShell::ScrollContentIntoView(nsIContent* aContent, mContentToScrollTo = nullptr; } + // If the target frame is an ancestor of a `content-visibility: auto` + // element ensure that it is laid out, so that the boundary rectangle is + // correct. + if (mContentToScrollTo) { + if (nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame()) { + if (frame->IsHiddenByContentVisibilityOnAnyAncestor( + nsIFrame::IncludeContentVisibility::Auto)) { + frame->PresShell()->EnsureReflowIfFrameHasHiddenContent(frame); + } + } + } + // Flush layout and attempt to scroll in the process. if (PresShell* presShell = composedDoc->GetPresShell()) { presShell->SetNeedLayoutFlush(); @@ -3703,7 +3717,9 @@ void PresShell::DoScrollContentIntoView() { NS_ASSERTION(mDidInitialize, "should have done initial reflow by now"); nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame(); - if (!frame || frame->IsHiddenByContentVisibilityOnAnyAncestor()) { + + if (!frame || frame->IsHiddenByContentVisibilityOnAnyAncestor( + nsIFrame::IncludeContentVisibility::Hidden)) { mContentToScrollTo->RemoveProperty(nsGkAtoms::scrolling); mContentToScrollTo = nullptr; return; @@ -3730,10 +3746,6 @@ void PresShell::DoScrollContentIntoView() { bool PresShell::ScrollFrameIntoView( nsIFrame* aTargetFrame, const Maybe& aKnownRectRelativeToTarget, ScrollAxis aVertical, ScrollAxis aHorizontal, ScrollFlags aScrollFlags) { - if (aTargetFrame->IsHiddenByContentVisibilityOnAnyAncestor()) { - return false; - } - // The scroll margin only applies to the whole bounds of the element, so don't // apply it if we get an arbitrary rect / point to scroll to. const nsMargin scrollMargin = @@ -4271,6 +4283,13 @@ void PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush) { */ FlushType flushType = aFlush.mFlushType; + // If this is a layout flush, first update the relevancy of any content + // of elements with `content-visibility: auto` so that the values + // returned from script queries are up-to-date. + if (flushType >= mozilla::FlushType::Layout) { + UpdateRelevancyOfContentVisibilityAutoFrames(); + } + MOZ_ASSERT(NeedFlush(flushType), "Why did we get called?"); AUTO_PROFILER_MARKER_TEXT( @@ -11898,3 +11917,28 @@ void PresShell::EnsureReflowIfFrameHasHiddenContent(nsIFrame* aFrame) { bool PresShell::IsForcingLayoutForHiddenContent(const nsIFrame* aFrame) const { return mHiddenContentInForcedLayout.Contains(aFrame->GetContent()); } + +void PresShell::UpdateRelevancyOfContentVisibilityAutoFrames() { + if (mContentVisibilityRelevancyToUpdate.isEmpty()) { + return; + } + + for (nsIFrame* frame : mContentVisibilityAutoFrames) { + frame->UpdateIsRelevantContent(mContentVisibilityRelevancyToUpdate); + } + + mContentVisibilityRelevancyToUpdate.clear(); +} + +void PresShell::ScheduleContentRelevancyUpdate(ContentRelevancyReason aReason) { + if (MOZ_UNLIKELY(mIsDestroying)) { + return; + } + + mContentVisibilityRelevancyToUpdate += aReason; + + SetNeedLayoutFlush(); + if (nsPresContext* presContext = GetPresContext()) { + presContext->RefreshDriver()->EnsureContentRelevancyUpdateHappens(); + } +} diff --git a/layout/base/PresShell.h b/layout/base/PresShell.h index 992a2fb29336..1c8d582342fd 100644 --- a/layout/base/PresShell.h +++ b/layout/base/PresShell.h @@ -13,6 +13,7 @@ #include // for FILE definition #include "FrameMetrics.h" +#include "LayoutConstants.h" #include "TouchManager.h" #include "Units.h" #include "Visibility.h" @@ -1749,6 +1750,13 @@ class PresShell final : public nsStubDocumentObserver, */ bool IsForcingLayoutForHiddenContent(const nsIFrame*) const; + void RegisterContentVisibilityAutoFrame(nsIFrame* aFrame) { + mContentVisibilityAutoFrames.Insert(aFrame); + } + + void UpdateRelevancyOfContentVisibilityAutoFrames(); + void ScheduleContentRelevancyUpdate(ContentRelevancyReason aReason); + private: ~PresShell(); @@ -3009,6 +3017,12 @@ class PresShell final : public nsStubDocumentObserver, nsTHashSet mHiddenContentInForcedLayout; + nsTHashSet mContentVisibilityAutoFrames; + + // The type of content relevancy to update the next time content relevancy + // updates are triggered for `content-visibility: auto` frames. + ContentRelevancy mContentVisibilityRelevancyToUpdate; + nsCallbackEventRequest* mFirstCallbackEventRequest = nullptr; nsCallbackEventRequest* mLastCallbackEventRequest = nullptr; diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 5e10d5e7edda..2f278e0ed4ac 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -1339,6 +1339,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext) mResizeSuppressed(false), mNotifyDOMContentFlushed(false), mNeedToUpdateIntersectionObservations(false), + mNeedToUpdateContentRelevancy(false), mInNormalTick(false), mAttemptedExtraTickSinceLastVsync(false), mHasExceededAfterLoadTickPeriod(false), @@ -1938,6 +1939,9 @@ auto nsRefreshDriver::GetReasonsToTick() const -> TickReasons { if (mNeedToUpdateIntersectionObservations) { reasons |= TickReasons::eNeedsToUpdateIntersectionObservations; } + if (mNeedToUpdateContentRelevancy) { + reasons |= TickReasons::eNeedsToUpdateContentRelevancy; + } if (!mVisualViewportResizeEvents.IsEmpty()) { reasons |= TickReasons::eHasVisualViewportResizeEvents; } @@ -1968,6 +1972,9 @@ void nsRefreshDriver::AppendTickReasonsToString(TickReasons aReasons, if (aReasons & TickReasons::eNeedsToUpdateIntersectionObservations) { aStr.AppendLiteral(" NeedsToUpdateIntersectionObservations"); } + if (aReasons & TickReasons::eNeedsToUpdateContentRelevancy) { + aStr.AppendLiteral(" NeedsToUpdateContentRelevancy"); + } if (aReasons & TickReasons::eHasVisualViewportResizeEvents) { aStr.AppendLiteral(" HasVisualViewportResizeEvents"); } @@ -2165,6 +2172,25 @@ void nsRefreshDriver::UpdateIntersectionObservations(TimeStamp aNowTime) { mNeedToUpdateIntersectionObservations = false; } +void nsRefreshDriver::UpdateRelevancyOfContentVisibilityAutoFrames() { + if (!mNeedToUpdateContentRelevancy) { + return; + } + + if (RefPtr topLevelPresShell = mPresContext->GetPresShell()) { + topLevelPresShell->UpdateRelevancyOfContentVisibilityAutoFrames(); + } + + mPresContext->Document()->EnumerateSubDocuments([](Document& aSubDoc) { + if (PresShell* presShell = aSubDoc.GetPresShell()) { + presShell->UpdateRelevancyOfContentVisibilityAutoFrames(); + } + return CallState::Continue; + }); + + mNeedToUpdateContentRelevancy = false; +} + void nsRefreshDriver::DispatchAnimationEvents() { if (!mPresContext) { return; @@ -2649,6 +2675,14 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime, pm->UpdatePopupPositions(this); } + // Update the relevancy of the content of any `content-visibility: auto` + // elements. The specification says: "Specifically, such changes will + // take effect between steps 13 and 14 of Update the Rendering step of + // the Processing Model (between “run the animation frame callbacks” and + // “run the update intersection observations steps”)." + // https://drafts.csswg.org/css-contain/#cv-notes + UpdateRelevancyOfContentVisibilityAutoFrames(); + UpdateIntersectionObservations(aNowTime); /* diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index 4d9af9f4530d..aabc94a04836 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -417,6 +417,11 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, mNeedToUpdateIntersectionObservations = true; } + void EnsureContentRelevancyUpdateHappens() { + EnsureTimerStarted(); + mNeedToUpdateContentRelevancy = true; + } + // Register a composition payload that will be forwarded to the layer manager // if the current or upcoming refresh tick does a paint. // If no paint happens, the payload is discarded. @@ -429,9 +434,10 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, eHasObservers = 1 << 0, eHasImageRequests = 1 << 1, eNeedsToUpdateIntersectionObservations = 1 << 2, - eHasVisualViewportResizeEvents = 1 << 3, - eHasScrollEvents = 1 << 4, - eHasVisualViewportScrollEvents = 1 << 5, + eNeedsToUpdateContentRelevancy = 1 << 3, + eHasVisualViewportResizeEvents = 1 << 4, + eHasScrollEvents = 1 << 5, + eHasVisualViewportScrollEvents = 1 << 6, }; void AddForceNotifyContentfulPaintPresContext(nsPresContext* aPresContext); @@ -473,6 +479,7 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, MOZ_CAN_RUN_SCRIPT void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime); void UpdateIntersectionObservations(mozilla::TimeStamp aNowTime); + void UpdateRelevancyOfContentVisibilityAutoFrames(); enum class IsExtraTick { No, @@ -611,6 +618,10 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, // all our documents. bool mNeedToUpdateIntersectionObservations : 1; + // True if we need to update the relevancy of `content-visibility: auto` + // elements in our documents. + bool mNeedToUpdateContentRelevancy : 1; + // True if we're currently within the scope of Tick() handling a normal // (timer-driven) tick. bool mInNormalTick : 1; diff --git a/layout/generic/ViewportFrame.cpp b/layout/generic/ViewportFrame.cpp index 95e134297b7d..475b944bedfd 100644 --- a/layout/generic/ViewportFrame.cpp +++ b/layout/generic/ViewportFrame.cpp @@ -177,7 +177,8 @@ nsDisplayWrapList* ViewportFrame::BuildDisplayListForTopLayer( continue; } - if (frame->IsHiddenByContentVisibilityOnAnyAncestor()) { + if (frame->IsHiddenByContentVisibilityOnAnyAncestor( + nsIFrame::IncludeContentVisibility::Hidden)) { continue; } diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index 857ff5683198..e94f4b6900d2 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -46,6 +46,7 @@ #include "nsCOMPtr.h" #include "nsFieldSetFrame.h" #include "nsFlexContainerFrame.h" +#include "nsFocusManager.h" #include "nsFrameList.h" #include "nsPlaceholderFrame.h" #include "nsIBaseWindow.h" @@ -381,7 +382,12 @@ bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const { return false; } - if (this != frame && frame->HidesContent()) { + // This method is used to determine if a frame is focusable, because it's + // called by nsIFrame::IsFocusable. `content-visibility: auto` should not + // force this frame to be unfocusable, so we only take into account + // `content-visibility: hidden` here. + if (this != frame && + frame->HidesContent(IncludeContentVisibility::Hidden)) { return false; } @@ -780,6 +786,16 @@ void nsIFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, UpdateVisibleDescendantsState(); } + if (disp->IsContentVisibilityAuto() && + IsContentVisibilityPropertyApplicable()) { + PresShell()->RegisterContentVisibilityAutoFrame(this); + auto* element = Element::FromNodeOrNull(GetContent()); + MOZ_ASSERT(element); + PresContext()->Document()->ObserveForContentVisibility(*element); + } else if (auto* element = Element::FromNodeOrNull(GetContent())) { + element->ClearContentRelevancy(); + } + // TODO(mrobinson): Once bug 1765615 is fixed, this should be called on // layout changes. In addition, when `content-visibility: auto` is implemented // this should also be called when scrolling or focus causes content to be @@ -855,6 +871,13 @@ void nsIFrame::DestroyFrom(nsIFrame* aDestructRoot, } } + if (disp->IsContentVisibilityAuto() && + IsContentVisibilityPropertyApplicable()) { + if (auto* element = Element::FromNodeOrNull(GetContent())) { + PresContext()->Document()->UnobserveForContentVisibility(*element); + } + } + // Disable visibility tracking. Note that we have to do this before we clear // frame properties and lose track of whether we were previously visible. // XXX(seth): It'd be ideal to assert that we're already marked nonvisible @@ -6832,12 +6855,53 @@ bool nsIFrame::IsContentDisabled() const { return element && element->IsDisabled(); } -bool nsIFrame::HidesContent() const { - if (!StyleDisplay()->IsContentVisibilityHidden()) { +bool nsIFrame::IsContentVisibilityPropertyApplicable() const { + return GetContent() && GetContent()->IsElement() && + (!StyleDisplay()->IsInlineFlow() || + IsFrameOfType(nsIFrame::eReplaced)); +} + +bool nsIFrame::IsContentRelevant() const { + MOZ_ASSERT(IsContentVisibilityPropertyApplicable()); + MOZ_ASSERT(StyleDisplay()->IsContentVisibilityAuto()); + + auto* element = Element::FromNodeOrNull(GetContent()); + MOZ_ASSERT(element); + + Maybe relevancy = element->GetContentRelevancy(); + if (relevancy.isSome()) { + return !relevancy->isEmpty(); + } + + // If there is no relevancy set, then this frame still has not received had + // the initial visibility callback call. In that case, only rely on whether + // or not it is inside a top layer element which will never change for this + // frame and allows proper rendering of the top layer. + return IsDescendantOfTopLayerElement(); +} + +bool nsIFrame::HidesContent( + const EnumSet& aInclude) const { + const auto& disp = *StyleDisplay(); + if (disp.IsContentVisibilityVisible()) { + return false; + }; + + if (!IsContentVisibilityPropertyApplicable()) { return false; } - return IsFrameOfType(nsIFrame::eReplaced) || !StyleDisplay()->IsInlineFlow(); + if (aInclude.contains(IncludeContentVisibility::Hidden) && + disp.IsContentVisibilityHidden()) { + return true; + } + + if (aInclude.contains(IncludeContentVisibility::Auto) && + disp.IsContentVisibilityAuto()) { + return !IsContentRelevant(); + } + + return false; } bool nsIFrame::HidesContentForLayout() const { @@ -6850,7 +6914,8 @@ bool nsIFrame::IsHiddenByContentVisibilityOfInFlowParentForLayout() const { !(Style()->IsAnonBox() && !IsFrameOfType(nsIFrame::eLineParticipant)); } -bool nsIFrame::IsHiddenByContentVisibilityOnAnyAncestor() const { +bool nsIFrame::IsHiddenByContentVisibilityOnAnyAncestor( + const EnumSet& aInclude) const { if (!StaticPrefs::layout_css_content_visibility_enabled()) { return false; } @@ -6858,7 +6923,7 @@ bool nsIFrame::IsHiddenByContentVisibilityOnAnyAncestor() const { bool isAnonymousBlock = Style()->IsAnonBox() && !IsFrameOfType(nsIFrame::eLineParticipant); for (nsIFrame* cur = GetInFlowParent(); cur; cur = cur->GetInFlowParent()) { - if (!isAnonymousBlock && cur->HidesContent()) { + if (!isAnonymousBlock && cur->HidesContent(aInclude)) { return true; } @@ -6871,6 +6936,111 @@ bool nsIFrame::IsHiddenByContentVisibilityOnAnyAncestor() const { return false; } +bool nsIFrame::HasSelectionInSubtree() { + if (IsSelected()) { + return true; + } + + RefPtr frameSelection = GetFrameSelection(); + const Selection* selection = + frameSelection->GetSelection(SelectionType::eNormal); + if (!selection) { + return false; + } + + for (uint32_t i = 0; i < selection->RangeCount(); i++) { + auto* range = selection->GetRangeAt(i); + MOZ_ASSERT(range); + + const auto* commonAncestorNode = + range->GetRegisteredClosestCommonInclusiveAncestor(); + if (commonAncestorNode->IsInclusiveDescendantOf(GetContent())) { + return true; + } + } + + return false; +} + +bool nsIFrame::IsDescendantOfTopLayerElement() const { + if (!GetContent()) { + return false; + } + + nsTArray topLayer = PresContext()->Document()->GetTopLayer(); + for (auto* element : topLayer) { + if (GetContent()->IsInclusiveFlatTreeDescendantOf(element)) { + return true; + } + } + + return false; +} + +void nsIFrame::UpdateIsRelevantContent( + const ContentRelevancy& aRelevancyToUpdate) { + MOZ_ASSERT(IsContentVisibilityPropertyApplicable()); + MOZ_ASSERT(StyleDisplay()->IsContentVisibilityAuto()); + + auto* element = Element::FromNodeOrNull(GetContent()); + MOZ_ASSERT(element); + + ContentRelevancy newRelevancy; + Maybe oldRelevancy = element->GetContentRelevancy(); + if (oldRelevancy.isSome()) { + newRelevancy = *oldRelevancy; + } + + auto setRelevancyValue = [&](ContentRelevancyReason reason, bool value) { + if (value) { + newRelevancy += reason; + } else { + newRelevancy -= reason; + } + }; + + if (!oldRelevancy || + aRelevancyToUpdate.contains(ContentRelevancyReason::Visible)) { + Maybe visible = element->GetVisibleForContentVisibility(); + if (visible.isSome()) { + setRelevancyValue(ContentRelevancyReason::Visible, *visible); + } + } + + if (!oldRelevancy || + aRelevancyToUpdate.contains(ContentRelevancyReason::FocusInSubtree)) { + setRelevancyValue(ContentRelevancyReason::FocusInSubtree, + element->State().HasAtLeastOneOfStates( + ElementState::FOCUS_WITHIN | ElementState::FOCUS)); + } + + if (!oldRelevancy || + aRelevancyToUpdate.contains(ContentRelevancyReason::Selected)) { + setRelevancyValue(ContentRelevancyReason::Selected, + HasSelectionInSubtree()); + } + + if (!oldRelevancy || + aRelevancyToUpdate.contains( + ContentRelevancyReason::DescendantOfTopLayerElement)) { + setRelevancyValue(ContentRelevancyReason::DescendantOfTopLayerElement, + IsDescendantOfTopLayerElement()); + } + + bool overallRelevancyChanged = + !oldRelevancy || oldRelevancy->isEmpty() != newRelevancy.isEmpty(); + if (!oldRelevancy || *oldRelevancy != newRelevancy) { + element->SetContentRelevancy(newRelevancy); + } + + if (overallRelevancyChanged) { + HandleLastRememberedSize(); + PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange, + NS_FRAME_IS_DIRTY); + InvalidateFrame(); + } +} + nsresult nsIFrame::CharacterDataChanged(const CharacterDataChangeInfo&) { MOZ_ASSERT_UNREACHABLE("should only be called for text frames"); return NS_OK; diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index bc4de570467b..d34d1d64a58a 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -536,6 +536,7 @@ class nsIFrame : public nsQueryFrame { using ReflowOutput = mozilla::ReflowOutput; using Visibility = mozilla::Visibility; using LengthPercentage = mozilla::LengthPercentage; + using ContentRelevancy = mozilla::ContentRelevancy; using nsDisplayItem = mozilla::nsDisplayItem; using nsDisplayList = mozilla::nsDisplayList; @@ -3180,11 +3181,34 @@ class nsIFrame : public nsQueryFrame { */ bool IsContentDisabled() const; + /** + * Whether the content-visibility CSS property applies to this frame. + */ + bool IsContentVisibilityPropertyApplicable() const; + + enum class IncludeContentVisibility { + Auto, + Hidden, + }; + + constexpr static mozilla::EnumSet + IncludeAllContentVisibility() { + return {IncludeContentVisibility::Auto, IncludeContentVisibility::Hidden}; + } + + /** + * Returns true if this frame's `content-visibility: auto` element is + * considered relevant content. + */ + bool IsContentRelevant() const; + /** * Whether this frame hides its contents via the `content-visibility` * property. + * @param aInclude specifies what kind of `content-visibility` to include. */ - bool HidesContent() const; + bool HidesContent(const mozilla::EnumSet& = + IncludeAllContentVisibility()) const; /** * Whether this frame hides its contents via the `content-visibility` @@ -3197,8 +3221,11 @@ class nsIFrame : public nsQueryFrame { /** * Returns true if this frame is entirely hidden due the `content-visibility` * property on an ancestor. + * @param aInclude specifies what kind of `content-visibility` to include. */ - bool IsHiddenByContentVisibilityOnAnyAncestor() const; + bool IsHiddenByContentVisibilityOnAnyAncestor( + const mozilla::EnumSet& = + IncludeAllContentVisibility()) const; /** * Returns true is this frame is hidden by its first unskipped in flow @@ -3206,6 +3233,27 @@ class nsIFrame : public nsQueryFrame { */ bool IsHiddenByContentVisibilityOfInFlowParentForLayout() const; + /** + * Whether or not this frame's content is a descendant of a top layer element + * used to determine if this frame is relevant content for + * `content-visibility: auto`. + */ + bool IsDescendantOfTopLayerElement() const; + + /** + * Returns true if this frame has a SelectionType::eNormal type selection in + * somewhere in its subtree of frames. This is used to determine content + * relevancy for `content-visibility: auto`. + */ + bool HasSelectionInSubtree(); + + /** + * Update the whether or not this frame is considered relevant content for the + * purposes of `content-visibility: auto` according to the rules specified in + * https://drafts.csswg.org/css-contain-2/#relevant-to-the-user. + */ + void UpdateIsRelevantContent(const ContentRelevancy& aRelevancyToUpdate); + /** * Get the "type" of the frame. * diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index e943fee81b5c..f9ec4319a6f5 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1612,6 +1612,10 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay { return mContentVisibility == StyleContentVisibility::Hidden; } + bool IsContentVisibilityAuto() const { + return mContentVisibility == StyleContentVisibility::Auto; + } + /* Returns whether the element has the transform property or a related * property. */ bool HasTransformStyle() const { diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index b1f478bd91c7..06bf47864d87 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -8655,6 +8655,12 @@ value: true mirror: always +# The margin used for detecting relevancy for `content-visibility: auto`. +- name: layout.css.content-visibility-relevant-content-margin + type: float + value: 50 # 50% + mirror: always + # Whether to block large cursors intersecting UI. - name: layout.cursor.block.enabled type: bool diff --git a/testing/web-platform/meta/css/css-contain/__dir__.ini b/testing/web-platform/meta/css/css-contain/__dir__.ini index d828a91efd16..4bc61b00f24e 100644 --- a/testing/web-platform/meta/css/css-contain/__dir__.ini +++ b/testing/web-platform/meta/css/css-contain/__dir__.ini @@ -1,2 +1,2 @@ -prefs: [layout.css.content-visibility.enabled:true] +prefs: [layout.css.content-visibility.enabled:true, layout.css.contain-intrinsic-size.enabled:true] leak-threshold: [rdd:51200] diff --git a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-058.html.ini b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-058.html.ini new file mode 100644 index 000000000000..0f634857b51e --- /dev/null +++ b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-058.html.ini @@ -0,0 +1,3 @@ +[content-visibility-058.html] + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1797467 + expected: FAIL diff --git a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-064.html.ini b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-064.html.ini index 7b1e2b6e3555..0100d6fd864a 100644 --- a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-064.html.ini +++ b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-064.html.ini @@ -1,3 +1,3 @@ [content-visibility-064.html] - fuzzy: - if os == "mac": maxDifference=0-11;totalPixels=0-44 + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1800868 + expected: FAIL diff --git a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-068.html.ini b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-068.html.ini index 3a753f59dbb7..fedc6eb1ee78 100644 --- a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-068.html.ini +++ b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-068.html.ini @@ -2,4 +2,5 @@ expected: if (os == "android") and fission: [OK, TIMEOUT] [Content Visibility: off-screen focus] + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1797447 expected: FAIL diff --git a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-069.html.ini b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-069.html.ini deleted file mode 100644 index be5eafa0a150..000000000000 --- a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-069.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[content-visibility-069.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] diff --git a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-070.html.ini b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-070.html.ini deleted file mode 100644 index 9071ecaf135c..000000000000 --- a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-070.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[content-visibility-070.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [Content Visibility: off-screen selection] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-071.html.ini b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-071.html.ini deleted file mode 100644 index 1bd7702bdfd6..000000000000 --- a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-071.html.ini +++ /dev/null @@ -1,20 +0,0 @@ -[content-visibility-071.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [Range flipped from back to front: ] - expected: FAIL - - [Range goes back and forth: ] - expected: FAIL - - [Range flipped from front to back: ] - expected: FAIL - - [Range shrunk to cover fewer elements: ] - expected: FAIL - - [Range extended to cover more elements: ] - expected: FAIL - - [One elements selected: ] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-072.html.ini b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-072.html.ini deleted file mode 100644 index 6cf1cea75d48..000000000000 --- a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-072.html.ini +++ /dev/null @@ -1,12 +0,0 @@ -[content-visibility-072.html] - [two.getBoundingClientRect(): ] - expected: FAIL - - [three.getBoundingClientRect(): ] - expected: FAIL - - [four.getBoundingClientRect(): ] - expected: FAIL - - [five.getBoundingClientRect(): ] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-with-top-layer-005.html.ini b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-with-top-layer-005.html.ini new file mode 100644 index 000000000000..f94a157f54dc --- /dev/null +++ b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-with-top-layer-005.html.ini @@ -0,0 +1,3 @@ +[content-visibility-with-top-layer-005.html] + fuzzy: + if os == "win": maxDifference=0-92;totalPixels=0-2 diff --git a/testing/web-platform/meta/css/css-sizing/contain-intrinsic-size/auto-003.html.ini b/testing/web-platform/meta/css/css-sizing/contain-intrinsic-size/auto-003.html.ini deleted file mode 100644 index 605e849d475d..000000000000 --- a/testing/web-platform/meta/css/css-sizing/contain-intrinsic-size/auto-003.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[auto-003.html] - expected: - if (os == "android") and fission: [OK, TIMEOUT] - [contain-intrinsic-size: auto] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-sizing/contain-intrinsic-size/auto-012.html.ini b/testing/web-platform/meta/css/css-sizing/contain-intrinsic-size/auto-012.html.ini deleted file mode 100644 index 1999d9b8b077..000000000000 --- a/testing/web-platform/meta/css/css-sizing/contain-intrinsic-size/auto-012.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[auto-012.html] - [Still sizing with last remembered size] - expected: FAIL - [Sizing with new last remembered size] - expected: FAIL diff --git a/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-070.html b/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-070.html index 4f63069edde4..a41b513e6269 100644 --- a/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-070.html +++ b/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-070.html @@ -59,8 +59,8 @@ async_test((t) => { function step2() { const r = container.getBoundingClientRect(); t.step(() => { - assert_equals(r.y, 3000, "step3 offset"); - assert_equals(r.height, 10, "step3 height"); + assert_equals(r.y, 3000, "step2 offset"); + assert_equals(r.height, 10, "step2 height"); }); document.scrollingElement.scrollTop = 3000; requestAnimationFrame(step3); @@ -69,8 +69,8 @@ async_test((t) => { function step3() { const r = container.getBoundingClientRect(); t.step(() => { - assert_less_than(r.y, 3000, "step2 offset"); - assert_equals(r.height, 10, "step2 height"); + assert_less_than(r.y, 3000, "step3 offset"); + assert_equals(r.height, 10, "step3 height"); }); document.scrollingElement.scrollTop = 0; requestAnimationFrame(step4); @@ -88,8 +88,8 @@ async_test((t) => { function step5() { const r = container.getBoundingClientRect(); t.step(() => { - assert_equals(r.y, 3000, "step4 offset"); - assert_equals(r.height, 10, "step4 height"); + assert_equals(r.y, 3000, "step5 offset"); + assert_equals(r.height, 10, "step5 height"); }); selection.removeAllRanges(); diff --git a/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-auto-in-iframe-ref.html b/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-auto-in-iframe-ref.html new file mode 100644 index 000000000000..03a2875b75a7 --- /dev/null +++ b/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-auto-in-iframe-ref.html @@ -0,0 +1,25 @@ + + + +CSS Content Visibility: auto container in an iframe (reference) + + + + + diff --git a/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-auto-in-iframe.html b/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-auto-in-iframe.html new file mode 100644 index 000000000000..ba02cac6684d --- /dev/null +++ b/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-auto-in-iframe.html @@ -0,0 +1,28 @@ + + + +CSS Content Visibility: auto container in an iframe + + + + + + + diff --git a/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-auto-intrinsic-width.html b/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-auto-intrinsic-width.html new file mode 100644 index 000000000000..bd90fff5143e --- /dev/null +++ b/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-auto-intrinsic-width.html @@ -0,0 +1,25 @@ + + + +Content Visibility: Elements with content-visibility: auto and intrinsic width should render correctly + + + + + + + +
+
+
diff --git a/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-with-top-layer-005.html b/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-with-top-layer-005.html new file mode 100644 index 000000000000..5283aea19734 --- /dev/null +++ b/testing/web-platform/tests/css/css-contain/content-visibility/content-visibility-with-top-layer-005.html @@ -0,0 +1,31 @@ + + + +CSS Content Visibility: dialog shows under c-v auto + + + + + + + + + +
+
+content +
PASS
+
+ + diff --git a/toolkit/components/find/nsFind.cpp b/toolkit/components/find/nsFind.cpp index 27d4b9775480..3328db506330 100644 --- a/toolkit/components/find/nsFind.cpp +++ b/toolkit/components/find/nsFind.cpp @@ -143,8 +143,9 @@ static bool IsVisibleNode(const nsINode* aNode) { return true; } - if (frame->HidesContent() || - frame->IsHiddenByContentVisibilityOnAnyAncestor()) { + if (frame->HidesContent(nsIFrame::IncludeContentVisibility::Hidden) || + frame->IsHiddenByContentVisibilityOnAnyAncestor( + nsIFrame::IncludeContentVisibility::Hidden)) { return false; }