Bug 464954: Update Access-Control implementation to latest draft and fix some bugs. r/sr=bz

This commit is contained in:
Jonas Sicking 2009-01-21 14:50:28 -08:00
parent 49bde88ff3
commit e7e7213705
14 changed files with 623 additions and 150 deletions

View File

@ -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();

View File

@ -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<nsIURI> 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<nsIURI> 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<nsIURI> 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<nsIURI> 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)
{

View File

@ -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<nsIURI> 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<nsIHttpChannel> 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<nsCString> 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<nsIChannelEventSink> 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<nsIHttpChannel> http = do_QueryInterface(aChannel);

View File

@ -77,7 +77,7 @@ public:
private:
nsresult UpdateChannel(nsIChannel* aChannel);
nsresult CheckRequestApproved(nsIRequest* aRequest);
nsresult CheckRequestApproved(nsIRequest* aRequest, PRBool aIsRedirect);
nsCOMPtr<nsIStreamListener> mOuterListener;
nsCOMPtr<nsIPrincipal> mRequestingPrincipal;

View File

@ -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) {

View File

@ -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.zip \
file_CrossSiteXHR_server.sjs \
test_CrossSiteXHR_cache.html \
file_CrossSiteXHR_cache_server.sjs \

View File

@ -1,8 +1,14 @@
<!DOCTYPE HTML>
<!--
NOTE! The content of this file is duplicated in file_CrossSiteXHR_inner.zip
and file_CrossSiteXHR_inner_data.sjs
Please update those files if you update this one.
-->
<html>
<head>
<script>
window.addEventListener('message', function(e) {
window.addEventListener("message", function(e) {
sendData = null;

Binary file not shown.

View File

@ -0,0 +1,92 @@
var data = '<!DOCTYPE HTML>\n\
<html>\n\
<head>\n\
<script>\n\
window.addEventListener("message", function(e) {\n\
\n\
sendData = null;\n\
\n\
req = eval(e.data);\n\
var res = {\n\
didFail: false,\n\
events: [],\n\
progressEvents: 0\n\
};\n\
\n\
var xhr = new XMLHttpRequest();\n\
for each(type in ["load", "abort", "error", "loadstart"]) {\n\
xhr.addEventListener(type, function(e) {\n\
res.events.push(e.type);\n\
}, false);\n\
}\n\
xhr.addEventListener("readystatechange", function(e) {\n\
res.events.push("rs" + xhr.readyState);\n\
}, false);\n\
xhr.addEventListener("progress", function(e) {\n\
res.progressEvents++;\n\
}, false);\n\
if (req.uploadProgress) {\n\
if (req.uploadProgress == "uploadProgress") {\n\
xhr.addEventListener("uploadProgress", function(e) {\n\
res.progressEvents++;\n\
}, false);\n\
}\n\
else {\n\
xhr.upload.addEventListener(req.uploadProgress, function(e) {\n\
res.progressEvents++;\n\
}, false);\n\
}\n\
}\n\
xhr.onload = function () {\n\
res.status = xhr.status;\n\
res.responseXML = xhr.responseXML ?\n\
(new XMLSerializer()).serializeToString(xhr.responseXML) :\n\
null;\n\
res.responseText = xhr.responseText;\n\
post(e, res);\n\
};\n\
xhr.onerror = function () {\n\
res.didFail = true;\n\
res.status = xhr.status;\n\
res.responseXML = xhr.responseXML ?\n\
(new XMLSerializer()).serializeToString(xhr.responseXML) :\n\
null;\n\
res.responseText = xhr.responseText;\n\
post(e, res);\n\
}\n\
\n\
if (req.withCred)\n\
xhr.withCredentials = true;\n\
if (req.body)\n\
sendData = req.body;\n\
\n\
res.events.push("opening");\n\
xhr.open(req.method, req.url, true);\n\
\n\
for (header in req.headers) {\n\
xhr.setRequestHeader(header, req.headers[header]);\n\
}\n\
\n\
res.events.push("sending");\n\
xhr.send(sendData);\n\
\n\
}, false);\n\
\n\
function post(e, res) {\n\
e.source.postMessage(res.toSource(), "*");\n\
}\n\
\n\
</script>\n\
</head>\n\
<body>\n\
Inner page\n\
</body>\n\
</html>'
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!");
}

View File

@ -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("<res>hello pass</res>\n");
// Send response body
if (!isPreflight) {
response.setHeader("Content-Type", "application/xml", false);
response.write("<res>hello pass</res>\n");
}
}
function sendHttp500(response, text) {

View File

@ -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.zip!/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,
"<res>hello pass</res>",
"wrong responseXML in test for " + allowOrigin);
is(res.responseText, "<res>hello pass</res>\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,
"<res>hello pass</res>",
"wrong responseXML in test for " + allowOrigin);
is(res.responseText, "<res>hello pass</res>\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, "<res>hello pass</res>",
"wrong responseXML in test for " + test.toSource());
is(res.responseText, "<res>hello pass</res>\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, "<res>hello pass</res>",
"wrong responseXML in test for " + test.toSource());
is(res.responseText, "<res>hello pass</res>\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, "<res>hello pass</res>",
"wrong responseXML in test for " + test.toSource());
is(res.responseText, "<res>hello pass</res>\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;

View File

@ -5266,26 +5266,25 @@ nsGlobalWindow::PostMessageMoz(const nsAString& aMessage, const nsAString& aOrig
nsIPrincipal* callerPrin = callerInnerWin->GetPrincipal();
if (!callerPrin)
return NS_OK;
nsCOMPtr<nsIURI> 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<nsIDocument> 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<nsIURI> callerURI = NS_GetInnermostURI(callerOuterURI);
if (!callerURI)
return NS_OK;
const nsCString& empty = EmptyCString();
nsCOMPtr<nsIURI> 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<PostMessageEvent> event =
new PostMessageEvent(nsContentUtils::IsCallerChrome()
? nsnull
: callerInnerWin->GetOuterWindowInternal(),
NS_ConvertUTF8toUTF16(origin),
origin,
aMessage,
this,
providedOrigin,

View File

@ -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<nsIURI> 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__

View File

@ -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("zip", "application/x-jar");
server.setIndexHandler(defaultDirHandler);
processLocations(server);