Oilpan: fix build after r202625.
[chromium-blink-merge.git] / third_party / WebKit / Source / core / css / CSSStyleSheet.cpp
blobb7461c1ba81aacc5a02411e654500713662e6df2
1 /*
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.
21 #include "config.h"
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"
45 namespace blink {
47 class StyleSheetCSSRuleList final : public CSSRuleList {
48 public:
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);
60 private:
61 StyleSheetCSSRuleList(CSSStyleSheet* sheet) : m_styleSheet(sheet) { }
63 #if !ENABLE(OILPAN)
64 void ref() override { m_styleSheet->ref(); }
65 void deref() override { m_styleSheet->deref(); }
66 #endif
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;
76 #if ENABLE(ASSERT)
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.
84 return !parentNode
85 || parentNode->isDocumentNode()
86 || isHTMLLinkElement(*parentNode)
87 || isHTMLStyleElement(*parentNode)
88 || isSVGStyleElement(*parentNode)
89 || parentNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE;
91 #endif
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)
105 ASSERT(sheet);
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.
147 #if !ENABLE(OILPAN)
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);
160 #endif
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();
171 return;
173 // Only cacheable stylesheets should have multiple clients.
174 ASSERT(m_contents->isCacheable());
176 // Copy-on-write.
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();
198 if (!owner)
199 return;
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])
211 continue;
212 m_childRuleCSSOMWrappers[i]->reattach(m_contents->ruleAt(i));
216 void CSSStyleSheet::setDisabled(bool disabled)
218 if (disabled == m_isDisabled)
219 return;
220 m_isDisabled = disabled;
222 didMutate();
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)
242 return nullptr;
244 if (m_childRuleCSSOMWrappers.isEmpty())
245 m_childRuleCSSOMWrappers.grow(ruleCount);
246 ASSERT(m_childRuleCSSOMWrappers.size() == ruleCount);
248 RefPtrWillBeMember<CSSRule>& cssRule = m_childRuleCSSOMWrappers[index];
249 if (!cssRule)
250 cssRule = m_contents->ruleAt(index)->createCSSOMWrapper(this);
251 return cssRule.get();
254 void CSSStyleSheet::clearOwnerNode()
256 didMutate(EntireStyleSheetUpdate);
257 if (m_ownerNode)
258 m_contents->unregisterClient(this);
259 m_ownerNode = nullptr;
262 bool CSSStyleSheet::canAccessRules() const
264 if (m_isInlineStylesheet)
265 return true;
266 KURL baseURL = m_contents->baseURL();
267 if (baseURL.isEmpty())
268 return true;
269 Document* document = ownerDocument();
270 if (!document)
271 return true;
272 if (document->securityOrigin()->canRequestNoSuborigin(baseURL))
273 return true;
274 if (m_allowRuleAccessFromOrigin && document->securityOrigin()->canAccessCheckSuborigins(m_allowRuleAccessFromOrigin.get()))
275 return true;
276 return false;
279 PassRefPtrWillBeRawPtr<CSSRuleList> CSSStyleSheet::rules()
281 return cssRules();
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()) + ").");
290 return 0;
292 CSSParserContext context(m_contents->parserContext(), UseCounter::getFrom(this));
293 RefPtrWillBeRawPtr<StyleRuleBase> rule = CSSParser::parseRule(context, m_contents.get(), ruleString);
295 if (!rule) {
296 exceptionState.throwDOMException(SyntaxError, "Failed to parse the rule '" + ruleString + "'.");
297 return 0;
299 RuleMutationScope mutationScope(this);
301 bool success = m_contents->wrapperInsertRule(rule, index);
302 if (!success) {
303 if (rule->isNamespaceRule())
304 exceptionState.throwDOMException(InvalidStateError, "Failed to insert the rule");
305 else
306 exceptionState.throwDOMException(HierarchyRequestError, "Failed to insert the rule.");
307 return 0;
309 if (!m_childRuleCSSOMWrappers.isEmpty())
310 m_childRuleCSSOMWrappers.insert(index, RefPtrWillBeMember<CSSRule>(nullptr));
312 return index;
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) + ").");
327 return;
329 RuleMutationScope mutationScope(this);
331 bool success = m_contents->wrapperDeleteRule(index);
332 if (!success) {
333 exceptionState.throwDOMException(InvalidStateError, "Failed to delete rule");
334 return;
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)
346 StringBuilder text;
347 text.append(selector);
348 text.appendLiteral(" { ");
349 text.append(style);
350 if (!style.isEmpty())
351 text.append(' ');
352 text.append('}');
353 insertRule(text.toString(), index, exceptionState);
355 // As per Microsoft documentation, always return -1.
356 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())
368 return nullptr;
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
391 if (!m_mediaQueries)
392 return nullptr;
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()
424 ASSERT(m_ownerNode);
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)
438 return;
440 m_loadCompleted = completed;
442 if (completed)
443 m_contents->clientLoadCompleted(this);
444 else
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);
460 } // namespace blink