selectors should match attribute name with case sensitivity based on element & docume...
authorrwlbuis@webkit.org <rwlbuis@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Aug 2013 01:00:10 +0000 (01:00 +0000)
committerrwlbuis@webkit.org <rwlbuis@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Aug 2013 01:00:10 +0000 (01:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=71152

Reviewed by Darin Adler.

Source/WebCore:

Support case-sensitive attribute name selecting for non HTML. In order to do this we have to
store the attribute name in the selector as-is when css parsing, and get the lowercase localName
on demand for case-insensitive matching. The only time we want case-insensitive matching is when
we try to match a HTML element in a HTML document.

Tests: fast/dom/SelectorAPI/attrname-case-insensitive.html
       fast/dom/SelectorAPI/attrname-case-sensitive.xhtml
       svg/css/case-sensitive-attrname-selectors.html

* css/CSSGrammar.y.in: do not lowercase attribute selector name.
* css/CSSParserValues.h:
(WebCore::CSSParserSelector::setAttribute):
* css/CSSSelector.cpp:
(WebCore::CSSSelector::setAttribute):
* css/CSSSelector.h: allow access to lowered version of attribute localName if needed.
(WebCore::CSSSelector::attributeCanonicalLocalName):
* css/SelectorChecker.cpp:
(WebCore::anyAttributeMatches): do only case-insensitive matching for HTML.
(WebCore::SelectorChecker::checkOne):
* css/SelectorChecker.h:
(WebCore::SelectorChecker::checkExactAttribute): do only case-insensitive matching for HTML.
* css/SelectorCheckerFastPath.cpp:
(WebCore::HTMLNames::checkExactAttributeValue):
* css/SelectorCheckerFastPath.h:
(WebCore::SelectorCheckerFastPath::matchesRightmostAttributeSelector):
* dom/Attribute.h:
(WebCore::Attribute::matches): use more convenient parameters.

LayoutTests:

Results matches FireFox nightly.

* fast/dom/SelectorAPI/attrname-case-insensitive-expected.txt: Added.
* fast/dom/SelectorAPI/attrname-case-insensitive.html: Check that in HTML documents CSS selectors
use case-insensitive attribute name matching for HTML elements, case-sensitive otherwise.
* fast/dom/SelectorAPI/attrname-case-sensitive-expected.txt: Added.
* fast/dom/SelectorAPI/attrname-case-sensitive.xhtml: Check that in XHTML documents CSS selectors
always use case-sensitive attribute name matching.
* svg/css/case-sensitive-attrname-selectors-expected.txt: Added.
* svg/css/case-sensitive-attrname-selectors.html: Check that in HTML documents CSS selectors in stylesheets
use case-insensitive attribute name matching for HTML elements, case-sensitive otherwise.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@153631 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/SelectorAPI/attrname-case-insensitive-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/SelectorAPI/attrname-case-insensitive.html [new file with mode: 0644]
LayoutTests/fast/dom/SelectorAPI/attrname-case-sensitive-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/SelectorAPI/attrname-case-sensitive.xhtml [new file with mode: 0644]
LayoutTests/svg/css/case-sensitive-attrname-selectors-expected.txt [new file with mode: 0644]
LayoutTests/svg/css/case-sensitive-attrname-selectors.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/CSSGrammar.y.in
Source/WebCore/css/CSSParserValues.h
Source/WebCore/css/CSSSelector.cpp
Source/WebCore/css/CSSSelector.h
Source/WebCore/css/SelectorChecker.cpp
Source/WebCore/css/SelectorChecker.h
Source/WebCore/css/SelectorCheckerFastPath.cpp
Source/WebCore/css/SelectorCheckerFastPath.h
Source/WebCore/dom/Attribute.h

index 13cf4af..390bb30 100644 (file)
@@ -1,3 +1,22 @@
+2013-08-01  Rob Buis  <rwlbuis@webkit.org>
+
+        selectors should match attribute name with case sensitivity based on element & document type
+        https://bugs.webkit.org/show_bug.cgi?id=71152
+
+        Reviewed by Darin Adler.
+
+        Results matches FireFox nightly.
+
+        * fast/dom/SelectorAPI/attrname-case-insensitive-expected.txt: Added.
+        * fast/dom/SelectorAPI/attrname-case-insensitive.html: Check that in HTML documents CSS selectors
+        use case-insensitive attribute name matching for HTML elements, case-sensitive otherwise.
+        * fast/dom/SelectorAPI/attrname-case-sensitive-expected.txt: Added.
+        * fast/dom/SelectorAPI/attrname-case-sensitive.xhtml: Check that in XHTML documents CSS selectors
+        always use case-sensitive attribute name matching.
+        * svg/css/case-sensitive-attrname-selectors-expected.txt: Added.
+        * svg/css/case-sensitive-attrname-selectors.html: Check that in HTML documents CSS selectors in stylesheets
+        use case-insensitive attribute name matching for HTML elements, case-sensitive otherwise.
+
 2013-08-01  Alexey Proskuryakov  <ap@apple.com>
 
         <rdar://problem/14625616> http/tests/download/basic-ascii.html and http/tests/download/literal-utf-8.html fails
diff --git a/LayoutTests/fast/dom/SelectorAPI/attrname-case-insensitive-expected.txt b/LayoutTests/fast/dom/SelectorAPI/attrname-case-insensitive-expected.txt
new file mode 100644 (file)
index 0000000..3d06534
--- /dev/null
@@ -0,0 +1,28 @@
+PASS document.querySelector('div[baR]') is document.getElementById('bar')
+PASS document.getElementById('bar').webkitMatchesSelector('div[baR]') is true
+PASS document.querySelector('div[bar]') is document.getElementById('bar')
+PASS document.getElementById('bar').webkitMatchesSelector('div[bar]') is true
+PASS document.querySelector('div[BAR]') is document.getElementById('bar')
+PASS document.getElementById('bar').webkitMatchesSelector('div[BAR]') is true
+PASS document.querySelector('div[bAR]') is document.getElementById('bar')
+PASS document.getElementById('bar').webkitMatchesSelector('div[bAR]') is true
+PASS document.querySelector('div[baR="10"]') is document.getElementById('bar')
+PASS document.getElementById('bar').webkitMatchesSelector('div[baR="10"]') is true
+PASS document.querySelector('div[bar="10"]') is document.getElementById('bar')
+PASS document.getElementById('bar').webkitMatchesSelector('div[bar="10"]') is true
+PASS document.querySelector('div[BAR="10"]') is document.getElementById('bar')
+PASS document.getElementById('bar').webkitMatchesSelector('div[BAR="10"]') is true
+PASS document.querySelector('div[bAR="10"]') is document.getElementById('bar')
+PASS document.getElementById('bar').webkitMatchesSelector('div[bAR="10"]') is true
+PASS document.querySelector('path[pathLength]') is document.getElementById('pa')
+PASS document.getElementById('pa').webkitMatchesSelector('path[pathLength]') is true
+PASS content.querySelector('path[pathlength]') is null
+PASS content.querySelector('path[pathLengTh]') is null
+PASS document.querySelector('path[pathLength="200"]') is document.getElementById('pa')
+PASS document.getElementById('pa').webkitMatchesSelector('path[pathLength="200"]') is true
+PASS content.querySelector('path[pathlength="200"]') is null
+PASS content.querySelector('path[pathLengTh="200"]') is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/SelectorAPI/attrname-case-insensitive.html b/LayoutTests/fast/dom/SelectorAPI/attrname-case-insensitive.html
new file mode 100644 (file)
index 0000000..f0a2afd
--- /dev/null
@@ -0,0 +1,48 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+    <!-- Check that in HTML documents CSS selectors use case-insensitive attribute name matching for HTML elements, case-sensitive otherwise. -->
+    <div id="content">
+        <div style="display: none">
+            <div baR="10" id="bar"></div>
+
+            <svg xmlns="http://www.w3.org/2000/svg">
+                <path id="pa" pathLength="200"/>
+            </svg>
+        </div>
+    </div>
+    <script>
+        var content = document.getElementById("content");
+        function checkMatchingSelector(selector, elementId) {
+            shouldBe("document.querySelector('" + selector + "')", "document.getElementById('" + elementId + "')");
+            shouldBeTrue("document.getElementById('" + elementId + "').webkitMatchesSelector('" + selector + "')");
+        }
+
+        function checkNonMatchingSelector(selector) {
+            shouldBeNull("content.querySelector('" + selector + "')");
+        }
+
+        // HTML should be matched case-insensitive
+        checkMatchingSelector('div[baR]', "bar");
+        checkMatchingSelector('div[bar]', "bar");
+        checkMatchingSelector('div[BAR]', "bar");
+        checkMatchingSelector('div[bAR]', "bar");
+        checkMatchingSelector('div[baR="10"]', "bar");
+        checkMatchingSelector('div[bar="10"]', "bar");
+        checkMatchingSelector('div[BAR="10"]', "bar");
+        checkMatchingSelector('div[bAR="10"]', "bar");
+
+        // non HTML should be matched case-sensitive
+        checkMatchingSelector('path[pathLength]', "pa");
+        checkNonMatchingSelector('path[pathlength]');
+        checkNonMatchingSelector('path[pathLengTh]');
+        checkMatchingSelector('path[pathLength="200"]', "pa");
+        checkNonMatchingSelector('path[pathlength="200"]');
+        checkNonMatchingSelector('path[pathLengTh="200"]');
+    </script>
+    <script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/dom/SelectorAPI/attrname-case-sensitive-expected.txt b/LayoutTests/fast/dom/SelectorAPI/attrname-case-sensitive-expected.txt
new file mode 100644 (file)
index 0000000..961dabe
--- /dev/null
@@ -0,0 +1,22 @@
+PASS document.querySelector('div[baR]') is document.getElementById('bar')
+PASS document.getElementById('bar').webkitMatchesSelector('div[baR]') is true
+PASS content.querySelector('div[bar]') is null
+PASS content.querySelector('div[BAR]') is null
+PASS content.querySelector('div[bAR]') is null
+PASS document.querySelector('div[baR="10"]') is document.getElementById('bar')
+PASS document.getElementById('bar').webkitMatchesSelector('div[baR="10"]') is true
+PASS content.querySelector('div[bar="10"]') is null
+PASS content.querySelector('div[BAR="10"]') is null
+PASS content.querySelector('div[bAR="10"]') is null
+PASS document.querySelector('path[pathLength]') is document.getElementById('pa')
+PASS document.getElementById('pa').webkitMatchesSelector('path[pathLength]') is true
+PASS content.querySelector('path[pathlength]') is null
+PASS content.querySelector('path[pathLengTh]') is null
+PASS document.querySelector('path[pathLength="200"]') is document.getElementById('pa')
+PASS document.getElementById('pa').webkitMatchesSelector('path[pathLength="200"]') is true
+PASS content.querySelector('path[pathlength="200"]') is null
+PASS content.querySelector('path[pathLengTh="200"]') is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/SelectorAPI/attrname-case-sensitive.xhtml b/LayoutTests/fast/dom/SelectorAPI/attrname-case-sensitive.xhtml
new file mode 100644 (file)
index 0000000..2f19363
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html
+     PUBLIC "-//W3C//DTD XHTML 1.1 Strict//EN"
+     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<script src="../../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+    <!-- Check that in XHTML documents CSS selectors always use case-sensitive attribute name matching. -->
+    <div id="content">
+        <div style="display: none">
+            <div baR="10" id="bar"></div>
+
+            <svg xmlns="http://www.w3.org/2000/svg">
+                <path id="pa" pathLength="200"/>
+            </svg>
+        </div>
+    </div>
+    <script>
+        var content = document.getElementById("content");
+        function checkMatchingSelector(selector, elementId) {
+            shouldBe("document.querySelector('" + selector + "')", "document.getElementById('" + elementId + "')");
+            shouldBeTrue("document.getElementById('" + elementId + "').webkitMatchesSelector('" + selector + "')");
+        }
+
+        function checkNonMatchingSelector(selector) {
+            shouldBeNull("content.querySelector('" + selector + "')");
+        }
+
+        // everything should match case-sensitive
+        checkMatchingSelector('div[baR]', "bar");
+        checkNonMatchingSelector('div[bar]', "bar");
+        checkNonMatchingSelector('div[BAR]', "bar");
+        checkNonMatchingSelector('div[bAR]', "bar");
+        checkMatchingSelector('div[baR="10"]', "bar");
+        checkNonMatchingSelector('div[bar="10"]', "bar");
+        checkNonMatchingSelector('div[BAR="10"]', "bar");
+        checkNonMatchingSelector('div[bAR="10"]', "bar");
+
+        checkMatchingSelector('path[pathLength]', "pa");
+        checkNonMatchingSelector('path[pathlength]');
+        checkNonMatchingSelector('path[pathLengTh]');
+        checkMatchingSelector('path[pathLength="200"]', "pa");
+        checkNonMatchingSelector('path[pathlength="200"]');
+        checkNonMatchingSelector('path[pathLengTh="200"]');
+    </script>
+    <script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/svg/css/case-sensitive-attrname-selectors-expected.txt b/LayoutTests/svg/css/case-sensitive-attrname-selectors-expected.txt
new file mode 100644 (file)
index 0000000..6530c73
--- /dev/null
@@ -0,0 +1,7 @@
+PASS window.getComputedStyle(document.getElementById("div1")).color is "rgb(0, 128, 0)"
+PASS window.getComputedStyle(document.getElementById("path1")).color is "rgb(0, 128, 0)"
+PASS window.getComputedStyle(document.getElementById("path2")).color is "rgb(0, 128, 0)"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/svg/css/case-sensitive-attrname-selectors.html b/LayoutTests/svg/css/case-sensitive-attrname-selectors.html
new file mode 100644 (file)
index 0000000..a9c633f
--- /dev/null
@@ -0,0 +1,48 @@
+<!doctype html>
+<html>
+    <head>
+        <style>
+            div[BAR] {
+                color: green;
+            }
+
+            path {
+                color: red;
+            }
+            *[pathLength] {
+                color: green;
+            }
+            *[pathlength] {
+                color: red;
+            }
+            *[pathLength="100"] {
+                color: green;
+            }
+            *[pathlength="100"] {
+                color: red;
+            }
+        </style>
+        <script src="../../fast/js/resources/js-test-pre.js"></script>
+    </head>
+    <body>
+        <div id="container">
+            <div id="div1" baR="10"></div>
+
+            <svg xmlns="http://www.w3.org/2000/svg">
+                <path id="path1" pathLength="200"/>
+                <path id="path2" pathLength="100"/>
+            </svg>
+        </div>
+        <script>
+            function checkStyle(prefix, count, property, value) {
+                for (var i = 1; i <= count; ++i)
+                    shouldBe("window.getComputedStyle(document.getElementById(\"" + prefix + i + "\"))." + property, "\"" + value + "\"");
+            }
+            checkStyle("div", 1, "color", "rgb(0, 128, 0)");
+            checkStyle("path", 2, "color", "rgb(0, 128, 0)");
+
+            document.getElementById("container").style.display = "none";
+        </script>
+        <script src="../../fast/js/resources/js-test-post.js"></script>
+    </body>
+</html>
index 38c660e..991e690 100644 (file)
@@ -1,3 +1,38 @@
+2013-08-01  Rob Buis  <rwlbuis@webkit.org>
+
+        selectors should match attribute name with case sensitivity based on element & document type
+        https://bugs.webkit.org/show_bug.cgi?id=71152
+
+        Reviewed by Darin Adler.
+
+        Support case-sensitive attribute name selecting for non HTML. In order to do this we have to
+        store the attribute name in the selector as-is when css parsing, and get the lowercase localName
+        on demand for case-insensitive matching. The only time we want case-insensitive matching is when
+        we try to match a HTML element in a HTML document.
+
+        Tests: fast/dom/SelectorAPI/attrname-case-insensitive.html
+               fast/dom/SelectorAPI/attrname-case-sensitive.xhtml
+               svg/css/case-sensitive-attrname-selectors.html
+
+        * css/CSSGrammar.y.in: do not lowercase attribute selector name.
+        * css/CSSParserValues.h:
+        (WebCore::CSSParserSelector::setAttribute):
+        * css/CSSSelector.cpp:
+        (WebCore::CSSSelector::setAttribute):
+        * css/CSSSelector.h: allow access to lowered version of attribute localName if needed.
+        (WebCore::CSSSelector::attributeCanonicalLocalName):
+        * css/SelectorChecker.cpp:
+        (WebCore::anyAttributeMatches): do only case-insensitive matching for HTML.
+        (WebCore::SelectorChecker::checkOne):
+        * css/SelectorChecker.h:
+        (WebCore::SelectorChecker::checkExactAttribute): do only case-insensitive matching for HTML.
+        * css/SelectorCheckerFastPath.cpp:
+        (WebCore::HTMLNames::checkExactAttributeValue):
+        * css/SelectorCheckerFastPath.h:
+        (WebCore::SelectorCheckerFastPath::matchesRightmostAttributeSelector):
+        * dom/Attribute.h:
+        (WebCore::Attribute::matches): use more convenient parameters.
+
 2013-08-01  Brent Fulgham  <bfulgham@apple.com>
 
         [Windows] WebKit1 Fullscreen Video Play is Broken
index 7385387..4e13dfe 100644 (file)
@@ -1347,33 +1347,30 @@ class:
 
 attr_name:
     IDENT maybe_space {
-        CSSParserString& str = $1;
-        if (parser->m_context.isHTMLDocument)
-            str.lower();
-        $$ = str;
+        $$ = $1;
     }
     ;
 
 attrib:
     '[' maybe_space attr_name ']' {
         $$ = parser->createFloatingSelector();
-        $$->setAttribute(QualifiedName(nullAtom, $3, nullAtom));
+        $$->setAttribute(QualifiedName(nullAtom, $3, nullAtom), parser->m_context.isHTMLDocument);
         $$->setMatch(CSSSelector::Set);
     }
     | '[' maybe_space attr_name match maybe_space ident_or_string maybe_space ']' {
         $$ = parser->createFloatingSelector();
-        $$->setAttribute(QualifiedName(nullAtom, $3, nullAtom));
+        $$->setAttribute(QualifiedName(nullAtom, $3, nullAtom), parser->m_context.isHTMLDocument);
         $$->setMatch((CSSSelector::Match)$4);
         $$->setValue($6);
     }
     | '[' maybe_space namespace_selector attr_name ']' {
         $$ = parser->createFloatingSelector();
-        $$->setAttribute(parser->determineNameInNamespace($3, $4));
+        $$->setAttribute(parser->determineNameInNamespace($3, $4), parser->m_context.isHTMLDocument);
         $$->setMatch(CSSSelector::Set);
     }
     | '[' maybe_space namespace_selector attr_name match maybe_space ident_or_string maybe_space ']' {
         $$ = parser->createFloatingSelector();
-        $$->setAttribute(parser->determineNameInNamespace($3, $4));
+        $$->setAttribute(parser->determineNameInNamespace($3, $4), parser->m_context.isHTMLDocument);
         $$->setMatch((CSSSelector::Match)$5);
         $$->setValue($7);
     }
index b899e1c..f28b8f5 100644 (file)
@@ -181,7 +181,7 @@ public:
     PassOwnPtr<CSSSelector> releaseSelector() { return m_selector.release(); }
 
     void setValue(const AtomicString& value) { m_selector->setValue(value); }
-    void setAttribute(const QualifiedName& value) { m_selector->setAttribute(value); }
+    void setAttribute(const QualifiedName& value, bool isCaseInsensitive) { m_selector->setAttribute(value, isCaseInsensitive); }
     void setArgument(const AtomicString& value) { m_selector->setArgument(value); }
     void setMatch(CSSSelector::Match value) { m_selector->m_match = value; }
     void setRelation(CSSSelector::Relation value) { m_selector->m_relation = value; }
index 1d963fe..e5c2be5 100644 (file)
@@ -712,10 +712,11 @@ String CSSSelector::selectorText(const String& rightSide) const
     return str.toString() + rightSide;
 }
 
