2 * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2004, 2006, 2007, 2012 Apple Inc. All rights reserved.
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.
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.
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.
22 #include "core/css/CSSStyleSheet.h"
24 #include "HTMLNames.h"
26 #include "bindings/v8/ExceptionState.h"
27 #include "bindings/v8/V8PerIsolateData.h"
28 #include "core/css/CSSCharsetRule.h"
29 #include "core/css/CSSImportRule.h"
30 #include "core/css/parser/BisonCSSParser.h"
31 #include "core/css/CSSRuleList.h"
32 #include "core/css/MediaList.h"
33 #include "core/css/StyleRule.h"
34 #include "core/css/StyleSheetContents.h"
35 #include "core/dom/Document.h"
36 #include "core/dom/ExceptionCode.h"
37 #include "core/dom/Node.h"
38 #include "core/frame/ContentSecurityPolicy.h"
39 #include "core/frame/UseCounter.h"
40 #include "core/inspector/InspectorInstrumentation.h"
41 #include "platform/weborigin/SecurityOrigin.h"
42 #include "wtf/text/StringBuilder.h"
46 class StyleSheetCSSRuleList FINAL : public CSSRuleList {
48 StyleSheetCSSRuleList(CSSStyleSheet* sheet) : m_styleSheet(sheet) { }
51 virtual void ref() OVERRIDE { m_styleSheet->ref(); }
52 virtual void deref() OVERRIDE { m_styleSheet->deref(); }
54 virtual unsigned length() const OVERRIDE { return m_styleSheet->length(); }
55 virtual CSSRule* item(unsigned index) const OVERRIDE { return m_styleSheet->item(index); }
57 virtual CSSStyleSheet* styleSheet() const OVERRIDE { return m_styleSheet; }
59 CSSStyleSheet* m_styleSheet;
63 static bool isAcceptableCSSStyleSheetParent(Node* parentNode)
65 // Only these nodes can be parents of StyleSheets, and they need to call clearOwnerNode() when moved out of document.
67 || parentNode->isDocumentNode()
68 || parentNode->hasTagName(HTMLNames::linkTag)
69 || parentNode->hasTagName(HTMLNames::styleTag)
70 || parentNode->hasTagName(SVGNames::styleTag)
71 || parentNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE;
75 PassRefPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtr<StyleSheetContents> sheet, CSSImportRule* ownerRule)
77 return adoptRef(new CSSStyleSheet(sheet, ownerRule));
80 PassRefPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtr<StyleSheetContents> sheet, Node* ownerNode)
82 return adoptRef(new CSSStyleSheet(sheet, ownerNode, false, TextPosition::minimumPosition()));
85 PassRefPtr<CSSStyleSheet> CSSStyleSheet::createInline(PassRefPtr<StyleSheetContents> sheet, Node* ownerNode, const TextPosition& startPosition)
88 return adoptRef(new CSSStyleSheet(sheet, ownerNode, true, startPosition));
91 PassRefPtr<CSSStyleSheet> CSSStyleSheet::createInline(Node* ownerNode, const KURL& baseURL, const TextPosition& startPosition, const String& encoding)
93 CSSParserContext parserContext(ownerNode->document(), 0, baseURL, encoding);
94 RefPtr<StyleSheetContents> sheet = StyleSheetContents::create(baseURL.string(), parserContext);
95 return adoptRef(new CSSStyleSheet(sheet.release(), ownerNode, true, startPosition));
98 CSSStyleSheet::CSSStyleSheet(PassRefPtr<StyleSheetContents> contents, CSSImportRule* ownerRule)
99 : m_contents(contents)
100 , m_isInlineStylesheet(false)
101 , m_isDisabled(false)
103 , m_ownerRule(ownerRule)
104 , m_startPosition(TextPosition::minimumPosition())
105 , m_loadCompleted(false)
107 m_contents->registerClient(this);
110 CSSStyleSheet::CSSStyleSheet(PassRefPtr<StyleSheetContents> contents, Node* ownerNode, bool isInlineStylesheet, const TextPosition& startPosition)
111 : m_contents(contents)
112 , m_isInlineStylesheet(isInlineStylesheet)
113 , m_isDisabled(false)
114 , m_ownerNode(ownerNode)
116 , m_startPosition(startPosition)
117 , m_loadCompleted(false)
119 ASSERT(isAcceptableCSSStyleSheetParent(ownerNode));
120 m_contents->registerClient(this);
123 CSSStyleSheet::~CSSStyleSheet()
125 // For style rules outside the document, .parentStyleSheet can become null even if the style rule
126 // is still observable from JavaScript. This matches the behavior of .parentNode for nodes, but
127 // it's not ideal because it makes the CSSOM's behavior depend on the timing of garbage collection.
128 for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
129 if (m_childRuleCSSOMWrappers[i])
130 m_childRuleCSSOMWrappers[i]->setParentStyleSheet(0);
133 if (m_mediaCSSOMWrapper)
134 m_mediaCSSOMWrapper->clearParentStyleSheet();
136 m_contents->unregisterClient(this);
139 void CSSStyleSheet::willMutateRules()
141 InspectorInstrumentation::willMutateRules(this);
142 // If we are the only client it is safe to mutate.
143 if (m_contents->hasOneClient() && !m_contents->isInMemoryCache()) {
144 m_contents->clearRuleSet();
145 if (m_contents->maybeCacheable())
146 StyleEngine::removeSheet(m_contents.get());
147 m_contents->setMutable();
150 // Only cacheable stylesheets should have multiple clients.
151 ASSERT(m_contents->isCacheable());
154 m_contents->unregisterClient(this);
155 m_contents = m_contents->copy();
156 m_contents->registerClient(this);
158 m_contents->setMutable();
160 // Any existing CSSOM wrappers need to be connected to the copied child rules.
161 reattachChildRuleCSSOMWrappers();
164 void CSSStyleSheet::didMutateRules()
166 ASSERT(m_contents->isMutable());
167 ASSERT(m_contents->hasOneClient());
169 InspectorInstrumentation::didMutateRules(this);
170 didMutate(PartialRuleUpdate);
173 void CSSStyleSheet::didMutate(StyleSheetUpdateType updateType)
175 Document* owner = ownerDocument();
179 // Need FullStyleUpdate when insertRule or deleteRule,
180 // because StyleSheetCollection::analyzeStyleSheetChange cannot detect partial rule update.
181 StyleResolverUpdateMode updateMode = updateType != PartialRuleUpdate ? AnalyzedStyleUpdate : FullStyleUpdate;
182 owner->modifiedStyleSheet(this, RecalcStyleDeferred, updateMode);
185 void CSSStyleSheet::reattachChildRuleCSSOMWrappers()
187 for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
188 if (!m_childRuleCSSOMWrappers[i])
190 m_childRuleCSSOMWrappers[i]->reattach(m_contents->ruleAt(i));
194 void CSSStyleSheet::setDisabled(bool disabled)
196 if (disabled == m_isDisabled)
198 m_isDisabled = disabled;
203 void CSSStyleSheet::setMediaQueries(PassRefPtr<MediaQuerySet> mediaQueries)
205 m_mediaQueries = mediaQueries;
206 if (m_mediaCSSOMWrapper && m_mediaQueries)
207 m_mediaCSSOMWrapper->reattach(m_mediaQueries.get());
209 // Add warning message to inspector whenever dpi/dpcm values are used for "screen" media.
210 reportMediaQueryWarningIfNeeded(ownerDocument(), m_mediaQueries.get());
213 unsigned CSSStyleSheet::length() const
215 return m_contents->ruleCount();
218 CSSRule* CSSStyleSheet::item(unsigned index)
220 unsigned ruleCount = length();
221 if (index >= ruleCount)
224 if (m_childRuleCSSOMWrappers.isEmpty())
225 m_childRuleCSSOMWrappers.grow(ruleCount);
226 ASSERT(m_childRuleCSSOMWrappers.size() == ruleCount);
228 RefPtr<CSSRule>& cssRule = m_childRuleCSSOMWrappers[index];
230 if (index == 0 && m_contents->hasCharsetRule()) {
231 ASSERT(!m_contents->ruleAt(0));
232 cssRule = CSSCharsetRule::create(this, m_contents->encodingFromCharsetRule());
234 cssRule = m_contents->ruleAt(index)->createCSSOMWrapper(this);
236 return cssRule.get();
239 bool CSSStyleSheet::canAccessRules() const
241 if (m_isInlineStylesheet)
243 KURL baseURL = m_contents->baseURL();
244 if (baseURL.isEmpty())
246 Document* document = ownerDocument();
249 if (document->securityOrigin()->canRequest(baseURL))
254 PassRefPtr<CSSRuleList> CSSStyleSheet::rules()
256 if (!canAccessRules())
259 RefPtr<StaticCSSRuleList> nonCharsetRules = StaticCSSRuleList::create();
260 unsigned ruleCount = length();
261 for (unsigned i = 0; i < ruleCount; ++i) {
262 CSSRule* rule = item(i);
263 if (rule->type() == CSSRule::CHARSET_RULE)
265 nonCharsetRules->rules().append(rule);
267 return nonCharsetRules.release();
270 unsigned CSSStyleSheet::insertRule(const String& ruleString, unsigned index, ExceptionState& exceptionState)
272 ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount());
274 if (Document* document = ownerDocument()) {
275 if (!document->contentSecurityPolicy()->allowStyleEval()) {
276 exceptionState.throwSecurityError(document->contentSecurityPolicy()->styleEvalDisabledErrorMessage());
281 if (index > length()) {
282 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is larger than the maximum index (" + String::number(length()) + ").");
285 CSSParserContext context(m_contents->parserContext(), UseCounter::getFrom(this));
286 BisonCSSParser p(context);
287 RefPtr<StyleRuleBase> rule = p.parseRule(m_contents.get(), ruleString);
290 exceptionState.throwDOMException(SyntaxError, "Failed to parse the rule '" + ruleString + "'.");
293 RuleMutationScope mutationScope(this);
295 bool success = m_contents->wrapperInsertRule(rule, index);
297 exceptionState.throwDOMException(HierarchyRequestError, "Failed to insert the rule.");
300 if (!m_childRuleCSSOMWrappers.isEmpty())
301 m_childRuleCSSOMWrappers.insert(index, RefPtr<CSSRule>());
306 unsigned CSSStyleSheet::insertRule(const String& rule, ExceptionState& exceptionState)
308 UseCounter::countDeprecation(activeExecutionContext(V8PerIsolateData::mainThreadIsolate()), UseCounter::CSSStyleSheetInsertRuleOptionalArg);
309 return insertRule(rule, 0, exceptionState);
312 void CSSStyleSheet::deleteRule(unsigned index, ExceptionState& exceptionState)
314 ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount());
316 if (index >= length()) {
317 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is larger than the maximum index (" + String::number(length() - 1) + ").");
320 RuleMutationScope mutationScope(this);
322 m_contents->wrapperDeleteRule(index);
324 if (!m_childRuleCSSOMWrappers.isEmpty()) {
325 if (m_childRuleCSSOMWrappers[index])
326 m_childRuleCSSOMWrappers[index]->setParentStyleSheet(0);
327 m_childRuleCSSOMWrappers.remove(index);
331 int CSSStyleSheet::addRule(const String& selector, const String& style, int index, ExceptionState& exceptionState)
334 text.append(selector);
335 text.appendLiteral(" { ");
337 if (!style.isEmpty())
340 insertRule(text.toString(), index, exceptionState);
342 // As per Microsoft documentation, always return -1.
346 int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionState& exceptionState)
348 return addRule(selector, style, length(), exceptionState);
352 PassRefPtr<CSSRuleList> CSSStyleSheet::cssRules()
354 if (!canAccessRules())
356 if (!m_ruleListCSSOMWrapper)
357 m_ruleListCSSOMWrapper = adoptPtr(new StyleSheetCSSRuleList(this));
358 return m_ruleListCSSOMWrapper.get();
361 String CSSStyleSheet::href() const
363 return m_contents->originalURL();
366 KURL CSSStyleSheet::baseURL() const
368 return m_contents->baseURL();
371 bool CSSStyleSheet::isLoading() const
373 return m_contents->isLoading();
376 MediaList* CSSStyleSheet::media() const
381 if (!m_mediaCSSOMWrapper)
382 m_mediaCSSOMWrapper = MediaList::create(m_mediaQueries.get(), const_cast<CSSStyleSheet*>(this));
383 return m_mediaCSSOMWrapper.get();
386 CSSStyleSheet* CSSStyleSheet::parentStyleSheet() const
388 return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0;
391 Document* CSSStyleSheet::ownerDocument() const
393 const CSSStyleSheet* root = this;
394 while (root->parentStyleSheet())
395 root = root->parentStyleSheet();
396 return root->ownerNode() ? &root->ownerNode()->document() : 0;
399 void CSSStyleSheet::clearChildRuleCSSOMWrappers()
401 m_childRuleCSSOMWrappers.clear();
404 bool CSSStyleSheet::sheetLoaded()
406 m_loadCompleted = m_ownerNode->sheetLoaded();
407 return m_loadCompleted;
410 void CSSStyleSheet::startLoadingDynamicSheet()
412 m_loadCompleted = false;
413 m_ownerNode->startLoadingDynamicSheet();