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 case PseudoFullScreenMediaDocument:
169 return FULL_SCREEN_MEDIA_DOCUMENT;
170 #endif
171
172 case PseudoInputListButton:
173 #if ENABLE(DATALIST)
174 return INPUT_LIST_BUTTON;
175 #endif
176 case PseudoUnknown:
177 case PseudoEmpty:
178 case PseudoFirstChild:
179 case PseudoFirstOfType:
180 case PseudoLastChild:
181 case PseudoLastOfType:
182 case PseudoOnlyChild:
183 case PseudoOnlyOfType:
184 case PseudoNthChild:
185 case PseudoNthOfType:
186 case PseudoNthLastChild:
187 case PseudoNthLastOfType:
188 case PseudoLink:
189 case PseudoVisited:
190 case PseudoAny:
191 case PseudoAnyLink:
192 case PseudoAutofill:
193 case PseudoHover:
194 case PseudoDrag:
195 case PseudoFocus:
196 case PseudoActive:
197 case PseudoChecked:
198 case PseudoEnabled:
199 case PseudoFullPageMedia:
200 case PseudoDefault:
201 case PseudoDisabled:
202 case PseudoOptional:
203 case PseudoRequired:
204 case PseudoReadOnly:
205 case PseudoReadWrite:
206 case PseudoValid:
207 case PseudoInvalid:
208 case PseudoIndeterminate:
209 case PseudoTarget:
210 case PseudoLang:
211 case PseudoNot:
212 case PseudoRoot:
213 case PseudoScrollbarBack:
214 case PseudoScrollbarForward:
215 case PseudoWindowInactive:
216 case PseudoCornerPresent:
217 case PseudoDecrement:
218 case PseudoIncrement:
219 case PseudoHorizontal:
220 case PseudoVertical:
221 case PseudoStart:
222 case PseudoEnd:
223 case PseudoDoubleButton:
224 case PseudoSingleButton:
225 case PseudoNoButton:
226 case PseudoFirstPage:
227 case PseudoLeftPage:
228 case PseudoRightPage:
229 case PseudoInRange:
230 case PseudoOutOfRange:
231 return NOPSEUDO;
232 case PseudoNotParsed:
233 ASSERT_NOT_REACHED();
234 return NOPSEUDO;
235 }
236
237 ASSERT_NOT_REACHED();
238 return NOPSEUDO;
239 }
240
nameToPseudoTypeMap()241 static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoTypeMap()
242 {
243 DEFINE_STATIC_LOCAL(AtomicString, active, ("active"));
244 DEFINE_STATIC_LOCAL(AtomicString, after, ("after"));
245 DEFINE_STATIC_LOCAL(AtomicString, any, ("-webkit-any("));
246 DEFINE_STATIC_LOCAL(AtomicString, anyLink, ("-webkit-any-link"));
247 DEFINE_STATIC_LOCAL(AtomicString, autofill, ("-webkit-autofill"));
248 DEFINE_STATIC_LOCAL(AtomicString, before, ("before"));
249 DEFINE_STATIC_LOCAL(AtomicString, checked, ("checked"));
250 DEFINE_STATIC_LOCAL(AtomicString, fileUploadButton, ("-webkit-file-upload-button"));
251 #if ENABLE(INPUT_SPEECH)
252 DEFINE_STATIC_LOCAL(AtomicString, inputSpeechButton, ("-webkit-input-speech-button"));
253 #endif
254 DEFINE_STATIC_LOCAL(AtomicString, defaultString, ("default"));
255 DEFINE_STATIC_LOCAL(AtomicString, disabled, ("disabled"));
256 DEFINE_STATIC_LOCAL(AtomicString, readOnly, ("read-only"));
257 DEFINE_STATIC_LOCAL(AtomicString, readWrite, ("read-write"));
258 DEFINE_STATIC_LOCAL(AtomicString, valid, ("valid"));
259 DEFINE_STATIC_LOCAL(AtomicString, invalid, ("invalid"));
260 DEFINE_STATIC_LOCAL(AtomicString, drag, ("-webkit-drag"));
261 DEFINE_STATIC_LOCAL(AtomicString, dragAlias, ("-khtml-drag")); // was documented with this name in Apple documentation, so keep an alia
262 DEFINE_STATIC_LOCAL(AtomicString, empty, ("empty"));
263 DEFINE_STATIC_LOCAL(AtomicString, enabled, ("enabled"));
264 DEFINE_STATIC_LOCAL(AtomicString, firstChild, ("first-child"));
265 DEFINE_STATIC_LOCAL(AtomicString, firstLetter, ("first-letter"));
266 DEFINE_STATIC_LOCAL(AtomicString, firstLine, ("first-line"));
267 DEFINE_STATIC_LOCAL(AtomicString, firstOfType, ("first-of-type"));
268 DEFINE_STATIC_LOCAL(AtomicString, fullPageMedia, ("-webkit-full-page-media"));
269 DEFINE_STATIC_LOCAL(AtomicString, nthChild, ("nth-child("));
270 DEFINE_STATIC_LOCAL(AtomicString, nthOfType, ("nth-of-type("));
271 DEFINE_STATIC_LOCAL(AtomicString, nthLastChild, ("nth-last-child("));
272 DEFINE_STATIC_LOCAL(AtomicString, nthLastOfType, ("nth-last-of-type("));
273 DEFINE_STATIC_LOCAL(AtomicString, focus, ("focus"));
274 DEFINE_STATIC_LOCAL(AtomicString, hover, ("hover"));
275 DEFINE_STATIC_LOCAL(AtomicString, indeterminate, ("indeterminate"));
276 DEFINE_STATIC_LOCAL(AtomicString, innerSpinButton, ("-webkit-inner-spin-button"));
277 #if ENABLE(DATALIST)
278 DEFINE_STATIC_LOCAL(AtomicString, inputListButton, ("-webkit-input-list-button"));
279 #endif
280 DEFINE_STATIC_LOCAL(AtomicString, inputPlaceholder, ("-webkit-input-placeholder"));
281 DEFINE_STATIC_LOCAL(AtomicString, lastChild, ("last-child"));
282 DEFINE_STATIC_LOCAL(AtomicString, lastOfType, ("last-of-type"));
283 DEFINE_STATIC_LOCAL(AtomicString, link, ("link"));
284 DEFINE_STATIC_LOCAL(AtomicString, lang, ("lang("));
285 DEFINE_STATIC_LOCAL(AtomicString, notStr, ("not("));
286 DEFINE_STATIC_LOCAL(AtomicString, onlyChild, ("only-child"));
287 DEFINE_STATIC_LOCAL(AtomicString, onlyOfType, ("only-of-type"));
288 DEFINE_STATIC_LOCAL(AtomicString, optional, ("optional"));
289 DEFINE_STATIC_LOCAL(AtomicString, outerSpinButton, ("-webkit-outer-spin-button"));
290 DEFINE_STATIC_LOCAL(AtomicString, required, ("required"));
291 DEFINE_STATIC_LOCAL(AtomicString, resizer, ("-webkit-resizer"));
292 DEFINE_STATIC_LOCAL(AtomicString, root, ("root"));
293 DEFINE_STATIC_LOCAL(AtomicString, scrollbar, ("-webkit-scrollbar"));
294 DEFINE_STATIC_LOCAL(AtomicString, scrollbarButton, ("-webkit-scrollbar-button"));
295 DEFINE_STATIC_LOCAL(AtomicString, scrollbarCorner, ("-webkit-scrollbar-corner"));
296 DEFINE_STATIC_LOCAL(AtomicString, scrollbarThumb, ("-webkit-scrollbar-thumb"));
297 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrack, ("-webkit-scrollbar-track"));
298 DEFINE_STATIC_LOCAL(AtomicString, scrollbarTrackPiece, ("-webkit-scrollbar-track-piece"));
299 DEFINE_STATIC_LOCAL(AtomicString, searchCancelButton, ("-webkit-search-cancel-button"));
300 DEFINE_STATIC_LOCAL(AtomicString, searchDecoration, ("-webkit-search-decoration"));
301 DEFINE_STATIC_LOCAL(AtomicString, searchResultsDecoration, ("-webkit-search-results-decoration"));
302 DEFINE_STATIC_LOCAL(AtomicString, searchResultsButton, ("-webkit-search-results-button"));
303 DEFINE_STATIC_LOCAL(AtomicString, selection, ("selection"));
304 DEFINE_STATIC_LOCAL(AtomicString, target, ("target"));
305 DEFINE_STATIC_LOCAL(AtomicString, visited, ("visited"));
306 DEFINE_STATIC_LOCAL(AtomicString, windowInactive, ("window-inactive"));
307 DEFINE_STATIC_LOCAL(AtomicString, decrement, ("decrement"));
308 DEFINE_STATIC_LOCAL(AtomicString, increment, ("increment"));
309 DEFINE_STATIC_LOCAL(AtomicString, start, ("start"));
310 DEFINE_STATIC_LOCAL(AtomicString, end, ("end"));
311 DEFINE_STATIC_LOCAL(AtomicString, horizontal, ("horizontal"));
312 DEFINE_STATIC_LOCAL(AtomicString, vertical, ("vertical"));
313 DEFINE_STATIC_LOCAL(AtomicString, doubleButton, ("double-button"));
314 DEFINE_STATIC_LOCAL(AtomicString, singleButton, ("single-button"));
315 DEFINE_STATIC_LOCAL(AtomicString, noButton, ("no-button"));
316 DEFINE_STATIC_LOCAL(AtomicString, cornerPresent, ("corner-present"));
317 // Paged Media pseudo-classes
318 DEFINE_STATIC_LOCAL(AtomicString, firstPage, ("first"));
319 DEFINE_STATIC_LOCAL(AtomicString, leftPage, ("left"));
320 DEFINE_STATIC_LOCAL(AtomicString, rightPage, ("right"));
321 #if ENABLE(FULLSCREEN_API)
322 DEFINE_STATIC_LOCAL(AtomicString, fullScreen, ("-webkit-full-screen"));
323 DEFINE_STATIC_LOCAL(AtomicString, fullScreenDocument, ("-webkit-full-screen-document"));
324 DEFINE_STATIC_LOCAL(AtomicString, fullScreenMediaDocument, ("-webkit-full-screen-media-document"));
325 #endif
326 DEFINE_STATIC_LOCAL(AtomicString, inRange, ("in-range"));
327 DEFINE_STATIC_LOCAL(AtomicString, outOfRange, ("out-of-range"));
328
329 static HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = 0;
330 if (!nameToPseudoType) {
331 nameToPseudoType = new HashMap<AtomicStringImpl*, CSSSelector::PseudoType>;
332 nameToPseudoType->set(active.impl(), CSSSelector::PseudoActive);
333 nameToPseudoType->set(after.impl(), CSSSelector::PseudoAfter);
334 nameToPseudoType->set(anyLink.impl(), CSSSelector::PseudoAnyLink);
335 nameToPseudoType->set(any.impl(), CSSSelector::PseudoAny);
336 nameToPseudoType->set(autofill.impl(), CSSSelector::PseudoAutofill);
337 nameToPseudoType->set(before.impl(), CSSSelector::PseudoBefore);
338 nameToPseudoType->set(checked.impl(), CSSSelector::PseudoChecked);
339 nameToPseudoType->set(fileUploadButton.impl(), CSSSelector::PseudoFileUploadButton);
340 #if ENABLE(INPUT_SPEECH)
341 nameToPseudoType->set(inputSpeechButton.impl(), CSSSelector::PseudoInputSpeechButton);
342 #endif
343 nameToPseudoType->set(defaultString.impl(), CSSSelector::PseudoDefault);
344 nameToPseudoType->set(disabled.impl(), CSSSelector::PseudoDisabled);
345 nameToPseudoType->set(readOnly.impl(), CSSSelector::PseudoReadOnly);
346 nameToPseudoType->set(readWrite.impl(), CSSSelector::PseudoReadWrite);
347 nameToPseudoType->set(valid.impl(), CSSSelector::PseudoValid);
348 nameToPseudoType->set(invalid.impl(), CSSSelector::PseudoInvalid);
349 nameToPseudoType->set(drag.impl(), CSSSelector::PseudoDrag);
350 nameToPseudoType->set(dragAlias.impl(), CSSSelector::PseudoDrag);
351 nameToPseudoType->set(enabled.impl(), CSSSelector::PseudoEnabled);
352 nameToPseudoType->set(empty.impl(), CSSSelector::PseudoEmpty);
353 nameToPseudoType->set(firstChild.impl(), CSSSelector::PseudoFirstChild);
354 nameToPseudoType->set(fullPageMedia.impl(), CSSSelector::PseudoFullPageMedia);
355 #if ENABLE(DATALIST)
356 nameToPseudoType->set(inputListButton.impl(), CSSSelector::PseudoInputListButton);
357 #endif
358 nameToPseudoType->set(inputPlaceholder.impl(), CSSSelector::PseudoInputPlaceholder);
359 nameToPseudoType->set(lastChild.impl(), CSSSelector::PseudoLastChild);
360 nameToPseudoType->set(lastOfType.impl(), CSSSelector::PseudoLastOfType);
361 nameToPseudoType->set(onlyChild.impl(), CSSSelector::PseudoOnlyChild);
362 nameToPseudoType->set(onlyOfType.impl(), CSSSelector::PseudoOnlyOfType);
363 nameToPseudoType->set(firstLetter.impl(), CSSSelector::PseudoFirstLetter);
364 nameToPseudoType->set(firstLine.impl(), CSSSelector::PseudoFirstLine);
365 nameToPseudoType->set(firstOfType.impl(), CSSSelector::PseudoFirstOfType);
366 nameToPseudoType->set(focus.impl(), CSSSelector::PseudoFocus);
367 nameToPseudoType->set(hover.impl(), CSSSelector::PseudoHover);
368 nameToPseudoType->set(indeterminate.impl(), CSSSelector::PseudoIndeterminate);
369 nameToPseudoType->set(innerSpinButton.impl(), CSSSelector::PseudoInnerSpinButton);
370 nameToPseudoType->set(link.impl(), CSSSelector::PseudoLink);
371 nameToPseudoType->set(lang.impl(), CSSSelector::PseudoLang);
372 nameToPseudoType->set(notStr.impl(), CSSSelector::PseudoNot);
373 nameToPseudoType->set(nthChild.impl(), CSSSelector::PseudoNthChild);
374 nameToPseudoType->set(nthOfType.impl(), CSSSelector::PseudoNthOfType);
375 nameToPseudoType->set(nthLastChild.impl(), CSSSelector::PseudoNthLastChild);
376 nameToPseudoType->set(nthLastOfType.impl(), CSSSelector::PseudoNthLastOfType);
377 nameToPseudoType->set(outerSpinButton.impl(), CSSSelector::PseudoOuterSpinButton);
378 nameToPseudoType->set(root.impl(), CSSSelector::PseudoRoot);
379 nameToPseudoType->set(windowInactive.impl(), CSSSelector::PseudoWindowInactive);
380 nameToPseudoType->set(decrement.impl(), CSSSelector::PseudoDecrement);
381 nameToPseudoType->set(increment.impl(), CSSSelector::PseudoIncrement);
382 nameToPseudoType->set(start.impl(), CSSSelector::PseudoStart);
383 nameToPseudoType->set(end.impl(), CSSSelector::PseudoEnd);
384 nameToPseudoType->set(horizontal.impl(), CSSSelector::PseudoHorizontal);
385 nameToPseudoType->set(vertical.impl(), CSSSelector::PseudoVertical);
386 nameToPseudoType->set(doubleButton.impl(), CSSSelector::PseudoDoubleButton);
387 nameToPseudoType->set(singleButton.impl(), CSSSelector::PseudoSingleButton);
388 nameToPseudoType->set(noButton.impl(), CSSSelector::PseudoNoButton);
389 nameToPseudoType->set(optional.impl(), CSSSelector::PseudoOptional);
390 nameToPseudoType->set(required.impl(), CSSSelector::PseudoRequired);
391 nameToPseudoType->set(resizer.impl(), CSSSelector::PseudoResizer);
392 nameToPseudoType->set(scrollbar.impl(), CSSSelector::PseudoScrollbar);
393 nameToPseudoType->set(scrollbarButton.impl(), CSSSelector::PseudoScrollbarButton);
394 nameToPseudoType->set(scrollbarCorner.impl(), CSSSelector::PseudoScrollbarCorner);
395 nameToPseudoType->set(scrollbarThumb.impl(), CSSSelector::PseudoScrollbarThumb);
396 nameToPseudoType->set(scrollbarTrack.impl(), CSSSelector::PseudoScrollbarTrack);
397 nameToPseudoType->set(scrollbarTrackPiece.impl(), CSSSelector::PseudoScrollbarTrackPiece);
398 nameToPseudoType->set(cornerPresent.impl(), CSSSelector::PseudoCornerPresent);
399 nameToPseudoType->set(searchCancelButton.impl(), CSSSelector::PseudoSearchCancelButton);
400 nameToPseudoType->set(searchDecoration.impl(), CSSSelector::PseudoSearchDecoration);
401 nameToPseudoType->set(searchResultsDecoration.impl(), CSSSelector::PseudoSearchResultsDecoration);
402 nameToPseudoType->set(searchResultsButton.impl(), CSSSelector::PseudoSearchResultsButton);
403 nameToPseudoType->set(selection.impl(), CSSSelector::PseudoSelection);
404 nameToPseudoType->set(target.impl(), CSSSelector::PseudoTarget);
405 nameToPseudoType->set(visited.impl(), CSSSelector::PseudoVisited);
406 nameToPseudoType->set(firstPage.impl(), CSSSelector::PseudoFirstPage);
407 nameToPseudoType->set(leftPage.impl(), CSSSelector::PseudoLeftPage);
408 nameToPseudoType->set(rightPage.impl(), CSSSelector::PseudoRightPage);
409 #if ENABLE(FULLSCREEN_API)
410 nameToPseudoType->set(fullScreen.impl(), CSSSelector::PseudoFullScreen);
411 nameToPseudoType->set(fullScreenDocument.impl(), CSSSelector::PseudoFullScreenDocument);
412 nameToPseudoType->set(fullScreenMediaDocument.impl(), CSSSelector::PseudoFullScreenMediaDocument);
413 #endif
414 nameToPseudoType->set(inRange.impl(), CSSSelector::PseudoInRange);
415 nameToPseudoType->set(outOfRange.impl(), CSSSelector::PseudoOutOfRange);
416 }
417 return nameToPseudoType;
418 }
419
parsePseudoType(const AtomicString & name)420 CSSSelector::PseudoType CSSSelector::parsePseudoType(const AtomicString& name)
421 {
422 if (name.isNull())
423 return PseudoUnknown;
424 HashMap<AtomicStringImpl*, CSSSelector::PseudoType>* nameToPseudoType = nameToPseudoTypeMap();
425 HashMap<AtomicStringImpl*, CSSSelector::PseudoType>::iterator slot = nameToPseudoType->find(name.impl());
426 return slot == nameToPseudoType->end() ? PseudoUnknown : slot->second;
427 }
428
extractPseudoType() const429 void CSSSelector::extractPseudoType() const
430 {
431 if (m_match != PseudoClass && m_match != PseudoElement && m_match != PagePseudoClass)
432 return;
433
434 m_pseudoType = parsePseudoType(value());
435
436 bool element = false; // pseudo-element
437 bool compat = false; // single colon compatbility mode
438 bool isPagePseudoClass = false; // Page pseudo-class
439
440 switch (m_pseudoType) {
441 case PseudoAfter:
442 case PseudoBefore:
443 case PseudoFirstLetter:
444 case PseudoFirstLine:
445 compat = true;
446 case PseudoFileUploadButton:
447 case PseudoInputListButton:
448 case PseudoInputPlaceholder:
449 #if ENABLE(INPUT_SPEECH)
450 case PseudoInputSpeechButton:
451 #endif
452 case PseudoInnerSpinButton:
453 case PseudoOuterSpinButton:
454 case PseudoResizer:
455 case PseudoScrollbar:
456 case PseudoScrollbarCorner:
457 case PseudoScrollbarButton:
458 case PseudoScrollbarThumb:
459 case PseudoScrollbarTrack:
460 case PseudoScrollbarTrackPiece:
461 case PseudoSearchCancelButton:
462 case PseudoSearchDecoration:
463 case PseudoSearchResultsDecoration:
464 case PseudoSearchResultsButton:
465 case PseudoSelection:
466 element = true;
467 break;
468 case PseudoUnknown:
469 case PseudoEmpty:
470 case PseudoFirstChild:
471 case PseudoFirstOfType:
472 case PseudoLastChild:
473 case PseudoLastOfType:
474 case PseudoOnlyChild:
475 case PseudoOnlyOfType:
476 case PseudoNthChild:
477 case PseudoNthOfType:
478 case PseudoNthLastChild:
479 case PseudoNthLastOfType:
480 case PseudoLink:
481 case PseudoVisited:
482 case PseudoAny:
483 case PseudoAnyLink:
484 case PseudoAutofill:
485 case PseudoHover:
486 case PseudoDrag:
487 case PseudoFocus:
488 case PseudoActive:
489 case PseudoChecked:
490 case PseudoEnabled:
491 case PseudoFullPageMedia:
492 case PseudoDefault:
493 case PseudoDisabled:
494 case PseudoOptional:
495 case PseudoRequired:
496 case PseudoReadOnly:
497 case PseudoReadWrite:
498 case PseudoValid:
499 case PseudoInvalid:
500 case PseudoIndeterminate:
501 case PseudoTarget:
502 case PseudoLang:
503 case PseudoNot:
504 case PseudoRoot:
505 case PseudoScrollbarBack:
506 case PseudoScrollbarForward:
507 case PseudoWindowInactive:
508 case PseudoCornerPresent:
509 case PseudoDecrement:
510 case PseudoIncrement:
511 case PseudoHorizontal:
512 case PseudoVertical:
513 case PseudoStart:
514 case PseudoEnd:
515 case PseudoDoubleButton:
516 case PseudoSingleButton:
517 case PseudoNoButton:
518 case PseudoNotParsed:
519 #if ENABLE(FULLSCREEN_API)
520 case PseudoFullScreen:
521 case PseudoFullScreenDocument:
522 case PseudoFullScreenMediaDocument:
523 #endif
524 case PseudoInRange:
525 case PseudoOutOfRange:
526 break;
527 case PseudoFirstPage:
528 case PseudoLeftPage:
529 case PseudoRightPage:
530 isPagePseudoClass = true;
531 break;
532 }
533
534 bool matchPagePseudoClass = (m_match == PagePseudoClass);
535 if (matchPagePseudoClass != isPagePseudoClass)
536 m_pseudoType = PseudoUnknown;
537 else if (m_match == PseudoClass && element) {
538 if (!compat)
539 m_pseudoType = PseudoUnknown;
540 else
541 m_match = PseudoElement;
542 } else if (m_match == PseudoElement && !element)
543 m_pseudoType = PseudoUnknown;
544 }
545
operator ==(const CSSSelector & other)546 bool CSSSelector::operator==(const CSSSelector& other)
547 {
548 const CSSSelector* sel1 = this;
549 const CSSSelector* sel2 = &other;
550
551 while (sel1 && sel2) {
552 if (sel1->m_tag != sel2->m_tag || sel1->attribute() != sel2->attribute() ||
553 sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match ||
554 sel1->value() != sel2->value() ||
555 sel1->pseudoType() != sel2->pseudoType() ||
556 sel1->argument() != sel2->argument())
557 return false;
558 sel1 = sel1->tagHistory();
559 sel2 = sel2->tagHistory();
560 }
561
562 if (sel1 || sel2)
563 return false;
564
565 return true;
566 }
567
selectorText() const568 String CSSSelector::selectorText() const
569 {
570 String str = "";
571
572 const AtomicString& prefix = m_tag.prefix();
573 const AtomicString& localName = m_tag.localName();
574 if (m_match == CSSSelector::None || !prefix.isNull() || localName != starAtom) {
575 if (prefix.isNull())
576 str = localName;
577 else {
578 str = prefix.string();
579 str.append("|");
580 str.append(localName);
581 }
582 }
583
584 const CSSSelector* cs = this;
585 while (true) {
586 if (cs->m_match == CSSSelector::Id) {
587 str += "#";
588 serializeIdentifier(cs->value(), str);
589 } else if (cs->m_match == CSSSelector::Class) {
590 str += ".";
591 serializeIdentifier(cs->value(), str);
592 } else if (cs->m_match == CSSSelector::PseudoClass || cs->m_match == CSSSelector::PagePseudoClass) {
593 str += ":";
594 str += cs->value();
595
596 switch (cs->pseudoType()) {
597 case PseudoNot:
598 ASSERT(cs->selectorList());
599 str += cs->selectorList()->first()->selectorText();
600 str += ")";
601 break;
602 case PseudoLang:
603 case PseudoNthChild:
604 case PseudoNthLastChild:
605 case PseudoNthOfType:
606 case PseudoNthLastOfType:
607 str += cs->argument();
608 str += ")";
609 break;
610 case PseudoAny: {
611 CSSSelector* firstSubSelector = cs->selectorList()->first();
612 for (CSSSelector* subSelector = firstSubSelector; subSelector; subSelector = CSSSelectorList::next(subSelector)) {
613 if (subSelector != firstSubSelector)
614 str += ",";
615 str += subSelector->selectorText();
616 }
617 str += ")";
618 break;
619 }
620 default:
621 break;
622 }
623 } else if (cs->m_match == CSSSelector::PseudoElement) {
624 str += "::";
625 str += cs->value();
626 } else if (cs->hasAttribute()) {
627 str += "[";
628 const AtomicString& prefix = cs->attribute().prefix();
629 if (!prefix.isNull()) {
630 str.append(prefix);
631 str.append("|");
632 }
633 str += cs->attribute().localName();
634 switch (cs->m_match) {
635 case CSSSelector::Exact:
636 str += "=";
637 break;
638 case CSSSelector::Set:
639 // set has no operator or value, just the attrName
640 str += "]";
641 break;
642 case CSSSelector::List:
643 str += "~=";
644 break;
645 case CSSSelector::Hyphen:
646 str += "|=";
647 break;
648 case CSSSelector::Begin:
649 str += "^=";
650 break;
651 case CSSSelector::End:
652 str += "$=";
653 break;
654 case CSSSelector::Contain:
655 str += "*=";
656 break;
657 default:
658 break;
659 }
660 if (cs->m_match != CSSSelector::Set) {
661 serializeString(cs->value(), str);
662 str += "]";
663 }
664 }
665 if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory())
666 break;
667 cs = cs->tagHistory();
668 }
669
670 if (CSSSelector* tagHistory = cs->tagHistory()) {
671 String tagHistoryText = tagHistory->selectorText();
672 if (cs->relation() == CSSSelector::DirectAdjacent)
673 str = tagHistoryText + " + " + str;
674 else if (cs->relation() == CSSSelector::IndirectAdjacent)
675 str = tagHistoryText + " ~ " + str;
676 else if (cs->relation() == CSSSelector::Child)
677 str = tagHistoryText + " > " + str;
678 else
679 // Descendant
680 str = tagHistoryText + " " + str;
681 }
682
683 return str;
684 }
685
attribute() const686 const QualifiedName& CSSSelector::attribute() const
687 {
688 switch (m_match) {
689 case Id:
690 return idAttr;
691 case Class:
692 return classAttr;
693 default:
694 return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName();
695 }
696 }
697
setAttribute(const QualifiedName & value)698 void CSSSelector::setAttribute(const QualifiedName& value)
699 {
700 createRareData();
701 m_data.m_rareData->m_attribute = value;
702 }
703
setArgument(const AtomicString & value)704 void CSSSelector::setArgument(const AtomicString& value)
705 {
706 createRareData();
707 m_data.m_rareData->m_argument = value;
708 }
709
setSelectorList(PassOwnPtr<CSSSelectorList> selectorList)710 void CSSSelector::setSelectorList(PassOwnPtr<CSSSelectorList> selectorList)
711 {
712 createRareData();
713 m_data.m_rareData->m_selectorList = selectorList;
714 }
715
parseNth()716 bool CSSSelector::parseNth()
717 {
718 if (!m_hasRareData)
719 return false;
720 if (m_parsedNth)
721 return true;
722 m_parsedNth = m_data.m_rareData->parseNth();
723 return m_parsedNth;
724 }
725
matchNth(int count)726 bool CSSSelector::matchNth(int count)
727 {
728 ASSERT(m_hasRareData);
729 return m_data.m_rareData->matchNth(count);
730 }
731
isSimple() const732 bool CSSSelector::isSimple() const
733 {
734 if (selectorList() || tagHistory() || matchesPseudoElement())
735 return false;
736
737 int numConditions = 0;
738
739 // hasTag() cannot be be used here because namespace may not be nullAtom.
740 // Example:
741 // @namespace "http://www.w3.org/2000/svg";
742 // svg:not(:root) { ...
743 if (m_tag != starAtom)
744 numConditions++;
745
746 if (m_match == Id || m_match == Class || m_match == PseudoClass)
747 numConditions++;
748
749 if (m_hasRareData && m_data.m_rareData->m_attribute != anyQName())
750 numConditions++;
751
752 // numConditions is 0 for a universal selector.
753 // numConditions is 1 for other simple selectors.
754 return numConditions <= 1;
755 }
756
RareData(PassRefPtr<AtomicStringImpl> value)757 CSSSelector::RareData::RareData(PassRefPtr<AtomicStringImpl> value)
758 : m_value(value.leakRef())
759 , m_a(0)
760 , m_b(0)
761 , m_attribute(anyQName())
762 , m_argument(nullAtom)
763 {
764 }
765
~RareData()766 CSSSelector::RareData::~RareData()
767 {
768 if (m_value)
769 m_value->deref();
770 }
771
772 // a helper function for parsing nth-arguments
parseNth()773 bool CSSSelector::RareData::parseNth()
774 {
775 String argument = m_argument.lower();
776
777 if (argument.isEmpty())
778 return false;
779
780 m_a = 0;
781 m_b = 0;
782 if (argument == "odd") {
783 m_a = 2;
784 m_b = 1;
785 } else if (argument == "even") {
786 m_a = 2;
787 m_b = 0;
788 } else {
789 size_t n = argument.find('n');
790 if (n != notFound) {
791 if (argument[0] == '-') {
792 if (n == 1)
793 m_a = -1; // -n == -1n
794 else
795 m_a = argument.substring(0, n).toInt();
796 } else if (!n)
797 m_a = 1; // n == 1n
798 else
799 m_a = argument.substring(0, n).toInt();
800
801 size_t p = argument.find('+', n);
802 if (p != notFound)
803 m_b = argument.substring(p + 1, argument.length() - p - 1).toInt();
804 else {
805 p = argument.find('-', n);
806 if (p != notFound)
807 m_b = -argument.substring(p + 1, argument.length() - p - 1).toInt();
808 }
809 } else
810 m_b = argument.toInt();
811 }
812 return true;
813 }
814
815 // a helper function for checking nth-arguments
matchNth(int count)816 bool CSSSelector::RareData::matchNth(int count)
817 {
818 if (!m_a)
819 return count == m_b;
820 else if (m_a > 0) {
821 if (count < m_b)
822 return false;
823 return (count - m_b) % m_a == 0;
824 } else {
825 if (count > m_b)
826 return false;
827 return (m_b - count) % (-m_a) == 0;
828 }
829 }
830
831 } // namespace WebCore
832