Title: [90004] trunk
Revision
90004
Author
[email protected]
Date
2011-06-29 02:39:58 -0700 (Wed, 29 Jun 2011)

Log Message

2011-06-29  Hayato Ito  <[email protected]>

        Reviewed by Hajime Morita.

        Make a shadow host transfer a focus to the first focusable element in the shadow root when its focus() is called.
        https://bugs.webkit.org/show_bug.cgi?id=62358.

        This patch doesn't take an effect on the following elements to keep a compatibility.
        - <input>, <textarea>, <video> and <audio> elements
        We'll address these elements separately after re-targeting focus events
        (bug 61421) is implemented.

        A shadow root's <content> is not considered in this patch.
        That should be addressed in another patch. See bug 63522.

        * fast/dom/shadow/shadow-host-transfer-focus-expected.txt: Added.
        * fast/dom/shadow/shadow-host-transfer-focus.html: Added.
2011-06-29  Hayato Ito  <[email protected]>

        Reviewed by Hajime Morita.

        Make a shadow host transfer a focus to the first focusable element in the shadow root when its focus() is called.
        https://bugs.webkit.org/show_bug.cgi?id=62358.

        This patch doesn't take an effect on the following elements to keep a compatibility.
        - <input>, <textarea>, <video> and <audio> elements
        We'll address these elements separately after re-targeting focus events
        (bug 61421) is implemented.

        A shadow root's <content> is not considered in this patch.
        That should be addressed in another patch. See bug 63522.

        Test: fast/dom/shadow/shadow-host-transfer-focus.html

        * dom/Element.cpp:
        (WebCore::Element::focus):
        * page/FocusController.cpp:
        (WebCore::shadowRoot):
        (WebCore::isTreeScopeOwner):
        (WebCore::FocusController::transferFocusToElementInShadowRoot):
        (WebCore::hasCustomFocusLogic):
        (WebCore::FocusController::findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot):
        (WebCore::FocusController::advanceFocusInDocumentOrder):
        (WebCore::ownerOfTreeScope):
        (WebCore::FocusController::findFocusableNodeAcrossTreeScope):
        * page/FocusController.h:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (90003 => 90004)


--- trunk/LayoutTests/ChangeLog	2011-06-29 09:32:21 UTC (rev 90003)
+++ trunk/LayoutTests/ChangeLog	2011-06-29 09:39:58 UTC (rev 90004)
@@ -1,3 +1,21 @@
+2011-06-29  Hayato Ito  <[email protected]>
+
+        Reviewed by Hajime Morita.
+
+        Make a shadow host transfer a focus to the first focusable element in the shadow root when its focus() is called.
+        https://bugs.webkit.org/show_bug.cgi?id=62358.
+
+        This patch doesn't take an effect on the following elements to keep a compatibility.
+        - <input>, <textarea>, <video> and <audio> elements
+        We'll address these elements separately after re-targeting focus events
+        (bug 61421) is implemented.
+
+        A shadow root's <content> is not considered in this patch.
+        That should be addressed in another patch. See bug 63522.
+
+        * fast/dom/shadow/shadow-host-transfer-focus-expected.txt: Added.
+        * fast/dom/shadow/shadow-host-transfer-focus.html: Added.
+
 2011-06-29  Roland Steiner  <[email protected]>
 
         Unreviewed: last last ruby expectations for WebKit2 Win.

Added: trunk/LayoutTests/fast/dom/shadow/shadow-host-transfer-focus-expected.txt (0 => 90004)


--- trunk/LayoutTests/fast/dom/shadow/shadow-host-transfer-focus-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/shadow/shadow-host-transfer-focus-expected.txt	2011-06-29 09:39:58 UTC (rev 90004)
@@ -0,0 +1,17 @@
+When a shadow host is focused, the shadow host should transfer focus to the first focusable element in the shadow root if there is an such an element.
+
+Focusing: shadow0
+PASS elementWasFocused(input0) is false
+PASS elementWasFocused(input1) is true
+PASS elementWasFocused(input2) is false
+PASS input0.value is ""
+PASS input1.value is ""
+PASS input2.value is ""
+Pressing: "a"
+PASS input0.value is ""
+PASS input1.value is "a"
+PASS input2.value is ""
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/dom/shadow/shadow-host-transfer-focus.html (0 => 90004)


