+2014-04-22 Ryosuke Niwa <rniwa@webkit.org>
+
+ REGRESSION (r157328): popover to check into flight ba.com dismisses instantly when focusing form
+ https://bugs.webkit.org/show_bug.cgi?id=131949
+
+ Reviewed by Darin Adler.
+
+ Add a test that dumps the event target and the related target of every mouse event
+ when dispatched inside an input element inside a details element.
+
+ This catches the regression as well as other bugs I encountered while fixing the bug.
+
+ We need a WK2 specific results because WK1 mac results contain an extra fake mouse move event.
+
+ * fast/events/shadow-event-path-expected.txt: Added.
+ * fast/events/shadow-event-path.html: Added.
+ * platform/mac-wk2/fast/events/shadow-event-expected.txt: Added.
+
2014-04-22 Ryosuke Niwa <rniwa@webkit.org>
Rollout r156635 since the old behavior was intentional.
--- /dev/null
+This test records target and relatedTarget at each element while dispatching a mouse click event at an input element.
+
+
+Content:<div id="detailsContainer"><details><summary><div id="divInsideSummary"><input id="target" type="text" size="10"></div></summary></details></div>
+
+mouseover@input#target
+ target:input#target
+ relatedTarget:null
+
+mouseover@div#divInsideSummary
+ target:input#target
+ relatedTarget:null
+
+mouseover@summary
+ target:input#target
+ relatedTarget:null
+
+mouseover@details
+ target:input#target
+ relatedTarget:null
+
+mouseover@div#detailsContainer
+ target:input#target
+ relatedTarget:null
+
+mouseover@body
+ target:input#target
+ relatedTarget:null
+
+mouseover@html
+ target:input#target
+ relatedTarget:null
+
+mouseover@document
+ target:input#target
+ relatedTarget:null
+
+mouseover@window
+ target:input#target
+ relatedTarget:null
+
+mouseenter@input#target
+ target:input#target
+ relatedTarget:null
+
+mouseenter@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseenter@summary
+ target:summary
+ relatedTarget:null
+
+mouseenter@details
+ target:details
+ relatedTarget:null
+
+mouseenter@div#detailsContainer
+ target:div#detailsContainer
+ relatedTarget:null
+
+mouseenter@body
+ target:body
+ relatedTarget:null
+
+mouseenter@html
+ target:html
+ relatedTarget:null
+
+mousemove@input#target
+ target:input#target
+ relatedTarget:null
+
+mousemove@div#divInsideSummary
+ target:input#target
+ relatedTarget:null
+
+mousemove@summary
+ target:input#target
+ relatedTarget:null
+
+mousemove@details
+ target:input#target
+ relatedTarget:null
+
+mousemove@div#detailsContainer
+ target:input#target
+ relatedTarget:null
+
+mousemove@body
+ target:input#target
+ relatedTarget:null
+
+mousemove@html
+ target:input#target
+ relatedTarget:null
+
+mousemove@document
+ target:input#target
+ relatedTarget:null
+
+mousemove@window
+ target:input#target
+ relatedTarget:null
+
+mouseout@input#target
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@div#divInsideSummary
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@summary
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@details
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@div#detailsContainer
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@body
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@html
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@document
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@window
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseleave@input#target
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseover@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@summary
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@details
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@body
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@html
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@document
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@window
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mousemove@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@summary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@details
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@body
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@html
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@document
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@window
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@summary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@details
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@body
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@html
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@document
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@window
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@summary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@details
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@body
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@html
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@document
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@window
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@summary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@details
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@body
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@html
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@document
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@window
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@summary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@details
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@body
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@html
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@document
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@window
+ target:div#divInsideSummary
+ relatedTarget:null
+
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<body>
+<p>This test records target and relatedTarget at each element while dispatching a mouse click event at an input element.</p>
+<div id="detailsContainer"><details><summary><div id="divInsideSummary"><input id="target" type="text" size="10"></summary></div></detials></div>
+<pre id="log"></pre>
+<script>
+
+if (window.testRunner)
+ testRunner.dumpAsText();
+
+function targetIdentifier(target) {
+ if (target === undefined || target === null)
+ return target;
+ if (target === window)
+ return 'window';
+ if (target === document)
+ return 'document';
+ return target.localName + (target.id ? '#' + target.id : '');
+}
+
+function attachListeners(eventname) {
+ var targets = Array.prototype.slice.call(document.querySelectorAll('*'));
+ targets.push(window);
+ targets.push(document);
+ targets.forEach(function (target) {
+ target.addEventListener(eventname, function (event) {
+ log.textContent += eventname + '@' + targetIdentifier(target) + '\n'
+ + ' target:' + targetIdentifier(event.target) + '\n'
+ + ' relatedTarget:' + targetIdentifier(event.relatedTarget) + '\n\n';
+ });
+ });
+}
+
+var log = document.getElementById('log');
+log.textContent = 'Content:' + detailsContainer.outerHTML + '\n\n';
+var target = document.getElementById('target');
+
+attachListeners('mousemove');
+attachListeners('mousedown');
+attachListeners('mouseover');
+attachListeners('mouseout');
+attachListeners('mouseenter');
+attachListeners('mouseleave');
+attachListeners('mouseup');
+attachListeners('click');
+
+function runTest() {
+ testRunner.waitUntilDone();
+ eventSender.mouseMoveTo(target.offsetLeft + target.offsetWidth / 2, target.offsetTop + target.offsetHeight / 2);
+ eventSender.mouseMoveTo(target.offsetLeft + target.offsetWidth + 100, target.offsetTop + target.offsetHeight / 2);
+ eventSender.mouseDown();
+ eventSender.mouseUp();
+ testRunner.notifyDone();
+}
+
+if (window.testRunner && !window.eventSender)
+ log.textContent += 'This test requires eventSender.';
+else if (window.eventSender) {
+ window.onload = runTest;
+}
+
+</script>
+</body>
+</html>
--- /dev/null
+This test records target and relatedTarget at each element while dispatching a mouse click event at an input element.
+
+
+Content:<div id="detailsContainer"><details><summary><div id="divInsideSummary"><input id="target" type="text" size="10"></div></summary></details></div>
+
+mouseover@input#target
+ target:input#target
+ relatedTarget:null
+
+mouseover@div#divInsideSummary
+ target:input#target
+ relatedTarget:null
+
+mouseover@summary
+ target:input#target
+ relatedTarget:null
+
+mouseover@details
+ target:input#target
+ relatedTarget:null
+
+mouseover@div#detailsContainer
+ target:input#target
+ relatedTarget:null
+
+mouseover@body
+ target:input#target
+ relatedTarget:null
+
+mouseover@html
+ target:input#target
+ relatedTarget:null
+
+mouseover@document
+ target:input#target
+ relatedTarget:null
+
+mouseover@window
+ target:input#target
+ relatedTarget:null
+
+mouseenter@input#target
+ target:input#target
+ relatedTarget:null
+
+mouseenter@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseenter@summary
+ target:summary
+ relatedTarget:null
+
+mouseenter@details
+ target:details
+ relatedTarget:null
+
+mouseenter@div#detailsContainer
+ target:div#detailsContainer
+ relatedTarget:null
+
+mouseenter@body
+ target:body
+ relatedTarget:null
+
+mouseenter@html
+ target:html
+ relatedTarget:null
+
+mousemove@input#target
+ target:input#target
+ relatedTarget:null
+
+mousemove@div#divInsideSummary
+ target:input#target
+ relatedTarget:null
+
+mousemove@summary
+ target:input#target
+ relatedTarget:null
+
+mousemove@details
+ target:input#target
+ relatedTarget:null
+
+mousemove@div#detailsContainer
+ target:input#target
+ relatedTarget:null
+
+mousemove@body
+ target:input#target
+ relatedTarget:null
+
+mousemove@html
+ target:input#target
+ relatedTarget:null
+
+mousemove@document
+ target:input#target
+ relatedTarget:null
+
+mousemove@window
+ target:input#target
+ relatedTarget:null
+
+mouseout@input#target
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@div#divInsideSummary
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@summary
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@details
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@div#detailsContainer
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@body
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@html
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@document
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseout@window
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseleave@input#target
+ target:input#target
+ relatedTarget:div#divInsideSummary
+
+mouseover@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@summary
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@details
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@body
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@html
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@document
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mouseover@window
+ target:div#divInsideSummary
+ relatedTarget:input#target
+
+mousemove@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@summary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@details
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@body
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@html
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@document
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousemove@window
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@summary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@details
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@body
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@html
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@document
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mousedown@window
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@summary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@details
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@body
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@html
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@document
+ target:div#divInsideSummary
+ relatedTarget:null
+
+mouseup@window
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@div#divInsideSummary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@summary
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@details
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@div#detailsContainer
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@body
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@html
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@document
+ target:div#divInsideSummary
+ relatedTarget:null
+
+click@window
+ target:div#divInsideSummary
+ relatedTarget:null
+
+
+2014-04-22 Ryosuke Niwa <rniwa@webkit.org>
+
+ REGRESSION (r157328): popover to check into flight ba.com dismisses instantly when focusing form
+ https://bugs.webkit.org/show_bug.cgi?id=131949
+
+ Reviewed by Darin Adler.
+
+ The regression was caused by two bugs:
+ 1. The event didn't stop propagating itself even when it should.
+ If the related target is same as the event origin, the event propagation should stop when the event reaches
+ the root of the related target's tree scope. Otherwise, it should stop when it reaches the related target.
+
+ 2. Mouse event's related target exposed nodes inside a user-agent shadow DOM when the related target appeared
+ inside the origin.
+
+ Fixed the bugs by re-introducing path shrinkage algorithm removed in r157328 into EventPath::setRelatedTarget
+ and adding an algorithm to determine the least common ancestor of the related target and the current target
+ in moveToParentOrShadowHost. The latter algorithm doesn't match the shadow DOM specification:
+ http://www.w3.org/TR/2013/WD-shadow-dom-20130514/
+ but it's good enough in terms of the Web exposed behavior as we don't support author defined insertion points.
+
+ Test: fast/events/shadow-event-path.html
+
+ * dom/EventDispatcher.cpp:
+ (WebCore::EventRelatedNodeResolver::moveToParentOrShadowHost):
+ (WebCore::EventRelatedNodeResolver::findHostOfTreeScopeInTargetTreeScope): Added.
+ (WebCore::EventDispatcher::dispatchEvent):
+ (WebCore::EventPath::setRelatedTarget):
+
2014-04-22 Ryosuke Niwa <rniwa@webkit.org>
Rollout r156635 since the old behavior was intentional.
#if ENABLE(TOUCH_EVENTS)
void updateTouchLists(const TouchEvent&);
#endif
- void setRelatedTarget(EventTarget&);
+ void setRelatedTarget(Node& origin, EventTarget&);
bool hasEventListeners(const AtomicString& eventType) const;
ASSERT(m_currentTreeScope->parentTreeScope() == &newTreeScope);
}
- if (m_relatedNodeInCurrentTreeScope) { // relatedNode is under the current tree scope
+ if (&newTreeScope == &m_relatedNodeTreeScope)
+ m_relatedNodeInCurrentTreeScope = &m_relatedNode;
+ else if (m_relatedNodeInCurrentTreeScope) {
ASSERT(m_currentTreeScope);
m_relatedNodeInCurrentTreeScope = &newTarget;
- } else if (&newTreeScope == &m_relatedNodeTreeScope) // relatedNode is in the current tree scope;
- m_relatedNodeInCurrentTreeScope = &m_relatedNode;
- // Otherwise, we haven't reached the tree scope that contains relatedNode yet.
+ } else {
+ if (!m_currentTreeScope) {
+ TreeScope* newTreeScopeAncestor = &newTreeScope;
+ do {
+ m_relatedNodeInCurrentTreeScope = findHostOfTreeScopeInTargetTreeScope(m_relatedNodeTreeScope, *newTreeScopeAncestor);
+ newTreeScopeAncestor = newTreeScopeAncestor->parentTreeScope();
+ if (newTreeScopeAncestor == &m_relatedNodeTreeScope) {
+ m_relatedNodeInCurrentTreeScope = &m_relatedNode;
+ break;
+ }
+ } while (newTreeScopeAncestor && !m_relatedNodeInCurrentTreeScope);
+ }
+ ASSERT(m_relatedNodeInCurrentTreeScope || findHostOfTreeScopeInTargetTreeScope(newTreeScope, m_relatedNodeTreeScope)
+ || &newTreeScope.documentScope() != &m_relatedNodeTreeScope.documentScope());
+ }
m_currentTreeScope = &newTreeScope;
return m_relatedNodeInCurrentTreeScope;
}
+ static Node* findHostOfTreeScopeInTargetTreeScope(const TreeScope& startingTreeScope, const TreeScope& targetScope)
+ {
+ ASSERT(&targetScope != &startingTreeScope);
+ Node* previousHost = 0;
+ for (const TreeScope* scope = &startingTreeScope; scope; scope = scope->parentTreeScope()) {
+ if (scope == &targetScope) {
+ ASSERT(previousHost);
+ ASSERT_WITH_SECURITY_IMPLICATION(&previousHost->treeScope() == &targetScope);
+ return previousHost;
+ }
+ if (scope->rootNode().isShadowRoot())
+ previousHost = toShadowRoot(scope->rootNode()).hostElement();
+ else
+ ASSERT_WITH_SECURITY_IMPLICATION(!scope->parentTreeScope());
+ }
+ return 0;
+ }
+
private:
Node& m_relatedNode;
const TreeScope& m_relatedNodeTreeScope;
EventPath eventPath(*node, *event);
if (EventTarget* relatedTarget = event->relatedTarget())
- eventPath.setRelatedTarget(*relatedTarget);
+ eventPath.setRelatedTarget(*node, *relatedTarget);
#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS)
if (event->isTouchEvent())
eventPath.updateTouchLists(*toTouchEvent(event.get()));
}
#endif
-void EventPath::setRelatedTarget(EventTarget& relatedTarget)
+void EventPath::setRelatedTarget(Node& origin, EventTarget& relatedTarget)
{
Node* relatedNode = relatedTarget.toNode();
if (!relatedNode)
EventRelatedNodeResolver resolver(*relatedNode);
+ bool originIsRelatedTarget = &origin == relatedNode;
+ Node& rootNodeInOriginTreeScope = origin.treeScope().rootNode();
+
size_t eventPathSize = m_path.size();
- for (size_t i = 0; i < eventPathSize; i++)
- toMouseOrFocusEventContext(*m_path[i]).setRelatedTarget(resolver.moveToParentOrShadowHost(*m_path[i]->node()));
+ size_t i = 0;
+ while (i < eventPathSize) {
+ Node* contextNode = m_path[i]->node();
+ Node* currentRelatedNode = resolver.moveToParentOrShadowHost(*contextNode);
+ if (!originIsRelatedTarget && m_path[i]->target() == currentRelatedNode)
+ break;
+ toMouseOrFocusEventContext(*m_path[i]).setRelatedTarget(currentRelatedNode);
+ i++;
+ if (originIsRelatedTarget && &rootNodeInOriginTreeScope == contextNode)
+ break;
+ }
+ m_path.shrink(i);
}
bool EventPath::hasEventListeners(const AtomicString& eventType) const