Title: [257976] trunk
Revision
257976
Author
[email protected]
Date
2020-03-06 03:53:49 -0800 (Fri, 06 Mar 2020)

Log Message

[intersection-observer] Accept a Document as an explicit root
https://bugs.webkit.org/show_bug.cgi?id=208047

Patch by Frederic Wang <[email protected]> on 2020-03-06
Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

* web-platform-tests/intersection-observer/document-scrolling-element-root-expected.txt:
Update expectation now that the test passes.

Source/WebCore:

This patch introduces a recent enhancement to the Intersection Observer specification: the
root initialization parameter can be explicitly be set to a Document. The typical use case
is when document is an iframe. See https://github.com/w3c/IntersectionObserver/issues/372

This patch also updates the way Element's intersection observer data is handled so that it is
more consistent with the explicit Document root case introduced here.

Test: imported/w3c/web-platform-tests/intersection-observer/document-scrolling-element-root.html

* dom/Document.cpp:
(WebCore::Document::~Document): Notify observers about our desctruction.
(WebCore::Document::updateIntersectionObservations): Use new method name. This does not
require null-check because ensureIntersectionObserverData() has been called in
IntersectionObserver::observe().
(WebCore::Document::ensureIntersectionObserverData): Return reference to intersection
observer data for this document, creating one if it does not exist.
* dom/Document.h: Add new intersection observer data, used for documents that are explicit
intersection observer roots.
(WebCore::Document::intersectionObserverDataIfExists): Return pointer to intersection
observer data or null if it does not exist.
* dom/Element.cpp:
(WebCore::Element::didMoveToNewDocument): Use new method name.
(WebCore::Element::disconnectFromIntersectionObservers): Ditto and null-check weak refs.
(WebCore::Element::intersectionObserverDataIfExists): Rename method to match Document's one
and be more explicit that it will be null if it does not exist.
(WebCore::Element::intersectionObserverData): Renamed.
* dom/Element.h: Renamed.
* html/LazyLoadImageObserver.cpp:
(WebCore::LazyLoadImageObserver::intersectionObserver): Initialize with a WTF::Optional
after API change.
* page/IntersectionObserver.cpp:
(WebCore::IntersectionObserver::create): Pass a Node* root, which can be null (implicit
root), Document* or Element* (explicit roots). This is determined from init.root.
(WebCore::IntersectionObserver::IntersectionObserver): Handle the case of explicit Document
root.
(WebCore::IntersectionObserver::~IntersectionObserver): Ditto and update method name for
the explicit Element case. Note that in both explicit root cases the corresponding
ensureIntersectionObserverData() method had been called in the constructor so they can
be safely deferenced.
(WebCore::IntersectionObserver::removeTargetRegistration): Use new method name.
* page/IntersectionObserver.h: Update comment and code now that explicit root is a Node* and
IntersectionObserver::Init::root is either an Element or a Document or null.
(WebCore::IntersectionObserver::root const): Ditto.
(): Deleted.
* page/IntersectionObserver.idl: Update IDL to match the spec IntersectionObserver::root
is a nullable Node and IntersectionObserverInit::root a nullable Element or Document.

Modified Paths

Diff

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (257975 => 257976)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2020-03-06 11:53:49 UTC (rev 257976)
@@ -1,3 +1,13 @@
+2020-03-06  Frederic Wang  <[email protected]>
+
+        [intersection-observer] Accept a Document as an explicit root
+        https://bugs.webkit.org/show_bug.cgi?id=208047
+
+        Reviewed by Simon Fraser.
+
+        * web-platform-tests/intersection-observer/document-scrolling-element-root-expected.txt:
+        Update expectation now that the test passes.
+
 2020-03-02  Rob Buis  <[email protected]>
 
         Fix behavior of pings regarding Origin header

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/document-scrolling-element-root-expected.txt (257975 => 257976)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/document-scrolling-element-root-expected.txt	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/document-scrolling-element-root-expected.txt	2020-03-06 11:53:49 UTC (rev 257976)
@@ -1,3 +1,4 @@
 
-FAIL Observer with explicit root which is the document. Type error
+PASS Observer with explicit root which is the document. 
+PASS First rAF. 
 

Modified: trunk/Source/WebCore/ChangeLog (257975 => 257976)


