1 /*
2 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
3 * 1999 Waldo Bastian (bastian@kde.org)
4 * 2001 Andreas Schlapbach (schlpbch@iam.unibe.ch)
5 * 2001-2003 Dirk Mueller (mueller@kde.org)
6 * Copyright (C) 2002, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
7 * Copyright (C) 2008 David Smith (catfish.man@gmail.com)
8 * Copyright (C) 2010 Google Inc. All rights reserved.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26 #include "config.h"
27 #include "CSSSelector.h"
28
29 #include "CSSOMUtils.h"
30 #include "CSSSelectorList.h"
31 #include "HTMLNames.h"
32 #include <wtf/Assertions.h>
33 #include <wtf/HashMap.h>
34 #include <wtf/StdLibExtras.h>
35 #include <wtf/Vector.h>
36
37 namespace WebCore {
38
39 using namespace HTMLNames;
40
createRareData()41 void CSSSelector::createRareData()
42 {
43 if (m_hasRareData)
44 return;
45 // Move the value to the rare data stucture.
46 m_data.m_rareData = new RareData(adoptRef(m_data.m_value));
47 m_hasRareData = true;
48 }
49
specificity() const50 unsigned CSSSelector::specificity() const
51 {
52 // make sure the result doesn't overflow
53 static const unsigned maxValueMask = 0xffffff;
54 unsigned total = 0;
55 for (const CSSSelector* selector = this; selector; selector = selector->tagHistory()) {
56 if (selector->m_isForPage)
57 return (total + selector->specificityForPage()) & maxValueMask;
58 total = (total + selector->specificityForOneSelector()) & maxValueMask;
59 }
60 return total;
61 }
62
specificityForOneSelector() const63 inline unsigned CSSSelector::specificityForOneSelector() const
64 {
65 // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function
66 // isn't quite correct.
67 unsigned s = (m_tag.localName() == starAtom ? 0 : 1);
68 switch (m_match) {
69 case Id:
70 s += 0x10000;
71 break;
72 case Exact:
73 case Class:
74 case Set:
75 case List:
76 case Hyphen:
77 case PseudoClass:
78 case PseudoElement:
79 case Contain:
80 case Begin:
81 case End:
82 // FIXME: PsuedoAny should base the specificity on the sub-selectors.
83 // See http://lists.w3.org/Archives/Public/www-style/2010Sep/0530.html
84 if (pseudoType() == PseudoNot) {
85 ASSERT(selectorList());
86 s += selectorList()->first()->specificityForOneSelector();
87 } else
88 s += 0x100;
89 case None:
90 break;
91 }
92 return s;
93 }
94
specificityForPage() const95 unsigned CSSSelector::specificityForPage() const
96 {
97 // See http://dev.w3.org/csswg/css3-page/#cascading-and-page-context
98 unsigned s = (m_tag.localName() == starAtom ? 0 : 4);
99
100 switch (pseudoType()) {
101 case PseudoFirstPage:
102 s += 2;
103 break;
104 case PseudoLeftPage:
105 case PseudoRightPage:
106 s += 1;
107 break;
108 case PseudoNotParsed:
109 break;
110 default:
111 ASSERT_NOT_REACHED();
112 }
113 return s;
114 }
115
pseudoId(PseudoType type)116 PseudoId CSSSelector::pseudoId(PseudoType type)
117 {
118 switch (type) {
119 case PseudoFirstLine:
120 return FIRST_LINE;
121 case PseudoFirstLetter:
122 return FIRST_LETTER;
123 case PseudoSelection:
124 return SELECTION;
125 case PseudoBefore:
126 return BEFORE;
127 case PseudoAfter:
128 return AFTER;
129 case PseudoFileUploadButton:
130 return FILE_UPLOAD_BUTTON;
131 case PseudoInputPlaceholder:
132 return INPUT_PLACEHOLDER;
133 #if ENABLE(INPUT_SPEECH)
134 case PseudoInputSpeechButton:
135 return INPUT_SPEECH_BUTTON;
136 #endif
137 case PseudoSearchCancelButton:
138 return SEARCH_CANCEL_BUTTON;
139 case PseudoSearchDecoration:
140 return SEARCH_DECORATION;
141 case PseudoSearchResultsDecoration:
142 return SEARCH_RESULTS_DECORATION;
143 case PseudoSearchResultsButton:
144 return SEARCH_RESULTS_BUTTON;
145 case PseudoScrollbar:
146 return SCROLLBAR;
147 case PseudoScrollbarButton:
148 return SCROLLBAR_BUTTON;
149 case PseudoScrollbarCorner:
150 return SCROLLBAR_CORNER;
151 case PseudoScrollbarThumb:
152 return SCROLLBAR_THUMB;
153 case PseudoScrollbarTrack:
154 return SCROLLBAR_TRACK;
155 case PseudoScrollbarTrackPiece:
156 return SCROLLBAR_TRACK_PIECE;
157 case PseudoResizer:
158 return RESIZER;
159 case PseudoInnerSpinButton:
160 return INNER_SPIN_BUTTON;
161 case PseudoOuterSpinButton:
162 return OUTER_SPIN_BUTTON;
163 #if ENABLE(FULLSCREEN_API)
164 case PseudoFullScreen:
165 return FULL_SCREEN;
166 case PseudoFullScreenDocument:
167 return FULL_SCREEN_DOCUMENT;
168 #endif
169
170 case PseudoInputListButton:
171 #if ENABLE(DATALIST)
172 return INPUT_LIST_BUTTON;
173 #endif
174 case PseudoUnknown:
175 case PseudoEmpty:
176 case PseudoFirstChild:
177 case PseudoFirstOfType:
178 case PseudoLastChild:
179 case PseudoLastOfType:
180 case PseudoOnlyChild:
181 case PseudoOnlyOfType:
182 case PseudoNthChild:
183 case PseudoNthOfType:
184 case PseudoNthLastChild:
185 case PseudoNthLastOfType:
186 case PseudoLink:
187 case PseudoVisited:
188 case PseudoAny:
189 case PseudoAnyLink:
190 case PseudoAutofill:
191 case PseudoHover:
192 case PseudoDrag:
193 case PseudoFocus:
194 case PseudoActive:
195 case PseudoChecked:
196 case PseudoEnabled:
197 case PseudoFullPageMedia:
198 case PseudoDefault:
199 case PseudoDisabled:
200 case PseudoOptional:
201 case PseudoRequired:
202 case PseudoReadOnly:
203 case PseudoReadWrite:
204 case PseudoValid:
205 case PseudoInvalid:
206 case PseudoIndeterminate:
207 case PseudoTarget:
208 case PseudoLang:
209 case PseudoNot:
210 case PseudoRoot:
211 case PseudoScrollbarBack:
212 case PseudoScrollbarForward:
213 case PseudoWindowInactive:
214 case PseudoCornerPresent:
215 case PseudoDecrement:
216 case PseudoIncrement:
217 case PseudoHorizontal:
218 case PseudoVertical:
219 case PseudoStart:
220 case PseudoEnd:
221 case PseudoDoubleButton:
222 case PseudoSingleButton:
223 case PseudoNoButton:
224 case PseudoFirstPage:
225 case PseudoLeftPage:
226 case PseudoRightPage:
227 case PseudoInRange:
228 case PseudoOutOfRange:
229 return NOPSEUDO;
230 case PseudoNotParsed:
231 ASSERT_NOT_REACHED();
232 return NOPSEUDO;
233 }
234
235 ASSERT_NOT_REACHED();
236 return NOPSEUDO;
237 }
238
nameToPseudoTypeMap()239 static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoTypeMap()
240 {
241 DEFINE_STATIC_LOCAL(AtomicString, active, ("active"));
242 DEFINE_STATIC_LOCAL(AtomicString, after, ("after"));
243 DEFINE_STATIC_LOCAL(AtomicString, any, ("-webkit-any("));
244 DEFINE_STATIC_LOCAL(AtomicString, anyLink, ("-webkit-any-link"));
245 DEFINE_STATIC_LOCAL(AtomicString, autofill, ("-webkit-autofill"));
246 DEFINE_STATIC_LOCAL(AtomicString, before, ("before"));
247 DEFINE_STATIC_LOCAL(AtomicString, checked, ("checked"));
248 DEFINE_STATIC_LOCAL(AtomicString, fileUploadButton, ("-webkit-file-upload-button"));
249 #if ENABLE(INPUT_SPEECH)
250 DEFINE_STATIC_LOCAL(AtomicString, inputSpeechButton, ("-webkit-input-speech-button"));
251 #endif
252 DEFINE_STATIC_LOCAL(AtomicString, defaultString, ("default"));
253 DEFINE_STATIC_LOCAL(AtomicString, disabled, ("disabled"));
254 DEFINE_STATIC_LOCAL(AtomicString, readOnly, ("read-only"));
255 DEFINE_STATIC_LOCAL(AtomicString, readWrite, ("read-write"));
256 DEFINE_STATIC_LOCAL(AtomicString, valid, ("valid"));
257 DEFINE_STATIC_LOCAL(AtomicString, invalid, ("invalid"));
258 DEFINE_STATIC_LOCAL(AtomicString, drag, ("-webkit-drag"));
259 DEFINE_STATIC_LOCAL(AtomicString, dragAlias, ("-khtml-drag")); // was documented with this name in Apple documentation, so keep an alia
260 DEFINE_STATIC_LOCAL(AtomicString, empty, ("empty"));
261 DEFINE_STATIC_LOCAL(AtomicString, enabled, ("enabled"));
262 DEFINE_STATIC_LOCAL(AtomicString, firstChild, ("first-child"));
263 DEFINE_STATIC_LOCAL(AtomicString, firstLetter, ("first-letter"));
264 DEFINE_STATIC_LOCAL(AtomicString, firstLine, ("first-line"));
265 DEFINE_STATIC_LOCAL(AtomicString, firstOfType, ("first-of-type"));
266 DEFINE_STATIC_LOCAL(AtomicString, fullPageMedia, ("-webkit-full-page-media"));
267 DEFINE_STATIC_LOCAL(AtomicString, nthChild, ("nth-child("));
268 DEFINE_STATIC_LOCAL(AtomicString, nthOfType, ("nth-of-type("));
269 DEFINE_STATIC_LOCAL(AtomicString, nthLastChild, ("nth-last-child("));
270 DEFINE_STATIC_LOCAL(AtomicString, nthLastOfType, ("nth-last-of-type("));
271 DEFINE_STATIC_LOCAL(AtomicString, focus, ("focus"));
272 DEFINE_STATIC_LOCAL(AtomicString, hover, ("hover"));
273 DEFINE_STATIC_LOCAL(AtomicString, indeterminate, ("indeterminate"));
274 DEFINE_STATIC_LOCAL(AtomicString, innerSpinButton, ("-webkit-inner-spin-button"));
275 #if ENABLE(DATALIST)
276 DEFINE_STATIC_LOCAL(AtomicString, inputListButton, ("-webkit-input-list-button"));
277 #endif
278 DEFINE_STATIC_LOCAL(AtomicString, inputPlaceholder, ("-webkit-input-placeholder"));
279 DEFINE_STATIC_LOCAL(AtomicString, lastChild, ("last-child"));
280 DEFINE_STATIC_LOCAL(AtomicString, lastOfType, ("last-of-type"));
281 DEFINE_STATIC_LOCAL(AtomicString, link, ("link"));
282 DEFINE_STATIC_LOCAL(AtomicString, lang, ("lang("));
283 DEFINE_STATIC_LOCAL(AtomicString, notStr, ("not("));
284 DEFINE_STATIC_LOCAL(AtomicString, onlyChild, ("only-child"));
285 DEFINE_STATIC_LOCAL(AtomicString, onlyOfType, ("only-of-type"));
286 DEFINE_STATIC_LOCAL(AtomicString, optional, ("optional"));
287 DEFINE_STATIC_LOCAL(AtomicString, outerSpinButton, ("-webkit-outer-spin-button"));
288 DEFINE_STATIC_LOCAL(AtomicString, required, ("required"));
289 DEFINE_STATIC_LOCAL(AtomicString, resizer, ("-webkit-resizer"));
290 DEFINE_STATIC_LOCAL(AtomicString, root, ("root"));
291 DEFINE_STATIC_LOCAL(AtomicString, scrollbar, ("-webkit-scrollbar"));
292 DEFINE_STATIC_LOCAL(AtomicString, scrollbarButton, ("-webkit-scrollbar-button"));
293 DEFINE_STATIC_LOCAL(AtomicString, scrollbarCorner, ("-webkit-scrollbar-corner"));
294 DEFINE_STATIC_LOCAL(AtomicString, scrollbarThumb, ("-webkit-scrollbar-thumb"));
295 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrack, ("-webkit-scrollbar-track"));
296 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrackPiece, ("-webkit-scrollbar-track-piece"));
297 DEFINE_STATIC_LOCAL(AtomicString, searchCancelButton, ("-webkit-search-cancel-button"));
298 DEFINE_STATIC_LOCAL(AtomicString, searchDecoration, ("-webkit-search-decoration"));
299 DEFINE_STATIC_LOCAL(AtomicString, searchResultsDecoration, ("-webkit-search-results-decoration"));
300 DEFINE_STATIC_LOCAL(AtomicString, searchResultsButton, ("-webkit-search-results-button"));
301 DEFINE_STATIC_LOCAL(AtomicString, selection, ("selection"));
302 DEFINE_STATIC_LOCAL(AtomicString, target, ("target"));
303 DEFINE_STATIC_LOCAL(AtomicString, visited, ("visited"));
304 DEFINE_STATIC_LOCAL(AtomicString, windowInactive, ("window-inactive"));
305 DEFINE_STATIC_LOCAL(AtomicString, decrement, ("decrement"));
306 DEFINE_STATIC_LOCAL(AtomicString, increment, ("increment"));
307 DEFINE_STATIC_LOCAL(AtomicString, start, ("start"));
308 DEFINE_STATIC_LOCAL(AtomicString, end, ("end"));
309 DEFINE_STATIC_LOCAL(AtomicString, horizontal, ("horizontal"));
310 DEFINE_STATIC_LOCAL(AtomicString, vertical, ("vertical"));
311 DEFINE_STATIC_LOCAL(AtomicString, doubleButton, ("double-button"));
312 DEFINE_STATIC_LOCAL(AtomicString, singleButton, ("single-button"));
313 DEFINE_STATIC_LOCAL(AtomicString, noButton, ("no-button"));
314 DEFINE_STATIC_LOCAL(AtomicString, cornerPresent, ("corner-present"));
315 // Paged Media pseudo-classes
316 DEFINE_STATIC_LOCAL(AtomicString, firstPage, ("first"));
317 DEFINE_STATIC_LOCAL(AtomicString, leftPage, ("left"));
318 DEFINE_STATIC_LOCAL(AtomicString, rightPage, ("right"));
319 #if ENABLE(FULLSCREEN_API)
320 DEFINE_STATIC_LOCAL(AtomicString, fullScreen, ("-webkit-full-screen"));
321 DEFINE_STATIC_LOCAL(AtomicString, fullScreenDocument, ("-webkit-full-screen-document"));
322 #endif
323 DEFINE_STATIC_LOCAL(AtomicString, inRange, ("in-range"));
324 DEFINE_STATIC_LOCAL(AtomicString, outOfRange, ("out-of-range"));
325
326 static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = 0;
327 if (!nameToPseudoType) {
328 nameToPseudoType = new HashMap<AtomicStringImpl*, CSSSelector::PseudoType>;
329 nameToPseudoType->set(active.impl(), CSSSelector::PseudoActive);
330 nameToPseudoType->set(after.impl(), CSSSelector::PseudoAfter);
331 nameToPseudoType->set(anyLink.impl(), CSSSelector::PseudoAnyLink);
332 nameToPseudoType->set(any.impl(), CSSSelector::PseudoAny);
333 nameToPseudoType->set(autofill.impl(), CSSSelector::PseudoAutofill);
334 nameToPseudoType->set(before.impl(), CSSSelector::PseudoBefore);
335 nameToPseudoType->set(checked.impl(), CSSSelector::PseudoChecked);
336 nameToPseudoType->set(fileUploadButton.impl(), CSSSelector::PseudoFileUploadButton);
337 #if ENABLE(INPUT_SPEECH)
338 nameToPseudoType->set(inputSpeechButton.impl(), CSSSelector::PseudoInputSpeechButton);
339 #endif
340 nameToPseudoType->set(defaultString.impl(), CSSSelector::PseudoDefault);
341 nameToPseudoType->set(disabled.impl(), CSSSelector::PseudoDisabled);
342 nameToPseudoType->set(readOnly.impl(), CSSSelector::PseudoReadOnly);
343 nameToPseudoType->set(readWrite.impl(), CSSSelector::PseudoReadWrite);
344 nameToPseudoType->set(valid.impl(), CSSSelector::PseudoValid);
345 nameToPseudoType->set(invalid.impl(), CSSSelector::PseudoInvalid);
346 nameToPseudoType->set(drag.impl(), CSSSelector::PseudoDrag);
347 nameToPseudoType->set(dragAlias.impl(), CSSSelector::PseudoDrag);
348 nameToPseudoType->set(enabled.impl(), CSSSelector::PseudoEnabled);
349 nameToPseudoType->set(empty.impl(), CSSSelector::PseudoEmpty);
350 nameToPseudoType->set(firstChild.impl(), CSSSelector::PseudoFirstChild);
351 nameToPseudoType->set(fullPageMedia.impl(), CSSSelector::PseudoFullPageMedia);
352 #if ENABLE(DATALIST)
353 nameToPseudoType->set(inputListButton.impl(), CSSSelector::PseudoInputListButton);
354 #endif
355 nameToPseudoType->set(inputPlaceholder.impl(), CSSSelector::PseudoInputPlaceholder);
356 nameToPseudoType->set(lastChild.impl(), CSSSelector::PseudoLastChild);
357 nameToPseudoType->set(lastOfType.impl(), CSSSelector::PseudoLastOfType);
358 nameToPseudoType->set(onlyChild.impl(), CSSSelector::PseudoOnlyChild);
359 nameToPseudoType->set(onlyOfType.impl(), CSSSelector::PseudoOnlyOfType);
360 nameToPseudoType->set(firstLetter.impl(), CSSSelector::PseudoFirstLetter);
361 nameToPseudoType->set(firstLine.impl(), CSSSelector::PseudoFirstLine);
362 nameToPseudoType->set(firstOfType.impl(), CSSSelector::PseudoFirstOfType);
363 nameToPseudoType->set(focus.impl(), CSSSelector::PseudoFocus);
364 nameToPseudoType->set(hover.impl(), CSSSelector::PseudoHover);
365 nameToPseudoType->set(indeterminate.impl(), CSSSelector::PseudoIndeterminate);
366 nameToPseudoType->set(innerSpinButton.impl(), CSSSelector::PseudoInnerSpinButton);
367 nameToPseudoType->set(link.impl(), CSSSelector::PseudoLink);
368 nameToPseudoType->set(lang.impl(), CSSSelector::PseudoLang);
369 nameToPseudoType->set(notStr.impl(), CSSSelector::PseudoNot);
370 nameToPseudoType->set(nthChild.impl(), CSSSelector::PseudoNthChild);
371 nameToPseudoType->set(nthOfType.impl(), CSSSelector::PseudoNthOfType);
372 nameToPseudoType->set(nthLastChild.impl(), CSSSelector::PseudoNthLastChild);
373 nameToPseudoType->set(nthLastOfType.impl(), CSSSelector::PseudoNthLastOfType);
374 nameToPseudoType->set(outerSpinButton.impl(), CSSSelector::PseudoOuterSpinButton);
375 nameToPseudoType->set(root.impl(), CSSSelector::PseudoRoot);
376 nameToPseudoType->set(windowInactive.impl(), CSSSelector::PseudoWindowInactive);
377 nameToPseudoType->set(decrement.impl(), CSSSelector::PseudoDecrement);
378 nameToPseudoType->set(increment.impl(), CSSSelector::PseudoIncrement);
379 nameToPseudoType->set(start.impl(), CSSSelector::PseudoStart);
380 nameToPseudoType->set(end.impl(), CSSSelector::PseudoEnd);
381 nameToPseudoType->set(horizontal.impl(), CSSSelector::PseudoHorizontal);
382 nameToPseudoType->set(vertical.impl(), CSSSelector::PseudoVertical);
383 nameToPseudoType->set(doubleButton.impl(), CSSSelector::PseudoDoubleButton);
384 nameToPseudoType->set(singleButton.impl(), CSSSelector::PseudoSingleButton);
385 nameToPseudoType->set(noButton.impl(), CSSSelector::PseudoNoButton);
386 nameToPseudoType->set(optional.impl(), CSSSelector::PseudoOptional);
387 nameToPseudoType->set(required.impl(), CSSSelector::PseudoRequired);
388 nameToPseudoType->set(resizer.impl(), CSSSelector::PseudoResizer);
389 nameToPseudoType->set(scrollbar.impl(), CSSSelector::PseudoScrollbar);
390 nameToPseudoType->set(scrollbarButton.impl(), CSSSelector::PseudoScrollbarButton);
391 nameToPseudoType->set(scrollbarCorner.impl(), CSSSelector::PseudoScrollbarCorner);
392 nameToPseudoType->set(scrollbarThumb.impl(), CSSSelector::PseudoScrollbarThumb);
393 nameToPseudoType->set(scrollbarTrack.impl(), CSSSelector::PseudoScrollbarTrack);
394 nameToPseudoType->set(scrollbarTrackPiece.impl(), CSSSelector::PseudoScrollbarTrackPiece);
395 nameToPseudoType->set(cornerPresent.impl(), CSSSelector::PseudoCornerPresent);
396 nameToPseudoType->set(searchCancelButton.impl(), CSSSelector::PseudoSearchCancelButton);
397 nameToPseudoType->set(searchDecoration.impl(), CSSSelector::PseudoSearchDecoration);
398 nameToPseudoType->set(searchResultsDecoration.impl(), CSSSelector::PseudoSearchResultsDecoration);
399 nameToPseudoType->set(searchResultsButton.impl(), CSSSelector::PseudoSearchResultsButton);
400 nameToPseudoType->set(selection.impl(), CSSSelector::PseudoSelection);
401 nameToPseudoType->set(target.impl(), CSSSelector::PseudoTarget);
402 nameToPseudoType->set(visited.impl(), CSSSelector::PseudoVisited);
403 nameToPseudoType->set(firstPage.impl(), CSSSelector::PseudoFirstPage);
404 nameToPseudoType->set(leftPage.impl(), CSSSelector::PseudoLeftPage);
405 nameToPseudoType->set(rightPage.impl(), CSSSelector::PseudoRightPage);
406 #if ENABLE(FULLSCREEN_API)
407 nameToPseudoType->set(fullScreen.impl(), CSSSelector::PseudoFullScreen);
408 nameToPseudoType->set(fullScreenDocument.impl(), CSSSelector::PseudoFullScreenDocument);
409 #endif
410 nameToPseudoType->set(inRange.impl(), CSSSelector::PseudoInRange);
411 nameToPseudoType->set(outOfRange.impl(), CSSSelector::PseudoOutOfRange);
412 }
413 return nameToPseudoType;
414 }
415
parsePseudoType(const AtomicString & name)416 CSSSelector::PseudoType CSSSelector::parsePseudoType(const AtomicString& name)
417 {
418 if (name.isNull())
419 return PseudoUnknown;
420 HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = nameToPseudoTypeMap();
421 HashMap<AtomicStringImpl*, CSSSelector::PseudoType>::iterator slot = nameToPseudoType->find(name.impl());
422 return slot == nameToPseudoType->end() ? PseudoUnknown : slot->second;
423 }
424
extractPseudoType() const425 void CSSSelector::extractPseudoType() const
426 {
427 if (m_match != PseudoClass && m_match != PseudoElement && m_match != PagePseudoClass)
428 return;
429
430 m_pseudoType = parsePseudoType(value());
431
432 bool element = false; // pseudo-element
433 bool compat = false; // single colon compatbility mode
434 bool isPagePseudoClass = false; // Page pseudo-class
435
436 switch (m_pseudoType) {
437 case PseudoAfter:
438 case PseudoBefore:
439 case PseudoFirstLetter:
440 case PseudoFirstLine:
441 compat = true;
442 case PseudoFileUploadButton:
443 case PseudoInputListButton:
444 case PseudoInputPlaceholder:
445 #if ENABLE(INPUT_SPEECH)
446 case PseudoInputSpeechButton:
447 #endif
448 case PseudoInnerSpinButton:
449 case PseudoOuterSpinButton:
450 case PseudoResizer:
451 case PseudoScrollbar:
452 case PseudoScrollbarCorner:
453 case PseudoScrollbarButton:
454 case PseudoScrollbarThumb:
455 case PseudoScrollbarTrack:
456 case PseudoScrollbarTrackPiece:
457 case PseudoSearchCancelButton:
458 case PseudoSearchDecoration:
459 case PseudoSearchResultsDecoration:
460 case PseudoSearchResultsButton:
461 case PseudoSelection:
462 element = true;
463 break;
464 case PseudoUnknown:
465 case PseudoEmpty:
466 case PseudoFirstChild:
467 case PseudoFirstOfType:
468 case PseudoLastChild:
469 case PseudoLastOfType:
470 case PseudoOnlyChild:
471 case PseudoOnlyOfType:
472 case PseudoNthChild:
473 case PseudoNthOfType:
474 case PseudoNthLastChild:
475 case PseudoNthLastOfType:
476 case PseudoLink:
477 case PseudoVisited:
478 case PseudoAny:
479 case PseudoAnyLink:
480 case PseudoAutofill:
481 case PseudoHover:
482 case PseudoDrag:
483 case PseudoFocus:
484 case PseudoActive:
485 case PseudoChecked:
486 case PseudoEnabled:
487 case PseudoFullPageMedia:
488 case PseudoDefault:
489 case PseudoDisabled:
490 case PseudoOptional:
491 case PseudoRequired:
492 case PseudoReadOnly:
493 case PseudoReadWrite:
494 case PseudoValid:
495 case PseudoInvalid:
496 case PseudoIndeterminate:
497 case PseudoTarget:
498 case PseudoLang:
499 case PseudoNot:
500 case PseudoRoot:
501 case PseudoScrollbarBack:
502 case PseudoScrollbarForward:
503 case PseudoWindowInactive:
504 case PseudoCornerPresent:
505 case PseudoDecrement:
506 case PseudoIncrement:
507 case PseudoHorizontal:
508 case PseudoVertical:
509 case PseudoStart:
510 case PseudoEnd:
511 case PseudoDoubleButton:
512 case PseudoSingleButton:
513 case PseudoNoButton:
514 case PseudoNotParsed:
515 #if ENABLE(FULLSCREEN_API)
516 case PseudoFullScreen:
517 case PseudoFullScreenDocument:
518 #endif
519 case PseudoInRange:
520 case PseudoOutOfRange:
521 break;
522 case PseudoFirstPage:
523 case PseudoLeftPage:
524 case PseudoRightPage:
525 isPagePseudoClass = true;
526 break;
527 }
528
529 bool matchPagePseudoClass = (m_match == PagePseudoClass);
530 if (matchPagePseudoClass != isPagePseudoClass)
531 m_pseudoType = PseudoUnknown;
532 else if (m_match == PseudoClass && element) {
533 if (!compat)
534 m_pseudoType = PseudoUnknown;
535 else
536 m_match = PseudoElement;
537 } else if (m_match == PseudoElement && !element)
538 m_pseudoType = PseudoUnknown;
539 }
540
operator ==(const CSSSelector & other)541 bool CSSSelector::operator==(const CSSSelector& other)
542 {
543 const CSSSelector* sel1 = this;
544 const CSSSelector* sel2 = &other;
545
546 while (sel1 && sel2) {
547 if (sel1->m_tag != sel2->m_tag || sel1->attribute() != sel2->attribute() ||
548 sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match ||
549 sel1->value() != sel2->value() ||
550 sel1->pseudoType() != sel2->pseudoType() ||
551 sel1->argument() != sel2->argument())
552 return false;
553 sel1 = sel1->tagHistory();
554 sel2 = sel2->tagHistory();
555 }
556
557 if (sel1 || sel2)
558 return false;
559
560 return true;
561 }
562
selectorText() const563 String CSSSelector::selectorText() const
564 {
565 String str = "";
566
567 const AtomicString& prefix = m_tag.prefix();
568 const AtomicString& localName = m_tag.localName();
569 if (m_match == CSSSelector::None || !prefix.isNull() || localName != starAtom) {
570 if (prefix.isNull())
571 str = localName;
572 else {
573 str = prefix.string();
574 str.append("|");
575 str.append(localName);
576 }
577 }
578
579 const CSSSelector* cs = this;
580 while (true) {
581 if (cs->m_match == CSSSelector::Id) {
582 str += "#";
583 serializeIdentifier(cs->value(), str);
584 } else if (cs->m_match == CSSSelector::Class) {
585 str += ".";
586 serializeIdentifier(cs->value(), str);
587 } else if (cs->m_match == CSSSelector::PseudoClass || cs->m_match == CSSSelector::PagePseudoClass) {
588 str += ":";
589 str += cs->value();
590
591 switch (cs->pseudoType()) {
592 case PseudoNot:
593 ASSERT(cs->selectorList());
594 str += cs->selectorList()->first()->selectorText();
595 str += ")";
596 break;
597 case PseudoLang:
598 case PseudoNthChild:
599 case PseudoNthLastChild:
600 case PseudoNthOfType:
601 case PseudoNthLastOfType:
602 str += cs->argument();
603 str += ")";
604 break;
605 case PseudoAny: {
606 CSSSelector* firstSubSelector = cs->selectorList()->first();
607 for (CSSSelector* subSelector = firstSubSelector; subSelector; subSelector = CSSSelectorList::next(subSelector)) {
608 if (subSelector != firstSubSelector)
609 str += ",";
610 str += subSelector->selectorText();
611 }
612 str += ")";
613 break;
614 }
615 default:
616 break;
617 }
618 } else if (cs->m_match == CSSSelector::PseudoElement) {
619 str += "::";
620 str += cs->value();
621 } else if (cs->hasAttribute()) {
622 str += "[";
623 const AtomicString& prefix = cs->attribute().prefix();
624 if (!prefix.isNull()) {
625 str.append(prefix);
626 str.append("|");
627 }
628 str += cs->attribute().localName();
629 switch (cs->m_match) {
630 case CSSSelector::Exact:
631 str += "=";
632 break;
633 case CSSSelector::Set:
634 // set has no operator or value, just the attrName
635 str += "]";
636 break;
637 case CSSSelector::List:
638 str += "~=";
639 break;
640 case CSSSelector::Hyphen:
641 str += "|=";
642 break;
643 case CSSSelector::Begin:
644 str += "^=";
645 break;
646 case CSSSelector::End:
647 str += "$=";
648 break;
649 case CSSSelector::Contain:
650 str += "*=";
651 break;
652 default:
653 break;
654 }
655 if (cs->m_match != CSSSelector::Set) {
656 serializeString(cs->value(), str);
657 str += "]";
658 }
659 }
660 if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory())
661 break;
662 cs = cs->tagHistory();
663 }
664
665 if (CSSSelector* tagHistory = cs->tagHistory()) {
666 String tagHistoryText = tagHistory->selectorText();
667 if (cs->relation() == CSSSelector::DirectAdjacent)
668 str = tagHistoryText + " + " + str;
669 else if (cs->relation() == CSSSelector::IndirectAdjacent)
670 str = tagHistoryText + " ~ " + str;
671 else if (cs->relation() == CSSSelector::Child)
672 str = tagHistoryText + " > " + str;
673 else
674 // Descendant
675 str = tagHistoryText + " " + str;
676 }
677
678 return str;
679 }
680
attribute() const681 const QualifiedName& CSSSelector::attribute() const
682 {
683 switch (m_match) {
684 case Id:
685 return idAttr;
686 case Class:
687 return classAttr;
688 default:
689 return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName();
690 }
691 }
692
setAttribute(const QualifiedName & value)693 void CSSSelector::setAttribute(const QualifiedName& value)
694 {
695 createRareData();
696 m_data.m_rareData->m_attribute = value;
697 }
698
setArgument(const AtomicString & value)699 void CSSSelector::setArgument(const AtomicString& value)
700 {
701 createRareData();
702 m_data.m_rareData->m_argument = value;
703 }
704
setSelectorList(PassOwnPtr<CSSSelectorList> selectorList)705 void CSSSelector::setSelectorList(PassOwnPtr<CSSSelectorList> selectorList)
706 {
707 createRareData();
708 m_data.m_rareData->m_selectorList = selectorList;
709 }
710
parseNth()711 bool CSSSelector::parseNth()
712 {
713 if (!m_hasRareData)
714 return false;
715 if (m_parsedNth)
716 return true;
717 m_parsedNth = m_data.m_rareData->parseNth();
718 return m_parsedNth;
719 }
720
matchNth(int count)721 bool CSSSelector::matchNth(int count)
722 {
723 ASSERT(m_hasRareData);
724 return m_data.m_rareData->matchNth(count);
725 }
726
isSimple() const727 bool CSSSelector::isSimple() const
728 {
729 if (selectorList() || tagHistory() || matchesPseudoElement())
730 return false;
731
732 int numConditions = 0;
733
734 // hasTag() cannot be be used here because namespace may not be nullAtom.
735 // Example:
736 // @namespace "http://www.w3.org/2000/svg";
737 // svg:not(:root) { ...
738 if (m_tag != starAtom)
739 numConditions++;
740
741 if (m_match == Id || m_match == Class || m_match == PseudoClass)
742 numConditions++;
743
744 if (m_hasRareData && m_data.m_rareData->m_attribute != anyQName())
745 numConditions++;
746
747 // numConditions is 0 for a universal selector.
748 // numConditions is 1 for other simple selectors.
749 return numConditions <= 1;
750 }
751
RareData(PassRefPtr<AtomicStringImpl> value)752 CSSSelector::RareData::RareData(PassRefPtr<AtomicStringImpl> value)
753 : m_value(value.leakRef())
754 , m_a(0)
755 , m_b(0)
756 , m_attribute(anyQName())
757 , m_argument(nullAtom)
758 {
759 }
760
~RareData()761 CSSSelector::RareData::~RareData()
762 {
763 if (m_value)
764 m_value->deref();
765 }
766
767 // a helper function for parsing nth-arguments
parseNth()768 bool CSSSelector::RareData::parseNth()
769 {
770 String argument = m_argument.lower();
771
772 if (argument.isEmpty())
773 return false;
774
775 m_a = 0;
776 m_b = 0;
777 if (argument == "odd") {
778 m_a = 2;
779 m_b = 1;
780 } else if (argument == "even") {
781 m_a = 2;
782 m_b = 0;
783 } else {
784 size_t n = argument.find('n');
785 if (n != notFound) {
786 if (argument[0] == '-') {
787 if (n == 1)
788 m_a = -1; // -n == -1n
789 else
790 m_a = argument.substring(0, n).toInt();
791 } else if (!n)
792 m_a = 1; // n == 1n
793 else
794 m_a = argument.substring(0, n).toInt();
795
796 size_t p = argument.find('+', n);
797 if (p != notFound)
798 m_b = argument.substring(p + 1, argument.length() - p - 1).toInt();
799 else {
800 p = argument.find('-', n);
801 if (p != notFound)
802 m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt();
803 }
804 } else
805 m_b = argument.toInt();
806 }
807 return true;
808 }
809
810 // a helper function for checking nth-arguments
matchNth(int count)811 bool CSSSelector::RareData::matchNth(int count)
812 {
813 if (!m_a)
814 return count == m_b;
815 else if (m_a > 0) {
816 if (count < m_b)
817 return false;
818 return (count - m_b) % m_a == 0;
819 } else {
820 if (count > m_b)
821 return false;
822 return (m_b - count) % (-m_a) == 0;
823 }
824 }
825
826 } // namespace WebCore
827