Bug 507762: Parse CSS style sheets independently of case-sensitivity, and instead check case correctly when using the stylesheets. r=dbaron
authorDavid Zbarsky <dzbarsky@gmail.com>
Mon, 28 Sep 2009 23:07:45 -0700
changeset 33262 2c6aeb49c93cf5aec2f0fd77a71fc0168861dd71
parent 33261 1deff8db43525a0d0caefaa9e23bec7922e349ae
child 33263 fbea0edb962240777766a95580cfc7e0287d6320
push idunknown
push userunknown
push dateunknown
reviewersdbaron
bugs507762
milestone1.9.3a1pre
Bug 507762: Parse CSS style sheets independently of case-sensitivity, and instead check case correctly when using the stylesheets. r=dbaron
content/base/src/nsDocument.cpp
content/html/document/src/nsHTMLDocument.cpp
content/xul/content/src/nsXULElement.h
layout/base/nsCSSFrameConstructor.cpp
layout/reftests/bugs/507762-1-ref.html
layout/reftests/bugs/507762-1.html
layout/reftests/bugs/507762-2-ref.html
layout/reftests/bugs/507762-2.html
layout/reftests/bugs/507762-3.html
layout/reftests/bugs/507762-4.html
layout/reftests/bugs/reftest.list
layout/style/nsCSSLoader.cpp
layout/style/nsCSSLoader.h
layout/style/nsCSSParser.cpp
layout/style/nsCSSRuleProcessor.cpp
layout/style/nsCSSStyleRule.cpp
layout/style/nsICSSLoader.h
layout/style/nsICSSParser.h
layout/style/nsICSSStyleRule.h
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1860,18 +1860,17 @@ nsDocument::Init()
                  NS_ERROR_OUT_OF_MEMORY);
 
 
   mOnloadBlocker = new nsOnloadBlocker();
   NS_ENSURE_TRUE(mOnloadBlocker, NS_ERROR_OUT_OF_MEMORY);
   
   NS_NewCSSLoader(this, &mCSSLoader);
   NS_ENSURE_TRUE(mCSSLoader, NS_ERROR_OUT_OF_MEMORY);
-  // Assume we're not HTML and not quirky, until we know otherwise
-  mCSSLoader->SetCaseSensitive(PR_TRUE);
+  // Assume we're not quirky, until we know otherwise
   mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
 
   mNodeInfoManager = new nsNodeInfoManager();
   NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ADDREF(mNodeInfoManager);
 
   nsresult  rv = mNodeInfoManager->Init(this);
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -269,20 +269,18 @@ NS_INTERFACE_MAP_END_INHERITING(nsDocume
 
 
 nsresult
 nsHTMLDocument::Init()
 {
   nsresult rv = nsDocument::Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Now reset the case-sensitivity of the CSSLoader, since we default
-  // to being HTML, not XHTML.  Also, reset the compatibility mode to
-  // match our compat mode.
-  CSSLoader()->SetCaseSensitive(!IsHTML());
+  // Now reset the compatibility mode of the CSSLoader
+  // to match our compat mode.
   CSSLoader()->SetCompatibilityMode(mCompatMode);
 
   PrePopulateIdentifierMap();
   return NS_OK;
 }
 
 
 void
@@ -677,17 +675,16 @@ nsHTMLDocument::StartDocumentLoad(const 
   }
 #ifdef DEBUG
   else {
     NS_ASSERTION(mIsRegularHTML,
                  "Hey, someone forgot to reset mIsRegularHTML!!!");
   }
 #endif
 
-  CSSLoader()->SetCaseSensitive(!IsHTML());
   CSSLoader()->SetCompatibilityMode(mCompatMode);
   
   PRBool needsParser = PR_TRUE;
   if (aCommand)
   {
     if (!nsCRT::strcmp(aCommand, "view delayedContentLoad")) {
       needsParser = PR_FALSE;
     }
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -308,17 +308,16 @@ public:
     }
 
 protected:
     static nsICSSParser* GetCSSParser()
     {
         if (!sCSSParser) {
             CallCreateInstance(kCSSParserCID, &sCSSParser);
             if (sCSSParser) {
-                sCSSParser->SetCaseSensitive(PR_TRUE);
                 sCSSParser->SetQuirkMode(PR_FALSE);
             }
         }
         return sCSSParser;
     }
     static nsICSSParser* sCSSParser;
 };
 
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -58,16 +58,17 @@
 #include "nsTableFrame.h"
 #include "nsTableColGroupFrame.h"
 #include "nsTableColFrame.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLTableColElement.h"
 #include "nsIDOMHTMLTableCaptionElem.h"
 #include "nsHTMLParts.h"
 #include "nsIPresShell.h"
+#include "nsUnicharUtils.h"
 #include "nsStyleSet.h"
 #include "nsIViewManager.h"
 #include "nsIEventStateManager.h"
 #include "nsIScrollableView.h"
 #include "nsStyleConsts.h"
 #include "nsTableOuterFrame.h"
 #include "nsIDOMXULElement.h"
 #include "nsHTMLContainerFrame.h"
