summaryrefslogtreecommitdiff
path: root/Source/WebCore/css/StyleSheetContents.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/css/StyleSheetContents.cpp')
-rw-r--r--Source/WebCore/css/StyleSheetContents.cpp298
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()