fix logic
[personal-kdelibs.git] / khtml / css / css_base.cpp
blob4f0a44b1056a2ac8b432e1b94ef0eb6dd5b7c948
1 /*
2 * This file is part of the DOM implementation for KDE.
4 * Copyright 1999-2003 Lars Knoll (knoll@kde.org)
5 * Copyright 1999 Waldo Bastian (bastian@kde.org)
6 * Copyright 2001 Andreas Schlapbach (schlpbch@iam.unibe.ch)
7 * Copyright 2001-2003 Dirk Mueller (mueller@kde.org)
8 * Copyright 2002 Apple Computer, Inc.
9 * Copyright 2004 Allan Sandfeld Jensen (kde@carewolf.com)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public License
22 * along with this library; see the file COPYING.LIB. If not, write to
23 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
27 //#define CSS_DEBUG
29 #include "css_base.h"
31 #include <assert.h>
32 #include <kdebug.h>
34 #ifdef CSS_DEBUG
35 #include "cssproperties.h"
36 #endif
38 #include "css_stylesheetimpl.h"
39 #include <xml/dom_docimpl.h>
40 #include <misc/htmlhashes.h>
41 #include "css_valueimpl.h"
42 using namespace DOM;
44 void StyleBaseImpl::checkLoaded() const
46 if(m_parent) m_parent->checkLoaded();
49 void StyleBaseImpl::checkPending() const
51 if(m_parent) m_parent->checkPending();
54 StyleSheetImpl* StyleBaseImpl::stylesheet()
56 StyleBaseImpl* b = this;
57 while(b && !b->isStyleSheet())
58 b = b->m_parent;
59 return static_cast<StyleSheetImpl *>(b);
62 KUrl StyleBaseImpl::baseURL()
64 // try to find the style sheet. If found look for its url.
65 // If it has none, look for the parentsheet, or the parentNode and
66 // try to find out about their url
68 StyleSheetImpl *sheet = stylesheet();
70 if(!sheet) return KUrl();
72 if(!sheet->href().isNull())
73 return KUrl( sheet->href().string() );
75 // find parent
76 if(sheet->parent()) return sheet->parent()->baseURL();
78 if(!sheet->ownerNode()) return KUrl();
80 return sheet->ownerNode()->document()->baseURL();
83 void StyleBaseImpl::setParsedValue(int propId, const CSSValueImpl *parsedValue,
84 bool important, QList<CSSProperty*> *propList)
86 QMutableListIterator<CSSProperty*> propIt(*propList);
87 propIt.toBack(); // just remove the top one - not sure what should happen if we have multiple instances of the property
88 CSSProperty* p;
89 while (propIt.hasPrevious()) {
90 p = propIt.previous();
91 if (p->m_id == propId && p->m_important == important ) {
92 delete propIt.value();
93 propIt.remove();
94 break;
98 CSSProperty *prop = new CSSProperty();
99 prop->m_id = propId;
100 prop->setValue((CSSValueImpl *) parsedValue);
101 prop->m_important = important;
103 propList->append(prop);
104 #ifdef CSS_DEBUG
105 kDebug( 6080 ) << "added property: " << getPropertyName(propId).string()
106 // non implemented yet << ", value: " << parsedValue->cssText().string()
107 << " important: " << prop->m_important;
108 #endif
111 // ------------------------------------------------------------------------------
113 StyleListImpl::~StyleListImpl()
115 StyleBaseImpl *n;
117 if(!m_lstChildren) return;
119 QListIterator<StyleBaseImpl*> it( *m_lstChildren );
120 while ( it.hasNext() )
122 n = it.next();
123 n->setParent(0);
124 if( !n->refCount() ) delete n;
126 delete m_lstChildren;
129 // --------------------------------------------------------------------------------
131 void CSSSelector::print(void)
133 kDebug( 6080 ) << "[Selector: tag = " << QString::number(makeId(tagNamespace.id(), tagLocalName.id()),16) << ", attr = \"" << makeId(attrNamespace.id(), attrLocalName.id()) << "\", match = \"" << match
134 << "\" value = \"" << value.string().string().toLatin1().constData() << "\" relation = " << (int)relation
135 << "]" << endl;
136 if ( tagHistory )
137 tagHistory->print();
138 kDebug( 6080 ) << " specificity = " << specificity();
141 unsigned int CSSSelector::specificity() const
144 int s = ((tagLocalName.id() == anyLocalName) ? 0 : 1);
145 switch(match)
147 case Id:
148 s += 0x10000;
149 break;
150 case Exact:
151 case Set:
152 case List:
153 case Class:
154 case Hyphen:
155 case PseudoClass:
156 case PseudoElement:
157 case Contain:
158 case Begin:
159 case End:
160 s += 0x100;
161 case None:
162 break;
164 if(tagHistory)
165 s += tagHistory->specificity();
166 // make sure it doesn't overflow
167 return s & 0xffffff;
170 void CSSSelector::extractPseudoType() const
172 if (match != PseudoClass && match != PseudoElement)
173 return;
174 _pseudoType = PseudoOther;
175 bool element = false;
176 bool compat = false;
177 if (!value.isEmpty()) {
178 value = value.string().lower();
179 switch (value[0].unicode()) {
180 case '-':
181 if (value == "-khtml-replaced")
182 _pseudoType = PseudoReplaced;
183 else
184 if (value == "-khtml-marker")
185 _pseudoType = PseudoMarker;
186 element = true;
187 break;
188 case 'a':
189 if (value == "active")
190 _pseudoType = PseudoActive;
191 else if (value == "after") {
192 _pseudoType = PseudoAfter;
193 element = compat = true;
195 break;
196 case 'b':
197 if (value == "before") {
198 _pseudoType = PseudoBefore;
199 element = compat = true;
201 break;
202 case 'c':
203 if (value == "checked")
204 _pseudoType = PseudoChecked;
205 else if (value == "contains(")
206 _pseudoType = PseudoContains;
207 break;
208 case 'd':
209 if (value == "disabled")
210 _pseudoType = PseudoDisabled;
211 if (value == "default")
212 _pseudoType = PseudoDefault;
213 break;
214 case 'e':
215 if (value == "empty")
216 _pseudoType = PseudoEmpty;
217 else if (value == "enabled")
218 _pseudoType = PseudoEnabled;
219 break;
220 case 'f':
221 if (value == "first-child")
222 _pseudoType = PseudoFirstChild;
223 else if (value == "first-letter") {
224 _pseudoType = PseudoFirstLetter;
225 element = compat = true;
227 else if (value == "first-line") {
228 _pseudoType = PseudoFirstLine;
229 element = compat = true;
231 else if (value == "first-of-type")
232 _pseudoType = PseudoFirstOfType;
233 else if (value == "focus")
234 _pseudoType = PseudoFocus;
235 break;
236 case 'h':
237 if (value == "hover")
238 _pseudoType = PseudoHover;
239 break;
240 case 'i':
241 if (value == "indeterminate")
242 _pseudoType = PseudoIndeterminate;
243 break;
244 case 'l':
245 if (value == "link")
246 _pseudoType = PseudoLink;
247 else if (value == "lang(")
248 _pseudoType = PseudoLang;
249 else if (value == "last-child")
250 _pseudoType = PseudoLastChild;
251 else if (value == "last-of-type")
252 _pseudoType = PseudoLastOfType;
253 break;
254 case 'n':
255 if (value == "not(")
256 _pseudoType = PseudoNot;
257 else if (value == "nth-child(")
258 _pseudoType = PseudoNthChild;
259 else if (value == "nth-last-child(")
260 _pseudoType = PseudoNthLastChild;
261 else if (value == "nth-of-type(")
262 _pseudoType = PseudoNthOfType;
263 else if (value == "nth-last-of-type(")
264 _pseudoType = PseudoNthLastOfType;
265 break;
266 case 'o':
267 if (value == "only-child")
268 _pseudoType = PseudoOnlyChild;
269 else if (value == "only-of-type")
270 _pseudoType = PseudoOnlyOfType;
271 break;
272 case 'r':
273 if (value == "root")
274 _pseudoType = PseudoRoot;
275 else if (value == "read-only")
276 _pseudoType = PseudoReadOnly;
277 else if (value == "read-write")
278 _pseudoType = PseudoReadWrite;
279 break;
280 case 's':
281 if (value == "selection") {
282 _pseudoType = PseudoSelection;
283 element = true;
285 break;
286 case 't':
287 if (value == "target")
288 _pseudoType = PseudoTarget;
289 break;
290 case 'v':
291 if (value == "visited")
292 _pseudoType = PseudoVisited;
293 break;
296 if (match == PseudoClass && element)
297 if (!compat) _pseudoType = PseudoOther;
298 else match = PseudoElement;
299 else
300 if (match == PseudoElement && !element)
301 _pseudoType = PseudoOther;
305 bool CSSSelector::operator == ( const CSSSelector &other ) const
307 const CSSSelector *sel1 = this;
308 const CSSSelector *sel2 = &other;
310 while ( sel1 && sel2 ) {
311 //assert(sel1->_pseudoType != PseudoNotParsed);
312 //assert(sel2->_pseudoType != PseudoNotParsed);
313 if ( sel1->tagLocalName.id() != sel2->tagLocalName.id() || sel1->attrLocalName.id() != sel2->attrLocalName.id() ||
314 sel1->tagNamespace.id() != sel2->tagNamespace.id() || sel1->attrNamespace.id() != sel2->attrNamespace.id() ||
315 sel1->relation != sel2->relation || sel1->match != sel2->match ||
316 sel1->value != sel2->value ||
317 sel1->pseudoType() != sel2->pseudoType() ||
318 sel1->string_arg != sel2->string_arg)
319 return false;
320 sel1 = sel1->tagHistory;
321 sel2 = sel2->tagHistory;
323 if ( sel1 || sel2 )
324 return false;
325 return true;
328 DOMString CSSSelector::selectorText() const
330 // FIXME: Support namespaces when dumping the selector text. This requires preserving
331 // the original namespace prefix used. Ugh. -dwh
332 DOMString str;
333 const CSSSelector* cs = this;
334 quint16 tag = cs->tagLocalName.id();
335 if (tag == anyLocalName && cs->match == CSSSelector::None)
336 str = "*";
337 else if (tag != anyLocalName)
338 str = LocalName::fromId(tag).toString();
340 const CSSSelector* op = 0;
341 while (true) {
342 if ( makeId(cs->attrNamespace.id(), cs->attrLocalName.id()) == ATTR_ID && cs->match == CSSSelector::Id )
344 str += "#";
345 str += cs->value;
347 else if ( cs->match == CSSSelector::Class )
349 str += ".";
350 str += cs->value;
352 else if ( cs->match == CSSSelector::PseudoClass )
354 str += ":";
355 str += cs->value;
356 if (!cs->string_arg.isEmpty()) { // e.g :nth-child(...)
357 str += cs->string_arg;
358 str += ")";
359 } else if (cs->simpleSelector && !op) { // :not(...)
360 op = cs;
361 cs = cs->simpleSelector;
362 continue;
365 else if ( cs->match == CSSSelector::PseudoElement )
367 str += "::";
368 str += cs->value;
370 // optional attribute
371 else if ( cs->attrLocalName.id() ) {
372 DOMString attrName = LocalName::fromId(cs->attrLocalName.id()).toString();
373 str += "[";
374 str += attrName;
375 switch (cs->match) {
376 case CSSSelector::Exact:
377 str += "=";
378 break;
379 case CSSSelector::Set:
380 break;
381 case CSSSelector::List:
382 str += "~=";
383 break;
384 case CSSSelector::Hyphen:
385 str += "|=";
386 break;
387 case CSSSelector::Begin:
388 str += "^=";
389 break;
390 case CSSSelector::End:
391 str += "$=";
392 break;
393 case CSSSelector::Contain:
394 str += "*=";
395 break;
396 default:
397 kWarning(6080) << "Unhandled case in CSSStyleRuleImpl::selectorText : match=" << cs->match;
399 if (cs->match != CSSSelector::Set) {
400 str += "\"";
401 str += cs->value;
402 str += "\"";
404 str += "]";
406 if (op && !cs->tagHistory) {
407 cs=op;
408 op=0;
409 str += ")";
412 if ((cs->relation != CSSSelector::SubSelector && !op) || !cs->tagHistory)
413 break;
414 cs = cs->tagHistory;
417 if ( cs->tagHistory ) {
418 DOMString tagHistoryText = cs->tagHistory->selectorText();
419 if ( cs->relation == DirectAdjacent )
420 str = tagHistoryText + " + " + str;
421 else if ( cs->relation == IndirectAdjacent )
422 str = tagHistoryText + " ~ " + str;
423 else if ( cs->relation == Child )
424 str = tagHistoryText + " > " + str;
425 else // Descendant
426 str = tagHistoryText + " " + str;
428 return str;
431 // ----------------------------------------------------------------------------