Home | History | Annotate | Download | only in css
      1 /*
      2  * (C) 1999-2003 Lars Knoll (knoll (at) kde.org)
      3  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  */
     20 
     21 #include "config.h"
     22 #include "CSSStyleSheet.h"
     23 
     24 #include "CSSImportRule.h"
     25 #include "CSSNamespace.h"
     26 #include "CSSParser.h"
     27 #include "CSSRuleList.h"
     28 #include "Document.h"
     29 #include "ExceptionCode.h"
     30 #include "HTMLNames.h"
     31 #include "Node.h"
     32 #include "SVGNames.h"
     33 #include "SecurityOrigin.h"
     34 #include "TextEncoding.h"
     35 #include <wtf/Deque.h>
     36 
     37 namespace WebCore {
     38 
     39 #if !ASSERT_DISABLED
     40 static bool isAcceptableCSSStyleSheetParent(Node* parentNode)
     41 {
     42     // Only these nodes can be parents of StyleSheets, and they need to call clearOwnerNode() when moved out of document.
     43     return !parentNode
     44         || parentNode->isDocumentNode()
     45         || parentNode->hasTagName(HTMLNames::linkTag)
     46         || parentNode->hasTagName(HTMLNames::styleTag)
     47 #if ENABLE(SVG)
     48         || parentNode->hasTagName(SVGNames::styleTag)
     49 #endif
     50         || parentNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE;
     51 }
     52 #endif
     53 
     54 CSSStyleSheet::CSSStyleSheet(CSSStyleSheet* parentSheet, const String& href, const KURL& baseURL, const String& charset)
     55     : StyleSheet(parentSheet, href, baseURL)
     56     , m_charset(charset)
     57     , m_loadCompleted(false)
     58     , m_strictParsing(!parentSheet || parentSheet->useStrictParsing())
     59     , m_isUserStyleSheet(parentSheet ? parentSheet->isUserStyleSheet() : false)
     60     , m_hasSyntacticallyValidCSSHeader(true)
     61 {
     62 }
     63 
     64 CSSStyleSheet::CSSStyleSheet(Node* parentNode, const String& href, const KURL& baseURL, const String& charset)
     65     : StyleSheet(parentNode, href, baseURL)
     66     , m_charset(charset)
     67     , m_loadCompleted(false)
     68     , m_strictParsing(false)
     69     , m_isUserStyleSheet(false)
     70     , m_hasSyntacticallyValidCSSHeader(true)
     71 {
     72     ASSERT(isAcceptableCSSStyleSheetParent(parentNode));
     73 }
     74 
     75 CSSStyleSheet::CSSStyleSheet(CSSRule* ownerRule, const String& href, const KURL& baseURL, const String& charset)
     76     : StyleSheet(ownerRule, href, baseURL)
     77     , m_charset(charset)
     78     , m_loadCompleted(false)
     79     , m_strictParsing(!ownerRule || ownerRule->useStrictParsing())
     80     , m_hasSyntacticallyValidCSSHeader(true)
     81 {
     82     CSSStyleSheet* parentSheet = ownerRule ? ownerRule->parentStyleSheet() : 0;
     83     m_isUserStyleSheet = parentSheet ? parentSheet->isUserStyleSheet() : false;
     84 }
     85 
     86 CSSStyleSheet::~CSSStyleSheet()
     87 {
     88 }
     89 
     90 CSSRule *CSSStyleSheet::ownerRule() const
     91 {
     92     return (parent() && parent()->isRule()) ? static_cast<CSSRule*>(parent()) : 0;
     93 }
     94 
     95 unsigned CSSStyleSheet::insertRule(const String& rule, unsigned index, ExceptionCode& ec)
     96 {
     97     ec = 0;
     98     if (index > length()) {
     99         ec = INDEX_SIZE_ERR;
    100         return 0;
    101     }
    102     CSSParser p(useStrictParsing());
    103     RefPtr<CSSRule> r = p.parseRule(this, rule);
    104 
    105     if (!r) {
    106         ec = SYNTAX_ERR;
    107         return 0;
    108     }
    109 
    110     // Throw a HIERARCHY_REQUEST_ERR exception if the rule cannot be inserted at the specified index.  The best
    111     // example of this is an @import rule inserted after regular rules.
    112     if (index > 0) {
    113         if (r->isImportRule()) {
    114             // Check all the rules that come before this one to make sure they are only @charset and @import rules.
    115             for (unsigned i = 0; i < index; ++i) {
    116                 if (!item(i)->isCharsetRule() && !item(i)->isImportRule()) {
    117                     ec = HIERARCHY_REQUEST_ERR;
    118                     return 0;
    119                 }
    120             }
    121         } else if (r->isCharsetRule()) {
    122             // The @charset rule has to come first and there can be only one.
    123             ec = HIERARCHY_REQUEST_ERR;
    124             return 0;
    125         }
    126     }
    127 
    128     insert(index, r.release());
    129 
    130     styleSheetChanged();
    131 
    132     return index;
    133 }
    134 
    135 int CSSStyleSheet::addRule(const String& selector, const String& style, int index, ExceptionCode& ec)
    136 {
    137     insertRule(selector + " { " + style + " }", index, ec);
    138 
    139     // As per Microsoft documentation, always return -1.
    140     return -1;
    141 }
    142 
    143 int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionCode& ec)
    144 {
    145     return addRule(selector, style, length(), ec);
    146 }
    147 
    148 PassRefPtr<CSSRuleList> CSSStyleSheet::cssRules(bool omitCharsetRules)
    149 {
    150     KURL url = finalURL();
    151     if (!url.isEmpty() && document() && !document()->securityOrigin()->canRequest(url))
    152         return 0;
    153     return CSSRuleList::create(this, omitCharsetRules);
    154 }
    155 
    156 void CSSStyleSheet::deleteRule(unsigned index, ExceptionCode& ec)
    157 {
    158     if (index >= length()) {
    159         ec = INDEX_SIZE_ERR;
    160         return;
    161     }
    162 
    163     ec = 0;
    164     item(index)->setParent(0);
    165     remove(index);
    166     styleSheetChanged();
    167 }
    168 
    169 void CSSStyleSheet::addNamespace(CSSParser* p, const AtomicString& prefix, const AtomicString& uri)
    170 {
    171     if (uri.isNull())
    172         return;
    173 
    174     m_namespaces = adoptPtr(new CSSNamespace(prefix, uri, m_namespaces.release()));
    175 
    176     if (prefix.isEmpty())
    177         // Set the default namespace on the parser so that selectors that omit namespace info will
    178         // be able to pick it up easily.
    179         p->m_defaultNamespace = uri;
    180 }
    181 
    182 const AtomicString& CSSStyleSheet::determineNamespace(const AtomicString& prefix)
    183 {
    184     if (prefix.isNull())
    185         return nullAtom; // No namespace. If an element/attribute has a namespace, we won't match it.
    186     if (prefix == starAtom)
    187         return starAtom; // We'll match any namespace.
    188     if (m_namespaces) {
    189         if (CSSNamespace* namespaceForPrefix = m_namespaces->namespaceForPrefix(prefix))
    190             return namespaceForPrefix->uri;
    191     }
    192     return nullAtom; // Assume we won't match any namespaces.
    193 }
    194 
    195 bool CSSStyleSheet::parseString(const String &string, bool strict)
    196 {
    197     return parseStringAtLine(string, strict, 0);
    198 }
    199 
    200 bool CSSStyleSheet::parseStringAtLine(const String& string, bool strict, int startLineNumber)
    201 {
    202     setStrictParsing(strict);
    203     CSSParser p(strict);
    204     p.parseSheet(this, string, startLineNumber);
    205     return true;
    206 }
    207 
    208 bool CSSStyleSheet::isLoading()
    209 {
    210     unsigned len = length();
    211     for (unsigned i = 0; i < len; ++i) {
    212         StyleBase* rule = item(i);
    213         if (rule->isImportRule() && static_cast<CSSImportRule*>(rule)->isLoading())
    214             return true;
    215     }
    216     return false;
    217 }
    218 
    219 void CSSStyleSheet::checkLoaded()
    220 {
    221     if (isLoading())
    222         return;
    223     if (parent())
    224         parent()->checkLoaded();
    225 
    226     // Avoid |this| being deleted by scripts that run via
    227     // ScriptableDocumentParser::executeScriptsWaitingForStylesheets().
    228     // See <rdar://problem/6622300>.
    229     RefPtr<CSSStyleSheet> protector(this);
    230     m_loadCompleted = ownerNode() ? ownerNode()->sheetLoaded() : true;
    231 }
    232 
    233 Document* CSSStyleSheet::document()
    234 {
    235     StyleBase* styleObject = this;
    236     while (styleObject) {
    237         if (styleObject->isCSSStyleSheet()) {
    238             Node* ownerNode = static_cast<CSSStyleSheet*>(styleObject)->ownerNode();
    239             if (ownerNode)
    240                 return ownerNode->document();
    241         }
    242         if (styleObject->isRule())
    243             styleObject = static_cast<CSSRule*>(styleObject)->parentStyleSheet();
    244         else
    245             styleObject = styleObject->parent();
    246     }
    247 
    248     return 0;
    249 }
    250 
    251 void CSSStyleSheet::styleSheetChanged()
    252 {
    253     StyleBase* root = this;
    254     while (StyleBase* parent = root->parent())
    255         root = parent;
    256     Document* documentToUpdate = root->isCSSStyleSheet() ? static_cast<CSSStyleSheet*>(root)->document() : 0;
    257 
    258     /* FIXME: We don't need to do everything updateStyleSelector does,
    259      * basically we just need to recreate the document's selector with the
    260      * already existing style sheets.
    261      */
    262     if (documentToUpdate)
    263         documentToUpdate->styleSelectorChanged(DeferRecalcStyle);
    264 }
    265 
    266 KURL CSSStyleSheet::completeURL(const String& url) const
    267 {
    268     // Always return a null URL when passed a null string.
    269     // FIXME: Should we change the KURL constructor to have this behavior?
    270     // See also Document::completeURL(const String&)
    271     if (url.isNull() || m_charset.isEmpty())
    272         return StyleSheet::completeURL(url);
    273     const TextEncoding encoding = TextEncoding(m_charset);
    274     return KURL(baseURL(), url, encoding);
    275 }
    276 
    277 void CSSStyleSheet::addSubresourceStyleURLs(ListHashSet<KURL>& urls)
    278 {
    279     Deque<CSSStyleSheet*> styleSheetQueue;
    280     styleSheetQueue.append(this);
    281 
    282     while (!styleSheetQueue.isEmpty()) {
    283         CSSStyleSheet* styleSheet = styleSheetQueue.takeFirst();
    284 
    285         for (unsigned i = 0; i < styleSheet->length(); ++i) {
    286             StyleBase* styleBase = styleSheet->item(i);
    287             if (!styleBase->isRule())
    288                 continue;
    289 
    290             CSSRule* rule = static_cast<CSSRule*>(styleBase);
    291             if (rule->isImportRule()) {
    292                 if (CSSStyleSheet* ruleStyleSheet = static_cast<CSSImportRule*>(rule)->styleSheet())
    293                     styleSheetQueue.append(ruleStyleSheet);
    294             }
    295             rule->addSubresourceStyleURLs(urls);
    296         }
    297     }
    298 }
    299 
    300 }
    301