--- trunk/Source/WebCore/ChangeLog	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/Source/WebCore/ChangeLog	2020-03-06 11:53:49 UTC (rev 257976)
@@ -1,3 +1,57 @@
+2020-03-06  Frederic Wang  <[email protected]>
+
+        [intersection-observer] Accept a Document as an explicit root
+        https://bugs.webkit.org/show_bug.cgi?id=208047
+
+        Reviewed by Simon Fraser.
+
+        This patch introduces a recent enhancement to the Intersection Observer specification: the
+        root initialization parameter can be explicitly be set to a Document. The typical use case
+        is when document is an iframe. See https://github.com/w3c/IntersectionObserver/issues/372
+
+        This patch also updates the way Element's intersection observer data is handled so that it is
+        more consistent with the explicit Document root case introduced here.
+
+        Test: imported/w3c/web-platform-tests/intersection-observer/document-scrolling-element-root.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::~Document): Notify observers about our desctruction.
+        (WebCore::Document::updateIntersectionObservations): Use new method name. This does not
+        require null-check because ensureIntersectionObserverData() has been called in
+        IntersectionObserver::observe().
+        (WebCore::Document::ensureIntersectionObserverData): Return reference to intersection
+        observer data for this document, creating one if it does not exist.
+        * dom/Document.h: Add new intersection observer data, used for documents that are explicit
+        intersection observer roots.
+        (WebCore::Document::intersectionObserverDataIfExists): Return pointer to intersection
+        observer data or null if it does not exist.
+        * dom/Element.cpp:
+        (WebCore::Element::didMoveToNewDocument): Use new method name.
+        (WebCore::Element::disconnectFromIntersectionObservers): Ditto and null-check weak refs.
+        (WebCore::Element::intersectionObserverDataIfExists): Rename method to match Document's one
+        and be more explicit that it will be null if it does not exist.
+        (WebCore::Element::intersectionObserverData): Renamed.
+        * dom/Element.h: Renamed.
+        * html/LazyLoadImageObserver.cpp:
+        (WebCore::LazyLoadImageObserver::intersectionObserver): Initialize with a WTF::Optional
+        after API change.
+        * page/IntersectionObserver.cpp:
+        (WebCore::IntersectionObserver::create): Pass a Node* root, which can be null (implicit
+        root), Document* or Element* (explicit roots). This is determined from init.root.
+        (WebCore::IntersectionObserver::IntersectionObserver): Handle the case of explicit Document
+        root.
+        (WebCore::IntersectionObserver::~IntersectionObserver): Ditto and update method name for
+        the explicit Element case. Note that in both explicit root cases the corresponding
+        ensureIntersectionObserverData() method had been called in the constructor so they can
+        be safely deferenced.
+        (WebCore::IntersectionObserver::removeTargetRegistration): Use new method name.
+        * page/IntersectionObserver.h: Update comment and code now that explicit root is a Node* and
+        IntersectionObserver::Init::root is either an Element or a Document or null.
+        (WebCore::IntersectionObserver::root const): Ditto.
+        (): Deleted.
+        * page/IntersectionObserver.idl: Update IDL to match the spec IntersectionObserver::root
+        is a nullable Node and IntersectionObserverInit::root a nullable Element or Document.
+
 2020-03-06  Yusuke Suzuki  <[email protected]>
 
         Put all generated JSCells in WebCore into IsoSubspace

Modified: trunk/Source/WebCore/dom/Document.cpp (257975 => 257976)


--- trunk/Source/WebCore/dom/Document.cpp	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/Source/WebCore/dom/Document.cpp	2020-03-06 11:53:49 UTC (rev 257976)
@@ -635,6 +635,18 @@
     if (m_logger)
         m_logger->removeObserver(*this);
 
+#if ENABLE(INTERSECTION_OBSERVER)
+    if (m_intersectionObserverData) {
+        for (const auto& observer : m_intersectionObserverData->observers) {
+            if (observer)
+                observer->rootDestroyed();
+        }
+        m_intersectionObserverData->observers.clear();
+        // Document cannot be a target.
+        ASSERT(m_intersectionObserverData->registrations.isEmpty());
+    }
+#endif
+
     ASSERT(allDocumentsMap().contains(m_identifier));
     allDocumentsMap().remove(m_identifier);
     // We need to remove from the contexts map very early in the destructor so that calling postTask() on this Document from another thread is safe.
