Changeset 206795 in webkit


Ignore:
Timestamp:
Oct 4, 2016 4:42:18 PM (8 years ago)
Author:
rniwa@webkit.org
Message:

ShadowRoot interface should have elementFromPoint
https://bugs.webkit.org/show_bug.cgi?id=162882

Reviewed by Chris Dumez.

Source/WebCore:

Add elementFromPoint to ShadowRoot's prototype as specified at:
https://www.w3.org/TR/shadow-dom/#extensions-to-the-documentorshadowroot-mixin
with changes proposed at https://github.com/w3c/csswg-drafts/issues/556

Added TreeScope::retargetToScope which implements

This patch also factors DocumentOrShadowRoot.idl out of Document and ShadowRoot interfaces to better match
the latest DOM specification: https://dom.spec.whatwg.org/#mixin-documentorshadowroot

Test: fast/shadow-dom/Document-prototype-elementFromPoint.html

  • CMakeLists.txt:
  • DerivedSources.make:
  • WebCore.xcodeproj/project.pbxproj:
  • dom/Document.cpp:

(WebCore::Document::nodeFromPoint): Moved to TreeScope.
(WebCore::Document::elementFromPoint): Moved to TreeScope.

  • dom/Document.h:
  • dom/Document.idl: Moved elementFromPoint and activeElement to DocumentOrShadowRoot.idl.
  • dom/DocumentOrShadowRoot.idl: Added.
  • dom/EventPath.cpp:

(WebCore::RelatedNodeRetargeter::checkConsistency): Use newly added TreeScope::retargetToScope.

  • dom/ShadowRoot.idl: Moved activeElement to DocumentOrShadowRoot.idl.
  • dom/TreeScope.cpp:

(WebCore::TreeScope::retargetToScope): Added. Implements https://dom.spec.whatwg.org/#retarget efficiently.
Instead of checking whether A (node) is a shadow-including inclusive ancestor of B (this scope) at each
parent, find the lowest ancestor which contains both A and B, and return the self-inclusive ancestor of B
in that tree. To find the lowest common ancestor in O(n), traverse all ancestors of A and B separately and
do a top-down traversal. The last tree scope in which A's ancestor and B's ancestor match is the lowest
common ancestor.
(WebCore::TreeScope::nodeFromPoint): Moved from Document.
(WebCore::TreeScope::elementFromPoint): Moved from Document. Use retargetToScope and parentInComposedTree
instead of parentNode and ancestorInThisScope to match the semantics proposed in
https://github.com/w3c/csswg-drafts/issues/556

  • dom/TreeScope.h:

LayoutTests:

Add a W3C style testharness.js test for elementFromPoint on ShadowRoot.

  • fast/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint-expected.txt: Added.
  • fast/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint.html: Added.
