Changeset 208641 in webkit


Ignore:
Timestamp:
Nov 11, 2016 9:03:27 PM (7 years ago)
Author:
rniwa@webkit.org
Message:

event.composedPath() does not include window
https://bugs.webkit.org/show_bug.cgi?id=164609
<rdar://problem/29210383>

Reviewed by Antti Koivisto.

Source/WebCore:

Fixed the bug by including WindowContext be a part of the regular EventPath. This also simplifies
dispatchEventInDOM which used to had a special logic for dispatching an event on the window.

Also fixed a bug in EventDispatcher::dispatchEvent that event.target would be nullptr when an event was
dispatched inside a disconnected shadow tree or prevented from propagating to the document tree.
Preserve the final target by simply saving event.target() prior to invoking the default event handler instead.

Test: fast/shadow-dom/event-path-with-window.html

  • dom/EventDispatcher.cpp:

(WebCore::WindowEventContext): Deleted. Moved to EventPath.cpp.
(WebCore::dispatchEventInDOM): Removed the code for WindowContext. The generic event dispatching logic
will do the same work now.
(WebCore::EventDispatcher::dispatchEvent): Restore the original target instead of using that of WindowContext.

  • dom/EventPath.cpp:

(WebCore::WindowEventContext): Moved from EventDispatcher.cpp. Also made it a subclass of EventContext.
(WebCore::WindowEventContext::handleLocalEvents): Added.
(WebCore::EventPath::EventPath): When the parent's nullptr, check if the current node is Document. If it is,
follow https://dom.spec.whatwg.org/#interface-document where it says:
"A document’s get the parent algorithm, given an event, returns null if event’s type attribute value is 'load'

or document does not have a browsing context, and the document’s associated Window object otherwise."

(WebCore::EventPath::setRelatedTarget): Skip over WindowContext.
(WebCore::EventPath::retargetTouch): Ditto.
(WebCore::EventPath::computePathUnclosedToTarget): When the target is DOMWindow, use its document as the target.
Also, include any event target that is not a node in the event path.

LayoutTests:

Added a W3C style testharness.js test for dispatching an inside a shadow tree connected to a document.

  • fast/shadow-dom/event-path-with-window-expected.txt: Added.
  • fast/shadow-dom/event-path-with-window.html: Added.
  • fast/shadow-dom/resources/event-path-test-helpers.js:

(dispatchEventWithLog): Traverse from document to window. Also include the event object in the log.

