OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2011 Apple Inc. All rights reserved. | 2 * Copyright (C) 2011 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
76 if (selectorMatches(m_selectors[i], targetElement, targetElement)) | 76 if (selectorMatches(m_selectors[i], targetElement, targetElement)) |
77 return true; | 77 return true; |
78 } | 78 } |
79 | 79 |
80 return false; | 80 return false; |
81 } | 81 } |
82 | 82 |
83 PassRefPtr<NodeList> SelectorDataList::queryAll(Node* rootNode) const | 83 PassRefPtr<NodeList> SelectorDataList::queryAll(Node* rootNode) const |
84 { | 84 { |
85 Vector<RefPtr<Node> > result; | 85 Vector<RefPtr<Node> > result; |
86 execute<false>(rootNode, result); | 86 executeQueryAll(rootNode, result); |
87 return StaticNodeList::adopt(result); | 87 return StaticNodeList::adopt(result); |
88 } | 88 } |
89 | 89 |
90 PassRefPtr<Element> SelectorDataList::queryFirst(Node* rootNode) const | 90 PassRefPtr<Element> SelectorDataList::queryFirst(Node* rootNode) const |
91 { | 91 { |
92 Vector<RefPtr<Node> > result; | 92 return executeQueryFirst(rootNode); |
93 execute<true>(rootNode, result); | |
94 if (result.isEmpty()) | |
95 return 0; | |
96 ASSERT(result.size() == 1); | |
97 ASSERT(result.first()->isElementNode()); | |
98 return toElement(result.first().get()); | |
99 } | 93 } |
100 | 94 |
101 static inline bool isTreeScopeRoot(Node* node) | 95 static inline bool isTreeScopeRoot(Node* node) |
102 { | 96 { |
103 ASSERT(node); | 97 ASSERT(node); |
104 return node->isDocumentNode() || node->isShadowRoot(); | 98 return node->isDocumentNode() || node->isShadowRoot(); |
105 } | 99 } |
106 | 100 |
101 template<bool firstMatchOnly> | |
102 void SelectorDataList::collectElementsByClassName(Node* rootNode, const SpaceSpl itString& classNames, Vector<Node*>& traversalRoots) const | |
103 { | |
104 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) { | |
105 if (element->hasClass() && element->classNames().containsAll(classNames) ) { | |
106 traversalRoots.append(element); | |
107 if (firstMatchOnly) | |
esprehn
2013/07/12 07:17:17
I'd rather you just created two methods.
Element*
tasak
2013/07/16 07:46:59
I see.
Done.
| |
108 return; | |
109 } | |
110 } | |
111 } | |
112 | |
113 inline bool SelectorDataList::canUseFastQuery(Node* rootNode) const | |
114 { | |
115 return m_selectors.size() == 1 && rootNode->inDocument() && !rootNode->docum ent()->inQuirksMode(); | |
esprehn
2013/07/12 07:17:17
Why does the root need to be inDocument?
tasak
2013/07/16 07:46:59
To use treeScope()->getElementById.
I guess, we h
| |
116 } | |
117 | |
118 // If returns true, traversalRoots has the elements that may match the selector query. | |
119 // | |
120 // If returns false, traversalRoots has the rootNode parameter or descendants of rootNode representing | |
121 // the subtree for which we can limit the querySelector traversal. | |
122 // | |
123 // The travseralRoots may be empty, regardless of the returned bool value, if th is method finds that the selectors won't | |
esprehn
2013/07/12 07:17:17
It seems like you're trying to do too much inside
tasak
2013/07/16 07:46:59
I see. I moved the codes for finding elements with
| |
124 // match any element. | |
125 bool SelectorDataList::findTraverseRoots(Node* rootNode, Vector<Node*>& traversa lRoots) const | |
126 { | |
127 // We need to return the matches in document order. To use id lookup while t here is possiblity of multiple matches | |
128 // we would need to sort the results. For now, just traverse the document in that case. | |
129 if (!canUseFastQuery(rootNode)) { | |
130 traversalRoots.append(rootNode); | |
131 return false; | |
132 } | |
133 | |
134 bool matchTraverseRoots = true; | |
135 bool startFromParent = false; | |
136 | |
137 for (const CSSSelector* selector = m_selectors[0].selector; selector; select or = selector->tagHistory()) { | |
esprehn
2013/07/12 07:17:17
Do you need an ASSERT at the start of this method?
tasak
2013/07/16 07:46:59
I see.
Done.
| |
138 if (selector->m_match == CSSSelector::Id && !rootNode->document()->conta insMultipleElementsWithId(selector->value())) { | |
139 Element* element = rootNode->treeScope()->getElementById(selector->v alue()); | |
140 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf (rootNode))) | |
141 rootNode = element; | |
142 else if (!element || matchTraverseRoots) | |
143 rootNode = 0; | |
144 if (matchTraverseRoots) { | |
145 if (rootNode) | |
146 traversalRoots.append(rootNode); | |
147 return true; | |
148 } | |
esprehn
2013/07/12 07:17:17
This loop is confusing, what is it doing?
tasak
2013/07/16 07:46:59
This loop is:
- looking at each selector from righ
| |
149 if (startFromParent && rootNode) | |
150 rootNode = rootNode->parentNode(); | |
151 if (rootNode) | |
152 traversalRoots.append(rootNode); | |
153 return false; | |
154 } | |
155 | |
156 // If we have both CSSSelector::Id and CSSSelector::Class at the same ti me, we should use Id | |
157 // to find traverse root. | |
158 if (!startFromParent && selector->m_match == CSSSelector::Class) { | |
159 SpaceSplitString classNames(selector->value(), rootNode->document()- >inQuirksMode()); | |
160 if (matchTraverseRoots) { | |
161 collectElementsByClassName<false>(rootNode, classNames, traversa lRoots); | |
162 } else { | |
163 for (Element* element = ElementTraversal::firstWithin(rootNode); element; ) { | |
164 if (element->hasClass() && element->classNames().containsAll (classNames)) { | |
165 traversalRoots.append(element); | |
166 element = ElementTraversal::nextSkippingChildren(element , rootNode); | |
esprehn
2013/07/12 07:17:17
Why doesn't this need to be recursive? If this ele
tasak
2013/07/16 07:46:59
Because this is for finding traversal roots (e.g.
| |
167 } else { | |
168 element = ElementTraversal::next(element, rootNode); | |
169 } | |
170 } | |
171 } | |
172 return matchTraverseRoots; | |
173 } | |
174 | |
175 if (selector->relation() == CSSSelector::SubSelector) | |
176 continue; | |
177 matchTraverseRoots = false; | |
178 if (selector->relation() == CSSSelector::DirectAdjacent || selector->rel ation() == CSSSelector::IndirectAdjacent) | |
179 startFromParent = true; | |
180 else | |
181 startFromParent = false; | |
182 } | |
183 if (rootNode) | |
184 traversalRoots.append(rootNode); | |
185 return false; | |
186 } | |
187 | |
188 void SelectorDataList::executeQueryAll(Node* rootNode, Vector<RefPtr<Node> >& ma tchedElements) const | |
189 { | |
190 Vector<Node*> traverseRoots; | |
191 bool matchTraverseRoots = findTraverseRoots(rootNode, traverseRoots); | |
192 | |
193 if (traverseRoots.isEmpty()) | |
194 return; | |
195 if (matchTraverseRoots) { | |
esprehn
2013/07/12 07:17:17
This is weird, even if matchTraverseRoots is false
tasak
2013/07/16 07:46:59
The matchTraverseRoots means whether elements foun
| |
196 ASSERT(m_selectors.size() == 1); | |
197 for (unsigned i = 0; i < traverseRoots.size(); ++i) { | |
198 Element* element = toElement(traverseRoots.at(i)); | |
199 if (selectorMatches(m_selectors[0], element, rootNode)) | |
200 matchedElements.append(element); | |
201 } | |
202 return; | |
203 } | |
204 | |
205 unsigned selectorCount = m_selectors.size(); | |
esprehn
2013/07/12 07:17:17
No need to cache this in a local
tasak
2013/07/16 07:46:59
Done.
| |
206 if (selectorCount == 1) { | |
207 const SelectorData& selector = m_selectors[0]; | |
208 for (unsigned i = 0; i < traverseRoots.size(); ++i) { | |
209 Node* traverseRoot = traverseRoots.at(i); | |
210 for (Element* element = ElementTraversal::firstWithin(traverseRoot); element; element = ElementTraversal::next(element, traverseRoot)) { | |
211 if (selectorMatches(selector, element, rootNode)) | |
212 matchedElements.append(element); | |
213 } | |
214 } | |
215 return; | |
216 } | |
217 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) { | |
218 for (unsigned i = 0; i < selectorCount; ++i) { | |
219 if (selectorMatches(m_selectors[i], element, rootNode)) { | |
220 matchedElements.append(element); | |
221 break; | |
222 } | |
223 } | |
224 } | |
225 } | |
226 | |
107 // If the first pair value is true, the returned Node is the single Element that may match the selector query. | 227 // If the first pair value is true, the returned Node is the single Element that may match the selector query. |
108 // | 228 // |
109 // If the first value is false, the returned Node is the rootNode parameter or a descendant of rootNode representing | 229 // If the first value is false, the returned Node is the rootNode parameter or a descendant of rootNode representing |
110 // the subtree for which we can limit the querySelector traversal. | 230 // the subtree for which we can limit the querySelector traversal. |
111 // | 231 // |
112 // The returned Node may be 0, regardless of the returned bool value, if this me thod finds that the selectors won't | 232 // The returned Node may be 0, regardless of the returned bool value, if this me thod finds that the selectors won't |
113 // match any element. | 233 // match any element. |
114 std::pair<bool, Node*> SelectorDataList::findTraverseRoot(Node* rootNode) const | 234 std::pair<bool, Node*> SelectorDataList::findTraverseRoot(Node* rootNode) const |
115 { | 235 { |
116 // We need to return the matches in document order. To use id lookup while t here is possiblity of multiple matches | 236 // We need to return the matches in document order. To use id lookup while t here is possiblity of multiple matches |
117 // we would need to sort the results. For now, just traverse the document in that case. | 237 // we would need to sort the results. For now, just traverse the document in that case. |
118 if (m_selectors.size() != 1) | |
119 return std::make_pair(false, rootNode); | |
120 if (!rootNode->inDocument()) | |
121 return std::make_pair(false, rootNode); | |
122 if (rootNode->document()->inQuirksMode()) | |
123 return std::make_pair(false, rootNode); | |
124 | |
125 bool matchSingleNode = true; | 238 bool matchSingleNode = true; |
126 bool startFromParent = false; | 239 bool startFromParent = false; |
127 for (const CSSSelector* selector = m_selectors[0].selector; selector; select or = selector->tagHistory()) { | 240 for (const CSSSelector* selector = m_selectors[0].selector; selector; select or = selector->tagHistory()) { |
128 if (selector->m_match == CSSSelector::Id && !rootNode->document()->conta insMultipleElementsWithId(selector->value())) { | 241 if (selector->m_match == CSSSelector::Id && !rootNode->document()->conta insMultipleElementsWithId(selector->value())) { |
129 Element* element = rootNode->treeScope()->getElementById(selector->v alue()); | 242 Element* element = rootNode->treeScope()->getElementById(selector->v alue()); |
130 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf (rootNode))) | 243 if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf (rootNode))) |
131 rootNode = element; | 244 rootNode = element; |
132 else if (!element || matchSingleNode) | 245 else if (!element || matchSingleNode) |
133 rootNode = 0; | 246 rootNode = 0; |
134 if (matchSingleNode) | 247 if (matchSingleNode) |
135 return std::make_pair(true, rootNode); | 248 return std::make_pair(true, rootNode); |
136 if (startFromParent && rootNode) | 249 if (startFromParent && rootNode) |
137 rootNode = rootNode->parentNode(); | 250 rootNode = rootNode->parentNode(); |
138 return std::make_pair(false, rootNode); | 251 return std::make_pair(false, rootNode); |
139 } | 252 } |
140 if (selector->relation() == CSSSelector::SubSelector) | 253 if (selector->relation() == CSSSelector::SubSelector) |
141 continue; | 254 continue; |
142 matchSingleNode = false; | 255 matchSingleNode = false; |
143 if (selector->relation() == CSSSelector::DirectAdjacent || selector->rel ation() == CSSSelector::IndirectAdjacent) | 256 if (selector->relation() == CSSSelector::DirectAdjacent || selector->rel ation() == CSSSelector::IndirectAdjacent) |
144 startFromParent = true; | 257 startFromParent = true; |
145 else | 258 else |
146 startFromParent = false; | 259 startFromParent = false; |
147 } | 260 } |
148 return std::make_pair(false, rootNode); | 261 return std::make_pair(false, rootNode); |
149 } | 262 } |
150 | 263 |
151 template <bool firstMatchOnly> | 264 Element* SelectorDataList::executeQueryFirst(Node* rootNode) const |
152 void SelectorDataList::execute(Node* rootNode, Vector<RefPtr<Node> >& matchedEle ments) const | |
153 { | 265 { |
154 std::pair<bool, Node*> traverseRoot = findTraverseRoot(rootNode); | 266 // fast path |
155 if (!traverseRoot.second) | 267 if (canUseFastQuery(rootNode)) { |
156 return; | 268 if (m_selectors[0].selector && !m_selectors[0].selector->tagHistory()) { |
157 Node* traverseRootNode = traverseRoot.second; | 269 const CSSSelector* selector = m_selectors[0].selector; |
158 if (traverseRoot.first) { | 270 if (selector->m_match == CSSSelector::Id && !rootNode->document()->c ontainsMultipleElementsWithId(selector->value())) { |
159 ASSERT(m_selectors.size() == 1); | 271 // just getElementById |
160 ASSERT(traverseRootNode->isElementNode()); | 272 Element* element = rootNode->treeScope()->getElementById(selecto r->value()); |
161 Element* element = toElement(traverseRootNode); | 273 return element && (isTreeScopeRoot(rootNode) || element->isDesce ndantOf(rootNode)) ? element : 0; |
esprehn
2013/07/12 07:17:17
Remove the isTreeScopeRoot checks, isDescendantOf
tasak
2013/07/16 07:46:59
We need this check, because this doesn't use findT
| |
162 if (selectorMatches(m_selectors[0], element, rootNode)) | 274 } |
163 matchedElements.append(element); | 275 if (selector->m_match == CSSSelector::Class) { |
164 return; | 276 Vector<Node*> nodes; |
277 SpaceSplitString classNames(selector->value(), rootNode->documen t()->inQuirksMode()); | |
278 collectElementsByClassName<true>(rootNode, classNames, nodes); | |
279 return nodes.size() > 0 ? toElement(nodes.at(0)) : 0; | |
esprehn
2013/07/12 07:17:17
nodes.isEmpty() ? 0 : nodes.first();
tasak
2013/07/16 07:46:59
I added findElementByClassName and removed this co
| |
280 } | |
281 } | |
282 | |
283 std::pair<bool, Node*> traverseRoot = findTraverseRoot(rootNode); | |
esprehn
2013/07/12 07:17:17
I think you want to return a Node* and have an out
tasak
2013/07/16 07:46:59
Done.
| |
284 if (!traverseRoot.second) | |
285 return 0; | |
286 Node* traverseRootNode = traverseRoot.second; | |
287 if (traverseRoot.first) { | |
288 ASSERT(m_selectors.size() == 1); | |
289 ASSERT(traverseRootNode->isElementNode()); | |
290 Element* element = toElement(traverseRootNode); | |
291 return selectorMatches(m_selectors[0], element, rootNode) ? element : 0; | |
292 } | |
293 rootNode = traverseRootNode; | |
165 } | 294 } |
166 | 295 |
167 unsigned selectorCount = m_selectors.size(); | 296 unsigned selectorCount = m_selectors.size(); |
168 if (selectorCount == 1) { | 297 if (selectorCount == 1) { |
169 const SelectorData& selector = m_selectors[0]; | 298 const SelectorData& selector = m_selectors[0]; |
170 for (Element* element = ElementTraversal::firstWithin(rootNode); element ; element = ElementTraversal::next(element, rootNode)) { | 299 for (Element* element = ElementTraversal::firstWithin(rootNode); element ; element = ElementTraversal::next(element, rootNode)) { |
171 if (selectorMatches(selector, element, rootNode)) { | 300 if (selectorMatches(selector, element, rootNode)) |
172 matchedElements.append(element); | 301 return element; |
173 if (firstMatchOnly) | |
174 return; | |
175 } | |
176 } | 302 } |
177 return; | 303 return 0; |
178 } | 304 } |
305 | |
179 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) { | 306 for (Element* element = ElementTraversal::firstWithin(rootNode); element; el ement = ElementTraversal::next(element, rootNode)) { |
180 for (unsigned i = 0; i < selectorCount; ++i) { | 307 for (unsigned i = 0; i < selectorCount; ++i) { |
181 if (selectorMatches(m_selectors[i], element, rootNode)) { | 308 if (selectorMatches(m_selectors[i], element, rootNode)) |
182 matchedElements.append(element); | 309 return element; |
183 if (firstMatchOnly) | |
184 return; | |
185 break; | |
186 } | |
187 } | 310 } |
188 } | 311 } |
312 return 0; | |
189 } | 313 } |
190 | 314 |
191 SelectorQuery::SelectorQuery(const CSSSelectorList& selectorList) | 315 SelectorQuery::SelectorQuery(const CSSSelectorList& selectorList) |
192 : m_selectorList(selectorList) | 316 : m_selectorList(selectorList) |
193 { | 317 { |
194 m_selectors.initialize(m_selectorList); | 318 m_selectors.initialize(m_selectorList); |
195 } | 319 } |
196 | 320 |
197 bool SelectorQuery::matches(Element* element) const | 321 bool SelectorQuery::matches(Element* element) const |
198 { | 322 { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
239 m_entries.add(selectors, selectorQuery.release()); | 363 m_entries.add(selectors, selectorQuery.release()); |
240 return rawSelectorQuery; | 364 return rawSelectorQuery; |
241 } | 365 } |
242 | 366 |
243 void SelectorQueryCache::invalidate() | 367 void SelectorQueryCache::invalidate() |
244 { | 368 { |
245 m_entries.clear(); | 369 m_entries.clear(); |
246 } | 370 } |
247 | 371 |
248 } | 372 } |
OLD | NEW |