diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/css/StyleSheetContents.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/css/StyleSheetContents.cpp')
-rw-r--r-- | Source/WebCore/css/StyleSheetContents.cpp | 298 |
1 files changed, 157 insertions, 141 deletions
diff --git a/Source/WebCore/css/StyleSheetContents.cpp b/Source/WebCore/css/StyleSheetContents.cpp index 5f5a0d1c7..d893d3aec 100644 --- a/Source/WebCore/css/StyleSheetContents.cpp +++ b/Source/WebCore/css/StyleSheetContents.cpp @@ -28,12 +28,15 @@ #include "Document.h" #include "MediaList.h" #include "Node.h" +#include "Page.h" +#include "PageConsoleClient.h" #include "RuleSet.h" #include "SecurityOrigin.h" #include "StyleProperties.h" #include "StyleRule.h" #include "StyleRuleImport.h" #include <wtf/Deque.h> +#include <wtf/NeverDestroyed.h> #include <wtf/Ref.h> namespace WebCore { @@ -59,13 +62,8 @@ unsigned StyleSheetContents::estimatedSizeInBytes() const StyleSheetContents::StyleSheetContents(StyleRuleImport* ownerRule, const String& originalURL, const CSSParserContext& context) : m_ownerRule(ownerRule) , m_originalURL(originalURL) - , m_loadCompleted(false) + , m_defaultNamespace(starAtom) , m_isUserStyleSheet(ownerRule && ownerRule->parentStyleSheet() && ownerRule->parentStyleSheet()->isUserStyleSheet()) - , m_hasSyntacticallyValidCSSHeader(true) - , m_didLoadErrorOccur(false) - , m_usesRemUnits(false) - , m_isMutable(false) - , m_isInMemoryCache(false) , m_parserContext(context) { } @@ -76,15 +74,14 @@ StyleSheetContents::StyleSheetContents(const StyleSheetContents& o) , m_originalURL(o.m_originalURL) , m_encodingFromCharsetRule(o.m_encodingFromCharsetRule) , m_importRules(o.m_importRules.size()) + , m_namespaceRules(o.m_namespaceRules.size()) , m_childRules(o.m_childRules.size()) , m_namespaces(o.m_namespaces) - , m_loadCompleted(true) + , m_defaultNamespace(o.m_defaultNamespace) , m_isUserStyleSheet(o.m_isUserStyleSheet) + , m_loadCompleted(true) , m_hasSyntacticallyValidCSSHeader(o.m_hasSyntacticallyValidCSSHeader) - , m_didLoadErrorOccur(false) - , m_usesRemUnits(o.m_usesRemUnits) - , m_isMutable(false) - , m_isInMemoryCache(false) + , m_usesStyleBasedEditability(o.m_usesStyleBasedEditability) , m_parserContext(o.m_parserContext) { ASSERT(o.isCacheable()); @@ -124,33 +121,40 @@ bool StyleSheetContents::isCacheable() const return true; } -void StyleSheetContents::parserAppendRule(PassRefPtr<StyleRuleBase> rule) +void StyleSheetContents::parserAppendRule(Ref<StyleRuleBase>&& rule) { ASSERT(!rule->isCharsetRule()); - if (rule->isImportRule()) { + + if (is<StyleRuleImport>(rule)) { // Parser enforces that @import rules come before anything else except @charset. ASSERT(m_childRules.isEmpty()); - m_importRules.append(static_cast<StyleRuleImport*>(rule.get())); + m_importRules.append(downcast<StyleRuleImport>(rule.ptr())); m_importRules.last()->setParentStyleSheet(this); m_importRules.last()->requestStyleSheet(); return; } -#if ENABLE(RESOLUTION_MEDIA_QUERY) - // Add warning message to inspector if dpi/dpcm values are used for screen media. - if (rule->isMediaRule()) - reportMediaQueryWarningIfNeeded(singleOwnerDocument(), static_cast<StyleRuleMedia*>(rule.get())->mediaQueries()); -#endif + if (is<StyleRuleNamespace>(rule)) { + // Parser enforces that @namespace rules come before all rules other than + // import/charset rules + ASSERT(m_childRules.isEmpty()); + StyleRuleNamespace& namespaceRule = downcast<StyleRuleNamespace>(rule.get()); + parserAddNamespace(namespaceRule.prefix(), namespaceRule.uri()); + m_namespaceRules.append(downcast<StyleRuleNamespace>(rule.ptr())); + return; + } + + if (is<StyleRuleMedia>(rule)) + reportMediaQueryWarningIfNeeded(singleOwnerDocument(), downcast<StyleRuleMedia>(rule.get()).mediaQueries()); // NOTE: The selector list has to fit into RuleData. <http://webkit.org/b/118369> // If we're adding a rule with a huge number of selectors, split it up into multiple rules - if (rule->isStyleRule() && toStyleRule(rule.get())->selectorList().componentCount() > RuleData::maximumSelectorComponentCount) { - Vector<RefPtr<StyleRule>> rules = toStyleRule(rule.get())->splitIntoMultipleRulesWithMaximumSelectorComponentCount(RuleData::maximumSelectorComponentCount); - m_childRules.appendVector(rules); + if (is<StyleRule>(rule) && downcast<StyleRule>(rule.get()).selectorList().componentCount() > RuleData::maximumSelectorComponentCount) { + m_childRules.appendVector(downcast<StyleRule>(rule.get()).splitIntoMultipleRulesWithMaximumSelectorComponentCount(RuleData::maximumSelectorComponentCount)); return; } - m_childRules.append(rule); + m_childRules.append(WTFMove(rule)); } StyleRuleBase* StyleSheetContents::ruleAt(unsigned index) const @@ -158,23 +162,24 @@ StyleRuleBase* StyleSheetContents::ruleAt(unsigned index) const ASSERT_WITH_SECURITY_IMPLICATION(index < ruleCount()); unsigned childVectorIndex = index; - if (hasCharsetRule()) { - if (index == 0) - return 0; - --childVectorIndex; - } if (childVectorIndex < m_importRules.size()) return m_importRules[childVectorIndex].get(); childVectorIndex -= m_importRules.size(); + + if (childVectorIndex < m_namespaceRules.size()) + return m_namespaceRules[childVectorIndex].get(); + + childVectorIndex -= m_namespaceRules.size(); + return m_childRules[childVectorIndex].get(); } unsigned StyleSheetContents::ruleCount() const { unsigned result = 0; - result += hasCharsetRule() ? 1 : 0; result += m_importRules.size(); + result += m_namespaceRules.size(); result += m_childRules.size(); return result; } @@ -191,6 +196,7 @@ void StyleSheetContents::clearRules() m_importRules[i]->clearParentStyleSheet(); } m_importRules.clear(); + m_namespaceRules.clear(); m_childRules.clear(); clearCharsetRule(); } @@ -202,7 +208,7 @@ void StyleSheetContents::parserSetEncodingFromCharsetRule(const String& encoding m_encodingFromCharsetRule = encoding; } -bool StyleSheetContents::wrapperInsertRule(PassRefPtr<StyleRuleBase> rule, unsigned index) +bool StyleSheetContents::wrapperInsertRule(Ref<StyleRuleBase>&& rule, unsigned index) { ASSERT(m_isMutable); ASSERT_WITH_SECURITY_IMPLICATION(index <= ruleCount()); @@ -210,31 +216,51 @@ bool StyleSheetContents::wrapperInsertRule(PassRefPtr<StyleRuleBase> rule, unsig ASSERT(!rule->isCharsetRule()); unsigned childVectorIndex = index; - // m_childRules does not contain @charset which is always in index 0 if it exists. - if (hasCharsetRule()) { - if (childVectorIndex == 0) { - // Nothing can be inserted before @charset. - return false; - } - --childVectorIndex; - } - if (childVectorIndex < m_importRules.size() || (childVectorIndex == m_importRules.size() && rule->isImportRule())) { // Inserting non-import rule before @import is not allowed. - if (!rule->isImportRule()) + if (!is<StyleRuleImport>(rule)) return false; - m_importRules.insert(childVectorIndex, static_cast<StyleRuleImport*>(rule.get())); + m_importRules.insert(childVectorIndex, downcast<StyleRuleImport>(rule.ptr())); m_importRules[childVectorIndex]->setParentStyleSheet(this); m_importRules[childVectorIndex]->requestStyleSheet(); // FIXME: Stylesheet doesn't actually change meaningfully before the imported sheets are loaded. return true; } // Inserting @import rule after a non-import rule is not allowed. - if (rule->isImportRule()) + if (is<StyleRuleImport>(rule)) return false; childVectorIndex -= m_importRules.size(); - - m_childRules.insert(childVectorIndex, rule); + + + if (childVectorIndex < m_namespaceRules.size() || (childVectorIndex == m_namespaceRules.size() && rule->isNamespaceRule())) { + // Inserting non-namespace rules other than import rule before @namespace is + // not allowed. + if (!is<StyleRuleNamespace>(rule)) + return false; + // Inserting @namespace rule when rules other than import/namespace/charset + // are present is not allowed. + if (!m_childRules.isEmpty()) + return false; + + StyleRuleNamespace& namespaceRule = downcast<StyleRuleNamespace>(rule.get()); + m_namespaceRules.insert(index, downcast<StyleRuleNamespace>(rule.ptr())); + + // For now to be compatible with IE and Firefox if a namespace rule with the same + // prefix is added, it overwrites previous ones. + // FIXME: The eventual correct behavior would be to ensure that the last value in + // the list wins. + parserAddNamespace(namespaceRule.prefix(), namespaceRule.uri()); + return true; + } + if (is<StyleRuleNamespace>(rule)) + return false; + childVectorIndex -= m_namespaceRules.size(); + + // If the number of selectors would overflow RuleData, we drop the operation. + if (is<StyleRule>(rule) && downcast<StyleRule>(rule.get()).selectorList().componentCount() > RuleData::maximumSelectorComponentCount) + return false; + + m_childRules.insert(childVectorIndex, WTFMove(rule)); return true; } @@ -244,13 +270,6 @@ void StyleSheetContents::wrapperDeleteRule(unsigned index) ASSERT_WITH_SECURITY_IMPLICATION(index < ruleCount()); unsigned childVectorIndex = index; - if (hasCharsetRule()) { - if (childVectorIndex == 0) { - clearCharsetRule(); - return; - } - --childVectorIndex; - } if (childVectorIndex < m_importRules.size()) { m_importRules[childVectorIndex]->clearParentStyleSheet(); m_importRules.remove(childVectorIndex); @@ -258,25 +277,32 @@ void StyleSheetContents::wrapperDeleteRule(unsigned index) } childVectorIndex -= m_importRules.size(); + if (childVectorIndex < m_namespaceRules.size()) { + if (!m_childRules.isEmpty()) + return; + m_namespaceRules.remove(childVectorIndex); + return; + } + childVectorIndex -= m_namespaceRules.size(); + m_childRules.remove(childVectorIndex); } void StyleSheetContents::parserAddNamespace(const AtomicString& prefix, const AtomicString& uri) { - if (uri.isNull() || prefix.isNull()) + ASSERT(!uri.isNull()); + if (prefix.isNull()) { + m_defaultNamespace = uri; return; + } PrefixNamespaceURIMap::AddResult result = m_namespaces.add(prefix, uri); if (result.isNewEntry) return; result.iterator->value = uri; } -const AtomicString& StyleSheetContents::determineNamespace(const AtomicString& prefix) +const AtomicString& StyleSheetContents::namespaceURIFromPrefix(const AtomicString& prefix) { - if (prefix.isNull()) - return nullAtom; // No namespace. If an element/attribute has a namespace, we won't match it. - if (prefix == starAtom) - return starAtom; // We'll match any namespace. PrefixNamespaceURIMap::const_iterator it = m_namespaces.find(prefix); if (it == m_namespaces.end()) return nullAtom; @@ -285,46 +311,42 @@ const AtomicString& StyleSheetContents::determineNamespace(const AtomicString& p void StyleSheetContents::parseAuthorStyleSheet(const CachedCSSStyleSheet* cachedStyleSheet, const SecurityOrigin* securityOrigin) { - // Check to see if we should enforce the MIME type of the CSS resource in strict mode. - // Running in iWeb 2 is one example of where we don't want to - <rdar://problem/6099748> - bool enforceMIMEType = isStrictParserMode(m_parserContext.mode) && m_parserContext.enforcesCSSMIMETypeInNoQuirksMode; - bool hasValidMIMEType = false; - String sheetText = cachedStyleSheet->sheetText(enforceMIMEType, &hasValidMIMEType); - - CSSParser p(parserContext()); - p.parseSheet(this, sheetText, 0, 0, true); - - // If we're loading a stylesheet cross-origin, and the MIME type is not standard, require the CSS - // to at least start with a syntactically valid CSS rule. - // This prevents an attacker playing games by injecting CSS strings into HTML, XML, JSON, etc. etc. - if (!hasValidMIMEType && !hasSyntacticallyValidCSSHeader()) { - bool isCrossOriginCSS = !securityOrigin || !securityOrigin->canRequest(baseURL()); - if (isCrossOriginCSS) { - clearRules(); - return; + bool isSameOriginRequest = securityOrigin && securityOrigin->canRequest(baseURL()); + CachedCSSStyleSheet::MIMETypeCheck mimeTypeCheck = isStrictParserMode(m_parserContext.mode) || !isSameOriginRequest ? CachedCSSStyleSheet::MIMETypeCheck::Strict : CachedCSSStyleSheet::MIMETypeCheck::Lax; + bool hasValidMIMEType = true; + String sheetText = cachedStyleSheet->sheetText(mimeTypeCheck, &hasValidMIMEType); + + if (!hasValidMIMEType) { + ASSERT(sheetText.isNull()); + if (auto* document = singleOwnerDocument()) { + if (auto* page = document->page()) { + if (isStrictParserMode(m_parserContext.mode)) + 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."); + else + 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."); + } } + return; } + + CSSParser p(parserContext()); + p.parseSheet(this, sheetText, CSSParser::RuleParsing::Deferred); + if (m_parserContext.needsSiteSpecificQuirks && isStrictParserMode(m_parserContext.mode)) { // Work around <https://bugs.webkit.org/show_bug.cgi?id=28350>. - DEFINE_STATIC_LOCAL(const String, mediaWikiKHTMLFixesStyleSheet, (ASCIILiteral("/* KHTML fix stylesheet */\n/* work around the horizontal scrollbars */\n#column-content { margin-left: 0; }\n\n"))); + static NeverDestroyed<const String> mediaWikiKHTMLFixesStyleSheet(ASCIILiteral("/* KHTML fix stylesheet */\n/* work around the horizontal scrollbars */\n#column-content { margin-left: 0; }\n\n")); // There are two variants of KHTMLFixes.css. One is equal to mediaWikiKHTMLFixesStyleSheet, // while the other lacks the second trailing newline. - if (baseURL().string().endsWith("/KHTMLFixes.css") && !sheetText.isNull() && mediaWikiKHTMLFixesStyleSheet.startsWith(sheetText) - && sheetText.length() >= mediaWikiKHTMLFixesStyleSheet.length() - 1) + if (baseURL().string().endsWith("/KHTMLFixes.css") && !sheetText.isNull() && mediaWikiKHTMLFixesStyleSheet.get().startsWith(sheetText) + && sheetText.length() >= mediaWikiKHTMLFixesStyleSheet.get().length() - 1) clearRules(); } } bool StyleSheetContents::parseString(const String& sheetText) { - return parseStringAtLine(sheetText, 0, false); -} - -bool StyleSheetContents::parseStringAtLine(const String& sheetText, int startLineNumber, bool createdByParser) -{ CSSParser p(parserContext()); - p.parseSheet(this, sheetText, startLineNumber, 0, createdByParser); - + p.parseSheet(this, sheetText, parserContext().mode != UASheetMode ? CSSParser::RuleParsing::Deferred : CSSParser::RuleParsing::Normal); return true; } @@ -342,10 +364,7 @@ void StyleSheetContents::checkLoaded() if (isLoading()) return; - // Avoid |this| being deleted by scripts that run via - // ScriptableDocumentParser::executeScriptsWaitingForStylesheets(). - // See <rdar://problem/6622300>. - Ref<StyleSheetContents> protect(*this); + Ref<StyleSheetContents> protectedThis(*this); StyleSheetContents* parentSheet = parentStyleSheet(); if (parentSheet) { parentSheet->checkLoaded(); @@ -399,61 +418,33 @@ Document* StyleSheetContents::singleOwnerDocument() const URL StyleSheetContents::completeURL(const String& url) const { - return CSSParser::completeURL(m_parserContext, url); + return m_parserContext.completeURL(url); } -void StyleSheetContents::addSubresourceStyleURLs(ListHashSet<URL>& urls) +static bool traverseSubresourcesInRules(const Vector<RefPtr<StyleRuleBase>>& rules, const std::function<bool (const CachedResource&)>& handler) { - Deque<StyleSheetContents*> styleSheetQueue; - styleSheetQueue.append(this); - - while (!styleSheetQueue.isEmpty()) { - StyleSheetContents* styleSheet = styleSheetQueue.takeFirst(); - - for (unsigned i = 0; i < styleSheet->m_importRules.size(); ++i) { - StyleRuleImport* importRule = styleSheet->m_importRules[i].get(); - if (importRule->styleSheet()) { - styleSheetQueue.append(importRule->styleSheet()); - addSubresourceURL(urls, importRule->styleSheet()->baseURL()); - } - } - for (unsigned i = 0; i < styleSheet->m_childRules.size(); ++i) { - StyleRuleBase* rule = styleSheet->m_childRules[i].get(); - if (rule->isStyleRule()) - static_cast<StyleRule*>(rule)->properties().addSubresourceStyleURLs(urls, this); - else if (rule->isFontFaceRule()) - static_cast<StyleRuleFontFace*>(rule)->properties().addSubresourceStyleURLs(urls, this); - } - } -} - -static bool childRulesHaveFailedOrCanceledSubresources(const Vector<RefPtr<StyleRuleBase>>& rules) -{ - for (unsigned i = 0; i < rules.size(); ++i) { - const StyleRuleBase* rule = rules[i].get(); + for (auto& rule : rules) { switch (rule->type()) { - case StyleRuleBase::Style: - if (static_cast<const StyleRule*>(rule)->properties().hasFailedOrCanceledSubresources()) + case StyleRuleBase::Style: { + auto* properties = downcast<StyleRule>(*rule).propertiesWithoutDeferredParsing(); + if (properties && properties->traverseSubresources(handler)) return true; break; + } case StyleRuleBase::FontFace: - if (static_cast<const StyleRuleFontFace*>(rule)->properties().hasFailedOrCanceledSubresources()) + if (downcast<StyleRuleFontFace>(*rule).properties().traverseSubresources(handler)) return true; break; - case StyleRuleBase::Media: - if (childRulesHaveFailedOrCanceledSubresources(static_cast<const StyleRuleMedia*>(rule)->childRules())) + case StyleRuleBase::Media: { + auto* childRules = downcast<StyleRuleMedia>(*rule).childRulesWithoutDeferredParsing(); + if (childRules && traverseSubresourcesInRules(*childRules, handler)) return true; break; + } case StyleRuleBase::Region: - if (childRulesHaveFailedOrCanceledSubresources(static_cast<const StyleRuleRegion*>(rule)->childRules())) - return true; - break; -#if ENABLE(SHADOW_DOM) - case StyleRuleBase::HostInternal: - if (childRulesHaveFailedOrCanceledSubresources(static_cast<const StyleRuleHost*>(rule)->childRules())) + if (traverseSubresourcesInRules(downcast<StyleRuleRegion>(*rule).childRules(), handler)) return true; break; -#endif case StyleRuleBase::Import: ASSERT_NOT_REACHED(); #if ASSERT_DISABLED @@ -461,12 +452,11 @@ static bool childRulesHaveFailedOrCanceledSubresources(const Vector<RefPtr<Style #endif case StyleRuleBase::Page: case StyleRuleBase::Keyframes: + case StyleRuleBase::Namespace: case StyleRuleBase::Unknown: case StyleRuleBase::Charset: case StyleRuleBase::Keyframe: -#if ENABLE(CSS3_CONDITIONAL_RULES) case StyleRuleBase::Supports: -#endif #if ENABLE(CSS_DEVICE_ADAPTATION) case StyleRuleBase::Viewport: #endif @@ -476,10 +466,38 @@ static bool childRulesHaveFailedOrCanceledSubresources(const Vector<RefPtr<Style return false; } -bool StyleSheetContents::hasFailedOrCanceledSubresources() const +bool StyleSheetContents::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const { - ASSERT(isCacheable()); - return childRulesHaveFailedOrCanceledSubresources(m_childRules); + for (auto& importRule : m_importRules) { + if (auto* cachedResource = importRule->cachedCSSStyleSheet()) { + if (handler(*cachedResource)) + return true; + } + auto* importedStyleSheet = importRule->styleSheet(); + if (importedStyleSheet && importedStyleSheet->traverseSubresources(handler)) + return true; + } + return traverseSubresourcesInRules(m_childRules, handler); +} + +bool StyleSheetContents::subresourcesAllowReuse(CachePolicy cachePolicy) const +{ + bool hasFailedOrExpiredResources = traverseSubresources([cachePolicy](const CachedResource& resource) { + if (resource.loadFailedOrCanceled()) + return true; + // We can't revalidate subresources individually so don't use reuse the parsed sheet if they need revalidation. + if (resource.makeRevalidationDecision(cachePolicy) != CachedResource::RevalidationDecision::No) + return true; + return false; + }); + return !hasFailedOrExpiredResources; +} + +bool StyleSheetContents::isLoadingSubresources() const +{ + return traverseSubresources([](const CachedResource& resource) { + return resource.isLoading(); + }); } StyleSheetContents* StyleSheetContents::parentStyleSheet() const @@ -495,23 +513,21 @@ void StyleSheetContents::registerClient(CSSStyleSheet* sheet) void StyleSheetContents::unregisterClient(CSSStyleSheet* sheet) { - size_t position = m_clients.find(sheet); - ASSERT(position != notFound); - m_clients.remove(position); + bool removed = m_clients.removeFirst(sheet); + ASSERT_UNUSED(removed, removed); } void StyleSheetContents::addedToMemoryCache() { - ASSERT(!m_isInMemoryCache); ASSERT(isCacheable()); - m_isInMemoryCache = true; + ++m_inMemoryCacheCount; } void StyleSheetContents::removedFromMemoryCache() { - ASSERT(m_isInMemoryCache); + ASSERT(m_inMemoryCacheCount); ASSERT(isCacheable()); - m_isInMemoryCache = false; + --m_inMemoryCacheCount; } void StyleSheetContents::shrinkToFit() |