Bug 658178 - 'Make XHR2 response/responseType work in Web Workers'. r=jst+sicking+mrbkap.

This commit is contained in:
Ben Turner 2011-11-07 17:01:29 -08:00
parent 1aa2b3ff73
commit dbd7d35169
11 changed files with 472 additions and 60 deletions

View File

@ -5442,10 +5442,15 @@ void
nsContentUtils::CheckCCWrapperTraversal(nsISupports* aScriptObjectHolder,
nsWrapperCache* aCache)
{
JSObject* wrapper = aCache->GetWrapper();
if (!wrapper) {
return;
}
nsXPCOMCycleCollectionParticipant* participant;
CallQueryInterface(aScriptObjectHolder, &participant);
DebugWrapperTraversalCallback callback(aCache->GetWrapper());
DebugWrapperTraversalCallback callback(wrapper);
participant->Traverse(aScriptObjectHolder, callback);
NS_ASSERTION(callback.mFound,
@ -5907,9 +5912,11 @@ nsContentUtils::TraceWrapper(nsWrapperCache* aCache, TraceCallback aCallback,
void *aClosure)
{
if (aCache->PreservingWrapper()) {
aCallback(nsIProgrammingLanguage::JAVASCRIPT,
aCache->GetWrapperPreserveColor(),
"Preserved wrapper", aClosure);
JSObject *wrapper = aCache->GetWrapperPreserveColor();
if (wrapper) {
aCallback(nsIProgrammingLanguage::JAVASCRIPT, wrapper,
"Preserved wrapper", aClosure);
}
}
else {
JSObject *expando = aCache->GetExpandoObjectPreserveColor();

View File

@ -453,6 +453,14 @@ nsXMLHttpRequest::~nsXMLHttpRequest()
NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
// This can happen if the XHR was only used by C++ (and so never created a JS
// wrapper) that also made an ArrayBuffer.
if (PreservingWrapper()) {
nsContentUtils::ReleaseWrapper(
static_cast<nsIDOMEventTarget*>(
static_cast<nsDOMEventTargetHelper*>(this)), this);
}
nsLayoutStatics::Release();
}

View File

@ -538,6 +538,13 @@ struct MainThreadChromeWorkerStructuredCloneCallbacks
AssertIsOnMainThread();
JSObject* clone =
MainThreadWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
aClosure);
if (clone) {
return clone;
}
clone =
ChromeWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
aClosure);
if (clone) {
@ -554,14 +561,15 @@ struct MainThreadChromeWorkerStructuredCloneCallbacks
{
AssertIsOnMainThread();
JSBool ok =
ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
if (ok) {
return ok;
if (MainThreadWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
aClosure) ||
ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
aClosure) ||
NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull)) {
return true;
}
JS_ClearPendingException(aCx);
return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull);
return false;
}
static void

View File