--- trunk/LayoutTests/fast/dom/shadow/shadow-host-transfer-focus.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/shadow/shadow-host-transfer-focus.html	2011-06-29 09:39:58 UTC (rev 90004)
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<p>
+When a shadow host is focused, the shadow host should transfer focus to the first focusable element in the shadow root if there is an such an element.
+</p>
+<div id="console"></div>
+<script>
+if (window.internals) {
+  layoutTestController.dumpAsText();
+
+  function shadowRoot(shadowHost) {
+    return internals.ensureShadowRoot(shadowHost);
+  }
+
+  function appendChildToShadow(shadowHost, child) {
+    return shadowRoot(shadowHost).appendChild(child)
+  }
+
+  function appendShadowHost(doc, parent) {
+    var shadowHost = doc.createElement('p');
+    shadowHost.tabIndex = 1;  // Makes sure that the shadow host is focusable.
+    parent = parent || doc.body;
+    return parent.appendChild(shadowHost);
+  }
+
+  var focusedElemements = [];
+
+  function focused(elem) {
+    focusedElements.push(elem)
+  }
+
+  function doFocus(elem) {
+    focusedElements = []
+    elem.focus()
+  }
+
+  function elementWasFocused(elem) {
+    for (var i = 0; i < focusedElements.length; ++i) {
+      if (focusedElements[i] == elem) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // For readability, I noted the DOM tree under the test here.
+  //
+  // - document
+  //   - shadow0 (tabindex=1)
+  //     - input0 (tabindex=-1)
+  //     - input1
+  //     - input2
+  var shadow0 = appendShadowHost(document);
+  var input0 = appendChildToShadow(shadow0, document.createElement('input'));
+  input0.tabIndex = -1
+  var input1 = appendChildToShadow(shadow0, document.createElement('input'));
+  var input2 = appendChildToShadow(shadow0, document.createElement('input'));
+
+  var elementIds = ['shadow0', 'input0', 'input1', 'input2']
+  for (var i = 0; i < elementIds.length; i++) {
+    var id = elementIds[i];
+    var element = window[id];
+    element.id = id;
+    element.addEventListener('focus', function() {focused(this);}, false);
+  }
+
+  debug('Focusing: shadow0');
+  doFocus(shadow0);
+  // Commented out due to bug 61421.
+  // shouldBeTrue('elementWasFocused(shadow0)');
+  shouldBeFalse('elementWasFocused(input0)');
+  shouldBeTrue('elementWasFocused(input1)');
+  shouldBeFalse('elementWasFocused(input2)');
+  if (window.eventSender) {
+    // Makes sure that we can change 'input1' element's value by pressing a key.
+    shouldBe('input0.value', '""');
+    shouldBe('input1.value', '""');
+    shouldBe('input2.value', '""');
+    debug('Pressing: "a"')
+    eventSender.keyDown('a');
+    shouldBe('input0.value', '""');
+    shouldBe('input1.value', '"a"');
+    shouldBe('input2.value', '""');
+  }
+
+  var successfullyParsed = true;
+}
+</script>
+<script src=""
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (90003 => 90004)


--- trunk/Source/WebCore/ChangeLog	2011-06-29 09:32:21 UTC (rev 90003)
+++ trunk/Source/WebCore/ChangeLog	2011-06-29 09:39:58 UTC (rev 90004)
@@ -1,3 +1,33 @@
+2011-06-29  Hayato Ito  <[email protected]>
+
+        Reviewed by Hajime Morita.
+
+        Make a shadow host transfer a focus to the first focusable element in the shadow root when its focus() is called.
+        https://bugs.webkit.org/show_bug.cgi?id=62358.
+
+        This patch doesn't take an effect on the following elements to keep a compatibility.
+        - <input>, <textarea>, <video> and <audio> elements
+        We'll address these elements separately after re-targeting focus events
+        (bug 61421) is implemented.
+
+        A shadow root's <content> is not considered in this patch.
+        That should be addressed in another patch. See bug 63522.
+
+        Test: fast/dom/shadow/shadow-host-transfer-focus.html
+
+        * dom/Element.cpp:
+        (WebCore::Element::focus):
+        * page/FocusController.cpp:
+        (WebCore::shadowRoot):
+        (WebCore::isTreeScopeOwner):
+        (WebCore::FocusController::transferFocusToElementInShadowRoot):
+        (WebCore::hasCustomFocusLogic):
+        (WebCore::FocusController::findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot):
+        (WebCore::FocusController::advanceFocusInDocumentOrder):
+        (WebCore::ownerOfTreeScope):
+        (WebCore::FocusController::findFocusableNodeAcrossTreeScope):
+        * page/FocusController.h:
+
 2011-06-29  Ryan Sleevi  <[email protected]>
 
         Reviewed by Dirk Schulze.

Modified: trunk/Source/WebCore/dom/Element.cpp (90003 => 90004)


--- trunk/Source/WebCore/dom/Element.cpp	2011-06-29 09:32:21 UTC (rev 90003)
+++ trunk/Source/WebCore/dom/Element.cpp	2011-06-29 09:39:58 UTC (rev 90004)
@@ -1610,6 +1610,8 @@
         // If a focus event handler changes the focus to a different node it
         // does not make sense to continue and update appearence.
         protect = this;
+        if (shadowRoot() && page->focusController()->transferFocusToElementInShadowRoot(this, restorePreviousSelection))
+            return;
         if (!page->focusController()->setFocusedNode(this, doc->frame()))
             return;
     }

Modified: trunk/Source/WebCore/page/FocusController.cpp (90003 => 90004)


--- trunk/Source/WebCore/page/FocusController.cpp	2011-06-29 09:32:21 UTC (rev 90003)
+++ trunk/Source/WebCore/page/FocusController.cpp	2011-06-29 09:39:58 UTC (rev 90004)
@@ -146,18 +146,33 @@
     }
 }
 
