From d86ade967d9b69028959b09c6c22fdd14d2872dc Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Wed, 23 Dec 2009 10:01:51 -0800 Subject: [PATCH] Bug 524223: Even in quirks mode, ignore linked stylesheets which were served with an inappropriate MIME type, if they're from a different origin than the requesting document or stylesheet. --- layout/style/nsCSSLoader.cpp | 101 ++++++++------- layout/style/test/Makefile.in | 11 +- layout/style/test/ccd-quirks.html | 124 +++++++++++++++++++ layout/style/test/ccd-standards.html | 123 ++++++++++++++++++ layout/style/test/ccd.sjs | 73 +++++++++++ layout/style/test/redirect-1.css | 0 layout/style/test/redirect-1.css^headers^ | 2 - layout/style/test/redirect-2.css | 0 layout/style/test/redirect-2.css^headers^ | 2 - layout/style/test/redirect-3.css | 0 layout/style/test/redirect-3.css^headers^ | 2 - layout/style/test/redirect.sjs | 5 + layout/style/test/test_bug397427.html | 38 +++--- layout/style/test/test_css_cross_domain.html | 100 +++++++++++++++ 14 files changed, 508 insertions(+), 73 deletions(-) create mode 100644 layout/style/test/ccd-quirks.html create mode 100644 layout/style/test/ccd-standards.html create mode 100644 layout/style/test/ccd.sjs delete mode 100644 layout/style/test/redirect-1.css delete mode 100644 layout/style/test/redirect-1.css^headers^ delete mode 100644 layout/style/test/redirect-2.css delete mode 100644 layout/style/test/redirect-2.css^headers^ delete mode 100644 layout/style/test/redirect-3.css delete mode 100644 layout/style/test/redirect-3.css^headers^ create mode 100644 layout/style/test/redirect.sjs create mode 100644 layout/style/test/test_css_cross_domain.html diff --git a/layout/style/nsCSSLoader.cpp b/layout/style/nsCSSLoader.cpp index da01c467e831..8f284e82dd0e 100644 --- a/layout/style/nsCSSLoader.cpp +++ b/layout/style/nsCSSLoader.cpp @@ -798,56 +798,73 @@ SheetLoadData::OnStreamComplete(nsIUnicharStreamLoader* aLoader, } } - if (aDataStream) { - nsCAutoString contentType; - if (channel) { - channel->GetContentType(contentType); - } - - PRBool validType = contentType.EqualsLiteral("text/css") || - contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) || - contentType.IsEmpty(); - - if (!validType) { - nsCAutoString spec; - channelURI->GetSpec(spec); - - const nsAFlatString& specUTF16 = NS_ConvertUTF8toUTF16(spec); - const nsAFlatString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType); - const PRUnichar *strings[] = { specUTF16.get(), ctypeUTF16.get() }; - - const char *errorMessage; - PRUint32 errorFlag; - - if (mLoader->mCompatMode == eCompatibility_NavQuirks) { - errorMessage = "MimeNotCssWarn"; - errorFlag = nsIScriptError::warningFlag; - } else { - // Drop the data stream so that we do not load it - aDataStream = nsnull; - - errorMessage = "MimeNotCss"; - errorFlag = nsIScriptError::errorFlag; - } - nsCOMPtr referrer = GetReferrerURI(); - nsContentUtils::ReportToConsole(nsContentUtils::eCSS_PROPERTIES, - errorMessage, - strings, NS_ARRAY_LENGTH(strings), - referrer, EmptyString(), 0, 0, errorFlag, - "CSS Loader"); - } - } - if (!aDataStream) { LOG_WARN((" No data stream; bailing")); mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE); return NS_OK; - } + } + + nsCAutoString contentType; + if (channel) { + channel->GetContentType(contentType); + } + + // In standards mode, a style sheet must have one of these MIME + // types to be processed at all. In quirks mode, we accept any + // MIME type, but only if the style sheet is same-origin with the + // requesting document or parent sheet. See bug 524223. + + PRBool validType = contentType.EqualsLiteral("text/css") || + contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) || + contentType.IsEmpty(); + + if (!validType) { + const char *errorMessage; + PRUint32 errorFlag; + PRBool sameOrigin = PR_TRUE; + + if (mLoaderPrincipal) { + PRBool subsumed; + result = mLoaderPrincipal->Subsumes(principal, &subsumed); + if (NS_FAILED(result) || !subsumed) { + sameOrigin = PR_FALSE; + } + } + + if (sameOrigin && mLoader->mCompatMode == eCompatibility_NavQuirks) { + errorMessage = "MimeNotCssWarn"; + errorFlag = nsIScriptError::warningFlag; + } else { + errorMessage = "MimeNotCss"; + errorFlag = nsIScriptError::errorFlag; + } + + nsCAutoString spec; + channelURI->GetSpec(spec); + + const nsAFlatString& specUTF16 = NS_ConvertUTF8toUTF16(spec); + const nsAFlatString& ctypeUTF16 = NS_ConvertASCIItoUTF16(contentType); + const PRUnichar *strings[] = { specUTF16.get(), ctypeUTF16.get() }; + + nsCOMPtr referrer = GetReferrerURI(); + nsContentUtils::ReportToConsole(nsContentUtils::eCSS_PROPERTIES, + errorMessage, + strings, NS_ARRAY_LENGTH(strings), + referrer, EmptyString(), 0, 0, errorFlag, + "CSS Loader"); + + if (errorFlag == nsIScriptError::errorFlag) { + LOG_WARN((" Ignoring sheet with improper MIME type %s", + contentType.get())); + mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE); + return NS_OK; + } + } // Enough to set the URIs on mSheet, since any sibling datas we have share // the same mInner as mSheet and will thus get the same URI. mSheet->SetURIs(channelURI, originalURI, channelURI); - + PRBool completed; return mLoader->ParseSheet(aDataStream, this, completed); } diff --git a/layout/style/test/Makefile.in b/layout/style/test/Makefile.in index 6734f9acae1d..602399325ad0 100644 --- a/layout/style/test/Makefile.in +++ b/layout/style/test/Makefile.in @@ -108,6 +108,7 @@ _TEST_FILES = test_acid3_test46.html \ test_cascade.html \ test_compute_data_with_start_struct.html \ test_computed_style_no_pseudo.html \ + test_css_cross_domain.html \ test_css_eof_handling.html \ test_descriptor_storage.html \ test_descriptor_syntax_errors.html \ @@ -156,14 +157,9 @@ _TEST_FILES = test_acid3_test46.html \ unstyled.css \ unstyled-frame.xml \ unstyled-frame.css \ - redirect-1.css \ + redirect.sjs \ post-redirect-1.css \ - redirect-1.css^headers^ \ - redirect-2.css \ post-redirect-2.css \ - redirect-2.css^headers^ \ - redirect-3.css \ - redirect-3.css^headers^ \ post-redirect-3.css \ xbl_bindings.xml \ empty.html \ @@ -174,6 +170,9 @@ _TEST_FILES = test_acid3_test46.html \ bug453896_iframe.html \ bug517224.sjs \ test_bug525952.html \ + ccd-quirks.html \ + ccd-standards.html \ + ccd.sjs \ $(NULL) _BROWSER_FILES = \ diff --git a/layout/style/test/ccd-quirks.html b/layout/style/test/ccd-quirks.html new file mode 100644 index 000000000000..61cbc9ff5d50 --- /dev/null +++ b/layout/style/test/ccd-quirks.html @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+ diff --git a/layout/style/test/ccd-standards.html b/layout/style/test/ccd-standards.html new file mode 100644 index 000000000000..2f1c56042874 --- /dev/null +++ b/layout/style/test/ccd-standards.html @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+ diff --git a/layout/style/test/ccd.sjs b/layout/style/test/ccd.sjs new file mode 100644 index 000000000000..058393e82ee4 --- /dev/null +++ b/layout/style/test/ccd.sjs @@ -0,0 +1,73 @@ +const DEBUG_all_valid = false; +const DEBUG_all_stub = false; + +function handleRequest(request, response) +{ + // Decode the query string to know what test we're doing. + + // character 1: 'I' = text/css response, 'J' = text/html response + let responseCSS = (request.queryString[0] == 'I'); + + // character 2: redirection type - we only care about whether we're + // ultimately same-origin with the requesting document ('A', 'D') or + // not ('B', 'C'). + let sameOrigin = (request.queryString[1] == 'A' || + request.queryString[1] == 'D'); + + // character 3: '1' = syntactically valid, '2' = invalid, '3' = http error + let malformed = (request.queryString[2] == '2'); + let httpError = (request.queryString[2] == '3'); + + // character 4: loaded with or @import (no action required) + + // character 5: loading document mode: 'q' = quirks, 's' = standards + let quirksMode = (request.queryString[4] == 'q'); + + // Our response contains a CSS rule that selects an element whose + // ID is the first four characters of the query string. + let selector = '#' + request.queryString.substring(0,4); + + // "Malformed" responses wrap the CSS rule in the construct + // {} ... + // This mimics what the CSS parser might see if an actual HTML + // document were fed to it. Because CSS parsers recover from + // errors by skipping tokens until they find something + // recognizable, a style rule appearing where I wrote '...' above + // will be honored! + let leader = (malformed ? '{}' : ''); + let trailer = (malformed ? '' : ''); + + // Standards mode documents will ignore the style sheet if it is being + // served as text/html (regardless of its contents). Quirks mode + // documents will ignore the style sheet if it is being served as + // text/html _and_ it is not same-origin. Regardless, style sheets + // are ignored if they come as the body of an HTTP error response. + // + // Style sheets that should be ignored paint the element red; those + // that should be honored paint it lime. + let color = ((responseCSS || (quirksMode && sameOrigin)) && !httpError + ? 'lime' : 'red'); + + // For debugging the test itself, we have the capacity to make every style + // sheet well-formed, or every style sheet do nothing. + if (DEBUG_all_valid) { + // In this mode, every test chip should turn blue. + response.setHeader('Content-Type', 'text/css'); + response.write(selector + '{background-color:blue}\n'); + } else if (DEBUG_all_stub) { + // In this mode, every test chip for a case where the true test + // sheet would be honored, should turn red. + response.setHeader('Content-Type', 'text/css'); + response.write(selector + '{}\n'); + } else { + // Normal operation. + if (httpError) + response.setStatusLine(request.httpVersion, 500, + "Internal Server Error"); + response.setHeader('Content-Type', + responseCSS ? 'text/css' : 'text/html'); + response.write(leader + selector + + '{background-color:' + color + '}' + + trailer + '\n'); + } +} diff --git a/layout/style/test/redirect-1.css b/layout/style/test/redirect-1.css deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/layout/style/test/redirect-1.css^headers^ b/layout/style/test/redirect-1.css^headers^ deleted file mode 100644 index 582de3f2be23..000000000000 --- a/layout/style/test/redirect-1.css^headers^ +++ /dev/null @@ -1,2 +0,0 @@ -HTTP 302 Found -Location: http://example.org/tests/layout/style/test/post-redirect-1.css diff --git a/layout/style/test/redirect-2.css b/layout/style/test/redirect-2.css deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/layout/style/test/redirect-2.css^headers^ b/layout/style/test/redirect-2.css^headers^ deleted file mode 100644 index 09280cc486b6..000000000000 --- a/layout/style/test/redirect-2.css^headers^ +++ /dev/null @@ -1,2 +0,0 @@ -HTTP 302 Found -Location: http://example.org/tests/layout/style/test/post-redirect-2.css diff --git a/layout/style/test/redirect-3.css b/layout/style/test/redirect-3.css deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/layout/style/test/redirect-3.css^headers^ b/layout/style/test/redirect-3.css^headers^ deleted file mode 100644 index 42419234f2d5..000000000000 --- a/layout/style/test/redirect-3.css^headers^ +++ /dev/null @@ -1,2 +0,0 @@ -HTTP 302 Found -Location: http://example.org/tests/layout/style/test/post-redirect-3.css diff --git a/layout/style/test/redirect.sjs b/layout/style/test/redirect.sjs new file mode 100644 index 000000000000..b6249cadff1b --- /dev/null +++ b/layout/style/test/redirect.sjs @@ -0,0 +1,5 @@ +function handleRequest(request, response) +{ + response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); + response.setHeader("Location", request.queryString, false); +} diff --git a/layout/style/test/test_bug397427.html b/layout/style/test/test_bug397427.html index cc978796db2d..7f7e59966f19 100644 --- a/layout/style/test/test_bug397427.html +++ b/layout/style/test/test_bug397427.html @@ -9,13 +9,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=397427 - - + + Mozilla Bug 397427 @@ -55,30 +55,30 @@ addLoadEvent(function() { "Redirect 3 did not get right base URI"); var ruleList = $("a").sheet.cssRules; - - is(ruleList[0].styleSheet.href, - window.location.href.replace(/test_bug397427.html$/, "redirect-1.css"), + + var redirHrefBase = + window.location.href.replace(/test_bug397427.html$/, + "redirect.sjs?http://example.org/tests/layout/style/test/post-"); + + is(ruleList[0].styleSheet.href, redirHrefBase + "redirect-1.css", "Unexpected href for imported sheet"); - todo(ruleList[0].href == window.location.href.replace(/test_bug397427.html$/, "redirect-1.css"), - "Rule href should be absolute"); - is(ruleList[1].styleSheet.href, - window.location.href.replace(/test_bug397427.html$/, "redirect-2.css"), + todo_is(ruleList[0].href, redirHrefBase + "redirect-1.css", + "Rule href should be absolute"); + is(ruleList[1].styleSheet.href, redirHrefBase + "redirect-2.css", "Unexpected href for imported sheet"); - todo(ruleList[1].href == window.location.href.replace(/test_bug397427.html$/, "redirect-2.css"), - "Rule href should be absolute"); + todo_is(ruleList[1].href, redirHrefBase + "redirect-2.css", + "Rule href should be absolute"); is($("b").href, "http://example.com/", "Unexpected href one"); is($("b").href, $("b").sheet.href, - "Should have the same href when not redirecing"); + "Should have the same href when not redirecting"); - is($("c").href, - window.location.href.replace(/test_bug397427.html$/, "redirect-2.css"), + is($("c").href, redirHrefBase + "redirect-2.css", "Unexpected href two"); is($("c").href, $("c").sheet.href, "Should have the same href when redirecting"); - - is($("d").href, - window.location.href.replace(/test_bug397427.html$/, "redirect-3.css"), + + is($("d").href, redirHrefBase + "redirect-3.css", "Unexpected href three"); is($("d").href, $("d").sheet.href, "Should have the same href when redirecting again"); diff --git a/layout/style/test/test_css_cross_domain.html b/layout/style/test/test_css_cross_domain.html new file mode 100644 index 000000000000..6447a2a2db80 --- /dev/null +++ b/layout/style/test/test_css_cross_domain.html @@ -0,0 +1,100 @@ + + + + + Test cross-domain CSS loading + + + + + + +Mozilla + Bug 524223 + +
+ +
+

 

+
  1. text/css
    1. same origin
      1. valid
      2. +
      3. malformed
      4. +
      5. http error
    2. +
    3. cross origin
      1. valid
      2. +
      3. malformed
      4. +
      5. http error
    4. +
    5. same to cross
      1. valid
      2. +
      3. malformed
      4. +
      5. http error
    6. +
    7. cross to same
      1. valid
      2. +
      3. malformed
      4. +
      5. http error
  2. +
  3. text/html
    1. same origin
      1. valid
      2. +
      3. malformed
      4. +
      5. http error
    2. +
    3. cross origin
      1. valid
      2. +
      3. malformed
      4. +
      5. http error
    4. +
    5. same to cross
      1. valid
      2. +
      3. malformed
      4. +
      5. http error
    6. +
    7. cross to same
      1. valid
      2. +
      3. malformed
      4. +
      5. http error
  4. +
+
+ +
+

Quirks

+ +
+ +
+

Standards

+ +
+ + + +