Unreviewed, rolling out r112500.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Mar 2012 07:18:09 +0000 (07:18 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Mar 2012 07:18:09 +0000 (07:18 +0000)
http://trac.webkit.org/changeset/112500
https://bugs.webkit.org/show_bug.cgi?id=82576

assertion failed on fast/events/tab-test-not-visible-
imagemap.html on gtk/qt (Requested by hayato on #webkit).

Patch by Sheriff Bot <webkit.review.bot@gmail.com> on 2012-03-29

Source/WebCore:

* dom/Element.cpp:
(WebCore::Element::focus):
* page/FocusController.cpp:
(WebCore):
(WebCore::shadowRoot):
(WebCore::isTreeScopeOwner):
(WebCore::FocusController::transferFocusToElementInShadowRoot):
(WebCore::hasCustomFocusLogic):
(WebCore::FocusController::findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot):
(WebCore::FocusController::advanceFocusInDocumentOrder):
(WebCore::ownerOfTreeScope):
(WebCore::FocusController::findFocusableNodeAcrossTreeScope):
(WebCore::FocusController::findFocusableNode):
(WebCore::nextNodeWithExactTabIndex):
(WebCore::previousNodeWithExactTabIndex):
(WebCore::nextNodeWithGreaterTabIndex):
(WebCore::previousNodeWithLowerTabIndex):
(WebCore::FocusController::nextFocusableNode):
(WebCore::FocusController::previousFocusableNode):
* page/FocusController.h:
(WebCore):
(FocusController):

LayoutTests:

* fast/dom/shadow/focus-navigation-expected.txt: Removed.
* fast/dom/shadow/focus-navigation.html: Removed.
* fast/dom/shadow/resources/shadow-dom.js:
(getNodeInShadowTreeStack):
* fast/dom/shadow/shadow-host-transfer-focus-expected.txt: Added.
* fast/dom/shadow/shadow-host-transfer-focus.html: Added.
* fast/dom/shadow/tab-order-iframe-and-shadow-expected.txt:
* fast/dom/shadow/tab-order-iframe-and-shadow.html:

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

12 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/shadow/focus-navigation-expected.txt [deleted file]
LayoutTests/fast/dom/shadow/focus-navigation.html [deleted file]
LayoutTests/fast/dom/shadow/resources/shadow-dom.js
LayoutTests/fast/dom/shadow/shadow-host-transfer-focus-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/shadow/shadow-host-transfer-focus.html [new file with mode: 0644]
LayoutTests/fast/dom/shadow/tab-order-iframe-and-shadow-expected.txt
LayoutTests/fast/dom/shadow/tab-order-iframe-and-shadow.html
Source/WebCore/ChangeLog
Source/WebCore/dom/Element.cpp
Source/WebCore/page/FocusController.cpp
Source/WebCore/page/FocusController.h

index 2d1211b..5614b84 100644 (file)
@@ -1,3 +1,21 @@
+2012-03-29  Sheriff Bot  <webkit.review.bot@gmail.com>
+
+        Unreviewed, rolling out r112500.
+        http://trac.webkit.org/changeset/112500
+        https://bugs.webkit.org/show_bug.cgi?id=82576
+
+        assertion failed on fast/events/tab-test-not-visible-
+        imagemap.html on gtk/qt (Requested by hayato on #webkit).
+
+        * fast/dom/shadow/focus-navigation-expected.txt: Removed.
+        * fast/dom/shadow/focus-navigation.html: Removed.
+        * fast/dom/shadow/resources/shadow-dom.js:
+        (getNodeInShadowTreeStack):
+        * fast/dom/shadow/shadow-host-transfer-focus-expected.txt: Added.
+        * fast/dom/shadow/shadow-host-transfer-focus.html: Added.
+        * fast/dom/shadow/tab-order-iframe-and-shadow-expected.txt:
+        * fast/dom/shadow/tab-order-iframe-and-shadow.html:
+
 2012-03-28  Hayato Ito  <hayato@chromium.org>
 
         Make focus navigation be compliant with Shadow DOM spec.
diff --git a/LayoutTests/fast/dom/shadow/focus-navigation-expected.txt b/LayoutTests/fast/dom/shadow/focus-navigation-expected.txt
deleted file mode 100644 (file)
index 01958de..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-This tests that pressing Tab key should traverse into shadow DOM subtrees, and pressing Shift-Tab should reverse the order.
-
-Should move from input-A-1 to input-B-1 in forward
-PASS
-Should move from input-B-1 to input-C-1 in forward
-PASS
-Should move from input-C-1 to host-B in forward
-PASS
-Should move from host-B to host-B//younger-input-A-1 in forward
-PASS
-Should move from host-B//younger-input-A-1 to host-B/older-input-A-1 in forward
-PASS
-Should move from host-B/older-input-A-1 to light-child-selected-1 in forward
-PASS
-Should move from light-child-selected-1 to host-B/older-input-B-1 in forward
-PASS
-Should move from host-B/older-input-B-1 to host-B//younger-input-B-1 in forward
-PASS
-Should move from host-B//younger-input-B-1 to host-B//younger-input-A-0 in forward
-PASS
-Should move from host-B//younger-input-A-0 to host-B/older-input-A-0 in forward
-PASS
-Should move from host-B/older-input-A-0 to light-child-selected-0 in forward
-PASS
-Should move from light-child-selected-0 to host-B/older-input-B-0 in forward
-PASS
-Should move from host-B/older-input-B-0 to host-B//younger-input-B-0 in forward
-PASS
-Should move from host-B//younger-input-B-0 to input-D-1 in forward
-PASS
-Should move from input-D-1 to input-A-0 in forward
-PASS
-Should move from input-A-0 to host-A/input-1 in forward
-PASS
-Should move from host-A/input-1 to host-A/input-20 in forward
-PASS
-Should move from host-A/input-20 to host-A/nested-host in forward
-PASS
-Should move from host-A/nested-host to host-A/nested-host/input-15 in forward
-PASS
-Should move from host-A/nested-host/input-15 to host-A/nested-host/input-25 in forward
-PASS
-Should move from host-A/nested-host/input-25 to input-B-0 in forward
-PASS
-Should move from input-B-0 to input-C-0 in forward
-PASS
-Should move from input-C-0 to input-D-0 in forward
-PASS
-Should move from input-D-0 to input-A-1 in forward
-PASS
-Should move from input-A-1 to input-D-0 in backward
-PASS
-Should move from input-D-0 to input-C-0 in backward
-PASS
-Should move from input-C-0 to input-B-0 in backward
-PASS
-Should move from input-B-0 to host-A/nested-host/input-25 in backward
-PASS
-Should move from host-A/nested-host/input-25 to host-A/nested-host/input-15 in backward
-PASS
-Should move from host-A/nested-host/input-15 to host-A/nested-host in backward
-PASS
-Should move from host-A/nested-host to host-A/input-20 in backward
-PASS
-Should move from host-A/input-20 to host-A/input-1 in backward
-PASS
-Should move from host-A/input-1 to input-A-0 in backward
-PASS
-Should move from input-A-0 to input-D-1 in backward
-PASS
-Should move from input-D-1 to host-B//younger-input-B-0 in backward
-PASS
-Should move from host-B//younger-input-B-0 to host-B/older-input-B-0 in backward
-PASS
-Should move from host-B/older-input-B-0 to light-child-selected-0 in backward
-PASS
-Should move from light-child-selected-0 to host-B/older-input-A-0 in backward
-PASS
-Should move from host-B/older-input-A-0 to host-B//younger-input-A-0 in backward
-PASS
-Should move from host-B//younger-input-A-0 to host-B//younger-input-B-1 in backward
-PASS
-Should move from host-B//younger-input-B-1 to host-B/older-input-B-1 in backward
-PASS
-Should move from host-B/older-input-B-1 to light-child-selected-1 in backward
-PASS
-Should move from light-child-selected-1 to host-B/older-input-A-1 in backward
-PASS
-Should move from host-B/older-input-A-1 to host-B//younger-input-A-1 in backward
-PASS
-Should move from host-B//younger-input-A-1 to host-B in backward
-PASS
-Should move from host-B to input-C-1 in backward
-PASS
-Should move from input-C-1 to input-B-1 in backward
-PASS
-Should move from input-B-1 to input-A-1 in backward
-PASS
-Should move from input-A-1 to input-D-0 in backward
-PASS
-Test finished.
-
-
-
-
-
diff --git a/LayoutTests/fast/dom/shadow/focus-navigation.html b/LayoutTests/fast/dom/shadow/focus-navigation.html
deleted file mode 100644 (file)
index 7e70957..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="../../js/resources/js-test-pre.js"></script>
-<script src="resources/shadow-dom.js"></script>
-</head>
-<body>
-<p>This tests that pressing Tab key should traverse into shadow DOM subtrees, and pressing Shift-Tab should reverse the order.</p>
-<pre id="console"></pre>
-<script>
-
-function prepareDOMTree(parent)
-{
-    parent.appendChild(
-        // FIXME: Use more descriptive ids for each elements.
-        createDOM('div', {'id': 'top-div'},
-                  createDOM('input', {'id': 'input-A-1', 'tabindex': 1}),
-                  createDOM('input', {'id': 'input-B-1', 'tabindex': 1}),
-                  createDOM('div'),  // Should not be selected.
-                  createDOM('input', {'id': 'input-A-0', 'tabindex': 0}),
-                  createDOM('div', {'id': 'host-A', 'tabindex': -1},
-                            createShadowRoot(
-                                createDOM('input', {'id': 'input-1', 'tabindex': 1}),
-                                createDOM('div', {'id': 'nested-host', 'tabindex': 30},
-                                          createShadowRoot(
-                                              createDOM('input', {'tabindex': -1}),
-                                              createDOM('input', {'id': 'input-15', 'tabindex': 15}),
-                                              createDOM('input', {'id': 'input-25', 'tabindex': 25}))),
-                                createDOM('input', {'id': 'input-20', 'tabindex': 20}))),
-                  createDOM('input', {'id': 'input-C-1', 'tabindex': 1}),
-                  createDOM('input', {'id': 'input-B-0', 'tabindex': 0}),
-                  createDOM('div', {'id': 'host-B', 'tabindex': 1},
-                            createShadowRoot(
-                                createDOM('input', {'id': 'older-input-A-0', 'tabindex': 0}),
-                                createDOM('input', {'id': 'older-input-A-1', 'tabindex': 1}),
-                                createDOM('content', {'select': '#light-child-selected-0, #light-child-selected-1'}),
-                                createDOM('input', {'id': 'older-input-B-0', 'tabindex': 0}),
-                                createDOM('input', {'id': 'older-input-B-1', 'tabindex': 1})),
-                            createShadowRoot(
-                                createDOM('input', {'id': 'younger-input-A-0', 'tabindex': 0}),
-                                createDOM('input', {'id': 'younger-input-A-1', 'tabindex': 1}),  // The first node in the focusScope
-                                createDOM('shadow'),
-                                createDOM('input', {'id': 'younger-input-B-0', 'tabindex': 0}),  // The last node in the focusScope
-                                createDOM('input', {'id': 'younger-input-B-1', 'tabindex': 1})),
-                            createDOM('input', {'id': 'light-child-selected-0', 'tabindex': 0}),
-                            createDOM('input', {'id': 'light-child-selected-1', 'tabindex': 1}),
-                            createDOM('input', {'id': 'light-child-non-selected-1', 'tabindex': 1})),
-                  createDOM('input', {'id': 'input-D-1', 'tabindex': 1}),
-                  createDOM('input', {'id': 'input-C-0', 'tabindex': 0}),
-                  createDOM('div', {'id': 'host-C', 'tabindex': -1},
-                            createShadowRoot(
-                                createDOM('input', {'tabindex': -1}))),
-                  createDOM('input', {'id': 'input-D-0', 'tabindex': 0})));
-
-    parent.offsetLeft;
-}
-
-function test() {
-    if (window.layoutTestController)
-        layoutTestController.dumpAsText();
-
-    if (!window.eventSender) {
-        testFailed('');
-        return;
-    }
-
-    prepareDOMTree(document.body);
-
-    // FIXME: Output inserted comments in this array to expected.txt for readability of the result.
-    elementsNavigatedInForward = [
-        'input-A-1', 'input-B-1',
-        // Traverse elements which have tabindex=1.
-        // Should skip every elements, even though they have the same tabindex, in non-focusable shadow host (id=host-A) 
-        // since non-focusable shadow host acted as if they were assinged to tabindex=0 so it was skipped in this turn.
-        'input-C-1',
-
-        // Traverse a focusable shadow host.
-        'host-B',
-
-        // Enter a focus scope of the shadow host and travese the first focusable node in shadow DOM navigation.
-        'host-B//younger-input-A-1',
-
-        // A <shadow> element should be resolved.
-        'host-B/older-input-A-1',
-
-        // A <content> element should be resolved.
-        'light-child-selected-1',
-        'host-B/older-input-B-1',
-        'host-B//younger-input-B-1',
-
-        // All elements with tabindex=1 had been traversed. So elements with tabindex=0 shooud follow.
-        'host-B//younger-input-A-0',
-        'host-B/older-input-A-0',
-        'light-child-selected-0',
-        'host-B/older-input-B-0',
-        'host-B//younger-input-B-0',
-
-        // Exit a focus scope of the shadow host (id=host-B) and traverse the next.
-        'input-D-1',
-
-        // All elements with tabindex=1 had been traversed in the outermost scope.
-        // So traverse elements with tabindex=0 in next.
-        'input-A-0',
-
-        // A non-focusable shadow host (id=host-A) will be "replaced" with its shadow DOM navigation.
-        'host-A/input-1',
-        'host-A/input-20',
-        'host-A/nested-host',
-
-        // Enter a nested focus scope inside of a shadow host (id=nested-host).
-        'host-A/nested-host/input-15',
-        'host-A/nested-host/input-25',
-
-        // Exit a nested shadow host.
-        'input-B-0',
-        'input-C-0',
-
-        // A non-focusable shadow host (id=host-C), which does not have focusable elements, should be skipped entirely.
-        'input-D-0',
-
-        // Wraps to the first element in the outermost focus scope.
-        'input-A-1',
-    ];
-
-
-    elementsNavigatedInBackward = [
-        'input-A-1',
-        'input-D-0',
-        'input-C-0',
-        'input-B-0',
-
-        // Tricky navigation. Travarse the deeply last element in nested shadow DOM navigation.
-        'host-A/nested-host/input-25',
-        'host-A/nested-host/input-15',
-        'host-A/nested-host',
-        'host-A/input-20',
-        'host-A/input-1',
-
-        // Exit a shado host.
-        'input-A-0',
-
-        // All elements with tabindex=0 had been traversed. So traverse elements with tabindex=1 in reverse order.
-        'input-D-1',
-
-        // Yet another tricky navigation. Should traverse the last element in the shadow DOM navigation.
-        'host-B//younger-input-B-0',
-        'host-B/older-input-B-0',
-        'light-child-selected-0',
-        'host-B/older-input-A-0',
-        'host-B//younger-input-A-0',
-
-        'host-B//younger-input-B-1',
-        'host-B/older-input-B-1',
-        'light-child-selected-1',
-        'host-B/older-input-A-1',
-        'host-B//younger-input-A-1',
-
-        // A shadow host should be selected just after the first element in its shadow DOM navigation.
-        // Note that a 'focus' event is not called here since a focus moves within the same shadow host.
-        'host-B',
-        'input-C-1',
-        'input-B-1',
-        'input-A-1',
-
-        // Wraps to the last element in the outermost focus scope.
-        'input-D-0',
-    ];
-
-    testFocusNavigationFowrad(elementsNavigatedInForward);
-    testFocusNavigationBackward(elementsNavigatedInBackward);
-
-    debug('Test finished.');
-}
-
-test();
-
-</script>
-</body>
-</html>
index c3be8a7..1557602 100644 (file)
@@ -34,22 +34,12 @@ function createDOM(tagName, attributes)
     return element;
 }
 
-function isShadowHost(node)
-{
-    return window.internals.oldestShadowRoot(node);
-}
-
 function isShadowRoot(node)
 {
     // FIXME: window.internals should have internals.isShadowRoot(node).
     return node.nodeName == "#shadow-root" || node.host;
 }
 
-function isIframeElement(element)
-{
-    return element && element.nodeName == 'IFRAME';
-}
-
 // You can spefify youngerShadowRoot by consecutive slashes.
 // See LayoutTests/fast/dom/shadow/get-element-by-id-in-shadow-root.html for actual usages.
 function getNodeInShadowTreeStack(path)
@@ -57,101 +47,13 @@ function getNodeInShadowTreeStack(path)
     var ids = path.split('/');
     var node = document.getElementById(ids[0]);
     for (var i = 1; node != null && i < ids.length; ++i) {
-        if (isIframeElement(node)) {
-            node = node.contentDocument.getElementById(ids[i]);
-            continue;
-        }
         if (isShadowRoot(node))
             node = internals.youngerShadowRoot(node);
-        else if (internals.oldestShadowRoot(node))
-            node = internals.oldestShadowRoot(node);
         else
-            return null;
+            node = internals.oldestShadowRoot(node);
         if (ids[i] != '')
             node = internals.getElementByIdInShadowRoot(node, ids[i]);
     }
     return node;
 }
 
-function dumpNode(node)
-{
-    if (!node)
-      return 'null';
-    var output = '' + node;
-    if (node.id)
-        output += ' id=' + node.id;
-    return output;
-}
-
-function innermostActiveElement(element)
-{
-    element = element || document.activeElement;
-    if (isIframeElement(element)) {
-        if (element.contentDocument.activeElement)
-            return innermostActiveElement(element.contentDocument.activeElement);
-        return element;
-    }
-    if (isShadowHost(element)) {
-        var shadowRoot = window.internals.oldestShadowRoot(element);
-        while (shadowRoot) {
-            if (shadowRoot.activeElement)
-                return innermostActiveElement(shadowRoot.activeElement);
-            shadowRoot = window.internals.youngerShadowRoot(shadowRoot);
-        }
-    }
-    return element;
-}
-
-function isInnermostActiveElement(id)
-{
-    var element = getNodeInShadowTreeStack(id);
-    if (!element) {
-        debug('FAIL: There is no such element with id: '+ from);
-        return false;
-    }
-    if (element == innermostActiveElement())
-        return true;
-    debug('Expected innermost activeElement is ' + id + ', but actual innermost activeElement is ' + dumpNode(innermostActiveElement()));
-    return false;
-}
-
-function shouldNavigateFocus(from, to, direction)
-{
-    debug('Should move from ' + from + ' to ' + to + ' in ' + direction);
-    var fromElement = getNodeInShadowTreeStack(from);
-    if (!fromElement) {
-      debug('FAIL: There is no such element with id: '+ from);
-      return;
-    }
-    fromElement.focus();
-    if (direction == 'forward')
-        navigateFocusForward();
-    else
-        navigateFocusBackward();
-    if (isInnermostActiveElement(to))
-        debug('PASS');
-    else
-        debug('FAIL');
-}
-
-function navigateFocusForward()
-{
-    eventSender.keyDown('\t');
-}
-
-function navigateFocusBackward()
-{
-    eventSender.keyDown('\t', ['shiftKey']);
-}
-
-function testFocusNavigationFowrad(elements)
-{
-    for (var i = 0; i + 1 < elements.length; ++i)
-        shouldNavigateFocus(elements[i], elements[i + 1], 'forward');
-}
-
-function testFocusNavigationBackward(elements)
-{
-    for (var i = 0; i + 1 < elements.length; ++i)
-        shouldNavigateFocus(elements[i], elements[i + 1], 'backward');
-}
diff --git a/LayoutTests/fast/dom/shadow/shadow-host-transfer-focus-expected.txt b/LayoutTests/fast/dom/shadow/shadow-host-transfer-focus-expected.txt
new file mode 100644 (file)
index 0000000..f681b09
--- /dev/null
@@ -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
+
diff --git a/LayoutTests/fast/dom/shadow/shadow-host-transfer-focus.html b/LayoutTests/fast/dom/shadow/shadow-host-transfer-focus.html
new file mode 100644 (file)
index 0000000..0fb6da7
--- /dev/null
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../js/resources/js-test-pre.js"></script>
+</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', '""');
+  }
+}
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
index f86a3a0..93a5e8c 100644 (file)
@@ -1,57 +1,34 @@
-CONSOLE MESSAGE: line 104: Uncaught ReferenceError: log is not defined
-This tests that pressing Tab key should traverse into iframe and shadow tree, and pressing Shift-Tab should reverse the order.
+This tests that pressing Tab key should traverse into iframe and shadow tree, and pressing Shift-Tab should reverse the order. Makes sure that a shadow host element should act like a iframe element.
 
