/* * This file is part of the DOM implementation for KDE. * * Copyright 1999-2003 Lars Knoll (knoll@kde.org) * Copyright 1999 Waldo Bastian (bastian@kde.org) * Copyright 2001 Andreas Schlapbach (schlpbch@iam.unibe.ch) * Copyright 2001-2003 Dirk Mueller (mueller@kde.org) * Copyright 2002 Apple Computer, Inc. * Copyright 2004 Allan Sandfeld Jensen (kde@carewolf.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ //#define CSS_DEBUG #include "css_base.h" #include #include #ifdef CSS_DEBUG #include "cssproperties.h" #endif #include "css_stylesheetimpl.h" #include #include "css_valueimpl.h" using namespace DOM; void StyleBaseImpl::checkLoaded() const { if(m_parent) m_parent->checkLoaded(); } void StyleBaseImpl::checkPending() const { if(m_parent) m_parent->checkPending(); } StyleSheetImpl* StyleBaseImpl::stylesheet() { StyleBaseImpl* b = this; while(b && !b->isStyleSheet()) b = b->m_parent; return static_cast(b); } KUrl StyleBaseImpl::baseURL() { // try to find the style sheet. If found look for its url. // If it has none, look for the parentsheet, or the parentNode and // try to find out about their url StyleSheetImpl *sheet = stylesheet(); if(!sheet) return KUrl(); if(!sheet->href().isNull()) return KUrl( sheet->href().string() ); // find parent if(sheet->parent()) return sheet->parent()->baseURL(); if(!sheet->ownerNode()) return KUrl(); return sheet->ownerNode()->document()->baseURL(); } void StyleBaseImpl::setParsedValue(int propId, const CSSValueImpl *parsedValue, bool important, QList *propList) { QMutableListIterator propIt(*propList); propIt.toBack(); // just remove the top one - not sure what should happen if we have multiple instances of the property CSSProperty* p; while (propIt.hasPrevious()) { p = propIt.previous(); if (p->m_id == propId && p->m_important == important ) { delete propIt.value(); propIt.remove(); break; } } CSSProperty *prop = new CSSProperty(); prop->m_id = propId; prop->setValue(const_cast(parsedValue)); prop->m_important = important; propList->append(prop); #ifdef CSS_DEBUG kDebug( 6080 ) << "added property: " << getPropertyName(propId).string() // non implemented yet << ", value: " << parsedValue->cssText().string() << " important: " << prop->m_important; #endif } // ------------------------------------------------------------------------------ StyleListImpl::~StyleListImpl() { StyleBaseImpl *n; if(!m_lstChildren) return; QListIterator it( *m_lstChildren ); while ( it.hasNext() ) { n = it.next(); n->setParent(0); if( !n->refCount() ) delete n; } delete m_lstChildren; } // -------------------------------------------------------------------------------- void CSSSelector::print(void) { kDebug( 6080 ) << "[Selector: tag = " << QString::number(makeId(tagNamespace.id(), tagLocalName.id()),16) << ", attr = \"" << makeId(attrNamespace.id(), attrLocalName.id()) << "\", match = \"" << match << "\" value = \"" << value.string().string().toLatin1().constData() << "\" relation = " << (int)relation << "]" << endl; if ( tagHistory ) tagHistory->print(); kDebug( 6080 ) << " specificity = " << specificity(); } unsigned int CSSSelector::specificity() const { int s = ((tagLocalName.id() == anyLocalName) ? 0 : 1); switch(match) { case Id: s += 0x10000; break; case Exact: case Set: case List: case Class: case Hyphen: case PseudoClass: case PseudoElement: case Contain: case Begin: case End: s += 0x100; case None: break; } if(tagHistory) s += tagHistory->specificity(); // make sure it doesn't overflow return s & 0xffffff; } void CSSSelector::extractPseudoType() const { if (match != PseudoClass && match != PseudoElement) return; _pseudoType = PseudoOther; bool element = false; bool compat = false; if (!value.isEmpty()) { value = value.string().lower(); switch (value[0].unicode()) { case '-': if (value == "-khtml-replaced") _pseudoType = PseudoReplaced; else if (value == "-khtml-marker") _pseudoType = PseudoMarker; element = true; break; case 'a': if (value == "active") _pseudoType = PseudoActive; else if (value == "after") { _pseudoType = PseudoAfter; element = compat = true; } break; case 'b': if (value == "before") { _pseudoType = PseudoBefore; element = compat = true; } break; case 'c': if (value == "checked") _pseudoType = PseudoChecked; else if (value == "contains(") _pseudoType = PseudoContains; break; case 'd': if (value == "disabled") _pseudoType = PseudoDisabled; if (value == "default") _pseudoType = PseudoDefault; break; case 'e': if (value == "empty") _pseudoType = PseudoEmpty; else if (value == "enabled") _pseudoType = PseudoEnabled; break; case 'f': if (value == "first-child") _pseudoType = PseudoFirstChild; else if (value == "first-letter") { _pseudoType = PseudoFirstLetter; element = compat = true; } else if (value == "first-line") { _pseudoType = PseudoFirstLine; element = compat = true; } else if (value == "first-of-type") _pseudoType = PseudoFirstOfType; else if (value == "focus") _pseudoType = PseudoFocus; break; case 'h': if (value == "hover") _pseudoType = PseudoHover; break; case 'i': if (value == "indeterminate") _pseudoType = PseudoIndeterminate; break; case 'l': if (value == "link") _pseudoType = PseudoLink; else if (value == "lang(") _pseudoType = PseudoLang; else if (value == "last-child") _pseudoType = PseudoLastChild; else if (value == "last-of-type") _pseudoType = PseudoLastOfType; break; case 'n': if (value == "not(") _pseudoType = PseudoNot; else if (value == "nth-child(") _pseudoType = PseudoNthChild; else if (value == "nth-last-child(") _pseudoType = PseudoNthLastChild; else if (value == "nth-of-type(") _pseudoType = PseudoNthOfType; else if (value == "nth-last-of-type(") _pseudoType = PseudoNthLastOfType; break; case 'o': if (value == "only-child") _pseudoType = PseudoOnlyChild; else if (value == "only-of-type") _pseudoType = PseudoOnlyOfType; break; case 'r': if (value == "root") _pseudoType = PseudoRoot; else if (value == "read-only") _pseudoType = PseudoReadOnly; else if (value == "read-write") _pseudoType = PseudoReadWrite; break; case 's': if (value == "selection") { _pseudoType = PseudoSelection; element = true; } break; case 't': if (value == "target") _pseudoType = PseudoTarget; break; case 'v': if (value == "visited") _pseudoType = PseudoVisited; break; } } if (match == PseudoClass && element) if (!compat) _pseudoType = PseudoOther; else match = PseudoElement; else if (match == PseudoElement && !element) _pseudoType = PseudoOther; } bool CSSSelector::operator == ( const CSSSelector &other ) const { const CSSSelector *sel1 = this; const CSSSelector *sel2 = &other; while ( sel1 && sel2 ) { //assert(sel1->_pseudoType != PseudoNotParsed); //assert(sel2->_pseudoType != PseudoNotParsed); if ( sel1->tagLocalName.id() != sel2->tagLocalName.id() || sel1->attrLocalName.id() != sel2->attrLocalName.id() || sel1->tagNamespace.id() != sel2->tagNamespace.id() || sel1->attrNamespace.id() != sel2->attrNamespace.id() || sel1->relation != sel2->relation || sel1->match != sel2->match || sel1->value != sel2->value || sel1->pseudoType() != sel2->pseudoType() || sel1->string_arg != sel2->string_arg) return false; sel1 = sel1->tagHistory; sel2 = sel2->tagHistory; } if ( sel1 || sel2 ) return false; return true; } DOMString CSSSelector::selectorText() const { // FIXME: Support namespaces when dumping the selector text. This requires preserving // the original namespace prefix used. Ugh. -dwh DOMString str; const CSSSelector* cs = this; quint16 tag = cs->tagLocalName.id(); if (tag == anyLocalName && cs->match == CSSSelector::None) str = "*"; else if (tag != anyLocalName) str = LocalName::fromId(tag).toString(); const CSSSelector* op = 0; while (true) { if ( makeId(cs->attrNamespace.id(), cs->attrLocalName.id()) == ATTR_ID && cs->match == CSSSelector::Id ) { str += "#"; str += cs->value; } else if ( cs->match == CSSSelector::Class ) { str += "."; str += cs->value; } else if ( cs->match == CSSSelector::PseudoClass ) { str += ":"; str += cs->value; if (!cs->string_arg.isEmpty()) { // e.g :nth-child(...) str += cs->string_arg; str += ")"; } else if (cs->simpleSelector && !op) { // :not(...) op = cs; cs = cs->simpleSelector; continue; } } else if ( cs->match == CSSSelector::PseudoElement ) { str += "::"; str += cs->value; } // optional attribute else if ( cs->attrLocalName.id() ) { DOMString attrName = LocalName::fromId(cs->attrLocalName.id()).toString(); str += "["; str += attrName; switch (cs->match) { case CSSSelector::Exact: str += "="; break; case CSSSelector::Set: break; case CSSSelector::List: str += "~="; break; case CSSSelector::Hyphen: str += "|="; break; case CSSSelector::Begin: str += "^="; break; case CSSSelector::End: str += "$="; break; case CSSSelector::Contain: str += "*="; break; default: kWarning(6080) << "Unhandled case in CSSStyleRuleImpl::selectorText : match=" << cs->match; } if (cs->match != CSSSelector::Set) { str += "\""; str += cs->value; str += "\""; } str += "]"; } if (op && !cs->tagHistory) { cs=op; op=0; str += ")"; } if ((cs->relation != CSSSelector::SubSelector && !op) || !cs->tagHistory) break; cs = cs->tagHistory; } if ( cs->tagHistory ) { DOMString tagHistoryText = cs->tagHistory->selectorText(); if ( cs->relation == DirectAdjacent ) str = tagHistoryText + DOMString(" + ") + str; else if ( cs->relation == IndirectAdjacent ) str = tagHistoryText + DOMString(" ~ ") + str; else if ( cs->relation == Child ) str = tagHistoryText + DOMString(" > ") + str; else // Descendant str = tagHistoryText + DOMString(" ") + str; } return str; } // ----------------------------------------------------------------------------