Location:
trunk
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r208639 r208641  
     12016-11-11  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        event.composedPath() does not include window
     4        https://bugs.webkit.org/show_bug.cgi?id=164609
     5        <rdar://problem/29210383>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        Added a W3C style testharness.js test for dispatching an inside a shadow tree connected to a document.
     10
     11        * fast/shadow-dom/event-path-with-window-expected.txt: Added.
     12        * fast/shadow-dom/event-path-with-window.html: Added.
     13        * fast/shadow-dom/resources/event-path-test-helpers.js:
     14        (dispatchEventWithLog): Traverse from document to window. Also include the event object in the log.
     15
    1162016-11-11  Joseph Pecoraro  <pecoraro@apple.com>
    217
  • trunk/LayoutTests/fast/shadow-dom/resources/event-path-test-helpers.js

    r200580 r208641  
    88    for (var nodeKey in shadow) {
    99        var startingNode = shadow[nodeKey];
    10         for (var node = startingNode; node; node = node.parentNode) {
     10        for (var node = startingNode; node; node = node == document ? window : node.parentNode) {
    1111            if (attachedNodes.indexOf(node) >= 0)
    1212                continue;
     
    2626    target.dispatchEvent(event);
    2727
    28     return {eventPath: eventPath, relatedTargets: relatedTargets, pathAtTargets: pathAtTargets};
     28    return {event: event, eventPath: eventPath, relatedTargets: relatedTargets, pathAtTargets: pathAtTargets};
    2929}
    3030
  • trunk/Source/WebCore/ChangeLog

    r208636 r208641  
     12016-11-11  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        event.composedPath() does not include window
     4        https://bugs.webkit.org/show_bug.cgi?id=164609
     5        <rdar://problem/29210383>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        Fixed the bug by including WindowContext be a part of the regular EventPath. This also simplifies
     10        dispatchEventInDOM which used to had a special logic for dispatching an event on the window.
     11
     12        Also fixed a bug in EventDispatcher::dispatchEvent that event.target would be nullptr when an event was
     13        dispatched inside a disconnected shadow tree or prevented from propagating to the document tree.
     14        Preserve the final target by simply saving event.target() prior to invoking the default event handler instead.
     15
     16        Test: fast/shadow-dom/event-path-with-window.html
     17
     18        * dom/EventDispatcher.cpp:
     19        (WebCore::WindowEventContext): Deleted. Moved to EventPath.cpp.
     20        (WebCore::dispatchEventInDOM): Removed the code for WindowContext. The generic event dispatching logic
     21        will do the same work now.
     22        (WebCore::EventDispatcher::dispatchEvent): Restore the original target instead of using that of WindowContext.
     23        * dom/EventPath.cpp:
     24        (WebCore::WindowEventContext): Moved from EventDispatcher.cpp. Also made it a subclass of EventContext.
     25        (WebCore::WindowEventContext::handleLocalEvents): Added.
     26        (WebCore::EventPath::EventPath): When the parent's nullptr, check if the current node is Document. If it is,
     27        follow https://dom.spec.whatwg.org/#interface-document where it says:
     28        "A document’s get the parent algorithm, given an event, returns null if event’s type attribute value is 'load'
     29         or document does not have a browsing context, and the document’s associated Window object otherwise."
     30        (WebCore::EventPath::setRelatedTarget): Skip over WindowContext.
     31        (WebCore::EventPath::retargetTouch): Ditto.
     32        (WebCore::EventPath::computePathUnclosedToTarget): When the target is DOMWindow, use its document as the target.
     33        Also, include any event target that is not a node in the event path.
     34
    1352016-11-11  Dave Hyatt  <hyatt@apple.com>
    236
  • trunk/Source/WebCore/dom/EventDispatcher.cpp

    r208112 r208641  
    4040namespace WebCore {
    4141
    42 class WindowEventContext {
    43 public:
    44     WindowEventContext(Node*, const EventContext*);
    45 
    46     DOMWindow* window() const { return m_window.get(); }
    47     EventTarget* target() const { return m_target.get(); }
    48     bool handleLocalEvents(Event&);
    49 
    50 private:
    51     RefPtr<DOMWindow> m_window;
    52     RefPtr<EventTarget> m_target;
    53 };
    54 
    55 WindowEventContext::WindowEventContext(Node* node, const EventContext* topEventContext)
    56 {
    57     Node* topLevelContainer = topEventContext ? topEventContext->node() : node;
    58     if (!is<Document>(*topLevelContainer))
    59         return;
    60 
    61     m_window = downcast<Document>(*topLevelContainer).domWindow();
    62     m_target = topEventContext ? topEventContext->target() : node;
    63 }
    64 
    65 bool WindowEventContext::handleLocalEvents(Event& event)
    66 {
    67     if (!m_window)
    68         return false;
    69 
    70     event.setTarget(m_target.copyRef());
    71     event.setCurrentTarget(m_window.get());
    72     m_window->fireEventListeners(event);
    73     return true;
    74 }
    75 
    7642void EventDispatcher::dispatchScopedEvent(Node& node, Event& event)
    7743{
     
    10268}
    10369
    104 static void dispatchEventInDOM(Event& event, const EventPath& path, WindowEventContext& windowEventContext)
     70static void dispatchEventInDOM(Event& event, const EventPath& path)
    10571{
    10672    // Trigger capturing event handlers, starting at the top and working our way down.
    10773    event.setEventPhase(Event::CAPTURING_PHASE);
    108 
    109     // We don't dispatch load events to the window. This quirk was originally
    110     // added because Mozilla doesn't propagate load events to the window object.
    111     bool shouldFireEventAtWindow = event.type() != eventNames().loadEvent;
    112     if (shouldFireEventAtWindow && windowEventContext.handleLocalEvents(event) && event.propagationStopped())
    113         return;
    11474
    11575    for (size_t i = path.size() - 1; i > 0; --i) {
     
    141101            return;
    142102    }
    143     if (event.bubbles() && !event.cancelBubble()) {
    144         event.setEventPhase(Event::BUBBLING_PHASE);
    145         if (shouldFireEventAtWindow)
    146             windowEventContext.handleLocalEvents(event);
    147     }
    148103}
    149104
     
    172127    ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
    173128
    174     WindowEventContext windowEventContext(node.get(), eventPath.lastContextIfExists());
    175 
    176129    InputElementClickState clickHandlingState;
    177130    if (is<HTMLInputElement>(*node))
     
    180133    if (!event.propagationStopped() && !eventPath.isEmpty()) {
    181134        event.setEventPath(eventPath);
    182         dispatchEventInDOM(event, eventPath, windowEventContext);
     135        dispatchEventInDOM(event, eventPath);
    183136        event.clearEventPath();
    184137    }
    185138
     139    auto* finalTarget = event.target();
    186140    event.setTarget(EventPath::eventTargetRespectingTargetRules(*node));
    187141    event.setCurrentTarget(nullptr);
     
    198152        callDefaultEventHandlersInTheBubblingOrder(event, eventPath);
    199153
    200     // Ensure that after event dispatch, the event's target object is the
    201     // outermost shadow DOM boundary.
    202     event.setTarget(windowEventContext.target());
     154    event.setTarget(finalTarget);
    203155    event.setCurrentTarget(nullptr);
    204156
  • trunk/Source/WebCore/dom/EventPath.cpp

    r206795 r208641  
    2222#include "EventPath.h"
    2323
     24#include "DOMWindow.h"
    2425#include "Event.h"
    2526#include "EventContext.h"
     
    3233
    3334namespace WebCore {
     35
     36class WindowEventContext final : public EventContext {
     37public:
     38    WindowEventContext(Node&, DOMWindow&, EventTarget*);
     39    void handleLocalEvents(Event&) const final;
     40};
     41
     42WindowEventContext::WindowEventContext(Node& node, DOMWindow& currentTarget, EventTarget* target)
     43    : EventContext(&node, &currentTarget, target)
     44{ }
     45
     46void WindowEventContext::handleLocalEvents(Event& event) const
     47{
     48    event.setTarget(m_target.get());
     49    event.setCurrentTarget(m_currentTarget.get());
     50    m_currentTarget->fireEventListeners(event);
     51}
    3452
    3553static inline bool shouldEventCrossShadowBoundary(Event& event, ShadowRoot& shadowRoot, EventTarget& target)
     
    109127
    110128            ContainerNode* parent = node->parentNode();
    111             if (!parent)
     129            if (UNLIKELY(!parent)) {
     130                // https://dom.spec.whatwg.org/#interface-document
     131                if (is<Document>(*node) && event.type() != eventNames().loadEvent) {
     132                    ASSERT(target);
     133                    if (auto* window = downcast<Document>(*node).domWindow())
     134                        m_path.append(std::make_unique<WindowEventContext>(*node, *window, target));
     135                }
    112136                return;
    113 
    114             if (ShadowRoot* shadowRootOfParent = parent->shadowRoot()) {
     137            }
     138
     139            auto* shadowRootOfParent = parent->shadowRoot();
     140            if (UNLIKELY(shadowRootOfParent)) {
    115141                if (auto* assignedSlot = shadowRootOfParent->findAssignedSlot(*node)) {
    116142                    // node is assigned to a slot. Continue dispatching the event at this slot.
     
    145171    size_t originalEventPathSize = m_path.size();
    146172    for (unsigned contextIndex = 0; contextIndex < originalEventPathSize; contextIndex++) {
    147         auto& context = downcast<MouseOrFocusEventContext>(*m_path[contextIndex]);
     173        auto& ambgiousContext = *m_path[contextIndex];
     174        if (!is<MouseOrFocusEventContext>(ambgiousContext))
     175            continue;
     176        auto& context = downcast<MouseOrFocusEventContext>(ambgiousContext);
    148177
    149178        Node& currentTarget = *context.node();
     
    188217            retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
    189218
    190         Node* currentRelatedNode = retargeter.currentNode(currentTarget);
    191         downcast<TouchEventContext>(*context).touchList(touchListType)->append(touch.cloneWithNewTarget(currentRelatedNode));
     219        if (is<TouchEventContext>(*context)) {
     220            Node* currentRelatedNode = retargeter.currentNode(currentTarget);
     221            downcast<TouchEventContext>(*context).touchList(touchListType)->append(touch.cloneWithNewTarget(currentRelatedNode));
     222        }
    192223
    193224        previousTreeScope = &currentTreeScope;
     
    224255}
    225256
     257// https://dom.spec.whatwg.org/#dom-event-composedpath
    226258Vector<EventTarget*> EventPath::computePathUnclosedToTarget(const EventTarget& target) const
    227259{
    228260    Vector<EventTarget*> path;
    229261    const Node* targetNode = const_cast<EventTarget&>(target).toNode();
    230     if (!targetNode)
    231         return path;
     262    if (!targetNode) {
     263        const DOMWindow* domWindow = const_cast<EventTarget&>(target).toDOMWindow();
     264        if (!domWindow)
     265            return path;
     266        targetNode = domWindow->document();
     267        ASSERT(targetNode);
     268    }
    232269
    233270    for (auto& context : m_path) {
     
    235272            if (targetNode->isUnclosedNode(*nodeInPath))
    236273                path.append(context->currentTarget());
    237         }
     274        } else
     275            path.append(context->currentTarget());
    238276    }
    239277
Note: See TracChangeset for help on using the changeset viewer.