| 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 9f9b58368ff266604e36f62763ec9d01d125e4a6..fd67e5a0e0b7db5e3a13b0eef8d5a478bb729c0f 100644
|
| --- a/third_party/WebKit/Source/core/page/FocusController.cpp
|
| +++ b/third_party/WebKit/Source/core/page/FocusController.cpp
|
| @@ -28,10 +28,12 @@
|
|
|
| #include "core/HTMLNames.h"
|
| #include "core/dom/AXObjectCache.h"
|
| +#include "core/dom/AssignedNodeTraversal.h"
|
| +#include "core/dom/CombinedNodeTraversal.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/ElementShadow.h"
|
| #include "core/dom/shadow/ShadowRoot.h"
|
| @@ -49,6 +51,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"
|
| @@ -78,11 +81,14 @@ public:
|
| static FocusNavigationScope ownedByNonFocusableFocusScopeOwner(Element&);
|
| static FocusNavigationScope ownedByShadowHost(const Element&);
|
| static FocusNavigationScope ownedByShadowInsertionPoint(HTMLShadowElement&);
|
| + static FocusNavigationScope ownedByHTMLSlotElement(HTMLSlotElement&);
|
| static FocusNavigationScope ownedByIFrame(const HTMLFrameOwnerElement&);
|
|
|
| private:
|
| explicit FocusNavigationScope(TreeScope*);
|
| + explicit FocusNavigationScope(HTMLSlotElement*);
|
| RawPtrWillBeMember<TreeScope> m_rootTreeScope;
|
| + RawPtrWillBeMember<HTMLSlotElement> m_rootSlot;
|
| };
|
|
|
| FocusNavigationScope::FocusNavigationScope(TreeScope* treeScope)
|
| @@ -91,13 +97,23 @@ FocusNavigationScope::FocusNavigationScope(TreeScope* treeScope)
|
| ASSERT(treeScope);
|
| }
|
|
|
| +FocusNavigationScope::FocusNavigationScope(HTMLSlotElement* slot)
|
| + : m_rootSlot(slot)
|
| +{
|
| + ASSERT(slot);
|
| +}
|
| +
|
| Node* FocusNavigationScope::rootNode() const
|
| {
|
| + if (m_rootSlot)
|
| + return m_rootSlot;
|
| return &m_rootTreeScope->rootNode();
|
| }
|
|
|
| Element* FocusNavigationScope::owner() const
|
| {
|
| + if (m_rootSlot)
|
| + return m_rootSlot;
|
| Node* root = rootNode();
|
| if (root->isShadowRoot()) {
|
| ShadowRoot* shadowRoot = toShadowRoot(root);
|
| @@ -111,6 +127,8 @@ Element* FocusNavigationScope::owner() const
|
|
|
| FocusNavigationScope FocusNavigationScope::focusNavigationScopeOf(const Node& node)
|
| {
|
| + if (AssignedNodeTraversal::isInAssignedScope(node))
|
| + return FocusNavigationScope(AssignedNodeTraversal::slot(node));
|
| return FocusNavigationScope(&node.treeScope());
|
| }
|
|
|
| @@ -118,8 +136,10 @@ FocusNavigationScope FocusNavigationScope::ownedByNonFocusableFocusScopeOwner(El
|
| {
|
| if (isShadowHost(element))
|
| return FocusNavigationScope::ownedByShadowHost(element);
|
| - ASSERT(isShadowInsertionPointFocusScopeOwner(element));
|
| - return FocusNavigationScope::ownedByShadowInsertionPoint(toHTMLShadowElement(element));
|
| + if (isShadowInsertionPointFocusScopeOwner(element))
|
| + return FocusNavigationScope::ownedByShadowInsertionPoint(toHTMLShadowElement(element));
|
| + ASSERT(isHTMLSlotElement(element));
|
| + return FocusNavigationScope::ownedByHTMLSlotElement(toHTMLSlotElement(element));
|
| }
|
|
|
| FocusNavigationScope FocusNavigationScope::ownedByShadowHost(const Element& element)
|
| @@ -141,6 +161,11 @@ FocusNavigationScope FocusNavigationScope::ownedByShadowInsertionPoint(HTMLShado
|
| return FocusNavigationScope(shadowInsertionPoint.olderShadowRoot());
|
| }
|
|
|
| +FocusNavigationScope FocusNavigationScope::ownedByHTMLSlotElement(HTMLSlotElement& element)
|
| +{
|
| + return FocusNavigationScope(&element);
|
| +}
|
| +
|
| inline void dispatchBlurEvent(const Document& document, Element& focusedElement)
|
| {
|
| focusedElement.dispatchBlurEvent(nullptr, WebFocusTypePage);
|
| @@ -224,7 +249,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)
|
| @@ -248,7 +273,10 @@ inline bool shouldVisit(Node& node)
|
| Element* findElementWithExactTabIndex(Node* start, int tabIndex, WebFocusType type)
|
| {
|
| // Search is inclusive of start
|
| - for (Node* node = start; node; node = type == WebFocusTypeForward ? ElementTraversal::next(*node) : ElementTraversal::previous(*node)) {
|
| + if (!start)
|
| + return nullptr;
|
| + CombinedNodeTraversal* traversal = new CombinedNodeTraversal(*start);
|
| + for (Node* node = start; node; node = type == WebFocusTypeForward ? traversal->next() : traversal->previous()) {
|
| if (shouldVisit(*node) && adjustedTabIndex(*node) == tabIndex)
|
| return toElement(node);
|
| }
|
| @@ -260,10 +288,14 @@ Element* nextElementWithGreaterTabIndex(Node* start, int tabIndex)
|
| // Search is inclusive of start
|
| int winningTabIndex = std::numeric_limits<short>::max() + 1;
|
| Node* winner = nullptr;
|
| - for (Node& node : NodeTraversal::startsAt(start)) {
|
| - int currentTabIndex = adjustedTabIndex(node);
|
| - if (shouldVisit(node) && currentTabIndex > tabIndex && currentTabIndex < winningTabIndex) {
|
| - winner = &node;
|
| + if (!start)
|
| + return nullptr;
|
| +
|
| + CombinedNodeTraversal* traversal = new CombinedNodeTraversal(*start);
|
| + for (Node* node = start; node; node = traversal->next()) {
|
| + int currentTabIndex = adjustedTabIndex(*node);
|
| + if (shouldVisit(*node) && currentTabIndex > tabIndex && currentTabIndex < winningTabIndex) {
|
| + winner = node;
|
| winningTabIndex = currentTabIndex;
|
| }
|
| }
|
| @@ -276,7 +308,8 @@ Element* previousElementWithLowerTabIndex(Node* start, int tabIndex)
|
| // Search is inclusive of start
|
| int winningTabIndex = 0;
|
| Node* winner = nullptr;
|
| - for (Node* node = start; node; node = ElementTraversal::previous(*node)) {
|
| + CombinedNodeTraversal* traversal = new CombinedNodeTraversal(*start);
|
| + for (Node* node = start; node; node = traversal->previous()) {
|
| int currentTabIndex = adjustedTabIndex(*node);
|
| if (shouldVisit(*node) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) {
|
| winner = node;
|
| @@ -293,13 +326,15 @@ Element* nextFocusableElement(const FocusNavigationScope& scope, Node* start)
|
| int tabIndex = adjustedTabIndex(*start);
|
| // If a node is excluded from the normal tabbing cycle, the next focusable node is determined by tree order
|
| if (tabIndex < 0) {
|
| - for (Node& node : NodeTraversal::startsAfter(*start)) {
|
| - if (shouldVisit(node) && adjustedTabIndex(node) >= 0)
|
| - return &toElement(node);
|
| + CombinedNodeTraversal* traversal = new CombinedNodeTraversal(*start);
|
| + for (Node* node = traversal->next(); node; node = traversal->next()) {
|
| + if (shouldVisit(*node) && adjustedTabIndex(*node) >= 0)
|
| + return &toElement(*node);
|
| }
|
| } else {
|
| // First try to find a node with the same tabindex as start that comes after start in the scope.
|
| - if (Element* winner = findElementWithExactTabIndex(ElementTraversal::next(*start), tabIndex, WebFocusTypeForward))
|
| + CombinedNodeTraversal* traversal = new CombinedNodeTraversal(*start);
|
| + if (Element* winner = findElementWithExactTabIndex(traversal->next(), tabIndex, WebFocusTypeForward))
|
| return winner;
|
| }
|
| if (!tabIndex) {
|
| @@ -311,9 +346,15 @@ Element* nextFocusableElement(const FocusNavigationScope& scope, Node* start)
|
| // 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 (Element* winner = nextElementWithGreaterTabIndex(scope.rootNode(), start ? adjustedTabIndex(*start) : 0))
|
| - return winner;
|
| + if (isHTMLSlotElement(scope.rootNode())) {
|
| + if (Element* winner = nextElementWithGreaterTabIndex(toHTMLSlotElement(scope.rootNode())->getAssignedNodes()[0].get(), start ? adjustedTabIndex(*start) : 0))
|
| + return winner;
|
| + return findElementWithExactTabIndex(toHTMLSlotElement(scope.rootNode())->getAssignedNodes()[0].get(), 0, WebFocusTypeForward);
|
| + }
|
|
|
| + if (Element* winner = nextElementWithGreaterTabIndex(scope.rootNode(), start ? adjustedTabIndex(*start) : 0)) {
|
| + 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 findElementWithExactTabIndex(scope.rootNode(), 0, WebFocusTypeForward);
|
| @@ -322,8 +363,12 @@ Element* nextFocusableElement(const FocusNavigationScope& scope, Node* start)
|
| Element* previousFocusableElement(const FocusNavigationScope& scope, Node* start)
|
| {
|
| Node* last = nullptr;
|
| - for (Node* node = scope.rootNode(); node; node = node->lastChild())
|
| - last = node;
|
| + if (isHTMLSlotElement(scope.rootNode()) && !toHTMLSlotElement(scope.rootNode())->getAssignedNodes().isEmpty()) {
|
| + last = toHTMLSlotElement(scope.rootNode())->getAssignedNodes().last().get();
|
| + } else {
|
| + for (Node* node = scope.rootNode(); node; node = node->lastChild())
|
| + last = node;
|
| + }
|
| ASSERT(last);
|
|
|
| // First try to find the last node in the scope that comes before start and has the same tabindex as start.
|
| @@ -331,7 +376,8 @@ Element* previousFocusableElement(const FocusNavigationScope& scope, Node* start
|
| Node* startingNode;
|
| int startingTabIndex;
|
| if (start) {
|
| - startingNode = ElementTraversal::previous(*start);
|
| + CombinedNodeTraversal* traversal = new CombinedNodeTraversal(*start);
|
| + startingNode = traversal->previous();
|
| startingTabIndex = adjustedTabIndex(*start);
|
| } else {
|
| startingNode = last;
|
| @@ -340,7 +386,8 @@ Element* previousFocusableElement(const FocusNavigationScope& scope, Node* start
|
|
|
| // 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* node = startingNode; node; node = ElementTraversal::previous(*node)) {
|
| + CombinedNodeTraversal* traversal = new CombinedNodeTraversal(*startingNode);
|
| + for (Node* node = startingNode; node; node = traversal->previous()) {
|
| if (shouldVisit(*node) && adjustedTabIndex(*node) >= 0)
|
| return toElement(node);
|
| }
|
| @@ -394,7 +441,7 @@ Element* findFocusableElementRecursivelyForward(const FocusNavigationScope& scop
|
| 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);
|
| @@ -410,6 +457,7 @@ Element* findFocusableElementRecursivelyBackward(const FocusNavigationScope& sco
|
| {
|
| // Starting node is exclusive.
|
| Element* found = findFocusableElementInternal(WebFocusTypeBackward, scope, start);
|
| +
|
| while (found) {
|
| // Now |found| is on a focusable shadow host.
|
| // Find inside shadow backwards. If any focusable element is found, return it, otherwise return
|
| @@ -433,7 +481,7 @@ Element* findFocusableElementRecursivelyBackward(const FocusNavigationScope& sco
|
| 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 node in descendant scope. If not found, find next focusable node within the
|
| // current scope.
|
| if (isNonFocusableFocusScopeOwner(*found)) {
|
|
|