<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[208641] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/208641">208641</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2016-11-11 21:03:27 -0800 (Fri, 11 Nov 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>event.composedPath() does not include window
https://bugs.webkit.org/show_bug.cgi?id=164609
&lt;rdar://problem/29210383&gt;

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:
&quot;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.&quot;
(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.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsfastshadowdomresourceseventpathtesthelpersjs">trunk/LayoutTests/fast/shadow-dom/resources/event-path-test-helpers.js</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomEventDispatchercpp">trunk/Source/WebCore/dom/EventDispatcher.cpp</a></li>
<li><a href="#trunkSourceWebCoredomEventPathcpp">trunk/Source/WebCore/dom/EventPath.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastshadowdomeventpathwithwindowexpectedtxt">trunk/LayoutTests/fast/shadow-dom/event-path-with-window-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastshadowdomeventpathwithwindowhtml">trunk/LayoutTests/fast/shadow-dom/event-path-with-window.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (208640 => 208641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-11-12 04:00:55 UTC (rev 208640)
+++ trunk/LayoutTests/ChangeLog        2016-11-12 05:03:27 UTC (rev 208641)
</span><span class="lines">@@ -1,3 +1,18 @@
</span><ins>+2016-11-11  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        event.composedPath() does not include window
+        https://bugs.webkit.org/show_bug.cgi?id=164609
+        &lt;rdar://problem/29210383&gt;
+
+        Reviewed by Antti Koivisto.
+
+        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.
+
</ins><span class="cx"> 2016-11-11  Joseph Pecoraro  &lt;pecoraro@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         test262: DataView get methods should allow for missing offset, set methods should allow for missing value
</span></span></pre></div>
<a id="trunkLayoutTestsfastshadowdomeventpathwithwindowexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/shadow-dom/event-path-with-window-expected.txt (0 => 208641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/shadow-dom/event-path-with-window-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/event-path-with-window-expected.txt        2016-11-12 05:03:27 UTC (rev 208641)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+
+PASS The event must propagate out of open mode shadow boundaries when the composed flag is set 
+PASS The event must propagate out of closed mode shadow boundaries when the composed flag is set 
+PASS The event must propagate out of open mode shadow boundaries when the composed flag is set 
+PASS The event must propagate out of closed mode shadow boundaries when the composed flag is set 
+PASS The event must not propagate out of open mode shadow tree in which the relative target and the relative related target are the same 
+PASS The event must not propagate out of closed mode shadow tree in which the relative target and the relative related target are the same 
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastshadowdomeventpathwithwindowhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/shadow-dom/event-path-with-window.html (0 => 208641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/shadow-dom/event-path-with-window.html                                (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/event-path-with-window.html        2016-11-12 05:03:27 UTC (rev 208641)
</span><span class="lines">@@ -0,0 +1,134 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;title&gt;Shadow DOM: Extensions to Event Interface&lt;/title&gt;
+&lt;meta name=&quot;author&quot; title=&quot;Ryosuke Niwa&quot; href=&quot;mailto:rniwa@webkit.org&quot;&gt;
+&lt;meta name=&quot;assert&quot; content=&quot;Event interface must have composedPath() as a method&quot;&gt;
+&lt;link rel=&quot;help&quot; href=&quot;http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-event-interface&quot;&gt;
+&lt;script src=&quot;../../resources/testharness.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../resources/testharnessreport.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;resources/event-path-test-helpers.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;div id=&quot;log&quot;&gt;&lt;/div&gt;
+&lt;script&gt;
+
+window.label = 'window';
+document.label = 'document';
+document.documentElement.label = 'html';
+document.body.label = 'body';
+
+// FIXME: Add a test for trimming event path.
+
+/*
+-SR: ShadowRoot  -S: Slot  target: (~)  *: indicates start  digit: event path order
+A ------------------------------- A-SR
++ B ------------ B-SR             + A1 ------ A1-SR (1)
+  + C            + B1 --- B1-SR   + A2-S      + A1a (*; 0)
+  + D --- D-SR     + B1a    + B1b --- B1b-SR
+          + D1              + B1c-S   + B1b1
+                                      + B1b2
+*/
+
+function testEventTargetAfterDispatching(mode) {
+    test(() =&gt; {
+        const nodes = createTestTree(mode);
+        const log = dispatchEventWithLog(nodes, nodes.A1a, new Event('my-event', {composed: false, bubbles: true}));
+
+        const expectedPath = ['A1a', 'A1-SR'];
+        assert_array_equals(log.eventPath, expectedPath);
+        assert_array_equals(log.eventPath.length, log.pathAtTargets.length);
+        assert_array_equals(log.pathAtTargets[0], expectedPath);
+        assert_array_equals(log.pathAtTargets[1], expectedPath);
+
+        assert_equals(log.event.target, nodes.A1a);
+        assert_equals(log.event.currentTarget, null);
+    }, 'The event must propagate out of ' + mode + ' mode shadow boundaries when the composed flag is set');
+}
+
+testEventTargetAfterDispatching('open');
+testEventTargetAfterDispatching('closed');
+
+/*
+-SR: ShadowRoot  -S: Slot  target: (~)  *: indicates start  digit: event path order
+window (7)
+ + document (6)
+   + body (5)
+     + A (4) --------------------------- A-SR (3)
+       + B ------------ B-SR             + A1 (2) --- A1-SR (1)
+         + C            + B1 --- B1-SR   + A2-S       + A1a (*; 0)
+         + D --- D-SR     + B1a    + B1b --- B1b-SR
+                 + D1              + B1c-S   + B1b1
+                                             + B1b2
+*/
+
+function testComposedEventInDocument(mode) {
+    test(() =&gt; {
+        const nodes = createTestTree(mode);
+        document.body.appendChild(nodes.A);
+
+        const log = dispatchEventWithLog(nodes, nodes.A1a, new Event('my-event', {composed: true, bubbles: true}));
+        let expectedPath = ['A1a', 'A1-SR', 'A1', 'A-SR', 'A', 'body', 'html', 'document', 'window'];
+
+        assert_array_equals(log.eventPath, expectedPath);
+        assert_array_equals(log.eventPath.length, log.pathAtTargets.length);
+        assert_array_equals(log.pathAtTargets[0], expectedPath);
+        assert_array_equals(log.pathAtTargets[1], expectedPath);
+        if (mode != 'open')
+            expectedPath = expectedPath.slice(expectedPath.indexOf('A1'));
+        assert_array_equals(log.pathAtTargets[2], expectedPath);
+        assert_array_equals(log.pathAtTargets[3], expectedPath);
+        if (mode != 'open')
+            expectedPath = expectedPath.slice(expectedPath.indexOf('A'));
+        assert_array_equals(log.pathAtTargets[4], expectedPath);
+        assert_array_equals(log.pathAtTargets[5], expectedPath);
+        assert_array_equals(log.pathAtTargets[6], expectedPath);
+        assert_array_equals(log.pathAtTargets[7], expectedPath);
+
+        assert_equals(log.event.target, nodes.A);
+        assert_equals(log.event.currentTarget, null);
+    }, 'The event must propagate out of ' + mode + ' mode shadow boundaries when the composed flag is set');
+}
+
+testComposedEventInDocument('open');
+testComposedEventInDocument('closed');
+
+/*
+-SR: ShadowRoot  -S: Slot  target: (~)  relatedTarget: [~]  *: indicates start  digit: event path order
+window
+ + document
+   + body
+     + A ------------------------------- A-SR (3)
+       + B ------------ B-SR             + A1 (2) -------- A1-SR (1)
+         + C            + B1 --- B1-SR   + A2-S [*; 0-3]   + A1a (*; 0)
+         + D --- D-SR     + B1a    + B1b --- B1b-SR
+                 + D1              + B1c-S   + B1b1
+                                             + B1b2
+*/
+
+function testComposedEventWithRelatedTargetInDocument(mode) {
+    test(() =&gt; {
+        const nodes = createTestTree(mode);
+        document.body.appendChild(nodes.A);
+
+        const log = dispatchEventWithLog(nodes, nodes.A1a, new MouseEvent('foo', {composed: true, bubbles: true, relatedTarget: nodes['A2-S']}));
+        let expectedPath = ['A1a', 'A1-SR', 'A1', 'A-SR'];
+
+        assert_array_equals(log.eventPath, expectedPath);
+        assert_array_equals(log.eventPath.length, log.pathAtTargets.length);
+        assert_array_equals(log.pathAtTargets[0], expectedPath);
+        assert_array_equals(log.pathAtTargets[1], expectedPath);
+        if (mode != 'open')
+            expectedPath = expectedPath.slice(expectedPath.indexOf('A1'));
+        assert_array_equals(log.pathAtTargets[2], expectedPath);
+        assert_array_equals(log.pathAtTargets[3], expectedPath);
+        assert_array_equals(log.relatedTargets, ['A2-S', 'A2-S', 'A2-S', 'A2-S']);
+    }, 'The event must not propagate out of ' + mode + ' mode shadow tree in which the relative target and the relative related target are the same');
+}
+
+testComposedEventWithRelatedTargetInDocument('open');
+testComposedEventWithRelatedTargetInDocument('closed');
+
+&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsfastshadowdomresourceseventpathtesthelpersjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/shadow-dom/resources/event-path-test-helpers.js (208640 => 208641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/shadow-dom/resources/event-path-test-helpers.js        2016-11-12 04:00:55 UTC (rev 208640)
+++ trunk/LayoutTests/fast/shadow-dom/resources/event-path-test-helpers.js        2016-11-12 05:03:27 UTC (rev 208641)
</span><span class="lines">@@ -7,7 +7,7 @@
</span><span class="cx">     var attachedNodes = [];
</span><span class="cx">     for (var nodeKey in shadow) {
</span><span class="cx">         var startingNode = shadow[nodeKey];
</span><del>-        for (var node = startingNode; node; node = node.parentNode) {
</del><ins>+        for (var node = startingNode; node; node = node == document ? window : node.parentNode) {
</ins><span class="cx">             if (attachedNodes.indexOf(node) &gt;= 0)
</span><span class="cx">                 continue;
</span><span class="cx">             attachedNodes.push(node);
</span><span class="lines">@@ -25,7 +25,7 @@
</span><span class="cx"> 
</span><span class="cx">     target.dispatchEvent(event);
</span><span class="cx"> 
</span><del>-    return {eventPath: eventPath, relatedTargets: relatedTargets, pathAtTargets: pathAtTargets};
</del><ins>+    return {event: event, eventPath: eventPath, relatedTargets: relatedTargets, pathAtTargets: pathAtTargets};
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /*
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (208640 => 208641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-11-12 04:00:55 UTC (rev 208640)
+++ trunk/Source/WebCore/ChangeLog        2016-11-12 05:03:27 UTC (rev 208641)
</span><span class="lines">@@ -1,3 +1,37 @@
</span><ins>+2016-11-11  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        event.composedPath() does not include window
+        https://bugs.webkit.org/show_bug.cgi?id=164609
+        &lt;rdar://problem/29210383&gt;
+
+        Reviewed by Antti Koivisto.
+
+        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:
+        &quot;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.&quot;
+        (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.
+
</ins><span class="cx"> 2016-11-11  Dave Hyatt  &lt;hyatt@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [CSS Parser] Support all the correct blend modes
</span></span></pre></div>
<a id="trunkSourceWebCoredomEventDispatchercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/EventDispatcher.cpp (208640 => 208641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/EventDispatcher.cpp        2016-11-12 04:00:55 UTC (rev 208640)
+++ trunk/Source/WebCore/dom/EventDispatcher.cpp        2016-11-12 05:03:27 UTC (rev 208641)
</span><span class="lines">@@ -39,40 +39,6 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-class WindowEventContext {
-public:
-    WindowEventContext(Node*, const EventContext*);
-
-    DOMWindow* window() const { return m_window.get(); }
-    EventTarget* target() const { return m_target.get(); }
-    bool handleLocalEvents(Event&amp;);
-
-private:
-    RefPtr&lt;DOMWindow&gt; m_window;
-    RefPtr&lt;EventTarget&gt; m_target;
-};
-
-WindowEventContext::WindowEventContext(Node* node, const EventContext* topEventContext)
-{
-    Node* topLevelContainer = topEventContext ? topEventContext-&gt;node() : node;
-    if (!is&lt;Document&gt;(*topLevelContainer))
-        return;
-
-    m_window = downcast&lt;Document&gt;(*topLevelContainer).domWindow();
-    m_target = topEventContext ? topEventContext-&gt;target() : node;
-}
-
-bool WindowEventContext::handleLocalEvents(Event&amp; event)
-{
-    if (!m_window)
-        return false;
-
-    event.setTarget(m_target.copyRef());
-    event.setCurrentTarget(m_window.get());
-    m_window-&gt;fireEventListeners(event);
-    return true;
-}
-
</del><span class="cx"> void EventDispatcher::dispatchScopedEvent(Node&amp; node, Event&amp; event)
</span><span class="cx"> {
</span><span class="cx">     // We need to set the target here because it can go away by the time we actually fire the event.
</span><span class="lines">@@ -101,17 +67,11 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void dispatchEventInDOM(Event&amp; event, const EventPath&amp; path, WindowEventContext&amp; windowEventContext)
</del><ins>+static void dispatchEventInDOM(Event&amp; event, const EventPath&amp; path)
</ins><span class="cx"> {
</span><span class="cx">     // Trigger capturing event handlers, starting at the top and working our way down.
</span><span class="cx">     event.setEventPhase(Event::CAPTURING_PHASE);
</span><span class="cx"> 
</span><del>-    // We don't dispatch load events to the window. This quirk was originally
-    // added because Mozilla doesn't propagate load events to the window object.
-    bool shouldFireEventAtWindow = event.type() != eventNames().loadEvent;
-    if (shouldFireEventAtWindow &amp;&amp; windowEventContext.handleLocalEvents(event) &amp;&amp; event.propagationStopped())
-        return;
-
</del><span class="cx">     for (size_t i = path.size() - 1; i &gt; 0; --i) {
</span><span class="cx">         const EventContext&amp; eventContext = path.contextAt(i);
</span><span class="cx">         if (eventContext.currentTargetSameAsTarget())
</span><span class="lines">@@ -140,11 +100,6 @@
</span><span class="cx">         if (event.propagationStopped())
</span><span class="cx">             return;
</span><span class="cx">     }
</span><del>-    if (event.bubbles() &amp;&amp; !event.cancelBubble()) {
-        event.setEventPhase(Event::BUBBLING_PHASE);
-        if (shouldFireEventAtWindow)
-            windowEventContext.handleLocalEvents(event);
-    }
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool EventDispatcher::dispatchEvent(Node* origin, Event&amp; event)
</span><span class="lines">@@ -171,8 +126,6 @@
</span><span class="cx"> 
</span><span class="cx">     ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
</span><span class="cx"> 
</span><del>-    WindowEventContext windowEventContext(node.get(), eventPath.lastContextIfExists());
-
</del><span class="cx">     InputElementClickState clickHandlingState;
</span><span class="cx">     if (is&lt;HTMLInputElement&gt;(*node))
</span><span class="cx">         downcast&lt;HTMLInputElement&gt;(*node).willDispatchEvent(event, clickHandlingState);
</span><span class="lines">@@ -179,10 +132,11 @@
</span><span class="cx"> 
</span><span class="cx">     if (!event.propagationStopped() &amp;&amp; !eventPath.isEmpty()) {
</span><span class="cx">         event.setEventPath(eventPath);
</span><del>-        dispatchEventInDOM(event, eventPath, windowEventContext);
</del><ins>+        dispatchEventInDOM(event, eventPath);
</ins><span class="cx">         event.clearEventPath();
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    auto* finalTarget = event.target();
</ins><span class="cx">     event.setTarget(EventPath::eventTargetRespectingTargetRules(*node));
</span><span class="cx">     event.setCurrentTarget(nullptr);
</span><span class="cx">     event.resetPropagationFlags();
</span><span class="lines">@@ -197,9 +151,7 @@
</span><span class="cx">     if (!event.defaultPrevented() &amp;&amp; !event.defaultHandled())
</span><span class="cx">         callDefaultEventHandlersInTheBubblingOrder(event, eventPath);
</span><span class="cx"> 
</span><del>-    // Ensure that after event dispatch, the event's target object is the
-    // outermost shadow DOM boundary.
-    event.setTarget(windowEventContext.target());
</del><ins>+    event.setTarget(finalTarget);
</ins><span class="cx">     event.setCurrentTarget(nullptr);
</span><span class="cx"> 
</span><span class="cx">     return !event.defaultPrevented();
</span></span></pre></div>
<a id="trunkSourceWebCoredomEventPathcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/EventPath.cpp (208640 => 208641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/EventPath.cpp        2016-11-12 04:00:55 UTC (rev 208640)
+++ trunk/Source/WebCore/dom/EventPath.cpp        2016-11-12 05:03:27 UTC (rev 208641)
</span><span class="lines">@@ -21,6 +21,7 @@
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> #include &quot;EventPath.h&quot;
</span><span class="cx"> 
</span><ins>+#include &quot;DOMWindow.h&quot;
</ins><span class="cx"> #include &quot;Event.h&quot;
</span><span class="cx"> #include &quot;EventContext.h&quot;
</span><span class="cx"> #include &quot;EventNames.h&quot;
</span><span class="lines">@@ -32,6 +33,23 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+class WindowEventContext final : public EventContext {
+public:
+    WindowEventContext(Node&amp;, DOMWindow&amp;, EventTarget*);
+    void handleLocalEvents(Event&amp;) const final;
+};
+
+WindowEventContext::WindowEventContext(Node&amp; node, DOMWindow&amp; currentTarget, EventTarget* target)
+    : EventContext(&amp;node, &amp;currentTarget, target)
+{ }
+
+void WindowEventContext::handleLocalEvents(Event&amp; event) const
+{
+    event.setTarget(m_target.get());
+    event.setCurrentTarget(m_currentTarget.get());
+    m_currentTarget-&gt;fireEventListeners(event);
+}
+
</ins><span class="cx"> static inline bool shouldEventCrossShadowBoundary(Event&amp; event, ShadowRoot&amp; shadowRoot, EventTarget&amp; target)
</span><span class="cx"> {
</span><span class="cx">     Node* targetNode = target.toNode();
</span><span class="lines">@@ -108,10 +126,18 @@
</span><span class="cx">                 break;
</span><span class="cx"> 
</span><span class="cx">             ContainerNode* parent = node-&gt;parentNode();
</span><del>-            if (!parent)
</del><ins>+            if (UNLIKELY(!parent)) {
+                // https://dom.spec.whatwg.org/#interface-document
+                if (is&lt;Document&gt;(*node) &amp;&amp; event.type() != eventNames().loadEvent) {
+                    ASSERT(target);
+                    if (auto* window = downcast&lt;Document&gt;(*node).domWindow())
+                        m_path.append(std::make_unique&lt;WindowEventContext&gt;(*node, *window, target));
+                }
</ins><span class="cx">                 return;
</span><ins>+            }
</ins><span class="cx"> 
</span><del>-            if (ShadowRoot* shadowRootOfParent = parent-&gt;shadowRoot()) {
</del><ins>+            auto* shadowRootOfParent = parent-&gt;shadowRoot();
+            if (UNLIKELY(shadowRootOfParent)) {
</ins><span class="cx">                 if (auto* assignedSlot = shadowRootOfParent-&gt;findAssignedSlot(*node)) {
</span><span class="cx">                     // node is assigned to a slot. Continue dispatching the event at this slot.
</span><span class="cx">                     parent = assignedSlot;
</span><span class="lines">@@ -144,7 +170,10 @@
</span><span class="cx">     TreeScope* previousTreeScope = nullptr;
</span><span class="cx">     size_t originalEventPathSize = m_path.size();
</span><span class="cx">     for (unsigned contextIndex = 0; contextIndex &lt; originalEventPathSize; contextIndex++) {
</span><del>-        auto&amp; context = downcast&lt;MouseOrFocusEventContext&gt;(*m_path[contextIndex]);
</del><ins>+        auto&amp; ambgiousContext = *m_path[contextIndex];
+        if (!is&lt;MouseOrFocusEventContext&gt;(ambgiousContext))
+            continue;
+        auto&amp; context = downcast&lt;MouseOrFocusEventContext&gt;(ambgiousContext);
</ins><span class="cx"> 
</span><span class="cx">         Node&amp; currentTarget = *context.node();
</span><span class="cx">         TreeScope&amp; currentTreeScope = currentTarget.treeScope();
</span><span class="lines">@@ -187,8 +216,10 @@
</span><span class="cx">         if (UNLIKELY(previousTreeScope &amp;&amp; &amp;currentTreeScope != previousTreeScope))
</span><span class="cx">             retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
</span><span class="cx"> 
</span><del>-        Node* currentRelatedNode = retargeter.currentNode(currentTarget);
-        downcast&lt;TouchEventContext&gt;(*context).touchList(touchListType)-&gt;append(touch.cloneWithNewTarget(currentRelatedNode));
</del><ins>+        if (is&lt;TouchEventContext&gt;(*context)) {
+            Node* currentRelatedNode = retargeter.currentNode(currentTarget);
+            downcast&lt;TouchEventContext&gt;(*context).touchList(touchListType)-&gt;append(touch.cloneWithNewTarget(currentRelatedNode));
+        }
</ins><span class="cx"> 
</span><span class="cx">         previousTreeScope = &amp;currentTreeScope;
</span><span class="cx">     }
</span><span class="lines">@@ -223,18 +254,25 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// https://dom.spec.whatwg.org/#dom-event-composedpath
</ins><span class="cx"> Vector&lt;EventTarget*&gt; EventPath::computePathUnclosedToTarget(const EventTarget&amp; target) const
</span><span class="cx"> {
</span><span class="cx">     Vector&lt;EventTarget*&gt; path;
</span><span class="cx">     const Node* targetNode = const_cast&lt;EventTarget&amp;&gt;(target).toNode();
</span><del>-    if (!targetNode)
-        return path;
</del><ins>+    if (!targetNode) {
+        const DOMWindow* domWindow = const_cast&lt;EventTarget&amp;&gt;(target).toDOMWindow();
+        if (!domWindow)
+            return path;
+        targetNode = domWindow-&gt;document();
+        ASSERT(targetNode);
+    }
</ins><span class="cx"> 
</span><span class="cx">     for (auto&amp; context : m_path) {
</span><span class="cx">         if (Node* nodeInPath = context-&gt;currentTarget()-&gt;toNode()) {
</span><span class="cx">             if (targetNode-&gt;isUnclosedNode(*nodeInPath))
</span><span class="cx">                 path.append(context-&gt;currentTarget());
</span><del>-        }
</del><ins>+        } else
+            path.append(context-&gt;currentTarget());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     return path;
</span></span></pre>
</div>
</div>

</body>
</html>