<!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
<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.</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 <rniwa@webkit.org>
+
+ event.composedPath() does not include window
+ https://bugs.webkit.org/show_bug.cgi?id=164609
+ <rdar://problem/29210383>
+
+ 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 <pecoraro@apple.com>
</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>+<!DOCTYPE html>
+<html>
+<head>
+<title>Shadow DOM: Extensions to Event Interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="Event interface must have composedPath() as a method">
+<link rel="help" href="http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-event-interface">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/event-path-test-helpers.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+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(() => {
+ 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(() => {
+ 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(() => {
+ 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');
+
+</script>
+</body>
+</html>
</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) >= 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 <rniwa@webkit.org>
+
+ event.composedPath() does not include window
+ https://bugs.webkit.org/show_bug.cgi?id=164609
+ <rdar://problem/29210383>
+
+ 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:
+ "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.
+
</ins><span class="cx"> 2016-11-11 Dave Hyatt <hyatt@apple.com>
</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&);
-
-private:
- RefPtr<DOMWindow> m_window;
- RefPtr<EventTarget> m_target;
-};
-
-WindowEventContext::WindowEventContext(Node* node, const EventContext* topEventContext)
-{
- Node* topLevelContainer = topEventContext ? topEventContext->node() : node;
- if (!is<Document>(*topLevelContainer))
- return;
-
- m_window = downcast<Document>(*topLevelContainer).domWindow();
- m_target = topEventContext ? topEventContext->target() : node;
-}
-
-bool WindowEventContext::handleLocalEvents(Event& event)
-{
- if (!m_window)
- return false;
-
- event.setTarget(m_target.copyRef());
- event.setCurrentTarget(m_window.get());
- m_window->fireEventListeners(event);
- return true;
-}
-
</del><span class="cx"> void EventDispatcher::dispatchScopedEvent(Node& node, Event& 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& event, const EventPath& path, WindowEventContext& windowEventContext)
</del><ins>+static void dispatchEventInDOM(Event& event, const EventPath& 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 && windowEventContext.handleLocalEvents(event) && event.propagationStopped())
- return;
-
</del><span class="cx"> for (size_t i = path.size() - 1; i > 0; --i) {
</span><span class="cx"> const EventContext& 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() && !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& 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<HTMLInputElement>(*node))
</span><span class="cx"> downcast<HTMLInputElement>(*node).willDispatchEvent(event, clickHandlingState);
</span><span class="lines">@@ -179,10 +132,11 @@
</span><span class="cx">
</span><span class="cx"> if (!event.propagationStopped() && !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() && !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 "config.h"
</span><span class="cx"> #include "EventPath.h"
</span><span class="cx">
</span><ins>+#include "DOMWindow.h"
</ins><span class="cx"> #include "Event.h"
</span><span class="cx"> #include "EventContext.h"
</span><span class="cx"> #include "EventNames.h"
</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&, DOMWindow&, EventTarget*);
+ void handleLocalEvents(Event&) const final;
+};
+
+WindowEventContext::WindowEventContext(Node& node, DOMWindow& currentTarget, EventTarget* target)
+ : EventContext(&node, &currentTarget, target)
+{ }
+
+void WindowEventContext::handleLocalEvents(Event& event) const
+{
+ event.setTarget(m_target.get());
+ event.setCurrentTarget(m_currentTarget.get());
+ m_currentTarget->fireEventListeners(event);
+}
+
</ins><span class="cx"> static inline bool shouldEventCrossShadowBoundary(Event& event, ShadowRoot& shadowRoot, EventTarget& 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->parentNode();
</span><del>- if (!parent)
</del><ins>+ if (UNLIKELY(!parent)) {
+ // https://dom.spec.whatwg.org/#interface-document
+ if (is<Document>(*node) && event.type() != eventNames().loadEvent) {
+ ASSERT(target);
+ if (auto* window = downcast<Document>(*node).domWindow())
+ m_path.append(std::make_unique<WindowEventContext>(*node, *window, target));
+ }
</ins><span class="cx"> return;
</span><ins>+ }
</ins><span class="cx">
</span><del>- if (ShadowRoot* shadowRootOfParent = parent->shadowRoot()) {
</del><ins>+ auto* shadowRootOfParent = parent->shadowRoot();
+ if (UNLIKELY(shadowRootOfParent)) {
</ins><span class="cx"> if (auto* assignedSlot = shadowRootOfParent->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 < originalEventPathSize; contextIndex++) {
</span><del>- auto& context = downcast<MouseOrFocusEventContext>(*m_path[contextIndex]);
</del><ins>+ auto& ambgiousContext = *m_path[contextIndex];
+ if (!is<MouseOrFocusEventContext>(ambgiousContext))
+ continue;
+ auto& context = downcast<MouseOrFocusEventContext>(ambgiousContext);
</ins><span class="cx">
</span><span class="cx"> Node& currentTarget = *context.node();
</span><span class="cx"> TreeScope& currentTreeScope = currentTarget.treeScope();
</span><span class="lines">@@ -187,8 +216,10 @@
</span><span class="cx"> if (UNLIKELY(previousTreeScope && &currentTreeScope != previousTreeScope))
</span><span class="cx"> retargeter.moveToNewTreeScope(previousTreeScope, currentTreeScope);
</span><span class="cx">
</span><del>- Node* currentRelatedNode = retargeter.currentNode(currentTarget);
- downcast<TouchEventContext>(*context).touchList(touchListType)->append(touch.cloneWithNewTarget(currentRelatedNode));
</del><ins>+ if (is<TouchEventContext>(*context)) {
+ Node* currentRelatedNode = retargeter.currentNode(currentTarget);
+ downcast<TouchEventContext>(*context).touchList(touchListType)->append(touch.cloneWithNewTarget(currentRelatedNode));
+ }
</ins><span class="cx">
</span><span class="cx"> previousTreeScope = &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<EventTarget*> EventPath::computePathUnclosedToTarget(const EventTarget& target) const
</span><span class="cx"> {
</span><span class="cx"> Vector<EventTarget*> path;
</span><span class="cx"> const Node* targetNode = const_cast<EventTarget&>(target).toNode();
</span><del>- if (!targetNode)
- return path;
</del><ins>+ if (!targetNode) {
+ const DOMWindow* domWindow = const_cast<EventTarget&>(target).toDOMWindow();
+ if (!domWindow)
+ return path;
+ targetNode = domWindow->document();
+ ASSERT(targetNode);
+ }
</ins><span class="cx">
</span><span class="cx"> for (auto& context : m_path) {
</span><span class="cx"> if (Node* nodeInPath = context->currentTarget()->toNode()) {
</span><span class="cx"> if (targetNode->isUnclosedNode(*nodeInPath))
</span><span class="cx"> path.append(context->currentTarget());
</span><del>- }
</del><ins>+ } else
+ path.append(context->currentTarget());
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> return path;
</span></span></pre>
</div>
</div>
</body>
</html>