Use NeverDestroyed instead of DEPRECATED_DEFINE_STATIC_LOCAL
[WebKit-https.git] / Source / WebCore / css / StyleSheetContents.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 "StyleSheetContents.h"
23
24 #include "CSSImportRule.h"
25 #include "CSSParser.h"
26 #include "CSSStyleSheet.h"
27 #include "CachedCSSStyleSheet.h"
28 #include "Document.h"
29 #include "MediaList.h"
30 #include "Node.h"
31 #include "Page.h"
32 #include "PageConsoleClient.h"
33 #include "RuleSet.h"
34 #include "SecurityOrigin.h"
35 #include "StyleProperties.h"
36 #include "StyleRule.h"
37 #include "StyleRuleImport.h"
38 #include <wtf/Deque.h>
39 #include <wtf/NeverDestroyed.h>
40 #include <wtf/Ref.h>
41
42 namespace WebCore {
43
44 // Rough size estimate for the memory cache.
45 unsigned StyleSheetContents::estimatedSizeInBytes() const
46 {
47     // Note that this does not take into account size of the strings hanging from various objects. 
48     // The assumption is that nearly all of of them are atomic and would exist anyway.
49     unsigned size = sizeof(*this);
50
51     // FIXME: This ignores the children of media and region rules.
52     // Most rules are StyleRules.
53     size += ruleCount() * StyleRule::averageSizeInBytes();
54
55     for (unsigned i = 0; i < m_importRules.size(); ++i) {
56         if (StyleSheetContents* sheet = m_importRules[i]->styleSheet())
57             size += sheet->estimatedSizeInBytes();
58     }
59     return size;
60 }
61
62 StyleSheetContents::StyleSheetContents(StyleRuleImport* ownerRule, const String& originalURL, const CSSParserContext& context)
63     : m_ownerRule(ownerRule)
64     , m_originalURL(originalURL)
65     , m_loadCompleted(false)
66     , m_isUserStyleSheet(ownerRule && ownerRule->parentStyleSheet() && ownerRule->parentStyleSheet()->isUserStyleSheet())
67     , m_hasSyntacticallyValidCSSHeader(true)
68     , m_didLoadErrorOccur(false)
69     , m_usesRemUnits(false)
70     , m_usesStyleBasedEditability(false)
71     , m_isMutable(false)
72     , m_isInMemoryCache(false)
73     , m_parserContext(context)
74 {
75 }
76
77 StyleSheetContents::StyleSheetContents(const StyleSheetContents& o)
78     : RefCounted<StyleSheetContents>()
79     , m_ownerRule(0)
80     , m_originalURL(o.m_originalURL)
81     , m_encodingFromCharsetRule(o.m_encodingFromCharsetRule)
82     , m_importRules(o.m_importRules.size())
83     , m_childRules(o.m_childRules.size())
84     , m_namespaces(o.m_namespaces)
85     , m_loadCompleted(true)
86     , m_isUserStyleSheet(o.m_isUserStyleSheet)
87     , m_hasSyntacticallyValidCSSHeader(o.m_hasSyntacticallyValidCSSHeader)
88     , m_didLoadErrorOccur(false)
89     , m_usesRemUnits(o.m_usesRemUnits)
90     , m_usesStyleBasedEditability(o.m_usesStyleBasedEditability)
91     , m_isMutable(false)
92     , m_isInMemoryCache(false)
93     , m_parserContext(o.m_parserContext)
94 {
95     ASSERT(o.isCacheable());
96
97     // FIXME: Copy import rules.
98     ASSERT(o.m_importRules.isEmpty());
99
100     for (unsigned i = 0; i < m_childRules.size(); ++i)
101         m_childRules[i] = o.m_childRules[i]->copy();
102 }
103
104 StyleSheetContents::~StyleSheetContents()
105 {
106     clearRules();
107 }
108
109 bool StyleSheetContents::isCacheable() const
110 {
111     // FIXME: Support copying import rules.
112     if (!m_importRules.isEmpty())
113         return false;
114     // FIXME: Support cached stylesheets in import rules.
115     if (m_ownerRule)
116         return false;
117     // This would require dealing with multiple clients for load callbacks.
118     if (!m_loadCompleted)
119         return false;
120     if (m_didLoadErrorOccur)
121         return false;
122     // It is not the original sheet anymore.
123     if (m_isMutable)
124         return false;
125     // If the header is valid we are not going to need to check the SecurityOrigin.
126     // FIXME: Valid mime type avoids the check too.
127     if (!m_hasSyntacticallyValidCSSHeader)
128         return false;
129     return true;
130 }
131
132 void StyleSheetContents::parserAppendRule(PassRefPtr<StyleRuleBase> rule)
133 {
134     ASSERT(!rule->isCharsetRule());
135     if (is<StyleRuleImport>(*rule)) {
136         // Parser enforces that @import rules come before anything else except @charset.
137         ASSERT(m_childRules.isEmpty());
138         m_importRules.append(downcast<StyleRuleImport>(rule.get()));
139         m_importRules.last()->setParentStyleSheet(this);
140         m_importRules.last()->requestStyleSheet();
141         return;
142     }
143
144 #if ENABLE(RESOLUTION_MEDIA_QUERY)
145     // Add warning message to inspector if dpi/dpcm values are used for screen media.
146     if (is<StyleRuleMedia>(*rule))
147         reportMediaQueryWarningIfNeeded(singleOwnerDocument(), downcast<StyleRuleMedia>(*rule).mediaQueries());
148 #endif
149
150     // NOTE: The selector list has to fit into RuleData. <http://webkit.org/b/118369>
151     // If we're adding a rule with a huge number of selectors, split it up into multiple rules
152     if (is<StyleRule>(*rule) && downcast<StyleRule>(*rule).selectorList().componentCount() > RuleData::maximumSelectorComponentCount) {
153         Vector<RefPtr<StyleRule>> rules = downcast<StyleRule>(*rule).splitIntoMultipleRulesWithMaximumSelectorComponentCount(RuleData::maximumSelectorComponentCount);
154         m_childRules.appendVector(rules);
155         return;
156     }
157
158     m_childRules.append(rule);
159 }
160
161 StyleRuleBase* StyleSheetContents::ruleAt(unsigned index) const
162 {
163     ASSERT_WITH_SECURITY_IMPLICATION(index < ruleCount());
164     
165     unsigned childVectorIndex = index;
166     if (hasCharsetRule()) {
167         if (index == 0)
168             return 0;
169         --childVectorIndex;
170     }
171     if (childVectorIndex < m_importRules.size())
172         return m_importRules[childVectorIndex].get();
173
174     childVectorIndex -= m_importRules.size();
175     return m_childRules[childVectorIndex].get();
176 }
177
178 unsigned StyleSheetContents::ruleCount() const
179 {
180     unsigned result = 0;
181     result += hasCharsetRule() ? 1 : 0;
182     result += m_importRules.size();
183     result += m_childRules.size();
184     return result;
185 }
186
187 void StyleSheetContents::clearCharsetRule()
188 {
189     m_encodingFromCharsetRule = String();
190 }
191
192 void StyleSheetContents::clearRules()
193 {
194     for (unsigned i = 0; i < m_importRules.size(); ++i) {
195         ASSERT(m_importRules.at(i)->parentStyleSheet() == this);
196         m_importRules[i]->clearParentStyleSheet();
197     }
198     m_importRules.clear();
199     m_childRules.clear();
200     clearCharsetRule();
201 }
202
203 void StyleSheetContents::parserSetEncodingFromCharsetRule(const String& encoding)
204 {
205     // Parser enforces that there is ever only one @charset.
206     ASSERT(m_encodingFromCharsetRule.isNull());
207     m_encodingFromCharsetRule = encoding; 
208 }
209
210 bool StyleSheetContents::wrapperInsertRule(PassRefPtr<StyleRuleBase> rule, unsigned index)
211 {
212     ASSERT(m_isMutable);
213     ASSERT_WITH_SECURITY_IMPLICATION(index <= ruleCount());
214     // Parser::parseRule doesn't currently allow @charset so we don't need to deal with it.
215     ASSERT(!rule->isCharsetRule());
216     
217     unsigned childVectorIndex = index;
218     // m_childRules does not contain @charset which is always in index 0 if it exists.
219     if (hasCharsetRule()) {
220         if (childVectorIndex == 0) {
221             // Nothing can be inserted before @charset.
222             return false;
223         }
224         --childVectorIndex;
225     }
226     
227     if (childVectorIndex < m_importRules.size() || (childVectorIndex == m_importRules.size() && rule->isImportRule())) {
228         // Inserting non-import rule before @import is not allowed.
229         if (!is<StyleRuleImport>(*rule))
230             return false;
231         m_importRules.insert(childVectorIndex, downcast<StyleRuleImport>(rule.get()));
232         m_importRules[childVectorIndex]->setParentStyleSheet(this);
233         m_importRules[childVectorIndex]->requestStyleSheet();
234         // FIXME: Stylesheet doesn't actually change meaningfully before the imported sheets are loaded.
235         return true;
236     }
237     // Inserting @import rule after a non-import rule is not allowed.
238     if (is<StyleRuleImport>(*rule))
239         return false;
240     childVectorIndex -= m_importRules.size();
241
242     // If the number of selectors would overflow RuleData, we drop the operation.
243     if (is<StyleRule>(*rule) && downcast<StyleRule>(*rule).selectorList().componentCount() > RuleData::maximumSelectorComponentCount)
244         return false;
245
246     m_childRules.insert(childVectorIndex, rule);
247     return true;
248 }
249
250 void StyleSheetContents::wrapperDeleteRule(unsigned index)
251 {
252     ASSERT(m_isMutable);
253     ASSERT_WITH_SECURITY_IMPLICATION(index < ruleCount());
254
255     unsigned childVectorIndex = index;
256     if (hasCharsetRule()) {
257         if (childVectorIndex == 0) {
258             clearCharsetRule();
259             return;
260         }
261         --childVectorIndex;
262     }
263     if (childVectorIndex < m_importRules.size()) {
264         m_importRules[childVectorIndex]->clearParentStyleSheet();
265         m_importRules.remove(childVectorIndex);
266         return;
267     }
268     childVectorIndex -= m_importRules.size();
269
270     m_childRules.remove(childVectorIndex);
271 }
272
273 void StyleSheetContents::parserAddNamespace(const AtomicString& prefix, const AtomicString& uri)
274 {
275     if (uri.isNull() || prefix.isNull())
276         return;
277     PrefixNamespaceURIMap::AddResult result = m_namespaces.add(prefix, uri);
278     if (result.isNewEntry)
279         return;
280     result.iterator->value = uri;
281 }
282
283 const AtomicString& StyleSheetContents::determineNamespace(const AtomicString& prefix)
284 {
285     if (prefix.isNull())
286         return nullAtom; // No namespace. If an element/attribute has a namespace, we won't match it.
287     if (prefix == starAtom)
288         return starAtom; // We'll match any namespace.
289     PrefixNamespaceURIMap::const_iterator it = m_namespaces.find(prefix);
290     if (it == m_namespaces.end())
291         return nullAtom;
292     return it->value;
293 }
294
295 void StyleSheetContents::parseAuthorStyleSheet(const CachedCSSStyleSheet* cachedStyleSheet, const SecurityOrigin* securityOrigin)
296 {
297     bool isSameOriginRequest = securityOrigin && securityOrigin->canRequest(baseURL());
298     CachedCSSStyleSheet::MIMETypeCheck mimeTypeCheck = isStrictParserMode(m_parserContext.mode) || !isSameOriginRequest ? CachedCSSStyleSheet::MIMETypeCheck::Strict : CachedCSSStyleSheet::MIMETypeCheck::Lax;
299     bool hasValidMIMEType = true;
300     String sheetText = cachedStyleSheet->sheetText(mimeTypeCheck, &hasValidMIMEType);
301
302     if (!hasValidMIMEType) {
303         ASSERT(sheetText.isNull());
304         if (auto* document = singleOwnerDocument()) {
305             if (auto* page = document->page()) {
306                 if (isStrictParserMode(m_parserContext.mode))
307                     page->console().addMessage(MessageSource::Security, MessageLevel::Error, "Did not parse stylesheet at '" + cachedStyleSheet->url().stringCenterEllipsizedToLength() + "' because non CSS MIME types are not allowed in strict mode.");
308                 else
309                     page->console().addMessage(MessageSource::Security, MessageLevel::Error, "Did not parse stylesheet at '" + cachedStyleSheet->url().stringCenterEllipsizedToLength() + "' because non CSS MIME types are not allowed for cross-origin stylesheets.");
310             }
311         }
312         return;
313     }
314
315     CSSParser p(parserContext());
316     p.parseSheet(this, sheetText, TextPosition(), nullptr, true);
317
318     if (m_parserContext.needsSiteSpecificQuirks && isStrictParserMode(m_parserContext.mode)) {
319         // Work around <https://bugs.webkit.org/show_bug.cgi?id=28350>.
320         static NeverDestroyed<const String> mediaWikiKHTMLFixesStyleSheet(ASCIILiteral("/* KHTML fix stylesheet */\n/* work around the horizontal scrollbars */\n#column-content { margin-left: 0; }\n\n"));
321         // There are two variants of KHTMLFixes.css. One is equal to mediaWikiKHTMLFixesStyleSheet,
322         // while the other lacks the second trailing newline.
323         if (baseURL().string().endsWith("/KHTMLFixes.css") && !sheetText.isNull() && mediaWikiKHTMLFixesStyleSheet.get().startsWith(sheetText)
324             && sheetText.length() >= mediaWikiKHTMLFixesStyleSheet.get().length() - 1)
325             clearRules();
326     }
327 }
328
329 bool StyleSheetContents::parseString(const String& sheetText)
330 {
331     return parseStringAtPosition(sheetText, TextPosition(), false);
332 }
333
334 bool StyleSheetContents::parseStringAtPosition(const String& sheetText, const TextPosition& textPosition, bool createdByParser)
335 {
336     CSSParser p(parserContext());
337     p.parseSheet(this, sheetText, textPosition, nullptr, createdByParser);
338     return true;
339 }
340
341 bool StyleSheetContents::isLoading() const
342 {
343     for (unsigned i = 0; i < m_importRules.size(); ++i) {
344         if (m_importRules[i]->isLoading())
345             return true;
346     }
347     return false;
348 }
349
350 void StyleSheetContents::checkLoaded()
351 {
352     if (isLoading())
353         return;
354
355     // Avoid |this| being deleted by scripts that run via
356     // ScriptableDocumentParser::executeScriptsWaitingForStylesheets().
357     // See <rdar://problem/6622300>.
358     Ref<StyleSheetContents> protect(*this);
359     StyleSheetContents* parentSheet = parentStyleSheet();
360     if (parentSheet) {
361         parentSheet->checkLoaded();
362         m_loadCompleted = true;
363         return;
364     }
365     RefPtr<Node> ownerNode = singleOwnerNode();
366     if (!ownerNode) {
367         m_loadCompleted = true;
368         return;
369     }
370     m_loadCompleted = ownerNode->sheetLoaded();
371     if (m_loadCompleted)
372         ownerNode->notifyLoadedSheetAndAllCriticalSubresources(m_didLoadErrorOccur);
373 }
374
375 void StyleSheetContents::notifyLoadedSheet(const CachedCSSStyleSheet* sheet)
376 {
377     ASSERT(sheet);
378     m_didLoadErrorOccur |= sheet->errorOccurred();
379 }
380
381 void StyleSheetContents::startLoadingDynamicSheet()
382 {
383     if (Node* owner = singleOwnerNode())
384         owner->startLoadingDynamicSheet();
385 }
386
387 StyleSheetContents* StyleSheetContents::rootStyleSheet() const
388 {
389     const StyleSheetContents* root = this;
390     while (root->parentStyleSheet())
391         root = root->parentStyleSheet();
392     return const_cast<StyleSheetContents*>(root);
393 }
394
395 Node* StyleSheetContents::singleOwnerNode() const
396 {
397     StyleSheetContents* root = rootStyleSheet();
398     if (root->m_clients.isEmpty())
399         return 0;
400     ASSERT(root->m_clients.size() == 1);
401     return root->m_clients[0]->ownerNode();
402 }
403
404 Document* StyleSheetContents::singleOwnerDocument() const
405 {
406     Node* ownerNode = singleOwnerNode();
407     return ownerNode ? &ownerNode->document() : 0;
408 }
409
410 URL StyleSheetContents::completeURL(const String& url) const
411 {
412     return CSSParser::completeURL(m_parserContext, url);
413 }
414
415 void StyleSheetContents::addSubresourceStyleURLs(ListHashSet<URL>& urls)
416 {
417     Deque<StyleSheetContents*> styleSheetQueue;
418     styleSheetQueue.append(this);
419
420     while (!styleSheetQueue.isEmpty()) {
421         StyleSheetContents* styleSheet = styleSheetQueue.takeFirst();
422         
423         for (auto& importRule : styleSheet->m_importRules) {
424             if (importRule->styleSheet()) {
425                 styleSheetQueue.append(importRule->styleSheet());
426                 addSubresourceURL(urls, importRule->styleSheet()->baseURL());
427             }
428         }
429         for (auto& rule : styleSheet->m_childRules) {
430             if (is<StyleRule>(*rule))
431                 downcast<StyleRule>(*rule).properties().addSubresourceStyleURLs(urls, this);
432             else if (is<StyleRuleFontFace>(*rule))
433                 downcast<StyleRuleFontFace>(*rule).properties().addSubresourceStyleURLs(urls, this);
434         }
435     }
436 }
437
438 static bool traverseSubresourcesInRules(const Vector<RefPtr<StyleRuleBase>>& rules, const std::function<bool (const CachedResource&)>& handler)
439 {
440     for (auto& rule : rules) {
441         switch (rule->type()) {
442         case StyleRuleBase::Style:
443             if (downcast<StyleRule>(*rule).properties().traverseSubresources(handler))
444                 return true;
445             break;
446         case StyleRuleBase::FontFace:
447             if (downcast<StyleRuleFontFace>(*rule).properties().traverseSubresources(handler))
448                 return true;
449             break;
450         case StyleRuleBase::Media:
451             if (traverseSubresourcesInRules(downcast<StyleRuleMedia>(*rule).childRules(), handler))
452                 return true;
453             break;
454         case StyleRuleBase::Region:
455             if (traverseSubresourcesInRules(downcast<StyleRuleRegion>(*rule).childRules(), handler))
456                 return true;
457             break;
458         case StyleRuleBase::Import:
459             ASSERT_NOT_REACHED();
460 #if ASSERT_DISABLED
461             FALLTHROUGH;
462 #endif
463         case StyleRuleBase::Page:
464         case StyleRuleBase::Keyframes:
465         case StyleRuleBase::Unknown:
466         case StyleRuleBase::Charset:
467         case StyleRuleBase::Keyframe:
468         case StyleRuleBase::Supports:
469 #if ENABLE(CSS_DEVICE_ADAPTATION)
470         case StyleRuleBase::Viewport:
471 #endif
472             break;
473         }
474     }
475     return false;
476 }
477
478 bool StyleSheetContents::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
479 {
480     for (auto& importRule : m_importRules) {
481         if (!importRule->styleSheet())
482             continue;
483         if (traverseSubresourcesInRules(importRule->styleSheet()->m_childRules, handler))
484             return true;
485     }
486     return traverseSubresourcesInRules(m_childRules, handler);
487 }
488
489 bool StyleSheetContents::subresourcesAllowReuse(CachePolicy cachePolicy) const
490 {
491     bool hasFailedOrExpiredResources = traverseSubresources([cachePolicy](const CachedResource& resource) {
492         if (resource.loadFailedOrCanceled())
493             return true;
494         // We can't revalidate subresources individually so don't use reuse the parsed sheet if they need revalidation.
495         if (resource.makeRevalidationDecision(cachePolicy) != CachedResource::RevalidationDecision::No)
496             return true;
497         return false;
498     });
499     return !hasFailedOrExpiredResources;
500 }
501
502 bool StyleSheetContents::isLoadingSubresources() const
503 {
504     return traverseSubresources([](const CachedResource& resource) {
505         return resource.isLoading();
506     });
507 }
508
509 StyleSheetContents* StyleSheetContents::parentStyleSheet() const
510 {
511     return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0;
512 }
513
514 void StyleSheetContents::registerClient(CSSStyleSheet* sheet)
515 {
516     ASSERT(!m_clients.contains(sheet));
517     m_clients.append(sheet);
518 }
519
520 void StyleSheetContents::unregisterClient(CSSStyleSheet* sheet)
521 {
522     bool removed = m_clients.removeFirst(sheet);
523     ASSERT_UNUSED(removed, removed);
524 }
525
526 void StyleSheetContents::addedToMemoryCache()
527 {
528     ASSERT(!m_isInMemoryCache);
529     ASSERT(isCacheable());
530     m_isInMemoryCache = true;
531 }
532
533 void StyleSheetContents::removedFromMemoryCache()
534 {
535     ASSERT(m_isInMemoryCache);
536     ASSERT(isCacheable());
537     m_isInMemoryCache = false;
538 }
539
540 void StyleSheetContents::shrinkToFit()
541 {
542     m_importRules.shrinkToFit();
543     m_childRules.shrinkToFit();
544 }
545
546 }