Implement proper handling of focus/blur events in regard to shadow DOM boundaries.
authorhayato@chromium.org <hayato@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 18 Aug 2011 02:41:10 +0000 (02:41 +0000)
committerhayato@chromium.org <hayato@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 18 Aug 2011 02:41:10 +0000 (02:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=61421

Reviewed by Dimitri Glazkov.

Introduces FocusEventDispatchMediator/BlurEventDispatchMediator so
that focus/blue events are stopped at the lowest common shadow boundary.

Source/WebCore:

* dom/Document.cpp:
(WebCore::Document::setFocusedNode):
* dom/Event.cpp:
(WebCore::FocusEventDispatchMediator::create):
(WebCore::FocusEventDispatchMediator::FocusEventDispatchMediator):
(WebCore::FocusEventDispatchMediator::dispatchEvent):
(WebCore::BlurEventDispatchMediator::create):
(WebCore::BlurEventDispatchMediator::BlurEventDispatchMediator):
(WebCore::BlurEventDispatchMediator::dispatchEvent):
* dom/Event.h:
* dom/Node.cpp:
(WebCore::Node::dispatchFocusEvent):
(WebCore::Node::dispatchBlurEvent):
* dom/Node.h:
* html/HTMLFormControlElement.cpp:
(WebCore::HTMLFormControlElement::dispatchBlurEvent):
* html/HTMLFormControlElement.h:
* html/HTMLSelectElement.cpp:
(WebCore::HTMLSelectElement::dispatchFocusEvent):
(WebCore::HTMLSelectElement::dispatchBlurEvent):
* html/HTMLSelectElement.h:
* html/HTMLTextFormControlElement.cpp:
(WebCore::HTMLTextFormControlElement::dispatchFocusEvent):
(WebCore::HTMLTextFormControlElement::dispatchBlurEvent):
* html/HTMLTextFormControlElement.h:
* page/FocusController.cpp:
(WebCore::dispatchEventsOnWindowAndFocusedNode):

LayoutTests:

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

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@93276 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/shadow/shadow-boundary-events-expected.txt
LayoutTests/fast/dom/shadow/shadow-boundary-events.html
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Event.cpp
Source/WebCore/dom/Event.h
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/Node.h
Source/WebCore/html/HTMLFormControlElement.cpp
Source/WebCore/html/HTMLFormControlElement.h
Source/WebCore/html/HTMLSelectElement.cpp
Source/WebCore/html/HTMLSelectElement.h
Source/WebCore/html/HTMLTextFormControlElement.cpp
Source/WebCore/html/HTMLTextFormControlElement.h
Source/WebCore/page/FocusController.cpp

index 7252fa01031c678214cd8465e834a55aeeeb47ea..97059ad50df4d3fc82c94570508e144eba639f72 100644 (file)
@@ -1,3 +1,15 @@
+2011-08-17  Hayato Ito  <hayato@chromium.org>
+
+        Implement proper handling of focus/blur events in regard to shadow DOM boundaries.
+        https://bugs.webkit.org/show_bug.cgi?id=61421
+
+        Reviewed by Dimitri Glazkov.
+
+        Introduces FocusEventDispatchMediator/BlurEventDispatchMediator so
+        that focus/blue events are stopped at the lowest common shadow boundary.
+
+        * fast/dom/shadow/shadow-boundary-events.html:
+
 2011-08-17  Ben Wells  <benwells@chromium.org>
 
         Canvas fill and fillRect with SourceIn, DestinationIn, SourceOut, DestinationAtop and Copy have errors
index a57b70ef26abc06d0e5a6c18ac8562945daaae52..6c465ca1e155ed562935dfdc2fce54210b76cde8 100644 (file)
@@ -68,6 +68,16 @@ Old focused node and new focused node exist in separated subtrees, crossing shad
 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)"]