-inline static ShadowRoot* shadowRoot(Node* node)
+static inline ShadowRoot* shadowRoot(Node* node)
 {
     return node->isElementNode() ? toElement(node)->shadowRoot() : 0;
 }
 
-inline static bool isTreeScopeOwner(Node* node)
+static inline bool isTreeScopeOwner(Node* node)
 {
     return node && (node->isFrameOwnerElement() || shadowRoot(node));
 }
 
-Node* FocusController::deepFocusableNode(FocusDirection direction, Node* node, KeyboardEvent* event)
+bool FocusController::transferFocusToElementInShadowRoot(Element* shadowHost, bool restorePreviousSelection)
 {
+    ASSERT(shadowRoot(shadowHost));
+    Node* node = findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(FocusDirectionForward, shadowHost, 0);
+    if (shadowHost == node)
+        return false;
+    toElement(node)->focus(restorePreviousSelection);
+    return true;
+}
+
+static inline bool hasCustomFocusLogic(Node* node)
+{
+    return node->hasTagName(inputTag) || node->hasTagName(textareaTag) || node->hasTagName(videoTag) || node->hasTagName(audioTag);
+}
+
+Node* FocusController::findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(FocusDirection direction, Node* node, KeyboardEvent* event)
+{
     // The node we found might be a HTMLFrameOwnerElement or a shadow host, so descend down the tree until we find either:
     // 1) a focusable node, or
     // 2) the deepest-nested HTMLFrameOwnerElement or shadow host.
@@ -170,11 +185,11 @@
             Document* document = owner->contentFrame()->document();
             foundNode = findFocusableNode(direction, document, 0, event);
         } else {
+            // FIXME: Until a focus re-targeting (bug 61421) is implemented,
+            // skipping these elements is the safest way to keep a compatibility.
+            if (hasCustomFocusLogic(node))
+                break;
             ASSERT(shadowRoot(node));
-            // FIXME: Some elements (e.g. HTMLInputElement and HTMLTextAreaElement) do extra work in their focus() methods.
-            // Skipping these elements is the safest fix until we find a better way.
-            if (node->hasTagName(inputTag) || node->hasTagName(textareaTag))
-                break;
             foundNode = findFocusableNode(direction, shadowRoot(node), 0, event);
         }
         if (!foundNode)
