• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@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
isAcceptableCSSStyleSheetParent(Node * parentNode)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 
CSSStyleSheet(CSSStyleSheet * parentSheet,const String & href,const KURL & baseURL,const String & charset)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 
CSSStyleSheet(Node * parentNode,const String & href,const KURL & baseURL,const String & charset)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 
CSSStyleSheet(CSSRule * ownerRule,const String & href,const KURL & baseURL,const String & charset)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 
~CSSStyleSheet()86 CSSStyleSheet::~CSSStyleSheet()
87 {
88 }
89 
ownerRule() const90 CSSRule *CSSStyleSheet::ownerRule() const
91 {
92     return (parent() && parent()->isRule()) ? static_cast<CSSRule*>(parent()) : 0;
93 }
94 
insertRule(const String & rule,unsigned index,ExceptionCode & ec)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 
addRule(const String & selector,const String & style,int index,ExceptionCode & ec)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 
addRule(const String & selector,const String & style,ExceptionCode & ec)143 int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionCode& ec)
144 {
145     return addRule(selector, style, length(), ec);
146 }
147 
cssRules(bool omitCharsetRules)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 
deleteRule(unsigned index,ExceptionCode & ec)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 
addNamespace(CSSParser * p,const AtomicString & prefix,const AtomicString & uri)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 
determineNamespace(const AtomicString & prefix)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 
parseString(const String & string,bool strict)195 bool CSSStyleSheet::parseString(const String &string, bool strict)
196 {
197     return parseStringAtLine(string, strict, 0);
198 }
199 
parseStringAtLine(const String & string,bool strict,int startLineNumber)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 
isLoading()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 
checkLoaded()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 
document()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 
styleSheetChanged()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 
completeURL(const String & url) const266 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 
addSubresourceStyleURLs(ListHashSet<KURL> & urls)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