Bug 389508: Fix regressions and bugs in the new Cross Site XMLHttpRequest support. r/sr=jst a=roc

git-svn-id: svn://10.0.0.236/trunk@231205 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
jonas%sicking.cc 2007-07-31 00:42:16 +00:00
parent 60a8dfe73b
commit 33c7643cbb
8 changed files with 132 additions and 40 deletions

View File

@ -73,8 +73,12 @@ nsCrossSiteListenerProxy::ForwardRequest(PRBool aFromStop)
}
mHasForwardedRequest = PR_TRUE;
mParser = nsnull;
mParserListener = nsnull;
if (mParser) {
mParser->Terminate();
mParser = nsnull;
mParserListener = nsnull;
}
if (mAcceptState != eAccept) {
mOuterRequest->Cancel(NS_ERROR_DOM_BAD_URI);
@ -290,8 +294,15 @@ nsCrossSiteListenerProxy::OnDataAvailable(nsIRequest* aRequest,
// Hold a local reference to make sure the parser doesn't go away
nsCOMPtr<nsIStreamListener> stackedListener = mParserListener;
return stackedListener->OnDataAvailable(aRequest, aContext, stream, aOffset,
aCount);
rv = stackedListener->OnDataAvailable(aRequest, aContext, stream, aOffset,
aCount);
// When we forward the request we also terminate the parsing which will
// result in an error bubbling up to here. We want to ignore the error
// in that case.
if (mHasForwardedRequest) {
rv = NS_OK;
}
return rv;
}
NS_IMETHODIMP
@ -302,7 +313,11 @@ nsCrossSiteListenerProxy::HandleStartElement(const PRUnichar *aName,
PRUint32 aLineNumber)
{
// We're done processing the prolog.
return ForwardRequest(PR_FALSE);
ForwardRequest(PR_FALSE);
// Block the parser since we don't want to spend more cycles on parsing
// stuff.
return NS_ERROR_HTMLPARSER_BLOCK;
}
NS_IMETHODIMP
@ -463,7 +478,11 @@ nsCrossSiteListenerProxy::WillBuildModel()
mParser->GetDTD(getter_AddRefs(dtd));
NS_ASSERTION(dtd, "missing dtd in WillBuildModel");
if (dtd && !(dtd->GetType() & NS_IPARSER_FLAG_XML)) {
return ForwardRequest(PR_FALSE);
ForwardRequest(PR_FALSE);
// Block the parser since we don't want to spend more cycles on parsing
// stuff.
return NS_ERROR_HTMLPARSER_BLOCK;
}
return NS_OK;

View File

@ -87,6 +87,7 @@
#include "nsDOMError.h"
#include "nsIHTMLDocument.h"
#include "nsWhitespaceTokenizer.h"
#include "nsIMultiPartChannel.h"
#define LOAD_STR "load"
#define ERROR_STR "error"
@ -116,6 +117,7 @@
#define XML_HTTP_REQUEST_MULTIPART (1 << 12) // Internal
#define XML_HTTP_REQUEST_USE_XSITE_AC (1 << 13) // Internal
#define XML_HTTP_REQUEST_NON_GET (1 << 14) // Internal
#define XML_HTTP_REQUEST_GOT_FINAL_STOP (1 << 15) // Internal
#define XML_HTTP_REQUEST_LOADSTATES \
(XML_HTTP_REQUEST_UNINITIALIZED | \
@ -1133,6 +1135,12 @@ nsXMLHttpRequest::GetCurrentHttpChannel()
static PRBool
IsSameOrigin(nsIPrincipal* aPrincipal, nsIChannel* aChannel)
{
if (!aPrincipal) {
// XXX Until we got our principal story straight we have to do this to
// support C++ callers.
return PR_TRUE;
}
nsCOMPtr<nsIURI> codebase;
nsresult rv = aPrincipal->GetURI(getter_AddRefs(codebase));
NS_ENSURE_SUCCESS(rv, rv);
@ -1629,6 +1637,19 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
rv = mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
}
nsCOMPtr<nsIMultiPartChannel> mpChannel = do_QueryInterface(request);
if (mpChannel) {
PRBool last;
rv = mpChannel->GetIsLastPart(&last);
NS_ENSURE_SUCCESS(rv, rv);
if (last) {
mState |= XML_HTTP_REQUEST_GOT_FINAL_STOP;
}
}
else {
mState |= XML_HTTP_REQUEST_GOT_FINAL_STOP;
}
mXMLParserStreamListener = nsnull;
mReadRequest = nsnull;
mContext = nsnull;
@ -1719,13 +1740,13 @@ nsXMLHttpRequest::RequestCompleted()
// Clear listeners here unless we're multipart
ChangeState(XML_HTTP_REQUEST_COMPLETED, PR_TRUE,
!(mState & XML_HTTP_REQUEST_MULTIPART));
!!(mState & XML_HTTP_REQUEST_GOT_FINAL_STOP));
if (NS_SUCCEEDED(rv) && domevent) {
NotifyEventListeners(loadEventListeners, domevent);
}
if (mState & XML_HTTP_REQUEST_MULTIPART) {
if (!(mState & XML_HTTP_REQUEST_GOT_FINAL_STOP)) {
// We're a multipart request, so we're not done. Reset to opened.
ChangeState(XML_HTTP_REQUEST_OPENED);
}
@ -1958,13 +1979,6 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
// Create our listener
nsCOMPtr<nsIStreamListener> listener = this;
if (!(mState & XML_HTTP_REQUEST_XSITEENABLED)) {
// Always create a nsCrossSiteListenerProxy here even if it's
// a same-origin request right now, since it could be redirected.
listener = new nsCrossSiteListenerProxy(listener, mPrincipal);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
}
if (mState & XML_HTTP_REQUEST_MULTIPART) {
listener = new nsMultipartProxyListener(listener);
if (!listener) {
@ -1972,6 +1986,13 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
}
}
if (!(mState & XML_HTTP_REQUEST_XSITEENABLED)) {
// Always create a nsCrossSiteListenerProxy here even if it's
// a same-origin request right now, since it could be redirected.
listener = new nsCrossSiteListenerProxy(listener, mPrincipal);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
}
// Bypass the network cache in cases where it makes no sense:
// 1) Multipart responses are very large and would likely be doomed by the
// cache once they grow too large, so they are not worth caching.
@ -2009,7 +2030,7 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
listener = new nsCrossSiteListenerProxy(acListener, mPrincipal);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
rv = mACGetChannel->AsyncOpen(acListener, nsnull);
rv = mACGetChannel->AsyncOpen(listener, nsnull);
}
else {
// Start reading from the channel
@ -2066,10 +2087,14 @@ nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
}
}
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
if (!httpChannel) // open() initializes mChannel, and open()
if (!mChannel) // open() initializes mChannel, and open()
return NS_ERROR_FAILURE; // must be called before first setRequestHeader()
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
if (!httpChannel) {
return NS_OK;
}
// Prevent modification to certain HTTP headers (see bug 302263), unless
// the executing script has UniversalBrowserWrite permission.

