diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index f8df0b124240..88c8bd19e6da 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -1359,6 +1359,26 @@ public: { return sThreadJSContextStack; } + + + /** + * Get the Origin of the passed in nsIPrincipal or nsIURI. If the passed in + * nsIURI or the URI of the passed in nsIPrincipal does not have a host, the + * origin is set to 'null'. + * + * The ASCII versions return a ASCII strings that are puny-code encoded, + * suitable for for example header values. The UTF versions return strings + * containing international characters. + * + * aPrincipal/aOrigin must not be null. + */ + static nsresult GetASCIIOrigin(nsIPrincipal* aPrincipal, + nsCString& aOrigin); + static nsresult GetASCIIOrigin(nsIURI* aURI, nsCString& aOrigin); + static nsresult GetUTFOrigin(nsIPrincipal* aPrincipal, + nsString& aOrigin); + static nsresult GetUTFOrigin(nsIURI* aURI, nsString& aOrigin); + private: static PRBool InitializeEventTable(); diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 0ae1d1009c7e..d32d3b850b67 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -4525,6 +4525,125 @@ nsSameOriginChecker::GetInterface(const nsIID & aIID, void **aResult) return QueryInterface(aIID, aResult); } +/* static */ +nsresult +nsContentUtils::GetASCIIOrigin(nsIPrincipal* aPrincipal, nsCString& aOrigin) +{ + NS_PRECONDITION(aPrincipal, "missing principal"); + + aOrigin.Truncate(); + + nsCOMPtr uri; + nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + + if (uri) { + return GetASCIIOrigin(uri, aOrigin); + } + + aOrigin.AssignLiteral("null"); + + return NS_OK; +} + +/* static */ +nsresult +nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsCString& aOrigin) +{ + NS_PRECONDITION(aURI, "missing uri"); + + aOrigin.Truncate(); + + nsCOMPtr uri = NS_GetInnermostURI(aURI); + NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); + + nsCString host; + nsresult rv = uri->GetAsciiHost(host); + + if (NS_SUCCEEDED(rv) && !host.IsEmpty()) { + nsCString scheme; + rv = uri->GetScheme(scheme); + NS_ENSURE_SUCCESS(rv, rv); + + aOrigin = scheme + NS_LITERAL_CSTRING("://") + host; + + // If needed, append the port + PRInt32 port; + uri->GetPort(&port); + if (port != -1) { + PRInt32 defaultPort = NS_GetDefaultPort(scheme.get()); + if (port != defaultPort) { + aOrigin.Append(':'); + aOrigin.AppendInt(port); + } + } + } + else { + aOrigin.AssignLiteral("null"); + } + + return NS_OK; +} + +/* static */ +nsresult +nsContentUtils::GetUTFOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin) +{ + NS_PRECONDITION(aPrincipal, "missing principal"); + + aOrigin.Truncate(); + + nsCOMPtr uri; + nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + + if (uri) { + return GetUTFOrigin(uri, aOrigin); + } + + aOrigin.AssignLiteral("null"); + + return NS_OK; +} + +/* static */ +nsresult +nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsString& aOrigin) +{ + NS_PRECONDITION(aURI, "missing uri"); + + aOrigin.Truncate(); + + nsCOMPtr uri = NS_GetInnermostURI(aURI); + NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); + + nsCString host; + nsresult rv = uri->GetHost(host); + + if (NS_SUCCEEDED(rv) && !host.IsEmpty()) { + nsCString scheme; + rv = uri->GetScheme(scheme); + NS_ENSURE_SUCCESS(rv, rv); + + aOrigin = NS_ConvertUTF8toUTF16(scheme + NS_LITERAL_CSTRING("://") + host); + + // If needed, append the port + PRInt32 port; + uri->GetPort(&port); + if (port != -1) { + PRInt32 defaultPort = NS_GetDefaultPort(scheme.get()); + if (port != defaultPort) { + aOrigin.Append(':'); + aOrigin.AppendInt(port); + } + } + } + else { + aOrigin.AssignLiteral("null"); + } + + return NS_OK; +} nsContentTypeParser::nsContentTypeParser(const nsAString& aString) : mString(aString), mService(nsnull) { diff --git a/content/base/src/nsCrossSiteListenerProxy.cpp b/content/base/src/nsCrossSiteListenerProxy.cpp index 03c593436c7c..a44d0817e215 100644 --- a/content/base/src/nsCrossSiteListenerProxy.cpp +++ b/content/base/src/nsCrossSiteListenerProxy.cpp @@ -105,7 +105,7 @@ NS_IMETHODIMP nsCrossSiteListenerProxy::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) { - mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest)); + mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest, PR_FALSE)); if (!mRequestApproved) { aRequest->Cancel(NS_ERROR_DOM_BAD_URI); mOuterListener->OnStartRequest(aRequest, aContext); @@ -157,45 +157,8 @@ IsValidHTTPToken(const nsCSubstring& aToken) } nsresult -GetOrigin(nsIPrincipal* aPrincipal, nsCString& aOrigin) -{ - aOrigin.AssignLiteral("null"); - - nsCString host; - nsCOMPtr uri; - nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = uri->GetAsciiHost(host); - NS_ENSURE_SUCCESS(rv, rv); - - if (!host.IsEmpty()) { - nsCString scheme; - rv = uri->GetScheme(scheme); - NS_ENSURE_SUCCESS(rv, rv); - - aOrigin = scheme + NS_LITERAL_CSTRING("://") + host; - - // If needed, append the port - PRInt32 port; - uri->GetPort(&port); - if (port != -1) { - PRInt32 defaultPort = NS_GetDefaultPort(scheme.get()); - if (port != defaultPort) { - aOrigin.Append(":"); - aOrigin.AppendInt(port); - } - } - } - else { - aOrigin.AssignLiteral("null"); - } - - return NS_OK; -} - -nsresult -nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest) +nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest, + PRBool aIsRedirect) { // Check if this was actually a cross domain request if (!mHasBeenCrossSite) { @@ -212,10 +175,14 @@ nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest) nsCOMPtr http = do_QueryInterface(aRequest); NS_ENSURE_TRUE(http, NS_ERROR_DOM_BAD_URI); - PRBool succeeded; - rv = http->GetRequestSucceeded(&succeeded); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(succeeded, NS_ERROR_DOM_BAD_URI); + // Redirects aren't success-codes. But necko already checked that it was a + // valid redirect. + if (!aIsRedirect) { + PRBool succeeded; + rv = http->GetRequestSucceeded(&succeeded); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(succeeded, NS_ERROR_DOM_BAD_URI); + } // Check the Access-Control-Allow-Origin header nsCAutoString allowedOriginHeader; @@ -225,10 +192,11 @@ nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest) if (mWithCredentials || !allowedOriginHeader.EqualsLiteral("*")) { nsCAutoString origin; - rv = GetOrigin(mRequestingPrincipal, origin); + rv = nsContentUtils::GetASCIIOrigin(mRequestingPrincipal, origin); NS_ENSURE_SUCCESS(rv, rv); - if (!allowedOriginHeader.Equals(origin)) { + if (!allowedOriginHeader.Equals(origin) || + origin.EqualsLiteral("null")) { return NS_ERROR_DOM_BAD_URI; } } @@ -268,6 +236,7 @@ nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest) // The "Access-Control-Allow-Headers" header contains a comma separated // list of header names. + headerVal.Truncate(); http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"), headerVal); nsTArray headers; @@ -335,8 +304,11 @@ nsCrossSiteListenerProxy::OnChannelRedirect(nsIChannel *aOldChannel, nsIChannel *aNewChannel, PRUint32 aFlags) { - nsresult rv = CheckRequestApproved(aOldChannel); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv; + if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) { + rv = CheckRequestApproved(aOldChannel, PR_TRUE); + NS_ENSURE_SUCCESS(rv, rv); + } nsCOMPtr outer = do_GetInterface(mOuterNotificationCallbacks); @@ -387,7 +359,7 @@ nsCrossSiteListenerProxy::UpdateChannel(nsIChannel* aChannel) // Add the Origin header nsCAutoString origin; - rv = GetOrigin(mRequestingPrincipal, origin); + rv = nsContentUtils::GetASCIIOrigin(mRequestingPrincipal, origin); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr http = do_QueryInterface(aChannel); diff --git a/content/base/src/nsCrossSiteListenerProxy.h b/content/base/src/nsCrossSiteListenerProxy.h index d5f3074fdf32..1ff5bc59bed8 100644 --- a/content/base/src/nsCrossSiteListenerProxy.h +++ b/content/base/src/nsCrossSiteListenerProxy.h @@ -77,7 +77,7 @@ public: private: nsresult UpdateChannel(nsIChannel* aChannel); - nsresult CheckRequestApproved(nsIRequest* aRequest); + nsresult CheckRequestApproved(nsIRequest* aRequest, PRBool aIsRedirect); nsCOMPtr mOuterListener; nsCOMPtr mRequestingPrincipal; diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index c8392d5297cd..75cfce45d011 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -489,8 +489,9 @@ nsACProxyListener::OnChannelRedirect(nsIChannel *aOldChannel, nsIChannel *aNewChannel, PRUint32 aFlags) { - // No redirects allowed for now. - return NS_ERROR_DOM_BAD_URI; + // Only internal redirects allowed for now. + return NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) ? + NS_OK : NS_ERROR_DOM_BAD_URI; } NS_IMETHODIMP @@ -2634,7 +2635,8 @@ nsXMLHttpRequest::Send(nsIVariant *aBody) mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT; } } - else if (!method.LowerCaseEqualsLiteral("get")) { + else if (!method.LowerCaseEqualsLiteral("get") && + !method.LowerCaseEqualsLiteral("head")) { mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT; } @@ -3111,14 +3113,16 @@ nsXMLHttpRequest::OnChannelRedirect(nsIChannel *aOldChannel, nsresult rv; - rv = CheckChannelForCrossSiteRequest(aNewChannel); - NS_ENSURE_SUCCESS(rv, rv); + if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) { + rv = CheckChannelForCrossSiteRequest(aNewChannel); + NS_ENSURE_SUCCESS(rv, rv); - // Disable redirects for preflighted cross-site requests entirely for now - // Note, do this after the call to CheckChannelForCrossSiteRequest - // to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date - if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) { - return NS_ERROR_DOM_BAD_URI; + // Disable redirects for preflighted cross-site requests entirely for now + // Note, do this after the call to CheckChannelForCrossSiteRequest + // to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date + if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) { + return NS_ERROR_DOM_BAD_URI; + } } if (mChannelEventSink) { diff --git a/content/base/test/Makefile.in b/content/base/test/Makefile.in index ae5cf4c5e079..22bc98bb6502 100644 --- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -246,6 +246,8 @@ _TEST_FILES = test_bug5141.html \ bug457746.sjs \ test_CrossSiteXHR.html \ file_CrossSiteXHR_inner.html \ + file_CrossSiteXHR_inner_data.sjs \ + file_CrossSiteXHR_inner.jar \ file_CrossSiteXHR_server.sjs \ test_CrossSiteXHR_cache.html \ file_CrossSiteXHR_cache_server.sjs \ diff --git a/content/base/test/file_CrossSiteXHR_inner.html b/content/base/test/file_CrossSiteXHR_inner.html index 94738434b25b..597d8f31cbe7 100644 --- a/content/base/test/file_CrossSiteXHR_inner.html +++ b/content/base/test/file_CrossSiteXHR_inner.html @@ -1,8 +1,14 @@ + + \n\ +\n\ +\n\ +Inner page\n\ +\n\ +' + +function handleRequest(request, response) +{ + response.setStatusLine(null, 302, "Follow me"); + response.setHeader("Location", "data:text/html," + escape(data)); + response.setHeader("Content-Type", "text/plain"); + response.write("Follow that guy!"); +} diff --git a/content/base/test/file_CrossSiteXHR_server.sjs b/content/base/test/file_CrossSiteXHR_server.sjs index 88c2afcc84ca..7415af75b19d 100644 --- a/content/base/test/file_CrossSiteXHR_server.sjs +++ b/content/base/test/file_CrossSiteXHR_server.sjs @@ -93,7 +93,13 @@ function handleRequest(request, response) } // Send response - + + if (query.hop) { + query.hop = parseInt(query.hop, 10); + hops = eval(query.hops); + query.allowOrigin = hops[query.hop-1].allowOrigin; + } + if (query.allowOrigin && (!isPreflight || !query.noAllowPreflight)) response.setHeader("Access-Control-Allow-Origin", query.allowOrigin); @@ -109,12 +115,23 @@ function handleRequest(request, response) if (query.allowMethods) response.setHeader("Access-Control-Allow-Methods", query.allowMethods); + } + + if (query.hop && query.hop < hops.length) { + newURL = hops[query.hop].server + + "/tests/content/base/test/file_CrossSiteXHR_server.sjs?" + + "hop=" + (query.hop + 1) + "&hops=" + query.hops; + response.setStatusLine(null, 302, "redirect"); + response.setHeader("Location", newURL); return; } - response.setHeader("Content-Type", "application/xml", false); - response.write("hello pass\n"); + // Send response body + if (!isPreflight) { + response.setHeader("Content-Type", "application/xml", false); + response.write("hello pass\n"); + } } function sendHttp500(response, text) { diff --git a/content/base/test/test_CrossSiteXHR.html b/content/base/test/test_CrossSiteXHR.html index c70222e9b532..714f406efd17 100644 --- a/content/base/test/test_CrossSiteXHR.html +++ b/content/base/test/test_CrossSiteXHR.html @@ -20,18 +20,35 @@ SimpleTest.waitForExplicitFinish(); var origins = - [['http://example.org'], - ['http://example.org:80', 'http://example.org'], - ['http://sub1.test1.example.org'], - ['http://test2.example.org:8000'], + [{ server: 'http://example.org' }, + { server: 'http://example.org:80', + origin: 'http://example.org' + }, + { server: 'http://sub1.test1.example.org' }, + { server: 'http://test2.example.org:8000' }, + { server: 'http://sub1.\xe4lt.example.org:8000', + origin: 'http://sub1.xn--lt-uia.example.org:8000' + }, + { server: 'http://sub2.\xe4lt.example.org', + origin: 'http://sub2.xn--lt-uia.example.org' + }, + { server: 'http://ex\xe4mple.test', + origin: 'http://xn--exmple-cua.test' + }, + { server: 'http://xn--exmple-cua.test' }, + { server: 'http://\u03c0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1.\u03b4\u03bf\u03ba\u03b9\u03bc\u03ae', + origin: 'http://xn--hxajbheg2az3al.xn--jxalpdlp' + }, + { origin: 'http://example.org', + file: 'jar:http://example.org/tests/content/base/test/file_CrossSiteXHR_inner.jar!/file_CrossSiteXHR_inner.html' + }, + { origin: 'null', + file: 'http://example.org/tests/content/base/test/file_CrossSiteXHR_inner_data.sjs' + }, + ]; + //['https://example.com:443'], //['https://sub1.test1.example.com:443'], - ['http://sub1.\xe4lt.example.org:8000', 'http://sub1.xn--lt-uia.example.org:8000'], - ['http://sub2.\xe4lt.example.org', 'http://sub2.xn--lt-uia.example.org'], - ['http://ex\xe4mple.test', 'http://xn--exmple-cua.test'], - ['http://\u03c0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1.\u03b4\u03bf\u03ba\u03b9\u03bc\u03ae', - 'http://xn--hxajbheg2az3al.xn--jxalpdlp'], - ]; window.addEventListener("message", function(e) { gen.send(e.data); @@ -45,14 +62,17 @@ function runTest() { loader.onload = function () { gen.next() }; // Test preflight-less requests - baseURL = "http://localhost:8888/tests/content/base/test/" + - "file_CrossSiteXHR_server.sjs?"; - for each(originPair in origins) { - origin = originPair[1] || originPair[0]; + basePath = "/tests/content/base/test/file_CrossSiteXHR_server.sjs?" + baseURL = "http://localhost:8888" + basePath; + for each(originEntry in origins) { + origin = originEntry.origin || originEntry.server; - loader.src = originPair[0] + "/tests/content/base/test/file_CrossSiteXHR_inner.html"; + loader.src = originEntry.file || + (originEntry.server + "/tests/content/base/test/file_CrossSiteXHR_inner.html"); yield; + var isNullOrigin = origin == "null"; + port = /:\d+/; passTests = [ origin, @@ -67,6 +87,7 @@ function runTest() { : origin + ":1234", port.test(origin) ? origin.replace(port, ":") : origin + ":", + origin + ".", origin + "/", origin + "#", origin + "?", @@ -87,53 +108,51 @@ function runTest() { origin.replace(/\/[^.]+\./, "/"), ]; - for each(method in ["GET", "POST"]) { - var headers = method == "POST" ? - { "Content-Type": "text/plain" } : - null; + if (isNullOrigin) { + passTests = ["*", "\t \t* \t "]; + failTests = failTests.filter(function(v) { return v != origin }); + failTests.push("null"); + } + + for each(allowOrigin in passTests) { + req = { + url: baseURL + + "allowOrigin=" + escape(allowOrigin) + + "&origin=" + escape(origin), + method: "GET", + }; + loaderWindow.postMessage(req.toSource(), isNullOrigin ? "*" : origin); - for each(allowOrigin in passTests) { - req = { - url: baseURL + - "allowOrigin=" + escape(allowOrigin) + - "&origin=" + escape(origin), - method: method, - headers: headers, - }; - loaderWindow.postMessage(req.toSource(), origin); + res = eval(yield); + is(res.didFail, false, "shouldn't have failed"); + is(res.status, 200, "wrong status"); + is(res.responseXML, + "hello pass", + "wrong responseXML in test for " + allowOrigin); + is(res.responseText, "hello pass\n", + "wrong responseText in test for " + allowOrigin); + is(res.events.join(","), + "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load", + "wrong responseText in test for " + allowOrigin); + } - res = eval(yield); - is(res.didFail, false, "shouldn't have failed"); - is(res.status, 200, "wrong status"); - is(res.responseXML, - "hello pass", - "wrong responseXML in test for " + allowOrigin); - is(res.responseText, "hello pass\n", - "wrong responseText in test for " + allowOrigin); - is(res.events.join(","), - "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load", - "wrong responseText in test for " + allowOrigin); - } + for each(allowOrigin in failTests) { + req = { + url: baseURL + "allowOrigin=" + escape(allowOrigin), + method: "GET", + }; + loaderWindow.postMessage(req.toSource(), isNullOrigin ? "*" : origin); - for each(allowOrigin in failTests) { - req = { - url: baseURL + "allowOrigin=" + escape(allowOrigin), - method: method, - headers: headers, - }; - loaderWindow.postMessage(req.toSource(), origin); - - res = eval(yield); - is(res.didFail, true, "should have failed for " + allowOrigin); - is(res.responseText, "", "should have no text for " + allowOrigin); - is(res.status, 0, "should have no status for " + allowOrigin); - is(res.responseXML, null, "should have no XML for " + allowOrigin); - is(res.events.join(","), - "opening,rs1,sending,rs1,loadstart,rs2,rs4,error", - "wrong events in test for " + allowOrigin); - is(res.progressEvents, 0, - "wrong events in test for " + allowOrigin); - } + res = eval(yield); + is(res.didFail, true, "should have failed for " + allowOrigin); + is(res.responseText, "", "should have no text for " + allowOrigin); + is(res.status, 0, "should have no status for " + allowOrigin); + is(res.responseXML, null, "should have no XML for " + allowOrigin); + is(res.events.join(","), + "opening,rs1,sending,rs1,loadstart,rs2,rs4,error", + "wrong events in test for " + allowOrigin); + is(res.progressEvents, 0, + "wrong events in test for " + allowOrigin); } } @@ -143,6 +162,15 @@ function runTest() { yield; passTests = [{ method: "GET", + noAllowPreflight: 1, + }, + { method: "GET", + headers: { "Content-Type": "baz/bin", + "Accept": "foo/bar", + "Accept-Language": "sv-SE" }, + noAllowPreflight: 1, + }, + { method: "GET", headers: { "x-my-header": "myValue" }, allowHeaders: "x-my-header", }, @@ -193,10 +221,14 @@ function runTest() { headers: { "x-my%-header": "myValue" }, allowHeaders: "x-my%-header", }, - { method: "GET", + { method: "HEAD", + noAllowPreflight: 1, + }, + { method: "HEAD", headers: { "Content-Type": "baz/bin", "Accept": "foo/bar", "Accept-Language": "sv-SE" }, + noAllowPreflight: 1, }, { method: "POST", body: "hi there", @@ -318,6 +350,28 @@ function runTest() { "y-my-header": "" }, allowHeaders: "x-my-header", }, + { method: "GET", + headers: { "myheader": "" }, + allowMethods: "myheader", + }, + { method: "HEAD", + headers: { "x-my-header": "myValue" }, + }, + { method: "HEAD", + headers: { "x-my-header": "myValue" }, + allowHeaders: "", + }, + { method: "HEAD", + headers: { "x-my-header": "myValue" }, + allowHeaders: "y-my-header", + }, + { method: "HEAD", + headers: { "x-my-header": "myValue" }, + allowHeaders: "x-my-header y-my-header", + }, + { method: "XXDELETE", + allowHeaders: "XXDELETE", + }, { method: "POST", noAllowPreflight: 1, }, @@ -409,13 +463,24 @@ function runTest() { is(res.didFail, false, "shouldn't have failed in test for " + test.toSource()); is(res.status, 200, "wrong status in test for " + test.toSource()); - is(res.responseXML, "hello pass", - "wrong responseXML in test for " + test.toSource()); - is(res.responseText, "hello pass\n", - "wrong responseText in test for " + test.toSource()); - is(res.events.join(","), - "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load", - "wrong responseText in test for " + test.toSource()); + if (test.method !== "HEAD") { + is(res.responseXML, "hello pass", + "wrong responseXML in test for " + test.toSource()); + is(res.responseText, "hello pass\n", + "wrong responseText in test for " + test.toSource()); + is(res.events.join(","), + "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load", + "wrong responseText in test for " + test.toSource()); + } + else { + is(res.responseXML, null, + "wrong responseXML in test for " + test.toSource()); + is(res.responseText, "", + "wrong responseText in test for " + test.toSource()); + is(res.events.join(","), + "opening,rs1,sending,rs1,loadstart,rs2,rs4,load", + "wrong responseText in test for " + test.toSource()); + } } for each(test in failTests) { @@ -584,6 +649,164 @@ function runTest() { } } + // Test redirects + is(loader.src, "http://example.org/tests/content/base/test/file_CrossSiteXHR_inner.html"); + is(origin, "http://example.org"); + + tests = [{ pass: 1, + method: "GET", + hops: [{ server: "http://example.com", + allowOrigin: origin + }, + ], + }, + { pass: 1, + method: "GET", + hops: [{ server: "http://example.com", + allowOrigin: origin + }, + { server: "http://example.org", + allowOrigin: origin + }, + ], + }, + { pass: 0, + method: "GET", + hops: [{ server: "http://example.com", + allowOrigin: origin + }, + { server: "http://example.org", + }, + ], + }, + { pass: 1, + method: "GET", + hops: [{ server: "http://example.org", + }, + { server: "http://example.org", + }, + { server: "http://example.com", + allowOrigin: origin + }, + ], + }, + { pass: 0, + method: "GET", + hops: [{ server: "http://example.org", + }, + { server: "http://example.org", + }, + { server: "http://example.com", + allowOrigin: origin + }, + { server: "http://example.org", + }, + ], + }, + { pass: 1, + method: "GET", + hops: [{ server: "http://example.com", + allowOrigin: origin + }, + { server: "http://test2.example.org:8000", + allowOrigin: origin + }, + { server: "http://sub2.xn--lt-uia.example.org", + allowOrigin: origin + }, + { server: "http://sub1.test1.example.org", + allowOrigin: origin + }, + ], + }, + { pass: 0, + method: "GET", + hops: [{ server: "http://example.com", + allowOrigin: origin + }, + { server: "http://test2.example.org:8000", + allowOrigin: origin + }, + { server: "http://sub2.xn--lt-uia.example.org", + allowOrigin: "x" + }, + { server: "http://sub1.test1.example.org", + allowOrigin: origin + }, + ], + }, + { pass: 1, + method: "GET", + hops: [{ server: "http://example.com", + allowOrigin: origin + }, + { server: "http://test2.example.org:8000", + allowOrigin: origin + }, + { server: "http://sub2.xn--lt-uia.example.org", + allowOrigin: "*" + }, + { server: "http://sub1.test1.example.org", + allowOrigin: origin + }, + ], + }, + { pass: 0, + method: "GET", + hops: [{ server: "http://example.com", + allowOrigin: origin + }, + { server: "http://test2.example.org:8000", + allowOrigin: origin + }, + { server: "http://sub2.xn--lt-uia.example.org", + allowOrigin: "*" + }, + { server: "http://sub1.test1.example.org", + }, + ], + }, + ]; + + for each(test in tests) { + req = { + url: test.hops[0].server + basePath + "hop=1&hops=" + + escape(test.hops.toSource()), + method: test.method, + }; + + loaderWindow.postMessage(req.toSource(), origin); + + res = eval(yield); + if (test.pass) { + is(res.didFail, false, + "shouldn't have failed in test for " + test.toSource()); + is(res.status, 200, "wrong status in test for " + test.toSource()); + is(res.responseXML, "hello pass", + "wrong responseXML in test for " + test.toSource()); + is(res.responseText, "hello pass\n", + "wrong responseText in test for " + test.toSource()); + is(res.events.join(","), + "opening,rs1,sending,rs1,loadstart,rs2,rs3,rs4,load", + "wrong responseText in test for " + test.toSource()); + } + else { + is(res.didFail, true, + "should have failed in test for " + test.toSource()); + is(res.status, 0, "wrong status in test for " + test.toSource()); + is(res.responseXML, null, + "wrong responseXML in test for " + test.toSource()); + is(res.responseText, "", + "wrong responseText in test for " + test.toSource()); + is(res.events.join(","), + "opening,rs1,sending,rs1,loadstart,rs2,rs4,error", + "wrong events in test for " + test.toSource()); + is(res.progressEvents, 0, + "wrong events in test for " + test.toSource()); + } + } + + SimpleTest.finish(); yield; diff --git a/docshell/test/Makefile.in b/docshell/test/Makefile.in index ee11e68e7c6f..f5d269db6898 100644 --- a/docshell/test/Makefile.in +++ b/docshell/test/Makefile.in @@ -60,7 +60,7 @@ _TEST_FILES = \ bug123696-subframe.html \ test_bug344861.html \ test_bug369814.html \ - bug369814.zip \ + bug369814.zip \ test_bug384014.html \ test_bug387979.html \ test_bug404548.html \ diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index 7fa5efd2f126..863a61376b18 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -5266,26 +5266,25 @@ nsGlobalWindow::PostMessageMoz(const nsAString& aMessage, const nsAString& aOrig nsIPrincipal* callerPrin = callerInnerWin->GetPrincipal(); if (!callerPrin) return NS_OK; + nsCOMPtr callerOuterURI; if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerOuterURI)))) return NS_OK; - if (!callerOuterURI) { + + nsAutoString origin; + if (callerOuterURI) { + // if the principal has a URI, use that to generate the origin + nsContentUtils::GetUTFOrigin(callerPrin, origin); + } + else { + // otherwise use the URI of the document to generate origin nsCOMPtr doc = do_QueryInterface(callerInnerWin->mDocument); if (!doc) return NS_OK; callerOuterURI = doc->GetDocumentURI(); - if (!callerOuterURI) - return NS_OK; + // if the principal has a URI, use that to generate the origin + nsContentUtils::GetUTFOrigin(callerOuterURI, origin); } - nsCOMPtr callerURI = NS_GetInnermostURI(callerOuterURI); - if (!callerURI) - return NS_OK; - const nsCString& empty = EmptyCString(); - nsCOMPtr callerOrigin; - if (NS_FAILED(callerURI->Clone(getter_AddRefs(callerOrigin))) || - NS_FAILED(callerOrigin->SetUserPass(empty))) - return NS_OK; - // Convert the provided origin string into a URI for comparison purposes. // "*" indicates no specific origin is required. @@ -5293,22 +5292,18 @@ nsGlobalWindow::PostMessageMoz(const nsAString& aMessage, const nsAString& aOrig if (!aOrigin.EqualsASCII("*")) { if (NS_FAILED(NS_NewURI(getter_AddRefs(providedOrigin), aOrigin))) return NS_ERROR_DOM_SYNTAX_ERR; - if (NS_FAILED(providedOrigin->SetUserPass(empty)) || - NS_FAILED(providedOrigin->SetPath(empty))) + if (NS_FAILED(providedOrigin->SetUserPass(EmptyCString())) || + NS_FAILED(providedOrigin->SetPath(EmptyCString()))) return NS_OK; } - nsCAutoString origin; - if (NS_FAILED(callerOrigin->GetPrePath(origin))) - return NS_OK; - // Create and asynchronously dispatch a runnable which will handle actual DOM // event creation and dispatch. nsRefPtr event = new PostMessageEvent(nsContentUtils::IsCallerChrome() ? nsnull : callerInnerWin->GetOuterWindowInternal(), - NS_ConvertUTF8toUTF16(origin), + origin, aMessage, this, providedOrigin, diff --git a/netwerk/base/public/nsNetUtil.h b/netwerk/base/public/nsNetUtil.h index a782cae0ef32..5db226153cbb 100644 --- a/netwerk/base/public/nsNetUtil.h +++ b/netwerk/base/public/nsNetUtil.h @@ -96,6 +96,7 @@ #include "nsIMutable.h" #include "nsIPropertyBag2.h" #include "nsIIDNService.h" +#include "nsIChannelEventSink.h" // Helper, to simplify getting the I/O service. inline const nsGetServiceByContractIDWithError @@ -1581,4 +1582,25 @@ NS_SecurityCompareURIs(nsIURI* aSourceURI, return NS_GetRealPort(targetBaseURI) == NS_GetRealPort(sourceBaseURI); } +inline PRBool +NS_IsInternalSameURIRedirect(nsIChannel *aOldChannel, + nsIChannel *aNewChannel, + PRUint32 aFlags) +{ + if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) { + return PR_FALSE; + } + + nsCOMPtr oldURI, newURI; + aOldChannel->GetURI(getter_AddRefs(oldURI)); + aNewChannel->GetURI(getter_AddRefs(newURI)); + + if (!oldURI || !newURI) { + return PR_FALSE; + } + + PRBool res; + return NS_SUCCEEDED(oldURI->Equals(newURI, &res)) && res; +} + #endif // !nsNetUtil_h__ diff --git a/testing/mochitest/server.js b/testing/mochitest/server.js index 74e550a12c74..26a68338a345 100644 --- a/testing/mochitest/server.js +++ b/testing/mochitest/server.js @@ -193,6 +193,7 @@ function createMochitestServer(serverBasePath) server.registerDirectory("/", serverBasePath); server.registerPathHandler("/server/shutdown", serverShutdown); server.registerContentType("sjs", "sjs"); // .sjs == CGI-like functionality + server.registerContentType("jar", "application/x-jar"); server.setIndexHandler(defaultDirHandler); processLocations(server);