Changeset 257976 in webkit


Ignore:
Timestamp:
Mar 6, 2020 3:53:49 AM (4 years ago)
Author:
commit-queue@webkit.org
Message:

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

Patch by Frederic Wang <fwang@igalia.com> 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.

Location:
trunk
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r257729 r257976  
     12020-03-06  Frederic Wang  <fwang@igalia.com>
     2
     3        [intersection-observer] Accept a Document as an explicit root
     4        https://bugs.webkit.org/show_bug.cgi?id=208047
     5
     6        Reviewed by Simon Fraser.
     7
     8        * web-platform-tests/intersection-observer/document-scrolling-element-root-expected.txt:
     9        Update expectation now that the test passes.
     10
    1112020-03-02  Rob Buis  <rbuis@igalia.com>
    212
  • trunk/LayoutTests/imported/w3c/web-platform-tests/intersection-observer/document-scrolling-element-root-expected.txt

    r257137 r257976  
    11
    2 FAIL Observer with explicit root which is the document. Type error
     2PASS Observer with explicit root which is the document.
     3PASS First rAF.
    34
  • trunk/Source/WebCore/ChangeLog

    r257975 r257976  
     12020-03-06  Frederic Wang  <fwang@igalia.com>
     2
     3        [intersection-observer] Accept a Document as an explicit root
     4        https://bugs.webkit.org/show_bug.cgi?id=208047
     5
     6        Reviewed by Simon Fraser.
     7
     8        This patch introduces a recent enhancement to the Intersection Observer specification: the
     9        root initialization parameter can be explicitly be set to a Document. The typical use case
     10        is when document is an iframe. See https://github.com/w3c/IntersectionObserver/issues/372
     11
     12        This patch also updates the way Element's intersection observer data is handled so that it is
     13        more consistent with the explicit Document root case introduced here.
     14
     15        Test: imported/w3c/web-platform-tests/intersection-observer/document-scrolling-element-root.html
     16
     17        * dom/Document.cpp:
     18        (WebCore::Document::~Document): Notify observers about our desctruction.
     19        (WebCore::Document::updateIntersectionObservations): Use new method name. This does not
     20        require null-check because ensureIntersectionObserverData() has been called in
     21        IntersectionObserver::observe().
     22        (WebCore::Document::ensureIntersectionObserverData): Return reference to intersection
     23        observer data for this document, creating one if it does not exist.
     24        * dom/Document.h: Add new intersection observer data, used for documents that are explicit
     25        intersection observer roots.
     26        (WebCore::Document::intersectionObserverDataIfExists): Return pointer to intersection
     27        observer data or null if it does not exist.
     28        * dom/Element.cpp:
     29        (WebCore::Element::didMoveToNewDocument): Use new method name.
     30        (WebCore::Element::disconnectFromIntersectionObservers): Ditto and null-check weak refs.
     31        (WebCore::Element::intersectionObserverDataIfExists): Rename method to match Document's one
     32        and be more explicit that it will be null if it does not exist.
     33        (WebCore::Element::intersectionObserverData): Renamed.
     34        * dom/Element.h: Renamed.
     35        * html/LazyLoadImageObserver.cpp:
     36        (WebCore::LazyLoadImageObserver::intersectionObserver): Initialize with a WTF::Optional
     37        after API change.
     38        * page/IntersectionObserver.cpp:
     39        (WebCore::IntersectionObserver::create): Pass a Node* root, which can be null (implicit
     40        root), Document* or Element* (explicit roots). This is determined from init.root.
     41        (WebCore::IntersectionObserver::IntersectionObserver): Handle the case of explicit Document
     42        root.
     43        (WebCore::IntersectionObserver::~IntersectionObserver): Ditto and update method name for
     44        the explicit Element case. Note that in both explicit root cases the corresponding
     45        ensureIntersectionObserverData() method had been called in the constructor so they can
     46        be safely deferenced.
     47        (WebCore::IntersectionObserver::removeTargetRegistration): Use new method name.
     48        * page/IntersectionObserver.h: Update comment and code now that explicit root is a Node* and
     49        IntersectionObserver::Init::root is either an Element or a Document or null.
     50        (WebCore::IntersectionObserver::root const): Ditto.
     51        (): Deleted.
     52        * page/IntersectionObserver.idl: Update IDL to match the spec IntersectionObserver::root
     53        is a nullable Node and IntersectionObserverInit::root a nullable Element or Document.
     54
    1552020-03-06  Yusuke Suzuki  <ysuzuki@apple.com>
    256
  • trunk/Source/WebCore/dom/Document.cpp

    r257862 r257976  
    636636        m_logger->removeObserver(*this);
    637637
     638#if ENABLE(INTERSECTION_OBSERVER)
     639    if (m_intersectionObserverData) {
     640        for (const auto& observer : m_intersectionObserverData->observers) {
     641            if (observer)
     642                observer->rootDestroyed();
     643        }
     644        m_intersectionObserverData->observers.clear();
     645        // Document cannot be a target.
     646        ASSERT(m_intersectionObserverData->registrations.isEmpty());
     647    }
     648#endif
     649
    638650    ASSERT(allDocumentsMap().contains(m_identifier));
    639651    allDocumentsMap().remove(m_identifier);
     
    75757587            continue;
    75767588        for (Element* target : observer->observationTargets()) {
    7577             auto& targetRegistrations = target->intersectionObserverData()->registrations;
     7589            auto& targetRegistrations = target->intersectionObserverDataIfExists()->registrations;
    75787590            auto index = targetRegistrations.findMatching([observer](auto& registration) {
    75797591                return registration.observer.get() == observer;
     
    76627674}
    76637675
     7676IntersectionObserverData& Document::ensureIntersectionObserverData()
     7677{
     7678    if (!m_intersectionObserverData)
     7679        m_intersectionObserverData = makeUnique<IntersectionObserverData>();
     7680    return *m_intersectionObserverData;
     7681}
     7682
    76647683#endif
    76657684
  • trunk/Source/WebCore/dom/Document.h

    r257862 r257976  
    253253#if ENABLE(INTERSECTION_OBSERVER)
    254254class IntersectionObserver;
     255struct IntersectionObserverData;
    255256#endif
    256257
     
    13901391    void updateIntersectionObservations();
    13911392    void scheduleInitialIntersectionObservationUpdate();
     1393    IntersectionObserverData& ensureIntersectionObserverData();
     1394    IntersectionObserverData* intersectionObserverDataIfExists() { return m_intersectionObserverData.get(); }
    13921395#endif
    13931396
     
    18241827    Timer m_intersectionObserversNotifyTimer;
    18251828    Timer m_intersectionObserversInitialUpdateTimer;
     1829    // This is only non-null when this document is an explicit root.
     1830    std::unique_ptr<IntersectionObserverData> m_intersectionObserverData;
    18261831#endif
    18271832
  • trunk/Source/WebCore/dom/Element.cpp

    r257846 r257976  
    20662066
    20672067#if ENABLE(INTERSECTION_OBSERVER)
    2068     if (auto* observerData = intersectionObserverData()) {
     2068    if (auto* observerData = intersectionObserverDataIfExists()) {
    20692069        for (const auto& observer : observerData->observers) {
    20702070            if (observer->hasObservationTargets()) {
     
    37253725void Element::disconnectFromIntersectionObservers()
    37263726{
    3727     auto* observerData = intersectionObserverData();
     3727    auto* observerData = intersectionObserverDataIfExists();
    37283728    if (!observerData)
    37293729        return;
    37303730
    3731     for (const auto& registration : observerData->registrations)
    3732         registration.observer->targetDestroyed(*this);
     3731    for (const auto& registration : observerData->registrations) {
     3732        if (registration.observer)
     3733            registration.observer->targetDestroyed(*this);
     3734    }
    37333735    observerData->registrations.clear();
    37343736
    3735     for (const auto& observer : observerData->observers)
    3736         observer->rootDestroyed();
     3737    for (const auto& observer : observerData->observers) {
     3738        if (observer)
     3739            observer->rootDestroyed();
     3740    }
    37373741    observerData->observers.clear();
    37383742}
     
    37463750}
    37473751
    3748 IntersectionObserverData* Element::intersectionObserverData()
     3752IntersectionObserverData* Element::intersectionObserverDataIfExists()
    37493753{
    37503754    return hasRareData() ? elementRareData()->intersectionObserverData() : nullptr;
  • trunk/Source/WebCore/dom/Element.h

    r257839 r257976  
    592592#if ENABLE(INTERSECTION_OBSERVER)
    593593    IntersectionObserverData& ensureIntersectionObserverData();
    594     IntersectionObserverData* intersectionObserverData();
     594    IntersectionObserverData* intersectionObserverDataIfExists();
    595595#endif
    596596
  • trunk/Source/WebCore/html/LazyLoadImageObserver.cpp

    r256786 r257976  
    8686    if (!m_lazyLoadIntersectionObserver) {
    8787        auto callback = LazyImageLoadIntersectionObserverCallback::create(document);
    88         IntersectionObserver::Init options { nullptr, emptyString(), { } };
     88        IntersectionObserver::Init options { WTF::nullopt, emptyString(), { } };
    8989        auto observer = IntersectionObserver::create(document, WTFMove(callback), WTFMove(options));
    9090        if (observer.hasException())
  • trunk/Source/WebCore/page/IntersectionObserver.cpp

    r251244 r257976  
    8787ExceptionOr<Ref<IntersectionObserver>> IntersectionObserver::create(Document& document, Ref<IntersectionObserverCallback>&& callback, IntersectionObserver::Init&& init)
    8888{
     89    Node* root = nullptr;
     90    if (init.root) {
     91        WTF::switchOn(*init.root, [&root] (RefPtr<Element> element) {
     92            root = element.get();
     93        }, [&root] (RefPtr<Document> document) {
     94            root = document.get();
     95        });
     96    }
     97
    8998    auto rootMarginOrException = parseRootMargin(init.rootMargin);
    9099    if (rootMarginOrException.hasException())
     
    104113    }
    105114
    106     return adoptRef(*new IntersectionObserver(document, WTFMove(callback), init.root, rootMarginOrException.releaseReturnValue(), WTFMove(thresholds)));
    107 }
    108 
    109 IntersectionObserver::IntersectionObserver(Document& document, Ref<IntersectionObserverCallback>&& callback, Element* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds)
     115    return adoptRef(*new IntersectionObserver(document, WTFMove(callback), root, rootMarginOrException.releaseReturnValue(), WTFMove(thresholds)));
     116}
     117
     118IntersectionObserver::IntersectionObserver(Document& document, Ref<IntersectionObserverCallback>&& callback, Node* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds)
    110119    : ActiveDOMObject(callback->scriptExecutionContext())
    111120    , m_root(root)
     
    114123    , m_callback(WTFMove(callback))
    115124{
    116     if (m_root) {
    117         auto& observerData = m_root->ensureIntersectionObserverData();
     125    if (is<Document>(m_root)) {
     126        auto& observerData = downcast<Document>(*m_root).ensureIntersectionObserverData();
     127        observerData.observers.append(makeWeakPtr(this));
     128    } else if (m_root) {
     129        ASSERT(is<Element>(m_root));
     130        auto& observerData = downcast<Element>(*m_root).ensureIntersectionObserverData();
    118131        observerData.observers.append(makeWeakPtr(this));
    119132    } else if (auto* frame = document.frame())
     
    126139IntersectionObserver::~IntersectionObserver()
    127140{
    128     if (m_root)
    129         m_root->intersectionObserverData()->observers.removeFirst(this);
     141    if (is<Document>(m_root)) {
     142        downcast<Document>(*m_root).intersectionObserverDataIfExists()->observers.removeFirst(this);
     143    } else if (m_root) {
     144        ASSERT(is<Element>(m_root));
     145        downcast<Element>(*m_root).intersectionObserverDataIfExists()->observers.removeFirst(this);
     146    }
    130147    disconnect();
    131148}
     
    202219bool IntersectionObserver::removeTargetRegistration(Element& target)
    203220{
    204     auto* observerData = target.intersectionObserverData();
     221    auto* observerData = target.intersectionObserverDataIfExists();
    205222    if (!observerData)
    206223        return false;
  • trunk/Source/WebCore/page/IntersectionObserver.h

    r251244 r257976  
    4242class Document;
    4343class Element;
     44class Node;
    4445
    4546struct IntersectionObserverRegistration {
     
    5152    WTF_MAKE_STRUCT_FAST_ALLOCATED;
    5253
    53     // IntersectionObservers for which the element that owns this IntersectionObserverData is the root.
     54    // IntersectionObservers for which the node that owns this IntersectionObserverData is the root.
    5455    // An IntersectionObserver is only owned by a JavaScript wrapper. ActiveDOMObject::hasPendingActivity
    5556    // is overridden to keep this wrapper alive while the observer has ongoing observations.
    5657    Vector<WeakPtr<IntersectionObserver>> observers;
    5758
    58     // IntersectionObserverRegistrations for which the element that owns this IntersectionObserverData is the target.
     59    // IntersectionObserverRegistrations for which the node that owns this IntersectionObserverData is the target.
    5960    Vector<IntersectionObserverRegistration> registrations;
    6061};
     
    6364public:
    6465    struct Init {
    65         Element* root { nullptr };
     66        Optional<Variant<RefPtr<Element>, RefPtr<Document>>> root;
    6667        String rootMargin;
    6768        Variant<double, Vector<double>> threshold;
     
    7475    Document* trackingDocument() const { return m_root ? &m_root->document() : m_implicitRootDocument.get(); }
    7576
    76     Element* root() const { return m_root; }
     77    Node* root() const { return m_root; }
    7778    String rootMargin() const;
    7879    const LengthBox& rootMarginBox() const { return m_rootMargin; }
     
    105106
    106107private:
    107     IntersectionObserver(Document&, Ref<IntersectionObserverCallback>&&, Element* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds);
     108    IntersectionObserver(Document&, Ref<IntersectionObserverCallback>&&, Node* root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds);
    108109
    109110    bool removeTargetRegistration(Element&);
     
    111112
    112113    WeakPtr<Document> m_implicitRootDocument;
    113     Element* m_root;
     114    Node* m_root;
    114115    LengthBox m_rootMargin;
    115116    Vector<double> m_thresholds;
  • trunk/Source/WebCore/page/IntersectionObserver.idl

    r237880 r257976  
    3434    EnabledAtRuntime=IntersectionObserver
    3535] interface IntersectionObserver {
    36     readonly attribute Element? root;
     36    readonly attribute Node? root;
    3737    readonly attribute DOMString rootMargin;
    3838    readonly attribute sequence<double> thresholds;
     
    4949]
    5050dictionary IntersectionObserverInit {
    51     Element? root = null;
     51    (Element or Document)? root = null;
    5252    DOMString rootMargin = "0px";
    5353    (double or sequence<double>) threshold = 0.0;
Note: See TracChangeset for help on using the changeset viewer.