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