You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/khtml/css/css_base.cpp

420 lines
13 KiB

/*
* This file is part of the DOM implementation for KDE.
*
* Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
* 1999 Waldo Bastian (bastian@kde.org)
* 2001 Andreas Schlapbach (schlpbch@iam.unibe.ch)
* 2001-2003 Dirk Mueller (mueller@kde.org)
* 2002 Apple Computer, Inc.
* 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 <assert.h>
#include <kdebug.h>
#include "css_base.h"
#ifdef CSS_DEBUG
#include "cssproperties.h"
#endif
#include "css_stylesheetimpl.h"
#include "xml/dom_docimpl.h"
#include "misc/htmlhashes.h"
#include "css_valueimpl.h"
using namespace DOM;
void StyleBaseImpl::checkLoaded() const
{
if(m_parent) m_parent->checkLoaded();
}
StyleSheetImpl* StyleBaseImpl::stylesheet()
{
StyleBaseImpl* b = this;
while(b && !b->isStyleSheet())
b = b->m_parent;
return static_cast<StyleSheetImpl *>(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()->getDocument()->baseURL();
}
void StyleBaseImpl::setParsedValue(int propId, const CSSValueImpl *parsedValue,
bool important, bool nonCSSHint, TQPtrList<CSSProperty> *propList)
{
TQPtrListIterator<CSSProperty> propIt(*propList);
propIt.toLast(); // just remove the top one - not sure what should happen if we have multiple instances of the property
while (propIt.current() &&
( propIt.current()->m_id != propId || propIt.current()->nonCSSHint != nonCSSHint ||
propIt.current()->m_important != important) )
--propIt;
if (propIt.current())
propList->removeRef(propIt.current());
CSSProperty *prop = new CSSProperty();
prop->m_id = propId;
prop->setValue((CSSValueImpl *) parsedValue);
prop->m_important = important;
prop->nonCSSHint = nonCSSHint;
propList->append(prop);
#ifdef CSS_DEBUG
kdDebug( 6080 ) << "added property: " << getPropertyName(propId).string()
// non implemented yet << ", value: " << parsedValue->cssText().string()
<< " important: " << prop->m_important
<< " nonCSS: " << prop->nonCSSHint << endl;
#endif
}
// ------------------------------------------------------------------------------
StyleListImpl::~StyleListImpl()
{
StyleBaseImpl *n;
if(!m_lstChildren) return;
for( n = m_lstChildren->first(); n != 0; n = m_lstChildren->next() )
{
n->setParent(0);
if( !n->refCount() ) delete n;
}
delete m_lstChildren;
}
// --------------------------------------------------------------------------------
void CSSSelector::print(void)
{
kdDebug( 6080 ) << "[Selector: tag = " << TQString::number(tag,16) << ", attr = \"" << attr << "\", match = \"" << match
<< "\" value = \"" << value.string().latin1() << "\" relation = " << (int)relation
<< "]" << endl;
if ( tagHistory )
tagHistory->print();
kdDebug( 6080 ) << " specificity = " << specificity() << endl;
}
unsigned int CSSSelector::specificity() const
{
if ( nonCSSHint )
return 0;
int s = ((localNamePart(tag) == 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.lower();
switch (value[0]) {
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;
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;
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->tag != sel2->tag || sel1->attr != sel2->attr ||
sel1->relation != sel2->relation || sel1->match != sel2->match ||
sel1->nonCSSHint != sel2->nonCSSHint ||
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;
Q_UINT16 tag = localNamePart(cs->tag);
if (tag == anyLocalName && cs->match == CSSSelector::None)
str = "*";
else if (tag != anyLocalName)
str = getTagName( cs->tag );
const CSSSelector* op = 0;
while (true) {
if ( cs->attr == 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->attr ) {
DOMString attrName = getAttrName( cs->attr );
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:
kdWarning(6080) << "Unhandled case in CSSStyleRuleImpl::selectorText : match=" << cs->match << endl;
}
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 + " + " + str;
else if ( cs->relation == IndirectAdjacent )
str = tagHistoryText + " ~ " + str;
else if ( cs->relation == Child )
str = tagHistoryText + " > " + str;
else // Descendant
str = tagHistoryText + " " + str;
}
return str;
}
// ----------------------------------------------------------------------------