2ffd440c27cb36fbfdf959352d8a77cf829ffa45
[WebKit-https.git] / WebCore / css / CSSSelector.cpp
1 /*
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
5  *               1999 Waldo Bastian (bastian@kde.org)
6  *               2001 Andreas Schlapbach (schlpbch@iam.unibe.ch)
7  *               2001-2003 Dirk Mueller (mueller@kde.org)
8  * Copyright (C) 2002, 2006 Apple Computer, Inc.
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., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 #include "config.h"
27 #include "CSSSelector.h"
28
29 namespace WebCore {
30
31 void CSSSelector::print()
32 {
33     if (m_tagHistory)
34         m_tagHistory->print();
35 }
36
37 unsigned int CSSSelector::specificity()
38 {
39     // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function
40     // isn't quite correct.
41     int s = (m_tag.localName() == starAtom ? 0 : 1);
42     switch (m_match) {
43         case Id:
44             s += 0x10000;
45             break;
46         case Exact:
47         case Class:
48         case Set:
49         case List:
50         case Hyphen:
51         case PseudoClass:
52         case PseudoElement:
53         case Contain:
54         case Begin:
55         case End:
56             s += 0x100;
57         case None:
58             break;
59     }
60
61     if (m_tagHistory)
62         s += m_tagHistory->specificity();
63
64     // make sure it doesn't overflow
65     return s & 0xffffff;
66 }
67
68 void CSSSelector::extractPseudoType() const
69 {
70     if (m_match != PseudoClass && m_match != PseudoElement)
71         return;
72
73     static AtomicString active("active");
74     static AtomicString after("after");
75     static AtomicString anyLink("-webkit-any-link");
76     static AtomicString autofill("-webkit-autofill");
77     static AtomicString before("before");
78     static AtomicString checked("checked");
79     static AtomicString fileUploadButton("-webkit-file-upload-button");
80     static AtomicString disabled("disabled");
81     static AtomicString drag("-webkit-drag");
82     static AtomicString empty("empty");
83     static AtomicString enabled("enabled");
84     static AtomicString firstChild("first-child");
85     static AtomicString firstLetter("first-letter");
86     static AtomicString firstLine("first-line");
87     static AtomicString firstOfType("first-of-type");
88     static AtomicString focus("focus");
89     static AtomicString hover("hover");
90     static AtomicString indeterminate("indeterminate");
91     static AtomicString link("link");
92     static AtomicString lang("lang(");
93     static AtomicString lastChild("last-child");
94     static AtomicString lastOfType("last-of-type");
95     static AtomicString notStr("not(");
96     static AtomicString onlyChild("only-child");
97     static AtomicString onlyOfType("only-of-type");
98     static AtomicString root("root");
99     static AtomicString searchCancelButton("-webkit-search-cancel-button");
100     static AtomicString searchDecoration("-webkit-search-decoration");
101     static AtomicString searchResultsDecoration("-webkit-search-results-decoration");
102     static AtomicString searchResultsButton("-webkit-search-results-button");
103     static AtomicString selection("selection");
104     static AtomicString sliderThumb("-webkit-slider-thumb");
105     static AtomicString target("target");
106     static AtomicString visited("visited");
107
108     bool element = false; // pseudo-element
109     bool compat = false; // single colon compatbility mode
110
111     m_pseudoType = PseudoUnknown;
112     if (m_value == active)
113         m_pseudoType = PseudoActive;
114     else if (m_value == after) {
115         m_pseudoType = PseudoAfter;
116         element = true;
117         compat = true;
118     } else if (m_value == anyLink)
119         m_pseudoType = PseudoAnyLink;
120     else if (m_value == autofill)
121         m_pseudoType = PseudoAutofill;
122     else if (m_value == before) {
123         m_pseudoType = PseudoBefore;
124         element = true;
125         compat = true;
126     } else if (m_value == checked)
127         m_pseudoType = PseudoChecked;
128     else if (m_value == fileUploadButton) {
129         m_pseudoType = PseudoFileUploadButton;
130         element = true;
131     } else if (m_value == disabled)
132         m_pseudoType = PseudoDisabled;
133     else if (m_value == drag)
134         m_pseudoType = PseudoDrag;
135     else if (m_value == enabled)
136         m_pseudoType = PseudoEnabled;
137     else if (m_value == empty)
138         m_pseudoType = PseudoEmpty;
139     else if (m_value == firstChild)
140         m_pseudoType = PseudoFirstChild;
141     else if (m_value == firstLetter) {
142         m_pseudoType = PseudoFirstLetter;
143         element = true;
144         compat = true;
145     } else if (m_value == firstLine) {
146         m_pseudoType = PseudoFirstLine;
147         element = true;
148         compat = true;
149     } else if (m_value == firstOfType)
150         m_pseudoType = PseudoFirstOfType;
151     else if (m_value == focus)
152         m_pseudoType = PseudoFocus;
153     else if (m_value == hover)
154         m_pseudoType = PseudoHover;
155     else if (m_value == indeterminate)
156         m_pseudoType = PseudoIndeterminate;
157     else if (m_value == link)
158         m_pseudoType = PseudoLink;
159     else if (m_value == lang)
160         m_pseudoType = PseudoLang;
161     else if (m_value == lastChild)
162         m_pseudoType = PseudoLastChild;
163     else if (m_value == lastOfType)
164         m_pseudoType = PseudoLastOfType;
165     else if (m_value == notStr)
166         m_pseudoType = PseudoNot;
167     else if (m_value == onlyChild)
168         m_pseudoType = PseudoOnlyChild;
169     else if (m_value == onlyOfType)
170         m_pseudoType = PseudoOnlyOfType;
171     else if (m_value == root)
172         m_pseudoType = PseudoRoot;
173     else if (m_value == searchCancelButton) {
174         m_pseudoType = PseudoSearchCancelButton;
175         element = true;
176     } else if (m_value == searchDecoration) {
177         m_pseudoType = PseudoSearchDecoration;
178         element = true;
179     } else if (m_value == searchResultsDecoration) {
180         m_pseudoType = PseudoSearchResultsDecoration;
181         element = true;
182     } else if (m_value == searchResultsButton) {
183         m_pseudoType = PseudoSearchResultsButton;
184         element = true;
185     }  else if (m_value == selection) {
186         m_pseudoType = PseudoSelection;
187         element = true;
188     } else if (m_value == sliderThumb) {
189         m_pseudoType = PseudoSliderThumb;
190         element = true;
191     } else if (m_value == target)
192         m_pseudoType = PseudoTarget;
193     else if (m_value == visited)
194         m_pseudoType = PseudoVisited;
195
196     if (m_match == PseudoClass && element) {
197         if (!compat) 
198             m_pseudoType = PseudoUnknown;
199         else 
200            m_match = PseudoElement;
201     } else if (m_match == PseudoElement && !element)
202         m_pseudoType = PseudoUnknown;
203 }
204
205 bool CSSSelector::operator==(const CSSSelector& other)
206 {
207     const CSSSelector* sel1 = this;
208     const CSSSelector* sel2 = &other;
209
210     while (sel1 && sel2) {
211         if (sel1->m_tag != sel2->m_tag || sel1->m_attr != sel2->m_attr ||
212              sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match ||
213              sel1->m_value != sel2->m_value ||
214              sel1->pseudoType() != sel2->pseudoType() ||
215              sel1->m_argument != sel2->m_argument)
216             return false;
217         sel1 = sel1->m_tagHistory;
218         sel2 = sel2->m_tagHistory;
219     }
220
221     if (sel1 || sel2)
222         return false;
223
224     return true;
225 }
226
227 String CSSSelector::selectorText() const
228 {
229     // FIXME: Support namespaces when dumping the selector text. -dwh
230     String str;
231     const CSSSelector* cs = this;
232     const AtomicString& localName = cs->m_tag.localName();
233
234     if (cs->m_match == CSSSelector::None || localName != starAtom)
235         str = localName;
236
237     while (true) {
238         if (cs->m_match == CSSSelector::Id) {
239             str += "#";
240             str += cs->m_value;
241         } else if (cs->m_match == CSSSelector::Class) {
242             str += ".";
243             str += cs->m_value;
244         } else if (cs->m_match == CSSSelector::PseudoClass) {
245             str += ":";
246             str += cs->m_value;
247         } else if (cs->m_match == CSSSelector::PseudoElement) {
248             str += "::";
249             str += cs->m_value;
250         } else if (cs->hasAttribute()) {
251             // FIXME: Add support for dumping namespaces.
252             String attrName = cs->m_attr.localName();
253             str += "[";
254             str += attrName;
255             switch (cs->m_match) {
256                 case CSSSelector::Exact:
257                     str += "=";
258                     break;
259                 case CSSSelector::Set:
260                     // set has no operator or value, just the attrName
261                     str += "]";
262                     break;
263                 case CSSSelector::List:
264                     str += "~=";
265                     break;
266                 case CSSSelector::Hyphen:
267                     str += "|=";
268                     break;
269                 case CSSSelector::Begin:
270                     str += "^=";
271                     break;
272                 case CSSSelector::End:
273                     str += "$=";
274                     break;
275                 case CSSSelector::Contain:
276                     str += "*=";
277                     break;
278                 default:
279                     break;
280             }
281             if (cs->m_match != CSSSelector::Set) {
282                 str += "\"";
283                 str += cs->m_value;
284                 str += "\"]";
285             }
286         }
287         if (cs->relation() != CSSSelector::SubSelector || !cs->m_tagHistory)
288             break;
289         cs = cs->m_tagHistory;
290     }
291
292     if (cs->m_tagHistory) {
293         String tagHistoryText = cs->m_tagHistory->selectorText();
294         if (cs->relation() == CSSSelector::DirectAdjacent)
295             str = tagHistoryText + " + " + str;
296         else if (cs->relation() == CSSSelector::IndirectAdjacent)
297             str = tagHistoryText + " ~ " + str;
298         else if (cs->relation() == CSSSelector::Child)
299             str = tagHistoryText + " > " + str;
300         else
301             // Descendant
302             str = tagHistoryText + " " + str;
303     }
304
305     return str;
306 }
307
308 } // namespace WebCore