-Should move from input-01 to input-13 in forward
-PASS
-Should move from input-13 to input-15 in forward
-PASS
-Should move from input-15 to input-02 in forward
-PASS
-Should move from input-02 to host-01/input-03 in forward
-PASS
-Should move from host-01/input-03 to input-04 in forward
-PASS
-Should move from input-04 to iframe/input-06 in forward
-PASS
-Should move from iframe/input-06 to iframe/host-02 in forward
-PASS
-Should move from iframe/host-02 to iframe/host-02/input-09 in forward
-PASS
-Should move from iframe/host-02/input-09 to iframe/host-02/input-08 in forward
-PASS
-Should move from iframe/host-02/input-08 to iframe/input-12 in forward
-PASS
-Should move from iframe/input-12 to iframe/input-11 in forward
-PASS
-Should move from iframe/input-11 to iframe/input-05 in forward
-PASS
-Should move from iframe/input-05 to input-14 in forward
-PASS
-Should move from input-14 to iframe/input-05 in backward
-PASS
-Should move from iframe/input-05 to iframe/input-11 in backward
-PASS
-Should move from iframe/input-11 to iframe/input-12 in backward
-PASS
-Should move from iframe/input-12 to iframe/host-02/input-08 in backward
-PASS
-Should move from iframe/host-02/input-08 to iframe/host-02/input-09 in backward
-PASS
-Should move from iframe/host-02/input-09 to iframe/host-02 in backward
-PASS
-Should move from iframe/host-02 to iframe/input-06 in backward
-PASS
-Should move from iframe/input-06 to input-04 in backward
-PASS
-Should move from input-04 to host-01/input-03 in backward
-PASS
-Should move from host-01/input-03 to input-02 in backward
-PASS
-Should move from input-02 to input-15 in backward
-PASS
-Should move from input-15 to input-13 in backward
-PASS
-Should move from input-13 to input-01 in backward
-PASS
+Focus input-01.
+id:input-01(tabIndex=1) is focused.
+
+Press Tab 11 times.
+id:input-13(tabIndex=1) is focused.
+id:input-15(tabIndex=2) is focused.
+id:input-02(tabIndex=0) is focused.
+id:input-04(tabIndex=0) is focused.
+id:iframe-input-06(tabIndex=1) is focused.
+id:iframe-shadow-input-09(tabIndex=1) is focused.
+id:iframe-shadow-input-08(tabIndex=0) is focused.
+id:iframe-input-12(tabIndex=1) is focused.
+id:iframe-input-11(tabIndex=2) is focused.
+id:iframe-input-05(tabIndex=0) is focused.
+id:input-14(tabIndex=0) is focused.
+
+Press Shift-Tab 11 times.
+id:iframe-input-05(tabIndex=0) is focused.
+id:iframe-input-11(tabIndex=2) is focused.
+id:iframe-input-12(tabIndex=1) is focused.
+id:iframe-shadow-input-08(tabIndex=0) is focused.
+id:iframe-shadow-input-09(tabIndex=1) is focused.
+id:iframe-input-06(tabIndex=1) is focused.
+id:input-04(tabIndex=0) is focused.
+id:input-02(tabIndex=0) is focused.
+id:input-15(tabIndex=2) is focused.
+id:input-13(tabIndex=1) is focused.
+id:input-01(tabIndex=1) is focused.
+
+Test finished.
 
 
index 58bba8a..10e9162 100644 (file)
@@ -1,18 +1,37 @@
 <!DOCTYPE html>
 <html>