@@ -7574,7 +7586,7 @@
         if (!observer->createTimestamp(timestamp))
             continue;
         for (Element* target : observer->observationTargets()) {
-            auto& targetRegistrations = target->intersectionObserverData()->registrations;
+            auto& targetRegistrations = target->intersectionObserverDataIfExists()->registrations;
             auto index = targetRegistrations.findMatching([observer](auto& registration) {
                 return registration.observer.get() == observer;
             });
@@ -7661,6 +7673,13 @@
         m_intersectionObserversInitialUpdateTimer.startOneShot(intersectionObserversInitialUpdateDelay);
 }
 
+IntersectionObserverData& Document::ensureIntersectionObserverData()
+{
+    if (!m_intersectionObserverData)
+        m_intersectionObserverData = makeUnique<IntersectionObserverData>();
+    return *m_intersectionObserverData;
+}
+
 #endif
 
 #if ENABLE(RESIZE_OBSERVER)

Modified: trunk/Source/WebCore/dom/Document.h (257975 => 257976)


--- trunk/Source/WebCore/dom/Document.h	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/Source/WebCore/dom/Document.h	2020-03-06 11:53:49 UTC (rev 257976)
@@ -252,6 +252,7 @@
 
 #if ENABLE(INTERSECTION_OBSERVER)
 class IntersectionObserver;
+struct IntersectionObserverData;
 #endif
 
 #if ENABLE(RESIZE_OBSERVER)
@@ -1389,6 +1390,8 @@
     unsigned numberOfIntersectionObservers() const { return m_intersectionObservers.size(); }
     void updateIntersectionObservations();
     void scheduleInitialIntersectionObservationUpdate();
+    IntersectionObserverData& ensureIntersectionObserverData();
+    IntersectionObserverData* intersectionObserverDataIfExists() { return m_intersectionObserverData.get(); }
 #endif
 
 #if ENABLE(RESIZE_OBSERVER)
@@ -1823,6 +1826,8 @@
     Vector<WeakPtr<IntersectionObserver>> m_intersectionObserversWithPendingNotifications;
     Timer m_intersectionObserversNotifyTimer;
     Timer m_intersectionObserversInitialUpdateTimer;
+    // This is only non-null when this document is an explicit root.
+    std::unique_ptr<IntersectionObserverData> m_intersectionObserverData;
 #endif
 
 #if ENABLE(RESIZE_OBSERVER)

Modified: trunk/Source/WebCore/dom/Element.cpp (257975 => 257976)


--- trunk/Source/WebCore/dom/Element.cpp	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/Source/WebCore/dom/Element.cpp	2020-03-06 11:53:49 UTC (rev 257976)
@@ -2065,7 +2065,7 @@
         CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded(*this, oldDocument, newDocument);
 
 #if ENABLE(INTERSECTION_OBSERVER)
