LayoutTests:
[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 #include "config.h"
26 #include "CSSSelector.h"
27
28 namespace WebCore {
29
30 void CSSSelector::print()
31 {
32     if (tagHistory)
33         tagHistory->print();
34 }
35
36 unsigned int CSSSelector::specificity()
37 {
38     // FIXME: Pseudo-elements and pseudo-classes do not have the same specificity. This function
39     // isn't quite correct.
40     int s = (tag.localName() == starAtom ? 0 : 1);
41     switch(match)
42     {
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     if (tagHistory)
61         s += tagHistory->specificity();
62     // make sure it doesn't overflow
63     return s & 0xffffff;
64 }
65
66 void CSSSelector::extractPseudoType() const
67 {
68     if (match != PseudoClass && match != PseudoElement)
69         return;
70     
71     static AtomicString active("active");
72     static AtomicString after("after");
73     static AtomicString anyLink("-webkit-any-link");
74     static AtomicString autofill("-webkit-autofill");
75     static AtomicString before("before");
76     static AtomicString checked("checked");
77     static AtomicString fileUploadButton("-webkit-file-upload-button");
78     static AtomicString disabled("disabled");
79     static AtomicString drag("-webkit-drag");
80     static AtomicString empty("empty");
81     static AtomicString enabled("enabled");
82     static AtomicString firstChild("first-child");
83     static AtomicString firstLetter("first-letter");
84     static AtomicString firstLine("first-line");
85     static AtomicString firstOfType("first-of-type");
86     static AtomicString focus("focus");
87     static AtomicString hover("hover");
88     static AtomicString indeterminate("indeterminate");
89     static AtomicString link("link");
90     static AtomicString lang("lang(");
91     static AtomicString lastChild("last-child");
92     static AtomicString lastOfType("last-of-type");
93     static AtomicString notStr("not(");
94     static AtomicString onlyChild("only-child");
95     static AtomicString onlyOfType("only-of-type");
96     static AtomicString root("root");
97     static AtomicString selection("selection");
98     static AtomicString sliderThumb("-webkit-slider-thumb");
99     static AtomicString target("target");
100     static AtomicString visited("visited");
101     bool element = false;       // pseudo-element
102     bool compat = false;        // single colon compatbility mode
103     
104     _pseudoType = PseudoOther;
105     if (value == active)
106         _pseudoType = PseudoActive;
107     else if (value == after) {
108         _pseudoType = PseudoAfter;
109         element = compat = true;
110     } else if (value == anyLink)
111         _pseudoType = PseudoAnyLink;
112     else if (value == autofill)
113         _pseudoType = PseudoAutofill;
114     else if (value == before) {
115         _pseudoType = PseudoBefore;
116         element = compat = true;
117     } else if (value == checked)
118         _pseudoType = PseudoChecked;
119     else if (value == fileUploadButton) {
120         _pseudoType = PseudoFileUploadButton;
121         element = true;
122     } else if (value == disabled)
123         _pseudoType = PseudoDisabled;
124     else if (value == drag)
125         _pseudoType = PseudoDrag;
126     else if (value == enabled)
127         _pseudoType = PseudoEnabled;
128     else if (value == empty)
129         _pseudoType = PseudoEmpty;
130     else if (value == firstChild)
131         _pseudoType = PseudoFirstChild;
132     else if (value == firstLetter) {
133         _pseudoType = PseudoFirstLetter;
134         element = compat = true;
135     } else if (value == firstLine) {
136         _pseudoType = PseudoFirstLine;
137         element = compat = true;
138     } else if (value == firstOfType)
139         _pseudoType = PseudoFirstOfType;
140     else if (value == focus)
141         _pseudoType = PseudoFocus;
142     else if (value == hover)
143         _pseudoType = PseudoHover;
144     else if (value == indeterminate)
145         _pseudoType = PseudoIndeterminate;
146     else if (value == link)
147         _pseudoType = PseudoLink;
148     else if (value == lang)
149         _pseudoType = PseudoLang;
150     else if (value == lastChild)
151         _pseudoType = PseudoLastChild;
152     else if (value == lastOfType)
153         _pseudoType = PseudoLastOfType;
154     else if (value == notStr)
155         _pseudoType = PseudoNot;
156     else if (value == onlyChild)
157         _pseudoType = PseudoOnlyChild;
158     else if (value == onlyOfType)
159         _pseudoType = PseudoOnlyOfType;
160     else if (value == root)
161         _pseudoType = PseudoRoot;
162     else if (value == selection) {
163         _pseudoType = PseudoSelection;
164         element = true;
165     } else if (value == sliderThumb) {
166         _pseudoType = PseudoSliderThumb;
167         element = true;
168     } else if (value == target)
169         _pseudoType = PseudoTarget;
170     else if (value == visited)
171         _pseudoType = PseudoVisited;
172     
173     if (match == PseudoClass && element)
174         if (!compat) 
175             _pseudoType = PseudoOther;
176         else 
177            match = PseudoElement;
178     else if (match == PseudoElement && !element)
179         _pseudoType = PseudoOther;
180 }
181
182 bool CSSSelector::operator == (const CSSSelector &other)
183 {
184     const CSSSelector *sel1 = this;
185     const CSSSelector *sel2 = &other;
186
187     while (sel1 && sel2) {
188         if (sel1->tag != sel2->tag || sel1->attr != sel2->attr ||
189              sel1->relation() != sel2->relation() || sel1->match != sel2->match ||
190              sel1->value != sel2->value ||
191              sel1->pseudoType() != sel2->pseudoType() ||
192              sel1->argument != sel2->argument)
193             return false;
194         sel1 = sel1->tagHistory;
195         sel2 = sel2->tagHistory;
196     }
197     if (sel1 || sel2)
198         return false;
199     return true;
200 }
201
202 String CSSSelector::selectorText() const
203 {
204     // FIXME: Support namespaces when dumping the selector text. -dwh
205     String str;
206     const CSSSelector* cs = this;
207     const AtomicString& localName = cs->tag.localName();
208     if (cs->match == CSSSelector::None || localName != starAtom)
209         str = localName;
210     while (true) {
211         if (cs->match == CSSSelector::Id) {
212             str += "#";
213             str += cs->value;
214         } else if (cs->match == CSSSelector::Class) {
215             str += ".";
216             str += cs->value;
217         } else if (cs->match == CSSSelector::PseudoClass) {
218             str += ":";
219             str += cs->value;
220         } else if (cs->match == CSSSelector::PseudoElement) {
221             str += "::";
222             str += cs->value;
223         } else if (cs->hasAttribute()) {
224             // FIXME: Add support for dumping namespaces.
225             String attrName = cs->attr.localName();
226             str += "[";
227             str += attrName;
228             switch (cs->match) {
229                 case CSSSelector::Exact:
230                     str += "=";
231                     break;
232                 case CSSSelector::Set:
233                     // set has no operator or value, just the attrName
234                     str += "]";
235                     break;
236                 case CSSSelector::List:
237                     str += "~=";
238                     break;
239                 case CSSSelector::Hyphen:
240                     str += "|=";
241                     break;
242                 case CSSSelector::Begin:
243                     str += "^=";
244                     break;
245                 case CSSSelector::End:
246                     str += "$=";
247                     break;
248                 case CSSSelector::Contain:
249                     str += "*=";
250                     break;
251                 default:
252                     break;
253             }
254             if (cs->match != CSSSelector::Set) {
255                 str += "\"";
256                 str += cs->value;
257                 str += "\"]";
258             }
259         }
260         if (cs->relation() != CSSSelector::SubSelector || !cs->tagHistory)
261             break;
262         cs = cs->tagHistory;
263     }
264     if (cs->tagHistory) {
265         String tagHistoryText = cs->tagHistory->selectorText();
266         if (cs->relation() == CSSSelector::DirectAdjacent)
267             str = tagHistoryText + " + " + str;
268         else if (cs->relation() == CSSSelector::IndirectAdjacent)
269             str = tagHistoryText + " ~ " + str;
270         else if (cs->relation() == CSSSelector::Child)
271             str = tagHistoryText + " > " + str;
272         else // Descendant
273             str = tagHistoryText + " " + str;
274     }
275     return str;
276 }
277
278 }