-<head>
-<script src="../../js/resources/js-test-pre.js"></script>
-<script src="resources/shadow-dom.js"></script>
-</head>
 <body>
-<p>This tests that pressing Tab key should traverse into iframe and shadow tree, and pressing Shift-Tab should reverse the order.</p>
+<p>This tests that pressing Tab key should traverse into iframe and shadow tree, and pressing Shift-Tab should reverse the order.
+ Makes sure that a shadow host element should act like a iframe element.</p>
 <pre id="console"></pre>
 <script>
+function log(msg) {
+    document.querySelector('#console').textContent += (msg + '\n');
+}
+
+function description(element) {
+   var msg = '';
+    if (element.id) {
+      msg += 'id:' + element.id;
+    }
+    msg += '(tabIndex=' + element.tabIndex + ')';
+    return msg;
+}
+
+function onFocus(event) {
+    log(description(event.target) + ' is focused.');
+}
+
+function addFocusEventListener(element) {
+    element.addEventListener('focus', onFocus, false);
+}
+
 function createTextInputElement(doc, id, tabIndex) {
     var input = doc.createElement('input');
     input.type = 'text';
     input.id = id;
     input.tabIndex = tabIndex;
+    addFocusEventListener(input);
     return input;
 }
 
@@ -25,11 +44,10 @@ if (window.layoutTestController) {
 
     function addShadowHost(doc) {
         var shadowHost = doc.createElement('p');
-        shadowHost.id = 'host-01';
-        shadowHost.tabIndex = -1;
+        shadowHost.tabIndex = -1;  // This shadow host (and a shadow tree in that) should be skipped.
         var shadow = internals.ensureShadowRoot(shadowHost);
         doc.body.appendChild(shadowHost);
-        shadow.appendChild(createTextInputElement(doc, 'input-03', 0));
+        shadow.appendChild(createTextInputElement(doc, 'shadow-input-03', 0));
     }
     addShadowHost(doc);
 
@@ -37,29 +55,27 @@ if (window.layoutTestController) {
 
     function addIframe(doc) {
         var iframe = doc.createElement('iframe');
-        iframe.id = 'iframe';
         doc.body.appendChild(iframe);
         doc = iframe.contentDocument;
 
-        doc.body.appendChild(createTextInputElement(doc, 'input-05', 0));
-        doc.body.appendChild(createTextInputElement(doc, 'input-06', 1));
-        doc.body.appendChild(createTextInputElement(doc, 'input-07', -1));
+        doc.body.appendChild(createTextInputElement(doc, 'iframe-input-05', 0));
+        doc.body.appendChild(createTextInputElement(doc, 'iframe-input-06', 1));
+        doc.body.appendChild(createTextInputElement(doc, 'iframe-input-07', -1));
 
         function addShadowHost(doc) {
             var shadowHost = doc.createElement('p');
-            shadowHost.id = 'host-02';
             shadowHost.tabIndex = 1;
             var shadow = internals.ensureShadowRoot(shadowHost);
             doc.body.appendChild(shadowHost);
 
-            shadow.appendChild(createTextInputElement(doc, 'input-08', 0));
-            shadow.appendChild(createTextInputElement(doc, 'input-09', 1));
-            shadow.appendChild(createTextInputElement(doc, 'input-10', -1));
+            shadow.appendChild(createTextInputElement(doc, 'iframe-shadow-input-08', 0));
+            shadow.appendChild(createTextInputElement(doc, 'iframe-shadow-input-09', 1));
+            shadow.appendChild(createTextInputElement(doc, 'iframe-shadow-input-10', -1));
         }
         addShadowHost(doc);
 
-        doc.body.appendChild(createTextInputElement(doc, 'input-11', 2));
-        doc.body.appendChild(createTextInputElement(doc, 'input-12', 1));
+        doc.body.appendChild(createTextInputElement(doc, 'iframe-input-11', 2));
+        doc.body.appendChild(createTextInputElement(doc, 'iframe-input-12', 1));
     }
     addIframe(doc);
 
@@ -67,40 +83,20 @@ if (window.layoutTestController) {
     doc.body.appendChild(createTextInputElement(doc, 'input-14', 0));
     doc.body.appendChild(createTextInputElement(doc, 'input-15', 2));
 
-    testFocusNavigationFowrad([
-        'input-01',
-        'input-13',
-        'input-15',
-        'input-02',
-        'host-01/input-03',
-        'input-04',
-        'iframe/input-06',
-        'iframe/host-02',
-        'iframe/host-02/input-09',
-        'iframe/host-02/input-08',
-        'iframe/input-12',
-        'iframe/input-11',
-        'iframe/input-05',
-        'input-14',
-    ]);
-
-    testFocusNavigationBackward([
-        'input-14',
-        'iframe/input-05',
-        'iframe/input-11',
-        'iframe/input-12',
-        'iframe/host-02/input-08',
-        'iframe/host-02/input-09',
-        'iframe/host-02',
-        'iframe/input-06',
-        'input-04',
-        'host-01/input-03',
-        'input-02',
-        'input-15',
-        'input-13',
-        'input-01',
-    ]);
+    log('Focus input-01.');
+    doc.getElementById('input-01').focus();
 
+    if (window.eventSender) {
+        var pressed = 11;
+        log('\nPress Tab ' + pressed + ' times.');
+        for (var i = 0; i < pressed; ++i) {
+            eventSender.keyDown('\t');
+        }
+        log('\nPress Shift-Tab ' + pressed + ' times.');
+        for (var i = 0; i < pressed; ++i) {
+            eventSender.keyDown('\t', ['shiftKey']);
+        }
+   }
    log('\nTest finished.');
 }
 </script>
index fc356ad..cf0633a 100644 (file)
@@ -1,3 +1,35 @@
+2012-03-29  Sheriff Bot  <webkit.review.bot@gmail.com>
+
+        Unreviewed, rolling out r112500.
+        http://trac.webkit.org/changeset/112500
+        https://bugs.webkit.org/show_bug.cgi?id=82576
+
+        assertion failed on fast/events/tab-test-not-visible-
+        imagemap.html on gtk/qt (Requested by hayato on #webkit).
+
+        * dom/Element.cpp:
+        (WebCore::Element::focus):
+        * page/FocusController.cpp:
+        (WebCore):
+        (WebCore::shadowRoot):
+        (WebCore::isTreeScopeOwner):
+        (WebCore::FocusController::transferFocusToElementInShadowRoot):
+        (WebCore::hasCustomFocusLogic):
+        (WebCore::FocusController::findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot):
+        (WebCore::FocusController::advanceFocusInDocumentOrder):
+        (WebCore::ownerOfTreeScope):
+        (WebCore::FocusController::findFocusableNodeAcrossTreeScope):
+        (WebCore::FocusController::findFocusableNode):
+        (WebCore::nextNodeWithExactTabIndex):
+        (WebCore::previousNodeWithExactTabIndex):
+        (WebCore::nextNodeWithGreaterTabIndex):
+        (WebCore::previousNodeWithLowerTabIndex):
+        (WebCore::FocusController::nextFocusableNode):
+        (WebCore::FocusController::previousFocusableNode):
+        * page/FocusController.h:
+        (WebCore):
+        (FocusController):
+
 2012-03-28  Kent Tamura  <tkent@chromium.org>
 
         Add TextFieldDecorationElement::decorate()
index 9532708..b29bfb4 100644 (file)
@@ -1548,6 +1548,8 @@ void Element::focus(bool restorePreviousSelection)
         // 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 (hasShadowRoot() && page->focusController()->transferFocusToElementInShadowRoot(this, restorePreviousSelection))
+            return;
         if (!page->focusController()->setFocusedNode(this, doc->frame()))
             return;
     }
index 504e4be..fb16df0 100644 (file)
@@ -48,7 +48,6 @@
 #include "KeyboardEvent.h"
 #include "Page.h"
 #include "Range.h"
-#include "ReifiedTreeTraversal.h"
 #include "RenderLayer.h"
 #include "RenderObject.h"
 #include "RenderWidget.h"
@@ -66,54 +65,6 @@ namespace WebCore {
 using namespace HTMLNames;
 using namespace std;
 
-static inline bool isShadowHost(const Node* node)
-{
-    return node && node->isElementNode() && toElement(node)->hasShadowRoot();
-}
-
-FocusScope::FocusScope(TreeScope* treeScope)
-    : m_rootTreeScope(treeScope)
-{
-    ASSERT(treeScope);
-    ASSERT(!treeScope->rootNode()->isShadowRoot() || toShadowRoot(treeScope->rootNode())->isYoungest());
-}
-
-Node* FocusScope::rootNode() const
-{
-    return m_rootTreeScope->rootNode();
-}
-
-Element* FocusScope::owner() const
-{
-    Node* root = rootNode();
-    if (root->isShadowRoot())
-        return root->shadowHost();
-    if (Frame* frame = root->document()->frame())
-        return frame->ownerElement();
-    return 0;
-}
-
-FocusScope FocusScope::focusScopeOf(Node* node)
-{
-    ASSERT(node);
-    TreeScope* scope = node->treeScope();
-    if (scope->rootNode()->isShadowRoot())
-        return FocusScope(toShadowRoot(scope->rootNode())->tree()->youngestShadowRoot());
-    return FocusScope(scope);
-}
-
-FocusScope FocusScope::focusScopeOwnedByShadowHost(Node* node)
-{
-    ASSERT(isShadowHost(node));
-    return FocusScope(toElement(node)->shadowTree()->youngestShadowRoot());
-}
-
-FocusScope FocusScope::focusScopeOwnedByIFrame(HTMLFrameOwnerElement* frame)
-{
-    ASSERT(frame && frame->contentFrame());
-    return FocusScope(frame->contentFrame()->document());
-}
-
 static inline void dispatchEventsOnWindowAndFocusedNode(Document* document, bool focused)
 {
     // If we have a focused node we should dispatch blur on it before we blur the window.
@@ -133,35 +84,6 @@ static inline void dispatchEventsOnWindowAndFocusedNode(Document* document, bool
         document->focusedNode()->dispatchFocusEvent(0);
 }
 
-static inline bool hasCustomFocusLogic(Node* node)
-{
-    return node->hasTagName(inputTag) || node->hasTagName(textareaTag) || node->hasTagName(videoTag) || node->hasTagName(audioTag);
-}
-
-static inline bool isNonFocusableShadowHost(Node* node, KeyboardEvent* event)
-{
-    ASSERT(node);
-    return !node->isKeyboardFocusable(event) && isShadowHost(node) && !hasCustomFocusLogic(node);
-}
-
-static inline bool isFocusableShadowHost(Node* node, KeyboardEvent* event)
-{
-    ASSERT(node);
-    return node->isKeyboardFocusable(event) && isShadowHost(node) && !hasCustomFocusLogic(node);
-}
-
-static inline int adjustedTabIndex(Node* node, KeyboardEvent* event)
-{
-    ASSERT(node);
-    return isNonFocusableShadowHost(node, event) ? 0 : node->tabIndex();
-}
-
-static inline bool shouldVisit(Node* node, KeyboardEvent* event)
-{
-    ASSERT(node);
-    return node->isKeyboardFocusable(event) || isNonFocusableShadowHost(node, event);
-}
-
 FocusController::FocusController(Page* page)
     : m_page(page)
     , m_isActive(false)
@@ -231,16 +153,52 @@ void FocusController::setFocused(bool focused)
     }
 }
 
-Node* FocusController::findFocusableNodeDecendingDownIntoFrameDocument(FocusDirection direction, Node* node, KeyboardEvent* event)
+static inline ShadowRoot* shadowRoot(Node* node)
 {
-    // The node we found might be a HTMLFrameOwnerElement, so descend down the tree until we find either:
+    return node->isElementNode() && toElement(node)->hasShadowRoot() ? toElement(node)->shadowTree()->youngestShadowRoot() : 0;
+}
+
+static inline bool isTreeScopeOwner(Node* node)
+{
+    return node && (node->isFrameOwnerElement() || shadowRoot(node));
+}
+
+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.
-    while (node && node->isFrameOwnerElement()) {
-        HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(node);
-        if (!owner->contentFrame())
-            break;
-        Node* foundNode = findFocusableNode(direction, FocusScope::focusScopeOwnedByIFrame(owner), 0, event);
+    // 2) the deepest-nested HTMLFrameOwnerElement or shadow host.
+    while (isTreeScopeOwner(node)) {
+        Node* foundNode;
+        if (node->isFrameOwnerElement()) {
+            HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(node);
+            if (!owner->contentFrame())
+                break;
+            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));
+            foundNode = findFocusableNode(direction, shadowRoot(node), 0, event);
+        }
         if (!foundNode)
             break;
         ASSERT(node != foundNode);
@@ -295,7 +253,7 @@ bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, Keyb
 
     document->updateLayoutIgnorePendingStylesheets();
 
-    RefPtr<Node> node = findFocusableNodeAcrossFocusScope(direction, FocusScope::focusScopeOf(currentNode ? currentNode : document), currentNode, event);
+    RefPtr<Node> node = findFocusableNodeAcrossTreeScope(direction, currentNode ? currentNode->treeScope() : document, currentNode, event);
 
     if (!node) {
         // We didn't find a node to focus, so we should try to pass focus to Chrome.
@@ -307,8 +265,8 @@ bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, Keyb
         }
 
         // Chrome doesn't want focus, so we should wrap focus.
-        node = findFocusableNodeRecursively(direction, FocusScope::focusScopeOf(m_page->mainFrame()->document()), 0, event);
-        node = findFocusableNodeDecendingDownIntoFrameDocument(direction, node.get(), event);
+        node = findFocusableNode(direction, m_page->mainFrame()->document(), 0, event);
+        node = findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(direction, node.get(), event);
 
         if (!node)
             return false;
@@ -360,70 +318,56 @@ bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, Keyb
     return true;
 }
 
-Node* FocusController::findFocusableNodeAcrossFocusScope(FocusDirection direction, FocusScope scope, Node* currentNode, KeyboardEvent* event)
+static inline Node* ownerOfTreeScope(TreeScope* scope)
 {
-    ASSERT(!currentNode || !isNonFocusableShadowHost(currentNode, event));
-    Node* found;
-    if (currentNode && direction == FocusDirectionForward && isFocusableShadowHost(currentNode, event)) {
-        Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusScope::focusScopeOwnedByShadowHost(currentNode), 0, event);
-        found = foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableNodeRecursively(direction, scope, currentNode, event);
-    } else
-        found = findFocusableNodeRecursively(direction, scope, currentNode, event);
+    ASSERT(scope);
+    if (scope->rootNode()->isShadowRoot())
+        return scope->rootNode()->shadowHost();
+    if (scope->rootNode()->document()->frame())
+        return scope->rootNode()->document()->frame()->ownerElement();
+    return 0;
+}
 
