Bug 1617154 - [intersection-observer] Accept a Document as an explicit root. r=emilio,bzbarsky

This patch allows users to specify a Document as an explicit root of an
intersection observer. For rationale, see the intent-to thread:
https://groups.google.com/forum/#!topic/mozilla.dev.platform/64nDLTAZGzY

It is implemented under a preference option enabled in Nightly:
dom.IntersectionObserverExplicitDocumentRoot.enabled
When disabled, the current TypeError exception is preserved so that
people can continue to feature detect the support.

The enhancement is tested by and enabled for the following test:
https://w3c-test.org/intersection-observer/document-scrolling-element-root.html

Differential Revision: https://phabricator.services.mozilla.com/D63766

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Frederic Wang 2020-03-03 12:25:30 +00:00
parent 41606ddbd6
commit f70d494fe7
6 changed files with 47 additions and 27 deletions

View File

@ -12,6 +12,7 @@
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/DocumentInlines.h"
@ -91,7 +92,20 @@ already_AddRefed<DOMIntersectionObserver> DOMIntersectionObserver::Constructor(
RefPtr<DOMIntersectionObserver> 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());
if (!StaticPrefs::
dom_IntersectionObserverExplicitDocumentRoot_enabled()) {
aRv.ThrowTypeError<dom::MSG_DOES_NOT_IMPLEMENT_INTERFACE>(
u"'root' member of IntersectionObserverInit", u"Element");
return nullptr;
}
observer->mRoot = aOptions.mRoot.Value().GetAsDocument();
}
}
if (!observer->SetRootMargin(aOptions.mRootMargin)) {
aRv.ThrowSyntaxError("rootMargin must be specified in pixels or percent.");
@ -257,7 +271,7 @@ enum class BrowsingContextOrigin { Similar, Different, Unknown };
// contexts" is gone, but this is still in the spec, see
// https://github.com/w3c/IntersectionObserver/issues/161
static BrowsingContextOrigin SimilarOrigin(const Element& aTarget,
const Element* aRoot) {
const nsINode* aRoot) {
if (!aRoot) {
return BrowsingContextOrigin::Unknown;
}
@ -466,10 +480,10 @@ void DOMIntersectionObserver::Update(Document* aDocument,
// document.
nsRect rootRect;
nsIFrame* rootFrame = nullptr;
Element* root = mRoot;
nsINode* root = mRoot;
Maybe<nsRect> remoteDocumentVisibleRect;
if (mRoot) {
if ((rootFrame = mRoot->GetPrimaryFrame())) {
if (mRoot && mRoot->IsElement()) {
if ((rootFrame = mRoot->AsElement()->GetPrimaryFrame())) {
nsRect rootRectRelativeToRootFrame;
if (rootFrame->IsScrollFrame()) {
// rootRectRelativeToRootFrame should be the content rect of rootFrame,
@ -485,21 +499,26 @@ void DOMIntersectionObserver::Update(Document* aDocument,
rootRect = nsLayoutUtils::TransformFrameRectToAncestor(
rootFrame, rootRectRelativeToRootFrame, containingBlock);
}
} else if (Document* topLevelDocument = GetTopLevelDocument(*aDocument)) {
if (PresShell* presShell = topLevelDocument->GetPresShell()) {
rootFrame = presShell->GetRootScrollFrame();
if (rootFrame) {
root = rootFrame->GetContent()->AsElement();
nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame);
rootRect = scrollFrame->GetScrollPortRect();
} else {
MOZ_ASSERT(!mRoot || mRoot->IsDocument());
Document* rootDocument =
mRoot ? mRoot->AsDocument() : GetTopLevelDocument(*aDocument);
if (rootDocument) {
if (PresShell* presShell = rootDocument->GetPresShell()) {
rootFrame = presShell->GetRootScrollFrame();
if (rootFrame) {
root = rootFrame->GetContent()->AsElement();
nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame);
rootRect = scrollFrame->GetScrollPortRect();
}
}
} else if (Maybe<OopIframeMetrics> metrics =
GetOopIframeMetrics(*aDocument)) {
// `implicit root` case in an out-of-process iframe.
rootFrame = metrics->mInProcessRootFrame;
rootRect = metrics->mInProcessRootRect;
remoteDocumentVisibleRect = Some(metrics->mRemoteDocumentVisibleRect);
}
} else if (Maybe<OopIframeMetrics> metrics =
GetOopIframeMetrics(*aDocument)) {
// `implicit root` case in an out-of-process iframe.
rootFrame = metrics->mInProcessRootFrame;
rootRect = metrics->mInProcessRootRect;
remoteDocumentVisibleRect = Some(metrics->mRemoteDocumentVisibleRect);
}
nsMargin rootMargin; // This root margin is NOT applied in `implicit root`

View File

@ -118,7 +118,7 @@ class DOMIntersectionObserver final : public nsISupports,
nsISupports* GetParentObject() const { return mOwner; }
Element* GetRoot() const { return mRoot; }
nsINode* GetRoot() const { return mRoot; }
void GetRootMargin(DOMString& aRetVal);
void GetThresholds(nsTArray<double>& aRetVal);
@ -151,7 +151,7 @@ class DOMIntersectionObserver final : public nsISupports,
RefPtr<Document> mDocument;
Variant<RefPtr<dom::IntersectionCallback>, NativeIntersectionObserverCallback>
mCallback;
RefPtr<Element> mRoot;
RefPtr<nsINode> mRoot;
StyleRect<LengthPercentage> mRootMargin;
nsTArray<double> mThresholds;

View File

@ -34,7 +34,7 @@ interface IntersectionObserver {
optional IntersectionObserverInit options = {});
[Constant]
readonly attribute Element? root;
readonly attribute Node? root;
[Constant]
readonly attribute DOMString rootMargin;
[Constant,Cached]
@ -57,7 +57,7 @@ dictionary IntersectionObserverEntryInit {
};
dictionary IntersectionObserverInit {
Element? root = null;
(Element or Document)? root = null;
DOMString rootMargin = "0px";
(double or sequence<double>) threshold = 0;
};

View File

@ -1749,6 +1749,11 @@
value: true
mirror: always
- name: dom.IntersectionObserverExplicitDocumentRoot.enabled
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: always
- name: dom.ipc.cancel_content_js_when_navigating
type: bool
value: true

View File

@ -1 +1 @@
prefs: [dom.IntersectionObserver.enabled:true]
prefs: [dom.IntersectionObserver.enabled:true, dom.IntersectionObserverExplicitDocumentRoot.enabled:true]

View File

@ -1,4 +0,0 @@
[document-scrolling-element-root.html]
[Observer with explicit root which is the document.]
expected: FAIL