Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / css / CSSStyleSheet.cpp
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2006, 2007, 2012, 2013 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 "AuthorStyleSheets.h"
25 #include "CSSCharsetRule.h"
26 #include "CSSFontFaceRule.h"
27 #include "CSSImportRule.h"
28 #include "CSSKeyframesRule.h"
29 #include "CSSParser.h"
30 #include "CSSRuleList.h"
31 #include "CSSStyleRule.h"
32 #include "CachedCSSStyleSheet.h"
33 #include "Document.h"
34 #include "ExceptionCode.h"
35 #include "ExtensionStyleSheets.h"
36 #include "HTMLNames.h"
37 #include "HTMLStyleElement.h"
38 #include "MediaList.h"
39 #include "Node.h"
40 #include "SVGNames.h"
41 #include "SVGStyleElement.h"
42 #include "SecurityOrigin.h"
43 #include "StyleResolver.h"
44 #include "StyleRule.h"
45 #include "StyleSheetContents.h"
46 #include <wtf/text/StringBuilder.h>
47
48 namespace WebCore {
49
50 class StyleSheetCSSRuleList : public CSSRuleList {
51 public:
52     StyleSheetCSSRuleList(CSSStyleSheet* sheet) : m_styleSheet(sheet) { }
53     
54 private:
55     virtual void ref() { m_styleSheet->ref(); }
56     virtual void deref() { m_styleSheet->deref(); }
57     
58     virtual unsigned length() const { return m_styleSheet->length(); }
59     virtual CSSRule* item(unsigned index) const { return m_styleSheet->item(index); }
60     
61     virtual CSSStyleSheet* styleSheet() const { return m_styleSheet; }
62
63     CSSStyleSheet* m_styleSheet;
64 };
65
66 #if !ASSERT_DISABLED
67 static bool isAcceptableCSSStyleSheetParent(Node* parentNode)
68 {
69     // Only these nodes can be parents of StyleSheets, and they need to call clearOwnerNode() when moved out of document.
70     return !parentNode
71         || parentNode->isDocumentNode()
72         || is<HTMLLinkElement>(*parentNode)
73         || is<HTMLStyleElement>(*parentNode)
74         || is<SVGStyleElement>(*parentNode)
75         || parentNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE;
76 }
77 #endif
78
79 Ref<CSSStyleSheet> CSSStyleSheet::create(Ref<StyleSheetContents>&& sheet, CSSImportRule* ownerRule)
80
81     return adoptRef(*new CSSStyleSheet(WTFMove(sheet), ownerRule));
82 }
83
84 Ref<CSSStyleSheet> CSSStyleSheet::create(Ref<StyleSheetContents>&& sheet, Node* ownerNode)
85
86     return adoptRef(*new CSSStyleSheet(WTFMove(sheet), ownerNode, TextPosition::minimumPosition(), false));
87 }
88
89 Ref<CSSStyleSheet> CSSStyleSheet::createInline(Node& ownerNode, const URL& baseURL, const TextPosition& startPosition, const String& encoding)
90 {
91     CSSParserContext parserContext(ownerNode.document(), baseURL, encoding);
92     return adoptRef(*new CSSStyleSheet(StyleSheetContents::create(baseURL.string(), parserContext), &ownerNode, startPosition, true));
93 }
94
95 CSSStyleSheet::CSSStyleSheet(Ref<StyleSheetContents>&& contents, CSSImportRule* ownerRule)
96     : m_contents(WTFMove(contents))
97     , m_isInlineStylesheet(false)
98     , m_isDisabled(false)
99     , m_mutatedRules(false)
100     , m_ownerNode(0)
101     , m_ownerRule(ownerRule)
102     , m_startPosition()
103 {
104     m_contents->registerClient(this);
105 }
106
107 CSSStyleSheet::CSSStyleSheet(Ref<StyleSheetContents>&& contents, Node* ownerNode, const TextPosition& startPosition, bool isInlineStylesheet)
108     : m_contents(WTFMove(contents))
109     , m_isInlineStylesheet(isInlineStylesheet)
110     , m_isDisabled(false)
111     , m_mutatedRules(false)
112     , m_ownerNode(ownerNode)
113     , m_ownerRule(0)
114     , m_startPosition(startPosition)
115 {
116     ASSERT(isAcceptableCSSStyleSheetParent(ownerNode));
117     m_contents->registerClient(this);
118 }
119
120 CSSStyleSheet::~CSSStyleSheet()
121 {
122     // For style rules outside the document, .parentStyleSheet can become null even if the style rule
123     // is still observable from JavaScript. This matches the behavior of .parentNode for nodes, but
124     // it's not ideal because it makes the CSSOM's behavior depend on the timing of garbage collection.
125     for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
126         if (m_childRuleCSSOMWrappers[i])
127             m_childRuleCSSOMWrappers[i]->setParentStyleSheet(0);
128     }
129     if (m_mediaCSSOMWrapper)
130         m_mediaCSSOMWrapper->clearParentStyleSheet();
131
132     m_contents->unregisterClient(this);
133 }
134
135 CSSStyleSheet::WhetherContentsWereClonedForMutation CSSStyleSheet::willMutateRules()
136 {
137     // If we are the only client it is safe to mutate.
138     if (m_contents->hasOneClient() && !m_contents->isInMemoryCache()) {
139         m_contents->setMutable();
140         return ContentsWereNotClonedForMutation;
141     }
142     // Only cacheable stylesheets should have multiple clients.
143     ASSERT(m_contents->isCacheable());
144
145     // Copy-on-write.
146     m_contents->unregisterClient(this);
147     m_contents = m_contents->copy();
148     m_contents->registerClient(this);
149
150     m_contents->setMutable();
151
152     // Any existing CSSOM wrappers need to be connected to the copied child rules.
153     reattachChildRuleCSSOMWrappers();
154
155     return ContentsWereClonedForMutation;
156 }
157
158 void CSSStyleSheet::didMutateRuleFromCSSStyleDeclaration()
159 {
160     ASSERT(m_contents->isMutable());
161     ASSERT(m_contents->hasOneClient());
162     didMutate();
163 }
164
165 void CSSStyleSheet::didMutateRules(RuleMutationType mutationType, WhetherContentsWereClonedForMutation contentsWereClonedForMutation, StyleRuleKeyframes* insertedKeyframesRule)
166 {
167     ASSERT(m_contents->isMutable());
168     ASSERT(m_contents->hasOneClient());
169
170     Document* owner = ownerDocument();
171     if (!owner)
172         return;
173
174     if (mutationType == RuleInsertion && !contentsWereClonedForMutation && !owner->authorStyleSheets().activeStyleSheetsContains(this)) {
175         if (insertedKeyframesRule) {
176             if (StyleResolver* resolver = owner->styleResolverIfExists())
177                 resolver->addKeyframeStyle(insertedKeyframesRule);
178             return;
179         }
180         owner->scheduleOptimizedStyleSheetUpdate();
181         return;
182     }
183
184     owner->styleResolverChanged(DeferRecalcStyle);
185
186     m_mutatedRules = true;
187 }
188
189 void CSSStyleSheet::didMutate()
190 {
191     Document* owner = ownerDocument();
192     if (!owner)
193         return;
194     owner->styleResolverChanged(DeferRecalcStyle);
195 }
196
197 void CSSStyleSheet::clearOwnerNode()
198 {
199     Document* owner = ownerDocument();
200     m_ownerNode = 0;
201     if (!owner)
202         return;
203     owner->styleResolverChanged(DeferRecalcStyleIfNeeded);
204 }
205
206 void CSSStyleSheet::reattachChildRuleCSSOMWrappers()
207 {
208     for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
209         if (!m_childRuleCSSOMWrappers[i])
210             continue;
211         m_childRuleCSSOMWrappers[i]->reattach(*m_contents->ruleAt(i));
212     }
213 }
214
215 void CSSStyleSheet::setDisabled(bool disabled)
216
217     if (disabled == m_isDisabled)
218         return;
219     m_isDisabled = disabled;
220
221     didMutate();
222 }
223
224 void CSSStyleSheet::setMediaQueries(PassRefPtr<MediaQuerySet> mediaQueries)
225 {
226     m_mediaQueries = mediaQueries;
227     if (m_mediaCSSOMWrapper && m_mediaQueries)
228         m_mediaCSSOMWrapper->reattach(m_mediaQueries.get());
229
230 #if ENABLE(RESOLUTION_MEDIA_QUERY)
231     // Add warning message to inspector whenever dpi/dpcm values are used for "screen" media.
232     reportMediaQueryWarningIfNeeded(ownerDocument(), m_mediaQueries.get());
233 #endif
234 }
235
236 unsigned CSSStyleSheet::length() const
237 {
238     return m_contents->ruleCount();
239 }
240
241 CSSRule* CSSStyleSheet::item(unsigned index)
242 {
243     unsigned ruleCount = length();
244     if (index >= ruleCount)
245         return 0;
246
247     if (m_childRuleCSSOMWrappers.isEmpty())
248         m_childRuleCSSOMWrappers.grow(ruleCount);
249     ASSERT(m_childRuleCSSOMWrappers.size() == ruleCount);
250     
251     RefPtr<CSSRule>& cssRule = m_childRuleCSSOMWrappers[index];
252     if (!cssRule) {
253         if (index == 0 && m_contents->hasCharsetRule()) {
254             ASSERT(!m_contents->ruleAt(0));
255             cssRule = CSSCharsetRule::create(this, m_contents->encodingFromCharsetRule());
256         } else
257             cssRule = m_contents->ruleAt(index)->createCSSOMWrapper(this);
258     }
259     return cssRule.get();
260 }
261
262 bool CSSStyleSheet::canAccessRules() const
263 {
264     if (m_isInlineStylesheet)
265         return true;
266     URL 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()->canRequest(baseURL))
273         return true;
274     return false;
275 }
276
277 RefPtr<CSSRuleList> CSSStyleSheet::rules()
278 {
279     if (!canAccessRules())
280         return nullptr;
281     // IE behavior.
282     RefPtr<StaticCSSRuleList> nonCharsetRules = StaticCSSRuleList::create();
283     unsigned ruleCount = length();
284     for (unsigned i = 0; i < ruleCount; ++i) {
285         CSSRule* rule = item(i);
286         if (rule->type() == CSSRule::CHARSET_RULE)
287             continue;
288         nonCharsetRules->rules().append(rule);
289     }
290     return nonCharsetRules;
291 }
292
293 unsigned CSSStyleSheet::insertRule(const String& ruleString, unsigned index, ExceptionCode& ec)
294 {
295     ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount());
296
297     ec = 0;
298     if (index > length()) {
299         ec = INDEX_SIZE_ERR;
300         return 0;
301     }
302     CSSParser p(m_contents.get().parserContext());
303     RefPtr<StyleRuleBase> rule = p.parseRule(m_contents.ptr(), ruleString);
304
305     if (!rule) {
306         ec = SYNTAX_ERR;
307         return 0;
308     }
309
310     RuleMutationScope mutationScope(this, RuleInsertion, is<StyleRuleKeyframes>(*rule) ? downcast<StyleRuleKeyframes>(rule.get()) : nullptr);
311
312     bool success = m_contents.get().wrapperInsertRule(rule, index);
313     if (!success) {
314         ec = HIERARCHY_REQUEST_ERR;
315         return 0;
316     }        
317     if (!m_childRuleCSSOMWrappers.isEmpty())
318         m_childRuleCSSOMWrappers.insert(index, RefPtr<CSSRule>());
319
320     return index;
321 }
322
323 void CSSStyleSheet::deleteRule(unsigned index, ExceptionCode& ec)
324 {
325     ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount());
326
327     ec = 0;
328     if (index >= length()) {
329         ec = INDEX_SIZE_ERR;
330         return;
331     }
332     RuleMutationScope mutationScope(this);
333
334     m_contents->wrapperDeleteRule(index);
335
336     if (!m_childRuleCSSOMWrappers.isEmpty()) {
337         if (m_childRuleCSSOMWrappers[index])
338             m_childRuleCSSOMWrappers[index]->setParentStyleSheet(0);
339         m_childRuleCSSOMWrappers.remove(index);
340     }
341 }
342
343 int CSSStyleSheet::addRule(const String& selector, const String& style, int index, ExceptionCode& ec)
344 {
345     StringBuilder text;
346     text.append(selector);
347     text.appendLiteral(" { ");
348     text.append(style);
349     if (!style.isEmpty())
350         text.append(' ');
351     text.append('}');
352     insertRule(text.toString(), index, ec);
353     
354     // As per Microsoft documentation, always return -1.
355     return -1;
356 }
357
358 int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionCode& ec)
359 {
360     return addRule(selector, style, length(), ec);
361 }
362
363
364 RefPtr<CSSRuleList> CSSStyleSheet::cssRules()
365 {
366     if (!canAccessRules())
367         return nullptr;
368     if (!m_ruleListCSSOMWrapper)
369         m_ruleListCSSOMWrapper = std::make_unique<StyleSheetCSSRuleList>(this);
370     return m_ruleListCSSOMWrapper.get();
371 }
372
373 String CSSStyleSheet::href() const
374 {
375     return m_contents->originalURL();
376 }
377
378 URL CSSStyleSheet::baseURL() const
379 {
380     return m_contents->baseURL();
381 }
382
383 bool CSSStyleSheet::isLoading() const
384 {
385     return m_contents->isLoading();
386 }
387
388 MediaList* CSSStyleSheet::media() const 
389
390     if (!m_mediaQueries)
391         return 0;
392
393     if (!m_mediaCSSOMWrapper)
394         m_mediaCSSOMWrapper = MediaList::create(m_mediaQueries.get(), const_cast<CSSStyleSheet*>(this));
395     return m_mediaCSSOMWrapper.get();
396 }
397
398 CSSStyleSheet* CSSStyleSheet::parentStyleSheet() const 
399
400     return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0; 
401 }
402
403 Document* CSSStyleSheet::ownerDocument() const
404 {
405     const CSSStyleSheet* root = this;
406     while (root->parentStyleSheet())
407         root = root->parentStyleSheet();
408     return root->ownerNode() ? &root->ownerNode()->document() : 0;
409 }
410
411 void CSSStyleSheet::clearChildRuleCSSOMWrappers()
412 {
413     m_childRuleCSSOMWrappers.clear();
414 }
415
416 CSSStyleSheet::RuleMutationScope::RuleMutationScope(CSSStyleSheet* sheet, RuleMutationType mutationType, StyleRuleKeyframes* insertedKeyframesRule)
417     : m_styleSheet(sheet)
418     , m_mutationType(mutationType)
419     , m_insertedKeyframesRule(insertedKeyframesRule)
420 {
421     ASSERT(m_styleSheet);
422     m_contentsWereClonedForMutation = m_styleSheet->willMutateRules();
423 }
424
425 CSSStyleSheet::RuleMutationScope::RuleMutationScope(CSSRule* rule)
426     : m_styleSheet(rule ? rule->parentStyleSheet() : 0)
427     , m_mutationType(OtherMutation)
428     , m_contentsWereClonedForMutation(ContentsWereNotClonedForMutation)
429     , m_insertedKeyframesRule(nullptr)
430 {
431     if (m_styleSheet)
432         m_contentsWereClonedForMutation = m_styleSheet->willMutateRules();
433 }
434
435 CSSStyleSheet::RuleMutationScope::~RuleMutationScope()
436 {
437     if (m_styleSheet)
438         m_styleSheet->didMutateRules(m_mutationType, m_contentsWereClonedForMutation, m_insertedKeyframesRule);
439 }
440
441 }