-    // If there's no focusable node to advance to, move up the focus scopes until we find one.
-    while (!found) {
-        Node* owner = scope.owner();
+Node* FocusController::findFocusableNodeAcrossTreeScope(FocusDirection direction, TreeScope* scope, Node* currentNode, KeyboardEvent* event)
+{
+    Node* node = findFocusableNode(direction, scope, currentNode, event);
+    // If there's no focusable node to advance to, move up the tree scopes until we find one.
+    while (!node && scope) {
+        Node* owner = ownerOfTreeScope(scope);
         if (!owner)
             break;
-        scope = FocusScope::focusScopeOf(owner);
-        if (direction == FocusDirectionBackward && isFocusableShadowHost(owner, event)) {
-            found = owner;
-            break;
-        }
-        found = findFocusableNodeRecursively(direction, scope, owner, event);
+        node = findFocusableNode(direction, owner->treeScope(), owner, event);
+        scope = owner->treeScope();
     }
-    found = findFocusableNodeDecendingDownIntoFrameDocument(direction, found, event);
-    return found;
+    node = findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(direction, node, event);
+    return node;
 }
 
-Node* FocusController::findFocusableNodeRecursively(FocusDirection direction, FocusScope scope, Node* start, KeyboardEvent* event)
-{
-    // Starting node is exclusive.
-    Node* found = findFocusableNode(direction, scope, start, event);
-    if (!found)
-        return 0;
-    if (direction == FocusDirectionForward) {
-        if (!isNonFocusableShadowHost(found, event))
-            return found;
-        Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusScope::focusScopeOwnedByShadowHost(found), 0, event);
-        return foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableNodeRecursively(direction, scope, found, event);
-    }
-    ASSERT(direction == FocusDirectionBackward);
-    if (isFocusableShadowHost(found, event)) {
-        Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusScope::focusScopeOwnedByShadowHost(found), 0, event);
-        return foundInInnerFocusScope ? foundInInnerFocusScope : found;
-    }
-    if (isNonFocusableShadowHost(found, event)) {
-        Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusScope::focusScopeOwnedByShadowHost(found), 0, event);
-        return foundInInnerFocusScope ? foundInInnerFocusScope :findFocusableNodeRecursively(direction, scope, found, event);
-    }
-    return found;
-}
 