@ -42,6 +42,7 @@
#include "jscntxt.h"
#include "jsfriendapi.h"
#include "Exceptions.h"
#include "WorkerPrivate.h"
#include "XMLHttpRequestPrivate.h"
@ -275,10 +276,12 @@ class XMLHttpRequest
SLOT_status,
SLOT_statusText,
SLOT_readyState,
SLOT_response,
SLOT_multipart,
SLOT_mozBackgroundRequest,
SLOT_withCredentials,
SLOT_upload,
SLOT_responseType,
SLOT_COUNT
};
@ -342,6 +345,7 @@ public:
HANDLE_STATE_VALUE(mStatus, SLOT_status)
HANDLE_STATE_VALUE(mStatusText, SLOT_statusText)
HANDLE_STATE_VALUE(mReadyState, SLOT_readyState)
HANDLE_STATE_VALUE(mResponse, SLOT_response)
#undef HANDLE_STATE_VALUE
@ -395,6 +399,11 @@ private:
return false;
}
JSString* textStr = JS_NewStringCopyN(aCx, "text", 4);
if (!textStr) {
return false;
}
jsval emptyString = JS_GetEmptyStringValue(aCx);
jsval zero = INT_TO_JSVAL(0);
@ -407,7 +416,9 @@ private:
!JS_SetReservedSlot(aCx, obj, SLOT_multipart, JSVAL_FALSE) ||
!JS_SetReservedSlot(aCx, obj, SLOT_mozBackgroundRequest, JSVAL_FALSE) ||
!JS_SetReservedSlot(aCx, obj, SLOT_withCredentials, JSVAL_FALSE) ||
!JS_SetReservedSlot(aCx, obj, SLOT_upload, JSVAL_NULL)) {
!JS_SetReservedSlot(aCx, obj, SLOT_upload, JSVAL_NULL) ||
!JS_SetReservedSlot(aCx, obj, SLOT_responseType,
STRING_TO_JSVAL(textStr))) {
return false;
}
@ -462,7 +473,7 @@ private:
if (JSVAL_IS_VOID(rval)) {
// Throw an exception.
JS_ReportError(aCx, "Unable to retrieve %s property", name);
exceptions::ThrowDOMExceptionForCode(aCx, INVALID_STATE_ERR);
return false;
}
@ -535,8 +546,13 @@ private:
return false; \
} \
\
jsval oldVal; \
if (!JS_GetReservedSlot(aCx, aObj, slot, &oldVal)) { \
return false; \
} \
\
jsval rval = *aVp; \
if (!priv->Set##_name (aCx, &rval) || \
if (!priv->Set##_name (aCx, oldVal, &rval) || \
!JS_SetReservedSlot(aCx, aObj, slot, rval)) { \
return false; \
} \
@ -548,6 +564,7 @@ private:
IMPL_SETTER(Multipart)
IMPL_SETTER(MozBackgroundRequest)
IMPL_SETTER(WithCredentials)
IMPL_SETTER(ResponseType)
#undef IMPL_SETTER
@ -783,13 +800,17 @@ JSPropertySpec XMLHttpRequest::sProperties[] = {
GENERIC_READONLY_PROPERTY(status)
GENERIC_READONLY_PROPERTY(statusText)
GENERIC_READONLY_PROPERTY(readyState)
GENERIC_READONLY_PROPERTY(response)
{ "multipart", 7, PROPERTY_FLAGS, GetProperty, SetMultipart },
{ "mozBackgroundRequest", 8, PROPERTY_FLAGS, GetProperty,
SetMozBackgroundRequest },
{ "withCredentials", 9, PROPERTY_FLAGS, GetProperty, SetWithCredentials },
{ "upload", SLOT_upload, PROPERTY_FLAGS, GetUpload,
{ "multipart", SLOT_multipart, PROPERTY_FLAGS, GetProperty, SetMultipart },
{ "mozBackgroundRequest", SLOT_mozBackgroundRequest, PROPERTY_FLAGS,
GetProperty, SetMozBackgroundRequest },
{ "withCredentials", SLOT_withCredentials, PROPERTY_FLAGS, GetProperty,
SetWithCredentials },
{ "upload", SLOT_upload, PROPERTY_FLAGS, GetUpload,
js_GetterOnlyPropertyStub },
{ "responseType", SLOT_responseType, PROPERTY_FLAGS, GetProperty,
SetResponseType },
{ sEventStrings[STRING_onreadystatechange], STRING_onreadystatechange,
PROPERTY_FLAGS, GetEventListener, SetEventListener },
{ sEventStrings[STRING_onabort], STRING_onabort, PROPERTY_FLAGS,

View File

@ -56,10 +56,12 @@ struct StateData
jsval mStatus;
jsval mStatusText;
jsval mReadyState;
jsval mResponse;
bool mResponseTextException;
bool mStatusException;
bool mStatusTextException;
bool mReadyStateException;
bool mResponseException;
};
bool

View File

@ -41,7 +41,6 @@
#include "nsIDOMEvent.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMProgressEvent.h"
#include "nsIJSContextStack.h"
#include "nsIRunnable.h"
#include "nsIXMLHttpRequest.h"
#include "nsIXPConnect.h"
@ -88,7 +87,6 @@ public:
bool mSeenUploadLoadStart;
// Only touched on the main thread.
nsString mPreviousResponseText;
nsCString mPreviousStatusText;
PRUint32 mSyncQueueKey;
PRUint32 mSyncEventResponseSyncQueueKey;
@ -158,7 +156,6 @@ public:
{
AssertIsOnMainThread();
mPreviousResponseText.Truncate();
mPreviousStatusText.Truncate();
if (mUploadEventListenersAttached) {
@ -439,7 +436,10 @@ NS_IMPL_ISUPPORTS2(LoadStartDetectionRunnable, nsIRunnable, nsIDOMEventListener)
class EventRunnable : public MainThreadProxyRunnable
{
nsString mType;
nsString mResponseText;
nsString mResponseType;
JSAutoStructuredCloneBuffer mResponseBuffer;
nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
jsval mResponse;
nsCString mStatusText;
PRUint64 mLoaded;
PRUint64 mTotal;
@ -453,25 +453,28 @@ class EventRunnable : public MainThreadProxyRunnable
bool mStatusException;
bool mStatusTextException;
bool mReadyStateException;
bool mResponseException;
public:
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
bool aLengthComputable, PRUint64 aLoaded, PRUint64 aTotal)
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
mLoaded(aLoaded), mTotal(aTotal), mChannelId(aProxy->mInnerChannelId),
mStatus(0), mReadyState(0), mUploadEvent(aUploadEvent),
mProgressEvent(true), mLengthComputable(aLengthComputable),
mResponseTextException(false), mStatusException(false),
mStatusTextException(false), mReadyStateException(false)
mResponse(JSVAL_VOID), mLoaded(aLoaded), mTotal(aTotal),
mChannelId(aProxy->mInnerChannelId), mStatus(0), mReadyState(0),
mUploadEvent(aUploadEvent), mProgressEvent(true),
mLengthComputable(aLengthComputable), mResponseTextException(false),
mStatusException(false), mStatusTextException(false),
mReadyStateException(false), mResponseException(false)
{ }
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
mLoaded(0), mTotal(0), mChannelId(aProxy->mInnerChannelId), mStatus(0),
mReadyState(0), mUploadEvent(aUploadEvent), mProgressEvent(false),
mLengthComputable(0), mResponseTextException(false),
mStatusException(false), mStatusTextException(false),
mReadyStateException(false)
mResponse(JSVAL_VOID), mLoaded(0), mTotal(0),
mChannelId(aProxy->mInnerChannelId), mStatus(0), mReadyState(0),
mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
mResponseTextException(false), mStatusException(false),
mStatusTextException(false), mReadyStateException(false),
mResponseException(false)
{ }
bool
@ -480,19 +483,41 @@ public:
nsRefPtr<nsXMLHttpRequest>& xhr = mProxy->mXHR;
NS_ASSERTION(xhr, "Must have an XHR here!");
if (NS_SUCCEEDED(xhr->GetResponseText(mResponseText))) {
if (mResponseText == mProxy->mPreviousResponseText) {
mResponseText.SetIsVoid(true);
if (NS_FAILED(xhr->GetResponseType(mResponseType))) {
NS_ERROR("This should never fail!");
}
jsval response;
if (NS_SUCCEEDED(xhr->GetResponse(aCx, &response))) {
if (JSVAL_IS_UNIVERSAL(response)) {
mResponse = response;
}
else {
mProxy->mPreviousResponseText = mResponseText;
// Anything subject to GC must be cloned.
JSStructuredCloneCallbacks* callbacks =
aWorkerPrivate->IsChromeWorker() ?
ChromeWorkerStructuredCloneCallbacks(true) :
WorkerStructuredCloneCallbacks(true);
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
if (mResponseBuffer.write(aCx, response, callbacks, &clonedObjects)) {
mClonedObjects.SwapElements(clonedObjects);
}
else {
NS_ASSERTION(JS_IsExceptionPending(aCx),
"This should really never fail unless OOM!");
mResponseException = true;
}
}
mResponseTextException = false;
}
else {
mResponseTextException = true;
mResponseException = true;
}
nsString responseText;
mResponseTextException = NS_FAILED(xhr->GetResponseText(responseText));
mStatusException = NS_FAILED(xhr->GetStatus(&mStatus));
if (NS_SUCCEEDED(xhr->GetStatusText(mStatusText))) {
@ -566,21 +591,44 @@ public:
xhr::StateData state;
state.mResponseTextException = mResponseTextException;
if (mResponseTextException || mResponseText.IsVoid()) {
state.mResponseText = JSVAL_VOID;
state.mResponseException = mResponseException;
if (!mResponseException) {
if (mResponseBuffer.data()) {
NS_ASSERTION(JSVAL_IS_VOID(mResponse), "Huh?!");
JSStructuredCloneCallbacks* callbacks =
aWorkerPrivate->IsChromeWorker() ?
ChromeWorkerStructuredCloneCallbacks(false) :
WorkerStructuredCloneCallbacks(false);
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
clonedObjects.SwapElements(mClonedObjects);
jsval response;
if (!mResponseBuffer.read(aCx, &response, callbacks, &clonedObjects)) {
return false;
}
mResponseBuffer.clear();
state.mResponse = response;
}
else {
state.mResponse = mResponse;
}
}
else if (mResponseText.IsEmpty()) {
state.mResponseText = JS_GetEmptyStringValue(aCx);
// This logic is all based on the assumption that mResponseTextException
// should be set if the responseType isn't "text". Otherwise we're going to
// hand out the wrong result if someone gets the responseText property.
state.mResponseTextException = mResponseTextException;
if (!mResponseTextException) {
NS_ASSERTION(JSVAL_IS_STRING(state.mResponse) ||
JSVAL_IS_NULL(state.mResponse),
"Bad response!");
state.mResponseText = state.mResponse;
}
else {
JSString* responseText = JS_NewUCStringCopyN(aCx, mResponseText.get(),
mResponseText.Length());
if (!responseText) {
return false;
}
mResponseText.Truncate();
state.mResponseText = STRING_TO_JSVAL(responseText);
state.mResponseText = JSVAL_VOID;
}
state.mStatusException = mStatusException;
@ -631,6 +679,19 @@ public:
JS_ReportPendingException(aCx);
}
// After firing the event set mResponse to JSVAL_NULL for chunked response
// types.
if (StringBeginsWith(mResponseType, NS_LITERAL_STRING("moz-chunked-"))) {
xhr::StateData newState = {
JSVAL_NULL, JSVAL_VOID, JSVAL_VOID, JSVAL_VOID, JSVAL_NULL,
false, false, false, false, false
};
if (!xhr::UpdateXHRState(aCx, target, mUploadEvent, newState)) {
return false;
}
}
return true;
}
};
@ -783,6 +844,34 @@ public:
}
};
class SetResponseTypeRunnable : public WorkerThreadProxySyncRunnable
{
nsString mResponseType;
public:
SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
const nsAString& aResponseType)
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
mResponseType(aResponseType)
{ }
intN
MainThreadRun()
{
nsresult rv = mProxy->mXHR->SetResponseType(mResponseType);
mResponseType.Truncate();
if (NS_SUCCEEDED(rv)) {
rv = mProxy->mXHR->GetResponseType(mResponseType);
}
return GetDOMExceptionCodeFromResult(rv);
}
void
GetResponseType(nsAString& aResponseType) {
aResponseType.Assign(mResponseType);
}
};
class AbortRunnable : public WorkerThreadProxySyncRunnable
{
public:
@ -911,10 +1000,12 @@ public:
}
mProxy->mInnerChannelId++;
mProxy->mPreviousResponseText.Truncate();
mProxy->mPreviousStatusText.Truncate();
rv = mProxy->mXHR->Open(mMethod, mURL, true, mUser, mPassword, 1);
if (NS_SUCCEEDED(rv)) {
rv = mProxy->mXHR->SetResponseType(NS_LITERAL_STRING("text"));
}
return GetDOMExceptionCodeFromResult(rv);
}
};
@ -1175,7 +1266,10 @@ Proxy::HandleEvent(nsIDOMEvent* aEvent)
runnable = new EventRunnable(this, !!uploadTarget, type);
}
runnable->Dispatch(nsnull);
{
RuntimeService::AutoSafeJSContext cx;
runnable->Dispatch(cx);
}
if (!uploadTarget) {
if (type.EqualsASCII(sEventStrings[STRING_loadstart])) {
@ -1276,7 +1370,7 @@ XMLHttpRequestPrivate::Notify(JSContext* aCx, Status aStatus)
}
bool
XMLHttpRequestPrivate::SetMultipart(JSContext* aCx, jsval *aVp)
XMLHttpRequestPrivate::SetMultipart(JSContext* aCx, jsval aOldVal, jsval *aVp)
{
mWorkerPrivate->AssertIsOnWorkerThread();
@ -1306,7 +1400,8 @@ XMLHttpRequestPrivate::SetMultipart(JSContext* aCx, jsval *aVp)
}
bool
XMLHttpRequestPrivate::SetMozBackgroundRequest(JSContext* aCx, jsval *aVp)
XMLHttpRequestPrivate::SetMozBackgroundRequest(JSContext* aCx, jsval aOldVal,
jsval *aVp)
{
mWorkerPrivate->AssertIsOnWorkerThread();
@ -1336,7 +1431,8 @@ XMLHttpRequestPrivate::SetMozBackgroundRequest(JSContext* aCx, jsval *aVp)
}
bool
XMLHttpRequestPrivate::SetWithCredentials(JSContext* aCx, jsval *aVp)
XMLHttpRequestPrivate::SetWithCredentials(JSContext* aCx, jsval aOldVal,
jsval *aVp)
{
mWorkerPrivate->AssertIsOnWorkerThread();
@ -1365,6 +1461,68 @@ XMLHttpRequestPrivate::SetWithCredentials(JSContext* aCx, jsval *aVp)
return true;
}
bool
XMLHttpRequestPrivate::SetResponseType(JSContext* aCx, jsval aOldVal,
jsval *aVp)
{
mWorkerPrivate->AssertIsOnWorkerThread();
if (mCanceled) {
return false;
}
if (!mProxy || SendInProgress()) {
ThrowDOMExceptionForCode(aCx, INVALID_STATE_ERR);
return false;
}
JSString* jsstr = JS_ValueToString(aCx, *aVp);
if (!jsstr) {
return false;
}
nsDependentJSString responseType;
if (!responseType.init(aCx, jsstr)) {
return false;
}
// "document" is fine for the main thread but not for a worker. Short-circuit
// that here.
if (responseType.EqualsLiteral("document")) {
*aVp = aOldVal;
return true;
}
nsRefPtr<SetResponseTypeRunnable> runnable =
new SetResponseTypeRunnable(mWorkerPrivate, mProxy, responseType);
if (!runnable->Dispatch(aCx)) {
return false;
}
nsString acceptedResponseType;
runnable->GetResponseType(acceptedResponseType);
if (acceptedResponseType == responseType) {
// Leave *aVp unchanged.
}
else if (acceptedResponseType.IsEmpty()) {
// Empty string.
*aVp = JS_GetEmptyStringValue(aCx);
}
else {
// Some other string.
jsstr = JS_NewUCStringCopyN(aCx, acceptedResponseType.get(),
acceptedResponseType.Length());
if (!jsstr) {
return false;
}
*aVp = STRING_TO_JSVAL(jsstr);
}
return true;
}
bool
XMLHttpRequestPrivate::Abort(JSContext* aCx)
{
@ -1680,8 +1838,8 @@ XMLHttpRequestPrivate::MaybeDispatchPrematureAbortEvents(JSContext* aCx,
NS_ASSERTION(mProxy, "Must have a proxy here!");
xhr::StateData state = {
JSVAL_VOID, JSVAL_VOID, JSVAL_VOID, INT_TO_JSVAL(4),
false, false, false, false
JSVAL_VOID, JSVAL_VOID, JSVAL_VOID, INT_TO_JSVAL(4), JSVAL_VOID,
false, false, false, false, false
};
if (mProxy->mSeenUploadLoadStart) {

View File

@ -111,13 +111,16 @@ public:
Notify(JSContext* aCx, Status aStatus);
bool
SetMultipart(JSContext* aCx, jsval *aVp);
SetMultipart(JSContext* aCx, jsval aOldVal, jsval *aVp);
bool
SetMozBackgroundRequest(JSContext* aCx, jsval *aVp);
SetMozBackgroundRequest(JSContext* aCx, jsval aOldVal, jsval *aVp);
bool
SetWithCredentials(JSContext* aCx, jsval *aVp);
SetWithCredentials(JSContext* aCx, jsval aOldVal, jsval *aVp);
bool
SetResponseType(JSContext* aCx, jsval aOldVal, jsval *aVp);
bool
Abort(JSContext* aCx);

View File

@ -104,6 +104,8 @@ _TEST_FILES = \
throwingOnerror_worker.js \
test_xhr.html \
xhr_worker.js \
test_xhr2.html \
xhr2_worker.js \
test_xhrAbort.html \
xhrAbort_worker.js \
testXHR.txt \

View File

@ -0,0 +1,38 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<!--
Tests of DOM Worker Threads XHR(Bug 450452 )
-->
<head>
<title>Test for DOM Worker Threads XHR (Bug 450452 )</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=450452">DOM Worker Threads XHR (Bug 450452)</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
var worker = new Worker("xhr2_worker.js");
worker.onmessage = function(event) {
is(event.data, "done", "Got correct result");
SimpleTest.finish();
}
worker.postMessage("testXHR.txt");
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,155 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
onmessage = function(event) {
const url = event.data;
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
xhr.send();
const refText = xhr.responseText;
function getResponse(type) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
if (type !== undefined) {
xhr.responseType = type;
}
xhr.send();
return xhr.response;
}
if (getResponse() != refText) {
throw new Error("unset responseType failed");
}
if (getResponse("") != refText) {
throw new Error("'' responseType failed");
}
if (getResponse("text") != refText) {
throw new Error("'text' responseType failed");
}
var array = new Uint8Array(getResponse("arraybuffer"));
if (String.fromCharCode.apply(String, array) != refText) {
throw new Error("'arraybuffer' responseType failed");
}
var blob = getResponse("blob");
if (new FileReaderSync().readAsText(blob) != refText) {
throw new Error("'blob' responseType failed");
}
// Make sure that we get invalid state exceptions when getting the wrong
// property.
function testResponseTextException(type) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
xhr.responseType = type;
xhr.send();
var exception;
try {
xhr.responseText;
}
catch(e) {
exception = e;
}
if (!exception || exception.code != DOMException.INVALID_STATE_ERR) {
throw new Error("Failed to throw when getting responseText on '" + type +
"' type");
}
}
testResponseTextException("arraybuffer");
testResponseTextException("blob");
// Make sure "document" works, but returns text.
xhr = new XMLHttpRequest();
if (xhr.responseType != "text") {
throw new Error("Default value for responseType is wrong!");
}
xhr.open("GET", url, false);
xhr.responseType = "document";
xhr.send();
if (xhr.responseText != refText) {
throw new Error("'document' type not working correctly");
}
// Make sure setting responseType before open or after send fails.
var exception;
xhr = new XMLHttpRequest();
try {
xhr.responseType = "arraybuffer";
}
catch(e) {
exception = e;
}
if (!exception || exception.code != DOMException.INVALID_STATE_ERR) {
throw new Error("Failed to throw when setting responseType before " +
"calling open()");
}
xhr.open("GET", url);
xhr.responseType = "text";
xhr.onload = function(event) {
if (event.target.response != refText) {
throw new Error("Bad response!");
}
xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "moz-chunked-text";
var lastIndex = 0;
xhr.onprogress = function(event) {
if (refText.substr(lastIndex, xhr.response.length) != xhr.response) {
throw new Error("Bad chunk!");
}
lastIndex += xhr.response.length;
};
xhr.onload = function(event) {
if (lastIndex != refText.length) {
throw new Error("Didn't see all the data!");
}
setTimeout(function() {
if (xhr.response !== null) {
throw new Error("Should have gotten null response outside of event!");
}
postMessage("done");
}, 0);
}
xhr.send(null);
};
xhr.send();
exception = null;
try {
xhr.responseType = "arraybuffer";
}
catch(e) {
exception = e;
}
if (!exception || exception.code != DOMException.INVALID_STATE_ERR) {
throw new Error("Failed to throw when setting responseType after " +
"calling send()");
}
}

View File

@ -1647,6 +1647,16 @@ extern JS_PUBLIC_DATA(jsid) JSID_EMPTY;
# define JSID_EMPTY ((jsid)JSID_TYPE_OBJECT)
#endif
/*
* Returns true iff the given jsval is immune to GC and can be used across
* multiple JSRuntimes without requiring any conversion API.
*/
static JS_ALWAYS_INLINE JSBool
JSVAL_IS_UNIVERSAL(jsval v)
{
return !JSVAL_IS_GCTHING(v);
}
/************************************************************************/
/* Lock and unlock the GC thing held by a jsval. */