From 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 27 Jun 2017 06:07:23 +0000 Subject: webkitgtk-2.16.5 --- Source/WebCore/css/StyleSheetContents.cpp | 298 ++++++++++++++++-------------- 1 file changed, 157 insertions(+), 141 deletions(-) (limited to 'Source/WebCore/css/StyleSheetContents.cpp') 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 +#include #include 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 rule) +void StyleSheetContents::parserAppendRule(Ref&& rule) { ASSERT(!rule->isCharsetRule()); - if (rule->isImportRule()) { + + if (is(rule)) { // Parser enforces that @import rules come before anything else except @charset. ASSERT(m_childRules.isEmpty()); - m_importRules.append(static_cast(rule.get())); + m_importRules.append(downcast(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(rule.get())->mediaQueries()); -#endif + if (is(rule)) { + // Parser enforces that @namespace rules come before all rules other than + // import/charset rules + ASSERT(m_childRules.isEmpty()); + StyleRuleNamespace& namespaceRule = downcast(rule.get()); + parserAddNamespace(namespaceRule.prefix(), namespaceRule.uri()); + m_namespaceRules.append(downcast(rule.ptr())); + return; + } + + if (is(rule)) + reportMediaQueryWarningIfNeeded(singleOwnerDocument(), downcast(rule.get()).mediaQueries()); // NOTE: The selector list has to fit into RuleData. // 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> rules = toStyleRule(rule.get())->splitIntoMultipleRulesWithMaximumSelectorComponentCount(RuleData::maximumSelectorComponentCount); - m_childRules.appendVector(rules); + if (is(rule) && downcast(rule.get()).selectorList().componentCount() > RuleData::maximumSelectorComponentCount) { + m_childRules.appendVector(downcast(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 rule, unsigned index) +bool StyleSheetContents::wrapperInsertRule(Ref&& rule, unsigned index) { ASSERT(m_isMutable); ASSERT_WITH_SECURITY_IMPLICATION(index <= ruleCount()); @@ -210,31 +216,51 @@ bool StyleSheetContents::wrapperInsertRule(PassRefPtr 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(rule)) return false; - m_importRules.insert(childVectorIndex, static_cast(rule.get())); + m_importRules.insert(childVectorIndex, downcast(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(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(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(rule.get()); + m_namespaceRules.insert(index, downcast(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(rule)) + return false; + childVectorIndex -= m_namespaceRules.size(); + + // If the number of selectors would overflow RuleData, we drop the operation. + if (is(rule) && downcast(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 - - 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 . - 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 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 . - Ref protect(*this); + Ref 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& urls) +static bool traverseSubresourcesInRules(const Vector>& rules, const std::function& handler) { - Deque 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(rule)->properties().addSubresourceStyleURLs(urls, this); - else if (rule->isFontFaceRule()) - static_cast(rule)->properties().addSubresourceStyleURLs(urls, this); - } - } -} - -static bool childRulesHaveFailedOrCanceledSubresources(const Vector>& 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(rule)->properties().hasFailedOrCanceledSubresources()) + case StyleRuleBase::Style: { + auto* properties = downcast(*rule).propertiesWithoutDeferredParsing(); + if (properties && properties->traverseSubresources(handler)) return true; break; + } case StyleRuleBase::FontFace: - if (static_cast(rule)->properties().hasFailedOrCanceledSubresources()) + if (downcast(*rule).properties().traverseSubresources(handler)) return true; break; - case StyleRuleBase::Media: - if (childRulesHaveFailedOrCanceledSubresources(static_cast(rule)->childRules())) + case StyleRuleBase::Media: { + auto* childRules = downcast(*rule).childRulesWithoutDeferredParsing(); + if (childRules && traverseSubresourcesInRules(*childRules, handler)) return true; break; + } case StyleRuleBase::Region: - if (childRulesHaveFailedOrCanceledSubresources(static_cast(rule)->childRules())) - return true; - break; -#if ENABLE(SHADOW_DOM) - case StyleRuleBase::HostInternal: - if (childRulesHaveFailedOrCanceledSubresources(static_cast(rule)->childRules())) + if (traverseSubresourcesInRules(downcast(*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& 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() -- cgit v1.2.1