@@ -244,7 +259,7 @@
 
         // Chrome doesn't want focus, so we should wrap focus.
         node = findFocusableNode(direction, m_page->mainFrame()->document(), 0, event);
-        node = deepFocusableNode(direction, node, event);
+        node = findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(direction, node, event);
 
         if (!node)
             return false;
@@ -296,6 +311,16 @@
     return true;
 }
 
+static inline Node* ownerOfTreeScope(TreeScope* scope)
+{
+    ASSERT(scope);
+    if (scope->isShadowRoot())
+        return scope->shadowHost();
+    if (scope->document()->frame())
+        return scope->document()->frame()->ownerElement();
+    return 0;
+}
+
 Node* FocusController::findFocusableNodeAcrossTreeScope(FocusDirection direction, TreeScope* scope, Node* currentNode, KeyboardEvent* event)
 {
     Node* node = findFocusableNode(direction, scope, currentNode, event);
@@ -307,7 +332,7 @@
         node = findFocusableNode(direction, owner->treeScope(), owner, event);
         scope = owner->treeScope();
     }
-    node = deepFocusableNode(direction, node, event);
+    node = findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(direction, node, event);
     return node;
 }
 
@@ -431,16 +456,6 @@
     return previousNodeWithLowerTabIndex(last, startingTabIndex, event);
 }
 
-Node* FocusController::ownerOfTreeScope(TreeScope* scope)
-{
-    ASSERT(scope);
-    if (scope->isShadowRoot())
-        return scope->shadowHost();
-    if (scope->document()->frame())
-        return scope->document()->frame()->ownerElement();
-    return 0;
-}
-
 static bool relinquishesEditingFocus(Node *node)
 {
     ASSERT(node);

Modified: trunk/Source/WebCore/page/FocusController.h (90003 => 90004)


--- trunk/Source/WebCore/page/FocusController.h	2011-06-29 09:32:21 UTC (rev 90003)
+++ trunk/Source/WebCore/page/FocusController.h	2011-06-29 09:39:58 UTC (rev 90004)
@@ -34,6 +34,7 @@
 namespace WebCore {
 
 struct FocusCandidate;
+class Element;
 class Frame;
 class IntRect;
 class KeyboardEvent;
@@ -61,13 +62,14 @@
     void setFocused(bool);
     bool isFocused() const { return m_isFocused; }
 
+    bool transferFocusToElementInShadowRoot(Element* shadowHost, bool restorePreviousSelection);
+
 private:
     bool advanceFocusDirectionally(FocusDirection, KeyboardEvent*);
     bool advanceFocusInDocumentOrder(FocusDirection, KeyboardEvent*, bool initialFocus);
 
     Node* findFocusableNodeAcrossTreeScope(FocusDirection, TreeScope* startScope, Node* start, KeyboardEvent*);
-    Node* deepFocusableNode(FocusDirection, Node*, KeyboardEvent*);
-    Node* ownerOfTreeScope(TreeScope*);
+    Node* findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(FocusDirection, Node*, KeyboardEvent*);
 
     // Searches through the given tree scope, starting from start node, for the next/previous selectable element that comes after/before start node.
     // The order followed is as specified in section 17.11.1 of the HTML4 spec, which is elements with tab indexes
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to