Extract UTI mapping and allow for additions
[WebKit-https.git] / Source / WebCore / css / StyleSheetContents.cpp
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004-2017 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 "ContentRuleListResults.h"
29 #include "Document.h"
30 #include "Frame.h"
31 #include "FrameLoader.h"
32 #include "MediaList.h"
33 #include "Node.h"
34 #include "Page.h"
35 #include "PageConsoleClient.h"
36 #include "ResourceLoadInfo.h"
37 #include "RuleSet.h"
38 #include "SecurityOrigin.h"
39 #include "StyleRule.h"
40 #include "StyleRuleImport.h"
41 #include <wtf/Deque.h>
42 #include <wtf/NeverDestroyed.h>
43 #include <wtf/Ref.h>
44
45 #if ENABLE(CONTENT_EXTENSIONS)
46 #include "UserContentController.h"
47 #endif
48
49 namespace WebCore {
50
51 // Rough size estimate for the memory cache.
52 unsigned StyleSheetContents::estimatedSizeInBytes() const
53 {
54     // Note that this does not take into account size of the strings hanging from various objects. 
55     // The assumption is that nearly all of of them are atomic and would exist anyway.
56     unsigned size = sizeof(*this);
57
58     // FIXME: This ignores the children of media and region rules.
59     // Most rules are StyleRules.
60     size += ruleCount() * StyleRule::averageSizeInBytes();
61
62     for (unsigned i = 0; i < m_importRules.size(); ++i) {
63         if (StyleSheetContents* sheet = m_importRules[i]->styleSheet())
64             size += sheet->estimatedSizeInBytes();
65     }
66     return size;
67 }
68
69 StyleSheetContents::StyleSheetContents(StyleRuleImport* ownerRule, const String& originalURL, const CSSParserContext& context)
70     : m_ownerRule(ownerRule)
71     , m_originalURL(originalURL)
72     , m_defaultNamespace(starAtom())
73     , m_isUserStyleSheet(ownerRule && ownerRule->parentStyleSheet() && ownerRule->parentStyleSheet()->isUserStyleSheet())
74     , m_parserContext(context)
75 {
76 }
77
78 StyleSheetContents::StyleSheetContents(const StyleSheetContents& o)
79     : RefCounted<StyleSheetContents>()
80     , m_ownerRule(0)
81     , m_originalURL(o.m_originalURL)
82     , m_encodingFromCharsetRule(o.m_encodingFromCharsetRule)
83     , m_importRules(o.m_importRules.size())
84     , m_namespaceRules(o.m_namespaceRules.size())
85     , m_childRules(o.m_childRules.size())
86     , m_namespaces(o.m_namespaces)
87     , m_defaultNamespace(o.m_defaultNamespace)
88     , m_isUserStyleSheet(o.m_isUserStyleSheet)
89     , m_loadCompleted(true)
90     , m_hasSyntacticallyValidCSSHeader(o.m_hasSyntacticallyValidCSSHeader)
91     , m_usesStyleBasedEditability(o.m_usesStyleBasedEditability)
92     , m_parserContext(o.m_parserContext)
93 {
94     ASSERT(o.isCacheable());
95
96     // FIXME: Copy import rules.
97     ASSERT(o.m_importRules.isEmpty());
98
99     for (unsigned i = 0; i < m_childRules.size(); ++i)
100         m_childRules[i] = o.m_childRules[i]->copy();
101 }
102
103 StyleSheetContents::~StyleSheetContents()
104 {
105     clearRules();
106 }
107
108 bool StyleSheetContents::isCacheable() const
109 {
110     // FIXME: Support copying import rules.
111     if (!m_importRules.isEmpty())
112         return false;
113     // FIXME: Support cached stylesheets in import rules.
114     if (m_ownerRule)
115         return false;
116     // This would require dealing with multiple clients for load callbacks.
117     if (!m_loadCompleted)
118         return false;
119     if (m_didLoadErrorOccur)
120         return false;
121     // It is not the original sheet anymore.
122     if (m_isMutable)
123         return false;
124     // If the header is valid we are not going to need to check the SecurityOrigin.
125     // FIXME: Valid mime type avoids the check too.
126     if (!m_hasSyntacticallyValidCSSHeader)
127         return false;
128     return true;
129 }
130
131 void StyleSheetContents::parserAppendRule(Ref<StyleRuleBase>&& rule)
132 {
133     ASSERT(!rule->isCharsetRule());
134
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.ptr()));
139         m_importRules.last()->setParentStyleSheet(this);
140         m_importRules.last()->requestStyleSheet();
141         return;
142     }
143
144     if (is<StyleRuleNamespace>(rule)) {
145         // Parser enforces that @namespace rules come before all rules other than
146         // import/charset rules
147         ASSERT(m_childRules.isEmpty());
148         StyleRuleNamespace& namespaceRule = downcast<StyleRuleNamespace>(rule.get());
149         parserAddNamespace(namespaceRule.prefix(), namespaceRule.uri());
150         m_namespaceRules.append(downcast<StyleRuleNamespace>(rule.ptr()));
151         return;
152     }
153
154     if (is<StyleRuleMedia>(rule))
155         reportMediaQueryWarningIfNeeded(singleOwnerDocument(), downcast<StyleRuleMedia>(rule.get()).mediaQueries());
156
157     // NOTE: The selector list has to fit into RuleData. <http://webkit.org/b/118369>
158     // If we're adding a rule with a huge number of selectors, split it up into multiple rules
159     if (is<StyleRule>(rule) && downcast<StyleRule>(rule.get()).selectorList().componentCount() > RuleData::maximumSelectorComponentCount) {
160         m_childRules.appendVector(downcast<StyleRule>(rule.get()).splitIntoMultipleRulesWithMaximumSelectorComponentCount(RuleData::maximumSelectorComponentCount));
161         return;
162     }
163
164     m_childRules.append(WTFMove(rule));
165 }
166
167 StyleRuleBase* StyleSheetContents::ruleAt(unsigned index) const
168 {
169     ASSERT_WITH_SECURITY_IMPLICATION(index < ruleCount());
170     
171     unsigned childVectorIndex = index;
172     if (childVectorIndex < m_importRules.size())
173         return m_importRules[childVectorIndex].get();
174
175     childVectorIndex -= m_importRules.size();
176     
177     if (childVectorIndex < m_namespaceRules.size())
178         return m_namespaceRules[childVectorIndex].get();
179     
180     childVectorIndex -= m_namespaceRules.size();
181     
182     return m_childRules[childVectorIndex].get();
183 }
184
185 unsigned StyleSheetContents::ruleCount() const
186 {
187     unsigned result = 0;
188     result += m_importRules.size();
189     result += m_namespaceRules.size();
190     result += m_childRules.size();
191     return result;
192 }
193
194 void StyleSheetContents::clearCharsetRule()
195 {
196     m_encodingFromCharsetRule = String();
197 }
198
199 void StyleSheetContents::clearRules()
200 {
201     for (unsigned i = 0; i < m_importRules.size(); ++i) {
202         ASSERT(m_importRules.at(i)->parentStyleSheet() == this);
203         m_importRules[i]->clearParentStyleSheet();
204     }
205     m_importRules.clear();
206     m_namespaceRules.clear();
207     m_childRules.clear();
208     clearCharsetRule();
209 }
210
211 void StyleSheetContents::parserSetEncodingFromCharsetRule(const String& encoding)
212 {
213     // Parser enforces that there is ever only one @charset.
214     ASSERT(m_encodingFromCharsetRule.isNull());
215     m_encodingFromCharsetRule = encoding; 
216 }
217
218 bool StyleSheetContents::wrapperInsertRule(Ref<StyleRuleBase>&& rule, unsigned index)
219 {
220     ASSERT(m_isMutable);
221     ASSERT_WITH_SECURITY_IMPLICATION(index <= ruleCount());
222     // Parser::parseRule doesn't currently allow @charset so we don't need to deal with it.
223     ASSERT(!rule->isCharsetRule());
224     
225     unsigned childVectorIndex = index;
226     if (childVectorIndex < m_importRules.size() || (childVectorIndex == m_importRules.size() && rule->isImportRule())) {
227         // Inserting non-import rule before @import is not allowed.
228         if (!is<StyleRuleImport>(rule))
229             return false;
230         m_importRules.insert(childVectorIndex, downcast<StyleRuleImport>(rule.ptr()));
231         m_importRules[childVectorIndex]->setParentStyleSheet(this);
232         m_importRules[childVectorIndex]->requestStyleSheet();
233         // FIXME: Stylesheet doesn't actually change meaningfully before the imported sheets are loaded.
234         return true;
235     }
236     // Inserting @import rule after a non-import rule is not allowed.
237     if (is<StyleRuleImport>(rule))
238         return false;
239     childVectorIndex -= m_importRules.size();
240
241     
242     if (childVectorIndex < m_namespaceRules.size() || (childVectorIndex == m_namespaceRules.size() && rule->isNamespaceRule())) {
243         // Inserting non-namespace rules other than import rule before @namespace is
244         // not allowed.
245         if (!is<StyleRuleNamespace>(rule))
246             return false;
247         // Inserting @namespace rule when rules other than import/namespace/charset
248         // are present is not allowed.
249         if (!m_childRules.isEmpty())
250             return false;
251         
252         StyleRuleNamespace& namespaceRule = downcast<StyleRuleNamespace>(rule.get());
253         m_namespaceRules.insert(index, downcast<StyleRuleNamespace>(rule.ptr()));
254         
255         // For now to be compatible with IE and Firefox if a namespace rule with the same
256         // prefix is added, it overwrites previous ones.
257         // FIXME: The eventual correct behavior would be to ensure that the last value in
258         // the list wins.
259         parserAddNamespace(namespaceRule.prefix(), namespaceRule.uri());
260         return true;
261     }
262     if (is<StyleRuleNamespace>(rule))
263         return false;
264     childVectorIndex -= m_namespaceRules.size();
265
266     // If the number of selectors would overflow RuleData, we drop the operation.
267     if (is<StyleRule>(rule) && downcast<StyleRule>(rule.get()).selectorList().componentCount() > RuleData::maximumSelectorComponentCount)
268         return false;
269
270     m_childRules.insert(childVectorIndex, WTFMove(rule));
271     return true;
272 }
273
274 void StyleSheetContents::wrapperDeleteRule(unsigned index)
275 {
276     ASSERT(m_isMutable);
277     ASSERT_WITH_SECURITY_IMPLICATION(index < ruleCount());
278
279     unsigned childVectorIndex = index;
280     if (childVectorIndex < m_importRules.size()) {
281         m_importRules[childVectorIndex]->clearParentStyleSheet();
282         m_importRules.remove(childVectorIndex);
283         return;
284     }
285     childVectorIndex -= m_importRules.size();
286
287     if (childVectorIndex < m_namespaceRules.size()) {
288         if (!m_childRules.isEmpty())
289             return;
290         m_namespaceRules.remove(childVectorIndex);
291         return;
292     }
293     childVectorIndex -= m_namespaceRules.size();
294
295     m_childRules.remove(childVectorIndex);
296 }
297
298 void StyleSheetContents::parserAddNamespace(const AtomicString& prefix, const AtomicString& uri)
299 {
300     ASSERT(!uri.isNull());
301     if (prefix.isNull()) {
302         m_defaultNamespace = uri;
303         return;
304     }
305     PrefixNamespaceURIMap::AddResult result = m_namespaces.add(prefix, uri);
306     if (result.isNewEntry)
307         return;
308     result.iterator->value = uri;
309 }
310
311 const AtomicString& StyleSheetContents::namespaceURIFromPrefix(const AtomicString& prefix)
312 {
313     PrefixNamespaceURIMap::const_iterator it = m_namespaces.find(prefix);
314     if (it == m_namespaces.end())
315         return nullAtom();
316     return it->value;
317 }
318
319 void StyleSheetContents::parseAuthorStyleSheet(const CachedCSSStyleSheet* cachedStyleSheet, const SecurityOrigin* securityOrigin)
320 {
321     bool isSameOriginRequest = securityOrigin && securityOrigin->canRequest(baseURL());
322     CachedCSSStyleSheet::MIMETypeCheckHint mimeTypeCheckHint = isStrictParserMode(m_parserContext.mode) || !isSameOriginRequest ? CachedCSSStyleSheet::MIMETypeCheckHint::Strict : CachedCSSStyleSheet::MIMETypeCheckHint::Lax;
323     bool hasValidMIMEType = true;
324     String sheetText = cachedStyleSheet->sheetText(mimeTypeCheckHint, &hasValidMIMEType);
325
326     if (!hasValidMIMEType) {
327         ASSERT(sheetText.isNull());
328         if (auto* document = singleOwnerDocument()) {
329             if (auto* page = document->page()) {
330                 if (isStrictParserMode(m_parserContext.mode))
331                     page->console().addMessage(MessageSource::Security, MessageLevel::Error, makeString("Did not parse stylesheet at '", cachedStyleSheet->url().stringCenterEllipsizedToLength(), "' because non CSS MIME types are not allowed in strict mode."));
332                 else if (!cachedStyleSheet->mimeTypeAllowedByNosniff())
333                     page->console().addMessage(MessageSource::Security, MessageLevel::Error, makeString("Did not parse stylesheet at '", cachedStyleSheet->url().stringCenterEllipsizedToLength(), "' because non CSS MIME types are not allowed when 'X-Content-Type: nosniff' is given."));
334                 else
335                     page->console().addMessage(MessageSource::Security, MessageLevel::Error, makeString("Did not parse stylesheet at '", cachedStyleSheet->url().stringCenterEllipsizedToLength(), "' because non CSS MIME types are not allowed for cross-origin stylesheets."));
336             }
337         }
338         return;
339     }
340
341     CSSParser(parserContext()).parseSheet(this, sheetText, CSSParser::RuleParsing::Deferred);
342 }
343
344 bool StyleSheetContents::parseString(const String& sheetText)
345 {
346     CSSParser p(parserContext());
347     p.parseSheet(this, sheetText, parserContext().mode != UASheetMode ? CSSParser::RuleParsing::Deferred : CSSParser::RuleParsing::Normal);
348     return true;
349 }
350
351 bool StyleSheetContents::isLoading() const
352 {
353     for (unsigned i = 0; i < m_importRules.size(); ++i) {
354         if (m_importRules[i]->isLoading())
355             return true;
356     }
357     return false;
358 }
359
360 void StyleSheetContents::checkLoaded()
361 {
362     if (isLoading())
363         return;
364
365     Ref<StyleSheetContents> protectedThis(*this);
366     StyleSheetContents* parentSheet = parentStyleSheet();
367     if (parentSheet) {
368         parentSheet->checkLoaded();
369         m_loadCompleted = true;
370         return;
371     }
372     RefPtr<Node> ownerNode = singleOwnerNode();
373     if (!ownerNode) {
374         m_loadCompleted = true;
375         return;
376     }
377     m_loadCompleted = ownerNode->sheetLoaded();
378     if (m_loadCompleted)
379         ownerNode->notifyLoadedSheetAndAllCriticalSubresources(m_didLoadErrorOccur);
380 }
381
382 void StyleSheetContents::notifyLoadedSheet(const CachedCSSStyleSheet* sheet)
383 {
384     ASSERT(sheet);
385     m_didLoadErrorOccur |= sheet->errorOccurred();
386     m_didLoadErrorOccur |= !sheet->mimeTypeAllowedByNosniff();
387 }
388
389 void StyleSheetContents::startLoadingDynamicSheet()
390 {
391     if (Node* owner = singleOwnerNode())
392         owner->startLoadingDynamicSheet();
393 }
394
395 StyleSheetContents* StyleSheetContents::rootStyleSheet() const
396 {
397     const StyleSheetContents* root = this;
398     while (root->parentStyleSheet())
399         root = root->parentStyleSheet();
400     return const_cast<StyleSheetContents*>(root);
401 }
402
403 Node* StyleSheetContents::singleOwnerNode() const
404 {
405     StyleSheetContents* root = rootStyleSheet();
406     if (root->m_clients.isEmpty())
407         return 0;
408     ASSERT(root->m_clients.size() == 1);
409     return root->m_clients[0]->ownerNode();
410 }
411
412 Document* StyleSheetContents::singleOwnerDocument() const
413 {
414     Node* ownerNode = singleOwnerNode();
415     return ownerNode ? &ownerNode->document() : 0;
416 }
417
418 URL StyleSheetContents::completeURL(const String& url) const
419 {
420     return m_parserContext.completeURL(url);
421 }
422
423 static bool traverseRulesInVector(const Vector<RefPtr<StyleRuleBase>>& rules, const WTF::Function<bool (const StyleRuleBase&)>& handler)
424 {
425     for (auto& rule : rules) {
426         if (handler(*rule))
427             return true;
428         switch (rule->type()) {
429         case StyleRuleBase::Media: {
430             auto* childRules = downcast<StyleRuleMedia>(*rule).childRulesWithoutDeferredParsing();
431             if (childRules && traverseRulesInVector(*childRules, handler))
432                 return true;
433             break;
434         }
435         case StyleRuleBase::Import:
436             ASSERT_NOT_REACHED();
437             break;
438         case StyleRuleBase::Style:
439         case StyleRuleBase::FontFace:
440         case StyleRuleBase::Page:
441         case StyleRuleBase::Keyframes:
442         case StyleRuleBase::Namespace:
443         case StyleRuleBase::Unknown:
444         case StyleRuleBase::Charset:
445         case StyleRuleBase::Keyframe:
446         case StyleRuleBase::Supports:
447 #if ENABLE(CSS_DEVICE_ADAPTATION)
448         case StyleRuleBase::Viewport:
449 #endif
450             break;
451         }
452     }
453     return false;
454 }
455
456 bool StyleSheetContents::traverseRules(const WTF::Function<bool (const StyleRuleBase&)>& handler) const
457 {
458     for (auto& importRule : m_importRules) {
459         if (handler(*importRule))
460             return true;
461         auto* importedStyleSheet = importRule->styleSheet();
462         if (importedStyleSheet && importedStyleSheet->traverseRules(handler))
463             return true;
464     }
465     return traverseRulesInVector(m_childRules, handler);
466 }
467
468 bool StyleSheetContents::traverseSubresources(const WTF::Function<bool (const CachedResource&)>& handler) const
469 {
470     return traverseRules([&] (const StyleRuleBase& rule) {
471         switch (rule.type()) {
472         case StyleRuleBase::Style: {
473             auto* properties = downcast<StyleRule>(rule).propertiesWithoutDeferredParsing();
474             return properties && properties->traverseSubresources(handler);
475         }
476         case StyleRuleBase::FontFace:
477             return downcast<StyleRuleFontFace>(rule).properties().traverseSubresources(handler);
478         case StyleRuleBase::Import:
479             if (auto* cachedResource = downcast<StyleRuleImport>(rule).cachedCSSStyleSheet())
480                 return handler(*cachedResource);
481             return false;
482         case StyleRuleBase::Media:
483         case StyleRuleBase::Page:
484         case StyleRuleBase::Keyframes:
485         case StyleRuleBase::Namespace:
486         case StyleRuleBase::Unknown:
487         case StyleRuleBase::Charset:
488         case StyleRuleBase::Keyframe:
489         case StyleRuleBase::Supports:
490 #if ENABLE(CSS_DEVICE_ADAPTATION)
491         case StyleRuleBase::Viewport:
492 #endif
493             return false;
494         };
495         ASSERT_NOT_REACHED();
496         return false;
497     });
498 }
499
500 bool StyleSheetContents::subresourcesAllowReuse(CachePolicy cachePolicy, FrameLoader& loader) const
501 {
502     bool hasFailedOrExpiredResources = traverseSubresources([cachePolicy, &loader](const CachedResource& resource) {
503         if (resource.loadFailedOrCanceled())
504             return true;
505         // We can't revalidate subresources individually so don't use reuse the parsed sheet if they need revalidation.
506         if (resource.makeRevalidationDecision(cachePolicy) != CachedResource::RevalidationDecision::No)
507             return true;
508
509 #if ENABLE(CONTENT_EXTENSIONS)
510         // If a cached subresource is blocked or made HTTPS by a content blocker, we cannot reuse the cached stylesheet.
511         auto* page = loader.frame().page();
512         auto* documentLoader = loader.documentLoader();
513         if (page && documentLoader) {
514             const auto& request = resource.resourceRequest();
515             auto results = page->userContentProvider().processContentRuleListsForLoad(request.url(), toResourceType(resource.type()), *documentLoader);
516             if (results.summary.blockedLoad || results.summary.madeHTTPS)
517                 return true;
518         }
519 #else
520         UNUSED_PARAM(loader);
521 #endif
522
523         return false;
524     });
525     return !hasFailedOrExpiredResources;
526 }
527
528 bool StyleSheetContents::isLoadingSubresources() const
529 {
530     return traverseSubresources([](const CachedResource& resource) {
531         return resource.isLoading();
532     });
533 }
534
535 StyleSheetContents* StyleSheetContents::parentStyleSheet() const
536 {
537     return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0;
538 }
539
540 void StyleSheetContents::registerClient(CSSStyleSheet* sheet)
541 {
542     ASSERT(!m_clients.contains(sheet));
543     m_clients.append(sheet);
544 }
545
546 void StyleSheetContents::unregisterClient(CSSStyleSheet* sheet)
547 {
548     bool removed = m_clients.removeFirst(sheet);
549     ASSERT_UNUSED(removed, removed);
550 }
551
552 void StyleSheetContents::addedToMemoryCache()
553 {
554     ASSERT(isCacheable());
555     ++m_inMemoryCacheCount;
556 }
557
558 void StyleSheetContents::removedFromMemoryCache()
559 {
560     ASSERT(m_inMemoryCacheCount);
561     ASSERT(isCacheable());
562     --m_inMemoryCacheCount;
563 }
564
565 void StyleSheetContents::shrinkToFit()
566 {
567     m_importRules.shrinkToFit();
568     m_childRules.shrinkToFit();
569 }
570
571 }