Bug 649133: Implement xhr.mozResponse and xhr.mozResponseType. r=sicking

This commit is contained in:
Masatoshi Kimura 2011-05-10 16:18:55 -07:00
Родитель b2d96a404e
Коммит 1fc43babc2
9 изменённых файлов: 439 добавлений и 87 удалений

Просмотреть файл

@ -46,6 +46,7 @@ interface nsIURI;
interface nsIVariant;
interface nsPIDOMWindow;
interface nsIInputStream;
interface nsIDOMBlob;
%{C++
// for jsval
@ -109,7 +110,7 @@ interface nsIXMLHttpRequestUpload : nsIXMLHttpRequestEventTarget {
* you're aware of all the security implications. And then think twice about
* it.
*/
[scriptable, uuid(af62a870-820c-4981-96a3-28ab17b779e1)]
[scriptable, uuid(a05a6424-a644-461a-a1ff-c7bbe96ea9e2)]
interface nsIXMLHttpRequest : nsISupports
{
/**
@ -141,11 +142,20 @@ interface nsIXMLHttpRequest : nsISupports
readonly attribute AString responseText;
/**
* The response to the request as a typed array ArrayBuffer.
* Determine a response format which response attribute returns.
* empty string (initial value) or "text": as text.
* "arraybuffer": as a typed array ArrayBuffer.
* "blob": as a File API Blob.
* "document": as a DOM Document object.
*/
attribute AString mozResponseType;
/**
* The response to the request as a specified format by responseType.
* NULL if the request is unsuccessful or
* has not yet been sent.
*/
readonly attribute jsval /*ArrayBuffer*/ mozResponseArrayBuffer;
[implicit_jscontext] readonly attribute jsval /* any */ mozResponse;
/**
* The status of the response to the request for HTTP requests.
@ -272,13 +282,19 @@ interface nsIXMLHttpRequest : nsISupports
* The state of the request.
*
* Possible values:
* 0 UNINITIALIZED open() has not been called yet.
* 1 LOADING send() has not been called yet.
* 2 LOADED send() has been called, headers and status are available.
* 3 INTERACTIVE Downloading, responseText holds the partial data.
* 4 COMPLETED Finished with all operations.
* 0 UNSENT open() has not been called yet.
* 1 OPENED send() has not been called yet.
* 2 HEADERS_RECEIVED
* send() has been called, headers and status are available.
* 3 LOADING Downloading, responseText holds the partial data.
* 4 DONE Finished with all operations.
*/
readonly attribute long readyState;
const unsigned short UNSENT = 0;
const unsigned short OPENED = 1;
const unsigned short HEADERS_RECEIVED = 2;
const unsigned short LOADING = 3;
const unsigned short DONE = 4;
readonly attribute unsigned short readyState;
/**
* Override the mime type returned by the server (if any). This may

Просмотреть файл

@ -70,6 +70,8 @@ EXPORTS_mozilla/dom = \
Link.h \
$(NULL)
LOCAL_INCLUDES = -I$(srcdir)/js/src/xpconnect/src
CPPSRCS = \
mozSanitizingSerializer.cpp \
nsAtomListUtils.cpp \

Просмотреть файл

@ -67,7 +67,7 @@
#include "nsIMIMEService.h"
#include "nsCExternalHandlerService.h"
#include "nsIVariant.h"
#include "nsVariant.h"
#include "xpcprivate.h"
#include "nsIParser.h"
#include "nsLoadListenerProxy.h"
#include "nsStringStream.h"
@ -100,6 +100,8 @@
#include "nsAsyncRedirectVerifyHelper.h"
#include "jstypedarray.h"
#include "nsStringBuffer.h"
#include "nsDOMFile.h"
#include "nsIFileChannel.h"
#define LOAD_STR "load"
#define ERROR_STR "error"
@ -113,13 +115,13 @@
// CIDs
// State
#define XML_HTTP_REQUEST_UNINITIALIZED (1 << 0) // 0
#define XML_HTTP_REQUEST_OPENED (1 << 1) // 1 aka LOADING
#define XML_HTTP_REQUEST_LOADED (1 << 2) // 2
#define XML_HTTP_REQUEST_INTERACTIVE (1 << 3) // 3
#define XML_HTTP_REQUEST_COMPLETED (1 << 4) // 4
#define XML_HTTP_REQUEST_SENT (1 << 5) // Internal, LOADING in IE and external view
#define XML_HTTP_REQUEST_STOPPED (1 << 6) // Internal, INTERACTIVE in IE and external view
#define XML_HTTP_REQUEST_UNSENT (1 << 0) // 0 UNSENT
#define XML_HTTP_REQUEST_OPENED (1 << 1) // 1 OPENED
#define XML_HTTP_REQUEST_HEADERS_RECEIVED (1 << 2) // 2 HEADERS_RECEIVED
#define XML_HTTP_REQUEST_LOADING (1 << 3) // 3 LOADING
#define XML_HTTP_REQUEST_DONE (1 << 4) // 4 DONE
#define XML_HTTP_REQUEST_SENT (1 << 5) // Internal, OPENED in IE and external view
#define XML_HTTP_REQUEST_STOPPED (1 << 6) // Internal, LOADING in IE and external view
// The above states are mutually exclusive, change with ChangeState() only.
// The states below can be combined.
#define XML_HTTP_REQUEST_ABORTED (1 << 7) // Internal
@ -137,11 +139,11 @@
#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 17) // Internal
#define XML_HTTP_REQUEST_LOADSTATES \
(XML_HTTP_REQUEST_UNINITIALIZED | \
(XML_HTTP_REQUEST_UNSENT | \
XML_HTTP_REQUEST_OPENED | \
XML_HTTP_REQUEST_LOADED | \
XML_HTTP_REQUEST_INTERACTIVE | \
XML_HTTP_REQUEST_COMPLETED | \
XML_HTTP_REQUEST_HEADERS_RECEIVED | \
XML_HTTP_REQUEST_LOADING | \
XML_HTTP_REQUEST_DONE | \
XML_HTTP_REQUEST_SENT | \
XML_HTTP_REQUEST_STOPPED)
@ -417,7 +419,8 @@ NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
/////////////////////////////////////////////
nsXMLHttpRequest::nsXMLHttpRequest()
: mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNINITIALIZED),
: mResponseType(XML_HTTP_RESPONSE_TYPE_DEFAULT),
mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNSENT),
mUploadTransferred(0), mUploadTotal(0), mUploadComplete(PR_TRUE),
mUploadProgress(0), mUploadProgressMax(0),
mErrorLoad(PR_FALSE), mTimerIsActive(PR_FALSE),
@ -437,7 +440,7 @@ nsXMLHttpRequest::~nsXMLHttpRequest()
if (mState & (XML_HTTP_REQUEST_STOPPED |
XML_HTTP_REQUEST_SENT |
XML_HTTP_REQUEST_INTERACTIVE)) {
XML_HTTP_REQUEST_LOADING)) {
Abort();
}
@ -661,7 +664,11 @@ nsXMLHttpRequest::GetResponseXML(nsIDOMDocument **aResponseXML)
{
NS_ENSURE_ARG_POINTER(aResponseXML);
*aResponseXML = nsnull;
if ((XML_HTTP_REQUEST_COMPLETED & mState) && mResponseXML) {
if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
mResponseType != XML_HTTP_RESPONSE_TYPE_DOCUMENT) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if ((XML_HTTP_REQUEST_DONE & mState) && mResponseXML) {
*aResponseXML = mResponseXML;
NS_ADDREF(*aResponseXML);
}
@ -819,23 +826,27 @@ NS_IMETHODIMP nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
aResponseText.Truncate();
if (mState & (XML_HTTP_REQUEST_COMPLETED |
XML_HTTP_REQUEST_INTERACTIVE)) {
if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
mResponseType != XML_HTTP_RESPONSE_TYPE_TEXT) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (mState & (XML_HTTP_REQUEST_DONE |
XML_HTTP_REQUEST_LOADING)) {
rv = ConvertBodyToText(aResponseText);
}
return rv;
}
/* readonly attribute jsval (ArrayBuffer) mozResponseArrayBuffer; */
NS_IMETHODIMP nsXMLHttpRequest::GetMozResponseArrayBuffer(jsval *aResult)
nsresult nsXMLHttpRequest::GetResponseArrayBuffer(jsval *aResult)
{
JSContext *cx = nsContentUtils::GetCurrentJSContext();
if (!cx)
return NS_ERROR_FAILURE;
if (!(mState & (XML_HTTP_REQUEST_COMPLETED |
XML_HTTP_REQUEST_INTERACTIVE))) {
if (!(mState & (XML_HTTP_REQUEST_DONE |
XML_HTTP_REQUEST_LOADING))) {
*aResult = JSVAL_NULL;
return NS_OK;
}
@ -856,6 +867,126 @@ NS_IMETHODIMP nsXMLHttpRequest::GetMozResponseArrayBuffer(jsval *aResult)
return NS_OK;
}
/* attribute AString responseType; */
NS_IMETHODIMP nsXMLHttpRequest::GetMozResponseType(nsAString& aResponseType)
{
switch (mResponseType) {
case XML_HTTP_RESPONSE_TYPE_DEFAULT:
aResponseType.Truncate();
break;
case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
aResponseType.AssignLiteral("arraybuffer");
break;
case XML_HTTP_RESPONSE_TYPE_BLOB:
aResponseType.AssignLiteral("blob");
break;
case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
aResponseType.AssignLiteral("document");
break;
case XML_HTTP_RESPONSE_TYPE_TEXT:
aResponseType.AssignLiteral("text");
break;
default:
NS_ERROR("Should not happen");
}
return NS_OK;
}
/* attribute AString responseType; */
NS_IMETHODIMP nsXMLHttpRequest::SetMozResponseType(const nsAString& aResponseType)
{
// If the state is not OPENED or HEADERS_RECEIVED raise an
// INVALID_STATE_ERR exception and terminate these steps.
if (!(mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT |
XML_HTTP_REQUEST_HEADERS_RECEIVED)))
return NS_ERROR_DOM_INVALID_STATE_ERR;
// Set the responseType attribute's value to the given value.
if (aResponseType.IsEmpty()) {
mResponseType = XML_HTTP_RESPONSE_TYPE_DEFAULT;
} else if (aResponseType.EqualsLiteral("arraybuffer")) {
mResponseType = XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER;
} else if (aResponseType.EqualsLiteral("blob")) {
mResponseType = XML_HTTP_RESPONSE_TYPE_BLOB;
} else if (aResponseType.EqualsLiteral("document")) {
mResponseType = XML_HTTP_RESPONSE_TYPE_DOCUMENT;
} else if (aResponseType.EqualsLiteral("text")) {
mResponseType = XML_HTTP_RESPONSE_TYPE_TEXT;
}
// If the given value is not the empty string, "arraybuffer",
// "blob", "document", or "text" terminate these steps.
// If the state is OPENED, SetCacheAsFile would have no effect here
// because the channel hasn't initialized the cache entry yet.
// SetCacheAsFile will be called from OnStartRequest.
// If the state is HEADERS_RECEIVED, however, we need to call
// it immediately because OnStartRequest is already dispatched.
if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel));
if (cc) {
cc->SetCacheAsFile(mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB);
}
}
return NS_OK;
}
/* readonly attribute jsval response; */
NS_IMETHODIMP nsXMLHttpRequest::GetMozResponse(JSContext *aCx, jsval *aResult)
{
nsresult rv = NS_OK;
switch (mResponseType) {
case XML_HTTP_RESPONSE_TYPE_DEFAULT:
case XML_HTTP_RESPONSE_TYPE_TEXT:
{
nsString str;
rv = GetResponseText(str);
if (NS_FAILED(rv)) return rv;
nsStringBuffer* buf;
*aResult = XPCStringConvert::ReadableToJSVal(aCx, str, &buf);
if (buf) {
str.ForgetSharedBuffer();
}
}
break;
case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
if (mState & XML_HTTP_REQUEST_DONE) {
rv = GetResponseArrayBuffer(aResult);
} else {
*aResult = JSVAL_NULL;
}
break;
case XML_HTTP_RESPONSE_TYPE_BLOB:
if (mState & XML_HTTP_REQUEST_DONE && mResponseBlob) {
JSObject* scope = JS_GetScopeChain(aCx);
rv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, aResult,
nsnull, PR_TRUE);
} else {
*aResult = JSVAL_NULL;
}
break;
case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
if (mState & XML_HTTP_REQUEST_DONE && mResponseXML) {
JSObject* scope = JS_GetScopeChain(aCx);
rv = nsContentUtils::WrapNative(aCx, scope, mResponseXML, aResult,
nsnull, PR_TRUE);
} else {
*aResult = JSVAL_NULL;
}
break;
default:
NS_ERROR("Should not happen");
}
return rv;
}
/* readonly attribute unsigned long status; */
NS_IMETHODIMP
nsXMLHttpRequest::GetStatus(PRUint32 *aStatus)
@ -882,9 +1013,9 @@ nsXMLHttpRequest::GetStatus(PRUint32 *aStatus)
// Someone's calling this before we got a response... Check our
// ReadyState. If we're at 3 or 4, then this means the connection
// errored before we got any data; return 0 in that case.
PRInt32 readyState;
PRUint16 readyState;
GetReadyState(&readyState);
if (readyState >= 3) {
if (readyState >= LOADING) {
*aStatus = 0;
return NS_OK;
}
@ -940,12 +1071,13 @@ nsXMLHttpRequest::Abort()
PRUint32 responseLength = mResponseBody.Length();
mResponseBody.Truncate();
mResponseBodyUnicode.SetIsVoid(PR_TRUE);
mResponseBlob = nsnull;
mState |= XML_HTTP_REQUEST_ABORTED;
if (!(mState & (XML_HTTP_REQUEST_UNINITIALIZED |
if (!(mState & (XML_HTTP_REQUEST_UNSENT |
XML_HTTP_REQUEST_OPENED |
XML_HTTP_REQUEST_COMPLETED))) {
ChangeState(XML_HTTP_REQUEST_COMPLETED, PR_TRUE);
XML_HTTP_REQUEST_DONE))) {
ChangeState(XML_HTTP_REQUEST_DONE, PR_TRUE);
}
if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) {
@ -963,7 +1095,7 @@ nsXMLHttpRequest::Abort()
// if they load a new url will cause nsXMLHttpRequest::Open to clear
// the abort state bit. If this occurs we're not uninitialized (bug 361773).
if (mState & XML_HTTP_REQUEST_ABORTED) {
ChangeState(XML_HTTP_REQUEST_UNINITIALIZED, PR_FALSE); // IE seems to do it
ChangeState(XML_HTTP_REQUEST_UNSENT, PR_FALSE); // IE seems to do it
}
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
@ -1263,8 +1395,8 @@ nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
PRBool authp = PR_FALSE;
if (mState & (XML_HTTP_REQUEST_OPENED |
XML_HTTP_REQUEST_LOADED |
XML_HTTP_REQUEST_INTERACTIVE |
XML_HTTP_REQUEST_HEADERS_RECEIVED |
XML_HTTP_REQUEST_LOADING |
XML_HTTP_REQUEST_SENT |
XML_HTTP_REQUEST_STOPPED)) {
// IE aborts as well
@ -1395,9 +1527,22 @@ nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
return NS_ERROR_FAILURE;
}
// Copy for our own use
xmlHttpRequest->mResponseBody.Append(fromRawSegment,count);
xmlHttpRequest->mResponseBodyUnicode.SetIsVoid(PR_TRUE);
if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB &&
xmlHttpRequest->mResponseBlob) {
xmlHttpRequest->ChangeState(XML_HTTP_REQUEST_LOADING);
*writeCount = count;
return NS_OK;
}
if (xmlHttpRequest->mResponseType != XML_HTTP_RESPONSE_TYPE_DOCUMENT) {
// Copy for our own use
PRUint32 previousLength = xmlHttpRequest->mResponseBody.Length();
xmlHttpRequest->mResponseBody.Append(fromRawSegment,count);
if (count > 0 && xmlHttpRequest->mResponseBody.Length() == previousLength) {
return NS_ERROR_OUT_OF_MEMORY;
}
xmlHttpRequest->mResponseBodyUnicode.SetIsVoid(PR_TRUE);
}
nsresult rv = NS_OK;
@ -1425,7 +1570,7 @@ nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
}
}
xmlHttpRequest->ChangeState(XML_HTTP_REQUEST_INTERACTIVE);
xmlHttpRequest->ChangeState(XML_HTTP_REQUEST_LOADING);
if (NS_SUCCEEDED(rv)) {
*writeCount = count;
@ -1436,6 +1581,35 @@ nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
return rv;
}
void nsXMLHttpRequest::CreateResponseBlob(nsIRequest *request)
{
nsCOMPtr<nsIFile> file;
nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(request));
if (cc) {
cc->GetCacheFile(getter_AddRefs(file));
if (!file) {
// cacheAsFile returns false if caching is inhibited
PRBool cacheAsFile = PR_FALSE;
if (NS_SUCCEEDED(cc->GetCacheAsFile(&cacheAsFile)) && cacheAsFile) {
}
}
} else {
nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(request);
if (fc) {
fc->GetFile(getter_AddRefs(file));
}
}
if (file) {
nsCAutoString contentType;
mChannel->GetContentType(contentType);
mResponseBlob = new nsDOMFile(file,
NS_ConvertASCIItoUTF16(contentType));
mResponseBody.Truncate();
mResponseBodyUnicode.SetIsVoid(PR_TRUE);
}
}
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
NS_IMETHODIMP
nsXMLHttpRequest::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
@ -1444,6 +1618,10 @@ nsXMLHttpRequest::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInp
NS_ABORT_IF_FALSE(mContext.get() == ctxt,"start context different from OnDataAvailable context");
if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB && !mResponseBlob) {
CreateResponseBlob(request);
}
PRUint32 totalRead;
return inStr->ReadSegments(nsXMLHttpRequest::StreamReaderFunc, (void*)this, count, &totalRead);
}
@ -1478,7 +1656,7 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
}
// Don't do anything if we have been aborted
if (mState & XML_HTTP_REQUEST_UNINITIALIZED)
if (mState & XML_HTTP_REQUEST_UNSENT)
return NS_OK;
if (mState & XML_HTTP_REQUEST_ABORTED) {
@ -1509,7 +1687,14 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
mContext = ctxt;
mState |= XML_HTTP_REQUEST_PARSEBODY;
mState &= ~XML_HTTP_REQUEST_MPART_HEADERS;
ChangeState(XML_HTTP_REQUEST_LOADED);
ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB) {
nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel));
if (cc) {
cc->SetCacheAsFile(PR_TRUE);
}
}
nsresult status;
request->GetStatus(&status);
@ -1525,11 +1710,13 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
// Reset responseBody
mResponseBody.Truncate();
mResponseBodyUnicode.SetIsVoid(PR_TRUE);
mResponseBlob = nsnull;
// Set up responseXML
PRBool parseBody = PR_TRUE;
PRBool parseBody = mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT ||
mResponseType == XML_HTTP_RESPONSE_TYPE_DOCUMENT;
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
if (httpChannel) {
if (parseBody && httpChannel) {
nsCAutoString method;
httpChannel->GetRequestMethod(method);
parseBody = !method.EqualsLiteral("HEAD");
@ -1668,7 +1855,7 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
// make sure to notify the listener if we were aborted
// XXX in fact, why don't we do the cleanup below in this case??
if (mState & XML_HTTP_REQUEST_UNINITIALIZED) {
if (mState & XML_HTTP_REQUEST_UNSENT) {
if (mXMLParserStreamListener)
(void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
return NS_OK;
@ -1690,6 +1877,31 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
if (NS_SUCCEEDED(status) && mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB) {
if (!mResponseBlob) {
CreateResponseBlob(request);
}
if (!mResponseBlob) {
// Smaller files may be written in cache map instead of separate files.
// Also, no-store response cannot be written in persistent cache.
nsCAutoString contentType;
mChannel->GetContentType(contentType);
// XXX We should change mResponseBody to be a raw malloc'ed buffer
// to avoid copying the data.
PRUint32 blobLen = mResponseBody.Length();
void *blobData = PR_Malloc(blobLen);
if (blobData) {
memcpy(blobData, mResponseBody.BeginReading(), blobLen);
mResponseBlob =
new nsDOMMemoryFile(blobData, blobLen, EmptyString(),
NS_ConvertASCIItoUTF16(contentType));
mResponseBody.Truncate();
}
NS_ASSERTION(mResponseBodyUnicode.IsVoid(),
"mResponseBodyUnicode should be empty");
}
}
channel->SetNotificationCallbacks(nsnull);
mNotificationCallbacks = nsnull;
mChannelEventSink = nsnull;
@ -1736,8 +1948,8 @@ nsXMLHttpRequest::RequestCompleted()
// If we're uninitialized at this point, we encountered an error
// earlier and listeners have already been notified. Also we do
// not want to do this if we already completed.
if (mState & (XML_HTTP_REQUEST_UNINITIALIZED |
XML_HTTP_REQUEST_COMPLETED)) {
if (mState & (XML_HTTP_REQUEST_UNSENT |
XML_HTTP_REQUEST_DONE)) {
return NS_OK;
}
@ -1753,7 +1965,7 @@ nsXMLHttpRequest::RequestCompleted()
}
}
ChangeState(XML_HTTP_REQUEST_COMPLETED, PR_TRUE);
ChangeState(XML_HTTP_REQUEST_DONE, PR_TRUE);
PRUint32 responseLength = mResponseBody.Length();
NS_NAMED_LITERAL_STRING(errorStr, ERROR_STR);
@ -2134,6 +2346,7 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
// Reset responseBody
mResponseBody.Truncate();
mResponseBodyUnicode.SetIsVoid(PR_TRUE);
mResponseBlob = nsnull;
// Reset responseXML
mResponseXML = nsnull;
@ -2369,20 +2582,20 @@ nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
/* readonly attribute long readyState; */
NS_IMETHODIMP
nsXMLHttpRequest::GetReadyState(PRInt32 *aState)
nsXMLHttpRequest::GetReadyState(PRUint16 *aState)
{
NS_ENSURE_ARG_POINTER(aState);
// Translate some of our internal states for external consumers
if (mState & XML_HTTP_REQUEST_UNINITIALIZED) {
*aState = 0; // UNINITIALIZED
if (mState & XML_HTTP_REQUEST_UNSENT) {
*aState = UNSENT;
} else if (mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
*aState = 1; // LOADING
} else if (mState & XML_HTTP_REQUEST_LOADED) {
*aState = 2; // LOADED
} else if (mState & (XML_HTTP_REQUEST_INTERACTIVE | XML_HTTP_REQUEST_STOPPED)) {
*aState = 3; // INTERACTIVE
} else if (mState & XML_HTTP_REQUEST_COMPLETED) {
*aState = 4; // COMPLETED
*aState = OPENED;
} else if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
*aState = HEADERS_RECEIVED;
} else if (mState & (XML_HTTP_REQUEST_LOADING | XML_HTTP_REQUEST_STOPPED)) {
*aState = LOADING;
} else if (mState & XML_HTTP_REQUEST_DONE) {
*aState = DONE;
} else {
NS_ERROR("Should not happen");
}
@ -2413,7 +2626,7 @@ nsXMLHttpRequest::GetMultipart(PRBool *_retval)
NS_IMETHODIMP
nsXMLHttpRequest::SetMultipart(PRBool aMultipart)
{
if (!(mState & XML_HTTP_REQUEST_UNINITIALIZED)) {
if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
// Can't change this while we're in the middle of something.
return NS_ERROR_IN_PROGRESS;
}
@ -2448,7 +2661,7 @@ nsXMLHttpRequest::SetMozBackgroundRequest(PRBool aMozBackgroundRequest)
if (!privileged)
return NS_ERROR_DOM_SECURITY_ERR;
if (!(mState & XML_HTTP_REQUEST_UNINITIALIZED)) {
if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
// Can't change this while we're in the middle of something.
return NS_ERROR_IN_PROGRESS;
}
@ -2545,7 +2758,7 @@ nsresult
nsXMLHttpRequest::Error(nsIDOMEvent* aEvent)
{
mResponseXML = nsnull;
ChangeState(XML_HTTP_REQUEST_COMPLETED);
ChangeState(XML_HTTP_REQUEST_DONE);
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
@ -2572,7 +2785,7 @@ nsXMLHttpRequest::ChangeState(PRUint32 aState, PRBool aBroadcast)
nsresult rv = NS_OK;
if (mProgressNotifier &&
!(aState & (XML_HTTP_REQUEST_LOADED | XML_HTTP_REQUEST_INTERACTIVE))) {
!(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
mTimerIsActive = PR_FALSE;
mProgressNotifier->Cancel();
}
@ -2581,7 +2794,7 @@ nsXMLHttpRequest::ChangeState(PRUint32 aState, PRBool aBroadcast)
aBroadcast &&
(mState & XML_HTTP_REQUEST_ASYNC ||
aState & XML_HTTP_REQUEST_OPENED ||
aState & XML_HTTP_REQUEST_COMPLETED)) {
aState & XML_HTTP_REQUEST_DONE)) {
nsCOMPtr<nsIDOMEvent> event;
rv = CreateReadystatechangeEvent(getter_AddRefs(event));
NS_ENSURE_SUCCESS(rv, rv);

Просмотреть файл

@ -224,6 +224,8 @@ protected:
PRUint32 toOffset,
PRUint32 count,
PRUint32 *writeCount);
nsresult GetResponseArrayBuffer(jsval *aResult);
void CreateResponseBlob(nsIRequest *request);
// Change the state of the object with this. The broadcast argument
// determines if the onreadystatechange listener should be called.
nsresult ChangeState(PRUint32 aState, PRBool aBroadcast = PR_TRUE);
@ -293,6 +295,16 @@ protected:
// will cause us to clear the cached value anyway.
nsString mResponseBodyUnicode;
enum {
XML_HTTP_RESPONSE_TYPE_DEFAULT,
XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER,
XML_HTTP_RESPONSE_TYPE_BLOB,
XML_HTTP_RESPONSE_TYPE_DOCUMENT,
XML_HTTP_RESPONSE_TYPE_TEXT
} mResponseType;
nsCOMPtr<nsIDOMBlob> mResponseBlob;
nsCString mOverrideMimeType;
/**

Просмотреть файл

@ -13,6 +13,7 @@
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var path = "/tests/content/base/test/";
@ -28,16 +29,19 @@ var failFiles = [['//example.com' + path + 'file_XHR_pass1.xml', 'GET'],
for (i = 0; i < passFiles.length; ++i) {
xhr = new XMLHttpRequest();
is(xhr.mozResponseType, "", "wrong initial responseType");
xhr.open(passFiles[i][1], passFiles[i][0], false);
xhr.send(null);
is(xhr.status, 200, "wrong status");
if (xhr.responseXML) {
is((new XMLSerializer()).serializeToString(xhr.responseXML.documentElement),
"<res>hello</res>",
"wrong response");
"wrong responseXML");
is(xhr.mozResponse, "<res>hello</res>\n", "wrong response");
}
else {
is(xhr.responseText, "hello pass\n", "wrong response");
is(xhr.responseText, "hello pass\n", "wrong responseText");
is(xhr.mozResponse, "hello pass\n", "wrong response");
}
}
@ -60,35 +64,127 @@ for (i = 0; i < failFiles.length; ++i) {
}
}
// test mozResponseArrayBuffer
// test response (responseType='document')
function checkResponseTextAccessThrows(xhr) {
var didthrow = false;
try { xhr.responseText } catch (e) { didthrow = true; }
ok(didthrow, "should have thrown when accessing responseText");
}
function checkResponseXMLAccessThrows(xhr) {
var didthrow = false;
try { xhr.responseXML } catch (e) { didthrow = true; }
ok(didthrow, "should have thrown when accessing responseXML");
}
function checkSetResponseTypeThrows(xhr) {
var didthrow = false;
try { xhr.mozResponseType = 'document'; } catch (e) { didthrow = true; }
ok(didthrow, "should have thrown when accessing responseType");
}
xhr = new XMLHttpRequest();
checkSetResponseTypeThrows(xhr);
xhr.open("GET", 'file_XHR_pass1.xml', false);
xhr.mozResponseType = 'document';
xhr.send(null);
checkSetResponseTypeThrows(xhr);
is(xhr.status, 200, "wrong status");
checkResponseTextAccessThrows(xhr);
is((new XMLSerializer()).serializeToString(xhr.mozResponse.documentElement),
"<res>hello</res>",
"wrong response");
// test response (responseType='text')
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_pass2.txt', false);
xhr.mozResponseType = 'text';
xhr.send(null);
is(xhr.status, 200, "wrong status");
checkResponseXMLAccessThrows(xhr);
is(xhr.mozResponse, "hello pass\n", "wrong response");
// test response (responseType='arraybuffer')
function arraybuffer_equals_to(ab, s) {
is(ab.byteLength, s.length, "wrong arraybuffer byteLength");
u8v = new Uint8Array(ab);
is(String.fromCharCode.apply(String, u8v), s, "wrong values");
}
// with a simple text file
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_pass2.txt', false);
xhr.mozResponseType = 'arraybuffer';
xhr.send(null);
is(xhr.status, 200, "wrong status");
ab = xhr.mozResponseArrayBuffer;
checkResponseTextAccessThrows(xhr);
checkResponseXMLAccessThrows(xhr);
ab = xhr.mozResponse;
ok(ab != null, "should have a non-null arraybuffer");
is(ab.byteLength, "hello pass\n".length, "wrong arraybuffer byteLength");
u8v = new Uint8Array(ab);
ok(String.fromCharCode([u8v[0], u8v[1], u8v[2], u8v[3], u8v[4]]), "hello", "wrong values");
arraybuffer_equals_to(ab, "hello pass\n");
// with a binary file
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_binary1.bin', false);
xhr.mozResponseType = 'arraybuffer';
xhr.send(null)
is(xhr.status, 200, "wrong status");
ab = xhr.mozResponseArrayBuffer;
checkResponseTextAccessThrows(xhr);
checkResponseXMLAccessThrows(xhr);
ab = xhr.mozResponse;
ok(ab != null, "should have a non-null arraybuffer");
is(ab.byteLength, 12, "wrong arraybuffer byteLength");
arraybuffer_equals_to(ab, "\xaa\xee\0\x03\xff\xff\xff\xff\xbb\xbb\xbb\xbb");
u8v = new Uint8Array(ab);
i32v = new Int32Array(ab);
u32v = new Uint32Array(ab, 8);
ok(u8v[0] == 0xaa && u8v[1] == 0xee && u8v[2] == 0x00 && u8v[3] == 0x03, "wrong initial 4 bytes");
is(i32v[1], -1, "wrong value, expected -1 (0xffffffff)");
is(u32v[0], 0xbbbbbbbb, "wrong value, expected 0xbbbbbbbb");
// test response (responseType='blob')
var onloadCount = 0;
function checkOnloadCount() {
if (++onloadCount >= 2) SimpleTest.finish();
};
// with a simple text file
xhr = new XMLHttpRequest();
xhr.open("GET", 'file_XHR_pass2.txt', false);
xhr.mozResponseType = 'blob';
xhr.send(null);
is(xhr.status, 200, "wrong status");
checkResponseTextAccessThrows(xhr);
checkResponseXMLAccessThrows(xhr);
b = xhr.mozResponse;
ok(b, "should have a non-null blob");
is(b.size, "hello pass\n".length, "wrong blob size");
fr = new FileReader();
fr.onload = function() {
ok(fr.result, "hello pass\n", "wrong values");
checkOnloadCount();
};
fr.readAsBinaryString(b);
// with a binary file
xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
switch (xhr.readyState) {
case 2:
is(xhr.status, 200, "wrong status");
xhr.mozResponseType = 'blob';
break;
case 4:
b = xhr.mozResponse;
ok(b != null, "should have a non-null blob");
is(b.size, 12, "wrong blob size");
fr = new FileReader();
fr.onload = function() {
is(fr.result, "\xaa\xee\0\x03\xff\xff\xff\xff\xbb\xbb\xbb\xbb", "wrong values");
checkOnloadCount();
};
xhr = null; // kill the XHR object
SpecialPowers.gc();
fr.readAsBinaryString(b);
break;
}
};
xhr.open("GET", 'file_XHR_binary1.bin', true);
xhr.send(null);
var client = new XMLHttpRequest();
client.onreadystatechange = function() {
@ -96,6 +192,7 @@ client.onreadystatechange = function() {
try {
is(client.responseXML, null, "responseXML should be null.");
is(client.responseText, "", "responseText should be empty string.");
is(client.mozResponse, "", "response should be empty string.");
is(client.status, 0, "status should be 0.");
is(client.statusText, "", "statusText should be empty string.");
is(client.getAllResponseHeaders(), "",

Просмотреть файл

@ -265,7 +265,7 @@ public:
nsresult status;
nsresult statusResult;
PRInt32 readyState;
PRUint16 readyState;
nsresult readyStateResult;
protected:

Просмотреть файл

@ -720,7 +720,7 @@ nsDOMWorkerXHR::SetRequestHeader(const nsACString& aHeader,
}
NS_IMETHODIMP
nsDOMWorkerXHR::GetReadyState(PRInt32* aReadyState)
nsDOMWorkerXHR::GetReadyState(PRUint16* aReadyState)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
@ -902,9 +902,21 @@ nsDOMWorkerXHR::SetWithCredentials(PRBool aWithCredentials)
return NS_OK;
}
/* readonly attribute jsval (ArrayBuffer) mozResponseArrayBuffer; */
NS_IMETHODIMP
nsDOMWorkerXHR::GetMozResponseArrayBuffer(jsval *aResult)
nsDOMWorkerXHR::GetMozResponseType(nsAString& aResponseText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDOMWorkerXHR::SetMozResponseType(const nsAString& aResponseText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* readonly attribute jsval response; */
NS_IMETHODIMP
nsDOMWorkerXHR::GetMozResponse(JSContext *aCx, jsval *aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

Просмотреть файл

@ -1090,7 +1090,7 @@ nsDOMWorkerXHRProxy::GetStatus(nsresult* _retval)
}
nsresult
nsDOMWorkerXHRProxy::GetReadyState(PRInt32* _retval)
nsDOMWorkerXHRProxy::GetReadyState(PRUint16* _retval)
{
NS_ASSERTION(_retval, "Null pointer!");

Просмотреть файл

@ -141,7 +141,7 @@ protected:
nsresult GetResponseText(nsAString& _retval);
nsresult GetStatusText(nsACString& _retval);
nsresult GetStatus(nsresult* _retval);
nsresult GetReadyState(PRInt32* _retval);
nsresult GetReadyState(PRUint16* _retval);
nsresult SetRequestHeader(const nsACString& aHeader,
const nsACString& aValue);
nsresult OverrideMimeType(const nsACString& aMimetype);