@@ -1707,41 +1708,48 @@ nsCSSFrameConstructor::CreateGeneratedCo
                                 nsDependentString(data.mContent.mString),
                                 nsnull, nsnull);
 
   case eStyleContentType_Attr:
     {
       nsCOMPtr<nsIAtom> attrName;
       PRInt32 attrNameSpace = kNameSpaceID_None;
       nsAutoString contentString(data.mContent.mString);
+      
       PRInt32 barIndex = contentString.FindChar('|'); // CSS namespace delimiter
       if (-1 != barIndex) {
         nsAutoString  nameSpaceVal;
         contentString.Left(nameSpaceVal, barIndex);
         PRInt32 error;
         attrNameSpace = nameSpaceVal.ToInteger(&error, 10);
         contentString.Cut(0, barIndex + 1);
         if (contentString.Length()) {
+          if (mDocument->IsHTML() && aParentContent->IsNodeOfType(nsINode::eHTML)) {
+            ToLowerCase(contentString);
+          }
           attrName = do_GetAtom(contentString);
         }
       }
       else {
+        if (mDocument->IsHTML() && aParentContent->IsNodeOfType(nsINode::eHTML)) {
+          ToLowerCase(contentString);
+        }
         attrName = do_GetAtom(contentString);
       }
 
       if (!attrName) {
         return nsnull;
       }
 
       nsCOMPtr<nsIContent> content;
       NS_NewAttributeContent(mDocument->NodeInfoManager(),
                              attrNameSpace, attrName, getter_AddRefs(content));
       return content.forget();
     }
