Diff
Modified: trunk/LayoutTests/ChangeLog (93037 => 93038)
--- trunk/LayoutTests/ChangeLog 2011-08-15 10:26:49 UTC (rev 93037)
+++ trunk/LayoutTests/ChangeLog 2011-08-15 11:02:10 UTC (rev 93038)
@@ -1,3 +1,17 @@
+2011-08-15 Hayato Ito <[email protected]>
+
+ Implement proper handling of focusin/focusout events in regard to shadow DOM boundaries.
+ https://bugs.webkit.org/show_bug.cgi?id=64249
+
+ Reviewed by Dimitri Glazkov.
+
+ Introduces FocusInEventDispatchMediator/FocusOutEventDispatchMediator so
+ that we can shrink ancestors of event target node considering shadow
+ DOM boundaries before dispatching focusin/focusout events.
+
+ * fast/dom/shadow/shadow-boundary-events-expected.txt:
+ * fast/dom/shadow/shadow-boundary-events.html:
+
2011-08-15 Pavel Feldman <[email protected]>
Web Inspector: not all of the properties have valid descriptors on all platforms.
Modified: trunk/LayoutTests/fast/dom/shadow/shadow-boundary-events-expected.txt (93037 => 93038)
--- trunk/LayoutTests/fast/dom/shadow/shadow-boundary-events-expected.txt 2011-08-15 10:26:49 UTC (rev 93037)
+++ trunk/LayoutTests/fast/dom/shadow/shadow-boundary-events-expected.txt 2011-08-15 11:02:10 UTC (rev 93038)
@@ -58,6 +58,16 @@
Moving mouse from shadowD/shadowF/shadowG/divH to shadowD/shadowK/divL
PASS dispatchedEvent("mouseover") is ["divL(<-shadowF)(@divL)", "shadowK(<-shadowF)(@shadowK)", "shadowK(<-shadowF)(@divJ)"]
PASS dispatchedEvent("mouseout") is ["divH(<-shadowK)(@divH)", "shadowG(<-shadowK)(@shadowG)", "shadowF(<-shadowK)(@shadowF)", "shadowF(<-shadowK)(@divE)"]
+
+Move focus from a node to its sibling node. All nodes are outside of shadow boundary.
+Moving focus from divB to divC
+PASS dispatchedEvent("focusin") is ["divC(@divC)", "divC(@divA)"]
+PASS dispatchedEvent("focusout") is ["divB(@divB)", "divB(@divA)"]
+
+Old focused node and new focused node exist in separated subtrees, crossing shadow boundaries. Making sure that an event is not dispatched beyond the lowest common boundary.
+Moving focus from shadowD/shadowF/shadowG/divH to shadowD/shadowK/divL
+PASS dispatchedEvent("focusin") is ["divL(@divL)", "shadowK(@shadowK)", "shadowK(@divJ)"]
+PASS dispatchedEvent("focusout") is ["divH(@divH)", "shadowG(@shadowG)", "shadowF(@shadowF)", "shadowF(@divE)"]
PASS successfullyParsed is true
TEST COMPLETE
Modified: trunk/LayoutTests/fast/dom/shadow/shadow-boundary-events.html (93037 => 93038)
--- trunk/LayoutTests/fast/dom/shadow/shadow-boundary-events.html 2011-08-15 10:26:49 UTC (rev 93037)
+++ trunk/LayoutTests/fast/dom/shadow/shadow-boundary-events.html 2011-08-15 11:02:10 UTC (rev 93038)
@@ -75,17 +75,17 @@
{
parent.appendChild(
createDom('div', {'id': 'divA', 'style': 'padding-top: 40px'},
- createDom('div', {'id': 'divB', 'style': 'width: 40px; height: 40px'}),
- createDom('div', {'id': 'divC', 'style': 'width: 40px; height: 40px'}),
+ createDom('div', {'id': 'divB', 'style': 'width: 40px; height: 40px', 'tabindex': 0}),
+ createDom('div', {'id': 'divC', 'style': 'width: 40px; height: 40px', 'tabindex': 0}),
createShadow('div', {'id': 'shadowD', 'style': 'padding-top: 40px'},
createDom('div', {'id': 'divE', 'style': 'padding-top: 40px'},
createShadow('div', {'id': 'shadowF', 'style': 'padding-top: 40px'},
createShadow('div', {'id': 'shadowG', 'style': 'padding-top: 40px'},
- createDom('div', {'id': 'divH', 'style': 'width: 40px; height: 40px'}),
- createDom('div', {'id': 'divI', 'style': 'width: 40px; height: 40px'})))),
+ createDom('div', {'id': 'divH', 'style': 'width: 40px; height: 40px', 'tabindex': 0}),
+ createDom('div', {'id': 'divI', 'style': 'width: 40px; height: 40px', 'tabindex': 0})))),
createDom('div', {'id': 'divJ', 'style': 'padding-top: 40px'},
createShadow('div', {'id': 'shadowK', 'style': 'padding-top: 40px'},
- createDom('div', {'id': 'divL', 'style': 'width: 40px; height: 40px'}))))));
+ createDom('div', {'id': 'divL', 'style': 'width: 40px; height: 40px', 'tabindex': 0}))))));
var ids = ['divA', 'divB', 'divC',
'shadowD', 'shadowD/divE', 'shadowD/shadowF', 'shadowD/shadowF/shadowG',
@@ -95,6 +95,8 @@
var element = getElementInShadow(ids[i]);
element.addEventListener('mouseover', recordEvent, false);
element.addEventListener('mouseout', recordEvent, false);
+ element.addEventListener('focusin', recordEvent, false);
+ element.addEventListener('focusout', recordEvent, false);
}
}
@@ -106,12 +108,21 @@
moveMouseOver(getElementInShadow(newElementId));
}
+function moveFocus(oldElementId, newElementId, message)
+{
+ debug('\n' + message + '\n' + 'Moving focus from ' + oldElementId + ' to ' + newElementId);
+ getElementInShadow(oldElementId).focus();
+ clearEventRecords();
+ getElementInShadow(newElementId).focus();
+}
+
function test()
{
if (window.layoutTestController)
layoutTestController.dumpAsText();
prepareDomTree(document.getElementById('sandbox'));
+ // Test for mouseover/mouseout events.
moveMouse('divB', 'divC',
'Move mouse from a node to its sibling node. All nodes are outside of shadow boundary.');
shouldBe('dispatchedEvent("mouseover")', '["divC(<-divB)(@divC)", "divC(<-divB)(@divA)"]');
@@ -166,6 +177,20 @@
'Target and relatedTarget exist in separated subtree, crossing shadow boundaries. Making sure that event is not dispatched beyond the lowest common boundary.');
shouldBe('dispatchedEvent("mouseover")', '["divL(<-shadowF)(@divL)", "shadowK(<-shadowF)(@shadowK)", "shadowK(<-shadowF)(@divJ)"]');
shouldBe('dispatchedEvent("mouseout")', '["divH(<-shadowK)(@divH)", "shadowG(<-shadowK)(@shadowG)", "shadowF(<-shadowK)(@shadowF)", "shadowF(<-shadowK)(@divE)"]');
+
+ // Test for focusin/focusout events.
+ moveFocus('divB', 'divC',
+ 'Move focus from a node to its sibling node. All nodes are outside of shadow boundary.');
+ shouldBe('dispatchedEvent("focusin")', '["divC(@divC)", "divC(@divA)"]');
+ shouldBe('dispatchedEvent("focusout")', '["divB(@divB)", "divB(@divA)"]');
+
+ moveFocus('shadowD/shadowF/shadowG/divH', 'shadowD/shadowK/divL',
+ 'Old focused node and new focused node exist in separated subtrees, crossing shadow boundaries. Making sure that an event is not dispatched beyond the lowest common boundary.');
+ shouldBe('dispatchedEvent("focusin")', '["divL(@divL)", "shadowK(@shadowK)", "shadowK(@divJ)"]');
+ shouldBe('dispatchedEvent("focusout")', '["divH(@divH)", "shadowG(@shadowG)", "shadowF(@shadowF)", "shadowF(@divE)"]');
+
+ // Omitted test cases where either a oldFocusedNode or newFocusedNode is an ancestor of the other.
+ // Due to a focus transfer mechanism on shadow hosts, a focused node should be a leaf node in general.
}
test();
Modified: trunk/Source/WebCore/ChangeLog (93037 => 93038)
--- trunk/Source/WebCore/ChangeLog 2011-08-15 10:26:49 UTC (rev 93037)
+++ trunk/Source/WebCore/ChangeLog 2011-08-15 11:02:10 UTC (rev 93038)
@@ -1,3 +1,33 @@
+2011-08-15 Hayato Ito <[email protected]>
+
+ Implement proper handling of focusin/focusout events in regard to shadow DOM boundaries.
+ https://bugs.webkit.org/show_bug.cgi?id=64249
+
+ Reviewed by Dimitri Glazkov.
+
+ Introduces FocusInEventDispatchMediator/FocusOutEventDispatchMediator so
+ that we can shrink ancestors of event target node considering shadow
+ DOM boundaries before dispatching focusin/focusout events.
+
+ Test: fast/dom/shadow/shadow-boundary-events.html
+
+ * dom/Document.cpp:
+ (WebCore::Document::setFocusedNode):
+ * dom/Node.cpp:
+ (WebCore::Node::dispatchFocusInEvent):
+ (WebCore::Node::dispatchFocusOutEvent):
+ (WebCore::Node::dispatchDOMActivateEvent):
+ (WebCore::Node::defaultEventHandler):
+ * dom/Node.h:
+ * dom/UIEvent.cpp:
+ (WebCore::FocusInEventDispatchMediator::create):
+ (WebCore::FocusInEventDispatchMediator::FocusInEventDispatchMediator):
+ (WebCore::FocusInEventDispatchMediator::dispatchEvent):
+ (WebCore::FocusOutEventDispatchMediator::create):
+ (WebCore::FocusOutEventDispatchMediator::FocusOutEventDispatchMediator):
+ (WebCore::FocusOutEventDispatchMediator::dispatchEvent):
+ * dom/UIEvent.h:
+
2011-08-15 Pavel Feldman <[email protected]>
Web Inspector: not all of the properties have valid descriptors on all platforms.
Modified: trunk/Source/WebCore/dom/Document.cpp (93037 => 93038)
--- trunk/Source/WebCore/dom/Document.cpp 2011-08-15 10:26:49 UTC (rev 93037)
+++ trunk/Source/WebCore/dom/Document.cpp 2011-08-15 11:02:10 UTC (rev 93038)
@@ -3133,10 +3133,10 @@
newFocusedNode = 0;
}
- oldFocusedNode->dispatchUIEvent(eventNames().focusoutEvent, 0, 0); // DOM level 3 name for the bubbling blur event.
+ oldFocusedNode->dispatchFocusOutEvent(eventNames().focusoutEvent, newFocusedNode); // DOM level 3 name for the bubbling blur event.
// FIXME: We should remove firing DOMFocusOutEvent event when we are sure no content depends
// on it, probably when <rdar://problem/8503958> is resolved.
- oldFocusedNode->dispatchUIEvent(eventNames().DOMFocusOutEvent, 0, 0); // DOM level 2 name for compatibility.
+ oldFocusedNode->dispatchFocusOutEvent(eventNames().DOMFocusOutEvent, newFocusedNode); // DOM level 2 name for compatibility.
if (m_focusedNode) {
// handler shifted focus
@@ -3177,10 +3177,10 @@
goto SetFocusedNodeDone;
}
- m_focusedNode->dispatchUIEvent(eventNames().focusinEvent, 0, 0); // DOM level 3 bubbling focus event.
+ m_focusedNode->dispatchFocusInEvent(eventNames().focusinEvent, oldFocusedNode); // DOM level 3 bubbling focus event.
// FIXME: We should remove firing DOMFocusInEvent event when we are sure no content depends
- // on it, probably when <rdar://problem/8503958> is resolved.
- m_focusedNode->dispatchUIEvent(eventNames().DOMFocusInEvent, 0, 0); // DOM level 2 for compatibility.
+ // on it, probably when <rdar://problem/8503958> is m.
+ m_focusedNode->dispatchFocusInEvent(eventNames().DOMFocusInEvent, oldFocusedNode); // DOM level 2 for compatibility.
if (m_focusedNode != newFocusedNode) {
// handler shifted focus
Modified: trunk/Source/WebCore/dom/Node.cpp (93037 => 93038)
--- trunk/Source/WebCore/dom/Node.cpp 2011-08-15 10:26:49 UTC (rev 93037)
+++ trunk/Source/WebCore/dom/Node.cpp 2011-08-15 11:02:10 UTC (rev 93038)
@@ -2734,15 +2734,24 @@
dispatchScopedEvent(MutationEvent::create(eventNames().DOMSubtreeModifiedEvent, true));
}
-void Node::dispatchUIEvent(const AtomicString& eventType, int detail, PassRefPtr<Event> underlyingEvent)
+void Node::dispatchFocusInEvent(const AtomicString& eventType, PassRefPtr<Node> oldFocusedNode)
{
ASSERT(!eventDispatchForbidden());
- ASSERT(eventType == eventNames().focusinEvent || eventType == eventNames().focusoutEvent ||
- eventType == eventNames().DOMFocusInEvent || eventType == eventNames().DOMFocusOutEvent || eventType == eventNames().DOMActivateEvent);
-
- bool cancelable = eventType == eventNames().DOMActivateEvent;
+ ASSERT(eventType == eventNames().focusinEvent || eventType == eventNames().DOMFocusInEvent);
+ dispatchScopedEventDispatchMediator(FocusInEventDispatchMediator::create(UIEvent::create(eventType, true, false, document()->defaultView(), 0), oldFocusedNode));
+}
- RefPtr<UIEvent> event = UIEvent::create(eventType, true, cancelable, document()->defaultView(), detail);
+void Node::dispatchFocusOutEvent(const AtomicString& eventType, PassRefPtr<Node> newFocusedNode)
+{
+ ASSERT(!eventDispatchForbidden());
+ ASSERT(eventType == eventNames().focusoutEvent || eventType == eventNames().DOMFocusOutEvent);
+ dispatchScopedEventDispatchMediator(FocusOutEventDispatchMediator::create(UIEvent::create(eventType, true, false, document()->defaultView(), 0), newFocusedNode));
+}
+
+void Node::dispatchDOMActivateEvent(int detail, PassRefPtr<Event> underlyingEvent)
+{
+ ASSERT(!eventDispatchForbidden());
+ RefPtr<UIEvent> event = UIEvent::create(eventNames().DOMActivateEvent, true, true, document()->defaultView(), detail);
event->setUnderlyingEvent(underlyingEvent);
dispatchScopedEvent(event.release());
}
@@ -2814,7 +2823,7 @@
frame->eventHandler()->defaultKeyboardEventHandler(static_cast<KeyboardEvent*>(event));
} else if (eventType == eventNames().clickEvent) {
int detail = event->isUIEvent() ? static_cast<UIEvent*>(event)->detail() : 0;
- dispatchUIEvent(eventNames().DOMActivateEvent, detail, event);
+ dispatchDOMActivateEvent(detail, event);
#if ENABLE(CONTEXT_MENUS)
} else if (eventType == eventNames().contextmenuEvent) {
if (Frame* frame = document()->frame())
Modified: trunk/Source/WebCore/dom/Node.h (93037 => 93038)
--- trunk/Source/WebCore/dom/Node.h 2011-08-15 10:26:49 UTC (rev 93037)
+++ trunk/Source/WebCore/dom/Node.h 2011-08-15 11:02:10 UTC (rev 93038)
@@ -552,7 +552,10 @@
virtual void handleLocalEvents(Event*);
void dispatchSubtreeModifiedEvent();
- void dispatchUIEvent(const AtomicString& eventType, int detail, PassRefPtr<Event> underlyingEvent);
+ void dispatchDOMActivateEvent(int detail, PassRefPtr<Event> underlyingEvent);
+ void dispatchFocusInEvent(const AtomicString& eventType, PassRefPtr<Node> oldFocusedNode);
+ void dispatchFocusOutEvent(const AtomicString& eventType, PassRefPtr<Node> newFocusedNode);
+
bool dispatchKeyEvent(const PlatformKeyboardEvent&);
bool dispatchWheelEvent(const PlatformWheelEvent&);
bool dispatchMouseEvent(const PlatformMouseEvent&, const AtomicString& eventType, int clickCount = 0, Node* relatedTarget = 0);
Modified: trunk/Source/WebCore/dom/UIEvent.cpp (93037 => 93038)
--- trunk/Source/WebCore/dom/UIEvent.cpp 2011-08-15 10:26:49 UTC (rev 93037)
+++ trunk/Source/WebCore/dom/UIEvent.cpp 2011-08-15 11:02:10 UTC (rev 93038)
@@ -24,6 +24,7 @@
#include "UIEvent.h"
#include "DOMWindow.h"
+#include "EventDispatcher.h"
namespace WebCore {
@@ -94,4 +95,38 @@
return 0;
}
+PassRefPtr<FocusInEventDispatchMediator> FocusInEventDispatchMediator::create(PassRefPtr<Event> event, PassRefPtr<Node> oldFocusedNode)
+{
+ return adoptRef(new FocusInEventDispatchMediator(event, oldFocusedNode));
+}
+
+FocusInEventDispatchMediator::FocusInEventDispatchMediator(PassRefPtr<Event> event, PassRefPtr<Node> oldFocusedNode)
+ : EventDispatchMediator(event)
+ , m_oldFocusedNode(oldFocusedNode)
+{
+}
+
+bool FocusInEventDispatchMediator::dispatchEvent(EventDispatcher* dispatcher) const
+{
+ dispatcher->adjustRelatedTarget(event(), m_oldFocusedNode);
+ return EventDispatchMediator::dispatchEvent(dispatcher);
+}
+
+PassRefPtr<FocusOutEventDispatchMediator> FocusOutEventDispatchMediator::create(PassRefPtr<Event> event, PassRefPtr<Node> newFocusedNode)
+{
+ return adoptRef(new FocusOutEventDispatchMediator(event, newFocusedNode));
+}
+
+FocusOutEventDispatchMediator::FocusOutEventDispatchMediator(PassRefPtr<Event> event, PassRefPtr<Node> newFocusedNode)
+ : EventDispatchMediator(event)
+ , m_newFocusedNode(newFocusedNode)
+{
+}
+
+bool FocusOutEventDispatchMediator::dispatchEvent(EventDispatcher* dispatcher) const
+{
+ dispatcher->adjustRelatedTarget(event(), m_newFocusedNode);
+ return EventDispatchMediator::dispatchEvent(dispatcher);
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/dom/UIEvent.h (93037 => 93038)
--- trunk/Source/WebCore/dom/UIEvent.h 2011-08-15 10:26:49 UTC (rev 93037)
+++ trunk/Source/WebCore/dom/UIEvent.h 2011-08-15 11:02:10 UTC (rev 93038)
@@ -70,6 +70,24 @@
int m_detail;
};
+ class FocusInEventDispatchMediator : public EventDispatchMediator {
+ public:
+ static PassRefPtr<FocusInEventDispatchMediator> create(PassRefPtr<Event>, PassRefPtr<Node> oldFocusedNode);
+ private:
+ explicit FocusInEventDispatchMediator(PassRefPtr<Event>, PassRefPtr<Node> oldFocusedNode);
+ virtual bool dispatchEvent(EventDispatcher*) const;
+ RefPtr<Node> m_oldFocusedNode;
+ };
+
+ class FocusOutEventDispatchMediator : public EventDispatchMediator {
+ public:
+ static PassRefPtr<FocusOutEventDispatchMediator> create(PassRefPtr<Event>, PassRefPtr<Node> newFocusedNode);
+ private:
+ explicit FocusOutEventDispatchMediator(PassRefPtr<Event>, PassRefPtr<Node> newFocusedNode);
+ virtual bool dispatchEvent(EventDispatcher*) const;
+ RefPtr<Node> m_newFocusedNode;
+ };
+
} // namespace WebCore
#endif // UIEvent_h