/** * This file is part of the CSS implementation for KDE. * * Copyright 1999-2003 Lars Knoll (knoll@kde.org) * Copyright 2003-2004 Apple Computer, Inc. * Copyright 2004-2010 Allan Sandfeld Jensen (kde@carewolf.com) * Copyright 2004-2008 Germain Garand (germain@ebooksfrance.org) * Copyright 2008 Vyacheslav Tokarev (tsjoker@gmail.com) * (C) 2005, 2006, 2008 Apple Computer, Inc. * * 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. */ #include "css/cssstyleselector.h" #include "rendering/render_style.h" #include "css/css_stylesheetimpl.h" #include "css/css_ruleimpl.h" #include "css/css_valueimpl.h" #include "css/csshelper.h" #include "css/css_webfont.h" #include "rendering/render_object.h" #include "html/html_documentimpl.h" #include "html/html_elementimpl.h" #include "xml/dom_elementimpl.h" #include "xml/dom_restyler.h" #include "dom/css_rule.h" #include "dom/css_value.h" #include "khtml_global.h" #include "khtmlpart_p.h" using namespace khtml; using namespace DOM; #include "css/cssproperties.h" #include "css/cssvalues.h" #include "css/css_mediaquery.h" #include "misc/khtmllayout.h" #include "khtml_settings.h" #include "misc/helper.h" #include "misc/loader.h" #include "rendering/font.h" #include "khtmlview.h" #include "khtml_part.h" #include #include #include #include #include #include #include #include #include #include // keep in sync with html4.css' #define KHTML_STYLE_VERSION 1 #undef PRELATIVE #undef PABSOLUTE // handle value "inherit" on a default inherited property #define HANDLE_INHERIT_ON_INHERITED_PROPERTY(prop, Prop) \ if (isInherit) \ {\ style->set##Prop(parentStyle->prop());\ return;\ } // handle value "inherit" on a default non-inherited property #define HANDLE_INHERIT_ON_NONINHERITED_PROPERTY(prop, Prop) \ if (isInherit) \ {\ style->setInheritedNoninherited(true);\ style->set##Prop(parentStyle->prop());\ return;\ } #define HANDLE_INITIAL(prop, Prop) \ if (isInitial) \ {\ style->set##Prop(RenderStyle::initial##Prop());\ return;\ } #define HANDLE_INITIAL_AND_INHERIT_ON_NONINHERITED_PROPERTY(prop, Prop) \ HANDLE_INITIAL(prop, Prop) \ else \ HANDLE_INHERIT_ON_NONINHERITED_PROPERTY(prop, Prop) #define HANDLE_INITIAL_AND_INHERIT_ON_INHERITED_PROPERTY(prop, Prop) \ HANDLE_INITIAL(prop, Prop) \ else \ HANDLE_INHERIT_ON_INHERITED_PROPERTY(prop, Prop) // all non-inherited properties #define HANDLE_INHERIT_AND_INITIAL_WITH_VALUE(prop, Prop, Value) \ HANDLE_INHERIT_ON_NONINHERITED_PROPERTY(prop, Prop) \ else if (isInitial) \ {\ style->set##Prop(RenderStyle::initial##Value());\ return;\ } #define HANDLE_BACKGROUND_INHERIT_AND_INITIAL(prop, Prop) \ if (isInherit) { \ BackgroundLayer* currChild = style->accessBackgroundLayers(); \ BackgroundLayer* prevChild = 0; \ const BackgroundLayer* currParent = parentStyle->backgroundLayers(); \ while (currParent && currParent->is##Prop##Set()) { \ if (!currChild) { \ /* Need to make a new layer.*/ \ currChild = new BackgroundLayer(); \ prevChild->setNext(currChild); \ } \ currChild->set##Prop(currParent->prop()); \ prevChild = currChild; \ currChild = prevChild->next(); \ currParent = currParent->next(); \ } \ \ while (currChild) { \ /* Reset any remaining layers to not have the property set. */ \ currChild->clear##Prop(); \ currChild = currChild->next(); \ } \ } else if (isInitial) { \ BackgroundLayer* currChild = style->accessBackgroundLayers(); \ currChild->set##Prop(RenderStyle::initial##Prop()); \ for (currChild = currChild->next(); currChild; currChild = currChild->next()) \ currChild->clear##Prop(); \ } #define HANDLE_BACKGROUND_VALUE(prop, Prop, value) { \ HANDLE_BACKGROUND_INHERIT_AND_INITIAL(prop, Prop) \ else { \ if (!value->isPrimitiveValue() && !value->isValueList()) \ return; \ BackgroundLayer* currChild = style->accessBackgroundLayers(); \ BackgroundLayer* prevChild = 0; \ if (value->isPrimitiveValue()) { \ map##Prop(currChild, value); \ currChild = currChild->next(); \ } \ else { \ /* Walk each value and put it into a layer, creating new layers as needed. */ \ CSSValueListImpl* valueList = static_cast(value); \ for (unsigned int i = 0; i < valueList->length(); i++) { \ if (!currChild) { \ /* Need to make a new layer to hold this value */ \ currChild = new BackgroundLayer(); \ prevChild->setNext(currChild); \ } \ map##Prop(currChild, valueList->item(i)); \ prevChild = currChild; \ currChild = currChild->next(); \ } \ } \ while (currChild) { \ /* Reset all remaining layers to not have the property set. */ \ currChild->clear##Prop(); \ currChild = currChild->next(); \ } \ } } #define HANDLE_INHERIT_COND(propID, prop, Prop) \ if (id == propID) \ {\ style->set##Prop(parentStyle->prop());\ return;\ } #define HANDLE_INHERIT_COND_WITH_BACKUP(propID, prop, propAlt, Prop) \ if (id == propID) { \ if (parentStyle->prop().isValid()) \ style->set##Prop(parentStyle->prop()); \ else \ style->set##Prop(parentStyle->propAlt()); \ return; \ } #define HANDLE_INITIAL_COND(propID, Prop) \ if (id == propID) \ {\ style->set##Prop(RenderStyle::initial##Prop());\ return;\ } #define HANDLE_INITIAL_COND_WITH_VALUE(propID, Prop, Value) \ if (id == propID) \ {\ style->set##Prop(RenderStyle::initial##Value());\ return;\ } namespace khtml { CSSStyleSelectorList *CSSStyleSelector::s_defaultStyle; CSSStyleSelectorList *CSSStyleSelector::s_defaultQuirksStyle; CSSStyleSelectorList *CSSStyleSelector::s_defaultNonCSSHintsStyle; CSSStyleSelectorList *CSSStyleSelector::s_defaultPrintStyle; CSSStyleSheetImpl *CSSStyleSelector::s_defaultSheet; CSSStyleSheetImpl *CSSStyleSelector::s_defaultNonCSSHintsSheet; RenderStyle* CSSStyleSelector::styleNotYetAvailable; CSSStyleSheetImpl *CSSStyleSelector::s_quirksSheet; enum PseudoState { PseudoUnknown, PseudoNone, PseudoLink, PseudoVisited}; static PseudoState pseudoState; CSSStyleSelector::CSSStyleSelector( DocumentImpl* doc, QString userStyleSheet, StyleSheetListImpl *styleSheets, const KUrl &url, bool _strictParsing ) { KHTMLView* view = doc->view(); KHTMLPart* part = doc->part(); m_fontSelector = new CSSFontSelector( doc ); init(part ? part->settings() : 0, doc); strictParsing = _strictParsing; selectors = 0; selectorCache = 0; propertiesBuffer = 0; nextPropertyIndexes = 0; userStyle = 0; userSheet = 0; logicalDpiY = doc->logicalDpiY(); if(logicalDpiY) // this may be null, not everyone uses khtmlview (Niko) computeFontSizes(logicalDpiY, part ? part->fontScaleFactor() : 100); // build a limited default style suitable to evaluation of media queries // containing relative constraints, like "screen and (max-width: 10em)" setupDefaultRootStyle(doc); if (view) m_medium = new MediaQueryEvaluator(view->mediaType(), view->part(), m_rootDefaultStyle); else m_medium = new MediaQueryEvaluator("all", 0, m_rootDefaultStyle); if ( !userStyleSheet.isEmpty() ) { userSheet = new DOM::CSSStyleSheetImpl(doc); userSheet->parseString( DOMString( userStyleSheet ) ); userStyle = new CSSStyleSelectorList(); userStyle->append( userSheet, m_medium, this ); } // add stylesheets from document authorStyle = 0; implicitStyle = 0; foreach (StyleSheetImpl* sh, styleSheets->styleSheets) { if ( sh->isCSSStyleSheet() ) { if ( static_cast(sh)->implicit() ) { if (!implicitStyle) implicitStyle = new CSSStyleSelectorList(); implicitStyle->append( static_cast( sh ), m_medium, this ); } else if ( sh->isCSSStyleSheet() && !sh->disabled()) { if (!authorStyle) authorStyle = new CSSStyleSelectorList(); authorStyle->append( static_cast( sh ), m_medium, this ); } } } buildLists(); //kDebug( 6080 ) << "number of style sheets in document " << authorStyleSheets.count(); //kDebug( 6080 ) << "CSSStyleSelector: author style has " << authorStyle->count() << " elements"; KUrl u = url; u.setQuery( QString() ); u.setRef( QString() ); encodedurl.file = u.url(); int pos = encodedurl.file.lastIndexOf('/'); encodedurl.path = encodedurl.file; if ( pos > 0 ) { encodedurl.path.truncate( pos ); encodedurl.path += '/'; } u.setPath( QString() ); encodedurl.host = u.url(); //kDebug() << "CSSStyleSelector::CSSStyleSelector encoded url " << encodedurl.path; } CSSStyleSelector::CSSStyleSelector( CSSStyleSheetImpl *sheet ) { m_fontSelector = new CSSFontSelector( sheet->doc() ); init(0L, 0L); KHTMLView *view = sheet->doc()->view(); setupDefaultRootStyle(sheet->doc()); if (view) m_medium = new MediaQueryEvaluator(view->mediaType(), view->part(), m_rootDefaultStyle); else m_medium = new MediaQueryEvaluator("screen", 0, m_rootDefaultStyle); if (sheet->implicit()) { implicitStyle = new CSSStyleSelectorList(); implicitStyle->append( sheet, m_medium, this ); } else { authorStyle = new CSSStyleSelectorList(); authorStyle->append( sheet, m_medium, this ); } } void CSSStyleSelector::init(const KHTMLSettings* _settings, DocumentImpl* doc) { element = 0; settings = _settings; logicalDpiY = 0; if(!s_defaultStyle) loadDefaultStyle(settings, doc); defaultStyle = s_defaultStyle; defaultPrintStyle = s_defaultPrintStyle; defaultQuirksStyle = s_defaultQuirksStyle; defaultNonCSSHintsStyle = s_defaultNonCSSHintsStyle; m_rootDefaultStyle = 0; m_medium = 0; } CSSStyleSelector::~CSSStyleSelector() { clearLists(); delete authorStyle; delete implicitStyle; delete userStyle; delete userSheet; delete m_rootDefaultStyle; delete m_medium; delete m_fontSelector; } void CSSStyleSelector::addSheet( CSSStyleSheetImpl *sheet ) { KHTMLView *view = sheet->doc()->view(); setupDefaultRootStyle(sheet->doc()); delete m_medium; m_medium = 0; delete authorStyle; authorStyle = 0; delete implicitStyle; implicitStyle = 0; if (view) m_medium = new MediaQueryEvaluator(view->mediaType(), view->part(), m_rootDefaultStyle); else m_medium = new MediaQueryEvaluator("screen", 0, m_rootDefaultStyle); if (sheet->implicit()) { if (!implicitStyle) implicitStyle = new CSSStyleSelectorList(); implicitStyle->append( sheet, m_medium, this ); } else { if (!authorStyle) authorStyle = new CSSStyleSelectorList(); authorStyle->append( sheet, m_medium, this ); } } void CSSStyleSelector::loadDefaultStyle(const KHTMLSettings *s, DocumentImpl *doc) { if(s_defaultStyle) return; MediaQueryEvaluator screenEval("screen"); MediaQueryEvaluator printEval("print"); { QFile f(KStandardDirs::locate( "data", "khtml/css/html4.css" ) ); f.open(QIODevice::ReadOnly); QByteArray file( f.size()+1, 0 ); int readbytes = f.read( file.data(), f.size() ); f.close(); if ( readbytes >= 0 ) file[readbytes] = '\0'; QString style = QLatin1String( file.data() ); QRegExp checkVersion( "KHTML_STYLE_VERSION:\\s*(\\d+)" ); checkVersion.setMinimal( true ); if (checkVersion.indexIn( style ) == -1 || checkVersion.cap(1).toInt() != KHTML_STYLE_VERSION) { qFatal( "!!!!!!! ERROR !!!!!!! - KHTML default stylesheet version mismatch. Aborting. Check your installation. File used was: %s. Expected STYLE_VERSION %d\n", QFileInfo( f ).absoluteFilePath().toLatin1().data(), KHTML_STYLE_VERSION ); } if(s) style += s->settingsToCSS(); DOMString str(style); s_defaultSheet = new DOM::CSSStyleSheetImpl(doc); s_defaultSheet->parseString( str ); // Collect only strict-mode rules. s_defaultStyle = new CSSStyleSelectorList(); s_defaultStyle->append( s_defaultSheet, &screenEval, doc->styleSelector() ); s_defaultPrintStyle = new CSSStyleSelectorList(); s_defaultPrintStyle->append( s_defaultSheet, &printEval, doc->styleSelector() ); } { QFile f(KStandardDirs::locate( "data", "khtml/css/quirks.css" ) ); f.open(QIODevice::ReadOnly); QByteArray file( f.size()+1, 0 ); int readbytes = f.read( file.data(), f.size() ); f.close(); if ( readbytes >= 0 ) file[readbytes] = '\0'; QString style = QLatin1String( file.data() ); DOMString str(style); s_quirksSheet = new DOM::CSSStyleSheetImpl(doc); s_quirksSheet->parseString( str ); // Collect only quirks-mode rules. s_defaultQuirksStyle = new CSSStyleSelectorList(); s_defaultQuirksStyle->append( s_quirksSheet, &screenEval, doc->styleSelector() ); } { QFile f(KStandardDirs::locate( "data", "khtml/css/presentational.css" ) ); f.open(QIODevice::ReadOnly); QByteArray file( f.size()+1, 0 ); int readbytes = f.read( file.data(), f.size() ); f.close(); if ( readbytes >= 0 ) file[readbytes] = '\0'; QString style = QLatin1String( file.data() ); DOMString str(style); s_defaultNonCSSHintsSheet = new DOM::CSSStyleSheetImpl(doc); s_defaultNonCSSHintsSheet->parseString( str ); s_defaultNonCSSHintsStyle = new CSSStyleSelectorList(); s_defaultNonCSSHintsStyle->append( s_defaultNonCSSHintsSheet, &screenEval, doc->styleSelector() ); } //kDebug() << "CSSStyleSelector: default style has " << defaultStyle->count() << " elements"; } void CSSStyleSelector::clear() { delete s_defaultStyle; delete s_defaultQuirksStyle; delete s_defaultPrintStyle; delete s_defaultNonCSSHintsStyle; delete s_defaultSheet; delete s_defaultNonCSSHintsSheet; delete styleNotYetAvailable; s_defaultStyle = 0; s_defaultQuirksStyle = 0; s_defaultPrintStyle = 0; s_defaultNonCSSHintsStyle = 0; s_defaultSheet = 0; s_defaultNonCSSHintsSheet = 0; styleNotYetAvailable = 0; } void CSSStyleSelector::reparseConfiguration() { // nice leak, but best we can do right now. hopefully it is only rare. s_defaultStyle = 0; s_defaultQuirksStyle = 0; s_defaultPrintStyle = 0; s_defaultNonCSSHintsStyle = 0; s_defaultSheet = 0; } #define MAXFONTSIZES 8 void CSSStyleSelector::computeFontSizes(int logicalDpiY, int zoomFactor) { computeFontSizesFor(logicalDpiY, zoomFactor, m_fontSizes, false); computeFontSizesFor(logicalDpiY, zoomFactor, m_fixedFontSizes, true); } void CSSStyleSelector::computeFontSizesFor(int logicalDpiY, int zoomFactor, QVector& fontSizes, bool isFixed) { Q_UNUSED( isFixed ); const float toPix = qMax(logicalDpiY, 96) / 72.0f; fontSizes.resize( MAXFONTSIZES ); float scale = 1.0; static const float fontFactors[] = {3.0f/5.0f, 3.0f/4.0f, 8.0f/9.0f, 1.0f, 6.0f/5.0f, 3.0f/2.0f, 2.0f, 3.0f}; static const float smallFontFactors[] = {3.0f/4.0f, 5.0f/6.0f, 8.0f/9.0f, 1.0f, 6.0f/5.0f, 3.0f/2.0f, 2.0f, 3.0f}; float mediumFontSize, factor; if (!khtml::printpainter) { scale *= zoomFactor / 100.0; mediumFontSize = settings->mediumFontSize() * toPix; m_minFontSize = settings->minFontSize() * toPix; } else { // ### depending on something / configurable ? mediumFontSize = 12; m_minFontSize = 6; } const float* factors = scale*mediumFontSize >= 12.5 ? fontFactors : smallFontFactors; for ( int i = 0; i < MAXFONTSIZES; i++ ) { factor = scale*factors[i]; fontSizes[i] = qMax(qRound(mediumFontSize * factor), m_minFontSize); //kDebug( 6080 ) << "index: " << i << " factor: " << factors[i] << " font pix size: " << qMax(qRound(mediumFontSize*factor), m_minFontSize); } } #undef MAXFONTSIZES RenderStyle* CSSStyleSelector::locateSimilarStyle() { ElementImpl *s=0, *t=0, *c=0; if (!element) return 0; // Check previous siblings. unsigned count = 0; NodeImpl* n = element; do { for (n = n->previousSibling(); n && !n->isElementNode(); n = n->previousSibling()); if (!n) break; ElementImpl *e = static_cast(n); if (++count > 10) break; if (!s) s = e; // sibling match if (e->id() != element->id()) continue; if (!t) t = e; // tag match if (element->hasClass()) { if (!e->hasClass()) continue; const DOMString& class1 = element->getAttribute(ATTR_CLASS); const DOMString& class2 = e->getAttribute(ATTR_CLASS); if (class1 != class2) continue; } if (!c) c = e; // class match break; } while(true); // if possible return sibling that matches tag and class if (c && c->renderer() && c->renderer()->style()) return c->renderer()->style(); // second best: return sibling that matches tag if (t && t->renderer() && t->renderer()->style()) return t->renderer()->style(); // third best: return sibling element if (s && s->renderer() && s->renderer()->style()) return s->renderer()->style(); // last attempt: return parent element NodeImpl* p = element->parentNode(); if (p && p->renderer()) return p->renderer()->style(); return 0; } static inline void bubbleSort( CSSOrderedProperty **b, CSSOrderedProperty **e ) { while( b < e ) { bool swapped = false; CSSOrderedProperty **y = e+1; CSSOrderedProperty **x = e; CSSOrderedProperty **swappedPos = 0; do { if ( !((**(--x)) < (**(--y))) ) { swapped = true; swappedPos = x; CSSOrderedProperty *tmp = *y; *y = *x; *x = tmp; } } while( x != b ); if ( !swapped ) break; b = swappedPos + 1; } } RenderStyle *CSSStyleSelector::styleForElement(ElementImpl *e, RenderStyle* fallbackParentStyle) { if (!e->document()->haveStylesheetsLoaded() || !e->document()->view()) { if (!styleNotYetAvailable) { styleNotYetAvailable = new RenderStyle(); styleNotYetAvailable->setDisplay(NONE); styleNotYetAvailable->ref(); } return styleNotYetAvailable; } // set some variables we will need pseudoState = PseudoUnknown; element = e; parentNode = e->parentNode(); parentStyle = ( parentNode && parentNode->renderer()) ? parentNode->renderer()->style() : fallbackParentStyle; view = element->document()->view(); part = view->part(); settings = part->settings(); logicalDpiY = element->document()->logicalDpiY(); // reset dynamic DOM dependencies e->document()->dynamicDomRestyler().resetDependencies(e); style = new RenderStyle(); if( parentStyle ) style->inheritFrom( parentStyle ); else parentStyle = style; const RenderObject *docElementRenderer = e->document()->documentElement()->renderer(); m_rootStyle = docElementRenderer ? docElementRenderer->style() : m_rootDefaultStyle; // try to sort out most style rules as early as possible. quint16 cssTagId = localNamePart(element->id()); int smatch = 0; int schecked = 0; // do aggressive selection of selectors to check // instead of going over whole constructed list, // skip selectors that won't match for sure (e.g. with different id or class) QVarLengthArray selectorsForCheck; // add unknown selectors to always be checked for (unsigned int i = otherSelector; i < selectors_size; i = nextSimilarSelector[i]) selectorsForCheck.append(i); // check if we got class attribute on element: add selectors with it to the list if (e->hasClass()) { const ClassNames& classNames = element->classNames(); for (unsigned int i = 0; i < classNames.size(); ++i) { WTF::HashMap::iterator it = classSelector.find((quintptr)classNames[i].impl()); if (it != classSelector.end()) for (unsigned int j = it->second; j < selectors_size; j = nextSimilarSelector[j]) selectorsForCheck.append(j); } } // check if we got id attribute on element: add selectors with it to the list DOMStringImpl* idValue = element->getAttributeImplById(ATTR_ID); if (idValue && idValue->length()) { bool caseSensitive = (e->document()->htmlMode() == DocumentImpl::XHtml) || strictParsing; AtomicString elementId = caseSensitive ? idValue : idValue->lower(); WTF::HashMap::iterator it = idSelector.find((quintptr)elementId.impl()); if (it != idSelector.end()) for (unsigned int j = it->second; j < selectors_size; j = nextSimilarSelector[j]) selectorsForCheck.append(j); } // add all selectors with given local tag WTF::HashMap::iterator it = tagSelector.find(cssTagId); if (it != tagSelector.end()) for (unsigned int j = it->second; j < selectors_size; j = nextSimilarSelector[j]) selectorsForCheck.append(j); // build per-element cache summaries. prepareToMatchElement(element, true); propsToApply.clear(); pseudoProps.clear(); // now go over selectors that we prepared for check // selectors yet in random order, so we store only matched selector indexes to sort after unsigned amountOfMatchedSelectors = 0; for (int k = 0; k < selectorsForCheck.size(); ++k) { unsigned i = selectorsForCheck[k]; quint16 tag = selectors[i]->tagLocalName.id(); if (cssTagId == tag || tag == anyLocalName) { ++schecked; checkSelector(i, e); if (selectorCache[i].state == Applies || selectorCache[i].state == AppliesPseudo) { selectorsForCheck[amountOfMatchedSelectors++] = i; } } else selectorCache[i].state = Invalid; } // sort only matched selectors and then collect properties qSort(selectorsForCheck.data(), selectorsForCheck.data() + amountOfMatchedSelectors); for (unsigned k = 0; k < amountOfMatchedSelectors; ++k) { unsigned i = selectorsForCheck[k]; if (selectorCache[i].state == Applies) { ++smatch; for (unsigned p = selectorCache[i].firstPropertyIndex; p < properties_size; p = nextPropertyIndexes[p]) propsToApply.append(propertiesBuffer + p); } else if (selectorCache[i].state == AppliesPseudo) { for (unsigned p = selectorCache[i].firstPropertyIndex; p < properties_size; p = nextPropertyIndexes[p]) { pseudoProps.append(propertiesBuffer + p); propertiesBuffer[p].pseudoId = (RenderStyle::PseudoId) selectors[i]->pseudoId; } } } // clear selectorsForCheck, it shouldn't be used after selectorsForCheck.clear(); // Inline style declarations, after all others. // Non-css hints from presentational attributes will also be collected here // receiving the proper priority so has to cascade from before author rules (cf.CSS 2.1-6.4.4). addInlineDeclarations(e); // qDebug( "styleForElement( %s )", e->tagName().string().toLatin1().constData() ); // qDebug( "%d selectors, %d checked, %d match, %d properties ( of %d )", // selectors_size, schecked, smatch, numPropsToApply, properties_size ); if (propsToApply.size()) bubbleSort(propsToApply.data(), propsToApply.data() + propsToApply.size() - 1); if (pseudoProps.size()) bubbleSort(pseudoProps.data(), pseudoProps.data() + pseudoProps.size() - 1); // we can't apply style rules without a view() and a part. This // tends to happen on delayed destruction of widget Renderobjects if ( part ) { fontDirty = false; if (propsToApply.size()) { for (unsigned int i = 0; i < propsToApply.size(); ++i) { if ( fontDirty && propsToApply[i]->priority >= (1 << 30) ) { // we are past the font properties, time to update to the // correct font style->htmlFont().update(logicalDpiY); fontDirty = false; } DOM::CSSProperty *prop = propsToApply[i]->prop; // if (prop->m_id == CSS_PROP__KONQ_USER_INPUT) kDebug(6080) << "El: "<nodeName().string() << " user-input: "<<((CSSPrimitiveValueImpl *)prop->value())->getIdent(); // if (prop->m_id == CSS_PROP_TEXT_TRANSFORM) kDebug(6080) << "El: "<nodeName().string(); applyRule( prop->m_id, prop->value() ); } if ( fontDirty ) { style->htmlFont().update(logicalDpiY); } } // Clean up our style object's display and text decorations (among other fixups). adjustRenderStyle(style, e); if (pseudoProps.size()) { fontDirty = false; //qDebug("%d applying %d pseudo props", e->cssTagId(), pseudoProps->count() ); for (unsigned int i = 0; i < pseudoProps.size(); ++i) { if ( fontDirty && pseudoProps[i]->priority >= (1 << 30) ) { // we are past the font properties, time to update to the // correct font //We have to do this for all pseudo styles RenderStyle *pseudoStyle = style->pseudoStyle; while ( pseudoStyle ) { pseudoStyle->htmlFont().update( logicalDpiY ); pseudoStyle = pseudoStyle->pseudoStyle; } fontDirty = false; } RenderStyle *pseudoStyle; pseudoStyle = style->getPseudoStyle(pseudoProps[i]->pseudoId); if (!pseudoStyle) { pseudoStyle = style->addPseudoStyle(pseudoProps[i]->pseudoId); if (pseudoStyle) pseudoStyle->inheritFrom( style ); } RenderStyle* oldStyle = style; RenderStyle* oldParentStyle = parentStyle; parentStyle = style; style = pseudoStyle; if ( pseudoStyle ) { DOM::CSSProperty *prop = pseudoProps[i]->prop; applyRule( prop->m_id, prop->value() ); } style = oldStyle; parentStyle = oldParentStyle; } if ( fontDirty ) { RenderStyle *pseudoStyle = style->pseudoStyle; while ( pseudoStyle ) { pseudoStyle->htmlFont().update( logicalDpiY ); pseudoStyle = pseudoStyle->pseudoStyle; } } } } // Now adjust all our pseudo-styles. RenderStyle *pseudoStyle = style->pseudoStyle; while (pseudoStyle) { adjustRenderStyle(pseudoStyle, 0); pseudoStyle = pseudoStyle->pseudoStyle; } // Try and share or partially share the style with our siblings RenderStyle *commonStyle = locateSimilarStyle(); if (commonStyle) style->compactWith(commonStyle); // Now return the style. return style; } void CSSStyleSelector::prepareToMatchElement(DOM::ElementImpl* e, bool withDeps) { rememberDependencies = withDeps; element = e; // build caches for element so it could be used in heuristic for descendant selectors // go up the tree and cache possible tags, classes and ids tagCache.clear(); idCache.clear(); classCache.clear(); ElementImpl* current = element; while (true) { NodeImpl* parent = current->parentNode(); if (!parent || !parent->isElementNode()) break; current = static_cast(parent); if (current->hasClass()) { const ClassNames& classNames = current->classNames(); for (unsigned i = 0; i < classNames.size(); ++i) classCache.add((quintptr)classNames[i].impl()); } DOMStringImpl* idValue = current->getAttributeImplById(ATTR_ID); if (idValue && idValue->length()) { bool caseSensitive = (current->document()->htmlMode() == DocumentImpl::XHtml) || strictParsing; AtomicString currentId = caseSensitive ? idValue : idValue->lower(); // though currentId is local and could be deleted from DOMStringImpl cache right away // don't care about that, cause selector values are stable and only they will be checked later idCache.add((quintptr)currentId.impl()); } tagCache.add(localNamePart(current->id())); } } void CSSStyleSelector::adjustRenderStyle(RenderStyle* style, DOM::ElementImpl *e) { // Cache our original display. style->setOriginalDisplay(style->display()); if (style->display() != NONE) { // If we have a that specifies a float property, in quirks mode we just drop the float // property. // Sites also commonly use display:inline/block on s and s. In quirks mode we force // these tags to retain their display types. if (!strictParsing && e) { if (e->id() == ID_TD) { style->setDisplay(TABLE_CELL); style->setFloating(FNONE); } else if (e->id() == ID_TABLE) style->setDisplay(style->isDisplayInlineType() ? INLINE_TABLE : TABLE); } // Table headers with a text-align of auto will change the text-align to center. if (e && e->id() == ID_TH && style->textAlign() == TAAUTO) style->setTextAlign(CENTER); // Mutate the display to BLOCK or TABLE for certain cases, e.g., if someone attempts to // position or float an inline, compact, or run-in. Cache the original display, since it // may be needed for positioned elements that have to compute their static normal flow // positions. We also force inline-level roots to be block-level. if (style->display() != BLOCK && style->display() != TABLE /*&& style->display() != BOX*/ && (style->position() == PABSOLUTE || style->position() == PFIXED || style->floating() != FNONE || (e && e->document()->documentElement() == e))) { if (style->display() == INLINE_TABLE) style->setDisplay(TABLE); // else if (style->display() == INLINE_BOX) // style->setDisplay(BOX); else if (style->display() == LIST_ITEM) { // It is a WinIE bug that floated list items lose their bullets, so we'll emulate the quirk, // but only in quirks mode. if (!strictParsing && style->floating() != FNONE) style->setDisplay(BLOCK); } else style->setDisplay(BLOCK); } else if (e && e->id() == ID_BUTTON && style->isOriginalDisplayInlineType()) { //