-Node* FocusController::findFocusableNode(FocusDirection direction, FocusScope scope, Node* node, KeyboardEvent* event)
+Node* FocusController::findFocusableNode(FocusDirection direction, TreeScope* scope, Node* node, KeyboardEvent* event)
 {
     return (direction == FocusDirectionForward)
         ? nextFocusableNode(scope, node, event)
         : previousFocusableNode(scope, node, event);
 }
 
-Node* FocusController::findNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent* event, FocusDirection direction)
+static Node* nextNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent* event)
 {
     // Search is inclusive of start
-    for (Node* node = start; node; node = (direction == FocusDirectionForward ? ReifiedTreeTraversal::traverseNextNodeWithoutCrossingUpperBoundary(node): ReifiedTreeTraversal::traversePreviousNodeWithoutCrossingUpperBoundary(node))) {
-        if (shouldVisit(node, event) && adjustedTabIndex(node, event) == tabIndex)
+    for (Node* node = start; node; node = node->traverseNextNode())
+        if (node->isKeyboardFocusable(event) && node->tabIndex() == tabIndex)
             return node;
-    }
+
+    return 0;
+}
+
+static Node* previousNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent* event)
+{
+    // Search is inclusive of start
+    for (Node* n = start; n; n = n->traversePreviousNode())
+        if (n->isKeyboardFocusable(event) && n->tabIndex() == tabIndex)
+            return n;
+
     return 0;
 }
 
