Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: third_party/WebKit/Source/core/dom/shadow/SlotScopedTraversal.cpp

Issue 2432293002: Fix focus navigation for nested slot case (Closed)
Patch Set: Minor cleanups Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/dom/shadow/SlotScopedTraversal.h" 5 #include "core/dom/shadow/SlotScopedTraversal.h"
6 6
7 #include "core/dom/Element.h" 7 #include "core/dom/Element.h"
8 #include "core/dom/ElementTraversal.h" 8 #include "core/dom/ElementTraversal.h"
9 #include "core/html/HTMLSlotElement.h" 9 #include "core/html/HTMLSlotElement.h"
10 10
11 namespace blink { 11 namespace blink {
12 12
13 HTMLSlotElement* SlotScopedTraversal::findScopeOwnerSlot( 13 HTMLSlotElement* SlotScopedTraversal::findScopeOwnerSlot(
14 const Element& current) { 14 const Element& current) {
15 if (Element* nearestAncestorAssignedToSlot = 15 if (Element* slottedAncestor =
16 SlotScopedTraversal::nearestAncestorAssignedToSlot(current)) 16 SlotScopedTraversal::nearestInclusiveAncestorAssignedToSlot(current))
17 return nearestAncestorAssignedToSlot->assignedSlot(); 17 return slottedAncestor->assignedSlot();
18 return nullptr; 18 return nullptr;
19 } 19 }
20 20
21 Element* SlotScopedTraversal::nearestAncestorAssignedToSlot( 21 Element* SlotScopedTraversal::nearestInclusiveAncestorAssignedToSlot(
22 const Element& current) { 22 const Element& current) {
23 // nearestAncestorAssignedToSlot returns an ancestor element of current which
24 // is directly assigned to a slot.
25 Element* element = const_cast<Element*>(&current); 23 Element* element = const_cast<Element*>(&current);
26 for (; element; element = element->parentElement()) { 24 for (; element; element = element->parentElement()) {
27 if (element->assignedSlot()) 25 if (element->assignedSlot())
28 break; 26 break;
29 } 27 }
30 return element; 28 return element;
31 } 29 }
32 30
33 Element* SlotScopedTraversal::next(const Element& current) { 31 Element* SlotScopedTraversal::next(const Element& current) {
34 // current.assignedSlot returns a slot only when current is assigned 32 Element* next = nullptr;
35 // explicitly 33 Element* slottedAncestor =
hayato 2016/10/26 04:24:12 Could you avoid to use "Ancestor" here? It is conf
kochi 2016/10/26 10:20:03 Does |slottedInclusiveAncestor| sound okay then? I
36 // If current is assigned to a slot, return a descendant of current, which is 34 SlotScopedTraversal::nearestInclusiveAncestorAssignedToSlot(current);
37 // in the assigned scope of the same slot as current. 35 // Search within children of an element which is assigned to a slot.
38 HTMLSlotElement* slot = current.assignedSlot(); 36 if (current.assignedSlot()) {
hayato 2016/10/26 04:24:12 This "if .. else .." looks redundant. We can use
kochi 2016/10/26 10:20:03 Done. Really good catch!
39 Element* nearestAncestorAssignedToSlot = 37 next = ElementTraversal::next(current, &current);
40 SlotScopedTraversal::nearestAncestorAssignedToSlot(current);
41 if (slot) {
42 if (Element* next = ElementTraversal::next(current, &current))
43 return next;
44 } else { 38 } else {
45 // If current is in assigned scope, find an assigned ancestor. 39 // If current is in assigned scope, find an assigned ancestor.
46 DCHECK(nearestAncestorAssignedToSlot); 40 DCHECK(slottedAncestor);
hayato 2016/10/26 04:24:12 We should DCHECK immediately after L34.
kochi 2016/10/26 10:20:03 Done.
47 if (Element* next = 41 next = ElementTraversal::next(current, slottedAncestor);
48 ElementTraversal::next(current, nearestAncestorAssignedToSlot))
49 return next;
50 slot = nearestAncestorAssignedToSlot->assignedSlot();
51 DCHECK(slot);
52 } 42 }
43
44 // Exclude elements that is assigned to another slot scope.
45 while (next && next->assignedSlot())
46 next = ElementTraversal::nextSkippingChildren(*next, &current);
47
48 if (next) {
49 DCHECK(!next->assignedSlot());
hayato 2016/10/26 04:24:12 Nit: I would remove this DCHECK because it is just
kochi 2016/10/26 10:20:03 Done.
50 return next;
51 }
52
53 // Seek to the next element assigned to the same slot.
54 HTMLSlotElement* slot = slottedAncestor->assignedSlot();
55 DCHECK(slot);
53 HeapVector<Member<Node>> assignedNodes = slot->assignedNodes(); 56 HeapVector<Member<Node>> assignedNodes = slot->assignedNodes();
54 size_t currentIndex = assignedNodes.find(*nearestAncestorAssignedToSlot); 57 size_t currentIndex = assignedNodes.find(*slottedAncestor);
55 DCHECK_NE(currentIndex, kNotFound); 58 DCHECK_NE(currentIndex, kNotFound);
56 for (++currentIndex; currentIndex < assignedNodes.size(); ++currentIndex) { 59 for (++currentIndex; currentIndex < assignedNodes.size(); ++currentIndex) {
57 if (assignedNodes[currentIndex]->isElementNode()) 60 if (assignedNodes[currentIndex]->isElementNode())
58 return toElement(assignedNodes[currentIndex]); 61 return toElement(assignedNodes[currentIndex]);
59 } 62 }
60 return nullptr; 63 return nullptr;
61 } 64 }
62 65
66 namespace {
67
68 Element* findPreviousSkippingShadowHost(Node* start,
69 const Element& scope,
70 const Element* expected) {
71 DCHECK(!isShadowHost(start));
72 Node* current = start;
73 while (current != expected) {
74 current = NodeTraversal::previousPostOrder(*current, &scope);
75 DCHECK(current);
76 if (!current->isElementNode())
77 continue;
78 if (isShadowHost(current))
79 break;
80 }
81 return toElement(current);
82 }
83
84 Element* previousInternal(const Element& current, const Element& scope) {
85 DCHECK(scope.assignedSlot());
86 DCHECK_NE(&current, &scope);
87 Element* previous = ElementTraversal::previous(current, &scope);
88 if (isShadowHost(current))
89 return previous;
90 return findPreviousSkippingShadowHost(
91 const_cast<Node*>(static_cast<const Node*>(&current)), scope, previous);
92 }
93
94 Element* lastWithinOrSelfInternal(const Element& scope) {
95 DCHECK(scope.assignedSlot());
96 // Shortcut for Shadow Host. Do not traverse in its light DOM.
97 if (isShadowHost(scope))
98 return const_cast<Element*>(&scope);
99 Element* last = ElementTraversal::lastWithin(scope);
100 if (!last || last == scope)
101 return const_cast<Element*>(&scope);
102 return findPreviousSkippingShadowHost(scope.lastChild(), scope, last);
103 }
104
105 } // namespace
106
63 Element* SlotScopedTraversal::previous(const Element& current) { 107 Element* SlotScopedTraversal::previous(const Element& current) {
64 Element* nearestAncestorAssignedToSlot = 108 Element* slottedAncestor =
65 SlotScopedTraversal::nearestAncestorAssignedToSlot(current); 109 SlotScopedTraversal::nearestInclusiveAncestorAssignedToSlot(current);
66 DCHECK(nearestAncestorAssignedToSlot); 110 DCHECK(slottedAncestor);
67 // NodeTraversal within nearestAncestorAssignedToSlot 111
68 if (Element* previous = 112 if (current != slottedAncestor) {
69 ElementTraversal::previous(current, nearestAncestorAssignedToSlot)) 113 // Search within children of an element which is assigned to a slot.
114 Element* previous = previousInternal(current, *slottedAncestor);
115 DCHECK(previous);
70 return previous; 116 return previous;
71 // If null, jump to previous assigned node's descendant 117 }
118
119 // Seek to the previous element assigned to the same slot.
72 const HeapVector<Member<Node>> assignedNodes = 120 const HeapVector<Member<Node>> assignedNodes =
73 nearestAncestorAssignedToSlot->assignedSlot()->assignedNodes(); 121 slottedAncestor->assignedSlot()->assignedNodes();
74 size_t currentIndex = 122 size_t currentIndex = assignedNodes.reverseFind(*slottedAncestor);
75 assignedNodes.reverseFind(*nearestAncestorAssignedToSlot);
76 DCHECK_NE(currentIndex, kNotFound); 123 DCHECK_NE(currentIndex, kNotFound);
77 for (; currentIndex > 0; --currentIndex) { 124 for (; currentIndex > 0; --currentIndex) {
78 const Member<Node> assignedPrevious = assignedNodes[currentIndex - 1]; 125 const Member<Node> assignedNode = assignedNodes[currentIndex - 1];
79 if (assignedPrevious->isElementNode()) { 126 if (!assignedNode->isElementNode())
80 if (Element* last = 127 continue;
81 ElementTraversal::lastWithin(*toElement(assignedPrevious))) 128 return lastWithinOrSelfInternal(*toElement(assignedNode));
82 return last; 129 }
83 return toElement(assignedPrevious); 130 return nullptr;
84 } 131 }
132
133 Element* SlotScopedTraversal::firstAssignedToSlot(HTMLSlotElement& slot) {
134 HeapVector<Member<Node>> assignedNodes = slot.assignedNodes();
135 for (auto assignedNode : assignedNodes) {
136 if (assignedNode->isElementNode())
137 return toElement(assignedNode);
138 }
139 return nullptr;
140 }
141
142 Element* SlotScopedTraversal::lastAssignedToSlot(HTMLSlotElement& slot) {
143 HeapVector<Member<Node>> assignedNodes = slot.assignedNodes();
144 for (auto assignedNode = assignedNodes.rbegin();
145 assignedNode != assignedNodes.rend(); ++assignedNode) {
146 if (!(*assignedNode)->isElementNode())
147 continue;
148 return lastWithinOrSelfInternal(*toElement(*assignedNode));
85 } 149 }
86 return nullptr; 150 return nullptr;
87 } 151 }
88 152
89 bool SlotScopedTraversal::isSlotScoped(const Element& current) { 153 bool SlotScopedTraversal::isSlotScoped(const Element& current) {
90 return SlotScopedTraversal::nearestAncestorAssignedToSlot(current); 154 return SlotScopedTraversal::nearestInclusiveAncestorAssignedToSlot(current);
91 } 155 }
92 156
93 } // namespace blink 157 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698