-  
+
   case eStyleContentType_Counter:
   case eStyleContentType_Counters:
     {
       nsCSSValue::Array* counters = data.mContent.mCounters;
       nsCounterList* counterList = mCounterManager.CounterListFor(
           nsDependentString(counters->Item(0).GetStringBufferValue()));
       if (!counterList)
         return nsnull;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/507762-1-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head>
+<body>
+<span style="background-color: green">text</span>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/507762-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@namespace url(test);
+div {
+background-color: red;
+}
+div[mixedCase=true] {
+background-color: green !important;
+}
+</style>
+<script>
+function f() {
+var elem = document.createElementNS("test","div");
+elem.setAttribute("mixedCase",true);
+elem.appendChild(document.createTextNode("text"));
+document.getElementsByTagName("body")[0].appendChild(elem); }
+</script>
+</head>
+<body onload="f();">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/507762-2-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head>
+<body>
+<div style="background-color: green">text</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/507762-2.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+div {
+background-color: red;
+}
+div[mixedCase=true] {
+background-color: green !important;
+}
+</style>
+<script>
+function f() {
+var elem = document.createElement("div");
+elem.setAttribute("mixedcase",true);
+elem.appendChild(document.createTextNode("text"));
+document.getElementsByTagName("body")[0].appendChild(elem); }
+</script>
+</head>
+<body onload="f();">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/507762-3.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@namespace url(test);
+div {
+background-color: green;
+}
+div[dir=RTL ] 
+{background-color: red !important;
+}
+</style>
+<script>
+function f() {
+var elem = document.createElementNS("test","div");
+elem.setAttribute("dir","rtl");
+elem.appendChild(document.createTextNode("text"));
+document.getElementsByTagName("body")[0].appendChild(elem); }
+</script>
+</head>
+<body onload="f();">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/507762-4.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+div {
+background-color: red;
+}
+div[dir=ltr] 
+{background-color: green !important;
+}
+</style>
+<script>
+function f() {
+var elem = document.createElement("div");
+elem.setAttribute("dir","LTR");
+elem.appendChild(document.createTextNode("text"));
+document.getElementsByTagName("body")[0].appendChild(elem); }
+</script>
+</head>
+<body onload="f();">
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1304,16 +1304,20 @@ fails-if(MOZ_WIDGET_TOOLKIT!="cocoa") ==
 == 503364-1a.html 503364-1-ref.html
 == 503364-1b.html 503364-1-ref.html
 == 504032-1.html 504032-1-ref.html
 == 505743-1.html about:blank
 == 506481-1.html 506481-1-ref.html
 == 507187-1.html 507187-1-ref.html
 == 507487-1.html 507487-1-ref.html
 == 507487-2.xhtml 507487-2-ref.xhtml
+== 507762-1.html 507762-1-ref.html
+== 507762-2.html 507762-2-ref.html
+== 507762-3.html 507762-1-ref.html
+== 507762-4.html 507762-2-ref.html
 == 508919-1.xhtml 508919-1-ref.xhtml
 == 509155-1.xhtml 509155-1-ref.xhtml
 == 512410.html 512410-ref.html
 == 512631-1.html 512631-1-ref.html
 == 513153-1a.html 513153-1-ref.html
 == 513153-1b.html 513153-1-ref.html
 == 513153-2a.html 513153-2-ref.html
 == 513153-2b.html 513153-2-ref.html
--- a/layout/style/nsCSSLoader.cpp
+++ b/layout/style/nsCSSLoader.cpp
@@ -269,17 +269,16 @@ SheetLoadData::Run()
  * Loader Implementation *
  *************************/
 
 // static
 nsCOMArray<nsICSSParser>* CSSLoaderImpl::gParsers = nsnull;
 
 CSSLoaderImpl::CSSLoaderImpl(void)
   : mDocument(nsnull), 
-    mCaseSensitive(PR_FALSE),
     mEnabled(PR_TRUE), 
     mCompatMode(eCompatibility_FullStandards),
     mDatasToNotifyOn(0)
 {
 }
 
 CSSLoaderImpl::~CSSLoaderImpl(void)
 {
@@ -327,23 +326,16 @@ CSSLoaderImpl::DropDocumentReference(voi
   // LoadSheet and just end up in SheetComplete immediately
   if (mPendingDatas.IsInitialized()) {
     StartAlternateLoads();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-CSSLoaderImpl::SetCaseSensitive(PRBool aCaseSensitive)
-{
-  mCaseSensitive = aCaseSensitive;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 CSSLoaderImpl::SetCompatibilityMode(nsCompatibility aCompatMode)
 {
   mCompatMode = aCompatMode;
   return NS_OK;
 }
 
 static PLDHashOperator
 CollectNonAlternates(nsURIAndPrincipalHashKey *aKey,
@@ -426,17 +418,16 @@ CSSLoaderImpl::GetParserFor(nsICSSStyleS
   }
 
   nsresult result = NS_OK;
   if (! *aParser) {
     result = NS_NewCSSParser(aParser);
   }
   
   if (*aParser) {
-    (*aParser)->SetCaseSensitive(mCaseSensitive);
     (*aParser)->SetQuirkMode(mCompatMode == eCompatibility_NavQuirks);
     if (aSheet) {
       (*aParser)->SetStyleSheet(aSheet);
     }
     (*aParser)->SetChildLoader(this);
   }
   return result;
 }
--- a/layout/style/nsCSSLoader.h
+++ b/layout/style/nsCSSLoader.h
@@ -313,17 +313,16 @@ public:
   NS_DECL_ISUPPORTS
 
   static void Shutdown(); // called at app shutdown
   
   // nsICSSLoader methods
   NS_IMETHOD Init(nsIDocument* aDocument);
   NS_IMETHOD DropDocumentReference(void);
 
-  NS_IMETHOD SetCaseSensitive(PRBool aCaseSensitive);
   NS_IMETHOD SetCompatibilityMode(nsCompatibility aCompatMode);
   NS_IMETHOD SetPreferredSheet(const nsAString& aTitle);
   NS_IMETHOD GetPreferredSheet(nsAString& aTitle);
 
   NS_IMETHOD GetParserFor(nsICSSStyleSheet* aSheet,
                           nsICSSParser** aParser);
   NS_IMETHOD RecycleParser(nsICSSParser* aParser);
 
@@ -489,17 +488,16 @@ private:
 
   // the load data needs access to the document...
   nsIDocument*      mDocument;  // the document we live for
 
 #ifdef DEBUG
   PRPackedBool            mSyncCallback;
 #endif
 
-  PRPackedBool      mCaseSensitive; // is document CSS case sensitive
   PRPackedBool      mEnabled; // is enabled to load new styles
   nsCompatibility   mCompatMode;
   nsString          mPreferredSheet;  // title of preferred sheet
 
   nsInterfaceHashtable<nsURIAndPrincipalHashKey,
                        nsICSSStyleSheet> mCompleteSheets;
   nsDataHashtable<nsURIAndPrincipalHashKey,
                   SheetLoadData*> mLoadingDatas; // weak refs
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -156,18 +156,16 @@ class CSSParserImpl : public nsICSSParse
 public:
   CSSParserImpl();
   virtual ~CSSParserImpl();
 
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet);
 
-  NS_IMETHOD SetCaseSensitive(PRBool aCaseSensitive);
-
   NS_IMETHOD SetQuirkMode(PRBool aQuirkMode);
 
 #ifdef  MOZ_SVG
   NS_IMETHOD SetSVGMode(PRBool aSVGMode);
 #endif
 
   NS_IMETHOD SetChildLoader(nsICSSLoader* aChildLoader);
 
@@ -620,19 +618,16 @@ protected:
 
   // True if unsafe rules should be allowed
   PRPackedBool mUnsafeRulesEnabled : 1;
 
   // True for parsing media lists for HTML attributes, where we have to
   // ignore CSS comments.
   PRPackedBool mHTMLMediaMode : 1;
 
-  // True if tagnames and attributes are case-sensitive
-  PRPackedBool  mCaseSensitive : 1;
-
   // This flag is set when parsing a non-box shorthand; it's used to not apply
   // some quirks during shorthand parsing
   PRPackedBool  mParsingCompoundProperty : 1;
 
   // If this flag is true, failure to resolve a namespace prefix
   // should set the low-level error to NS_ERROR_DOM_NAMESPACE_ERR
   PRPackedBool  mUnresolvablePrefixException : 1;
 
@@ -723,17 +718,16 @@ CSSParserImpl::CSSParserImpl()
     mScanner(),
     mChildLoader(nsnull),
     mSection(eCSSSection_Charset),
     mNameSpaceMap(nsnull),
     mHavePushBack(PR_FALSE),
     mNavQuirkMode(PR_FALSE),
     mUnsafeRulesEnabled(PR_FALSE),
     mHTMLMediaMode(PR_FALSE),
-    mCaseSensitive(PR_FALSE),
     mParsingCompoundProperty(PR_FALSE),
     mUnresolvablePrefixException(PR_FALSE)
 #ifdef DEBUG
     , mScannerInited(PR_FALSE)
 #endif
 {
 }
 
@@ -758,24 +752,16 @@ CSSParserImpl::SetStyleSheet(nsICSSStyle
       mNameSpaceMap = nsnull;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-CSSParserImpl::SetCaseSensitive(PRBool aCaseSensitive)
-{
-  NS_ASSERTION(aCaseSensitive == PR_TRUE || aCaseSensitive == PR_FALSE, "bad PRBool value");
-  mCaseSensitive = aCaseSensitive;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 CSSParserImpl::SetQuirkMode(PRBool aQuirkMode)
 {
   NS_ASSERTION(aQuirkMode == PR_TRUE || aQuirkMode == PR_FALSE, "bad PRBool value");
   mNavQuirkMode = aQuirkMode;
   return NS_OK;
 }
 
 #ifdef MOZ_SVG
@@ -2704,17 +2690,17 @@ CSSParserImpl::ParseTypeOrUniversalSelec
 
       if (! GetToken(PR_FALSE)) {
         REPORT_UNEXPECTED_EOF(PETypeSelEOF);
         return eSelectorParsingStatus_Error;
       }
       if (eCSSToken_Ident == mToken.mType) {  // element name
         aDataMask |= SEL_MASK_ELEM;
 
-        aSelector.SetTag(mToken.mIdent, mCaseSensitive);
+        aSelector.SetTag(mToken.mIdent);
       }
       else if (mToken.IsSymbol('*')) {  // universal selector
         aDataMask |= SEL_MASK_ELEM;
         // don't set tag
       }
       else {
         REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
         UngetToken();
@@ -2742,32 +2728,31 @@ CSSParserImpl::ParseTypeOrUniversalSelec
       aSelector.SetNameSpace(nameSpaceID);
 
       if (! GetToken(PR_FALSE)) {
         REPORT_UNEXPECTED_EOF(PETypeSelEOF);
         return eSelectorParsingStatus_Error;
       }
       if (eCSSToken_Ident == mToken.mType) {  // element name
         aDataMask |= SEL_MASK_ELEM;
-       
-        aSelector.SetTag(mToken.mIdent, mCaseSensitive);
+        aSelector.SetTag(mToken.mIdent);
       }
       else if (mToken.IsSymbol('*')) {  // universal selector
         aDataMask |= SEL_MASK_ELEM;
         // don't set tag
       }
       else {
         REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
         UngetToken();
         return eSelectorParsingStatus_Error;
       }
     }
     else {  // was element name
       SetDefaultNamespaceOnSelector(aSelector);
-      aSelector.SetTag(buffer, mCaseSensitive);
+      aSelector.SetTag(buffer);
 
       aDataMask |= SEL_MASK_ELEM;
     }
     if (! GetToken(PR_FALSE)) {   // premature eof is ok (here!)
       return eSelectorParsingStatus_Done;
     }
   }
   else if (mToken.IsSymbol('|')) {  // No namespace
@@ -2776,17 +2761,17 @@ CSSParserImpl::ParseTypeOrUniversalSelec
 
     // get mandatory tag
     if (! GetToken(PR_FALSE)) {
       REPORT_UNEXPECTED_EOF(PETypeSelEOF);
       return eSelectorParsingStatus_Error;
     }
     if (eCSSToken_Ident == mToken.mType) {  // element name
       aDataMask |= SEL_MASK_ELEM;
-      aSelector.SetTag(mToken.mIdent, mCaseSensitive);
+      aSelector.SetTag(mToken.mIdent);
     }
     else if (mToken.IsSymbol('*')) {  // universal selector
       aDataMask |= SEL_MASK_ELEM;
       // don't set tag
     }
     else {
       REPORT_UNEXPECTED_TOKEN(PETypeSelNotType);
       UngetToken();
@@ -2878,19 +2863,16 @@ CSSParserImpl::ParseAttributeSelector(PR
     }
   }
   else {  // malformed
     REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected);
     UngetToken();
     return eSelectorParsingStatus_Error;
   }
 
-  if (! mCaseSensitive) {
-    ToLowerCase(attr);
-  }
   if (! GetToken(PR_TRUE)) { // premature EOF
     REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF);
     return eSelectorParsingStatus_Error;
   }
   if ((eCSSToken_Symbol == mToken.mType) ||
       (eCSSToken_Includes == mToken.mType) ||
       (eCSSToken_Dashmatch == mToken.mType) ||
       (eCSSToken_Beginsmatch == mToken.mType) ||
@@ -2934,21 +2916,22 @@ CSSParserImpl::ParseAttributeSelector(PR
         nsAutoString  value(mToken.mIdent);
         if (! GetToken(PR_TRUE)) { // premature EOF
           REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF);
           return eSelectorParsingStatus_Error;
         }
         if (mToken.IsSymbol(']')) {
           PRBool isCaseSensitive = PR_TRUE;
 
-          // If we're parsing a style sheet for an HTML document, and
-          // the attribute selector is for a non-namespaced attribute,
-          // then check to see if it's one of the known attributes whose
-          // VALUE is case-insensitive.
-          if (!mCaseSensitive && nameSpaceID == kNameSpaceID_None) {
+          // For cases when this style sheet is applied to an HTML
+          // element in an HTML document, and the attribute selector is
+          // for a non-namespaced attribute, then check to see if it's
+          // one of the known attributes whose VALUE is
+          // case-insensitive.
+          if (nameSpaceID == kNameSpaceID_None) {
             static const char* caseInsensitiveHTMLAttribute[] = {
               // list based on http://www.w3.org/TR/html4/
               "lang",
               "dir",
               "http-equiv",
               "text",
               "link",
               "vlink",
@@ -4703,58 +4686,41 @@ CSSParserImpl::ParseAttr(nsCSSValue& aVa
           }
           attr.AppendInt(nameSpaceID, 10);
           attr.Append(PRUnichar('|'));
           if (! GetToken(PR_FALSE)) {
             REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
             return PR_FALSE;
           }
           if (eCSSToken_Ident == mToken.mType) {
-            if (mCaseSensitive) {
-              attr.Append(mToken.mIdent);
-            } else {
-              nsAutoString buffer;
-              ToLowerCase(mToken.mIdent, buffer);
-              attr.Append(buffer);
-            }
+            attr.Append(mToken.mIdent);
           }
           else {
             REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
             UngetToken();
             return PR_FALSE;
           }
         }
         else {  // no namespace
-          if (mCaseSensitive) {
-            attr = holdIdent;
-          }
-          else {
-            ToLowerCase(holdIdent, attr);
-          }
+          attr = holdIdent;
         }
       }
       else if (mToken.IsSymbol('*')) {  // namespace wildcard
         // Wildcard namespace makes no sense here and is not allowed
         REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
         UngetToken();
         return PR_FALSE;
       }
       else if (mToken.IsSymbol('|')) {  // explicit NO namespace
         if (! GetToken(PR_FALSE)) {
           REPORT_UNEXPECTED_EOF(PEAttributeNameEOF);
           return PR_FALSE;
         }
         if (eCSSToken_Ident == mToken.mType) {
-          if (mCaseSensitive) {
-            attr.Append(mToken.mIdent);
-          } else {
-            nsAutoString buffer;
-            ToLowerCase(mToken.mIdent, buffer);
-            attr.Append(buffer);
-          }
+          attr.Append(mToken.mIdent);
         }
         else {
           REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected);
           UngetToken();
           return PR_FALSE;
         }
       }
       else {
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -1162,35 +1162,37 @@ IsSignificantChild(nsIContent* aChild, P
   return nsStyleUtil::IsSignificantChild(aChild, aTextIsSignificant,
                                          aWhitespaceIsSignificant);
 }
 
 // This function is to be called once we have fetched a value for an attribute
 // whose namespace and name match those of aAttrSelector.  This function
 // performs comparisons on the value only, based on aAttrSelector->mFunction.
 static PRBool AttrMatchesValue(const nsAttrSelector* aAttrSelector,
-                               const nsString& aValue)
+                               const nsString& aValue, PRBool isHTML)
 {
   NS_PRECONDITION(aAttrSelector, "Must have an attribute selector");
 
   // http://lists.w3.org/Archives/Public/www-style/2008Apr/0038.html
   // *= (CONTAINSMATCH) ~= (INCLUDES) ^= (BEGINSMATCH) $= (ENDSMATCH)
   // all accept the empty string, but match nothing.
   if (aAttrSelector->mValue.IsEmpty() &&
       (aAttrSelector->mFunction == NS_ATTR_FUNC_INCLUDES ||
        aAttrSelector->mFunction == NS_ATTR_FUNC_ENDSMATCH ||
        aAttrSelector->mFunction == NS_ATTR_FUNC_BEGINSMATCH ||
        aAttrSelector->mFunction == NS_ATTR_FUNC_CONTAINSMATCH))
     return PR_FALSE;
 
   const nsDefaultStringComparator defaultComparator;
   const nsCaseInsensitiveStringComparator ciComparator;
-  const nsStringComparator& comparator = aAttrSelector->mCaseSensitive
+  const nsStringComparator& comparator =
+      (aAttrSelector->mCaseSensitive || !isHTML)
                 ? static_cast<const nsStringComparator&>(defaultComparator)
                 : static_cast<const nsStringComparator&>(ciComparator);
+
   switch (aAttrSelector->mFunction) {
     case NS_ATTR_FUNC_EQUALS: 
       return aValue.Equals(aAttrSelector->mValue, comparator);
     case NS_ATTR_FUNC_INCLUDES: 
       return ValueIncludes(aValue, aAttrSelector->mValue, comparator);
     case NS_ATTR_FUNC_DASHMATCH: 
       return nsStyleUtil::DashMatchCompare(aValue, aAttrSelector->mValue, comparator);
     case NS_ATTR_FUNC_ENDSMATCH:
@@ -1229,28 +1231,23 @@ static PRBool SelectorMatches(RuleProces
 
 {
   // namespace/tag match
   // optimization : bail out early if we can
   if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
        data.mNameSpaceID != aSelector->mNameSpace))
     return PR_FALSE;
 
-  if (aSelector->mLowercaseTag) {
-    //If we tested that this is an HTML node in a text/html document and
-    //had some tweaks in RuleHash, we could remove case-sensitivity from
-    //style sheets.
-    if (data.mIsHTMLContent) {
-      if (data.mContentTag != aSelector->mLowercaseTag)
-        return PR_FALSE;
-    }
-    else {
-      if (data.mContentTag != aSelector->mCasedTag)
-        return PR_FALSE;
-    }
+  const PRBool isHTML =
+    data.mIsHTMLContent && data.mContent->GetOwnerDoc()->IsHTML();
+
+  if (aSelector->mLowercaseTag && 
+      (isHTML ? aSelector->mLowercaseTag : aSelector->mCasedTag) !=
+        data.mContentTag) {
+    return PR_FALSE;
   }
 
   PRBool result = PR_TRUE;
   const PRBool isNegated = (aDependence != nsnull);
   // The selectors for which we set node bits are, unfortunately, early
   // in this function (because they're pseudo-classes, which are
   // generally quick to test, and thus earlier).  If they were later,
   // we'd probably avoid setting those bits in more cases where setting
@@ -1674,18 +1671,21 @@ static PRBool SelectorMatches(RuleProces
       // if no attributes on the content, no match
       result = PR_FALSE;
     } else {
       NS_ASSERTION(data.mContent,
                    "Must have content if either data.mHasAttributes or "
                    "aAttribute is set!");
       result = PR_TRUE;
       nsAttrSelector* attr = aSelector->mAttrList;
+      nsIAtom* matchAttribute;
+
       do {
-        if (attr->mAttr == aAttribute) {
+        matchAttribute = isHTML ? attr->mLowercaseAttr : attr->mCasedAttr;
+        if (matchAttribute == aAttribute) {
           // XXX we should really have a namespace, not just an attr
           // name, in HasAttributeDependentStyle!
           result = PR_TRUE;
           if (aDependence)
             *aDependence = PR_TRUE;
         }
         else if (attr->mNameSpace == kNameSpaceID_Unknown) {
           // Attr selector with a wildcard namespace.  We have to examine all
@@ -1696,59 +1696,60 @@ static PRBool SelectorMatches(RuleProces
           // have a chance at matching, of course, are ones that the element
           // actually has attributes in), short-circuiting if we ever match.
           PRUint32 attrCount = data.mContent->GetAttrCount();
           result = PR_FALSE;
           for (PRUint32 i = 0; i < attrCount; ++i) {
             const nsAttrName* attrName =
               data.mContent->GetAttrNameAt(i);
             NS_ASSERTION(attrName, "GetAttrCount lied or GetAttrNameAt failed");
-            if (attrName->LocalName() != attr->mAttr) {
+            if (attrName->LocalName() != matchAttribute) {
               continue;
             }
             if (attr->mFunction == NS_ATTR_FUNC_SET) {
               result = PR_TRUE;
             } else {
               nsAutoString value;
 #ifdef DEBUG
               PRBool hasAttr =
 #endif
                 data.mContent->GetAttr(attrName->NamespaceID(),
                                        attrName->LocalName(), value);
               NS_ASSERTION(hasAttr, "GetAttrNameAt lied");
-              result = AttrMatchesValue(attr, value);
+              result = AttrMatchesValue(attr, value, isHTML);
             }
 
             // At this point |result| has been set by us
             // explicitly in this loop.  If it's PR_FALSE, we may still match
             // -- the content may have another attribute with the same name but
             // in a different namespace.  But if it's PR_TRUE, we are done (we
             // can short-circuit the boolean OR described above).
             if (result) {
               break;
             }
           }
         }
         else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) {
           result =
             data.mContent->
-              AttrValueIs(attr->mNameSpace, attr->mAttr, attr->mValue,
-                          attr->mCaseSensitive ? eCaseMatters : eIgnoreCase);
+              AttrValueIs(attr->mNameSpace, matchAttribute, attr->mValue,
+                          (!isHTML || attr->mCaseSensitive) ? eCaseMatters
+                                                            : eIgnoreCase);
         }
-        else if (!data.mContent->HasAttr(attr->mNameSpace, attr->mAttr)) {
+        else if (!data.mContent->HasAttr(attr->mNameSpace, matchAttribute)) {
           result = PR_FALSE;
         }
         else if (attr->mFunction != NS_ATTR_FUNC_SET) {
           nsAutoString value;
 #ifdef DEBUG
           PRBool hasAttr =
 #endif
-              data.mContent->GetAttr(attr->mNameSpace, attr->mAttr, value);
+              data.mContent->GetAttr(attr->mNameSpace, matchAttribute, value);
           NS_ASSERTION(hasAttr, "HasAttr lied");
-          result = AttrMatchesValue(attr, value);
+          result = AttrMatchesValue(attr, value, isHTML);
         }
         
         attr = attr->mNext;
       } while (attr && result);
     }
   }
   nsAtomList* IDList = aSelector->mIDList;
   if (result && IDList) {
@@ -2321,17 +2322,18 @@ AddRule(RuleValue* aRuleInfo, void* aCas
       // Build mClassSelectors
       if (negation->mClassList) {
         classArray->AppendElement(selector);
       }
 
       // Build mAttributeSelectors.
       for (nsAttrSelector *attr = negation->mAttrList; attr;
            attr = attr->mNext) {
-        nsTArray<nsCSSSelector*> *array = cascade->AttributeListFor(attr->mAttr);
+        nsTArray<nsCSSSelector*> *array =
+          cascade->AttributeListFor(attr->mCasedAttr);
         if (!array)
           return PR_FALSE;
         array->AppendElement(selector);
       }
     }
   }
 
   return PR_TRUE;
--- a/layout/style/nsCSSStyleRule.cpp
+++ b/layout/style/nsCSSStyleRule.cpp
@@ -194,58 +194,70 @@ nsPseudoClassList::~nsPseudoClassList(vo
   if (u.mMemory)
     NS_Free(u.mMemory);
   NS_CSS_DELETE_LIST_MEMBER(nsPseudoClassList, this, mNext);
 }
 
 nsAttrSelector::nsAttrSelector(PRInt32 aNameSpace, const nsString& aAttr)
   : mValue(),
     mNext(nsnull),
-    mAttr(nsnull),
+    mLowercaseAttr(nsnull),
+    mCasedAttr(nsnull),
     mNameSpace(aNameSpace),
     mFunction(NS_ATTR_FUNC_SET),
     mCaseSensitive(1)
 {
   MOZ_COUNT_CTOR(nsAttrSelector);
 
-  mAttr = do_GetAtom(aAttr);
+  nsAutoString lowercase;
+  ToLowerCase(aAttr, lowercase);
+  
+  mCasedAttr = do_GetAtom(aAttr);
+  mLowercaseAttr = do_GetAtom(lowercase);
 }
 
 nsAttrSelector::nsAttrSelector(PRInt32 aNameSpace, const nsString& aAttr, PRUint8 aFunction, 
                                const nsString& aValue, PRBool aCaseSensitive)
   : mValue(aValue),
     mNext(nsnull),
-    mAttr(nsnull),
+    mLowercaseAttr(nsnull),
+    mCasedAttr(nsnull),
     mNameSpace(aNameSpace),
     mFunction(aFunction),
     mCaseSensitive(aCaseSensitive)
 {
   MOZ_COUNT_CTOR(nsAttrSelector);
 
-  mAttr = do_GetAtom(aAttr);
+  nsAutoString lowercase;
+  ToLowerCase(aAttr, lowercase);
+  
+  mCasedAttr = do_GetAtom(aAttr);
+  mLowercaseAttr = do_GetAtom(lowercase);
 }
 
-nsAttrSelector::nsAttrSelector(PRInt32 aNameSpace, nsIAtom* aAttr,
-                               PRUint8 aFunction, const nsString& aValue,
-                               PRBool aCaseSensitive)
+nsAttrSelector::nsAttrSelector(PRInt32 aNameSpace,  nsIAtom* aLowercaseAttr,
+                               nsIAtom* aCasedAttr, PRUint8 aFunction, 
+                               const nsString& aValue, PRBool aCaseSensitive)
   : mValue(aValue),
     mNext(nsnull),
-    mAttr(aAttr),
+    mLowercaseAttr(aLowercaseAttr),
+    mCasedAttr(aCasedAttr),
     mNameSpace(aNameSpace),
     mFunction(aFunction),
     mCaseSensitive(aCaseSensitive)
 {
   MOZ_COUNT_CTOR(nsAttrSelector);
 }
 
 nsAttrSelector*
 nsAttrSelector::Clone(PRBool aDeep) const
 {
   nsAttrSelector *result =
-    new nsAttrSelector(mNameSpace, mAttr, mFunction, mValue, mCaseSensitive);
+    new nsAttrSelector(mNameSpace, mLowercaseAttr, mCasedAttr, 
+                       mFunction, mValue, mCaseSensitive);
 
   if (aDeep)
     NS_CSS_CLONE_LIST_MEMBER(nsAttrSelector, this, mNext, result, (PR_FALSE));
 
   return result;
 }
 
 nsAttrSelector::~nsAttrSelector(void)
@@ -332,33 +344,28 @@ void nsCSSSelector::Reset(void)
   mOperator = PRUnichar(0);
 }
 
 void nsCSSSelector::SetNameSpace(PRInt32 aNameSpace)
 {
   mNameSpace = aNameSpace;
 }
 
-void nsCSSSelector::SetTag(const nsString& aTag, PRBool aCaseMatters)
+void nsCSSSelector::SetTag(const nsString& aTag)
 {
   if (aTag.IsEmpty()) {
     mLowercaseTag = mCasedTag =  nsnull;
     return;
   }
 
   mCasedTag = do_GetAtom(aTag);
  
-  if (aCaseMatters) {
-    mLowercaseTag = mCasedTag;
-  } 
-  else {
-    nsAutoString lowercase(aTag);
-    ToLowerCase(lowercase);
-    mLowercaseTag = do_GetAtom(lowercase);
-  }
+  nsAutoString lowercase;
+  ToLowerCase(aTag, lowercase);
+  mLowercaseTag = do_GetAtom(lowercase);
 }
 
 void nsCSSSelector::AddID(const nsString& aID)
 {
   if (!aID.IsEmpty()) {
     nsAtomList** list = &mIDList;
     while (nsnull != *list) {
       list = &((*list)->mNext);
@@ -652,17 +659,17 @@ nsCSSSelector::AppendToStringWithoutComb
             nsAutoString prefix;
             prefixAtom->ToString(prefix);
             aString.Append(prefix);
             aString.Append(PRUnichar('|'));
           }
         }
       }
       // Append the attribute name
-      list->mAttr->ToString(temp);
+      list->mCasedAttr->ToString(temp);
       aString.Append(temp);
 
       if (list->mFunction != NS_ATTR_FUNC_SET) {
         // Append the function
         if (list->mFunction == NS_ATTR_FUNC_INCLUDES)
           aString.Append(PRUnichar('~'));
         else if (list->mFunction == NS_ATTR_FUNC_DASHMATCH)
           aString.Append(PRUnichar('|'));
--- a/layout/style/nsICSSLoader.h
+++ b/layout/style/nsICSSLoader.h
@@ -67,17 +67,16 @@ typedef void (*nsCSSLoaderCallbackFunc)(
 
 class nsICSSLoader : public nsISupports {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICSS_LOADER_IID)
 
   NS_IMETHOD Init(nsIDocument* aDocument) = 0;
   NS_IMETHOD DropDocumentReference(void) = 0; // notification that doc is going away
 
-  NS_IMETHOD SetCaseSensitive(PRBool aCaseSensitive) = 0;
   NS_IMETHOD SetCompatibilityMode(nsCompatibility aCompatMode) = 0;
   NS_IMETHOD SetPreferredSheet(const nsAString& aTitle) = 0;
   NS_IMETHOD GetPreferredSheet(nsAString& aTitle) = 0;
 
   // Get/Recycle a CSS parser for general use
   NS_IMETHOD GetParserFor(nsICSSStyleSheet* aSheet,
                           nsICSSParser** aParser) = 0;
   NS_IMETHOD RecycleParser(nsICSSParser* aParser) = 0;
--- a/layout/style/nsICSSParser.h
+++ b/layout/style/nsICSSParser.h
@@ -69,19 +69,16 @@ class nsICSSParser : public nsISupports 
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICSS_PARSER_IID)
 
   // Set a style sheet for the parser to fill in. The style sheet must
   // implement the nsICSSStyleSheet interface.  Null can be passed in to clear
   // out an existing stylesheet reference.
   NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet) = 0;
 
-  // Set whether or not tags & classes are case sensitive or uppercased
-  NS_IMETHOD SetCaseSensitive(PRBool aCaseSensitive) = 0;
-
   // Set whether or not to emulate Nav quirks
   NS_IMETHOD SetQuirkMode(PRBool aQuirkMode) = 0;
 
 #ifdef  MOZ_SVG
   // Set whether or not we are in an SVG element
   NS_IMETHOD SetSVGMode(PRBool aSVGMode) = 0;
 #endif
 
--- a/layout/style/nsICSSStyleRule.h
+++ b/layout/style/nsICSSStyleRule.h
@@ -116,29 +116,32 @@ private:
 #define NS_ATTR_FUNC_ENDSMATCH  5     // [attr$=value] (ends with)
 #define NS_ATTR_FUNC_CONTAINSMATCH 6  // [attr*=value] (contains substring)
 
 struct nsAttrSelector {
 public:
   nsAttrSelector(PRInt32 aNameSpace, const nsString& aAttr);
   nsAttrSelector(PRInt32 aNameSpace, const nsString& aAttr, PRUint8 aFunction, 
                  const nsString& aValue, PRBool aCaseSensitive);
-  nsAttrSelector(PRInt32 aNameSpace, nsIAtom* aAttr, PRUint8 aFunction, 
+  nsAttrSelector(PRInt32 aNameSpace, nsIAtom* aLowercaseAttr, 
+                 nsIAtom* aCasedAttr, PRUint8 aFunction, 
                  const nsString& aValue, PRBool aCaseSensitive);
   ~nsAttrSelector(void);
 
   /** Do a deep clone.  Should be used only on the first in the linked list. */
   nsAttrSelector* Clone() const { return Clone(PR_TRUE); }
 
   nsString        mValue;
   nsAttrSelector* mNext;
-  nsCOMPtr<nsIAtom> mAttr;
+  nsCOMPtr<nsIAtom> mLowercaseAttr;
+  nsCOMPtr<nsIAtom> mCasedAttr;
   PRInt32         mNameSpace;
   PRUint8         mFunction;
-  PRPackedBool    mCaseSensitive;
+  PRPackedBool    mCaseSensitive; // If we are in an HTML document,
+                                  // is the value case sensitive?
 private: 
   nsAttrSelector* Clone(PRBool aDeep) const;
 
   // These are not supported and are not implemented! 
   nsAttrSelector(const nsAttrSelector& aCopy);
   nsAttrSelector& operator=(const nsAttrSelector& aCopy); 
 };
 
@@ -147,17 +150,17 @@ public:
   nsCSSSelector(void);
   ~nsCSSSelector(void);
 
   /** Do a deep clone.  Should be used only on the first in the linked list. */
   nsCSSSelector* Clone() const { return Clone(PR_TRUE, PR_TRUE); }
 
   void Reset(void);
   void SetNameSpace(PRInt32 aNameSpace);
-  void SetTag(const nsString& aTag, PRBool aCaseSensitive);
+  void SetTag(const nsString& aTag);
   void AddID(const nsString& aID);
   void AddClass(const nsString& aClass);
   void AddPseudoClass(nsIAtom* aPseudoClass);
   void AddPseudoClass(nsIAtom* aPseudoClass, const PRUnichar* aString);
   void AddPseudoClass(nsIAtom* aPseudoClass, const PRInt32* aIntPair);
   void AddAttribute(PRInt32 aNameSpace, const nsString& aAttr);
   void AddAttribute(PRInt32 aNameSpace, const nsString& aAttr, PRUint8 aFunc, 
                     const nsString& aValue, PRBool aCaseSensitive);