-    if (auto* observerData = intersectionObserverData()) {
+    if (auto* observerData = intersectionObserverDataIfExists()) {
         for (const auto& observer : observerData->observers) {
             if (observer->hasObservationTargets()) {
                 oldDocument.removeIntersectionObserver(*observer);
@@ -3724,16 +3724,20 @@
 
 void Element::disconnectFromIntersectionObservers()
 {
-    auto* observerData = intersectionObserverData();
+    auto* observerData = intersectionObserverDataIfExists();
     if (!observerData)
         return;
 
-    for (const auto& registration : observerData->registrations)
-        registration.observer->targetDestroyed(*this);
+    for (const auto& registration : observerData->registrations) {
+        if (registration.observer)
+            registration.observer->targetDestroyed(*this);
+    }
     observerData->registrations.clear();
 
-    for (const auto& observer : observerData->observers)
-        observer->rootDestroyed();
+    for (const auto& observer : observerData->observers) {
+        if (observer)
+            observer->rootDestroyed();
+    }
     observerData->observers.clear();
 }
 
@@ -3745,7 +3749,7 @@
     return *rareData.intersectionObserverData();
 }
 
-IntersectionObserverData* Element::intersectionObserverData()
+IntersectionObserverData* Element::intersectionObserverDataIfExists()
 {
     return hasRareData() ? elementRareData()->intersectionObserverData() : nullptr;
 }

Modified: trunk/Source/WebCore/dom/Element.h (257975 => 257976)


--- trunk/Source/WebCore/dom/Element.h	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/Source/WebCore/dom/Element.h	2020-03-06 11:53:49 UTC (rev 257976)
@@ -591,7 +591,7 @@
 
 #if ENABLE(INTERSECTION_OBSERVER)
     IntersectionObserverData& ensureIntersectionObserverData();
-    IntersectionObserverData* intersectionObserverData();
+    IntersectionObserverData* intersectionObserverDataIfExists();
 #endif
 
 #if ENABLE(RESIZE_OBSERVER)

Modified: trunk/Source/WebCore/html/LazyLoadImageObserver.cpp (257975 => 257976)


--- trunk/Source/WebCore/html/LazyLoadImageObserver.cpp	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/Source/WebCore/html/LazyLoadImageObserver.cpp	2020-03-06 11:53:49 UTC (rev 257976)
@@ -85,7 +85,7 @@
 {
     if (!m_lazyLoadIntersectionObserver) {
         auto callback = LazyImageLoadIntersectionObserverCallback::create(document);
-        IntersectionObserver::Init options { nullptr, emptyString(), { } };
+        IntersectionObserver::Init options { WTF::nullopt, emptyString(), { } };
         auto observer = IntersectionObserver::create(document, WTFMove(callback), WTFMove(options));
         if (observer.hasException())
             return nullptr;

Modified: trunk/Source/WebCore/page/IntersectionObserver.cpp (257975 => 257976)


--- trunk/Source/WebCore/page/IntersectionObserver.cpp	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/Source/WebCore/page/IntersectionObserver.cpp	2020-03-06 11:53:49 UTC (rev 257976)
@@ -86,6 +86,15 @@
 
 ExceptionOr<Ref<IntersectionObserver>> IntersectionObserver::create(Document& document, Ref<IntersectionObserverCallback>&& callback, IntersectionObserver::Init&& init)
 {
+    Node* root = nullptr;
+    if (init.root) {
+        WTF::switchOn(*init.root, [&root] (RefPtr<Element> element) {
+            root = element.get();
+        }, [&root] (RefPtr<Document> document) {
+            root = document.get();
+        });
+    }
+
     auto rootMarginOrException = parseRootMargin(init.rootMargin);
     if (rootMarginOrException.hasException())
         return rootMarginOrException.releaseException();
@@ -103,10 +112,10 @@
             return Exception { RangeError, "Failed to construct 'IntersectionObserver': all thresholds must lie in the range [0.0, 1.0]." };
     }
 
-    return adoptRef(*new IntersectionObserver(document, WTFMove(callback), init.root, rootMarginOrException.releaseReturnValue(), WTFMove(thresholds)));
+    return adoptRef(*new IntersectionObserver(document, WTFMove(callback), root, rootMarginOrException.releaseReturnValue(), WTFMove(thresholds)));
 }
 
-IntersectionObserver::IntersectionObserver(Document& document, Ref<IntersectionObserverCallback>&& callback, Element* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds)
+IntersectionObserver::IntersectionObserver(Document& document, Ref<IntersectionObserverCallback>&& callback, Node* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds)
     : ActiveDOMObject(callback->scriptExecutionContext())
     , m_root(root)
     , m_rootMargin(WTFMove(parsedRootMargin))
@@ -113,9 +122,13 @@
     , m_thresholds(WTFMove(thresholds))
     , m_callback(WTFMove(callback))
 {
-    if (m_root) {
-        auto& observerData = m_root->ensureIntersectionObserverData();
+    if (is<Document>(m_root)) {
+        auto& observerData = downcast<Document>(*m_root).ensureIntersectionObserverData();
         observerData.observers.append(makeWeakPtr(this));
+    } else if (m_root) {
+        ASSERT(is<Element>(m_root));
+        auto& observerData = downcast<Element>(*m_root).ensureIntersectionObserverData();
+        observerData.observers.append(makeWeakPtr(this));
     } else if (auto* frame = document.frame())
         m_implicitRootDocument = makeWeakPtr(frame->mainFrame().document());
 
@@ -125,8 +138,12 @@
 
 IntersectionObserver::~IntersectionObserver()
 {
-    if (m_root)
-        m_root->intersectionObserverData()->observers.removeFirst(this);
+    if (is<Document>(m_root)) {
+        downcast<Document>(*m_root).intersectionObserverDataIfExists()->observers.removeFirst(this);
+    } else if (m_root) {
+        ASSERT(is<Element>(m_root));
+        downcast<Element>(*m_root).intersectionObserverDataIfExists()->observers.removeFirst(this);
+    }
     disconnect();
 }
 
@@ -201,7 +218,7 @@
 
 bool IntersectionObserver::removeTargetRegistration(Element& target)
 {
-    auto* observerData = target.intersectionObserverData();
+    auto* observerData = target.intersectionObserverDataIfExists();
     if (!observerData)
         return false;
 

Modified: trunk/Source/WebCore/page/IntersectionObserver.h (257975 => 257976)


--- trunk/Source/WebCore/page/IntersectionObserver.h	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/Source/WebCore/page/IntersectionObserver.h	2020-03-06 11:53:49 UTC (rev 257976)
@@ -41,6 +41,7 @@
 
 class Document;
 class Element;
+class Node;
 
 struct IntersectionObserverRegistration {
     WeakPtr<IntersectionObserver> observer;
@@ -50,12 +51,12 @@
 struct IntersectionObserverData {
     WTF_MAKE_STRUCT_FAST_ALLOCATED;
 
-    // IntersectionObservers for which the element that owns this IntersectionObserverData is the root.
+    // IntersectionObservers for which the node that owns this IntersectionObserverData is the root.
     // An IntersectionObserver is only owned by a _javascript_ wrapper. ActiveDOMObject::hasPendingActivity
     // is overridden to keep this wrapper alive while the observer has ongoing observations.
     Vector<WeakPtr<IntersectionObserver>> observers;
 
-    // IntersectionObserverRegistrations for which the element that owns this IntersectionObserverData is the target.
+    // IntersectionObserverRegistrations for which the node that owns this IntersectionObserverData is the target.
     Vector<IntersectionObserverRegistration> registrations;
 };
 
@@ -62,7 +63,7 @@
 class IntersectionObserver : public RefCounted<IntersectionObserver>, public ActiveDOMObject, public CanMakeWeakPtr<IntersectionObserver> {
 public:
     struct Init {
-        Element* root { nullptr };
+        Optional<Variant<RefPtr<Element>, RefPtr<Document>>> root;
         String rootMargin;
         Variant<double, Vector<double>> threshold;
     };
@@ -73,7 +74,7 @@
 
     Document* trackingDocument() const { return m_root ? &m_root->document() : m_implicitRootDocument.get(); }
 
-    Element* root() const { return m_root; }
+    Node* root() const { return m_root; }
     String rootMargin() const;
     const LengthBox& rootMarginBox() const { return m_rootMargin; }
     const Vector<double>& thresholds() const { return m_thresholds; }
@@ -104,13 +105,13 @@
     void stop() override;
 
 private:
-    IntersectionObserver(Document&, Ref<IntersectionObserverCallback>&&, Element* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds);
+    IntersectionObserver(Document&, Ref<IntersectionObserverCallback>&&, Node* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds);
 
     bool removeTargetRegistration(Element&);
     void removeAllTargets();
 
     WeakPtr<Document> m_implicitRootDocument;
-    Element* m_root;
+    Node* m_root;
     LengthBox m_rootMargin;
     Vector<double> m_thresholds;
     RefPtr<IntersectionObserverCallback> m_callback;

Modified: trunk/Source/WebCore/page/IntersectionObserver.idl (257975 => 257976)


--- trunk/Source/WebCore/page/IntersectionObserver.idl	2020-03-06 11:11:40 UTC (rev 257975)
+++ trunk/Source/WebCore/page/IntersectionObserver.idl	2020-03-06 11:53:49 UTC (rev 257976)
@@ -33,7 +33,7 @@
     Constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options),
     EnabledAtRuntime=IntersectionObserver
 ] interface IntersectionObserver {
-    readonly attribute Element? root;
+    readonly attribute Node? root;
     readonly attribute DOMString rootMargin;
     readonly attribute sequence<double> thresholds;
 
@@ -48,7 +48,7 @@
     EnabledBySetting=IntersectionObserver
 ]
 dictionary IntersectionObserverInit {
-    Element? root = null;
+    (Element or Document)? root = null;
     DOMString rootMargin = "0px";
     (double or sequence<double>) threshold = 0.0;
 };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to