Changeset 88421 in webkit


Ignore:
Timestamp:
Jun 8, 2011 10:33:12 PM (13 years ago)
Author:
hayato@chromium.org
Message:

2011-06-08 Hayato Ito <hayato@chromium.org>

Reviewed by Dimitri Glazkov.

A forward/backward tab traversal now visits focusable elements in a shadow root.
https://bugs.webkit.org/show_bug.cgi?id=61410

Like a iframe element, a shadow host becomes a scope of
tabindex. That means all descendant elements in a shadow root are
skipped if the host node of the shadow root is not focusable.

The patch doesn't affect HTMLInputElement and HTMLTextAreaElement,
which uses a shadow root and do extra works in their focus()
method.

A shadow root's <content> is not considered in this patch.
That will be addressed in a following patch.

  • fast/dom/shadow/tab-order-iframe-and-shadow-expected.txt: Added.
  • fast/dom/shadow/tab-order-iframe-and-shadow.html: Added.

2011-06-08 Hayato Ito <hayato@chromium.org>

Reviewed by Dimitri Glazkov.

A forward/backward tab traversal now visits focusable elements in a shadow root.
https://bugs.webkit.org/show_bug.cgi?id=61410

Test: fast/dom/shadow/tab-order-iframe-and-shadow.html

Like a iframe element, a shadow host becomes a scope of
tabindex. That means all descendant elements in a shadow root are
skipped if the host node of the shadow root is not focusable.

The patch doesn't affect HTMLInputElement and HTMLTextAreaElement,
which uses a shadow root and do extra works in their focus()
method.

A shadow root's <content> is not considered in this patch.
That will be addressed in a following patch.

  • page/FocusController.cpp: (WebCore::shadowRoot): (WebCore::isTreeScopeOwner): (WebCore::FocusController::deepFocusableNode): (WebCore::FocusController::advanceFocusInDocumentOrder): (WebCore::FocusController::findFocusableNodeAcrossTreeScope): (WebCore::FocusController::findFocusableNode): (WebCore::FocusController::nextFocusableNode): (WebCore::FocusController::previousFocusableNode): (WebCore::FocusController::ownerOfTreeScope):
  • page/FocusController.h:
