Commit 830bbee9 authored by timloh@chromium.org's avatar timloh@chromium.org

CSSStyleSheet::insertRule should respect the default namespace

This patch fixes CSSStyleSheet::insertRule to respect the default
namespace a stylesheet. The default namespace is currently only stored
during initial parsing of a stylesheet, so this moves the value to be
stored on the StyleSheetContents. This is stored separately to the rest
of the namespaces to avoid an extra hash map lookup, but could probably
be added to the m_namespaces map keyed off a null AtomicString.

The added test passes in FF and IE, but fails in Safari.

Review URL: https://codereview.chromium.org/1322543004

git-svn-id: svn://svn.chromium.org/blink/trunk@201712 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 050985bf
<!doctype html>
<style>
@namespace url(banana);
*|div {
width: 100px;
height: 100px;
background-color: green;
}
</style>
<div id=target></div>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script>
test(function(){
document.styleSheets[0].insertRule('#target { background-color: red; }', 1);
assert_equals(getComputedStyle(target).backgroundColor, 'rgb(0, 128, 0)');
}, "CSSStyleSheet::insertRule should respect default namespaces");
</script>
......@@ -60,6 +60,7 @@ unsigned StyleSheetContents::estimatedSizeInBytes() const
StyleSheetContents::StyleSheetContents(StyleRuleImport* ownerRule, const String& originalURL, const CSSParserContext& context)
: m_ownerRule(ownerRule)
, m_originalURL(originalURL)
, m_defaultNamespace(starAtom)
, m_hasSyntacticallyValidCSSHeader(true)
, m_didLoadErrorOccur(false)
, m_isMutable(false)
......@@ -77,6 +78,7 @@ StyleSheetContents::StyleSheetContents(const StyleSheetContents& o)
, m_importRules(o.m_importRules.size())
, m_childRules(o.m_childRules.size())
, m_namespaces(o.m_namespaces)
, m_defaultNamespace(o.m_defaultNamespace)
, m_hasSyntacticallyValidCSSHeader(o.m_hasSyntacticallyValidCSSHeader)
, m_didLoadErrorOccur(false)
, m_isMutable(false)
......@@ -258,8 +260,11 @@ void StyleSheetContents::wrapperDeleteRule(unsigned index)
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;
......
......@@ -64,6 +64,7 @@ public:
const CSSParserContext& parserContext() const { return m_parserContext; }
const AtomicString& defaultNamespace() { return m_defaultNamespace; }
const AtomicString& determineNamespace(const AtomicString& prefix);
void parseAuthorStyleSheet(const CSSStyleSheetResource*, const SecurityOrigin*);
......@@ -174,6 +175,7 @@ private:
WillBeHeapVector<RefPtrWillBeMember<StyleRuleBase>> m_childRules;
typedef HashMap<AtomicString, AtomicString> PrefixNamespaceURIMap;
PrefixNamespaceURIMap m_namespaces;
AtomicString m_defaultNamespace;
bool m_hasSyntacticallyValidCSSHeader : 1;
bool m_didLoadErrorOccur : 1;
......
......@@ -33,7 +33,7 @@ void CSSParser::parseDeclarationListForInspector(const CSSParserContext& context
void CSSParser::parseSelector(const CSSParserContext& context, const String& selector, CSSSelectorList& selectorList)
{
CSSTokenizer::Scope scope(selector);
CSSSelectorParser::parseSelector(scope.tokenRange(), context, starAtom, nullptr, selectorList);
CSSSelectorParser::parseSelector(scope.tokenRange(), context, nullptr, selectorList);
}
PassRefPtrWillBeRawPtr<StyleRuleBase> CSSParser::parseRule(const CSSParserContext& context, StyleSheetContents* styleSheet, const String& rule)
......
......@@ -31,7 +31,6 @@ namespace blink {
CSSParserImpl::CSSParserImpl(const CSSParserContext& context, StyleSheetContents* styleSheet)
: m_context(context)
, m_defaultNamespace(starAtom)
, m_styleSheet(styleSheet)
, m_observerWrapper(nullptr)
{
......@@ -389,8 +388,6 @@ PassRefPtrWillBeRawPtr<StyleRuleNamespace> CSSParserImpl::consumeNamespaceRule(C
if (uri.isNull() || !prelude.atEnd())
return nullptr; // Parse error, expected string or URI
if (namespacePrefix.isNull())
m_defaultNamespace = uri;
return StyleRuleNamespace::create(namespacePrefix, uri);
}
......@@ -539,7 +536,7 @@ PassRefPtrWillBeRawPtr<StyleRulePage> CSSParserImpl::consumePageRule(CSSParserTo
OwnPtr<CSSParserSelector> selector;
if (!typeSelector.isNull() && pseudo.isNull()) {
selector = CSSParserSelector::create(QualifiedName(nullAtom, typeSelector, m_defaultNamespace));
selector = CSSParserSelector::create(QualifiedName(nullAtom, typeSelector, m_styleSheet->defaultNamespace()));
} else {
selector = CSSParserSelector::create();
if (!pseudo.isNull()) {
......@@ -549,7 +546,7 @@ PassRefPtrWillBeRawPtr<StyleRulePage> CSSParserImpl::consumePageRule(CSSParserTo
return nullptr; // Parse error; unknown page pseudo-class
}
if (!typeSelector.isNull())
selector->prependTagSelector(QualifiedName(nullAtom, typeSelector, m_defaultNamespace));
selector->prependTagSelector(QualifiedName(nullAtom, typeSelector, m_styleSheet->defaultNamespace()));
}
if (m_observerWrapper) {
......@@ -602,7 +599,7 @@ static void observeSelectors(CSSParserObserverWrapper& wrapper, CSSParserTokenRa
PassRefPtrWillBeRawPtr<StyleRule> CSSParserImpl::consumeStyleRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
{
CSSSelectorList selectorList;
CSSSelectorParser::parseSelector(prelude, m_context, m_defaultNamespace, m_styleSheet, selectorList);
CSSSelectorParser::parseSelector(prelude, m_context, m_styleSheet, selectorList);
if (!selectorList.isValid())
return nullptr; // Parse error, invalid selector list
......
......@@ -84,7 +84,7 @@ private:
static PassRefPtrWillBeRawPtr<StyleRuleCharset> consumeCharsetRule(CSSParserTokenRange prelude);
PassRefPtrWillBeRawPtr<StyleRuleImport> consumeImportRule(CSSParserTokenRange prelude);
PassRefPtrWillBeRawPtr<StyleRuleNamespace> consumeNamespaceRule(CSSParserTokenRange prelude); // This can set m_defaultNamespace
PassRefPtrWillBeRawPtr<StyleRuleNamespace> consumeNamespaceRule(CSSParserTokenRange prelude);
PassRefPtrWillBeRawPtr<StyleRuleMedia> consumeMediaRule(CSSParserTokenRange prelude, CSSParserTokenRange block);
PassRefPtrWillBeRawPtr<StyleRuleSupports> consumeSupportsRule(CSSParserTokenRange prelude, CSSParserTokenRange block);
PassRefPtrWillBeRawPtr<StyleRuleViewport> consumeViewportRule(CSSParserTokenRange prelude, CSSParserTokenRange block);
......@@ -106,7 +106,6 @@ private:
WillBeHeapVector<CSSProperty, 256> m_parsedProperties;
CSSParserContext m_context;
AtomicString m_defaultNamespace;
RawPtrWillBeMember<StyleSheetContents> m_styleSheet;
// For the inspector
......
......@@ -50,9 +50,9 @@ static void recordSelectorStats(const CSSParserContext& context, const CSSSelect
}
}
void CSSSelectorParser::parseSelector(CSSParserTokenRange range, const CSSParserContext& context, const AtomicString& defaultNamespace, StyleSheetContents* styleSheet, CSSSelectorList& output)
void CSSSelectorParser::parseSelector(CSSParserTokenRange range, const CSSParserContext& context, StyleSheetContents* styleSheet, CSSSelectorList& output)
{
CSSSelectorParser parser(context, defaultNamespace, styleSheet);
CSSSelectorParser parser(context, styleSheet);
range.consumeWhitespace();
CSSSelectorList result;
parser.consumeComplexSelectorList(range, result);
......@@ -63,9 +63,8 @@ void CSSSelectorParser::parseSelector(CSSParserTokenRange range, const CSSParser
ASSERT(!(output.isValid() && parser.m_failedParsing));
}
CSSSelectorParser::CSSSelectorParser(const CSSParserContext& context, const AtomicString& defaultNamespace, StyleSheetContents* styleSheet)
CSSSelectorParser::CSSSelectorParser(const CSSParserContext& context, StyleSheetContents* styleSheet)
: m_context(context)
, m_defaultNamespace(defaultNamespace)
, m_styleSheet(styleSheet)
, m_failedParsing(false)
{
......@@ -162,7 +161,7 @@ PassOwnPtr<CSSParserSelector> CSSSelectorParser::consumeCompoundSelector(CSSPars
if (!compoundSelector) {
if (hasNamespace)
return CSSParserSelector::create(determineNameInNamespace(namespacePrefix, elementName));
return CSSParserSelector::create(QualifiedName(nullAtom, elementName, m_defaultNamespace));
return CSSParserSelector::create(QualifiedName(nullAtom, elementName, defaultNamespace()));
}
prependTypeSelectorIfNeeded(namespacePrefix, elementName, compoundSelector.get());
return compoundSelector.release();
......@@ -536,20 +535,27 @@ bool CSSSelectorParser::consumeANPlusB(CSSParserTokenRange& range, std::pair<int
return true;
}
const AtomicString& CSSSelectorParser::defaultNamespace() const
{
if (!m_styleSheet)
return starAtom;
return m_styleSheet->defaultNamespace();
}
QualifiedName CSSSelectorParser::determineNameInNamespace(const AtomicString& prefix, const AtomicString& localName)
{
if (!m_styleSheet)
return QualifiedName(prefix, localName, m_defaultNamespace);
return QualifiedName(prefix, localName, defaultNamespace());
return QualifiedName(prefix, localName, m_styleSheet->determineNamespace(prefix));
}
void CSSSelectorParser::prependTypeSelectorIfNeeded(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector* compoundSelector)
{
if (elementName.isNull() && m_defaultNamespace == starAtom && !compoundSelector->crossesTreeScopes())
if (elementName.isNull() && defaultNamespace() == starAtom && !compoundSelector->crossesTreeScopes())
return;
AtomicString determinedElementName = elementName.isNull() ? starAtom : elementName;
AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleSheet ? m_styleSheet->determineNamespace(namespacePrefix) : m_defaultNamespace;
AtomicString determinedNamespace = namespacePrefix != nullAtom && m_styleSheet ? m_styleSheet->determineNamespace(namespacePrefix) : defaultNamespace();
QualifiedName tag(namespacePrefix, determinedElementName, determinedNamespace);
if (compoundSelector->crossesTreeScopes())
......
......@@ -19,12 +19,12 @@ class StyleSheetContents;
class CORE_EXPORT CSSSelectorParser {
STACK_ALLOCATED();
public:
static void parseSelector(CSSParserTokenRange, const CSSParserContext&, const AtomicString& defaultNamespace, StyleSheetContents*, CSSSelectorList&);
static void parseSelector(CSSParserTokenRange, const CSSParserContext&, StyleSheetContents*, CSSSelectorList&);
static bool consumeANPlusB(CSSParserTokenRange&, std::pair<int, int>&);
private:
CSSSelectorParser(const CSSParserContext&, const AtomicString& defaultNamespace, StyleSheetContents*);
CSSSelectorParser(const CSSParserContext&, StyleSheetContents*);
// These will all consume trailing comments if successful
......@@ -48,6 +48,7 @@ private:
CSSSelector::Match consumeAttributeMatch(CSSParserTokenRange&);
CSSSelector::AttributeMatchType consumeAttributeFlags(CSSParserTokenRange&);
const AtomicString& defaultNamespace() const;
QualifiedName determineNameInNamespace(const AtomicString& prefix, const AtomicString& localName);
void prependTypeSelectorIfNeeded(const AtomicString& namespacePrefix, const AtomicString& elementName, CSSParserSelector*);
void rewriteSpecifiersWithElementNameForCustomPseudoElement(const QualifiedName& tag, CSSParserSelector*, bool tagIsImplicit);
......@@ -55,7 +56,6 @@ private:
static PassOwnPtr<CSSParserSelector> addSimpleSelectorToCompound(PassOwnPtr<CSSParserSelector> compoundSelector, PassOwnPtr<CSSParserSelector> simpleSelector);
const CSSParserContext& m_context;
AtomicString m_defaultNamespace;
RawPtrWillBeMember<StyleSheetContents> m_styleSheet; // FIXME: Should be const
bool m_failedParsing;
......
......@@ -113,10 +113,12 @@ TEST(CSSSelectorParserTest, InvalidANPlusB)
}
}
TEST(CSSSelectorParserTest, ContentPseudoInCompound)
TEST(CSSSelectorParserTest, ShadowDomPseudoInCompound)
{
const char* testCases[][2] = {
{ "::content", "*::content" }, // crbug.com/478969
{ "::shadow", "*::shadow" }, // crbug.com/478969
{ ".a::shadow", ".a::shadow" },
{ "::content", "::content" },
{ ".a::content", ".a::content" },
{ "::content.a", ".a::content" },
{ "::content.a.b", ".b.a::content" },
......@@ -128,7 +130,7 @@ TEST(CSSSelectorParserTest, ContentPseudoInCompound)
CSSTokenizer::Scope scope(testCases[i][0]);
CSSParserTokenRange range = scope.tokenRange();
CSSSelectorList list;
CSSSelectorParser::parseSelector(range, CSSParserContext(HTMLStandardMode, nullptr), nullAtom, nullptr, list);
CSSSelectorParser::parseSelector(range, CSSParserContext(HTMLStandardMode, nullptr), nullptr, list);
EXPECT_STREQ(testCases[i][1], list.selectorsText().ascii().data());
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment