File indexing completed on 2024-04-28 15:22:38
0001 /* 0002 * This file is part of the DOM implementation for KDE. 0003 * 0004 * Copyright 1999-2003 Lars Knoll (knoll@kde.org) 0005 * Copyright 1999 Waldo Bastian (bastian@kde.org) 0006 * Copyright 2001 Andreas Schlapbach (schlpbch@iam.unibe.ch) 0007 * Copyright 2001-2003 Dirk Mueller (mueller@kde.org) 0008 * Copyright 2002 Apple Computer, Inc. 0009 * Copyright 2004 Allan Sandfeld Jensen (kde@carewolf.com) 0010 * 0011 * This library is free software; you can redistribute it and/or 0012 * modify it under the terms of the GNU Library General Public 0013 * License as published by the Free Software Foundation; either 0014 * version 2 of the License, or (at your option) any later version. 0015 * 0016 * This library is distributed in the hope that it will be useful, 0017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0019 * Library General Public License for more details. 0020 * 0021 * You should have received a copy of the GNU Library General Public License 0022 * along with this library; see the file COPYING.LIB. If not, write to 0023 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0024 * Boston, MA 02110-1301, USA. 0025 */ 0026 0027 //#define CSS_DEBUG 0028 0029 #include "css_base.h" 0030 0031 #include <assert.h> 0032 #include "khtml_debug.h" 0033 0034 #ifdef CSS_DEBUG 0035 #include "cssproperties.h" 0036 #endif 0037 0038 #include "css_stylesheetimpl.h" 0039 #include <xml/dom_docimpl.h> 0040 #include "css_valueimpl.h" 0041 using namespace DOM; 0042 0043 void StyleBaseImpl::checkLoaded() const 0044 { 0045 if (m_parent) { 0046 m_parent->checkLoaded(); 0047 } 0048 } 0049 0050 void StyleBaseImpl::checkPending() const 0051 { 0052 if (m_parent) { 0053 m_parent->checkPending(); 0054 } 0055 } 0056 0057 StyleSheetImpl *StyleBaseImpl::stylesheet() 0058 { 0059 StyleBaseImpl *b = this; 0060 while (b && !b->isStyleSheet()) { 0061 b = b->m_parent; 0062 } 0063 return static_cast<StyleSheetImpl *>(b); 0064 } 0065 0066 QUrl StyleBaseImpl::baseURL() 0067 { 0068 // try to find the style sheet. If found look for its url. 0069 // If it has none, look for the parentsheet, or the parentNode and 0070 // try to find out about their url 0071 0072 StyleSheetImpl *sheet = stylesheet(); 0073 0074 if (!sheet) { 0075 return QUrl(); 0076 } 0077 0078 if (!sheet->href().isNull()) { 0079 return QUrl(sheet->href().string()); 0080 } 0081 0082 // find parent 0083 if (sheet->parent()) { 0084 return sheet->parent()->baseURL(); 0085 } 0086 0087 if (!sheet->ownerNode()) { 0088 return QUrl(); 0089 } 0090 0091 return sheet->ownerNode()->document()->baseURL(); 0092 } 0093 0094 void StyleBaseImpl::setParsedValue(int propId, const CSSValueImpl *parsedValue, 0095 bool important, QList<CSSProperty *> *propList) 0096 { 0097 QMutableListIterator<CSSProperty *> propIt(*propList); 0098 propIt.toBack(); // just remove the top one - not sure what should happen if we have multiple instances of the property 0099 CSSProperty *p; 0100 while (propIt.hasPrevious()) { 0101 p = propIt.previous(); 0102 if (p->m_id == propId && p->m_important == important) { 0103 delete propIt.value(); 0104 propIt.remove(); 0105 break; 0106 } 0107 } 0108 0109 CSSProperty *prop = new CSSProperty(); 0110 prop->m_id = propId; 0111 prop->setValue(const_cast<CSSValueImpl *>(parsedValue)); 0112 prop->m_important = important; 0113 0114 propList->append(prop); 0115 #ifdef CSS_DEBUG 0116 qCDebug(KHTML_LOG) << "added property: " << getPropertyName(propId).string() 0117 // non implemented yet << ", value: " << parsedValue->cssText().string() 0118 << " important: " << prop->m_important; 0119 #endif 0120 } 0121 0122 // ------------------------------------------------------------------------------ 0123 0124 StyleListImpl::~StyleListImpl() 0125 { 0126 StyleBaseImpl *n; 0127 0128 if (!m_lstChildren) { 0129 return; 0130 } 0131 0132 QListIterator<StyleBaseImpl *> it(*m_lstChildren); 0133 while (it.hasNext()) { 0134 n = it.next(); 0135 n->setParent(nullptr); 0136 if (!n->refCount()) { 0137 delete n; 0138 } 0139 } 0140 delete m_lstChildren; 0141 } 0142 0143 // -------------------------------------------------------------------------------- 0144 0145 void CSSSelector::print(void) 0146 { 0147 // qCDebug(KHTML_LOG) << "[Selector: tag = " << QString::number(makeId(tagNamespace.id(), tagLocalName.id()),16) << ", attr = \"" << makeId(attrNamespace.id(), attrLocalName.id()) << "\", match = \"" << match 0148 // << "\" value = \"" << value.string().string().toLatin1().constData() << "\" relation = " << (int)relation 0149 // << "]"; 0150 if (tagHistory) { 0151 tagHistory->print(); 0152 } 0153 // qCDebug(KHTML_LOG) << " specificity = " << specificity(); 0154 } 0155 0156 unsigned int CSSSelector::specificity() const 0157 { 0158 0159 int s = ((tagLocalName.id() == anyLocalName) ? 0 : 1); 0160 switch (match) { 0161 case Id: 0162 s += 0x10000; 0163 break; 0164 case Exact: 0165 case Set: 0166 case List: 0167 case Class: 0168 case Hyphen: 0169 case PseudoClass: 0170 case PseudoElement: 0171 case Contain: 0172 case Begin: 0173 case End: 0174 s += 0x100; 0175 case None: 0176 break; 0177 } 0178 if (tagHistory) { 0179 s += tagHistory->specificity(); 0180 } 0181 // make sure it doesn't overflow 0182 return s & 0xffffff; 0183 } 0184 0185 void CSSSelector::extractPseudoType() const 0186 { 0187 if (match != PseudoClass && match != PseudoElement) { 0188 return; 0189 } 0190 _pseudoType = PseudoOther; 0191 bool element = false; 0192 bool compat = false; 0193 if (!value.isEmpty()) { 0194 value = value.string().lower(); 0195 switch (value[0].unicode()) { 0196 case '-': 0197 if (value == "-khtml-replaced") { 0198 _pseudoType = PseudoReplaced; 0199 } else if (value == "-khtml-marker") { 0200 _pseudoType = PseudoMarker; 0201 } 0202 element = true; 0203 break; 0204 case 'a': 0205 if (value == "active") { 0206 _pseudoType = PseudoActive; 0207 } else if (value == "after") { 0208 _pseudoType = PseudoAfter; 0209 element = compat = true; 0210 } 0211 break; 0212 case 'b': 0213 if (value == "before") { 0214 _pseudoType = PseudoBefore; 0215 element = compat = true; 0216 } 0217 break; 0218 case 'c': 0219 if (value == "checked") { 0220 _pseudoType = PseudoChecked; 0221 } else if (value == "contains(") { 0222 _pseudoType = PseudoContains; 0223 } 0224 break; 0225 case 'd': 0226 if (value == "disabled") { 0227 _pseudoType = PseudoDisabled; 0228 } 0229 if (value == "default") { 0230 _pseudoType = PseudoDefault; 0231 } 0232 break; 0233 case 'e': 0234 if (value == "empty") { 0235 _pseudoType = PseudoEmpty; 0236 } else if (value == "enabled") { 0237 _pseudoType = PseudoEnabled; 0238 } 0239 break; 0240 case 'f': 0241 if (value == "first-child") { 0242 _pseudoType = PseudoFirstChild; 0243 } else if (value == "first-letter") { 0244 _pseudoType = PseudoFirstLetter; 0245 element = compat = true; 0246 } else if (value == "first-line") { 0247 _pseudoType = PseudoFirstLine; 0248 element = compat = true; 0249 } else if (value == "first-of-type") { 0250 _pseudoType = PseudoFirstOfType; 0251 } else if (value == "focus") { 0252 _pseudoType = PseudoFocus; 0253 } 0254 break; 0255 case 'h': 0256 if (value == "hover") { 0257 _pseudoType = PseudoHover; 0258 } 0259 break; 0260 case 'i': 0261 if (value == "indeterminate") { 0262 _pseudoType = PseudoIndeterminate; 0263 } 0264 break; 0265 case 'l': 0266 if (value == "link") { 0267 _pseudoType = PseudoLink; 0268 } else if (value == "lang(") { 0269 _pseudoType = PseudoLang; 0270 } else if (value == "last-child") { 0271 _pseudoType = PseudoLastChild; 0272 } else if (value == "last-of-type") { 0273 _pseudoType = PseudoLastOfType; 0274 } 0275 break; 0276 case 'n': 0277 if (value == "not(") { 0278 _pseudoType = PseudoNot; 0279 } else if (value == "nth-child(") { 0280 _pseudoType = PseudoNthChild; 0281 } else if (value == "nth-last-child(") { 0282 _pseudoType = PseudoNthLastChild; 0283 } else if (value == "nth-of-type(") { 0284 _pseudoType = PseudoNthOfType; 0285 } else if (value == "nth-last-of-type(") { 0286 _pseudoType = PseudoNthLastOfType; 0287 } 0288 break; 0289 case 'o': 0290 if (value == "only-child") { 0291 _pseudoType = PseudoOnlyChild; 0292 } else if (value == "only-of-type") { 0293 _pseudoType = PseudoOnlyOfType; 0294 } 0295 break; 0296 case 'r': 0297 if (value == "root") { 0298 _pseudoType = PseudoRoot; 0299 } else if (value == "read-only") { 0300 _pseudoType = PseudoReadOnly; 0301 } else if (value == "read-write") { 0302 _pseudoType = PseudoReadWrite; 0303 } 0304 break; 0305 case 's': 0306 if (value == "selection") { 0307 _pseudoType = PseudoSelection; 0308 element = true; 0309 } 0310 break; 0311 case 't': 0312 if (value == "target") { 0313 _pseudoType = PseudoTarget; 0314 } 0315 break; 0316 case 'v': 0317 if (value == "visited") { 0318 _pseudoType = PseudoVisited; 0319 } 0320 break; 0321 } 0322 } 0323 if (match == PseudoClass && element) 0324 if (!compat) { 0325 _pseudoType = PseudoOther; 0326 } else { 0327 match = PseudoElement; 0328 } 0329 else if (match == PseudoElement && !element) { 0330 _pseudoType = PseudoOther; 0331 } 0332 } 0333 0334 bool CSSSelector::operator == (const CSSSelector &other) const 0335 { 0336 const CSSSelector *sel1 = this; 0337 const CSSSelector *sel2 = &other; 0338 0339 while (sel1 && sel2) { 0340 //assert(sel1->_pseudoType != PseudoNotParsed); 0341 //assert(sel2->_pseudoType != PseudoNotParsed); 0342 if (sel1->tagLocalName.id() != sel2->tagLocalName.id() || sel1->attrLocalName.id() != sel2->attrLocalName.id() || 0343 sel1->tagNamespace.id() != sel2->tagNamespace.id() || sel1->attrNamespace.id() != sel2->attrNamespace.id() || 0344 sel1->relation != sel2->relation || sel1->match != sel2->match || 0345 sel1->value != sel2->value || 0346 sel1->pseudoType() != sel2->pseudoType() || 0347 sel1->string_arg != sel2->string_arg) { 0348 return false; 0349 } 0350 sel1 = sel1->tagHistory; 0351 sel2 = sel2->tagHistory; 0352 } 0353 if (sel1 || sel2) { 0354 return false; 0355 } 0356 return true; 0357 } 0358 0359 DOMString CSSSelector::selectorText() const 0360 { 0361 // FIXME: Support namespaces when dumping the selector text. This requires preserving 0362 // the original namespace prefix used. Ugh. -dwh 0363 DOMString str; 0364 const CSSSelector *cs = this; 0365 quint16 tag = cs->tagLocalName.id(); 0366 if (tag == anyLocalName && cs->match == CSSSelector::None) { 0367 str = "*"; 0368 } else if (tag != anyLocalName) { 0369 str = LocalName::fromId(tag).toString(); 0370 } 0371 0372 const CSSSelector *op = nullptr; 0373 while (true) { 0374 if (makeId(cs->attrNamespace.id(), cs->attrLocalName.id()) == ATTR_ID && cs->match == CSSSelector::Id) { 0375 str += "#"; 0376 str += cs->value; 0377 } else if (cs->match == CSSSelector::Class) { 0378 str += "."; 0379 str += cs->value; 0380 } else if (cs->match == CSSSelector::PseudoClass) { 0381 str += ":"; 0382 str += cs->value; 0383 if (!cs->string_arg.isEmpty()) { // e.g :nth-child(...) 0384 str += cs->string_arg; 0385 str += ")"; 0386 } else if (cs->simpleSelector && !op) { // :not(...) 0387 op = cs; 0388 cs = cs->simpleSelector; 0389 continue; 0390 } 0391 } else if (cs->match == CSSSelector::PseudoElement) { 0392 str += "::"; 0393 str += cs->value; 0394 } 0395 // optional attribute 0396 else if (cs->attrLocalName.id()) { 0397 DOMString attrName = LocalName::fromId(cs->attrLocalName.id()).toString(); 0398 str += "["; 0399 str += attrName; 0400 switch (cs->match) { 0401 case CSSSelector::Exact: 0402 str += "="; 0403 break; 0404 case CSSSelector::Set: 0405 break; 0406 case CSSSelector::List: 0407 str += "~="; 0408 break; 0409 case CSSSelector::Hyphen: 0410 str += "|="; 0411 break; 0412 case CSSSelector::Begin: 0413 str += "^="; 0414 break; 0415 case CSSSelector::End: 0416 str += "$="; 0417 break; 0418 case CSSSelector::Contain: 0419 str += "*="; 0420 break; 0421 default: 0422 qCWarning(KHTML_LOG) << "Unhandled case in CSSStyleRuleImpl::selectorText : match=" << cs->match; 0423 } 0424 if (cs->match != CSSSelector::Set) { 0425 str += "\""; 0426 str += cs->value; 0427 str += "\""; 0428 } 0429 str += "]"; 0430 } 0431 if (op && !cs->tagHistory) { 0432 cs = op; 0433 op = nullptr; 0434 str += ")"; 0435 } 0436 0437 if ((cs->relation != CSSSelector::SubSelector && !op) || !cs->tagHistory) { 0438 break; 0439 } 0440 cs = cs->tagHistory; 0441 } 0442 0443 if (cs->tagHistory) { 0444 DOMString tagHistoryText = cs->tagHistory->selectorText(); 0445 if (cs->relation == DirectAdjacent) { 0446 str = tagHistoryText + DOMString(" + ") + str; 0447 } else if (cs->relation == IndirectAdjacent) { 0448 str = tagHistoryText + DOMString(" ~ ") + str; 0449 } else if (cs->relation == Child) { 0450 str = tagHistoryText + DOMString(" > ") + str; 0451 } else { // Descendant 0452 str = tagHistoryText + DOMString(" ") + str; 0453 } 0454 } 0455 return str; 0456 } 0457 0458 // ----------------------------------------------------------------------------