+
+Move focus from a node to its sibling node. All nodes are outside of shadow boundary.
+Moving focus from divB to divC
+PASS dispatchedEvent("focus") is ["divC(@divA)(capturing phase)", "divC(@divC)"]
+PASS dispatchedEvent("blur") is ["divB(@divA)(capturing phase)", "divB(@divB)"]
+
+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("focus") is ["shadowK(@divJ)(capturing phase)", "shadowK(@shadowK)(capturing phase)", "divL(@divL)"]
+PASS dispatchedEvent("blur") is ["shadowF(@divE)(capturing phase)", "shadowF(@shadowF)(capturing phase)", "shadowG(@shadowG)(capturing phase)", "divH(@divH)"]
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 72e8972b2e0601829cb11e94420468b24e7083b6..a8e28c8d155aa7513cc0b5796517560cc408f464 100644 (file)
@@ -57,6 +57,8 @@ function recordEvent(event)
         eventString += '(<-' + event.relatedTarget.id + ')';
     if (event.currentTarget)
         eventString += '(@' + event.currentTarget.id + ')';
+    if (event.eventPhase == 1)
+        eventString += '(capturing phase)';
     eventRecords[eventType].push(eventString);
 }
 
@@ -97,6 +99,8 @@ function prepareDomTree(parent)
         element.addEventListener('mouseout', recordEvent, false);
         element.addEventListener('focusin', recordEvent, false);
         element.addEventListener('focusout', recordEvent, false);
+        element.addEventListener('focus', recordEvent, true);  // capturing phase
+        element.addEventListener('blur', recordEvent, true);  // capturing phase
     }
 }
 
@@ -191,6 +195,17 @@ function test()
 
     // 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 for focus/blur events. Event listners should be registerd on captureing phase.
+    moveFocus('divB', 'divC',
+              'Move focus from a node to its sibling node. All nodes are outside of shadow boundary.');
+    shouldBe('dispatchedEvent("focus")', '["divC(@divA)(capturing phase)", "divC(@divC)"]');
+    shouldBe('dispatchedEvent("blur")', '["divB(@divA)(capturing phase)", "divB(@divB)"]');
+
+    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("focus")', '["shadowK(@divJ)(capturing phase)", "shadowK(@shadowK)(capturing phase)", "divL(@divL)"]');
+    shouldBe('dispatchedEvent("blur")', '["shadowF(@divE)(capturing phase)", "shadowF(@shadowF)(capturing phase)", "shadowG(@shadowG)(capturing phase)", "divH(@divH)"]');
 }
 
 test();
index 1f803cdf36bfa2a714e267ada0a5414e7a017d24..2867f5e5cc31cec7b8a72d7b3dc70b8c828ee013 100644 (file)
@@ -1,3 +1,41 @@
+2011-08-17  Hayato Ito  <hayato@chromium.org>
+
+        Implement proper handling of focus/blur events in regard to shadow DOM boundaries.
+        https://bugs.webkit.org/show_bug.cgi?id=61421
+
+        Reviewed by Dimitri Glazkov.
+
+        Introduces FocusEventDispatchMediator/BlurEventDispatchMediator so
+        that focus/blue events are stopped at the lowest common shadow boundary.
+
+        * dom/Document.cpp:
+        (WebCore::Document::setFocusedNode):
+        * dom/Event.cpp:
+        (WebCore::FocusEventDispatchMediator::create):
+        (WebCore::FocusEventDispatchMediator::FocusEventDispatchMediator):
+        (WebCore::FocusEventDispatchMediator::dispatchEvent):
+        (WebCore::BlurEventDispatchMediator::create):
+        (WebCore::BlurEventDispatchMediator::BlurEventDispatchMediator):
+        (WebCore::BlurEventDispatchMediator::dispatchEvent):
+        * dom/Event.h:
+        * dom/Node.cpp:
+        (WebCore::Node::dispatchFocusEvent):
+        (WebCore::Node::dispatchBlurEvent):
+        * dom/Node.h:
+        * html/HTMLFormControlElement.cpp:
+        (WebCore::HTMLFormControlElement::dispatchBlurEvent):
+        * html/HTMLFormControlElement.h:
+        * html/HTMLSelectElement.cpp:
+        (WebCore::HTMLSelectElement::dispatchFocusEvent):
+        (WebCore::HTMLSelectElement::dispatchBlurEvent):
+        * html/HTMLSelectElement.h:
+        * html/HTMLTextFormControlElement.cpp:
+        (WebCore::HTMLTextFormControlElement::dispatchFocusEvent):
+        (WebCore::HTMLTextFormControlElement::dispatchBlurEvent):
+        * html/HTMLTextFormControlElement.h:
+        * page/FocusController.cpp:
+        (WebCore::dispatchEventsOnWindowAndFocusedNode):
+
 2011-08-17  Ben Wells  <benwells@chromium.org>
 
         Canvas fill and fillRect with SourceIn, DestinationIn, SourceOut, DestinationAtop and Copy have errors