Location:
trunk
Files:
3 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r206791 r206795  
     12016-10-03  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        ShadowRoot interface should have elementFromPoint
     4        https://bugs.webkit.org/show_bug.cgi?id=162882
     5
     6        Reviewed by Chris Dumez.
     7
     8        Add a W3C style testharness.js test for elementFromPoint on ShadowRoot.
     9
     10        * fast/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint-expected.txt: Added.
     11        * fast/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint.html: Added.
     12
    1132016-10-04  Myles C. Maxfield  <mmaxfield@apple.com>
    214
  • trunk/Source/WebCore/CMakeLists.txt

    r206679 r206795  
    392392    dom/Document.idl
    393393    dom/DocumentFragment.idl
     394    dom/DocumentOrShadowRoot.idl
    394395    dom/DocumentType.idl
    395396    dom/Element.idl
  • trunk/Source/WebCore/ChangeLog

    r206791 r206795  
     12016-10-03  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        ShadowRoot interface should have elementFromPoint
     4        https://bugs.webkit.org/show_bug.cgi?id=162882
     5
     6        Reviewed by Chris Dumez.
     7
     8        Add elementFromPoint to ShadowRoot's prototype as specified at:
     9        https://www.w3.org/TR/shadow-dom/#extensions-to-the-documentorshadowroot-mixin
     10        with changes proposed at https://github.com/w3c/csswg-drafts/issues/556
     11
     12        Added TreeScope::retargetToScope which implements
     13
     14        This patch also factors DocumentOrShadowRoot.idl out of Document and ShadowRoot interfaces to better match
     15        the latest DOM specification: https://dom.spec.whatwg.org/#mixin-documentorshadowroot
     16
     17        Test: fast/shadow-dom/Document-prototype-elementFromPoint.html
     18
     19        * CMakeLists.txt:
     20        * DerivedSources.make:
     21        * WebCore.xcodeproj/project.pbxproj:
     22        * dom/Document.cpp:
     23        (WebCore::Document::nodeFromPoint): Moved to TreeScope.
     24        (WebCore::Document::elementFromPoint): Moved to TreeScope.
     25        * dom/Document.h:
     26        * dom/Document.idl: Moved elementFromPoint and activeElement to DocumentOrShadowRoot.idl.
     27        * dom/DocumentOrShadowRoot.idl: Added.
     28        * dom/EventPath.cpp:
     29        (WebCore::RelatedNodeRetargeter::checkConsistency): Use newly added TreeScope::retargetToScope.
     30        * dom/ShadowRoot.idl: Moved activeElement to DocumentOrShadowRoot.idl.
     31        * dom/TreeScope.cpp:
     32        (WebCore::TreeScope::retargetToScope): Added. Implements https://dom.spec.whatwg.org/#retarget efficiently.
     33        Instead of checking whether A (node) is a shadow-including inclusive ancestor of B (this scope) at each
     34        parent, find the lowest ancestor which contains both A and B, and return the self-inclusive ancestor of B
     35        in that tree. To find the lowest common ancestor in O(n), traverse all ancestors of A and B separately and
     36        do a top-down traversal. The last tree scope in which A's ancestor and B's ancestor match is the lowest
     37        common ancestor.
     38        (WebCore::TreeScope::nodeFromPoint): Moved from Document.
     39        (WebCore::TreeScope::elementFromPoint): Moved from Document. Use retargetToScope and parentInComposedTree
     40        instead of parentNode and ancestorInThisScope to match the semantics proposed in
     41        https://github.com/w3c/csswg-drafts/issues/556
     42        * dom/TreeScope.h:
     43
    1442016-10-04  Myles C. Maxfield  <mmaxfield@apple.com>
    245
  • trunk/Source/WebCore/DerivedSources.make

    r206440 r206795  
    302302    $(WebCore)/dom/Document.idl \
    303303    $(WebCore)/dom/DocumentFragment.idl \
     304    $(WebCore)/dom/DocumentOrShadowRoot.idl \
    304305    $(WebCore)/dom/DocumentType.idl \
    305306    $(WebCore)/dom/Element.idl \
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r206717 r206795  
    1087810878                9B03D8061BB3110D00B764E8 /* WritableStreamBuiltins.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WritableStreamBuiltins.h; sourceTree = "<group>"; };
    1087910879                9B03D8061BB3110D00B764E9 /* WritableStreamInternalsBuiltins.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WritableStreamInternalsBuiltins.h; sourceTree = "<group>"; };
     10880                9B0FE8731D9E02DF004A8ACB /* DocumentOrShadowRoot.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DocumentOrShadowRoot.idl; sourceTree = "<group>"; };
    1088010881                9B19B67E1B964E5200348745 /* ShadowRoot.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ShadowRoot.idl; sourceTree = "<group>"; };
    1088110882                9B1AB0791648C69D0051F3F2 /* HTMLFormControlsCollection.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = HTMLFormControlsCollection.idl; sourceTree = "<group>"; };
     
    2301523016                                CE057FA31220731100A476D5 /* DocumentMarkerController.cpp */,
    2301623017                                CE057FA41220731100A476D5 /* DocumentMarkerController.h */,
     23018                                9B0FE8731D9E02DF004A8ACB /* DocumentOrShadowRoot.idl */,
    2301723019                                14947FFB12F80CD200A0F631 /* DocumentOrderedMap.cpp */,
    2301823020                                14947FFC12F80CD200A0F631 /* DocumentOrderedMap.h */,
  • trunk/Source/WebCore/dom/Document.cpp

    r206753 r206795  
    14261426}
    14271427
    1428 Node* Document::nodeFromPoint(const LayoutPoint& clientPoint, LayoutPoint* localPoint)
    1429 {
    1430     if (!frame() || !view())
    1431         return nullptr;
    1432    
    1433     Frame& frame = *this->frame();
    1434    
    1435     float scaleFactor = frame.pageZoomFactor() * frame.frameScaleFactor();
    1436 
    1437     LayoutPoint contentsPoint = clientPoint;
    1438     contentsPoint.scale(scaleFactor, scaleFactor);
    1439     contentsPoint.moveBy(view()->contentsScrollPosition());
    1440 
    1441     LayoutRect visibleRect;
    1442 #if PLATFORM(IOS)
    1443     visibleRect = view()->unobscuredContentRect();
    1444 #else
    1445     visibleRect = view()->visibleContentRect();
    1446 #endif
    1447     if (!visibleRect.contains(contentsPoint))
    1448         return nullptr;
    1449 
    1450     HitTestResult result(contentsPoint);
    1451     renderView()->hitTest(HitTestRequest(), result);
    1452 
    1453     if (localPoint)
    1454         *localPoint = result.localPoint();
    1455 
    1456     return result.innerNode();
    1457 }
    1458 
    1459 Element* Document::elementFromPoint(const LayoutPoint& clientPoint)
    1460 {
    1461     if (!hasLivingRenderTree())
    1462         return nullptr;
    1463 
    1464     Node* node = nodeFromPoint(clientPoint);
    1465     while (node && !is<Element>(*node))
    1466         node = node->parentNode();
    1467 
    1468     if (node)
    1469         node = ancestorInThisScope(node);
    1470 
    1471     return downcast<Element>(node);
    1472 }
    1473 
    14741428RefPtr<Range> Document::caretRangeFromPoint(int x, int y)
    14751429{
  • trunk/Source/WebCore/dom/Document.h

    r206753 r206795  
    398398    NamedFlowCollection& namedFlows();
    399399
    400     Element* elementFromPoint(int x, int y) { return elementFromPoint(LayoutPoint(x, y)); }
    401     WEBCORE_EXPORT Element* elementFromPoint(const LayoutPoint& clientPoint);
    402 
    403400    WEBCORE_EXPORT RefPtr<Range> caretRangeFromPoint(int x, int y);
    404401    RefPtr<Range> caretRangeFromPoint(const LayoutPoint& clientPoint);
     
    13641361    PageVisibilityState pageVisibilityState() const;
    13651362
    1366     Node* nodeFromPoint(const LayoutPoint& clientPoint, LayoutPoint* localPoint = nullptr);
    1367 
    13681363    template <CollectionType collectionType>
    13691364    Ref<HTMLCollection> ensureCachedCollection();
  • trunk/Source/WebCore/dom/Document.idl

    r206723 r206795  
    127127    readonly attribute DOMString readyState;
    128128
    129     Element elementFromPoint(optional long x = 0, optional long y = 0);
    130129    Range caretRangeFromPoint(optional long x = 0, optional long y = 0);
    131130
     
    142141    HTMLCollection getElementsByClassName(DOMString classNames);
    143142
    144     readonly attribute Element? activeElement;
    145143    boolean hasFocus();
    146144
     
    215213Document implements ParentNode;
    216214Document implements NonElementParentNode;
     215Document implements DocumentOrShadowRoot;
    217216Document implements GlobalEventHandlers;
  • trunk/Source/WebCore/dom/EventPath.cpp

    r206463 r206795  
    361361void RelatedNodeRetargeter::checkConsistency(Node& currentTarget)
    362362{
    363     ASSERT(!m_retargetedRelatedNode || currentTarget.isUnclosedNode(*m_retargetedRelatedNode));
    364 
    365     // http://w3c.github.io/webcomponents/spec/shadow/#dfn-retargeting-algorithm
    366     Node& base = currentTarget;
    367     for (Node* targetAncestor = &m_relatedNode; targetAncestor; targetAncestor = targetAncestor->parentOrShadowHostNode()) {
    368         if (targetAncestor->rootNode().containsIncludingShadowDOM(&base)) {
    369             ASSERT(m_retargetedRelatedNode == targetAncestor);
    370             return;
    371         }
    372     }
    373     ASSERT(!m_retargetedRelatedNode || m_hasDifferentTreeRoot);
    374 }
    375 #endif
    376 
    377 }
     363    if (!m_retargetedRelatedNode)
     364        return;
     365    ASSERT(currentTarget.isUnclosedNode(*m_retargetedRelatedNode));
     366    ASSERT(m_retargetedRelatedNode == &currentTarget.treeScope().retargetToScope(m_relatedNode));
     367}
     368#endif
     369
     370}
  • trunk/Source/WebCore/dom/ShadowRoot.idl

    r206723 r206795  
    2929] interface ShadowRoot : DocumentFragment {
    3030    readonly attribute ShadowRootMode   mode;
    31     readonly attribute Element          activeElement;
    3231    readonly attribute Element          host;
    3332
     
    3736// "user-agent" is a WebKit extension that is not exposed to the Web.
    3837enum ShadowRootMode { "user-agent", "closed", "open" };
     38
     39ShadowRoot implements DocumentOrShadowRoot;
  • trunk/Source/WebCore/dom/TreeScope.cpp

    r203324 r206795  
    3333#include "FocusController.h"
    3434#include "Frame.h"
     35#include "FrameView.h"
    3536#include "HTMLAnchorElement.h"
    3637#include "HTMLFrameOwnerElement.h"
     
    4041#include "IdTargetObserverRegistry.h"
    4142#include "Page.h"
     43#include "RenderView.h"
    4244#include "RuntimeEnabledFeatures.h"
    4345#include "ShadowRoot.h"
     
    167169        return;
    168170    m_elementsByName->remove(name, element);
     171}
     172
     173
     174Node& TreeScope::retargetToScope(Node& node) const
     175{
     176    auto& scope = node.treeScope();
     177    if (LIKELY(this == &scope || !node.isInShadowTree()))
     178        return node;
     179    ASSERT(is<ShadowRoot>(scope.rootNode()));
     180
     181    Vector<TreeScope*, 8> nodeTreeScopes;
     182    for (auto* currentScope = &scope; currentScope; currentScope = currentScope->parentTreeScope())
     183        nodeTreeScopes.append(currentScope);
     184    ASSERT(nodeTreeScopes.size() >= 2);
     185
     186    Vector<const TreeScope*, 8> ancestorScopes;
     187    for (auto* currentScope = this; currentScope; currentScope = currentScope->parentTreeScope())
     188        ancestorScopes.append(currentScope);
     189
     190    size_t i = nodeTreeScopes.size();
     191    size_t j = ancestorScopes.size();
     192    while (i > 0 && j > 0 && nodeTreeScopes[i - 1] == ancestorScopes[j - 1]) {
     193        --i;
     194        --j;
     195    }
     196
     197    bool nodeIsInOuterTreeScope = !i;
     198    if (nodeIsInOuterTreeScope)
     199        return node;
     200
     201    ShadowRoot& shadowRootInLowestCommonTreeScope = downcast<ShadowRoot>(nodeTreeScopes[i - 1]->rootNode());
     202    return *shadowRootInLowestCommonTreeScope.host();
    169203}
    170204
     
    244278
    245279    return m_labelsByForAttribute->getElementByLabelForAttribute(*forAttributeValue.impl(), *this);
     280}
     281
     282Node* TreeScope::nodeFromPoint(const LayoutPoint& clientPoint, LayoutPoint* localPoint)
     283{
     284    auto* frame = documentScope().frame();
     285    auto* view = documentScope().view();
     286    if (!frame || !view)
     287        return nullptr;
     288
     289    float scaleFactor = frame->pageZoomFactor() * frame->frameScaleFactor();
     290
     291    LayoutPoint contentsPoint = clientPoint;
     292    contentsPoint.scale(scaleFactor, scaleFactor);
     293    contentsPoint.moveBy(view->contentsScrollPosition());
     294
     295    LayoutRect visibleRect;
     296#if PLATFORM(IOS)
     297    visibleRect = view->unobscuredContentRect();
     298#else
     299    visibleRect = view->visibleContentRect();
     300#endif
     301    if (!visibleRect.contains(contentsPoint))
     302        return nullptr;
     303
     304    HitTestResult result(contentsPoint);
     305    documentScope().renderView()->hitTest(HitTestRequest(), result);
     306
     307    if (localPoint)
     308        *localPoint = result.localPoint();
     309
     310    return result.innerNode();
     311}
     312
     313Element* TreeScope::elementFromPoint(int x, int y)
     314{
     315    Document& document = documentScope();
     316    if (!document.hasLivingRenderTree())
     317        return nullptr;
     318
     319    Node* node = nodeFromPoint(LayoutPoint(x, y), nullptr);
     320    if (!node)
     321        return nullptr;
     322
     323    node = &retargetToScope(*node);
     324    while (!is<Element>(*node)) {
     325        node = node->parentInComposedTree();
     326        if (!node)
     327            break;
     328        node = &retargetToScope(*node);
     329    }
     330
     331    return downcast<Element>(node);
    246332}
    247333
  • trunk/Source/WebCore/dom/TreeScope.h

    r204717 r206795  
    7272    static ptrdiff_t documentScopeMemoryOffset() { return OBJECT_OFFSETOF(TreeScope, m_documentScope); }
    7373
     74    // https://dom.spec.whatwg.org/#retarget
     75    Node& retargetToScope(Node&) const;
     76
    7477    Node* ancestorInThisScope(Node*) const;
    7578
     
    8386    void removeLabel(const AtomicStringImpl& forAttributeValue, HTMLLabelElement&);
    8487    HTMLLabelElement* labelElementForId(const AtomicString& forAttributeValue);
     88
     89    WEBCORE_EXPORT Element* elementFromPoint(int x, int y);
    8590
    8691    DOMSelection* getSelection() const;
     
    111116        m_documentScope = document;
    112117    }
     118
     119    Node* nodeFromPoint(const LayoutPoint& clientPoint, LayoutPoint* localPoint);
    113120
    114121private:
  • trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp

    r206665 r206795  
    32143214
    32153215#if ENABLE(CUSTOM_ELEMENTS)
    3216     RuntimeEnabledFeatures::sharedFeatures().setCustomElementsEnabled(store.getBoolValueForKey(WebPreferencesKey::customElementsEnabledKey()));
     3216    RuntimeEnabledFeatures::sharedFeatures().setCustomElementsEnabled(true || store.getBoolValueForKey(WebPreferencesKey::customElementsEnabledKey()));
    32173217#endif
    32183218
Note: See TracChangeset for help on using the changeset viewer.