Changeset 112500 in webkit
- Timestamp:
- Mar 28, 2012 9:59:04 PM (12 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 2 deleted
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r112499 r112500 1 2012-03-28 Hayato Ito <hayato@chromium.org> 2 3 Make focus navigation be compliant with Shadow DOM spec. 4 https://bugs.webkit.org/show_bug.cgi?id=78588 5 6 Reviewed by Dimitri Glazkov. 7 8 * fast/dom/shadow/focus-navigation-expected.txt: Added. 9 * fast/dom/shadow/focus-navigation.html: Added. 10 * fast/dom/shadow/resources/shadow-dom.js: 11 (isShadowHost): 12 (isIframeElement): 13 (getNodeInShadowTreeStack): 14 (dumpNode): 15 (innermostActiveElement): 16 (isInnermostActiveElement): 17 (shouldNavigateFocus): 18 (navigateFocusForward): 19 (navigateFocusBackward): 20 (testFocusNavigationFowrad): 21 (testFocusNavigationBackward): 22 * fast/dom/shadow/shadow-host-transfer-focus-expected.txt: Removed. 23 * fast/dom/shadow/shadow-host-transfer-focus.html: Removed. 24 * fast/dom/shadow/tab-order-iframe-and-shadow-expected.txt: 25 * fast/dom/shadow/tab-order-iframe-and-shadow.html: 26 1 27 2012-03-28 Li Yin <li.yin@intel.com> 2 28 -
trunk/LayoutTests/fast/dom/shadow/resources/shadow-dom.js
r111505 r112500 35 35 } 36 36 37 function isShadowHost(node) 38 { 39 return window.internals.oldestShadowRoot(node); 40 } 41 37 42 function isShadowRoot(node) 38 43 { 39 44 // FIXME: window.internals should have internals.isShadowRoot(node). 40 45 return node.nodeName == "#shadow-root" || node.host; 46 } 47 48 function isIframeElement(element) 49 { 50 return element && element.nodeName == 'IFRAME'; 41 51 } 42 52 … … 48 58 var node = document.getElementById(ids[0]); 49 59 for (var i = 1; node != null && i < ids.length; ++i) { 60 if (isIframeElement(node)) { 61 node = node.contentDocument.getElementById(ids[i]); 62 continue; 63 } 50 64 if (isShadowRoot(node)) 51 65 node = internals.youngerShadowRoot(node); 66 else if (internals.oldestShadowRoot(node)) 67 node = internals.oldestShadowRoot(node); 52 68 else 53 node = internals.oldestShadowRoot(node);69 return null; 54 70 if (ids[i] != '') 55 71 node = internals.getElementByIdInShadowRoot(node, ids[i]); … … 58 74 } 59 75 76 function dumpNode(node) 77 { 78 if (!node) 79 return 'null'; 80 var output = '' + node; 81 if (node.id) 82 output += ' id=' + node.id; 83 return output; 84 } 85 86 function innermostActiveElement(element) 87 { 88 element = element || document.activeElement; 89 if (isIframeElement(element)) { 90 if (element.contentDocument.activeElement) 91 return innermostActiveElement(element.contentDocument.activeElement); 92 return element; 93 } 94 if (isShadowHost(element)) { 95 var shadowRoot = window.internals.oldestShadowRoot(element); 96 while (shadowRoot) { 97 if (shadowRoot.activeElement) 98 return innermostActiveElement(shadowRoot.activeElement); 99 shadowRoot = window.internals.youngerShadowRoot(shadowRoot); 100 } 101 } 102 return element; 103 } 104 105 function isInnermostActiveElement(id) 106 { 107 var element = getNodeInShadowTreeStack(id); 108 if (!element) { 109 debug('FAIL: There is no such element with id: '+ from); 110 return false; 111 } 112 if (element == innermostActiveElement()) 113 return true; 114 debug('Expected innermost activeElement is ' + id + ', but actual innermost activeElement is ' + dumpNode(innermostActiveElement())); 115 return false; 116 } 117 118 function shouldNavigateFocus(from, to, direction) 119 { 120 debug('Should move from ' + from + ' to ' + to + ' in ' + direction); 121 var fromElement = getNodeInShadowTreeStack(from); 122 if (!fromElement) { 123 debug('FAIL: There is no such element with id: '+ from); 124 return; 125 } 126 fromElement.focus(); 127 if (direction == 'forward') 128 navigateFocusForward(); 129 else 130 navigateFocusBackward(); 131 if (isInnermostActiveElement(to)) 132 debug('PASS'); 133 else 134 debug('FAIL'); 135 } 136 137 function navigateFocusForward() 138 { 139 eventSender.keyDown('\t'); 140 } 141 142 function navigateFocusBackward() 143 { 144 eventSender.keyDown('\t', ['shiftKey']); 145 } 146 147 function testFocusNavigationFowrad(elements) 148 { 149 for (var i = 0; i + 1 < elements.length; ++i) 150 shouldNavigateFocus(elements[i], elements[i + 1], 'forward'); 151 } 152 153 function testFocusNavigationBackward(elements) 154 { 155 for (var i = 0; i + 1 < elements.length; ++i) 156 shouldNavigateFocus(elements[i], elements[i + 1], 'backward'); 157 } -
trunk/LayoutTests/fast/dom/shadow/tab-order-iframe-and-shadow-expected.txt
r88421 r112500 1 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. 1 CONSOLE MESSAGE: line 104: Uncaught ReferenceError: log is not defined 2 This tests that pressing Tab key should traverse into iframe and shadow tree, and pressing Shift-Tab should reverse the order. 2 3 3 Focus input-01. 4 id:input-01(tabIndex=1) is focused. 5 6 Press Tab 11 times. 7 id:input-13(tabIndex=1) is focused. 8 id:input-15(tabIndex=2) is focused. 9 id:input-02(tabIndex=0) is focused. 10 id:input-04(tabIndex=0) is focused. 11 id:iframe-input-06(tabIndex=1) is focused. 12 id:iframe-shadow-input-09(tabIndex=1) is focused. 13 id:iframe-shadow-input-08(tabIndex=0) is focused. 14 id:iframe-input-12(tabIndex=1) is focused. 15 id:iframe-input-11(tabIndex=2) is focused. 16 id:iframe-input-05(tabIndex=0) is focused. 17 id:input-14(tabIndex=0) is focused. 18 19 Press Shift-Tab 11 times. 20 id:iframe-input-05(tabIndex=0) is focused. 21 id:iframe-input-11(tabIndex=2) is focused. 22 id:iframe-input-12(tabIndex=1) is focused. 23 id:iframe-shadow-input-08(tabIndex=0) is focused. 24 id:iframe-shadow-input-09(tabIndex=1) is focused. 25 id:iframe-input-06(tabIndex=1) is focused. 26 id:input-04(tabIndex=0) is focused. 27 id:input-02(tabIndex=0) is focused. 28 id:input-15(tabIndex=2) is focused. 29 id:input-13(tabIndex=1) is focused. 30 id:input-01(tabIndex=1) is focused. 31 32 Test finished. 4 Should move from input-01 to input-13 in forward 5 PASS 6 Should move from input-13 to input-15 in forward 7 PASS 8 Should move from input-15 to input-02 in forward 9 PASS 10 Should move from input-02 to host-01/input-03 in forward 11 PASS 12 Should move from host-01/input-03 to input-04 in forward 13 PASS 14 Should move from input-04 to iframe/input-06 in forward 15 PASS 16 Should move from iframe/input-06 to iframe/host-02 in forward 17 PASS 18 Should move from iframe/host-02 to iframe/host-02/input-09 in forward 19 PASS 20 Should move from iframe/host-02/input-09 to iframe/host-02/input-08 in forward 21 PASS 22 Should move from iframe/host-02/input-08 to iframe/input-12 in forward 23 PASS 24 Should move from iframe/input-12 to iframe/input-11 in forward 25 PASS 26 Should move from iframe/input-11 to iframe/input-05 in forward 27 PASS 28 Should move from iframe/input-05 to input-14 in forward 29 PASS 30 Should move from input-14 to iframe/input-05 in backward 31 PASS 32 Should move from iframe/input-05 to iframe/input-11 in backward 33 PASS 34 Should move from iframe/input-11 to iframe/input-12 in backward 35 PASS 36 Should move from iframe/input-12 to iframe/host-02/input-08 in backward 37 PASS 38 Should move from iframe/host-02/input-08 to iframe/host-02/input-09 in backward 39 PASS 40 Should move from iframe/host-02/input-09 to iframe/host-02 in backward 41 PASS 42 Should move from iframe/host-02 to iframe/input-06 in backward 43 PASS 44 Should move from iframe/input-06 to input-04 in backward 45 PASS 46 Should move from input-04 to host-01/input-03 in backward 47 PASS 48 Should move from host-01/input-03 to input-02 in backward 49 PASS 50 Should move from input-02 to input-15 in backward 51 PASS 52 Should move from input-15 to input-13 in backward 53 PASS 54 Should move from input-13 to input-01 in backward 55 PASS 33 56 34 57 -
trunk/LayoutTests/fast/dom/shadow/tab-order-iframe-and-shadow.html
r89682 r112500 1 1 <!DOCTYPE html> 2 2 <html> 3 <head> 4 <script src="../../js/resources/js-test-pre.js"></script> 5 <script src="resources/shadow-dom.js"></script> 6 </head> 3 7 <body> 4 <p>This tests that pressing Tab key should traverse into iframe and shadow tree, and pressing Shift-Tab should reverse the order. 5 Makes sure that a shadow host element should act like a iframe element.</p> 8 <p>This tests that pressing Tab key should traverse into iframe and shadow tree, and pressing Shift-Tab should reverse the order.</p> 6 9 <pre id="console"></pre> 7 10 <script> 8 function log(msg) {9 document.querySelector('#console').textContent += (msg + '\n');10 }11 12 function description(element) {13 var msg = '';14 if (element.id) {15 msg += 'id:' + element.id;16 }17 msg += '(tabIndex=' + element.tabIndex + ')';18 return msg;19 }20 21 function onFocus(event) {22 log(description(event.target) + ' is focused.');23 }24 25 function addFocusEventListener(element) {26 element.addEventListener('focus', onFocus, false);27 }28 29 11 function createTextInputElement(doc, id, tabIndex) { 30 12 var input = doc.createElement('input'); … … 32 14 input.id = id; 33 15 input.tabIndex = tabIndex; 34 addFocusEventListener(input);35 16 return input; 36 17 } … … 45 26 function addShadowHost(doc) { 46 27 var shadowHost = doc.createElement('p'); 47 shadowHost.tabIndex = -1; // This shadow host (and a shadow tree in that) should be skipped. 28 shadowHost.id = 'host-01'; 29 shadowHost.tabIndex = -1; 48 30 var shadow = internals.ensureShadowRoot(shadowHost); 49 31 doc.body.appendChild(shadowHost); 50 shadow.appendChild(createTextInputElement(doc, ' shadow-input-03', 0));32 shadow.appendChild(createTextInputElement(doc, 'input-03', 0)); 51 33 } 52 34 addShadowHost(doc); … … 56 38 function addIframe(doc) { 57 39 var iframe = doc.createElement('iframe'); 40 iframe.id = 'iframe'; 58 41 doc.body.appendChild(iframe); 59 42 doc = iframe.contentDocument; 60 43 61 doc.body.appendChild(createTextInputElement(doc, 'i frame-input-05', 0));62 doc.body.appendChild(createTextInputElement(doc, 'i frame-input-06', 1));63 doc.body.appendChild(createTextInputElement(doc, 'i frame-input-07', -1));44 doc.body.appendChild(createTextInputElement(doc, 'input-05', 0)); 45 doc.body.appendChild(createTextInputElement(doc, 'input-06', 1)); 46 doc.body.appendChild(createTextInputElement(doc, 'input-07', -1)); 64 47 65 48 function addShadowHost(doc) { 66 49 var shadowHost = doc.createElement('p'); 50 shadowHost.id = 'host-02'; 67 51 shadowHost.tabIndex = 1; 68 52 var shadow = internals.ensureShadowRoot(shadowHost); 69 53 doc.body.appendChild(shadowHost); 70 54 71 shadow.appendChild(createTextInputElement(doc, 'i frame-shadow-input-08', 0));72 shadow.appendChild(createTextInputElement(doc, 'i frame-shadow-input-09', 1));73 shadow.appendChild(createTextInputElement(doc, 'i frame-shadow-input-10', -1));55 shadow.appendChild(createTextInputElement(doc, 'input-08', 0)); 56 shadow.appendChild(createTextInputElement(doc, 'input-09', 1)); 57 shadow.appendChild(createTextInputElement(doc, 'input-10', -1)); 74 58 } 75 59 addShadowHost(doc); 76 60 77 doc.body.appendChild(createTextInputElement(doc, 'i frame-input-11', 2));78 doc.body.appendChild(createTextInputElement(doc, 'i frame-input-12', 1));61 doc.body.appendChild(createTextInputElement(doc, 'input-11', 2)); 62 doc.body.appendChild(createTextInputElement(doc, 'input-12', 1)); 79 63 } 80 64 addIframe(doc); … … 84 68 doc.body.appendChild(createTextInputElement(doc, 'input-15', 2)); 85 69 86 log('Focus input-01.'); 87 doc.getElementById('input-01').focus(); 70 testFocusNavigationFowrad([ 71 'input-01', 72 'input-13', 73 'input-15', 74 'input-02', 75 'host-01/input-03', 76 'input-04', 77 'iframe/input-06', 78 'iframe/host-02', 79 'iframe/host-02/input-09', 80 'iframe/host-02/input-08', 81 'iframe/input-12', 82 'iframe/input-11', 83 'iframe/input-05', 84 'input-14', 85 ]); 88 86 89 if (window.eventSender) { 90 var pressed = 11; 91 log('\nPress Tab ' + pressed + ' times.'); 92 for (var i = 0; i < pressed; ++i) { 93 eventSender.keyDown('\t'); 94 } 95 log('\nPress Shift-Tab ' + pressed + ' times.'); 96 for (var i = 0; i < pressed; ++i) { 97 eventSender.keyDown('\t', ['shiftKey']); 98 } 99 } 87 testFocusNavigationBackward([ 88 'input-14', 89 'iframe/input-05', 90 'iframe/input-11', 91 'iframe/input-12', 92 'iframe/host-02/input-08', 93 'iframe/host-02/input-09', 94 'iframe/host-02', 95 'iframe/input-06', 96 'input-04', 97 'host-01/input-03', 98 'input-02', 99 'input-15', 100 'input-13', 101 'input-01', 102 ]); 103 100 104 log('\nTest finished.'); 101 105 } -
trunk/Source/WebCore/ChangeLog
r112499 r112500 1 2012-03-28 Hayato Ito <hayato@chromium.org> 2 3 Let focus navigation be compliant with Shadow DOM spec. 4 https://bugs.webkit.org/show_bug.cgi?id=78588 5 6 Reviewed by Dimitri Glazkov. 7 8 Sequential focus navigation now behaves exactly as specified in the Shadow DOM spec. 9 10 According to the Shadow DOM spec: 11 The shadow DOM navigation order sequence is inserted into the document navigation order: 12 1. immediately after the shadow host, if the shadow host is focusable; or 13 2. in place of the shadow host as if the shadow host were assigned the value of auto for determining its position. 14 15 Prior to this patch, sequential focus navigation goes into Shadow DOM, but it is incomplete 16 since insertion points, such as <content> elements or <shadow> elements, are not resolved at all. 17 Now focus navigation can traverse shadow DOM subtrees in 'reified tree order', resolving lower boundaries transparently. 18 19 Implementation notes: 20 Prior to this patch, sequential focus navigation does not go into Shadow DOM if a shadow host is non-focusable. 21 Now focus navigation must go into Shadow DOM subtrees even if a show host is not focusable as described in 2). 22 To support this behavior, this patch introduced adjustedTabIndex() locally in FocusController so that 23 it does not skip a non-focusable shadow host in current focus scope. 24 After finding a *pseudo* focusable element in current focus scope, it tries to resolve a focused element recursively, 25 considering a nested focus scope inside of a shadow host or iframe. 26 To traverse Shadow DOM subtrees, a FocusController makes use of ReifiedTreeTraversal APIs, which was introduced in r112055. 27 28 This change does not affect an existing behavior if a shadow dom is not involved. 29 30 Test: fast/dom/shadow/focus-navigation.html 31 32 * dom/Element.cpp: 33 (WebCore::Element::focus): 34 * page/FocusController.cpp: 35 (WebCore::isShadowHost): 36 (WebCore): 37 (WebCore::FocusScope::FocusScope): 38 (WebCore::FocusScope::rootNode): 39 (WebCore::FocusScope::owner): 40 (WebCore::FocusScope::focusScopeOf): 41 (WebCore::FocusScope::focusScopeOwnedByShadowHost): 42 (WebCore::FocusScope::focusScopeOwnedByIFrame): 43 (WebCore::hasCustomFocusLogic): 44 (WebCore::isNonFocusableShadowHost): 45 (WebCore::isFocusableShadowHost): 46 (WebCore::adjustedTabIndex): 47 (WebCore::shouldVisit): 48 (WebCore::FocusController::findFocusableNodeDecendingDownIntoFrameDocument): 49 (WebCore::FocusController::advanceFocusInDocumentOrder): 50 (WebCore::FocusController::findFocusableNodeAcrossFocusScope): 51 (WebCore::FocusController::findFocusableNodeRecursively): 52 (WebCore::FocusController::findFocusableNode): 53 (WebCore::FocusController::findNodeWithExactTabIndex): 54 (WebCore::nextNodeWithGreaterTabIndex): 55 (WebCore::previousNodeWithLowerTabIndex): 56 (WebCore::FocusController::nextFocusableNode): 57 (WebCore::FocusController::previousFocusableNode): 58 * page/FocusController.h: 59 (WebCore): 60 (FocusScope): 61 (FocusController): 62 1 63 2012-03-28 Li Yin <li.yin@intel.com> 2 64 -
trunk/Source/WebCore/dom/Element.cpp
r112170 r112500 1549 1549 // does not make sense to continue and update appearence. 1550 1550 protect = this; 1551 if (hasShadowRoot() && page->focusController()->transferFocusToElementInShadowRoot(this, restorePreviousSelection))1552 return;1553 1551 if (!page->focusController()->setFocusedNode(this, doc->frame())) 1554 1552 return; -
trunk/Source/WebCore/page/FocusController.cpp
r108959 r112500 49 49 #include "Page.h" 50 50 #include "Range.h" 51 #include "ReifiedTreeTraversal.h" 51 52 #include "RenderLayer.h" 52 53 #include "RenderObject.h" … … 66 67 using namespace std; 67 68 69 static inline bool isShadowHost(const Node* node) 70 { 71 return node && node->isElementNode() && toElement(node)->hasShadowRoot(); 72 } 73 74 FocusScope::FocusScope(TreeScope* treeScope) 75 : m_rootTreeScope(treeScope) 76 { 77 ASSERT(treeScope); 78 ASSERT(!treeScope->rootNode()->isShadowRoot() || toShadowRoot(treeScope->rootNode())->isYoungest()); 79 } 80 81 Node* FocusScope::rootNode() const 82 { 83 return m_rootTreeScope->rootNode(); 84 } 85 86 Element* FocusScope::owner() const 87 { 88 Node* root = rootNode(); 89 if (root->isShadowRoot()) 90 return root->shadowHost(); 91 if (Frame* frame = root->document()->frame()) 92 return frame->ownerElement(); 93 return 0; 94 } 95 96 FocusScope FocusScope::focusScopeOf(Node* node) 97 { 98 ASSERT(node); 99 TreeScope* scope = node->treeScope(); 100 if (scope->rootNode()->isShadowRoot()) 101 return FocusScope(toShadowRoot(scope->rootNode())->tree()->youngestShadowRoot()); 102 return FocusScope(scope); 103 } 104 105 FocusScope FocusScope::focusScopeOwnedByShadowHost(Node* node) 106 { 107 ASSERT(isShadowHost(node)); 108 return FocusScope(toElement(node)->shadowTree()->youngestShadowRoot()); 109 } 110 111 FocusScope FocusScope::focusScopeOwnedByIFrame(HTMLFrameOwnerElement* frame) 112 { 113 ASSERT(frame && frame->contentFrame()); 114 return FocusScope(frame->contentFrame()->document()); 115 } 116 68 117 static inline void dispatchEventsOnWindowAndFocusedNode(Document* document, bool focused) 69 118 { … … 83 132 if (focused && document->focusedNode()) 84 133 document->focusedNode()->dispatchFocusEvent(0); 134 } 135 136 static inline bool hasCustomFocusLogic(Node* node) 137 { 138 return node->hasTagName(inputTag) || node->hasTagName(textareaTag) || node->hasTagName(videoTag) || node->hasTagName(audioTag); 139 } 140 141 static inline bool isNonFocusableShadowHost(Node* node, KeyboardEvent* event) 142 { 143 ASSERT(node); 144 return !node->isKeyboardFocusable(event) && isShadowHost(node) && !hasCustomFocusLogic(node); 145 } 146 147 static inline bool isFocusableShadowHost(Node* node, KeyboardEvent* event) 148 { 149 ASSERT(node); 150 return node->isKeyboardFocusable(event) && isShadowHost(node) && !hasCustomFocusLogic(node); 151 } 152 153 static inline int adjustedTabIndex(Node* node, KeyboardEvent* event) 154 { 155 ASSERT(node); 156 return isNonFocusableShadowHost(node, event) ? 0 : node->tabIndex(); 157 } 158 159 static inline bool shouldVisit(Node* node, KeyboardEvent* event) 160 { 161 ASSERT(node); 162 return node->isKeyboardFocusable(event) || isNonFocusableShadowHost(node, event); 85 163 } 86 164 … … 154 232 } 155 233 156 static inline ShadowRoot* shadowRoot(Node* node) 157 { 158 return node->isElementNode() && toElement(node)->hasShadowRoot() ? toElement(node)->shadowTree()->youngestShadowRoot() : 0; 159 } 160 161 static inline bool isTreeScopeOwner(Node* node) 162 { 163 return node && (node->isFrameOwnerElement() || shadowRoot(node)); 164 } 165 166 bool FocusController::transferFocusToElementInShadowRoot(Element* shadowHost, bool restorePreviousSelection) 167 { 168 ASSERT(shadowRoot(shadowHost)); 169 Node* node = findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(FocusDirectionForward, shadowHost, 0); 170 if (shadowHost == node) 171 return false; 172 toElement(node)->focus(restorePreviousSelection); 173 return true; 174 } 175 176 static inline bool hasCustomFocusLogic(Node* node) 177 { 178 return node->hasTagName(inputTag) || node->hasTagName(textareaTag) || node->hasTagName(videoTag) || node->hasTagName(audioTag); 179 } 180 181 Node* FocusController::findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(FocusDirection direction, Node* node, KeyboardEvent* event) 182 { 183 // The node we found might be a HTMLFrameOwnerElement or a shadow host, so descend down the tree until we find either: 234 Node* FocusController::findFocusableNodeDecendingDownIntoFrameDocument(FocusDirection direction, Node* node, KeyboardEvent* event) 235 { 236 // The node we found might be a HTMLFrameOwnerElement, so descend down the tree until we find either: 184 237 // 1) a focusable node, or 185 // 2) the deepest-nested HTMLFrameOwnerElement or shadow host. 186 while (isTreeScopeOwner(node)) { 187 Node* foundNode; 188 if (node->isFrameOwnerElement()) { 189 HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(node); 190 if (!owner->contentFrame()) 191 break; 192 Document* document = owner->contentFrame()->document(); 193 foundNode = findFocusableNode(direction, document, 0, event); 194 } else { 195 // FIXME: Until a focus re-targeting (bug 61421) is implemented, 196 // skipping these elements is the safest way to keep a compatibility. 197 if (hasCustomFocusLogic(node)) 198 break; 199 ASSERT(shadowRoot(node)); 200 foundNode = findFocusableNode(direction, shadowRoot(node), 0, event); 201 } 238 // 2) the deepest-nested HTMLFrameOwnerElement. 239 while (node && node->isFrameOwnerElement()) { 240 HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(node); 241 if (!owner->contentFrame()) 242 break; 243 Node* foundNode = findFocusableNode(direction, FocusScope::focusScopeOwnedByIFrame(owner), 0, event); 202 244 if (!foundNode) 203 245 break; … … 254 296 document->updateLayoutIgnorePendingStylesheets(); 255 297 256 RefPtr<Node> node = findFocusableNodeAcross TreeScope(direction, currentNode ? currentNode->treeScope() : document, currentNode, event);298 RefPtr<Node> node = findFocusableNodeAcrossFocusScope(direction, FocusScope::focusScopeOf(currentNode ? currentNode : document), currentNode, event); 257 299 258 300 if (!node) { … … 266 308 267 309 // Chrome doesn't want focus, so we should wrap focus. 268 node = findFocusableNode (direction, m_page->mainFrame()->document(), 0, event);269 node = findFocusableNodeDecendingDownIntoFrameDocument OrShadowRoot(direction, node.get(), event);310 node = findFocusableNodeRecursively(direction, FocusScope::focusScopeOf(m_page->mainFrame()->document()), 0, event); 311 node = findFocusableNodeDecendingDownIntoFrameDocument(direction, node.get(), event); 270 312 271 313 if (!node) … … 319 361 } 320 362 321 static inline Node* ownerOfTreeScope(TreeScope* scope) 322 { 323 ASSERT(scope); 324 if (scope->rootNode()->isShadowRoot()) 325 return scope->rootNode()->shadowHost(); 326 if (scope->rootNode()->document()->frame()) 327 return scope->rootNode()->document()->frame()->ownerElement(); 328 return 0; 329 } 330 331 Node* FocusController::findFocusableNodeAcrossTreeScope(FocusDirection direction, TreeScope* scope, Node* currentNode, KeyboardEvent* event) 332 { 333 Node* node = findFocusableNode(direction, scope, currentNode, event); 334 // If there's no focusable node to advance to, move up the tree scopes until we find one. 335 while (!node && scope) { 336 Node* owner = ownerOfTreeScope(scope); 363 Node* FocusController::findFocusableNodeAcrossFocusScope(FocusDirection direction, FocusScope scope, Node* currentNode, KeyboardEvent* event) 364 { 365 ASSERT(!currentNode || !isNonFocusableShadowHost(currentNode, event)); 366 Node* found; 367 if (currentNode && direction == FocusDirectionForward && isFocusableShadowHost(currentNode, event)) { 368 Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusScope::focusScopeOwnedByShadowHost(currentNode), 0, event); 369 found = foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableNodeRecursively(direction, scope, currentNode, event); 370 } else 371 found = findFocusableNodeRecursively(direction, scope, currentNode, event); 372 373 // If there's no focusable node to advance to, move up the focus scopes until we find one. 374 while (!found) { 375 Node* owner = scope.owner(); 337 376 if (!owner) 338 377 break; 339 node = findFocusableNode(direction, owner->treeScope(), owner, event); 340 scope = owner->treeScope(); 341 } 342 node = findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(direction, node, event); 343 return node; 344 } 345 346 347 Node* FocusController::findFocusableNode(FocusDirection direction, TreeScope* scope, Node* node, KeyboardEvent* event) 378 scope = FocusScope::focusScopeOf(owner); 379 if (direction == FocusDirectionBackward && isFocusableShadowHost(owner, event)) { 380 found = owner; 381 break; 382 } 383 found = findFocusableNodeRecursively(direction, scope, owner, event); 384 } 385 found = findFocusableNodeDecendingDownIntoFrameDocument(direction, found, event); 386 return found; 387 } 388 389 Node* FocusController::findFocusableNodeRecursively(FocusDirection direction, FocusScope scope, Node* start, KeyboardEvent* event) 390 { 391 // Starting node is exclusive. 392 Node* found = findFocusableNode(direction, scope, start, event); 393 if (!found) 394 return 0; 395 if (direction == FocusDirectionForward) { 396 if (!isNonFocusableShadowHost(found, event)) 397 return found; 398 Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusScope::focusScopeOwnedByShadowHost(found), 0, event); 399 return foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableNodeRecursively(direction, scope, found, event); 400 } 401 ASSERT(direction == FocusDirectionBackward); 402 if (isFocusableShadowHost(found, event)) { 403 Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusScope::focusScopeOwnedByShadowHost(found), 0, event); 404 return foundInInnerFocusScope ? foundInInnerFocusScope : found; 405 } 406 if (isNonFocusableShadowHost(found, event)) { 407 Node* foundInInnerFocusScope = findFocusableNodeRecursively(direction, FocusScope::focusScopeOwnedByShadowHost(found), 0, event); 408 return foundInInnerFocusScope ? foundInInnerFocusScope :findFocusableNodeRecursively(direction, scope, found, event); 409 } 410 return found; 411 } 412 413 Node* FocusController::findFocusableNode(FocusDirection direction, FocusScope scope, Node* node, KeyboardEvent* event) 348 414 { 349 415 return (direction == FocusDirectionForward) … … 352 418 } 353 419 354 static Node* nextNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent* event)420 Node* FocusController::findNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent* event, FocusDirection direction) 355 421 { 356 422 // Search is inclusive of start 357 for (Node* node = start; node; node = node->traverseNextNode())358 if ( node->isKeyboardFocusable(event) && node->tabIndex() == tabIndex)423 for (Node* node = start; node; node = (direction == FocusDirectionForward ? ReifiedTreeTraversal::traverseNextNodeWithoutCrossingUpperBoundary(node): ReifiedTreeTraversal::traversePreviousNodeWithoutCrossingUpperBoundary(node))) { 424 if (shouldVisit(node, event) && adjustedTabIndex(node, event) == tabIndex) 359 425 return node; 360 361 return 0; 362 } 363 364 static Node* previousNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent* event) 365 { 366 // Search is inclusive of start 367 for (Node* n = start; n; n = n->traversePreviousNode()) 368 if (n->isKeyboardFocusable(event) && n->tabIndex() == tabIndex) 369 return n; 370 426 } 371 427 return 0; 372 428 } … … 377 433 int winningTabIndex = std::numeric_limits<short>::max() + 1; 378 434 Node* winner = 0; 379 for (Node* n = start; n; n = n->traverseNextNode())380 if ( n->isKeyboardFocusable(event) && n->tabIndex() > tabIndex && n->tabIndex() < winningTabIndex) {435 for (Node* n = start; n; n = ReifiedTreeTraversal::traverseNextNodeWithoutCrossingUpperBoundary(n)) 436 if (shouldVisit(n, event) && n->tabIndex() > tabIndex && n->tabIndex() < winningTabIndex) { 381 437 winner = n; 382 438 winningTabIndex = n->tabIndex(); … … 391 447 int winningTabIndex = 0; 392 448 Node* winner = 0; 393 for (Node* n = start; n; n = n->traversePreviousNode()) 394 if (n->isKeyboardFocusable(event) && n->tabIndex() < tabIndex && n->tabIndex() > winningTabIndex) { 449 for (Node* n = start; n; n = ReifiedTreeTraversal::traversePreviousNodeWithoutCrossingUpperBoundary(n)) { 450 int currentTabIndex = adjustedTabIndex(n, event); 451 if ((shouldVisit(n, event) || isNonFocusableShadowHost(n, event)) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) { 395 452 winner = n; 396 winningTabIndex = n->tabIndex();397 } 398 453 winningTabIndex = currentTabIndex; 454 } 455 } 399 456 return winner; 400 457 } 401 458 402 Node* FocusController::nextFocusableNode( TreeScope*scope, Node* start, KeyboardEvent* event)459 Node* FocusController::nextFocusableNode(FocusScope scope, Node* start, KeyboardEvent* event) 403 460 { 404 461 if (start) { 462 int tabIndex = adjustedTabIndex(start, event); 405 463 // If a node is excluded from the normal tabbing cycle, the next focusable node is determined by tree order 406 if ( start->tabIndex()< 0) {407 for (Node* n = start->traverseNextNode(); n; n = n->traverseNextNode())408 if ( n->isKeyboardFocusable(event) && n->tabIndex() >= 0)464 if (tabIndex < 0) { 465 for (Node* n = ReifiedTreeTraversal::traverseNextNodeWithoutCrossingUpperBoundary(start); n; n = ReifiedTreeTraversal::traverseNextNodeWithoutCrossingUpperBoundary(n)) { 466 if (shouldVisit(n, event) && adjustedTabIndex(n, event) >= 0) 409 467 return n; 468 } 410 469 } 411 470 412 471 // First try to find a node with the same tabindex as start that comes after start in the scope. 413 if (Node* winner = nextNodeWithExactTabIndex(start->traverseNextNode(), start->tabIndex(), event))472 if (Node* winner = findNodeWithExactTabIndex(ReifiedTreeTraversal::traverseNextNodeWithoutCrossingUpperBoundary(start), tabIndex, event, FocusDirectionForward)) 414 473 return winner; 415 474 416 if (! start->tabIndex())475 if (!tabIndex) 417 476 // We've reached the last node in the document with a tabindex of 0. This is the end of the tabbing order. 418 477 return 0; … … 422 481 // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if start is null), and 423 482 // 2) comes first in the scope, if there's a tie. 424 if (Node* winner = nextNodeWithGreaterTabIndex(scope ->rootNode(), start ? start->tabIndex() : 0, event))483 if (Node* winner = nextNodeWithGreaterTabIndex(scope.rootNode(), start ? adjustedTabIndex(start, event) : 0, event)) 425 484 return winner; 426 485 427 486 // There are no nodes with a tabindex greater than start's tabindex, 428 487 // so find the first node with a tabindex of 0. 429 return nextNodeWithExactTabIndex(scope->rootNode(), 0, event);430 } 431 432 Node* FocusController::previousFocusableNode( TreeScope*scope, Node* start, KeyboardEvent* event)488 return findNodeWithExactTabIndex(scope.rootNode(), 0, event, FocusDirectionForward); 489 } 490 491 Node* FocusController::previousFocusableNode(FocusScope scope, Node* start, KeyboardEvent* event) 433 492 { 434 493 Node* last; 435 for (last = scope ->rootNode(); last->lastChild(); last = last->lastChild()) { }494 for (last = scope.rootNode(); ReifiedTreeTraversal::lastChildWithoutCrossingUpperBoundary(last); last = ReifiedTreeTraversal::lastChildWithoutCrossingUpperBoundary(last)) { } 436 495 437 496 // First try to find the last node in the scope that comes before start and has the same tabindex as start. … … 440 499 int startingTabIndex; 441 500 if (start) { 442 startingNode = start->traversePreviousNode();443 startingTabIndex = start->tabIndex();501 startingNode = ReifiedTreeTraversal::traversePreviousNodeWithoutCrossingUpperBoundary(start); 502 startingTabIndex = adjustedTabIndex(start, event); 444 503 } else { 445 504 startingNode = last; … … 449 508 // However, if a node is excluded from the normal tabbing cycle, the previous focusable node is determined by tree order 450 509 if (startingTabIndex < 0) { 451 for (Node* n = startingNode; n; n = n->traversePreviousNode())452 if ( n->isKeyboardFocusable(event) && n->tabIndex() >= 0)510 for (Node* n = startingNode; n; n = ReifiedTreeTraversal::traversePreviousNodeWithoutCrossingUpperBoundary(n)) { 511 if (shouldVisit(n, event) && adjustedTabIndex(n, event) >= 0) 453 512 return n; 454 } 455 456 if (Node* winner = previousNodeWithExactTabIndex(startingNode, startingTabIndex, event)) 513 } 514 } 515 516 if (Node* winner = findNodeWithExactTabIndex(startingNode, startingTabIndex, event, FocusDirectionBackward)) 457 517 return winner; 458 518 … … 460 520 // 1) has the highest non-zero tabindex (that is less than start's tabindex), and 461 521 // 2) comes last in the scope, if there's a tie. 462 startingTabIndex = (start && start ->tabIndex()) ? start->tabIndex(): std::numeric_limits<short>::max();522 startingTabIndex = (start && startingTabIndex) ? startingTabIndex : std::numeric_limits<short>::max(); 463 523 return previousNodeWithLowerTabIndex(last, startingTabIndex, event); 464 524 } -
trunk/Source/WebCore/page/FocusController.h
r106875 r112500 36 36 37 37 struct FocusCandidate; 38 class Document; 38 39 class Element; 39 40 class Frame; 41 class HTMLFrameOwnerElement; 40 42 class IntRect; 41 43 class KeyboardEvent; … … 43 45 class Page; 44 46 class TreeScope; 47 48 class FocusScope { 49 public: 50 Node* rootNode() const; 51 Element* owner() const; 52 static FocusScope focusScopeOf(Node*); 53 static FocusScope focusScopeOwnedByShadowHost(Node*); 54 static FocusScope focusScopeOwnedByIFrame(HTMLFrameOwnerElement*); 55 56 private: 57 explicit FocusScope(TreeScope*); 58 TreeScope* m_rootTreeScope; 59 }; 45 60 46 61 class FocusController { … … 67 82 bool containingWindowIsVisible() const { return m_containingWindowIsVisible; } 68 83 69 bool transferFocusToElementInShadowRoot(Element* shadowHost, bool restorePreviousSelection);70 71 84 private: 72 85 FocusController(Page*); … … 75 88 bool advanceFocusInDocumentOrder(FocusDirection, KeyboardEvent*, bool initialFocus); 76 89 77 Node* findFocusableNodeAcrossTreeScope(FocusDirection, TreeScope* startScope, Node* start, KeyboardEvent*); 78 Node* findFocusableNodeDecendingDownIntoFrameDocumentOrShadowRoot(FocusDirection, Node*, KeyboardEvent*); 90 Node* findFocusableNodeAcrossFocusScope(FocusDirection, FocusScope startScope, Node* start, KeyboardEvent*); 91 Node* findFocusableNodeRecursively(FocusDirection, FocusScope, Node* start, KeyboardEvent*); 92 Node* findFocusableNodeDecendingDownIntoFrameDocument(FocusDirection, Node*, KeyboardEvent*); 79 93 80 94 // Searches through the given tree scope, starting from start node, for the next/previous selectable element that comes after/before start node. … … 87 101 // 88 102 // See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1 89 inline Node* findFocusableNode(FocusDirection, TreeScope*, Node* start, KeyboardEvent*);103 inline Node* findFocusableNode(FocusDirection, FocusScope, Node* start, KeyboardEvent*); 90 104 91 Node* nextFocusableNode(TreeScope*, Node* start, KeyboardEvent*); 92 Node* previousFocusableNode(TreeScope*, Node* start, KeyboardEvent*); 105 Node* nextFocusableNode(FocusScope, Node* start, KeyboardEvent*); 106 Node* previousFocusableNode(FocusScope, Node* start, KeyboardEvent*); 107 108 Node* findNodeWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent*, FocusDirection); 93 109 94 110 bool advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, FocusDirection, KeyboardEvent*);
Note: See TracChangeset
for help on using the changeset viewer.