2007-02-09 Nicholas Shanks <webkit@nickshanks.com>
[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 notStr("not(");
94     static AtomicString root("root");
95     static AtomicString searchCancelButton("-webkit-search-cancel-button");
96     static AtomicString searchDecoration("-webkit-search-decoration");
97     static AtomicString searchResultsDecoration("-webkit-search-results-decoration");
98     static AtomicString searchResultsButton("-webkit-search-results-button");
99     static AtomicString selection("selection");
100     static AtomicString sliderThumb("-webkit-slider-thumb");
101     static AtomicString target("target");
102     static AtomicString visited("visited");
103
104     bool element = false; // pseudo-element
105     bool compat = false; // single colon compatbility mode
106
107     m_pseudoType = PseudoUnknown;
108     if (m_value == active)
109         m_pseudoType = PseudoActive;
110     else if (m_value == after) {
111         m_pseudoType = PseudoAfter;
112         element = true;
113         compat = true;
114     } else if (m_value == anyLink)
115         m_pseudoType = PseudoAnyLink;
116     else if (m_value == autofill)
117         m_pseudoType = PseudoAutofill;
118     else if (m_value == before) {
119         m_pseudoType = PseudoBefore;
120         element = true;
121         compat = true;
122     } else if (m_value == checked)
123         m_pseudoType = PseudoChecked;
124     else if (m_value == fileUploadButton) {
125         m_pseudoType = PseudoFileUploadButton;
126         element = true;
127     } else if (m_value == disabled)
128         m_pseudoType = PseudoDisabled;
129     else if (m_value == drag)
130         m_pseudoType = PseudoDrag;
131     else if (m_value == enabled)
132         m_pseudoType = PseudoEnabled;
133     else if (m_value == empty)
134         m_pseudoType = PseudoEmpty;
135     else if (m_value == firstChild)
136         m_pseudoType = PseudoFirstChild;
137     else if (m_value == firstLetter) {
138         m_pseudoType = PseudoFirstLetter;
139         element = true;
140         compat = true;
141     } else if (m_value == firstLine) {
142         m_pseudoType = PseudoFirstLine;
143         element = true;
144         compat = true;
145     } else if (m_value == firstOfType)
146         m_pseudoType = PseudoFirstOfType;
147     else if (m_value == focus)
148         m_pseudoType = PseudoFocus;
149     else if (m_value == hover)
150         m_pseudoType = PseudoHover;
151     else if (m_value == indeterminate)
152         m_pseudoType = PseudoIndeterminate;
153     else if (m_value == link)
154         m_pseudoType = PseudoLink;
155     else if (m_value == lang)
156         m_pseudoType = PseudoLang;
157     else if (m_value == notStr)
158         m_pseudoType = PseudoNot;
159     else if (m_value == root)
160         m_pseudoType = PseudoRoot;
161     else if (m_value == searchCancelButton) {
162         m_pseudoType = PseudoSearchCancelButton;
163         element = true;
164     } else if (m_value == searchDecoration) {
165         m_pseudoType = PseudoSearchDecoration;
166         element = true;
167     } else if (m_value == searchResultsDecoration) {
168         m_pseudoType = PseudoSearchResultsDecoration;
169         element = true;
170     } else if (m_value == searchResultsButton) {
171         m_pseudoType = PseudoSearchResultsButton;
172         element = true;
173     }  else if (m_value == selection) {
174         m_pseudoType = PseudoSelection;
175         element = true;
176     } else if (m_value == sliderThumb) {
177         m_pseudoType = PseudoSliderThumb;
178         element = true;
179     } else if (m_value == target)
180         m_pseudoType = PseudoTarget;
181     else if (m_value == visited)
182         m_pseudoType = PseudoVisited;
183
184     if (m_match == PseudoClass && element) {
185         if (!compat) 
186             m_pseudoType = PseudoUnknown;
187         else 
188            m_match = PseudoElement;
189     } else if (m_match == PseudoElement && !element)
190         m_pseudoType = PseudoUnknown;
191 }
192
193 bool CSSSelector::operator==(const CSSSelector& other)
194 {
195     const CSSSelector* sel1 = this;
196     const CSSSelector* sel2 = &other;
197
198     while (sel1 && sel2) {
199         if (sel1->m_tag != sel2->m_tag || sel1->m_attr != sel2->m_attr ||
200              sel1->relation() != sel2->relation() || sel1->m_match != sel2->m_match ||
201              sel1->m_value != sel2->m_value ||
202              sel1->pseudoType() != sel2->pseudoType() ||
203              sel1->m_argument != sel2->m_argument)
204             return false;
205         sel1 = sel1->m_tagHistory;
206         sel2 = sel2->m_tagHistory;
207     }
208
209     if (sel1 || sel2)
210         return false;
211
212     return true;
213 }
214
215 String CSSSelector::selectorText() const
216 {
217     // FIXME: Support namespaces when dumping the selector text. -dwh
218     String str;
219     const CSSSelector* cs = this;
220     const AtomicString& localName = cs->m_tag.localName();
221
222     if (cs->m_match == CSSSelector::None || localName != starAtom)
223         str = localName;
224
225     while (true) {
226         if (cs->m_match == CSSSelector::Id) {
227             str += "#";
228             str += cs->m_value;
229         } else if (cs->m_match == CSSSelector::Class) {
230             str += ".";
231             str += cs->m_value;
232         } else if (cs->m_match == CSSSelector::PseudoClass) {
233             str += ":";
234             str += cs->m_value;
235         } else if (cs->m_match == CSSSelector::PseudoElement) {
236             str += "::";
237             str += cs->m_value;
238         } else if (cs->hasAttribute()) {
239             // FIXME: Add support for dumping namespaces.
240             String attrName = cs->m_attr.localName();
241             str += "[";
242             str += attrName;
243             switch (cs->m_match) {
244                 case CSSSelector::Exact:
245                     str += "=";
246                     break;
247                 case CSSSelector::Set:
248                     // set has no operator or value, just the attrName
249                     str += "]";
250                     break;
251                 case CSSSelector::List:
252                     str += "~=";
253                     break;
254                 case CSSSelector::Hyphen:
255                     str += "|=";
256                     break;
257                 case CSSSelector::Begin:
258                     str += "^=";
259                     break;
260                 case CSSSelector::End:
261                     str += "$=";
262                     break;
263                 case CSSSelector::Contain:
264                     str += "*=";
265                     break;
266                 default:
267                     break;
268             }
269             if (cs->m_match != CSSSelector::Set) {
270                 str += "\"";
271                 str += cs->m_value;
272                 str += "\"]";
273             }
274         }
275         if (cs->relation() != CSSSelector::SubSelector || !cs->m_tagHistory)
276             break;
277         cs = cs->m_tagHistory;
278     }
279
280     if (cs->m_tagHistory) {
281         String tagHistoryText = cs->m_tagHistory->selectorText();
282         if (cs->relation() == CSSSelector::DirectAdjacent)
283             str = tagHistoryText + " + " + str;
284         else if (cs->relation() == CSSSelector::IndirectAdjacent)
285             str = tagHistoryText + " ~ " + str;
286         else if (cs->relation() == CSSSelector::Child)
287             str = tagHistoryText + " > " + str;
288         else
289             // Descendant
290             str = tagHistoryText + " " + str;
291     }
292
293     return str;
294 }
295
296 } // namespace WebCore