index d42d7d4afdc1dc097d2254615bd80c069c3e317a..9370d559d82e7ad358c7c23330dec9f4dd4c01ab 100644 (file)
@@ -3125,7 +3125,7 @@ bool Document::setFocusedNode(PassRefPtr<Node> prpNewFocusedNode)
         }
 
         // Dispatch the blur event and let the node do any other blur related activities (important for text fields)
-        oldFocusedNode->dispatchBlurEvent();
+        oldFocusedNode->dispatchBlurEvent(newFocusedNode);
 
         if (m_focusedNode) {
             // handler shifted focus
@@ -3169,7 +3169,7 @@ bool Document::setFocusedNode(PassRefPtr<Node> prpNewFocusedNode)
         m_focusedNode = newFocusedNode;
 
         // Dispatch the focus event and let the node do any other focus related activities (important for text fields)
-        m_focusedNode->dispatchFocusEvent();
+        m_focusedNode->dispatchFocusEvent(oldFocusedNode);
 
         if (m_focusedNode != newFocusedNode) {
             // handler shifted focus
index 8db5a123aa965bbfaf5e5a69291ce817ee48847d..91379710ef39e3dc80ef98af587e8d01b5040810 100644 (file)
@@ -308,4 +308,39 @@ bool EventDispatchMediator::dispatchEvent(EventDispatcher* dispatcher) const
     return dispatcher->dispatchEvent(m_event.get());
 }
 
+PassRefPtr<FocusEventDispatchMediator> FocusEventDispatchMediator::create(PassRefPtr<Node> oldFocusedNode)
+{
+    return adoptRef(new FocusEventDispatchMediator(oldFocusedNode));
+}
+
+FocusEventDispatchMediator::FocusEventDispatchMediator(PassRefPtr<Node> oldFocusedNode)
+    : EventDispatchMediator(Event::create(eventNames().focusEvent, false, false))
+    , m_oldFocusedNode(oldFocusedNode)
+{
+}
+
+bool FocusEventDispatchMediator::dispatchEvent(EventDispatcher* dispatcher) const
+{
+    dispatcher->adjustRelatedTarget(event(), m_oldFocusedNode);
+    return EventDispatchMediator::dispatchEvent(dispatcher);
+}
+
+PassRefPtr<BlurEventDispatchMediator> BlurEventDispatchMediator::create(PassRefPtr<Node> newFocusedNode)
+{
+    return adoptRef(new BlurEventDispatchMediator(newFocusedNode));
+}
+
+BlurEventDispatchMediator::BlurEventDispatchMediator(PassRefPtr<Node> newFocusedNode)
+    : EventDispatchMediator(Event::create(eventNames().blurEvent, false, false))
+    , m_newFocusedNode(newFocusedNode)
+{
+}
+
+bool BlurEventDispatchMediator::dispatchEvent(EventDispatcher* dispatcher) const
+{
+    dispatcher->adjustRelatedTarget(event(), m_newFocusedNode);
+    return EventDispatchMediator::dispatchEvent(dispatcher);
+}
+
+
 } // namespace WebCore
index f4557812bebb4ed41dc662180582239da50b48e1..0d6217ccac690179706086fa671fd7a2ac9bb1a3 100644 (file)
@@ -230,6 +230,24 @@ inline void EventDispatchMediator::setEvent(PassRefPtr<Event> event)
     m_event = event;
 }
 
