From f44db6a28b77a2f17a7d2e0d929fe194f1b88b52 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 15 Jan 2024 05:54:48 -0600 Subject: [PATCH] Issue #2456 - Intersection Observer - Accept a Document as an explicit root. https://bugzilla.mozilla.org/show_bug.cgi?id=1617154 --- dom/base/DOMIntersectionObserver.cpp | 75 ++++++++++++++++---------- dom/base/DOMIntersectionObserver.h | 4 +- dom/webidl/IntersectionObserver.webidl | 4 +- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/dom/base/DOMIntersectionObserver.cpp b/dom/base/DOMIntersectionObserver.cpp index 7ee1a4c938..aaa1609b33 100644 --- a/dom/base/DOMIntersectionObserver.cpp +++ b/dom/base/DOMIntersectionObserver.cpp @@ -80,7 +80,14 @@ DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal, RefPtr observer = new DOMIntersectionObserver(window.forget(), aCb); - observer->mRoot = aOptions.mRoot; + if (!aOptions.mRoot.IsNull()) { + if (aOptions.mRoot.Value().IsElement()) { + observer->mRoot = aOptions.mRoot.Value().GetAsElement(); + } else { + MOZ_ASSERT(aOptions.mRoot.Value().IsDocument()); + observer->mRoot = aOptions.mRoot.Value().GetAsDocument(); + } + } if (!observer->SetRootMargin(aOptions.mRootMargin)) { aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR, @@ -258,6 +265,30 @@ EdgeInclusiveIntersection(const nsRect& aRect, const nsRect& aOtherRect) return Some(nsRect(left, top, right - left, bottom - top)); } +// NOTE: This returns nullptr if |aDocument| is in a cross process. +static nsIDocument* GetTopLevelDocument(const nsIDocument& aDocument) { + nsCOMPtr presShell = aDocument.GetShell(); + + if (presShell) { + nsIFrame* rootFrame = presShell->GetRootScrollFrame(); + if (rootFrame) { + nsPresContext* presContext = rootFrame->PresContext(); + while (!presContext->IsRootContentDocument()) { + // Walk up the tree + presContext = presContext->GetParentPresContext(); + if (!presContext) { + break; + } + } + if(presContext && presContext->IsRootContentDocument()) { + return presContext->Document(); + } + } + } + + return nullptr; +} + enum class BrowsingContextInfo { SimilarOriginBrowsingContext, DifferentOriginBrowsingContext, @@ -267,14 +298,12 @@ enum class BrowsingContextInfo { void DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time) { - Element* root = nullptr; + nsINode* root = mRoot; nsIFrame* rootFrame = nullptr; nsRect rootRect; - if (mRoot) { - root = mRoot; - rootFrame = root->GetPrimaryFrame(); - if (rootFrame) { + if (mRoot && mRoot->IsElement()) { + if ((rootFrame = mRoot->AsElement()->GetPrimaryFrame())) { nsRect rootRectRelativeToRootFrame; if (rootFrame->GetType() == nsGkAtoms::scrollFrame) { // rootRectRelativeToRootFrame should be the content rect of rootFrame, not including the scrollbars. @@ -292,30 +321,18 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time containingBlock); } } else { - nsCOMPtr presShell = aDocument->GetShell(); - if (presShell) { - rootFrame = presShell->GetRootScrollFrame(); - if (rootFrame) { - nsPresContext* presContext = rootFrame->PresContext(); - while (!presContext->IsRootContentDocument()) { - // Walk up the tree - presContext = presContext->GetParentPresContext(); - if (!presContext) { - break; + MOZ_ASSERT(!mRoot || mRoot->IsInUncomposedDoc()); + nsIDocument* rootDocument = + mRoot ? mRoot->GetUncomposedDoc() : GetTopLevelDocument(*aDocument); + if (rootDocument) { + if (nsIPresShell* presShell = rootDocument->GetShell()) { + rootFrame = presShell->GetRootScrollFrame(); + if (rootFrame) { + root = rootFrame->GetContent()->AsElement(); + nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame); + if (scrollFrame) { + rootRect = scrollFrame->GetScrollPortRect(); } - nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame(); - if (rootScrollFrame) { - rootFrame = rootScrollFrame; - } else { - break; - } - } - root = rootFrame->GetContent()->AsElement(); - nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame); - // If we end up with a null root frame for some reason, we'll proceed - // with an empty root intersection rect. - if (scrollFrame) { - rootRect = scrollFrame->GetScrollPortRect(); } } } diff --git a/dom/base/DOMIntersectionObserver.h b/dom/base/DOMIntersectionObserver.h index a72454007c..f309544978 100644 --- a/dom/base/DOMIntersectionObserver.h +++ b/dom/base/DOMIntersectionObserver.h @@ -145,7 +145,7 @@ public: return mOwner; } - Element* GetRoot() const { + nsINode* GetRoot() const { return mRoot; } @@ -178,7 +178,7 @@ protected: nsCOMPtr mOwner; RefPtr mDocument; RefPtr mCallback; - RefPtr mRoot; + RefPtr mRoot; nsCSSRect mRootMargin; nsTArray mThresholds; diff --git a/dom/webidl/IntersectionObserver.webidl b/dom/webidl/IntersectionObserver.webidl index 83200d950d..b5c904c139 100644 --- a/dom/webidl/IntersectionObserver.webidl +++ b/dom/webidl/IntersectionObserver.webidl @@ -30,7 +30,7 @@ interface IntersectionObserverEntry { Pref="dom.intersectionObserver.enabled"] interface IntersectionObserver { [Constant] - readonly attribute Element? root; + readonly attribute Node? root; [Constant] readonly attribute DOMString rootMargin; [Constant,Cached] @@ -56,7 +56,7 @@ dictionary IntersectionObserverEntryInit { }; dictionary IntersectionObserverInit { - Element? root = null; + (Element or Document)? root = null; DOMString rootMargin = "0px"; (double or sequence) threshold = 0; };