From 28a2229ea6c19553c1e702773e5163f3e5b744f6 Mon Sep 17 00:00:00 2001 From: Jonas Sicking Date: Mon, 4 Oct 2010 17:41:07 -0700 Subject: [PATCH] Bug 597301: Update CORS to latest spec. r=jst a=blocker --- content/base/src/nsCrossSiteListenerProxy.cpp | 32 +- content/base/src/nsCrossSiteListenerProxy.h | 8 +- content/base/src/nsXMLHttpRequest.cpp | 47 +- .../base/test/file_CrossSiteXHR_server.sjs | 7 + content/base/test/test_CrossSiteXHR.html | 601 ++++++++++++------ .../html/content/src/nsHTMLMediaElement.cpp | 1 - content/media/nsMediaStream.cpp | 1 - 7 files changed, 437 insertions(+), 260 deletions(-) diff --git a/content/base/src/nsCrossSiteListenerProxy.cpp b/content/base/src/nsCrossSiteListenerProxy.cpp index 7a865bff2b1..470b7627735 100644 --- a/content/base/src/nsCrossSiteListenerProxy.cpp +++ b/content/base/src/nsCrossSiteListenerProxy.cpp @@ -155,7 +155,7 @@ NS_IMETHODIMP nsCrossSiteListenerProxy::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) { - mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest, PR_FALSE)); + mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest)); if (!mRequestApproved) { if (nsXMLHttpRequest::sAccessControlCache) { nsCOMPtr channel = do_QueryInterface(aRequest); @@ -219,8 +219,7 @@ IsValidHTTPToken(const nsCSubstring& aToken) } nsresult -nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest, - PRBool aIsRedirect) +nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest) { // Check if this was actually a cross domain request if (!mHasBeenCrossSite) { @@ -241,21 +240,6 @@ nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest, nsCOMPtr http = do_QueryInterface(aRequest); NS_ENSURE_TRUE(http, 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); - if (!succeeded) { - PRUint32 responseStatus; - rv = http->GetResponseStatus(&responseStatus); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(mAllowedHTTPErrors.Contains(responseStatus), - NS_ERROR_DOM_BAD_URI); - } - } - // Check the Access-Control-Allow-Origin header nsCAutoString allowedOriginHeader; rv = http->GetResponseHeader( @@ -286,13 +270,21 @@ nsCrossSiteListenerProxy::CheckRequestApproved(nsIRequest* aRequest, } if (mIsPreflight) { + PRBool succeeded; + rv = http->GetRequestSucceeded(&succeeded); + NS_ENSURE_SUCCESS(rv, rv); + if (!succeeded) { + return NS_ERROR_DOM_BAD_URI; + } + nsCAutoString headerVal; // The "Access-Control-Allow-Methods" header contains a comma separated // list of method names. http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"), headerVal); PRBool foundMethod = mPreflightMethod.EqualsLiteral("GET") || - mPreflightMethod.EqualsLiteral("POST"); + mPreflightMethod.EqualsLiteral("HEAD") || + mPreflightMethod.EqualsLiteral("POST"); nsCCharSeparatedTokenizer methodTokens(headerVal, ','); while(methodTokens.hasMoreTokens()) { const nsDependentCSubstring& method = methodTokens.nextToken(); @@ -379,7 +371,7 @@ nsCrossSiteListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel, { nsresult rv; if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) { - rv = CheckRequestApproved(aOldChannel, PR_TRUE); + rv = CheckRequestApproved(aOldChannel); if (NS_FAILED(rv)) { if (nsXMLHttpRequest::sAccessControlCache) { nsCOMPtr oldURI; diff --git a/content/base/src/nsCrossSiteListenerProxy.h b/content/base/src/nsCrossSiteListenerProxy.h index d190f38815c..cfce5e06104 100644 --- a/content/base/src/nsCrossSiteListenerProxy.h +++ b/content/base/src/nsCrossSiteListenerProxy.h @@ -84,14 +84,9 @@ public: // Must be called at startup. static void Startup(); - void AllowHTTPResult(PRUint32 aResultCode) - { - mAllowedHTTPErrors.AppendElement(aResultCode); - } - private: nsresult UpdateChannel(nsIChannel* aChannel); - nsresult CheckRequestApproved(nsIRequest* aRequest, PRBool aIsRedirect); + nsresult CheckRequestApproved(nsIRequest* aRequest); nsCOMPtr mOuterListener; nsCOMPtr mRequestingPrincipal; @@ -102,7 +97,6 @@ private: PRBool mIsPreflight; nsCString mPreflightMethod; nsTArray mPreflightHeaders; - nsTArray mAllowedHTTPErrors; nsCOMPtr mRedirectCallback; nsCOMPtr mOldRedirectChannel; nsCOMPtr mNewRedirectChannel; diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index 5539991ef22..aad740972bc 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -1586,8 +1586,6 @@ nsXMLHttpRequest::GetCurrentHttpChannel() nsresult nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel) { - nsresult rv; - // First check if cross-site requests are enabled if ((mState & XML_HTTP_REQUEST_XSITEENABLED)) { return NS_OK; @@ -1611,24 +1609,10 @@ nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel) httpChannel->GetRequestMethod(method); if (!mACUnsafeHeaders.IsEmpty() || HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR)) || - (mUpload && mUpload->HasListeners())) { - mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT; - } - else if (method.LowerCaseEqualsLiteral("post")) { - nsCAutoString contentTypeHeader; - httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), - contentTypeHeader); - - nsCAutoString contentType, charset; - rv = NS_ParseContentType(contentTypeHeader, contentType, charset); - NS_ENSURE_SUCCESS(rv, rv); - - if (!contentType.LowerCaseEqualsLiteral("text/plain")) { - mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT; - } - } - else if (!method.LowerCaseEqualsLiteral("get") && - !method.LowerCaseEqualsLiteral("head")) { + (mUpload && mUpload->HasListeners()) || + (!method.LowerCaseEqualsLiteral("get") && + !method.LowerCaseEqualsLiteral("post") && + !method.LowerCaseEqualsLiteral("head"))) { mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT; } @@ -2488,7 +2472,7 @@ nsXMLHttpRequest::Send(nsIVariant *aBody) // ignore the necessary headers for an empty Content-Type. nsCOMPtr uploadChannel2(do_QueryInterface(httpChannel)); // This assertion will fire if buggy extensions are installed - NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel"); + NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2"); if (uploadChannel2) { uploadChannel2->ExplicitSetUploadStream(postDataStream, contentType, -1, method, PR_FALSE); @@ -2508,6 +2492,23 @@ nsXMLHttpRequest::Send(nsIVariant *aBody) } } + if (httpChannel) { + nsCAutoString contentTypeHeader; + rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), + contentTypeHeader); + if (NS_SUCCEEDED(rv)) { + nsCAutoString contentType, charset; + rv = NS_ParseContentType(contentTypeHeader, contentType, charset); + NS_ENSURE_SUCCESS(rv, rv); + + if (!contentType.LowerCaseEqualsLiteral("text/plain") && + !contentType.LowerCaseEqualsLiteral("application/x-www-form-urlencoded") && + !contentType.LowerCaseEqualsLiteral("multipart/form-data")) { + mACUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type")); + } + } + } + // Reset responseBody mResponseBody.Truncate(); mResponseBodyUnicode.SetIsVoid(PR_TRUE); @@ -2765,8 +2766,10 @@ nsXMLHttpRequest::SetRequestHeader(const nsACString& header, // Check for dangerous cross-site headers PRBool safeHeader = !!(mState & XML_HTTP_REQUEST_XSITEENABLED); if (!safeHeader) { + // Content-Type isn't always safe, but we'll deal with it in Send() const char *kCrossOriginSafeHeaders[] = { - "accept", "accept-language", "content-type" + "accept", "accept-language", "content-language", "content-type", + "last-event-id" }; for (i = 0; i < NS_ARRAY_LENGTH(kCrossOriginSafeHeaders); ++i) { if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) { diff --git a/content/base/test/file_CrossSiteXHR_server.sjs b/content/base/test/file_CrossSiteXHR_server.sjs index c97bffffa52..895965659a6 100644 --- a/content/base/test/file_CrossSiteXHR_server.sjs +++ b/content/base/test/file_CrossSiteXHR_server.sjs @@ -100,6 +100,13 @@ function handleRequest(request, response) query.allowOrigin = hops[query.hop-1].allowOrigin; query.allowHeaders = hops[query.hop-1].allowHeaders; } + + if (!isPreflight && query.status) { + response.setStatusLine(null, query.status, query.statusMessage); + } + if (isPreflight && query.preflightStatus) { + response.setStatusLine(null, query.preflightStatus, "preflight status"); + } if (query.allowOrigin && (!isPreflight || !query.noAllowPreflight)) response.setHeader("Access-Control-Allow-Origin", query.allowOrigin); diff --git a/content/base/test/test_CrossSiteXHR.html b/content/base/test/test_CrossSiteXHR.html index 83ca57ce958..25c25ff05f3 100644 --- a/content/base/test/test_CrossSiteXHR.html +++ b/content/base/test/test_CrossSiteXHR.html @@ -43,299 +43,490 @@ function runTest() { origin = "http://example.org"; yield; - passTests = [{ method: "GET", + tests = [// Plain request + { pass: 1, + method: "GET", noAllowPreflight: 1, }, - { method: "GET", - headers: { "Content-Type": "baz/bin", + + // Default allowed headers + { pass: 1, + method: "GET", + headers: { "Content-Type": "text/plain", "Accept": "foo/bar", "Accept-Language": "sv-SE" }, noAllowPreflight: 1, }, - { method: "GET", + { pass: 0, + method: "GET", + headers: { "Content-Type": "foo/bar", + "Accept": "foo/bar", + "Accept-Language": "sv-SE" }, + noAllowPreflight: 1, + }, + + // Custom headers + { pass: 1, + method: "GET", headers: { "x-my-header": "myValue" }, allowHeaders: "x-my-header", }, - { method: "GET", + { pass: 1, + method: "GET", + headers: { "x-my-header": "myValue" }, + allowHeaders: "X-My-Header", + }, + { pass: 1, + method: "GET", + headers: { "x-my-header": "myValue", + "long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header": "secondValue" }, + allowHeaders: "x-my-header, long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header", + }, + { pass: 1, + method: "GET", + headers: { "x-my%-header": "myValue" }, + allowHeaders: "x-my%-header", + }, + { pass: 0, + method: "GET", + headers: { "x-my-header": "myValue" }, + }, + { pass: 0, + method: "GET", + headers: { "x-my-header": "" }, + }, + { pass: 0, + method: "GET", + headers: { "x-my-header": "myValue" }, + allowHeaders: "", + }, + { pass: 0, + method: "GET", + headers: { "x-my-header": "myValue" }, + allowHeaders: "y-my-header", + }, + { pass: 0, + method: "GET", + headers: { "x-my-header": "myValue" }, + allowHeaders: "x-my-header y-my-header", + }, + { pass: 0, + method: "GET", + headers: { "x-my-header": "myValue" }, + allowHeaders: "x-my-header, y-my-header z", + }, + { pass: 0, + method: "GET", + headers: { "x-my-header": "myValue" }, + allowHeaders: "x-my-header, y-my-he(ader", + }, + { pass: 0, + method: "GET", + headers: { "myheader": "" }, + allowMethods: "myheader", + }, + + // Multiple custom headers + { pass: 1, + method: "GET", headers: { "x-my-header": "myValue", "second-header": "secondValue", "third-header": "thirdValue" }, allowHeaders: "x-my-header, second-header, third-header", }, - { method: "GET", + { pass: 1, + method: "GET", headers: { "x-my-header": "myValue", "second-header": "secondValue", "third-header": "thirdValue" }, allowHeaders: "x-my-header,second-header,third-header", }, - { method: "GET", + { pass: 1, + method: "GET", headers: { "x-my-header": "myValue", "second-header": "secondValue", "third-header": "thirdValue" }, allowHeaders: "x-my-header ,second-header ,third-header", }, - { method: "GET", + { pass: 1, + method: "GET", headers: { "x-my-header": "myValue", "second-header": "secondValue", "third-header": "thirdValue" }, allowHeaders: "x-my-header , second-header , third-header", }, - { method: "GET", + { pass: 1, + method: "GET", headers: { "x-my-header": "myValue", "second-header": "secondValue" }, allowHeaders: ", x-my-header, , ,, second-header, , ", }, - { method: "GET", + { pass: 1, + method: "GET", headers: { "x-my-header": "myValue", "second-header": "secondValue" }, allowHeaders: "x-my-header, second-header, unused-header", }, - { method: "GET", - headers: { "x-my-header": "myValue" }, - allowHeaders: "X-My-Header", - }, - { method: "GET", + { pass: 0, + method: "GET", headers: { "x-my-header": "myValue", - "long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header": "secondValue" }, - allowHeaders: "x-my-header, long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header", + "y-my-header": "secondValue" }, + allowHeaders: "x-my-header", }, - { method: "GET", - headers: { "x-my%-header": "myValue" }, - allowHeaders: "x-my%-header", + { pass: 0, + method: "GET", + headers: { "x-my-header": "", + "y-my-header": "" }, + allowHeaders: "x-my-header", }, - { method: "HEAD", + + // HEAD requests + { pass: 1, + method: "HEAD", noAllowPreflight: 1, }, - { method: "HEAD", - headers: { "Content-Type": "baz/bin", + + // HEAD with safe headers + { pass: 1, + method: "HEAD", + headers: { "Content-Type": "text/plain", "Accept": "foo/bar", "Accept-Language": "sv-SE" }, noAllowPreflight: 1, }, - { method: "POST", + { pass: 0, + method: "HEAD", + headers: { "Content-Type": "foo/bar", + "Accept": "foo/bar", + "Accept-Language": "sv-SE" }, + noAllowPreflight: 1, + }, + + // HEAD with custom headers + { pass: 1, + method: "HEAD", + headers: { "x-my-header": "myValue" }, + allowHeaders: "x-my-header", + }, + { pass: 0, + method: "HEAD", + headers: { "x-my-header": "myValue" }, + }, + { pass: 0, + method: "HEAD", + headers: { "x-my-header": "myValue" }, + allowHeaders: "", + }, + { pass: 0, + method: "HEAD", + headers: { "x-my-header": "myValue" }, + allowHeaders: "y-my-header", + }, + { pass: 0, + method: "HEAD", + headers: { "x-my-header": "myValue" }, + allowHeaders: "x-my-header y-my-header", + }, + + // POST tests + { pass: 1, + method: "POST", body: "hi there", noAllowPreflight: 1, }, - { method: "POST", + { pass: 1, + method: "POST", }, - { method: "POST", + { pass: 1, + method: "POST", + noAllowPreflight: 1, + }, + + // POST with standard headers + { pass: 1, + method: "POST", body: "hi there", headers: { "Content-Type": "text/plain" }, noAllowPreflight: 1, }, - { method: "POST", + { pass: 1, + method: "POST", + body: "hi there", + headers: { "Content-Type": "multipart/form-data" }, + noAllowPreflight: 1, + }, + { pass: 1, + method: "POST", + body: "hi there", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + noAllowPreflight: 1, + }, + { pass: 0, + method: "POST", body: "hi there", headers: { "Content-Type": "foo/bar" }, }, - { method: "POST", + { pass: 0, + method: "POST", + headers: { "Content-Type": "foo/bar" }, + }, + { pass: 1, + method: "POST", body: "hi there", headers: { "Content-Type": "text/plain", "Accept": "foo/bar", "Accept-Language": "sv-SE" }, noAllowPreflight: 1, }, - { method: "POST", + + // POST with custom headers + { pass: 1, + method: "POST", body: "hi there", headers: { "Accept": "foo/bar", "Accept-Language": "sv-SE", "x-my-header": "myValue" }, allowHeaders: "x-my-header", }, - { method: "POST", + { pass: 1, + method: "POST", headers: { "Content-Type": "text/plain", "x-my-header": "myValue" }, allowHeaders: "x-my-header", }, - { method: "POST", + { pass: 1, + method: "POST", + body: "hi there", + headers: { "Content-Type": "text/plain", + "x-my-header": "myValue" }, + allowHeaders: "x-my-header", + }, + { pass: 1, + method: "POST", + body: "hi there", + headers: { "Content-Type": "foo/bar", + "x-my-header": "myValue" }, + allowHeaders: "x-my-header, content-type", + }, + { pass: 0, + method: "POST", + body: "hi there", + headers: { "Content-Type": "foo/bar" }, + noAllowPreflight: 1, + }, + { pass: 0, + method: "POST", body: "hi there", headers: { "Content-Type": "foo/bar", "x-my-header": "myValue" }, allowHeaders: "x-my-header", }, - { method: "POST", + { pass: 1, + method: "POST", headers: { "x-my-header": "myValue" }, allowHeaders: "x-my-header", }, - { method: "POST", + { pass: 1, + method: "POST", body: "hi there", headers: { "x-my-header": "myValue" }, allowHeaders: "x-my-header, $_%", }, - { method: "DELETE", + + // Other methods + { pass: 1, + method: "DELETE", allowMethods: "DELETE", }, - { method: "DELETE", - allowMethods: "POST, PUT, DELETE", - }, - { method: "DELETE", - allowMethods: "POST, DELETE, PUT", - }, - { method: "DELETE", - allowMethods: "DELETE, POST, PUT", - }, - { method: "DELETE", - allowMethods: "POST ,PUT ,DELETE", - }, - { method: "DELETE", - allowMethods: "POST,PUT,DELETE", - }, - { method: "DELETE", - allowMethods: "POST , PUT , DELETE", - }, - { method: "DELETE", - allowMethods: " ,, PUT ,, , , DELETE , ,", - }, - { method: "POST", - body: "hi there", - headers: { "Content-Type": "text/plain" }, - uploadProgress: "uploadprogress", - }, - { method: "POST", - body: "hi there", - headers: { "Content-Type": "text/plain" }, - uploadProgress: "progress", - }, - ]; - failTests = [{ method: "GET", - headers: { "x-my-header": "myValue" }, - }, - { method: "GET", - headers: { "x-my-header": "myValue" }, - allowHeaders: "", - }, - { method: "GET", - headers: { "x-my-header": "myValue" }, - allowHeaders: "y-my-header", - }, - { method: "GET", - headers: { "x-my-header": "myValue" }, - allowHeaders: "x-my-header y-my-header", - }, - { method: "GET", - headers: { "x-my-header": "myValue" }, - allowHeaders: "x-my-header, y-my-header z", - }, - { method: "GET", - headers: { "x-my-header": "myValue" }, - allowHeaders: "x-my-header, y-my-he(ader", - }, - { method: "GET", - headers: { "x-my-header": "myValue", - "y-my-header": "secondValue" }, - allowHeaders: "x-my-header", - }, - { method: "GET", - headers: { "x-my-header": "" }, - }, - { method: "GET", - headers: { "x-my-header": "", - "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: "DELETE", + { pass: 0, + method: "DELETE", allowHeaders: "DELETE", }, - { method: "POST", - noAllowPreflight: 1, + { pass: 0, + method: "DELETE", }, - { method: "POST", - body: "hi there", - headers: { "Content-Type": "foo/bar" }, - noAllowPreflight: 1, - }, - { method: "DELETE", - }, - { method: "DELETE", + { pass: 0, + method: "DELETE", allowMethods: "", }, - { method: "DELETE", + { pass: 1, + method: "DELETE", + allowMethods: "POST, PUT, DELETE", + }, + { pass: 1, + method: "DELETE", + allowMethods: "POST, DELETE, PUT", + }, + { pass: 1, + method: "DELETE", + allowMethods: "DELETE, POST, PUT", + }, + { pass: 1, + method: "DELETE", + allowMethods: "POST ,PUT ,DELETE", + }, + { pass: 1, + method: "DELETE", + allowMethods: "POST,PUT,DELETE", + }, + { pass: 1, + method: "DELETE", + allowMethods: "POST , PUT , DELETE", + }, + { pass: 1, + method: "DELETE", + allowMethods: " ,, PUT ,, , , DELETE , ,", + }, + { pass: 0, + method: "DELETE", allowMethods: "PUT", }, - { method: "DELETE", + { pass: 0, + method: "DELETE", allowMethods: "DELETEZ", }, - { method: "DELETE", + { pass: 0, + method: "DELETE", allowMethods: "DELETE PUT", }, - { method: "DELETE", + { pass: 0, + method: "DELETE", allowMethods: "DELETE, PUT Z", }, - { method: "DELETE", + { pass: 0, + method: "DELETE", allowMethods: "DELETE, PU(T", }, - { method: "DELETE", + { pass: 0, + method: "DELETE", allowMethods: "PUT DELETE", }, - { method: "DELETE", + { pass: 0, + method: "DELETE", allowMethods: "PUT Z, DELETE", }, - { method: "DELETE", + { pass: 0, + method: "DELETE", allowMethods: "PU(T, DELETE", }, - { method: "MYMETHOD", + { pass: 0, + method: "MYMETHOD", allowMethods: "myMethod", }, - { method: "PUT", + { pass: 0, + method: "PUT", allowMethods: "put", }, - { method: "POST", + + // Progress events + { pass: 1, + method: "POST", body: "hi there", headers: { "Content-Type": "text/plain" }, - noAllowPreflight: 1, uploadProgress: "uploadprogress", }, - { method: "POST", + { pass: 1, + method: "POST", body: "hi there", headers: { "Content-Type": "text/plain" }, - noAllowPreflight: 1, uploadProgress: "progress", }, + { pass: 0, + method: "POST", + body: "hi there", + headers: { "Content-Type": "text/plain" }, + uploadProgress: "uploadprogress", + noAllowPreflight: 1, + }, + { pass: 0, + method: "POST", + body: "hi there", + headers: { "Content-Type": "text/plain" }, + uploadProgress: "progress", + noAllowPreflight: 1, + }, + + // Status messages + { pass: 1, + method: "GET", + noAllowPreflight: 1, + status: 404, + statusMessage: "nothin' here", + }, + { pass: 1, + method: "GET", + noAllowPreflight: 1, + status: 401, + statusMessage: "no can do", + }, + { pass: 1, + method: "POST", + body: "hi there", + headers: { "Content-Type": "foo/bar" }, + allowHeaders: "content-type", + status: 500, + statusMessage: "server boo", + }, + { pass: 1, + method: "GET", + noAllowPreflight: 1, + status: 200, + statusMessage: "Yes!!", + }, + { pass: 0, + method: "GET", + headers: { "x-my-header": "header value" }, + allowHeaders: "x-my-header", + preflightStatus: 400 + }, + { pass: 1, + method: "GET", + headers: { "x-my-header": "header value" }, + allowHeaders: "x-my-header", + preflightStatus: 200 + }, + ]; if (!runPreflightTests) { - passTests = failTests = []; + tests = []; } - for each(test in passTests) { - req = { - url: baseURL + "&allowOrigin=" + escape(origin) + - "&origin=" + escape(origin) + - "&requestMethod=" + test.method, + for each(test in tests) { + var req = { + url: baseURL + "allowOrigin=" + escape(origin), method: test.method, headers: test.headers, uploadProgress: test.uploadProgress, body: test.body, }; + if (test.pass) { + req.url += "&origin=" + escape(origin) + + "&requestMethod=" + test.method; + } + if (test.noAllowPreflight) req.url += "&noAllowPreflight"; - if ("headers" in test) { + if (test.pass && "headers" in test) { + function isUnsafeHeader(name) { + lName = name.toLowerCase(); + return lName != "accept" && + lName != "accept-language" && + (lName != "content-type" || + ["text/plain", + "multipart/form-data", + "application/x-www-form-urlencoded"] + .indexOf(test.headers[name].toLowerCase()) == -1); + } req.url += "&headers=" + escape(test.headers.toSource()); reqHeaders = - escape([name for (name in test.headers)].map(String.toLowerCase).filter(function(name) - name != "content-type" && - name != "accept" && - name != "accept-language").sort().join(",")); + escape([name for (name in test.headers)] + .filter(isUnsafeHeader) + .map(String.toLowerCase) + .sort() + .join(",")); req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : ""; } if ("allowHeaders" in test) @@ -344,71 +535,63 @@ function runTest() { req.url += "&allowMethods=" + escape(test.allowMethods); if (test.body) req.url += "&body=" + escape(test.body); - + if (test.status) { + req.url += "&status=" + test.status; + req.url += "&statusMessage=" + escape(test.statusMessage); + } + if (test.preflightStatus) + req.url += "&preflightStatus=" + test.preflightStatus; loaderWindow.postMessage(req.toSource(), origin); - res = eval(yield); - 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.statusText, "OK", "wrong status text 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()); + + if (test.pass) { + is(res.didFail, false, + "shouldn't have failed in test for " + test.toSource()); + if (test.status) { + is(res.status, test.status, "wrong status in test for " + test.toSource()); + is(res.statusText, test.statusMessage, "wrong status text for " + test.toSource()); + } + else { + is(res.status, 200, "wrong status in test for " + test.toSource()); + is(res.statusText, "OK", "wrong status text 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()); + } } 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.statusText, undefined, "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,load", - "wrong responseText in test for " + test.toSource()); + "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()); } } - for each(test in failTests) { - req = { - url: baseURL + "allowOrigin=" + escape(origin), - method: test.method, - headers: test.headers, - uploadProgress: test.uploadProgress, - body: test.body, - }; - - if (test.noAllowPreflight) - req.url += "&noAllowPreflight"; - - if ("allowHeaders" in test) - req.url += "&allowHeaders=" + escape(test.allowHeaders); - if ("allowMethods" in test) - req.url += "&allowMethods=" + escape(test.allowMethods); - - loaderWindow.postMessage(req.toSource(), origin); - - res = eval(yield); - 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.statusText, undefined, "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()); - } - - // Test cookie behavior tests = [{ pass: 1, method: "GET", diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index 76c4f582125..df00c38b307 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -973,7 +973,6 @@ nsresult nsHTMLMediaElement::LoadResource(nsIURI* aURI) listener = crossSiteListener; NS_ENSURE_TRUE(crossSiteListener, NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_SUCCESS(rv, rv); - crossSiteListener->AllowHTTPResult(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE); } else { rv = nsContentUtils::GetSecurityManager()-> CheckLoadURIWithPrincipal(NodePrincipal(), diff --git a/content/media/nsMediaStream.cpp b/content/media/nsMediaStream.cpp index d43f5a33650..f159a95f651 100644 --- a/content/media/nsMediaStream.cpp +++ b/content/media/nsMediaStream.cpp @@ -449,7 +449,6 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener) listener = crossSiteListener; NS_ENSURE_TRUE(crossSiteListener, NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_SUCCESS(rv, rv); - crossSiteListener->AllowHTTPResult(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE); } else { nsresult rv = nsContentUtils::GetSecurityManager()-> CheckLoadURIWithPrincipal(element->NodePrincipal(),