Location:
trunk
Files:
2 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r88418 r88421  
     12011-06-08  Hayato Ito  <hayato@chromium.org>
     2
     3        Reviewed by Dimitri Glazkov.
     4
     5        A forward/backward tab traversal now visits focusable elements in a shadow root.
     6        https://bugs.webkit.org/show_bug.cgi?id=61410
     7
     8        Like a iframe element, a shadow host becomes a scope of
     9        tabindex. That means all descendant elements in a shadow root are
     10        skipped if the host node of the shadow root is not focusable.
     11
     12        The patch doesn't affect HTMLInputElement and HTMLTextAreaElement,
     13        which uses a shadow root and do extra works in their focus()
     14        method.
     15
     16        A shadow root's <content> is not considered in this patch.
     17        That will be addressed in a following patch.
     18
     19        * fast/dom/shadow/tab-order-iframe-and-shadow-expected.txt: Added.
     20        * fast/dom/shadow/tab-order-iframe-and-shadow.html: Added.
     21
    1222011-06-08  Hayato Ito  <hayato@chromium.org>
    223
  • trunk/Source/WebCore/ChangeLog

    r88418 r88421  
     12011-06-08  Hayato Ito  <hayato@chromium.org>
     2
     3        Reviewed by Dimitri Glazkov.
     4
     5        A forward/backward tab traversal now visits focusable elements in a shadow root.
     6        https://bugs.webkit.org/show_bug.cgi?id=61410
     7
     8        Test: fast/dom/shadow/tab-order-iframe-and-shadow.html
     9
     10        Like a iframe element, a shadow host becomes a scope of
     11        tabindex. That means all descendant elements in a shadow root are
     12        skipped if the host node of the shadow root is not focusable.
     13
     14        The patch doesn't affect HTMLInputElement and HTMLTextAreaElement,
     15        which uses a shadow root and do extra works in their focus()
     16        method.
     17
     18        A shadow root's <content> is not considered in this patch.
     19        That will be addressed in a following patch.
     20
     21        * page/FocusController.cpp:
     22        (WebCore::shadowRoot):
     23        (WebCore::isTreeScopeOwner):
     24        (WebCore::FocusController::deepFocusableNode):
     25        (WebCore::FocusController::advanceFocusInDocumentOrder):
     26        (WebCore::FocusController::findFocusableNodeAcrossTreeScope):
     27        (WebCore::FocusController::findFocusableNode):
     28        (WebCore::FocusController::nextFocusableNode):
     29        (WebCore::FocusController::previousFocusableNode):
     30        (WebCore::FocusController::ownerOfTreeScope):
     31        * page/FocusController.h:
     32
    1332011-06-08  Hayato Ito  <hayato@chromium.org>
    234
  • trunk/Source/WebCore/page/FocusController.cpp

    r87874 r88421  
    5454#include "ScrollAnimator.h"
    5555#include "Settings.h"
     56#include "ShadowRoot.h"
    5657#include "SpatialNavigation.h"
    5758#include "Widget.h"
     
    146147}
    147148
     149inline static ShadowRoot* shadowRoot(Node* node)
     150{
     151    return node->isElementNode() ? toElement(node)->shadowRoot() : 0;
     152}
     153
     154inline static bool isTreeScopeOwner(Node* node)
     155{
     156    return node && (node->isFrameOwnerElement() || shadowRoot(node));
     157}
     158
    148159Node* FocusController::deepFocusableNode(FocusDirection direction, Node* node, KeyboardEvent* event)
    149160{
    150     // The node we found might be a HTMLFrameOwnerElement, so descend down the frame tree until we find either:
     161    // The node we found might be a HTMLFrameOwnerElement or a shadow host, so descend down the tree until we find either:
    151162    // 1) a focusable node, or
    152     // 2) the deepest-nested HTMLFrameOwnerElement
    153     while (node && node->isFrameOwnerElement()) {
    154         HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(node);
    155         if (!owner->contentFrame())
     163    // 2) the deepest-nested HTMLFrameOwnerElement or shadow host.
     164    while (isTreeScopeOwner(node)) {
     165        Node* foundNode;
     166        if (node->isFrameOwnerElement()) {
     167            HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(node);
     168            if (!owner->contentFrame())
     169                break;
     170            Document* document = owner->contentFrame()->document();
     171            foundNode = findFocusableNode(direction, document, 0, event);
     172        } else {
     173            ASSERT(shadowRoot(node));
     174            // FIXME: Some elements (e.g. HTMLInputElement and HTMLTextAreaElement) do extra work in their focus() methods.
     175            // Skipping these elements is the safest fix until we find a better way.
     176            if (node->hasTagName(inputTag) || node->hasTagName(textareaTag))
     177                break;
     178            foundNode = findFocusableNode(direction, shadowRoot(node), 0, event);
     179        }
     180        if (!foundNode)
    156181            break;
    157 
    158         Document* document = owner->contentFrame()->document();
    159 
    160         node = (direction == FocusDirectionForward)
    161             ? nextFocusableNode(document, 0, event)
    162             : previousFocusableNode(document, 0, event);
    163         if (!node) {
    164             node = owner;
    165             break;
    166         }
    167     }
    168 
     182        ASSERT(node != foundNode);
     183        node = foundNode;
     184    }
    169185    return node;
    170186}
     
    216232    document->updateLayoutIgnorePendingStylesheets();
    217233
    218     Node* node = (direction == FocusDirectionForward)
    219         ? nextFocusableNode(document, currentNode, event)
    220         : previousFocusableNode(document, currentNode, event);
    221            
    222     // If there's no focusable node to advance to, move up the frame tree until we find one.
    223     while (!node && frame) {
    224         Frame* parentFrame = frame->tree()->parent();
    225         if (!parentFrame)
    226             break;
    227 
    228         Document* parentDocument = parentFrame->document();
    229 
    230         HTMLFrameOwnerElement* owner = frame->ownerElement();
    231         if (!owner)
    232             break;
    233 
    234         node = (direction == FocusDirectionForward)
    235             ? nextFocusableNode(parentDocument, owner, event)
    236             : previousFocusableNode(parentDocument, owner, event);
    237 
    238         frame = parentFrame;
    239     }
    240 
    241     node = deepFocusableNode(direction, node, event);
     234    Node* node = findFocusableNodeAcrossTreeScope(direction, currentNode ? currentNode->treeScope() : document, currentNode, event);
    242235
    243236    if (!node) {
     
    251244
    252245        // Chrome doesn't want focus, so we should wrap focus.
    253         Document* d = m_page->mainFrame()->document();
    254         node = (direction == FocusDirectionForward)
    255             ? nextFocusableNode(d, 0, event)
    256             : previousFocusableNode(d, 0, event);
    257 
     246        node = findFocusableNode(direction, m_page->mainFrame()->document(), 0, event);
    258247        node = deepFocusableNode(direction, node, event);
    259248
     
    306295    static_cast<Element*>(node)->focus(false);
    307296    return true;
     297}
     298
     299Node* FocusController::findFocusableNodeAcrossTreeScope(FocusDirection direction, TreeScope* scope, Node* currentNode, KeyboardEvent* event)
     300{
     301    Node* node = findFocusableNode(direction, scope, currentNode, event);
     302    // If there's no focusable node to advance to, move up the tree scopes until we find one.
     303    while (!node && scope) {
     304        Node* owner = ownerOfTreeScope(scope);
     305        if (!owner)
     306            break;
     307        node = findFocusableNode(direction, owner->treeScope(), owner, event);
     308        scope = owner->treeScope();
     309    }
     310    node = deepFocusableNode(direction, node, event);
     311    return node;
     312}
     313
     314
     315Node* FocusController::findFocusableNode(FocusDirection direction, TreeScope* scope, Node* node, KeyboardEvent* event)
     316{
     317    return (direction == FocusDirectionForward)
     318        ? nextFocusableNode(scope, node, event)
     319        : previousFocusableNode(scope, node, event);
    308320}
    309321
     
    356368}
    357369
    358 Node* FocusController::nextFocusableNode(TreeScope* within, Node* start, KeyboardEvent* event)
     370Node* FocusController::nextFocusableNode(TreeScope* scope, Node* start, KeyboardEvent* event)
    359371{
    360372    if (start) {
     
    366378        }
    367379
    368         // First try to find a node with the same tabindex as start that comes after start in the tree scope.
     380        // First try to find a node with the same tabindex as start that comes after start in the scope.
    369381        if (Node* winner = nextNodeWithExactTabIndex(start->traverseNextNode(), start->tabIndex(), event))
    370382            return winner;
     
    375387    }
    376388
    377     // Look for the first node in the tree scope that:
     389    // Look for the first node in the scope that:
    378390    // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if start is null), and
    379     // 2) comes first in the tree scope, if there's a tie.
    380     if (Node* winner = nextNodeWithGreaterTabIndex(within, start ? start->tabIndex() : 0, event))
     391    // 2) comes first in the scope, if there's a tie.
     392    if (Node* winner = nextNodeWithGreaterTabIndex(scope, start ? start->tabIndex() : 0, event))
    381393        return winner;
    382394
    383395    // There are no nodes with a tabindex greater than start's tabindex,
    384396    // so find the first node with a tabindex of 0.
    385     return nextNodeWithExactTabIndex(within, 0, event);
    386 }
    387 
    388 Node* FocusController::previousFocusableNode(TreeScope* within, Node* start, KeyboardEvent* event)
     397    return nextNodeWithExactTabIndex(scope, 0, event);
     398}
     399
     400Node* FocusController::previousFocusableNode(TreeScope* scope, Node* start, KeyboardEvent* event)
    389401{
    390402    Node* last;
    391     for (last = within; last->lastChild(); last = last->lastChild()) { }
    392 
    393     // First try to find the last node in the tree scope that comes before start and has the same tabindex as start.
    394     // If start is null, find the last node in the tree scope with a tabindex of 0.
     403    for (last = scope; last->lastChild(); last = last->lastChild()) { }
     404
     405    // First try to find the last node in the scope that comes before start and has the same tabindex as start.
     406    // If start is null, find the last node in the scope with a tabindex of 0.
    395407    Node* startingNode;
    396408    int startingTabIndex;
     
    415427    // There are no nodes before start with the same tabindex as start, so look for a node that:
    416428    // 1) has the highest non-zero tabindex (that is less than start's tabindex), and
    417     // 2) comes last in the tree scope, if there's a tie.
     429    // 2) comes last in the scope, if there's a tie.
    418430    startingTabIndex = (start && start->tabIndex()) ? start->tabIndex() : std::numeric_limits<short>::max();
    419431    return previousNodeWithLowerTabIndex(last, startingTabIndex, event);
     432}
     433
     434Node* FocusController::ownerOfTreeScope(TreeScope* scope)
     435{
     436    ASSERT(scope);
     437    if (scope->isShadowRoot())
     438        return scope->shadowHost();
     439    if (scope->document()->frame())
     440        return scope->document()->frame()->ownerElement();
     441    return 0;
    420442}
    421443
  • trunk/Source/WebCore/page/FocusController.h

    r87874 r88421  
    6666    bool advanceFocusInDocumentOrder(FocusDirection, KeyboardEvent*, bool initialFocus);
    6767
     68    Node* findFocusableNodeAcrossTreeScope(FocusDirection, TreeScope* startScope, Node* start, KeyboardEvent*);
    6869    Node* deepFocusableNode(FocusDirection, Node*, KeyboardEvent*);
     70    Node* ownerOfTreeScope(TreeScope*);
     71
     72    // Searches through the given tree scope, starting from start node, for the next/previous selectable element that comes after/before start node.
     73    // The order followed is as specified in section 17.11.1 of the HTML4 spec, which is elements with tab indexes
     74    // first (from lowest to highest), and then elements without tab indexes (in document order).
     75    //
     76    // @param start The node from which to start searching. The node after this will be focused. May be null.
     77    //
     78    // @return The focus node that comes after/before start node.
     79    //
     80    // See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1
     81    inline Node* findFocusableNode(FocusDirection, TreeScope*, Node* start, KeyboardEvent*);
     82
     83    Node* nextFocusableNode(TreeScope*, Node* start, KeyboardEvent*);
     84    Node* previousFocusableNode(TreeScope*, Node* start, KeyboardEvent*);
    6985
    7086    bool advanceFocusDirectionallyInContainer(Node* container, const IntRect& startingRect, FocusDirection, KeyboardEvent*);
     
    7793    bool m_isChangingFocusedFrame;
    7894
    79     // Searches through the document, starting from start node, for the next selectable element that comes after start node.
    80     // The order followed is as specified in section 17.11.1 of the HTML4 spec, which is elements with tab indexes
    81     // first (from lowest to highest), and then elements without tab indexes (in document order).
    82     //
    83     // @param within The tree scope where a search is executed.
    84     // @param start The node from which to start searching. The node before this will be focused. May be null.
    85     //
    86     // @return The focus node that comes after start node.
    87     //
    88     // See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1
    89     Node* nextFocusableNode(TreeScope* within, Node* start, KeyboardEvent*);
    90 
    91     // Searches through the document, starting from start node, for the previous selectable element that comes before start node.
    92     // The order followed is as specified in section 17.11.1 of the HTML4 spec, which is elements with tab indexes
    93     // first (from lowest to highest), and then elements without tab indexes (in document order).
    94     //
    95     // @param within The tree scope where a search is executed.
    96     // @param start The node from which to start searching. The node before this will be focused. May be null.
    97     //
    98     // @return The focus node that comes before start node.
    99     //
    100     // See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1
    101     Node* previousFocusableNode(TreeScope* within, Node* start, KeyboardEvent*);
    10295};
    10396
Note: See TracChangeset for help on using the changeset viewer.