Chromium Code Reviews| Index: third_party/WebKit/Source/core/page/FocusController.cpp |
| diff --git a/third_party/WebKit/Source/core/page/FocusController.cpp b/third_party/WebKit/Source/core/page/FocusController.cpp |
| index 11067ff303170ed912a78025704172e92cb09407..efdfedcf16563ceb967b847049a850f729245a38 100644 |
| --- a/third_party/WebKit/Source/core/page/FocusController.cpp |
| +++ b/third_party/WebKit/Source/core/page/FocusController.cpp |
| @@ -28,11 +28,12 @@ |
| #include "core/HTMLNames.h" |
| #include "core/dom/AXObjectCache.h" |
| +#include "core/dom/ContainerNode.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/Element.h" |
| #include "core/dom/ElementTraversal.h" |
| -#include "core/dom/NodeTraversal.h" |
| #include "core/dom/Range.h" |
| +#include "core/dom/shadow/AssignedElementTraversal.h" |
| #include "core/dom/shadow/ElementShadow.h" |
| #include "core/dom/shadow/ShadowRoot.h" |
| #include "core/editing/EditingUtilities.h" // For firstPositionInOrBeforeNode |
| @@ -49,6 +50,7 @@ |
| #include "core/html/HTMLImageElement.h" |
| #include "core/html/HTMLPlugInElement.h" |
| #include "core/html/HTMLShadowElement.h" |
| +#include "core/html/HTMLSlotElement.h" |
| #include "core/html/HTMLTextFormControlElement.h" |
| #include "core/input/EventHandler.h" |
| #include "core/page/ChromeClient.h" |
| @@ -69,89 +71,165 @@ inline bool isShadowInsertionPointFocusScopeOwner(Element& element) |
| return isActiveShadowInsertionPoint(element) && toHTMLShadowElement(element).olderShadowRoot(); |
| } |
| -class FocusNavigationScope { |
| +class ScopedFocusNavigation { |
| STACK_ALLOCATED(); |
| public: |
| - Element* firstElement() const; |
| - Element* lastElement() const; |
| + Element* currentElement() const; |
| + void setCurrentElement(Element*); |
| + void moveToNext(); |
| + void moveToPrevious(); |
| + void moveToFirst(); |
| + void moveToLast(); |
| Element* owner() const; |
| - static FocusNavigationScope focusNavigationScopeOf(const Element&); |
| - static FocusNavigationScope ownedByNonFocusableFocusScopeOwner(Element&); |
| - static FocusNavigationScope ownedByShadowHost(const Element&); |
| - static FocusNavigationScope ownedByShadowInsertionPoint(HTMLShadowElement&); |
| - static FocusNavigationScope ownedByIFrame(const HTMLFrameOwnerElement&); |
| + static ScopedFocusNavigation focusNavigationScopeOf(const Element& root, const Element* current); |
|
hayato
2016/03/03 05:42:22
You might want to rename |focusNavigationScopeOf|
yuzuchan
2016/03/04 03:48:37
Renamed to createScopedFocusNavigation
|
| + static ScopedFocusNavigation ownedByNonFocusableFocusScopeOwner(Element&); |
| + static ScopedFocusNavigation ownedByShadowHost(const Element&); |
| + static ScopedFocusNavigation ownedByShadowInsertionPoint(HTMLShadowElement&); |
| + static ScopedFocusNavigation ownedByHTMLSlotElement(const HTMLSlotElement&); |
| + static ScopedFocusNavigation ownedByIFrame(const HTMLFrameOwnerElement&); |
| private: |
| - explicit FocusNavigationScope(TreeScope*); |
| - ContainerNode& rootNode() const; |
| - RawPtrWillBeMember<TreeScope> m_rootTreeScope; |
| + explicit ScopedFocusNavigation(TreeScope*, const Element*); |
|
hayato
2016/03/03 05:42:22
We do not need to specify |explicit| if a construc
yuzuchan
2016/03/04 03:48:38
Done.
|
| + explicit ScopedFocusNavigation(HTMLSlotElement*, const Element*); |
| + RawPtrWillBeMember<ContainerNode> m_rootNode; |
| + RawPtrWillBeMember<HTMLSlotElement> m_rootSlot; |
| + RawPtrWillBeMember<Element> m_current; |
| }; |
| -FocusNavigationScope::FocusNavigationScope(TreeScope* treeScope) |
| - : m_rootTreeScope(treeScope) |
| +ScopedFocusNavigation::ScopedFocusNavigation(TreeScope* treeScope, const Element* current) |
|
hayato
2016/03/03 05:42:22
"TreeScope*" can be "TreeScope&".
yuzuchan
2016/03/04 03:48:38
Done.
|
| + : m_rootNode(treeScope->rootNode()) |
| + , m_current(const_cast<Element*>(current)) |
| { |
| ASSERT(treeScope); |
| } |
| -ContainerNode& FocusNavigationScope::rootNode() const |
| +ScopedFocusNavigation::ScopedFocusNavigation(HTMLSlotElement* slot, const Element* current) |
|
hayato
2016/03/03 05:42:22
"HTMLSlotElement*" can be "HTMLSlotElement&".
Then
yuzuchan
2016/03/04 03:48:37
Done.
|
| + : m_rootSlot(slot) |
| + , m_current(const_cast<Element*>(current)) |
| { |
| - return m_rootTreeScope->rootNode(); |
| + ASSERT(slot); |
| } |
| -Element* FocusNavigationScope::firstElement() const |
| +Element* ScopedFocusNavigation::currentElement() const |
| { |
| - ContainerNode& root = rootNode(); |
| - return root.isElementNode() ? &toElement(root) : ElementTraversal::next(root); |
| + return m_current; |
| } |
| -Element* FocusNavigationScope::lastElement() const |
| +void ScopedFocusNavigation::setCurrentElement(Element* element) |
| { |
| - return ElementTraversal::lastWithin(rootNode()); |
| + m_current = element; |
| } |
| -Element* FocusNavigationScope::owner() const |
| +void ScopedFocusNavigation::moveToNext() |
| { |
| - ContainerNode& root = rootNode(); |
| - if (root.isShadowRoot()) { |
| - ShadowRoot& shadowRoot = toShadowRoot(root); |
| + if (!m_current) |
|
hayato
2016/03/03 05:42:22
If |moveToNext| is only called when |m_current| is
yuzuchan
2016/03/04 03:48:38
Done.
|
| + return; |
| + if (m_rootSlot) { |
| + m_current = AssignedElementTraversal::next(*m_current); |
| + } else { |
| + m_current = ElementTraversal::next(*m_current); |
| + while (m_current && AssignedElementTraversal::isInAssignedScope(*m_current)) |
| + m_current = ElementTraversal::next(*m_current); |
| + } |
| +} |
| + |
| +void ScopedFocusNavigation::moveToPrevious() |
| +{ |
| + if (!m_current) |
|
hayato
2016/03/03 05:42:22
ditto
yuzuchan
2016/03/04 03:48:38
Done.
|
| + return; |
| + if (m_rootSlot) { |
| + m_current = AssignedElementTraversal::previous(*m_current); |
| + } else { |
| + m_current = ElementTraversal::previous(*m_current); |
| + while (m_current && AssignedElementTraversal::isInAssignedScope(*m_current)) |
| + m_current = ElementTraversal::previous(*m_current); |
| + } |
| +} |
| + |
| +void ScopedFocusNavigation::moveToFirst() |
| +{ |
| + if (m_rootSlot) { |
| + if (!m_rootSlot->getAssignedNodes().isEmpty()) |
| + m_current = toElement(m_rootSlot->getAssignedNodes().first().get()); |
|
hayato
2016/03/03 05:42:22
ditto: getAssignedNodes().first() could be a non-e
yuzuchan
2016/03/04 03:48:37
Done.
|
| + else |
| + m_current = nullptr; |
| + } else { |
| + Element* first = m_rootNode->isElementNode() ? &toElement(*m_rootNode) : ElementTraversal::next(*m_rootNode); |
| + while (first && AssignedElementTraversal::isInAssignedScope(*first)) |
| + first = ElementTraversal::next(*first, m_rootNode); |
| + m_current = first; |
| + } |
| +} |
| + |
| +void ScopedFocusNavigation::moveToLast() |
| +{ |
| + if (m_rootSlot) { |
| + if (!m_rootSlot->getAssignedNodes().isEmpty()) { |
| + m_current = toElement(m_rootSlot->getAssignedNodes().last().get()); |
|
hayato
2016/03/03 05:42:22
ditto: getAssignedNodes().last() could be a non-el
yuzuchan
2016/03/04 03:48:37
Done.
|
| + } else { |
| + m_current = nullptr; |
| + } |
| + } else { |
| + Element* last = ElementTraversal::lastWithin(*m_rootNode); |
| + while (last && AssignedElementTraversal::isInAssignedScope(*last)) |
| + last = ElementTraversal::previous(*last, m_rootNode); |
| + m_current = last; |
| + } |
| +} |
| + |
| +Element* ScopedFocusNavigation::owner() const |
| +{ |
| + if (m_rootSlot) |
| + return m_rootSlot; |
| + if (m_rootNode->isShadowRoot()) { |
| + ShadowRoot& shadowRoot = toShadowRoot(*m_rootNode); |
| return shadowRoot.isYoungest() ? shadowRoot.host() : shadowRoot.shadowInsertionPointOfYoungerShadowRoot(); |
| } |
| // FIXME: Figure out the right thing for OOPI here. |
| - if (Frame* frame = root.document().frame()) |
| + if (Frame* frame = m_rootNode->document().frame()) |
| return frame->deprecatedLocalOwner(); |
| return nullptr; |
| } |
| -FocusNavigationScope FocusNavigationScope::focusNavigationScopeOf(const Element& element) |
| +ScopedFocusNavigation ScopedFocusNavigation::focusNavigationScopeOf(const Element& root, const Element* current) |
| { |
| - return FocusNavigationScope(&element.treeScope()); |
| + if (AssignedElementTraversal::isInAssignedScope(root)) |
| + return ScopedFocusNavigation(AssignedElementTraversal::slot(root), current); |
| + return ScopedFocusNavigation(&root.treeScope(), current); |
| } |
| -FocusNavigationScope FocusNavigationScope::ownedByNonFocusableFocusScopeOwner(Element& element) |
| +ScopedFocusNavigation ScopedFocusNavigation::ownedByNonFocusableFocusScopeOwner(Element& element) |
| { |
| if (isShadowHost(element)) |
| - return FocusNavigationScope::ownedByShadowHost(element); |
| - ASSERT(isShadowInsertionPointFocusScopeOwner(element)); |
| - return FocusNavigationScope::ownedByShadowInsertionPoint(toHTMLShadowElement(element)); |
| + return ScopedFocusNavigation::ownedByShadowHost(element); |
| + if (isShadowInsertionPointFocusScopeOwner(element)) |
| + return ScopedFocusNavigation::ownedByShadowInsertionPoint(toHTMLShadowElement(element)); |
| + ASSERT(isHTMLSlotElement(element)); |
| + return ScopedFocusNavigation::ownedByHTMLSlotElement(toHTMLSlotElement(element)); |
| } |
| -FocusNavigationScope FocusNavigationScope::ownedByShadowHost(const Element& element) |
| +ScopedFocusNavigation ScopedFocusNavigation::ownedByShadowHost(const Element& element) |
| { |
| ASSERT(isShadowHost(element)); |
| - return FocusNavigationScope(&element.shadow()->youngestShadowRoot()); |
| + return ScopedFocusNavigation(&element.shadow()->youngestShadowRoot(), nullptr); |
| } |
| -FocusNavigationScope FocusNavigationScope::ownedByIFrame(const HTMLFrameOwnerElement& frame) |
| +ScopedFocusNavigation ScopedFocusNavigation::ownedByIFrame(const HTMLFrameOwnerElement& frame) |
| { |
| ASSERT(frame.contentFrame()); |
| ASSERT(frame.contentFrame()->isLocalFrame()); |
| - return FocusNavigationScope(toLocalFrame(frame.contentFrame())->document()); |
| + return ScopedFocusNavigation(toLocalFrame(frame.contentFrame())->document(), nullptr); |
| } |
| -FocusNavigationScope FocusNavigationScope::ownedByShadowInsertionPoint(HTMLShadowElement& shadowInsertionPoint) |
| +ScopedFocusNavigation ScopedFocusNavigation::ownedByShadowInsertionPoint(HTMLShadowElement& shadowInsertionPoint) |
| { |
| ASSERT(isShadowInsertionPointFocusScopeOwner(shadowInsertionPoint)); |
| - return FocusNavigationScope(shadowInsertionPoint.olderShadowRoot()); |
| + return ScopedFocusNavigation(shadowInsertionPoint.olderShadowRoot(), nullptr); |
| +} |
| + |
| +ScopedFocusNavigation ScopedFocusNavigation::ownedByHTMLSlotElement(const HTMLSlotElement& element) |
| +{ |
| + return ScopedFocusNavigation(const_cast<HTMLSlotElement*>(&element), nullptr); |
| } |
| inline void dispatchBlurEvent(const Document& document, Element& focusedElement) |
| @@ -234,7 +312,7 @@ inline bool isKeyboardFocusableShadowHost(const Element& element) |
| inline bool isNonFocusableFocusScopeOwner(Element& element) |
| { |
| - return isNonKeyboardFocusableShadowHost(element) || isShadowInsertionPointFocusScopeOwner(element); |
| + return isNonKeyboardFocusableShadowHost(element) || isShadowInsertionPointFocusScopeOwner(element) || isHTMLSlotElement(element); |
| } |
| inline bool isShadowHostDelegatesFocus(const Element& element) |
| @@ -252,59 +330,65 @@ inline bool shouldVisit(Element& element) |
| return element.isKeyboardFocusable() || isNonFocusableFocusScopeOwner(element); |
| } |
| -Element* findElementWithExactTabIndex(Element* start, int tabIndex, WebFocusType type) |
| +Element* findElementWithExactTabIndex(ScopedFocusNavigation& scope, int tabIndex, WebFocusType type) |
| { |
| // Search is inclusive of start |
| - for (Element* element = start; element; element = type == WebFocusTypeForward ? ElementTraversal::next(*element) : ElementTraversal::previous(*element)) { |
| - if (shouldVisit(*element) && adjustedTabIndex(*element) == tabIndex) |
| - return element; |
| + for (; scope.currentElement(); type == WebFocusTypeForward ? scope.moveToNext() : scope.moveToPrevious()) { |
| + Element* current = scope.currentElement(); |
| + if (shouldVisit(*current) && adjustedTabIndex(*current) == tabIndex) |
| + return current; |
| } |
| return nullptr; |
| } |
| -Element* nextElementWithGreaterTabIndex(Element* start, int tabIndex) |
| +Element* nextElementWithGreaterTabIndex(ScopedFocusNavigation& scope, int tabIndex) |
| { |
| // Search is inclusive of start |
| int winningTabIndex = std::numeric_limits<short>::max() + 1; |
| Element* winner = nullptr; |
| - for (Element& element : ElementTraversal::startsAt(start)) { |
| - int currentTabIndex = adjustedTabIndex(element); |
| - if (shouldVisit(element) && currentTabIndex > tabIndex && currentTabIndex < winningTabIndex) { |
| - winner = &element; |
| + for (; scope.currentElement(); scope.moveToNext()) { |
| + Element* current = scope.currentElement(); |
| + int currentTabIndex = adjustedTabIndex(*current); |
| + if (shouldVisit(*current) && currentTabIndex > tabIndex && currentTabIndex < winningTabIndex) { |
| + winner = current; |
| winningTabIndex = currentTabIndex; |
| } |
| } |
| return winner; |
| } |
| -Element* previousElementWithLowerTabIndex(Element* start, int tabIndex) |
| +Element* previousElementWithLowerTabIndex(ScopedFocusNavigation& scope, int tabIndex) |
| { |
| // Search is inclusive of start |
| int winningTabIndex = 0; |
| Element* winner = nullptr; |
| - for (Element* element = start; element; element = ElementTraversal::previous(*element)) { |
| - int currentTabIndex = adjustedTabIndex(*element); |
| - if (shouldVisit(*element) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) { |
| - winner = element; |
| + for (; scope.currentElement(); scope.moveToPrevious()) { |
| + Element* current = scope.currentElement(); |
| + int currentTabIndex = adjustedTabIndex(*current); |
| + if (shouldVisit(*current) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) { |
| + winner = current; |
| winningTabIndex = currentTabIndex; |
| } |
| } |
| return winner; |
| } |
| -Element* nextFocusableElement(const FocusNavigationScope& scope, Element* start) |
| +Element* nextFocusableElement(ScopedFocusNavigation& scope) |
| { |
| - if (start) { |
| - int tabIndex = adjustedTabIndex(*start); |
| + Element* current = scope.currentElement(); |
| + if (current) { |
| + int tabIndex = adjustedTabIndex(*current); |
| // If an element is excluded from the normal tabbing cycle, the next focusable element is determined by tree order |
| if (tabIndex < 0) { |
| - for (Element& element : ElementTraversal::startsAfter(*start)) { |
| - if (shouldVisit(element) && adjustedTabIndex(element) >= 0) |
| - return &element; |
| + for (scope.moveToNext(); scope.currentElement(); scope.moveToNext()) { |
| + current = scope.currentElement(); |
| + if (shouldVisit(*current) && adjustedTabIndex(*current) >= 0) |
| + return current; |
| } |
| } else { |
| // First try to find an element with the same tabindex as start that comes after start in the scope. |
| - if (Element* winner = findElementWithExactTabIndex(ElementTraversal::next(*start), tabIndex, WebFocusTypeForward)) |
| + scope.moveToNext(); |
| + if (Element* winner = findElementWithExactTabIndex(scope, tabIndex, WebFocusTypeForward)) |
| return winner; |
| } |
| if (!tabIndex) { |
| @@ -316,46 +400,51 @@ Element* nextFocusableElement(const FocusNavigationScope& scope, Element* start) |
| // Look for the first element 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 (Element* winner = nextElementWithGreaterTabIndex(scope.firstElement(), start ? adjustedTabIndex(*start) : 0)) |
| + scope.moveToFirst(); |
| + if (Element* winner = nextElementWithGreaterTabIndex(scope, current ? adjustedTabIndex(*current) : 0)) { |
| return winner; |
| + } |
| // There are no elements with a tabindex greater than start's tabindex, |
| // so find the first element with a tabindex of 0. |
| - return findElementWithExactTabIndex(scope.firstElement(), 0, WebFocusTypeForward); |
| + scope.moveToFirst(); |
| + return findElementWithExactTabIndex(scope, 0, WebFocusTypeForward); |
| } |
| -Element* previousFocusableElement(const FocusNavigationScope& scope, Element* start) |
| +Element* previousFocusableElement(ScopedFocusNavigation& scope) |
| { |
| - Element* lastElement = scope.lastElement(); |
| - |
| // First try to find the last element in the scope that comes before start and has the same tabindex as start. |
| // If start is null, find the last element in the scope with a tabindex of 0. |
| - Element* startElement; |
| int startTabIndex; |
| - if (start) { |
| - startElement = ElementTraversal::previous(*start); |
| - startTabIndex = adjustedTabIndex(*start); |
| + Element* current = scope.currentElement(); |
| + if (current) { |
| + scope.moveToPrevious(); |
| + startTabIndex = adjustedTabIndex(*current); |
| } else { |
| - startElement = lastElement; |
| + scope.moveToLast(); |
| startTabIndex = 0; |
| } |
| // However, if an element is excluded from the normal tabbing cycle, the previous focusable element is determined by tree order |
| if (startTabIndex < 0) { |
| - for (Element* element = startElement; element; element = ElementTraversal::previous(*element)) { |
| - if (shouldVisit(*element) && adjustedTabIndex(*element) >= 0) |
| - return element; |
| + scope.moveToPrevious(); |
| + for (; scope.currentElement(); scope.moveToPrevious()) { |
| + current = scope.currentElement(); |
| + if (shouldVisit(*current) && adjustedTabIndex(*current) >= 0) |
| + return current; |
| } |
| } else { |
| - if (Element* winner = findElementWithExactTabIndex(startElement, startTabIndex, WebFocusTypeBackward)) |
| + if (Element* winner = findElementWithExactTabIndex(scope, startTabIndex, WebFocusTypeBackward)) |
| return winner; |
| } |
| // There are no elements before start with the same tabindex as start, so look for an element 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. |
| - startTabIndex = (start && startTabIndex) ? startTabIndex : std::numeric_limits<short>::max(); |
| - return previousElementWithLowerTabIndex(lastElement, startTabIndex); |
| + startTabIndex = (current && startTabIndex) ? startTabIndex : std::numeric_limits<short>::max(); |
| + scope.moveToLast(); |
| + |
| + return previousElementWithLowerTabIndex(scope, startTabIndex); |
| } |
| // Searches through the given tree scope, starting from start element, for the next/previous |
| @@ -371,58 +460,60 @@ Element* previousFocusableElement(const FocusNavigationScope& scope, Element* st |
| // |
| // [1] https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation |
| // [2] https://w3c.github.io/webcomponents/spec/shadow/#focus-navigation |
| -inline Element* findFocusableElementInternal(WebFocusType type, const FocusNavigationScope& scope, Element* element) |
| +inline Element* findFocusableElementInternal(WebFocusType type, ScopedFocusNavigation& scope) |
| { |
| - Element* found = (type == WebFocusTypeForward) ? nextFocusableElement(scope, element) : previousFocusableElement(scope, element); |
| + Element* found = (type == WebFocusTypeForward) ? nextFocusableElement(scope) : previousFocusableElement(scope); |
| return found; |
| } |
| -Element* findFocusableElementRecursivelyForward(const FocusNavigationScope& scope, Element* start) |
| +Element* findFocusableElementRecursivelyForward(ScopedFocusNavigation& scope) |
| { |
| // Starting element is exclusive. |
| - Element* found = findFocusableElementInternal(WebFocusTypeForward, scope, start); |
| + Element* found = findFocusableElementInternal(WebFocusTypeForward, scope); |
| while (found) { |
| if (isShadowHostDelegatesFocus(*found)) { |
| // If tabindex is positive, find focusable element inside its shadow tree. |
| if (found->tabIndex() >= 0 && isShadowHostWithoutCustomFocusLogic(*found)) { |
| - FocusNavigationScope innerScope = FocusNavigationScope::ownedByShadowHost(*found); |
| - if (Element* foundInInnerFocusScope = findFocusableElementRecursivelyForward(innerScope, nullptr)) |
| + ScopedFocusNavigation innerScope = ScopedFocusNavigation::ownedByShadowHost(*found); |
| + if (Element* foundInInnerFocusScope = findFocusableElementRecursivelyForward(innerScope)) |
| return foundInInnerFocusScope; |
| } |
| // Skip to the next element in the same scope. |
| - found = findFocusableElementInternal(WebFocusTypeForward, scope, found); |
| + found = findFocusableElementInternal(WebFocusTypeForward, scope); |
| continue; |
| } |
| if (!isNonFocusableFocusScopeOwner(*found)) |
| return found; |
| - // Now |found| is on a non focusable scope owner (either shadow host or <shadow>) |
| + // Now |found| is on a non focusable scope owner (either shadow host or <shadow> or slot) |
| // Find inside the inward scope and return it if found. Otherwise continue searching in the same |
| // scope. |
| - FocusNavigationScope innerScope = FocusNavigationScope::ownedByNonFocusableFocusScopeOwner(*found); |
| - if (Element* foundInInnerFocusScope = findFocusableElementRecursivelyForward(innerScope, nullptr)) |
| + ScopedFocusNavigation innerScope = ScopedFocusNavigation::ownedByNonFocusableFocusScopeOwner(*found); |
| + if (Element* foundInInnerFocusScope = findFocusableElementRecursivelyForward(innerScope)) |
| return foundInInnerFocusScope; |
| - found = findFocusableElementInternal(WebFocusTypeForward, scope, found); |
| + scope.setCurrentElement(found); |
| + found = findFocusableElementInternal(WebFocusTypeForward, scope); |
| } |
| return nullptr; |
| } |
| -Element* findFocusableElementRecursivelyBackward(const FocusNavigationScope& scope, Element* start) |
| +Element* findFocusableElementRecursivelyBackward(ScopedFocusNavigation& scope) |
| { |
| // Starting element is exclusive. |
| - Element* found = findFocusableElementInternal(WebFocusTypeBackward, scope, start); |
| + Element* found = findFocusableElementInternal(WebFocusTypeBackward, scope); |
| + |
| while (found) { |
| // Now |found| is on a focusable shadow host. |
| // Find inside shadow backwards. If any focusable element is found, return it, otherwise return |
| // the host itself. |
| if (isKeyboardFocusableShadowHost(*found)) { |
| - FocusNavigationScope innerScope = FocusNavigationScope::ownedByShadowHost(*found); |
| - Element* foundInInnerFocusScope = findFocusableElementRecursivelyBackward(innerScope, nullptr); |
| + ScopedFocusNavigation innerScope = ScopedFocusNavigation::ownedByShadowHost(*found); |
| + Element* foundInInnerFocusScope = findFocusableElementRecursivelyBackward(innerScope); |
| if (foundInInnerFocusScope) |
| return foundInInnerFocusScope; |
| if (isShadowHostDelegatesFocus(*found)) { |
| - found = findFocusableElementInternal(WebFocusTypeBackward, scope, found); |
| + found = findFocusableElementInternal(WebFocusTypeBackward, scope); |
| continue; |
| } |
| return found; |
| @@ -431,33 +522,37 @@ Element* findFocusableElementRecursivelyBackward(const FocusNavigationScope& sco |
| // If delegatesFocus is true and tabindex is negative, skip the whole shadow tree under the |
| // shadow host. |
| if (isShadowHostDelegatesFocus(*found) && found->tabIndex() < 0) { |
| - found = findFocusableElementInternal(WebFocusTypeBackward, scope, found); |
| + found = findFocusableElementInternal(WebFocusTypeBackward, scope); |
| continue; |
| } |
| - // Now |found| is on a non focusable scope owner (either shadow host or <shadow>). |
| + // Now |found| is on a non focusable scope owner (either shadow host or <shadow> or slot). |
| // Find focusable element in descendant scope. If not found, find next focusable element within the |
| // current scope. |
| if (isNonFocusableFocusScopeOwner(*found)) { |
| - FocusNavigationScope innerScope = FocusNavigationScope::ownedByNonFocusableFocusScopeOwner(*found); |
| - Element* foundInInnerFocusScope = findFocusableElementRecursivelyBackward(innerScope, nullptr); |
| + ScopedFocusNavigation innerScope = ScopedFocusNavigation::ownedByNonFocusableFocusScopeOwner(*found); |
| + Element* foundInInnerFocusScope = findFocusableElementRecursivelyBackward(innerScope); |
| + |
| + |
| if (foundInInnerFocusScope) |
| return foundInInnerFocusScope; |
| - found = findFocusableElementInternal(WebFocusTypeBackward, scope, found); |
| + found = findFocusableElementInternal(WebFocusTypeBackward, scope); |
| continue; |
| } |
| if (!isShadowHostDelegatesFocus(*found)) |
| return found; |
| - found = findFocusableElementInternal(WebFocusTypeBackward, scope, found); |
| + |
| + scope.setCurrentElement(found); |
| + found = findFocusableElementInternal(WebFocusTypeBackward, scope); |
| } |
| return nullptr; |
| } |
| -Element* findFocusableElementRecursively(WebFocusType type, const FocusNavigationScope& scope, Element* start) |
| +Element* findFocusableElementRecursively(WebFocusType type, ScopedFocusNavigation& scope) |
| { |
| return (type == WebFocusTypeForward) ? |
| - findFocusableElementRecursivelyForward(scope, start) : |
| - findFocusableElementRecursivelyBackward(scope, start); |
| + findFocusableElementRecursivelyForward(scope) : |
| + findFocusableElementRecursivelyBackward(scope); |
| } |
| Element* findFocusableElementDescendingDownIntoFrameDocument(WebFocusType type, Element* element) |
| @@ -470,7 +565,8 @@ Element* findFocusableElementDescendingDownIntoFrameDocument(WebFocusType type, |
| if (!owner.contentFrame() || !owner.contentFrame()->isLocalFrame()) |
| break; |
| toLocalFrame(owner.contentFrame())->document()->updateLayoutIgnorePendingStylesheets(); |
| - Element* foundElement = findFocusableElementRecursively(type, FocusNavigationScope::ownedByIFrame(owner), nullptr); |
| + ScopedFocusNavigation scope = ScopedFocusNavigation::ownedByIFrame(owner); |
| + Element* foundElement = findFocusableElementRecursively(type, scope); |
| if (!foundElement) |
| break; |
| ASSERT(element != foundElement); |
| @@ -479,56 +575,57 @@ Element* findFocusableElementDescendingDownIntoFrameDocument(WebFocusType type, |
| return element; |
| } |
| -Element* findFocusableElementAcrossFocusScopesForward(const FocusNavigationScope& scope, Element* current) |
| +Element* findFocusableElementAcrossFocusScopesForward(ScopedFocusNavigation& scope) |
| { |
| + Element* current = scope.currentElement(); |
| ASSERT(!current || !isNonFocusableShadowHost(*current)); |
| Element* found; |
| if (current && isShadowHostWithoutCustomFocusLogic(*current)) { |
| - FocusNavigationScope innerScope = FocusNavigationScope::ownedByShadowHost(*current); |
| - Element* foundInInnerFocusScope = findFocusableElementRecursivelyForward(innerScope, nullptr); |
| - found = foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableElementRecursivelyForward(scope, current); |
| + ScopedFocusNavigation innerScope = ScopedFocusNavigation::ownedByShadowHost(*current); |
| + Element* foundInInnerFocusScope = findFocusableElementRecursivelyForward(innerScope); |
| + found = foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableElementRecursivelyForward(scope); |
| } else { |
| - found = findFocusableElementRecursivelyForward(scope, current); |
| + found = findFocusableElementRecursivelyForward(scope); |
| } |
| // If there's no focusable element to advance to, move up the focus scopes until we find one. |
| - FocusNavigationScope currentScope = scope; |
| + ScopedFocusNavigation currentScope = scope; |
| while (!found) { |
| Element* owner = currentScope.owner(); |
| if (!owner) |
| break; |
| - currentScope = FocusNavigationScope::focusNavigationScopeOf(*owner); |
| - found = findFocusableElementRecursivelyForward(currentScope, owner); |
| + currentScope = ScopedFocusNavigation::focusNavigationScopeOf(*owner, owner); |
| + found = findFocusableElementRecursivelyForward(currentScope); |
| } |
| return findFocusableElementDescendingDownIntoFrameDocument(WebFocusTypeForward, found); |
| } |
| -Element* findFocusableElementAcrossFocusScopesBackward(const FocusNavigationScope& scope, Element* current) |
| +Element* findFocusableElementAcrossFocusScopesBackward(ScopedFocusNavigation& scope) |
| { |
| - ASSERT(!current || !isNonFocusableShadowHost(*current)); |
| - Element* found = findFocusableElementRecursivelyBackward(scope, current); |
| + ASSERT(!scope.currentElement() || !isNonFocusableShadowHost(*scope.currentElement())); |
| + Element* found = findFocusableElementRecursivelyBackward(scope); |
| // If there's no focusable element to advance to, move up the focus scopes until we find one. |
| - FocusNavigationScope currentScope = scope; |
| + ScopedFocusNavigation currentScope = scope; |
| while (!found) { |
| Element* owner = currentScope.owner(); |
| if (!owner) |
| break; |
| - currentScope = FocusNavigationScope::focusNavigationScopeOf(*owner); |
| + currentScope = ScopedFocusNavigation::focusNavigationScopeOf(*owner, owner); |
| if (isKeyboardFocusableShadowHost(*owner) && !isShadowHostDelegatesFocus(*owner)) { |
| found = owner; |
| break; |
| } |
| - found = findFocusableElementRecursivelyBackward(currentScope, owner); |
| + found = findFocusableElementRecursivelyBackward(currentScope); |
| } |
| return findFocusableElementDescendingDownIntoFrameDocument(WebFocusTypeBackward, found); |
| } |
| -Element* findFocusableElementAcrossFocusScopes(WebFocusType type, const FocusNavigationScope& scope, Element* current) |
| +Element* findFocusableElementAcrossFocusScopes(WebFocusType type, ScopedFocusNavigation& scope) |
| { |
| return (type == WebFocusTypeForward) ? |
| - findFocusableElementAcrossFocusScopesForward(scope, current) : |
| - findFocusableElementAcrossFocusScopesBackward(scope, current); |
| + findFocusableElementAcrossFocusScopesForward(scope) : |
| + findFocusableElementAcrossFocusScopesBackward(scope); |
| } |
| inline Element* adjustToElement(Node* node, WebFocusType type) |
| @@ -541,6 +638,8 @@ inline Element* adjustToElement(Node* node, WebFocusType type) |
| // The returned element is used as an *exclusive* start element. Thus, we should return the result of ElementTraversal::previous(*node), |
| // instead of ElementTraversal::next(*node), if type == WebFocusTypeForward, and vice-versa. |
| // The caller will call ElementTraversal::{next/previous} for the returned value and get the {next|previous} element of the |node|. |
| + |
| + // TODO(yuzus) Use ScopedFocusNavigation traversal here. |
| return (type == WebFocusTypeForward) ? ElementTraversal::previous(*node) : ElementTraversal::next(*node); |
| } |
| @@ -755,8 +854,8 @@ bool FocusController::advanceFocusInDocumentOrder(LocalFrame* frame, Element* st |
| current = adjustToElement(frame->selection().start().anchorNode(), type); |
| document->updateLayoutIgnorePendingStylesheets(); |
| - |
| - RefPtrWillBeRawPtr<Element> element = findFocusableElementAcrossFocusScopes(type, FocusNavigationScope::focusNavigationScopeOf(current ? *current : *document->documentElement()), current); |
| + ScopedFocusNavigation scope = ScopedFocusNavigation::focusNavigationScopeOf(current ? *current : *document->documentElement(), current); |
| + RefPtrWillBeRawPtr<Element> element = findFocusableElementAcrossFocusScopes(type, scope); |
| if (!element) { |
| // If there's a RemoteFrame on the ancestor chain, we need to continue |
| @@ -778,7 +877,8 @@ bool FocusController::advanceFocusInDocumentOrder(LocalFrame* frame, Element* st |
| } |
| // Chrome doesn't want focus, so we should wrap focus. |
| - element = findFocusableElementRecursively(type, FocusNavigationScope::focusNavigationScopeOf(*toLocalFrame(m_page->mainFrame())->document()->documentElement()), nullptr); |
| + ScopedFocusNavigation scope = ScopedFocusNavigation::focusNavigationScopeOf(*toLocalFrame(m_page->mainFrame())->document()->documentElement(), nullptr); |
| + element = findFocusableElementRecursively(type, scope); |
| element = findFocusableElementDescendingDownIntoFrameDocument(type, element.get()); |
| if (!element) |
| @@ -838,13 +938,15 @@ Element* FocusController::findFocusableElement(WebFocusType type, Element& eleme |
| { |
| // FIXME: No spacial navigation code yet. |
| ASSERT(type == WebFocusTypeForward || type == WebFocusTypeBackward); |
| - return findFocusableElementAcrossFocusScopes(type, FocusNavigationScope::focusNavigationScopeOf(element), &element); |
| + ScopedFocusNavigation scope = ScopedFocusNavigation::focusNavigationScopeOf(element, &element); |
| + return findFocusableElementAcrossFocusScopes(type, scope); |
| } |
| Element* FocusController::findFocusableElementInShadowHost(const Element& shadowHost) |
| { |
| ASSERT(shadowHost.authorShadowRoot()); |
| - return findFocusableElementAcrossFocusScopes(WebFocusTypeForward, FocusNavigationScope::ownedByShadowHost(shadowHost), nullptr); |
| + ScopedFocusNavigation scope = ScopedFocusNavigation::ownedByShadowHost(shadowHost); |
| + return findFocusableElementAcrossFocusScopes(WebFocusTypeForward, scope); |
| } |
| static bool relinquishesEditingFocus(const Element& element) |