Title: [93038] trunk
Revision
93038
Author
[email protected]
Date
2011-08-15 04:02:10 -0700 (Mon, 15 Aug 2011)

Log Message

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.

Source/WebCore:

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:

LayoutTests:

* fast/dom/shadow/shadow-boundary-events-expected.txt:
* fast/dom/shadow/shadow-boundary-events.html:

Modified Paths

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
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to