+class FocusEventDispatchMediator : public EventDispatchMediator {
+public:
+    static PassRefPtr<FocusEventDispatchMediator> create(PassRefPtr<Node> oldFocusedNode);
+private:
+    explicit FocusEventDispatchMediator(PassRefPtr<Node> oldFocusedNode);
+    virtual bool dispatchEvent(EventDispatcher*) const;
+    RefPtr<Node> m_oldFocusedNode;
+};
+
+class BlurEventDispatchMediator : public EventDispatchMediator {
+public:
+    static PassRefPtr<BlurEventDispatchMediator> create(PassRefPtr<Node> newFocusedNode);
+private:
+    explicit BlurEventDispatchMediator(PassRefPtr<Node> newFocusedNode);
+    virtual bool dispatchEvent(EventDispatcher*) const;
+    RefPtr<Node> m_newFocusedNode;
+};
+
 } // namespace WebCore
 
 #endif // Event_h
index aeb62e0f84dea97d42831998d09c77170bbdc213..a0888ccc5dd3710e7e893f80e4241a96703119c9 100644 (file)
@@ -2777,24 +2777,24 @@ bool Node::dispatchWheelEvent(const PlatformWheelEvent& event)
     return EventDispatcher::dispatchEvent(this, WheelEventDispatchMediator::create(event, document()->defaultView()));
 }
 
-void Node::dispatchFocusEvent()
+void Node::dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode)
 {
     if (document()->page())
         document()->page()->chrome()->client()->elementDidFocus(this);
     
-    dispatchEvent(Event::create(eventNames().focusEvent, false, false));
+    EventDispatcher::dispatchEvent(this, FocusEventDispatchMediator::create(oldFocusedNode));
 }
 
 void Node::willBlur()
 {
 }
 
-void Node::dispatchBlurEvent()
+void Node::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode)
 {
     if (document()->page())
         document()->page()->chrome()->client()->elementDidBlur(this);
-    
-    dispatchEvent(Event::create(eventNames().blurEvent, false, false));
+
+    EventDispatcher::dispatchEvent(this, BlurEventDispatchMediator::create(newFocusedNode));
 }
 
 void Node::dispatchChangeEvent()
index 6eb7679afca6560fcd9a677e210bf9784e05a74a..445d81b491c8dc650f11664304fe5f8aa7dded43 100644 (file)
@@ -562,9 +562,9 @@ public:
     bool dispatchMouseEvent(const PlatformMouseEvent&, const AtomicString& eventType, int clickCount = 0, Node* relatedTarget = 0);
     void dispatchSimulatedClick(PassRefPtr<Event> underlyingEvent, bool sendMouseEvents = false, bool showPressedLook = true);
 
-    virtual void dispatchFocusEvent();
+    virtual void dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode);
     virtual void willBlur();
-    virtual void dispatchBlurEvent();
+    virtual void dispatchBlurEvent(PassRefPtr<Node> newFocusedNode);
     virtual void dispatchChangeEvent();
     virtual void dispatchInputEvent();
 
index f3697ce17154f193cc4a8d91d608d451afa79bb6..de2f3b758571672690ad3ccdcfdbdc5d5c7bd01f 100644 (file)
@@ -430,9 +430,9 @@ void HTMLFormControlElement::setCustomValidity(const String& error)
     validity()->setCustomErrorMessage(error);
 }
 
-void HTMLFormControlElement::dispatchBlurEvent()
+void HTMLFormControlElement::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode)
 {
-    HTMLElement::dispatchBlurEvent();
+    HTMLElement::dispatchBlurEvent(newFocusedNode);
     hideVisibleValidationMessage();
 }
 
index 1b3a5fcb6454895cbfb3088f3035c5b038933ae6..e042ad38e8a0dab9c30d747ac75f31742049365a 100644 (file)
@@ -121,7 +121,7 @@ protected:
 
     virtual void recalcStyle(StyleChange);
 
