css_base.cpp

00001 /*
00002  * This file is part of the DOM implementation for KDE.
00003  *
00004  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
00005  *               1999 Waldo Bastian (bastian@kde.org)
00006  *               2001 Andreas Schlapbach (schlpbch@iam.unibe.ch)
00007  *               2001-2003 Dirk Mueller (mueller@kde.org)
00008  *               2002 Apple Computer, Inc.
00009  *               2004 Allan Sandfeld Jensen (kde@carewolf.com)
00010  *
00011  * This library is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU Library General Public
00013  * License as published by the Free Software Foundation; either
00014  * version 2 of the License, or (at your option) any later version.
00015  *
00016  * This library is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * Library General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Library General Public License
00022  * along with this library; see the file COPYING.LIB.  If not, write to
00023  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00024  * Boston, MA 02110-1301, USA.
00025  */
00026 
00027 //#define CSS_DEBUG
00028 
00029 #include <assert.h>
00030 #include <kdebug.h>
00031 
00032 #include "css_base.h"
00033 
00034 #ifdef CSS_DEBUG
00035 #include "cssproperties.h"
00036 #endif
00037 
00038 #include "css_stylesheetimpl.h"
00039 #include "xml/dom_docimpl.h"
00040 #include "misc/htmlhashes.h"
00041 #include "css_valueimpl.h"
00042 using namespace DOM;
00043 
00044 void StyleBaseImpl::checkLoaded() const
00045 {
00046     if(m_parent) m_parent->checkLoaded();
00047 }
00048 
00049 StyleSheetImpl* StyleBaseImpl::stylesheet()
00050 {
00051     StyleBaseImpl* b = this;
00052     while(b && !b->isStyleSheet())
00053         b = b->m_parent;
00054     return static_cast<StyleSheetImpl *>(b);
00055 }
00056 
00057 KURL StyleBaseImpl::baseURL()
00058 {
00059     // try to find the style sheet. If found look for its url.
00060     // If it has none, look for the parentsheet, or the parentNode and
00061     // try to find out about their url
00062 
00063     StyleSheetImpl *sheet = stylesheet();
00064 
00065     if(!sheet) return KURL();
00066 
00067     if(!sheet->href().isNull())
00068         return KURL( sheet->href().string() );
00069 
00070     // find parent
00071     if(sheet->parent()) return sheet->parent()->baseURL();
00072 
00073     if(!sheet->ownerNode()) return KURL();
00074 
00075     return sheet->ownerNode()->getDocument()->baseURL();
00076 }
00077 
00078 void StyleBaseImpl::setParsedValue(int propId, const CSSValueImpl *parsedValue,
00079                    bool important, bool nonCSSHint, QPtrList<CSSProperty> *propList)
00080 {
00081     QPtrListIterator<CSSProperty> propIt(*propList);
00082     propIt.toLast(); // just remove the top one - not sure what should happen if we have multiple instances of the property
00083     while (propIt.current() &&
00084            ( propIt.current()->m_id != propId || propIt.current()->nonCSSHint != nonCSSHint ||
00085              propIt.current()->m_important != important) )
00086         --propIt;
00087     if (propIt.current())
00088         propList->removeRef(propIt.current());
00089 
00090     CSSProperty *prop = new CSSProperty();
00091     prop->m_id = propId;
00092     prop->setValue((CSSValueImpl *) parsedValue);
00093     prop->m_important = important;
00094     prop->nonCSSHint = nonCSSHint;
00095 
00096     propList->append(prop);
00097 #ifdef CSS_DEBUG
00098     kdDebug( 6080 ) << "added property: " << getPropertyName(propId).string()
00099                     // non implemented yet << ", value: " << parsedValue->cssText().string()
00100                     << " important: " << prop->m_important
00101                     << " nonCSS: " << prop->nonCSSHint << endl;
00102 #endif
00103 }
00104 
00105 // ------------------------------------------------------------------------------
00106 
00107 StyleListImpl::~StyleListImpl()
00108 {
00109     StyleBaseImpl *n;
00110 
00111     if(!m_lstChildren) return;
00112 
00113     for( n = m_lstChildren->first(); n != 0; n = m_lstChildren->next() )
00114     {
00115         n->setParent(0);
00116         if( !n->refCount() ) delete n;
00117     }
00118     delete m_lstChildren;
00119 }
00120 
00121 // --------------------------------------------------------------------------------
00122 
00123 void CSSSelector::print(void)
00124 {
00125     kdDebug( 6080 ) << "[Selector: tag = " <<       QString::number(tag,16) << ", attr = \"" << attr << "\", match = \"" << match
00126             << "\" value = \"" << value.string().latin1() << "\" relation = " << (int)relation
00127             << "]" << endl;
00128     if ( tagHistory )
00129         tagHistory->print();
00130     kdDebug( 6080 ) << "    specificity = " << specificity() << endl;
00131 }
00132 
00133 unsigned int CSSSelector::specificity() const
00134 {
00135     if ( nonCSSHint )
00136         return 0;
00137 
00138     int s = ((localNamePart(tag) == anyLocalName) ? 0 : 1);
00139     switch(match)
00140     {
00141     case Id:
00142     s += 0x10000;
00143     break;
00144     case Exact:
00145     case Set:
00146     case List:
00147     case Class:
00148     case Hyphen:
00149     case PseudoClass:
00150     case PseudoElement:
00151     case Contain:
00152     case Begin:
00153     case End:
00154         s += 0x100;
00155     case None:
00156         break;
00157     }
00158     if(tagHistory)
00159         s += tagHistory->specificity();
00160     // make sure it doesn't overflow
00161     return s & 0xffffff;
00162 }
00163 
00164 void CSSSelector::extractPseudoType() const
00165 {
00166     if (match != PseudoClass && match != PseudoElement)
00167         return;
00168     _pseudoType = PseudoOther;
00169     bool element = false;
00170     bool compat = false;
00171     if (!value.isEmpty()) {
00172         value = value.lower();
00173         switch (value[0]) {
00174             case '-':
00175                 if (value == "-khtml-replaced")
00176                     _pseudoType = PseudoReplaced;
00177                 else
00178                 if (value == "-khtml-marker")
00179                     _pseudoType = PseudoMarker;
00180                 element = true;
00181                 break;
00182             case 'a':
00183                 if (value == "active")
00184                     _pseudoType = PseudoActive;
00185                 else if (value == "after") {
00186                     _pseudoType = PseudoAfter;
00187                     element = compat = true;
00188                 }
00189                 break;
00190             case 'b':
00191                 if (value == "before") {
00192                     _pseudoType = PseudoBefore;
00193                     element = compat = true;
00194                 }
00195                 break;
00196             case 'c':
00197                 if (value == "checked")
00198                     _pseudoType = PseudoChecked;
00199                 else if (value == "contains(")
00200                     _pseudoType = PseudoContains;
00201                 break;
00202             case 'd':
00203                 if (value == "disabled")
00204                     _pseudoType = PseudoDisabled;
00205                 break;
00206             case 'e':
00207                 if (value == "empty")
00208                     _pseudoType = PseudoEmpty;
00209                 else if (value == "enabled")
00210                     _pseudoType = PseudoEnabled;
00211                 break;
00212             case 'f':
00213                 if (value == "first-child")
00214                     _pseudoType = PseudoFirstChild;
00215                 else if (value == "first-letter") {
00216                     _pseudoType = PseudoFirstLetter;
00217                     element = compat = true;
00218                 }
00219                 else if (value == "first-line") {
00220                     _pseudoType = PseudoFirstLine;
00221                     element = compat = true;
00222                 }
00223                 else if (value == "first-of-type")
00224                     _pseudoType = PseudoFirstOfType;
00225                 else if (value == "focus")
00226                     _pseudoType = PseudoFocus;
00227                 break;
00228             case 'h':
00229                 if (value == "hover")
00230                     _pseudoType = PseudoHover;
00231                 break;
00232             case 'i':
00233                 if (value == "indeterminate")
00234                     _pseudoType = PseudoIndeterminate;
00235                 break;
00236             case 'l':
00237                 if (value == "link")
00238                     _pseudoType = PseudoLink;
00239                 else if (value == "lang(")
00240                     _pseudoType = PseudoLang;
00241                 else if (value == "last-child")
00242                     _pseudoType = PseudoLastChild;
00243                 else if (value == "last-of-type")
00244                     _pseudoType = PseudoLastOfType;
00245                 break;
00246             case 'n':
00247                 if (value == "not(")
00248                     _pseudoType = PseudoNot;
00249                 else if (value == "nth-child(")
00250                     _pseudoType = PseudoNthChild;
00251                 else if (value == "nth-last-child(")
00252                     _pseudoType = PseudoNthLastChild;
00253                 else if (value == "nth-of-type(")
00254                     _pseudoType = PseudoNthOfType;
00255                 else if (value == "nth-last-of-type(")
00256                     _pseudoType = PseudoNthLastOfType;
00257                 break;
00258             case 'o':
00259                 if (value == "only-child")
00260                     _pseudoType = PseudoOnlyChild;
00261                 else if (value == "only-of-type")
00262                     _pseudoType = PseudoOnlyOfType;
00263                 break;
00264             case 'r':
00265                 if (value == "root")
00266                     _pseudoType = PseudoRoot;
00267                 break;
00268             case 's':
00269                 if (value == "selection") {
00270                     _pseudoType = PseudoSelection;
00271                     element = true;
00272                 }
00273                 break;
00274             case 't':
00275                 if (value == "target")
00276                     _pseudoType = PseudoTarget;
00277                 break;
00278             case 'v':
00279                 if (value == "visited")
00280                     _pseudoType = PseudoVisited;
00281                 break;
00282         }
00283     }
00284     if (match == PseudoClass && element)
00285         if (!compat) _pseudoType = PseudoOther;
00286         else match = PseudoElement;
00287     else
00288     if (match == PseudoElement && !element)
00289         _pseudoType = PseudoOther;
00290 }
00291 
00292 
00293 bool CSSSelector::operator == ( const CSSSelector &other ) const
00294 {
00295     const CSSSelector *sel1 = this;
00296     const CSSSelector *sel2 = &other;
00297 
00298     while ( sel1 && sel2 ) {
00299         //assert(sel1->_pseudoType != PseudoNotParsed);
00300         //assert(sel2->_pseudoType != PseudoNotParsed);
00301     if ( sel1->tag != sel2->tag || sel1->attr != sel2->attr ||
00302          sel1->relation != sel2->relation || sel1->match != sel2->match ||
00303          sel1->nonCSSHint != sel2->nonCSSHint ||
00304          sel1->value != sel2->value ||
00305              sel1->pseudoType() != sel2->pseudoType() ||
00306              sel1->string_arg != sel2->string_arg)
00307         return false;
00308     sel1 = sel1->tagHistory;
00309     sel2 = sel2->tagHistory;
00310     }
00311     if ( sel1 || sel2 )
00312     return false;
00313     return true;
00314 }
00315 
00316 DOMString CSSSelector::selectorText() const
00317 {
00318     // FIXME: Support namespaces when dumping the selector text.  This requires preserving
00319     // the original namespace prefix used. Ugh. -dwh
00320     DOMString str;
00321     const CSSSelector* cs = this;
00322     Q_UINT16 tag = localNamePart(cs->tag);
00323     if (tag == anyLocalName && cs->match == CSSSelector::None)
00324         str = "*";
00325     else if (tag != anyLocalName)
00326         str = getTagName( cs->tag );
00327 
00328     const CSSSelector* op = 0;
00329     while (true) {
00330         if ( cs->attr == ATTR_ID && cs->match == CSSSelector::Id )
00331         {
00332             str += "#";
00333             str += cs->value;
00334         }
00335         else if ( cs->match == CSSSelector::Class )
00336         {
00337             str += ".";
00338             str += cs->value;
00339         }
00340         else if ( cs->match == CSSSelector::PseudoClass )
00341         {
00342             str += ":";
00343             str += cs->value;
00344             if (!cs->string_arg.isEmpty()) { // e.g :nth-child(...)
00345                 str += cs->string_arg;
00346                 str += ")";
00347             } else if (cs->simpleSelector && !op) { // :not(...)
00348                 op = cs;
00349                 cs = cs->simpleSelector;
00350                 continue;
00351             }
00352         }
00353         else if ( cs->match == CSSSelector::PseudoElement )
00354         {
00355             str += "::";
00356             str += cs->value;
00357         }
00358         // optional attribute
00359         else if ( cs->attr ) {
00360             DOMString attrName = getAttrName( cs->attr );
00361             str += "[";
00362             str += attrName;
00363             switch (cs->match) {
00364             case CSSSelector::Exact:
00365                 str += "=";
00366                 break;
00367             case CSSSelector::Set:
00368                 break;
00369             case CSSSelector::List:
00370                 str += "~=";
00371                 break;
00372             case CSSSelector::Hyphen:
00373                 str += "|=";
00374                 break;
00375             case CSSSelector::Begin:
00376                 str += "^=";
00377                 break;
00378             case CSSSelector::End:
00379                 str += "$=";
00380                 break;
00381             case CSSSelector::Contain:
00382                 str += "*=";
00383                 break;
00384             default:
00385                 kdWarning(6080) << "Unhandled case in CSSStyleRuleImpl::selectorText : match=" << cs->match << endl;
00386             }
00387             if (cs->match != CSSSelector::Set) {
00388                 str += "\"";
00389                 str += cs->value;
00390                 str += "\"";
00391             }
00392             str += "]";
00393         }
00394         if (op && !cs->tagHistory) {
00395             cs=op;
00396             op=0;
00397             str += ")";
00398         }
00399 
00400         if ((cs->relation != CSSSelector::SubSelector && !op) || !cs->tagHistory)
00401             break;
00402         cs = cs->tagHistory;
00403     }
00404 
00405     if ( cs->tagHistory ) {
00406         DOMString tagHistoryText = cs->tagHistory->selectorText();
00407         if ( cs->relation == DirectAdjacent )
00408             str = tagHistoryText + " + " + str;
00409         else if ( cs->relation == IndirectAdjacent )
00410             str = tagHistoryText + " ~ " + str;
00411         else if ( cs->relation == Child )
00412             str = tagHistoryText + " > " + str;
00413         else // Descendant
00414             str = tagHistoryText + " " + str;
00415     }
00416     return str;
00417 }
00418 
00419 // ----------------------------------------------------------------------------
KDE Home | KDE Accessibility Home | Description of Access Keys