@@ -432,8 +376,8 @@ static Node* nextNodeWithGreaterTabIndex(Node* start, int tabIndex, KeyboardEven
     // Search is inclusive of start
     int winningTabIndex = std::numeric_limits<short>::max() + 1;
     Node* winner = 0;
-    for (Node* n = start; n; n = ReifiedTreeTraversal::traverseNextNodeWithoutCrossingUpperBoundary(n))
-        if (shouldVisit(n, event) && n->tabIndex() > tabIndex && n->tabIndex() < winningTabIndex) {
+    for (Node* n = start; n; n = n->traverseNextNode())
+        if (n->isKeyboardFocusable(event) && n->tabIndex() > tabIndex && n->tabIndex() < winningTabIndex) {
             winner = n;
             winningTabIndex = n->tabIndex();
         }
@@ -446,33 +390,30 @@ static Node* previousNodeWithLowerTabIndex(Node* start, int tabIndex, KeyboardEv
     // Search is inclusive of start
     int winningTabIndex = 0;
     Node* winner = 0;
-    for (Node* n = start; n; n = ReifiedTreeTraversal::traversePreviousNodeWithoutCrossingUpperBoundary(n)) {
-        int currentTabIndex = adjustedTabIndex(n, event);
-        if ((shouldVisit(n, event) || isNonFocusableShadowHost(n, event)) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) {
+    for (Node* n = start; n; n = n->traversePreviousNode())
+        if (n->isKeyboardFocusable(event) && n->tabIndex() < tabIndex && n->tabIndex() > winningTabIndex) {
             winner = n;
-            winningTabIndex = currentTabIndex;
+            winningTabIndex = n->tabIndex();
         }
-    }
+
     return winner;
 }
 
-Node* FocusController::nextFocusableNode(FocusScope scope, Node* start, KeyboardEvent* event)
+Node* FocusController::nextFocusableNode(TreeScope* scope, Node* start, KeyboardEvent* event)
 {
     if (start) {
-        int tabIndex = adjustedTabIndex(start, event);
         // If a node is excluded from the normal tabbing cycle, the next focusable node is determined by tree order
-        if (tabIndex < 0) {
-            for (Node* n = ReifiedTreeTraversal::traverseNextNodeWithoutCrossingUpperBoundary(start); n; n = ReifiedTreeTraversal::traverseNextNodeWithoutCrossingUpperBoundary(n)) {
-                if (shouldVisit(n, event) && adjustedTabIndex(n, event) >= 0)
+        if (start->tabIndex() < 0) {
+            for (Node* n = start->traverseNextNode(); n; n = n->traverseNextNode())
+                if (n->isKeyboardFocusable(event) && n->tabIndex() >= 0)
                     return n;
-            }
         }
 
         // First try to find a node with the same tabindex as start that comes after start in the scope.
-        if (Node* winner = findNodeWithExactTabIndex(ReifiedTreeTraversal::traverseNextNodeWithoutCrossingUpperBoundary(start), tabIndex, event, FocusDirectionForward))
+        if (Node* winner = nextNodeWithExactTabIndex(start->traverseNextNode(), start->tabIndex(), event))
             return winner;
 
-        if (!tabIndex)
+        if (!start->tabIndex())
             // We've reached the last node in the document with a tabindex of 0. This is the end of the tabbing order.
             return 0;
     }
@@ -480,26 +421,26 @@ Node* FocusController::nextFocusableNode(FocusScope scope, Node* start, Keyboard
     // Look for the first node in the scope that:
     // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if start is null), and
     // 2) comes first in the scope, if there's a tie.
-    if (Node* winner = nextNodeWithGreaterTabIndex(scope.rootNode(), start ? adjustedTabIndex(start, event) : 0, event))
+    if (Node* winner = nextNodeWithGreaterTabIndex(scope->rootNode(), start ? start->tabIndex() : 0, event))
         return winner;
 
     // There are no nodes with a tabindex greater than start's tabindex,
     // so find the first node with a tabindex of 0.
-    return findNodeWithExactTabIndex(scope.rootNode(), 0, event, FocusDirectionForward);
+    return nextNodeWithExactTabIndex(scope->rootNode(), 0, event);
 }
 
-Node* FocusController::previousFocusableNode(FocusScope scope, Node* start, KeyboardEvent* event)
+Node* FocusController::previousFocusableNode(TreeScope* scope, Node* start, KeyboardEvent* event)
 {
     Node* last;
-    for (last = scope.rootNode(); ReifiedTreeTraversal::lastChildWithoutCrossingUpperBoundary(last); last = ReifiedTreeTraversal::lastChildWithoutCrossingUpperBoundary(last)) { }
+    for (last = scope->rootNode(); last->lastChild(); last = last->lastChild()) { }
 
     // First try to find the last node in the scope that comes before start and has the same tabindex as start.
     // If start is null, find the last node in the scope with a tabindex of 0.
     Node* startingNode;
     int startingTabIndex;
     if (start) {
-        startingNode = ReifiedTreeTraversal::traversePreviousNodeWithoutCrossingUpperBoundary(start);
-        startingTabIndex = adjustedTabIndex(start, event);
+        startingNode = start->traversePreviousNode();
+        startingTabIndex = start->tabIndex();
     } else {
         startingNode = last;
         startingTabIndex = 0;
@@ -507,19 +448,18 @@ Node* FocusController::previousFocusableNode(FocusScope scope, Node* start, Keyb
 
     // However, if a node is excluded from the normal tabbing cycle, the previous focusable node is determined by tree order
     if (startingTabIndex < 0) {
-        for (Node* n = startingNode; n; n = ReifiedTreeTraversal::traversePreviousNodeWithoutCrossingUpperBoundary(n)) {
-            if (shouldVisit(n, event) && adjustedTabIndex(n, event) >= 0)
+        for (Node* n = startingNode; n; n = n->traversePreviousNode())
+            if (n->isKeyboardFocusable(event) && n->tabIndex() >= 0)
                 return n;
-        }
     }
 
-    if (Node* winner = findNodeWithExactTabIndex(startingNode, startingTabIndex, event, FocusDirectionBackward))
+    if (Node* winner = previousNodeWithExactTabIndex(startingNode, startingTabIndex, event))
         return winner;
 
     // There are no nodes before start with the same tabindex as start, so look for a node that:
     // 1) has the highest non-zero tabindex (that is less than start's tabindex), and
     // 2) comes last in the scope, if there's a tie.
-    startingTabIndex = (start && startingTabIndex) ? startingTabIndex : std::numeric_limits<short>::max();
+    startingTabIndex = (start && start->tabIndex()) ? start->tabIndex() : std::numeric_limits<short>::max();
     return previousNodeWithLowerTabIndex(last, startingTabIndex, event);
 }
 
index 89089e7..3b56377 100644 (file)
 namespace WebCore {
 
 struct FocusCandidate;
-class Document;
 class Element;
 class Frame;
-class HTMLFrameOwnerElement;
 class IntRect;
 class KeyboardEvent;
 class Node;
 class Page;
 class TreeScope;
 
-class FocusScope {
-public:
-    Node* rootNode() const;
-    Element* owner() const;
-    static FocusScope focusScopeOf(Node*);
-    static FocusScope focusScopeOwnedByShadowHost(Node*);
-    static FocusScope focusScopeOwnedByIFrame(HTMLFrameOwnerElement*);
-
-private:
-    explicit FocusScope(TreeScope*);
-    TreeScope* m_rootTreeScope;
-};
-
 class FocusController {
     WTF_MAKE_NONCOPYABLE(FocusController); WTF_MAKE_FAST_ALLOCATED;
 public:
@@ -81,15 +66,16 @@ public:
     void setContainingWindowIsVisible(bool);
     bool containingWindowIsVisible() const { return m_containingWindowIsVisible; }
 
+    bool transferFocusToElementInShadowRoot(Element* shadowHost, bool restorePreviousSelection);
+
 private:
     FocusController(Page*);
 
     bool advanceFocusDirectionally(FocusDirection, KeyboardEvent*);
     bool advanceFocusInDocumentOrder(FocusDirection, KeyboardEvent*, bool initialFocus);
 
-    Node* findFocusableNodeAcrossFocusScope(FocusDirection, FocusScope startScope, Node* start, KeyboardEvent*);
-    Node* findFocusableNodeRecursively(FocusDirection, FocusScope, Node* start, KeyboardEvent*);
-    Node* findFocusableNodeDecendingDownIntoFrameDocument(FocusDirection, Node*, KeyboardEvent*);
+    Node* findFocusableNodeAcrossTreeScope(FocusDirection, TreeScope* startScope, Node* start, KeyboardEvent*);
+    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
@@ -100,12 +86,10 @@ private:
     // @return The focus node that comes after/before start node.
     //
     // See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1
-    inline Node* findFocusableNode(FocusDirection, FocusScope, Node* start, KeyboardEvent*);
-
-    Node* nextFocusableNode(FocusScope, Node* start, KeyboardEvent*);
-    Node* previousFocusableNode(FocusScope, Node* start, KeyboardEvent*);
+    inline Node* findFocusableNode(FocusDirection, TreeScope*, Node* start, KeyboardEvent*);
 
-    Node* findNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent*, FocusDirection);
+    Node* nextFocusableNode(TreeScope*, Node* start, KeyboardEvent*);
+    Node* previousFocusableNode(TreeScope*, Node* start, KeyboardEvent*);
 
     bool advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, FocusDirection, KeyboardEvent*);
     void findFocusCandidateInContainer(Node* container, const LayoutRect& startingRect, FocusDirection, KeyboardEvent*, FocusCandidate& closest);