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 "bindings/core/v8/ExceptionState.h"
25 #include "bindings/core/v8/V8Binding.h"
26 #include "bindings/core/v8/V8PerIsolateData.h"
27 #include "core/HTMLNames.h"
28 #include "core/SVGNames.h"
29 #include "core/css/CSSImportRule.h"
30 #include "core/css/CSSRuleList.h"
31 #include "core/css/MediaList.h"
32 #include "core/css/StyleRule.h"
33 #include "core/css/StyleSheetContents.h"
34 #include "core/css/parser/CSSParser.h"
35 #include "core/dom/Document.h"
36 #include "core/dom/ExceptionCode.h"
37 #include "core/dom/Node.h"
38 #include "core/frame/UseCounter.h"
39 #include "core/html/HTMLStyleElement.h"
40 #include "core/inspector/InspectorInstrumentation.h"
41 #include "core/svg/SVGStyleElement.h"
42 #include "platform/weborigin/SecurityOrigin.h"
43 #include "wtf/text/StringBuilder.h"
47 class StyleSheetCSSRuleList final
: public CSSRuleList
{
49 static PassOwnPtrWillBeRawPtr
<StyleSheetCSSRuleList
> create(CSSStyleSheet
* sheet
)
51 return adoptPtrWillBeNoop(new StyleSheetCSSRuleList(sheet
));
54 DEFINE_INLINE_VIRTUAL_TRACE()
56 visitor
->trace(m_styleSheet
);
57 CSSRuleList::trace(visitor
);
61 StyleSheetCSSRuleList(CSSStyleSheet
* sheet
) : m_styleSheet(sheet
) { }
64 void ref() override
{ m_styleSheet
->ref(); }
65 void deref() override
{ m_styleSheet
->deref(); }
68 unsigned length() const override
{ return m_styleSheet
->length(); }
69 CSSRule
* item(unsigned index
) const override
{ return m_styleSheet
->item(index
); }
71 CSSStyleSheet
* styleSheet() const override
{ return m_styleSheet
; }
73 RawPtrWillBeMember
<CSSStyleSheet
> m_styleSheet
;
77 static bool isAcceptableCSSStyleSheetParent(Node
* parentNode
)
79 // Only these nodes can be parents of StyleSheets, and they need to call
80 // clearOwnerNode() when moved out of document.
81 // Destruction of the style sheet counts as being "moved out of the
82 // document", but only in the non-oilpan version of blink. I.e. don't call
83 // clearOwnerNode() in the owner's destructor in oilpan.
85 || parentNode
->isDocumentNode()
86 || isHTMLLinkElement(*parentNode
)
87 || isHTMLStyleElement(*parentNode
)
88 || isSVGStyleElement(*parentNode
)
89 || parentNode
->nodeType() == Node::PROCESSING_INSTRUCTION_NODE
;
93 PassRefPtrWillBeRawPtr
<CSSStyleSheet
> CSSStyleSheet::create(PassRefPtrWillBeRawPtr
<StyleSheetContents
> sheet
, CSSImportRule
* ownerRule
)
95 return adoptRefWillBeNoop(new CSSStyleSheet(sheet
, ownerRule
));
98 PassRefPtrWillBeRawPtr
<CSSStyleSheet
> CSSStyleSheet::create(PassRefPtrWillBeRawPtr
<StyleSheetContents
> sheet
, Node
* ownerNode
)
100 return adoptRefWillBeNoop(new CSSStyleSheet(sheet
, ownerNode
, false, TextPosition::minimumPosition()));
103 PassRefPtrWillBeRawPtr
<CSSStyleSheet
> CSSStyleSheet::createInline(PassRefPtrWillBeRawPtr
<StyleSheetContents
> sheet
, Node
* ownerNode
, const TextPosition
& startPosition
)
106 return adoptRefWillBeNoop(new CSSStyleSheet(sheet
, ownerNode
, true, startPosition
));
109 PassRefPtrWillBeRawPtr
<CSSStyleSheet
> CSSStyleSheet::createInline(Node
* ownerNode
, const KURL
& baseURL
, const TextPosition
& startPosition
, const String
& encoding
)
111 CSSParserContext
parserContext(ownerNode
->document(), 0, baseURL
, encoding
);
112 RefPtrWillBeRawPtr
<StyleSheetContents
> sheet
= StyleSheetContents::create(baseURL
.string(), parserContext
);
113 return adoptRefWillBeNoop(new CSSStyleSheet(sheet
.release(), ownerNode
, true, startPosition
));
116 CSSStyleSheet::CSSStyleSheet(PassRefPtrWillBeRawPtr
<StyleSheetContents
> contents
, CSSImportRule
* ownerRule
)
117 : m_contents(contents
)
118 , m_isInlineStylesheet(false)
119 , m_isDisabled(false)
120 , m_ownerNode(nullptr)
121 , m_ownerRule(ownerRule
)
122 , m_startPosition(TextPosition::minimumPosition())
123 , m_loadCompleted(false)
125 m_contents
->registerClient(this);
128 CSSStyleSheet::CSSStyleSheet(PassRefPtrWillBeRawPtr
<StyleSheetContents
> contents
, Node
* ownerNode
, bool isInlineStylesheet
, const TextPosition
& startPosition
)
129 : m_contents(contents
)
130 , m_isInlineStylesheet(isInlineStylesheet
)
131 , m_isDisabled(false)
132 , m_ownerNode(ownerNode
)
133 , m_ownerRule(nullptr)
134 , m_startPosition(startPosition
)
135 , m_loadCompleted(false)
137 ASSERT(isAcceptableCSSStyleSheetParent(ownerNode
));
138 m_contents
->registerClient(this);
141 CSSStyleSheet::~CSSStyleSheet()
143 // With oilpan the parent style sheet pointer is strong and the sheet and
144 // its RuleCSSOMWrappers die together and we don't need to clear them here.
145 // Also with oilpan the StyleSheetContents client pointers are weak and
146 // therefore do not need to be cleared here.
148 // For style rules outside the document, .parentStyleSheet can become null even if the style rule
149 // is still observable from JavaScript. This matches the behavior of .parentNode for nodes, but
150 // it's not ideal because it makes the CSSOM's behavior depend on the timing of garbage collection.
151 for (unsigned i
= 0; i
< m_childRuleCSSOMWrappers
.size(); ++i
) {
152 if (m_childRuleCSSOMWrappers
[i
])
153 m_childRuleCSSOMWrappers
[i
]->setParentStyleSheet(0);
156 if (m_mediaCSSOMWrapper
)
157 m_mediaCSSOMWrapper
->clearParentStyleSheet();
159 m_contents
->unregisterClient(this);
163 void CSSStyleSheet::willMutateRules()
165 // If we are the only client it is safe to mutate.
166 if (m_contents
->clientSize() <= 1 && !m_contents
->isInMemoryCache()) {
167 m_contents
->clearRuleSet();
168 if (Document
* document
= ownerDocument())
169 m_contents
->removeSheetFromCache(document
);
170 m_contents
->setMutable();
173 // Only cacheable stylesheets should have multiple clients.
174 ASSERT(m_contents
->isCacheable());
177 m_contents
->unregisterClient(this);
178 m_contents
= m_contents
->copy();
179 m_contents
->registerClient(this);
181 m_contents
->setMutable();
183 // Any existing CSSOM wrappers need to be connected to the copied child rules.
184 reattachChildRuleCSSOMWrappers();
187 void CSSStyleSheet::didMutateRules()
189 ASSERT(m_contents
->isMutable());
190 ASSERT(m_contents
->clientSize() <= 1);
192 didMutate(PartialRuleUpdate
);
195 void CSSStyleSheet::didMutate(StyleSheetUpdateType updateType
)
197 Document
* owner
= ownerDocument();
201 // Need FullStyleUpdate when insertRule or deleteRule,
202 // because StyleSheetCollection::analyzeStyleSheetChange cannot detect partial rule update.
203 StyleResolverUpdateMode updateMode
= updateType
!= PartialRuleUpdate
? AnalyzedStyleUpdate
: FullStyleUpdate
;
204 owner
->modifiedStyleSheet(this, updateMode
);
207 void CSSStyleSheet::reattachChildRuleCSSOMWrappers()
209 for (unsigned i
= 0; i
< m_childRuleCSSOMWrappers
.size(); ++i
) {
210 if (!m_childRuleCSSOMWrappers
[i
])
212 m_childRuleCSSOMWrappers
[i
]->reattach(m_contents
->ruleAt(i
));
216 void CSSStyleSheet::setDisabled(bool disabled
)
218 if (disabled
== m_isDisabled
)
220 m_isDisabled
= disabled
;
225 void CSSStyleSheet::setMediaQueries(PassRefPtrWillBeRawPtr
<MediaQuerySet
> mediaQueries
)
227 m_mediaQueries
= mediaQueries
;
228 if (m_mediaCSSOMWrapper
&& m_mediaQueries
)
229 m_mediaCSSOMWrapper
->reattach(m_mediaQueries
.get());
233 unsigned CSSStyleSheet::length() const
235 return m_contents
->ruleCount();
238 CSSRule
* CSSStyleSheet::item(unsigned index
)
240 unsigned ruleCount
= length();
241 if (index
>= ruleCount
)
244 if (m_childRuleCSSOMWrappers
.isEmpty())
245 m_childRuleCSSOMWrappers
.grow(ruleCount
);
246 ASSERT(m_childRuleCSSOMWrappers
.size() == ruleCount
);
248 RefPtrWillBeMember
<CSSRule
>& cssRule
= m_childRuleCSSOMWrappers
[index
];
250 cssRule
= m_contents
->ruleAt(index
)->createCSSOMWrapper(this);
251 return cssRule
.get();
254 void CSSStyleSheet::clearOwnerNode()
256 didMutate(EntireStyleSheetUpdate
);
258 m_contents
->unregisterClient(this);
259 m_ownerNode
= nullptr;
262 bool CSSStyleSheet::canAccessRules() const
264 if (m_isInlineStylesheet
)
266 KURL baseURL
= m_contents
->baseURL();
267 if (baseURL
.isEmpty())
269 Document
* document
= ownerDocument();
272 if (document
->securityOrigin()->canRequestNoSuborigin(baseURL
))
274 if (m_allowRuleAccessFromOrigin
&& document
->securityOrigin()->canAccessCheckSuborigins(m_allowRuleAccessFromOrigin
.get()))
279 PassRefPtrWillBeRawPtr
<CSSRuleList
> CSSStyleSheet::rules()
284 unsigned CSSStyleSheet::insertRule(const String
& ruleString
, unsigned index
, ExceptionState
& exceptionState
)
286 ASSERT(m_childRuleCSSOMWrappers
.isEmpty() || m_childRuleCSSOMWrappers
.size() == m_contents
->ruleCount());
288 if (index
> length()) {
289 exceptionState
.throwDOMException(IndexSizeError
, "The index provided (" + String::number(index
) + ") is larger than the maximum index (" + String::number(length()) + ").");
292 CSSParserContext
context(m_contents
->parserContext(), UseCounter::getFrom(this));
293 RefPtrWillBeRawPtr
<StyleRuleBase
> rule
= CSSParser::parseRule(context
, m_contents
.get(), ruleString
);
296 exceptionState
.throwDOMException(SyntaxError
, "Failed to parse the rule '" + ruleString
+ "'.");
299 RuleMutationScope
mutationScope(this);
301 bool success
= m_contents
->wrapperInsertRule(rule
, index
);
303 if (rule
->isNamespaceRule())
304 exceptionState
.throwDOMException(InvalidStateError
, "Failed to insert the rule");
306 exceptionState
.throwDOMException(HierarchyRequestError
, "Failed to insert the rule.");
309 if (!m_childRuleCSSOMWrappers
.isEmpty())
310 m_childRuleCSSOMWrappers
.insert(index
, RefPtrWillBeMember
<CSSRule
>(nullptr));
315 unsigned CSSStyleSheet::insertRule(const String
& rule
, ExceptionState
& exceptionState
)
317 UseCounter::countDeprecation(callingExecutionContext(V8PerIsolateData::mainThreadIsolate()), UseCounter::CSSStyleSheetInsertRuleOptionalArg
);
318 return insertRule(rule
, 0, exceptionState
);
321 void CSSStyleSheet::deleteRule(unsigned index
, ExceptionState
& exceptionState
)
323 ASSERT(m_childRuleCSSOMWrappers
.isEmpty() || m_childRuleCSSOMWrappers
.size() == m_contents
->ruleCount());
325 if (index
>= length()) {
326 exceptionState
.throwDOMException(IndexSizeError
, "The index provided (" + String::number(index
) + ") is larger than the maximum index (" + String::number(length() - 1) + ").");
329 RuleMutationScope
mutationScope(this);
331 bool success
= m_contents
->wrapperDeleteRule(index
);
333 exceptionState
.throwDOMException(InvalidStateError
, "Failed to delete rule");
337 if (!m_childRuleCSSOMWrappers
.isEmpty()) {
338 if (m_childRuleCSSOMWrappers
[index
])
339 m_childRuleCSSOMWrappers
[index
]->setParentStyleSheet(0);
340 m_childRuleCSSOMWrappers
.remove(index
);
344 int CSSStyleSheet::addRule(const String
& selector
, const String
& style
, int index
, ExceptionState
& exceptionState
)
347 text
.append(selector
);
348 text
.appendLiteral(" { ");
350 if (!style
.isEmpty())
353 insertRule(text
.toString(), index
, exceptionState
);
355 // As per Microsoft documentation, always return -1.
359 int CSSStyleSheet::addRule(const String
& selector
, const String
& style
, ExceptionState
& exceptionState
)
361 return addRule(selector
, style
, length(), exceptionState
);
365 PassRefPtrWillBeRawPtr
<CSSRuleList
> CSSStyleSheet::cssRules()
367 if (!canAccessRules())
369 if (!m_ruleListCSSOMWrapper
)
370 m_ruleListCSSOMWrapper
= StyleSheetCSSRuleList::create(this);
371 return m_ruleListCSSOMWrapper
.get();
374 String
CSSStyleSheet::href() const
376 return m_contents
->originalURL();
379 KURL
CSSStyleSheet::baseURL() const
381 return m_contents
->baseURL();
384 bool CSSStyleSheet::isLoading() const
386 return m_contents
->isLoading();
389 MediaList
* CSSStyleSheet::media() const
394 if (!m_mediaCSSOMWrapper
)
395 m_mediaCSSOMWrapper
= MediaList::create(m_mediaQueries
.get(), const_cast<CSSStyleSheet
*>(this));
396 return m_mediaCSSOMWrapper
.get();
399 CSSStyleSheet
* CSSStyleSheet::parentStyleSheet() const
401 return m_ownerRule
? m_ownerRule
->parentStyleSheet() : nullptr;
404 Document
* CSSStyleSheet::ownerDocument() const
406 const CSSStyleSheet
* root
= this;
407 while (root
->parentStyleSheet())
408 root
= root
->parentStyleSheet();
409 return root
->ownerNode() ? &root
->ownerNode()->document() : nullptr;
412 void CSSStyleSheet::setAllowRuleAccessFromOrigin(PassRefPtr
<SecurityOrigin
> allowedOrigin
)
414 m_allowRuleAccessFromOrigin
= allowedOrigin
;
417 void CSSStyleSheet::clearChildRuleCSSOMWrappers()
419 m_childRuleCSSOMWrappers
.clear();
422 bool CSSStyleSheet::sheetLoaded()
425 setLoadCompleted(m_ownerNode
->sheetLoaded());
426 return m_loadCompleted
;
429 void CSSStyleSheet::startLoadingDynamicSheet()
431 setLoadCompleted(false);
432 m_ownerNode
->startLoadingDynamicSheet();
435 void CSSStyleSheet::setLoadCompleted(bool completed
)
437 if (completed
== m_loadCompleted
)
440 m_loadCompleted
= completed
;
443 m_contents
->clientLoadCompleted(this);
445 m_contents
->clientLoadStarted(this);
448 DEFINE_TRACE(CSSStyleSheet
)
450 visitor
->trace(m_contents
);
451 visitor
->trace(m_mediaQueries
);
452 visitor
->trace(m_ownerNode
);
453 visitor
->trace(m_ownerRule
);
454 visitor
->trace(m_mediaCSSOMWrapper
);
455 visitor
->trace(m_childRuleCSSOMWrappers
);
456 visitor
->trace(m_ruleListCSSOMWrapper
);
457 StyleSheet::trace(visitor
);