-    virtual void dispatchBlurEvent();
+    virtual void dispatchBlurEvent(PassRefPtr<Node> newFocusedNode);
     virtual void detach();
 
     // This must be called any time the result of willValidate() has changed.
index 87ac64f40bdc05f85ff3df5efdffb524d0aaa0cc..53c0da7308d1dbad35fff338a82aff3175b05fa4 100644 (file)
@@ -367,16 +367,16 @@ void HTMLSelectElement::reset()
     setNeedsValidityCheck();
 }
 
-void HTMLSelectElement::dispatchFocusEvent()
+void HTMLSelectElement::dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode)
 {
     SelectElement::dispatchFocusEvent(m_data, this);
-    HTMLFormControlElementWithState::dispatchFocusEvent();
+    HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedNode);
 }
 
-void HTMLSelectElement::dispatchBlurEvent()
+void HTMLSelectElement::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode)
 {
     SelectElement::dispatchBlurEvent(m_data, this);
-    HTMLFormControlElementWithState::dispatchBlurEvent();
+    HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedNode);
 }
 
 void HTMLSelectElement::defaultEventHandler(Event* event)
index 4c7d5404d2918eb7f7db6558695653526e2d19e2..a0c4a9d85406feb0b535a8d92f4af64f389f02fa 100644 (file)
@@ -103,8 +103,8 @@ private:
 
     virtual void recalcStyle(StyleChange);
 
-    virtual void dispatchFocusEvent();
-    virtual void dispatchBlurEvent();
+    virtual void dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode);
+    virtual void dispatchBlurEvent(PassRefPtr<Node> newFocusedNode);
     
     virtual bool canStartSelection() const { return false; }
 
index d519db1026815e0c711d71a7e16ec8b67261e27e..d75296acd54af9167315cc0ebdefd123ea300dd1 100644 (file)
@@ -64,20 +64,20 @@ void HTMLTextFormControlElement::insertedIntoDocument()
     setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue);
 }
 
-void HTMLTextFormControlElement::dispatchFocusEvent()
+void HTMLTextFormControlElement::dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode)
 {
     if (supportsPlaceholder())
         updatePlaceholderVisibility(false);
     handleFocusEvent();
-    HTMLFormControlElementWithState::dispatchFocusEvent();
+    HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedNode);
 }
 
-void HTMLTextFormControlElement::dispatchBlurEvent()
+void HTMLTextFormControlElement::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode)
 {
     if (supportsPlaceholder())
         updatePlaceholderVisibility(false);
     handleBlurEvent();
-    HTMLFormControlElementWithState::dispatchBlurEvent();
+    HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedNode);
 }
 
 void HTMLTextFormControlElement::defaultEventHandler(Event* event)
index 242ae054aa8d2b2b743d049d301f54ac71c451ab..efc4b11d37e45c005b2c50e94c704d5b79506853 100644 (file)
@@ -101,8 +101,8 @@ private:
     int computeSelectionEnd() const;
     TextFieldSelectionDirection computeSelectionDirection() const;
 
-    virtual void dispatchFocusEvent();
-    virtual void dispatchBlurEvent();
+    virtual void dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode);
+    virtual void dispatchBlurEvent(PassRefPtr<Node> newFocusedNode);
 
     bool isPlaceholderEmpty() const;
 
index 4aeff0a8975073d54c732c90c131a11be0c4d2d1..f7e70023c525d85e489152ddbce9724f8d1beacc 100644 (file)
@@ -77,10 +77,10 @@ static inline void dispatchEventsOnWindowAndFocusedNode(Document* document, bool
     }
 
     if (!focused && document->focusedNode())
-        document->focusedNode()->dispatchBlurEvent();
+        document->focusedNode()->dispatchBlurEvent(0);
     document->dispatchWindowEvent(Event::create(focused ? eventNames().focusEvent : eventNames().blurEvent, false, false));
     if (focused && document->focusedNode())
-        document->focusedNode()->dispatchFocusEvent();
+        document->focusedNode()->dispatchFocusEvent(0);
 }
 
 FocusController::FocusController(Page* page)