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 "Node.h" 31 #include "SecurityOrigin.h" 32 #include "TextEncoding.h" 33 #include <wtf/Deque.h> 34 35 namespace WebCore { 36 37 CSSStyleSheet::CSSStyleSheet(CSSStyleSheet* parentSheet, const String& href, const KURL& baseURL, const String& charset) 38 : StyleSheet(parentSheet, href, baseURL) 39 , m_doc(parentSheet ? parentSheet->doc() : 0) 40 , m_namespaces(0) 41 , m_charset(charset) 42 , m_loadCompleted(false) 43 , m_strictParsing(!parentSheet || parentSheet->useStrictParsing()) 44 , m_isUserStyleSheet(parentSheet ? parentSheet->isUserStyleSheet() : false) 45 , m_hasSyntacticallyValidCSSHeader(true) 46 { 47 } 48 49 CSSStyleSheet::CSSStyleSheet(Node* parentNode, const String& href, const KURL& baseURL, const String& charset) 50 : StyleSheet(parentNode, href, baseURL) 51 , m_doc(parentNode->document()) 52 , m_namespaces(0) 53 , m_charset(charset) 54 , m_loadCompleted(false) 55 , m_strictParsing(false) 56 , m_isUserStyleSheet(false) 57 , m_hasSyntacticallyValidCSSHeader(true) 58 { 59 } 60 61 CSSStyleSheet::CSSStyleSheet(CSSRule* ownerRule, const String& href, const KURL& baseURL, const String& charset) 62 : StyleSheet(ownerRule, href, baseURL) 63 , m_namespaces(0) 64 , m_charset(charset) 65 , m_loadCompleted(false) 66 , m_strictParsing(!ownerRule || ownerRule->useStrictParsing()) 67 , m_hasSyntacticallyValidCSSHeader(true) 68 { 69 CSSStyleSheet* parentSheet = ownerRule ? ownerRule->parentStyleSheet() : 0; 70 m_doc = parentSheet ? parentSheet->doc() : 0; 71 m_isUserStyleSheet = parentSheet ? parentSheet->isUserStyleSheet() : false; 72 } 73 74 CSSStyleSheet::~CSSStyleSheet() 75 { 76 delete m_namespaces; 77 } 78 79 CSSRule *CSSStyleSheet::ownerRule() const 80 { 81 return (parent() && parent()->isRule()) ? static_cast<CSSRule*>(parent()) : 0; 82 } 83 84 unsigned CSSStyleSheet::insertRule(const String& rule, unsigned index, ExceptionCode& ec) 85 { 86 ec = 0; 87 if (index > length()) { 88 ec = INDEX_SIZE_ERR; 89 return 0; 90 } 91 CSSParser p(useStrictParsing()); 92 RefPtr<CSSRule> r = p.parseRule(this, rule); 93 94 if (!r) { 95 ec = SYNTAX_ERR; 96 return 0; 97 } 98 99 // ### 100 // HIERARCHY_REQUEST_ERR: Raised if the rule cannot be inserted at the specified index e.g. if an 101 //@import rule is inserted after a standard rule set or other at-rule. 102 insert(index, r.release()); 103 104 styleSheetChanged(); 105 106 return index; 107 } 108 109 int CSSStyleSheet::addRule(const String& selector, const String& style, int index, ExceptionCode& ec) 110 { 111 insertRule(selector + " { " + style + " }", index, ec); 112 113 // As per Microsoft documentation, always return -1. 114 return -1; 115 } 116 117 int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionCode& ec) 118 { 119 return addRule(selector, style, length(), ec); 120 } 121 122 123 PassRefPtr<CSSRuleList> CSSStyleSheet::cssRules(bool omitCharsetRules) 124 { 125 if (doc() && !doc()->securityOrigin()->canRequest(baseURL())) 126 return 0; 127 return CSSRuleList::create(this, omitCharsetRules); 128 } 129 130 void CSSStyleSheet::deleteRule(unsigned index, ExceptionCode& ec) 131 { 132 if (index >= length()) { 133 ec = INDEX_SIZE_ERR; 134 return; 135 } 136 137 ec = 0; 138 remove(index); 139 styleSheetChanged(); 140 } 141 142 void CSSStyleSheet::addNamespace(CSSParser* p, const AtomicString& prefix, const AtomicString& uri) 143 { 144 if (uri.isNull()) 145 return; 146 147 m_namespaces = new CSSNamespace(prefix, uri, m_namespaces); 148 149 if (prefix.isEmpty()) 150 // Set the default namespace on the parser so that selectors that omit namespace info will 151 // be able to pick it up easily. 152 p->m_defaultNamespace = uri; 153 } 154 155 const AtomicString& CSSStyleSheet::determineNamespace(const AtomicString& prefix) 156 { 157 if (prefix.isNull()) 158 return nullAtom; // No namespace. If an element/attribute has a namespace, we won't match it. 159 if (prefix == starAtom) 160 return starAtom; // We'll match any namespace. 161 if (m_namespaces) { 162 CSSNamespace* ns = m_namespaces->namespaceForPrefix(prefix); 163 if (ns) 164 return ns->uri(); 165 } 166 return nullAtom; // Assume we wont match any namespaces. 167 } 168 169 bool CSSStyleSheet::parseString(const String &string, bool strict) 170 { 171 setStrictParsing(strict); 172 CSSParser p(strict); 173 p.parseSheet(this, string); 174 return true; 175 } 176 177 bool CSSStyleSheet::isLoading() 178 { 179 unsigned len = length(); 180 for (unsigned i = 0; i < len; ++i) { 181 StyleBase* rule = item(i); 182 if (rule->isImportRule() && static_cast<CSSImportRule*>(rule)->isLoading()) 183 return true; 184 } 185 return false; 186 } 187 188 void CSSStyleSheet::checkLoaded() 189 { 190 if (isLoading()) 191 return; 192 if (parent()) 193 parent()->checkLoaded(); 194 195 // Avoid |this| being deleted by scripts that run via HTMLTokenizer::executeScriptsWaitingForStylesheets(). 196 // See <rdar://problem/6622300>. 197 RefPtr<CSSStyleSheet> protector(this); 198 m_loadCompleted = ownerNode() ? ownerNode()->sheetLoaded() : true; 199 } 200 201 void CSSStyleSheet::styleSheetChanged() 202 { 203 StyleBase* root = this; 204 while (StyleBase* parent = root->parent()) 205 root = parent; 206 Document* documentToUpdate = root->isCSSStyleSheet() ? static_cast<CSSStyleSheet*>(root)->doc() : 0; 207 208 /* FIXME: We don't need to do everything updateStyleSelector does, 209 * basically we just need to recreate the document's selector with the 210 * already existing style sheets. 211 */ 212 if (documentToUpdate) 213 documentToUpdate->updateStyleSelector(); 214 } 215 216 KURL CSSStyleSheet::completeURL(const String& url) const 217 { 218 // Always return a null URL when passed a null string. 219 // FIXME: Should we change the KURL constructor to have this behavior? 220 // See also Document::completeURL(const String&) 221 if (url.isNull() || m_charset.isEmpty()) 222 return StyleSheet::completeURL(url); 223 const TextEncoding encoding = TextEncoding(m_charset); 224 return KURL(baseURL(), url, encoding); 225 } 226 227 void CSSStyleSheet::addSubresourceStyleURLs(ListHashSet<KURL>& urls) 228 { 229 Deque<CSSStyleSheet*> styleSheetQueue; 230 styleSheetQueue.append(this); 231 232 while (!styleSheetQueue.isEmpty()) { 233 CSSStyleSheet* styleSheet = styleSheetQueue.first(); 234 styleSheetQueue.removeFirst(); 235 236 for (unsigned i = 0; i < styleSheet->length(); ++i) { 237 StyleBase* styleBase = styleSheet->item(i); 238 if (!styleBase->isRule()) 239 continue; 240 241 CSSRule* rule = static_cast<CSSRule*>(styleBase); 242 if (rule->isImportRule()) { 243 if (CSSStyleSheet* ruleStyleSheet = static_cast<CSSImportRule*>(rule)->styleSheet()) 244 styleSheetQueue.append(ruleStyleSheet); 245 } 246 rule->addSubresourceStyleURLs(urls); 247 } 248 } 249 } 250 251 } 252