-void CSSSelector::setAttribute(const QualifiedName& value)
+void CSSSelector::setAttribute(const QualifiedName& value, bool isCaseInsensitive)
 {
     createRareData();
     m_data.m_rareData->m_attribute = value;
+    m_data.m_rareData->m_attributeCanonicalLocalName = isCaseInsensitive ? value.localName().lower() : value.localName();
 }
 
 void CSSSelector::setArgument(const AtomicString& value)
index 0d79484..55ac624 100644 (file)
@@ -204,11 +204,12 @@ namespace WebCore {
         const QualifiedName& tagQName() const;
         const AtomicString& value() const;
         const QualifiedName& attribute() const;
+        const AtomicString& attributeCanonicalLocalName() const;
         const AtomicString& argument() const { return m_hasRareData ? m_data.m_rareData->m_argument : nullAtom; }
         const CSSSelectorList* selectorList() const { return m_hasRareData ? m_data.m_rareData->m_selectorList.get() : 0; }
 
         void setValue(const AtomicString&);
-        void setAttribute(const QualifiedName&);
+        void setAttribute(const QualifiedName&, bool isCaseInsensitive);
         void setArgument(const AtomicString&);
         void setSelectorList(PassOwnPtr<CSSSelectorList>);
 
@@ -263,6 +264,7 @@ namespace WebCore {
             int m_a; // Used for :nth-*
             int m_b; // Used for :nth-*
             QualifiedName m_attribute; // used for attribute selector
+            AtomicString m_attributeCanonicalLocalName;
             AtomicString m_argument; // Used for :contains, :lang and :nth-*
             OwnPtr<CSSSelectorList> m_selectorList; // Used for :-webkit-any and :not
         
@@ -286,6 +288,13 @@ inline const QualifiedName& CSSSelector::attribute() const
     return m_data.m_rareData->m_attribute;
 }
 
+inline const AtomicString& CSSSelector::attributeCanonicalLocalName() const
+{
+    ASSERT(isAttributeSelector());
+    ASSERT(m_hasRareData);
+    return m_data.m_rareData->m_attributeCanonicalLocalName;
+}
+
 inline bool CSSSelector::matchesPseudoElement() const
 {
     if (m_pseudoType == PseudoUnknown)
index 72e06a1..dce3138 100644 (file)
@@ -354,16 +354,16 @@ static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::M
     return true;
 }
 
-static bool anyAttributeMatches(Element* element, CSSSelector::Match match, const QualifiedName& selectorAttr, const AtomicString& selectorValue, bool caseSensitive)
+static bool anyAttributeMatches(Element* element, const CSSSelector* selector, const QualifiedName& selectorAttr, bool caseSensitive)
 {
     ASSERT(element->hasAttributesWithoutUpdate());
     for (size_t i = 0; i < element->attributeCount(); ++i) {
         const Attribute* attributeItem = element->attributeItem(i);
 
-        if (!attributeItem->matches(selectorAttr))
+        if (!attributeItem->matches(selectorAttr.prefix(), element->isHTMLElement() ? selector->attributeCanonicalLocalName() : selectorAttr.localName(), selectorAttr.namespaceURI()))
             continue;
 
-        if (attributeValueMatches(attributeItem, match, selectorValue, caseSensitive))
+        if (attributeValueMatches(attributeItem, static_cast<CSSSelector::Match>(selector->m_match), selector->value(), caseSensitive))
             return true;
     }
 
@@ -387,14 +387,13 @@ bool SelectorChecker::checkOne(const SelectorCheckingContext& context) const
         return element->hasID() && element->idForStyleResolution() == selector->value();
 
     if (selector->isAttributeSelector()) {
-        const QualifiedName& attr = selector->attribute();
-
         if (!element->hasAttributes())
             return false;
 
+        const QualifiedName& attr = selector->attribute();
         bool caseSensitive = !m_documentIsHTML || HTMLDocument::isCaseSensitiveAttribute(attr);
 
-        if (!anyAttributeMatches(element, static_cast<CSSSelector::Match>(selector->m_match), attr, selector->value(), caseSensitive))
+        if (!anyAttributeMatches(element, selector, attr, caseSensitive))
             return false;
     }
 
index 0f6b04c..9499151 100644 (file)
@@ -91,7 +91,7 @@ public:
     static bool tagMatches(const Element*, const QualifiedName&);
     static bool isCommonPseudoClassSelector(const CSSSelector*);
     static bool matchesFocusPseudoClass(const Element*);
-    static bool checkExactAttribute(const Element*, const QualifiedName& selectorAttributeName, const AtomicStringImpl* value);
+    static bool checkExactAttribute(const Element*, const CSSSelector*, const QualifiedName& selectorAttributeName, const AtomicStringImpl* value);
 
     enum LinkMatchMask { MatchLink = 1, MatchVisited = 2, MatchAll = MatchLink | MatchVisited };
     static unsigned determineLinkMatchType(const CSSSelector*);
@@ -128,14 +128,15 @@ inline bool SelectorChecker::tagMatches(const Element* element, const QualifiedN
     return namespaceURI == starAtom || namespaceURI == element->namespaceURI();
 }
 
-inline bool SelectorChecker::checkExactAttribute(const Element* element, const QualifiedName& selectorAttributeName, const AtomicStringImpl* value)
+inline bool SelectorChecker::checkExactAttribute(const Element* element, const CSSSelector* selector, const QualifiedName& selectorAttributeName, const AtomicStringImpl* value)
 {
     if (!element->hasAttributesWithoutUpdate())
         return false;
+    const AtomicString& localName = element->isHTMLElement() ? selector->attributeCanonicalLocalName() : selectorAttributeName.localName();
     unsigned size = element->attributeCount();
     for (unsigned i = 0; i < size; ++i) {
         const Attribute* attribute = element->attributeItem(i);
-        if (attribute->matches(selectorAttributeName) && (!value || attribute->value().impl() == value))
+        if (attribute->matches(selectorAttributeName.prefix(), localName, selectorAttributeName.namespaceURI()) && (!value || attribute->value().impl() == value))
             return true;
     }
     return false;
index 84fcaee..bdc5144 100644 (file)
@@ -85,7 +85,7 @@ inline bool checkIDValue(const Element* element, const CSSSelector* selector)
 
 inline bool checkExactAttributeValue(const Element* element, const CSSSelector* selector)
 {
-    return SelectorChecker::checkExactAttribute(element, selector->attribute(), selector->value().impl());
+    return SelectorChecker::checkExactAttribute(element, selector, selector->attribute(), selector->value().impl());
 }
 
 inline bool checkTagValue(const Element* element, const CSSSelector* selector)
index 6c8e3cc..f1d1045 100644 (file)
@@ -54,7 +54,7 @@ private:
 inline bool SelectorCheckerFastPath::matchesRightmostAttributeSelector() const
 {
     if (m_selector->m_match == CSSSelector::Exact || m_selector->m_match == CSSSelector::Set)
-        return SelectorChecker::checkExactAttribute(m_element, m_selector->attribute(), m_selector->value().impl());
+        return SelectorChecker::checkExactAttribute(m_element, m_selector, m_selector->attribute(), m_selector->value().impl());
     ASSERT(!m_selector->isAttributeSelector());
     return true;
 }
index 4a2fcad..61c9ec5 100644 (file)
@@ -51,7 +51,7 @@ public:
     const QualifiedName& name() const { return m_name; }
 
     bool isEmpty() const { return m_value.isEmpty(); }
-    bool matches(const QualifiedName&) const;
+    bool matches(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI) const;
 
     void setValue(const AtomicString& value) { m_value = value; }
     void setPrefix(const AtomicString& prefix) { m_name.setPrefix(prefix); }
@@ -72,11 +72,11 @@ private:
     AtomicString m_value;
 };
 
-inline bool Attribute::matches(const QualifiedName& qualifiedName) const
+inline bool Attribute::matches(const AtomicString& prefix, const AtomicString& localName, const AtomicString& namespaceURI) const
 {
-    if (qualifiedName.localName() != localName())
+    if (localName != this->localName())
         return false;
-    return qualifiedName.prefix() == starAtom || qualifiedName.namespaceURI() == namespaceURI();
+    return prefix == starAtom || namespaceURI == this->namespaceURI();
 }
 
 } // namespace WebCore