View File

@ -168,6 +168,7 @@ protected:
nsCOMPtr<nsISupports> mContext;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIChannel> mChannel;
// mReadRequest is different from mChannel for multipart requests
nsCOMPtr<nsIRequest> mReadRequest;
nsCOMPtr<nsIDOMDocument> mDocument;
nsCOMPtr<nsIChannel> mACGetChannel;

View File

@ -47,6 +47,8 @@ include $(topsrcdir)/config/rules.mk
_TEST_FILES = test_bug5141.html \
test_bug51034.html \
test_bug218236.html \
file_bug218236_multipart.txt \
file_bug218236_multipart.txt^headers^ \
test_bug218277.html \
test_bug238409.html \
test_bug276037-1.html \

View File

@ -0,0 +1,15 @@
----boundary
Content-Type: text/html
<html><body>File 1</body></html>
------boundary
Content-Type: text/html
<p>File 2</p>
------boundary
Content-Type: text/plain
File 3
--

View File

@ -0,0 +1,2 @@
HTTP 200 OK
Content-Type: multipart/x-mixed-replace;boundary="----boundary"

View File

@ -14,30 +14,48 @@
<pre id="test">
<script class="testbody" type="text/javascript">
var passFiles = ['file_CrossSiteXHR_pass1.xml',
'file_CrossSiteXHR_pass2.xml',
'file_CrossSiteXHR_pass3.xml'];
var local = "http://example.com/tests/content/base/test/";
var post = "http://people.mozilla.com/cgi-bin/sicking/xhr/posttest.pl/";
var failFiles = ['file_CrossSiteXHR_fail1.xml',
'file_CrossSiteXHR_fail2.xml',
'file_CrossSiteXHR_fail3.xml',
'file_CrossSiteXHR_fail4.xml'];
var passFiles = [[local + 'file_CrossSiteXHR_pass1.xml', 'GET'],
[local + 'file_CrossSiteXHR_pass2.xml', 'GET'],
[local + 'file_CrossSiteXHR_pass3.xml', 'GET'],
[post + 'pass1', 'POST'],
[post + 'pass2', 'POST'],
[post + 'pass3', 'POST'],
];
var failFiles = [[local + 'file_CrossSiteXHR_fail1.xml', 'GET'],
[local + 'file_CrossSiteXHR_fail2.xml', 'GET'],
[local + 'file_CrossSiteXHR_fail3.xml', 'GET'],
[local + 'file_CrossSiteXHR_fail4.xml', 'GET'],
[post + 'fail1', 'POST'],
[post + 'fail2', 'POST'],
[post + 'fail3', 'POST'],
[post + 'fail4', 'POST'],
[post + 'fail5', 'POST'],
];
for (i = 0; i < passFiles.length; ++i) {
xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.com/tests/content/base/test/" + passFiles[i], false);
xhr.open(passFiles[i][1], passFiles[i][0], false);
xhr.send(null);
ok(xhr.status == 200, "wrong status");
ok((new XMLSerializer()).serializeToString(xhr.responseXML.documentElement) ==
is(xhr.status, 200, "wrong status");
if (xhr.responseXML) {
is((new XMLSerializer()).serializeToString(xhr.responseXML.documentElement),
"<res>hello</res>",
"wrong response");
"wrong response");
}
else {
is(xhr.responseText, "hello pass\n", "wrong response");
}
}
for (i = 0; i < failFiles.length; ++i) {
xhr = new XMLHttpRequest();
success = false;
try {
xhr.open("GET", "http://example.com/tests/content/base/test/" + failFiles[i], false);
xhr.open(failFiles[i][1], failFiles[i][0], false);
xhr.send(null);
}
catch (e) {
@ -45,8 +63,6 @@ for (i = 0; i < failFiles.length; ++i) {
}
ok(success, "should have thrown");
}
</script>
</pre>
</body>

View File

@ -27,17 +27,21 @@ SimpleTest.waitForExplicitFinish();
var url_200 = window.location.href;
var url_404 = url_200.replace(/[^/]+$/, "this_file_is_not_going_to_be_there.dummy");
var url_connection_error = url_200.replace(/^(\w+:\/\/[^/]+?)(:\d+)?\//, "$1:9546/");
var url_multipart = "file_bug218236_multipart.txt";
// List of tests: name of the test, URL to be requested, expected sequence
// of events and optionally a function to be called from readystatechange handler.
// Numbers in the list of events are values of XMLHttpRequest.readyState
// when readystatechange event is triggered.
var tests = [
["200 OK", url_200, [1, 2, 3, 4, "load"], null],
["404 Not Found", url_404, [1, 2, 3, 4, "load"], null],
["connection error", url_connection_error, [1, 2, 4, "error"], null],
["abort() call on readyState = 1", url_200, [1, 4], doAbort1],
["abort() call on readyState = 2", url_200, [1, 2, 4], doAbort2],
["200 OK", url_200, [1, 2, 3, 4, "load"], 0, null],
["404 Not Found", url_404, [1, 2, 3, 4, "load"], 0, null],
["connection error", url_connection_error, [1, 2, 4, "error"], 0, null],
["abort() call on readyState = 1", url_200, [1, 4], 0, doAbort1],
["abort() call on readyState = 2", url_200, [1, 2, 4], 0, doAbort2],
["multipart document", url_multipart, [1, 2, 3, 4, "load",
1, 2, 3, 4, "load",
1, 2, 3, 4, "load"], 1, null],
];
var testName = null;
@ -45,6 +49,7 @@ var currentState = 0;
var currentSequence = null;
var expectedSequence = null;
var currentCallback = null;
var finalizeTimeoutID = null;
var request = null;
@ -69,6 +74,7 @@ function runNextTest() {
// Prepare request object
request = new XMLHttpRequest();
request.multipart = test[3];
request.open("GET", test[1]);
request.onreadystatechange = onReadyStateChange;
request.onload = onLoad;
@ -79,7 +85,7 @@ function runNextTest() {
currentState = 0;
currentSequence = [];
expectedSequence = test[2];
currentCallback = test[3];
currentCallback = test[4];
// Start request
request.send(null);
@ -89,12 +95,16 @@ function runNextTest() {
}
function finalizeTest() {
ok(compareArrays(expectedSequence, currentSequence), "event sequence for '" + testName + "' should be " + expectedSequence.join(", "));
finalizeTimeoutID = null;
ok(compareArrays(expectedSequence, currentSequence), "event sequence for '" + testName + "' was " + currentSequence.join(", "));
runNextTest();
}
function onReadyStateChange() {
clearTimeout(finalizeTimeoutID);
finalizeTimeoutID = null;
// Ignore duplicated calls for the same ready state
if (request.readyState != currentState) {
currentState = request.readyState;
@ -103,7 +113,9 @@ function onReadyStateChange() {
if (currentState == 4) {
// Allow remaining event to fire but then we are finished with this test
setTimeout(finalizeTest, 0);
// unless we get another onReadyStateChange in which case we'll cancel
// this timeout
finalizeTimeoutID = setTimeout(finalizeTest, 0);
}
if (currentCallback)