зеркало из https://github.com/mozilla/gecko-dev.git
Bug 658178 - 'Make XHR2 response/responseType work in Web Workers'. r=jst+sicking+mrbkap.
This commit is contained in:
Родитель
6bf243becc
Коммит
408f05bed8
|
@ -5442,10 +5442,15 @@ void
|
||||||
nsContentUtils::CheckCCWrapperTraversal(nsISupports* aScriptObjectHolder,
|
nsContentUtils::CheckCCWrapperTraversal(nsISupports* aScriptObjectHolder,
|
||||||
nsWrapperCache* aCache)
|
nsWrapperCache* aCache)
|
||||||
{
|
{
|
||||||
|
JSObject* wrapper = aCache->GetWrapper();
|
||||||
|
if (!wrapper) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nsXPCOMCycleCollectionParticipant* participant;
|
nsXPCOMCycleCollectionParticipant* participant;
|
||||||
CallQueryInterface(aScriptObjectHolder, &participant);
|
CallQueryInterface(aScriptObjectHolder, &participant);
|
||||||
|
|
||||||
DebugWrapperTraversalCallback callback(aCache->GetWrapper());
|
DebugWrapperTraversalCallback callback(wrapper);
|
||||||
|
|
||||||
participant->Traverse(aScriptObjectHolder, callback);
|
participant->Traverse(aScriptObjectHolder, callback);
|
||||||
NS_ASSERTION(callback.mFound,
|
NS_ASSERTION(callback.mFound,
|
||||||
|
@ -5907,9 +5912,11 @@ nsContentUtils::TraceWrapper(nsWrapperCache* aCache, TraceCallback aCallback,
|
||||||
void *aClosure)
|
void *aClosure)
|
||||||
{
|
{
|
||||||
if (aCache->PreservingWrapper()) {
|
if (aCache->PreservingWrapper()) {
|
||||||
aCallback(nsIProgrammingLanguage::JAVASCRIPT,
|
JSObject *wrapper = aCache->GetWrapperPreserveColor();
|
||||||
aCache->GetWrapperPreserveColor(),
|
if (wrapper) {
|
||||||
"Preserved wrapper", aClosure);
|
aCallback(nsIProgrammingLanguage::JAVASCRIPT, wrapper,
|
||||||
|
"Preserved wrapper", aClosure);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
JSObject *expando = aCache->GetExpandoObjectPreserveColor();
|
JSObject *expando = aCache->GetExpandoObjectPreserveColor();
|
||||||
|
|
|
@ -453,6 +453,14 @@ nsXMLHttpRequest::~nsXMLHttpRequest()
|
||||||
NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
|
NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
|
||||||
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
|
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();
|
nsLayoutStatics::Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -538,6 +538,13 @@ struct MainThreadChromeWorkerStructuredCloneCallbacks
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
JSObject* clone =
|
JSObject* clone =
|
||||||
|
MainThreadWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
|
||||||
|
aClosure);
|
||||||
|
if (clone) {
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone =
|
||||||
ChromeWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
|
ChromeWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
|
||||||
aClosure);
|
aClosure);
|
||||||
if (clone) {
|
if (clone) {
|
||||||
|
@ -554,14 +561,15 @@ struct MainThreadChromeWorkerStructuredCloneCallbacks
|
||||||
{
|
{
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
JSBool ok =
|
if (MainThreadWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
|
||||||
ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
|
aClosure) ||
|
||||||
if (ok) {
|
ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
|
||||||
return ok;
|
aClosure) ||
|
||||||
|
NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_ClearPendingException(aCx);
|
return false;
|
||||||
return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
#include "jsfriendapi.h"
|
#include "jsfriendapi.h"
|
||||||
|
|
||||||
|
#include "Exceptions.h"
|
||||||
#include "WorkerPrivate.h"
|
#include "WorkerPrivate.h"
|
||||||
#include "XMLHttpRequestPrivate.h"
|
#include "XMLHttpRequestPrivate.h"
|
||||||
|
|
||||||
|
@ -275,10 +276,12 @@ class XMLHttpRequest
|
||||||
SLOT_status,
|
SLOT_status,
|
||||||
SLOT_statusText,
|
SLOT_statusText,
|
||||||
SLOT_readyState,
|
SLOT_readyState,
|
||||||
|
SLOT_response,
|
||||||
SLOT_multipart,
|
SLOT_multipart,
|
||||||
SLOT_mozBackgroundRequest,
|
SLOT_mozBackgroundRequest,
|
||||||
SLOT_withCredentials,
|
SLOT_withCredentials,
|
||||||
SLOT_upload,
|
SLOT_upload,
|
||||||
|
SLOT_responseType,
|
||||||
|
|
||||||
SLOT_COUNT
|
SLOT_COUNT
|
||||||
};
|
};
|
||||||
|
@ -342,6 +345,7 @@ public:
|
||||||
HANDLE_STATE_VALUE(mStatus, SLOT_status)
|
HANDLE_STATE_VALUE(mStatus, SLOT_status)
|
||||||
HANDLE_STATE_VALUE(mStatusText, SLOT_statusText)
|
HANDLE_STATE_VALUE(mStatusText, SLOT_statusText)
|
||||||
HANDLE_STATE_VALUE(mReadyState, SLOT_readyState)
|
HANDLE_STATE_VALUE(mReadyState, SLOT_readyState)
|
||||||
|
HANDLE_STATE_VALUE(mResponse, SLOT_response)
|
||||||
|
|
||||||
#undef HANDLE_STATE_VALUE
|
#undef HANDLE_STATE_VALUE
|
||||||
|
|
||||||
|
@ -395,6 +399,11 @@ private:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSString* textStr = JS_NewStringCopyN(aCx, "text", 4);
|
||||||
|
if (!textStr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
jsval emptyString = JS_GetEmptyStringValue(aCx);
|
jsval emptyString = JS_GetEmptyStringValue(aCx);
|
||||||
jsval zero = INT_TO_JSVAL(0);
|
jsval zero = INT_TO_JSVAL(0);
|
||||||
|
|
||||||
|
@ -407,7 +416,9 @@ private:
|
||||||
!JS_SetReservedSlot(aCx, obj, SLOT_multipart, JSVAL_FALSE) ||
|
!JS_SetReservedSlot(aCx, obj, SLOT_multipart, JSVAL_FALSE) ||
|
||||||
!JS_SetReservedSlot(aCx, obj, SLOT_mozBackgroundRequest, JSVAL_FALSE) ||
|
!JS_SetReservedSlot(aCx, obj, SLOT_mozBackgroundRequest, JSVAL_FALSE) ||
|
||||||
!JS_SetReservedSlot(aCx, obj, SLOT_withCredentials, 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +473,7 @@ private:
|
||||||
|
|
||||||
if (JSVAL_IS_VOID(rval)) {
|
if (JSVAL_IS_VOID(rval)) {
|
||||||
// Throw an exception.
|
// Throw an exception.
|
||||||
JS_ReportError(aCx, "Unable to retrieve %s property", name);
|
exceptions::ThrowDOMExceptionForCode(aCx, INVALID_STATE_ERR);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,8 +546,13 @@ private:
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
jsval oldVal; \
|
||||||
|
if (!JS_GetReservedSlot(aCx, aObj, slot, &oldVal)) { \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
jsval rval = *aVp; \
|
jsval rval = *aVp; \
|
||||||
if (!priv->Set##_name (aCx, &rval) || \
|
if (!priv->Set##_name (aCx, oldVal, &rval) || \
|
||||||
!JS_SetReservedSlot(aCx, aObj, slot, rval)) { \
|
!JS_SetReservedSlot(aCx, aObj, slot, rval)) { \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
|
@ -548,6 +564,7 @@ private:
|
||||||
IMPL_SETTER(Multipart)
|
IMPL_SETTER(Multipart)
|
||||||
IMPL_SETTER(MozBackgroundRequest)
|
IMPL_SETTER(MozBackgroundRequest)
|
||||||
IMPL_SETTER(WithCredentials)
|
IMPL_SETTER(WithCredentials)
|
||||||
|
IMPL_SETTER(ResponseType)
|
||||||
|
|
||||||
#undef IMPL_SETTER
|
#undef IMPL_SETTER
|
||||||
|
|
||||||
|
@ -783,13 +800,17 @@ JSPropertySpec XMLHttpRequest::sProperties[] = {
|
||||||
GENERIC_READONLY_PROPERTY(status)
|
GENERIC_READONLY_PROPERTY(status)
|
||||||
GENERIC_READONLY_PROPERTY(statusText)
|
GENERIC_READONLY_PROPERTY(statusText)
|
||||||
GENERIC_READONLY_PROPERTY(readyState)
|
GENERIC_READONLY_PROPERTY(readyState)
|
||||||
|
GENERIC_READONLY_PROPERTY(response)
|
||||||
|
|
||||||
{ "multipart", 7, PROPERTY_FLAGS, GetProperty, SetMultipart },
|
{ "multipart", SLOT_multipart, PROPERTY_FLAGS, GetProperty, SetMultipart },
|
||||||
{ "mozBackgroundRequest", 8, PROPERTY_FLAGS, GetProperty,
|
{ "mozBackgroundRequest", SLOT_mozBackgroundRequest, PROPERTY_FLAGS,
|
||||||
SetMozBackgroundRequest },
|
GetProperty, SetMozBackgroundRequest },
|
||||||
{ "withCredentials", 9, PROPERTY_FLAGS, GetProperty, SetWithCredentials },
|
{ "withCredentials", SLOT_withCredentials, PROPERTY_FLAGS, GetProperty,
|
||||||
{ "upload", SLOT_upload, PROPERTY_FLAGS, GetUpload,
|
SetWithCredentials },
|
||||||
|
{ "upload", SLOT_upload, PROPERTY_FLAGS, GetUpload,
|
||||||
js_GetterOnlyPropertyStub },
|
js_GetterOnlyPropertyStub },
|
||||||
|
{ "responseType", SLOT_responseType, PROPERTY_FLAGS, GetProperty,
|
||||||
|
SetResponseType },
|
||||||
{ sEventStrings[STRING_onreadystatechange], STRING_onreadystatechange,
|
{ sEventStrings[STRING_onreadystatechange], STRING_onreadystatechange,
|
||||||
PROPERTY_FLAGS, GetEventListener, SetEventListener },
|
PROPERTY_FLAGS, GetEventListener, SetEventListener },
|
||||||
{ sEventStrings[STRING_onabort], STRING_onabort, PROPERTY_FLAGS,
|
{ sEventStrings[STRING_onabort], STRING_onabort, PROPERTY_FLAGS,
|
||||||
|
|
|
@ -56,10 +56,12 @@ struct StateData
|
||||||
jsval mStatus;
|
jsval mStatus;
|
||||||
jsval mStatusText;
|
jsval mStatusText;
|
||||||
jsval mReadyState;
|
jsval mReadyState;
|
||||||
|
jsval mResponse;
|
||||||
bool mResponseTextException;
|
bool mResponseTextException;
|
||||||
bool mStatusException;
|
bool mStatusException;
|
||||||
bool mStatusTextException;
|
bool mStatusTextException;
|
||||||
bool mReadyStateException;
|
bool mReadyStateException;
|
||||||
|
bool mResponseException;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
#include "nsIDOMEvent.h"
|
#include "nsIDOMEvent.h"
|
||||||
#include "nsIDOMEventListener.h"
|
#include "nsIDOMEventListener.h"
|
||||||
#include "nsIDOMProgressEvent.h"
|
#include "nsIDOMProgressEvent.h"
|
||||||
#include "nsIJSContextStack.h"
|
|
||||||
#include "nsIRunnable.h"
|
#include "nsIRunnable.h"
|
||||||
#include "nsIXMLHttpRequest.h"
|
#include "nsIXMLHttpRequest.h"
|
||||||
#include "nsIXPConnect.h"
|
#include "nsIXPConnect.h"
|
||||||
|
@ -88,7 +87,6 @@ public:
|
||||||
bool mSeenUploadLoadStart;
|
bool mSeenUploadLoadStart;
|
||||||
|
|
||||||
// Only touched on the main thread.
|
// Only touched on the main thread.
|
||||||
nsString mPreviousResponseText;
|
|
||||||
nsCString mPreviousStatusText;
|
nsCString mPreviousStatusText;
|
||||||
PRUint32 mSyncQueueKey;
|
PRUint32 mSyncQueueKey;
|
||||||
PRUint32 mSyncEventResponseSyncQueueKey;
|
PRUint32 mSyncEventResponseSyncQueueKey;
|
||||||
|
@ -158,7 +156,6 @@ public:
|
||||||
{
|
{
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
mPreviousResponseText.Truncate();
|
|
||||||
mPreviousStatusText.Truncate();
|
mPreviousStatusText.Truncate();
|
||||||
|
|
||||||
if (mUploadEventListenersAttached) {
|
if (mUploadEventListenersAttached) {
|
||||||
|
@ -439,7 +436,10 @@ NS_IMPL_ISUPPORTS2(LoadStartDetectionRunnable, nsIRunnable, nsIDOMEventListener)
|
||||||
class EventRunnable : public MainThreadProxyRunnable
|
class EventRunnable : public MainThreadProxyRunnable
|
||||||
{
|
{
|
||||||
nsString mType;
|
nsString mType;
|
||||||
nsString mResponseText;
|
nsString mResponseType;
|
||||||
|
JSAutoStructuredCloneBuffer mResponseBuffer;
|
||||||
|
nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
|
||||||
|
jsval mResponse;
|
||||||
nsCString mStatusText;
|
nsCString mStatusText;
|
||||||
PRUint64 mLoaded;
|
PRUint64 mLoaded;
|
||||||
PRUint64 mTotal;
|
PRUint64 mTotal;
|
||||||
|
@ -453,25 +453,28 @@ class EventRunnable : public MainThreadProxyRunnable
|
||||||
bool mStatusException;
|
bool mStatusException;
|
||||||
bool mStatusTextException;
|
bool mStatusTextException;
|
||||||
bool mReadyStateException;
|
bool mReadyStateException;
|
||||||
|
bool mResponseException;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
|
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
|
||||||
bool aLengthComputable, PRUint64 aLoaded, PRUint64 aTotal)
|
bool aLengthComputable, PRUint64 aLoaded, PRUint64 aTotal)
|
||||||
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
|
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
|
||||||
mLoaded(aLoaded), mTotal(aTotal), mChannelId(aProxy->mInnerChannelId),
|
mResponse(JSVAL_VOID), mLoaded(aLoaded), mTotal(aTotal),
|
||||||
mStatus(0), mReadyState(0), mUploadEvent(aUploadEvent),
|
mChannelId(aProxy->mInnerChannelId), mStatus(0), mReadyState(0),
|
||||||
mProgressEvent(true), mLengthComputable(aLengthComputable),
|
mUploadEvent(aUploadEvent), mProgressEvent(true),
|
||||||
mResponseTextException(false), mStatusException(false),
|
mLengthComputable(aLengthComputable), mResponseTextException(false),
|
||||||
mStatusTextException(false), mReadyStateException(false)
|
mStatusException(false), mStatusTextException(false),
|
||||||
|
mReadyStateException(false), mResponseException(false)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
|
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
|
||||||
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
|
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
|
||||||
mLoaded(0), mTotal(0), mChannelId(aProxy->mInnerChannelId), mStatus(0),
|
mResponse(JSVAL_VOID), mLoaded(0), mTotal(0),
|
||||||
mReadyState(0), mUploadEvent(aUploadEvent), mProgressEvent(false),
|
mChannelId(aProxy->mInnerChannelId), mStatus(0), mReadyState(0),
|
||||||
mLengthComputable(0), mResponseTextException(false),
|
mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
|
||||||
mStatusException(false), mStatusTextException(false),
|
mResponseTextException(false), mStatusException(false),
|
||||||
mReadyStateException(false)
|
mStatusTextException(false), mReadyStateException(false),
|
||||||
|
mResponseException(false)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -480,19 +483,41 @@ public:
|
||||||
nsRefPtr<nsXMLHttpRequest>& xhr = mProxy->mXHR;
|
nsRefPtr<nsXMLHttpRequest>& xhr = mProxy->mXHR;
|
||||||
NS_ASSERTION(xhr, "Must have an XHR here!");
|
NS_ASSERTION(xhr, "Must have an XHR here!");
|
||||||
|
|
||||||
if (NS_SUCCEEDED(xhr->GetResponseText(mResponseText))) {
|
if (NS_FAILED(xhr->GetResponseType(mResponseType))) {
|
||||||
if (mResponseText == mProxy->mPreviousResponseText) {
|
NS_ERROR("This should never fail!");
|
||||||
mResponseText.SetIsVoid(true);
|
}
|
||||||
|
|
||||||
|
jsval response;
|
||||||
|
if (NS_SUCCEEDED(xhr->GetResponse(aCx, &response))) {
|
||||||
|
if (JSVAL_IS_UNIVERSAL(response)) {
|
||||||
|
mResponse = response;
|
||||||
}
|
}
|
||||||
else {
|
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 {
|
else {
|
||||||
mResponseTextException = true;
|
mResponseException = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsString responseText;
|
||||||
|
mResponseTextException = NS_FAILED(xhr->GetResponseText(responseText));
|
||||||
|
|
||||||
mStatusException = NS_FAILED(xhr->GetStatus(&mStatus));
|
mStatusException = NS_FAILED(xhr->GetStatus(&mStatus));
|
||||||
|
|
||||||
if (NS_SUCCEEDED(xhr->GetStatusText(mStatusText))) {
|
if (NS_SUCCEEDED(xhr->GetStatusText(mStatusText))) {
|
||||||
|
@ -566,21 +591,44 @@ public:
|
||||||
|
|
||||||
xhr::StateData state;
|
xhr::StateData state;
|
||||||
|
|
||||||
state.mResponseTextException = mResponseTextException;
|
state.mResponseException = mResponseException;
|
||||||
if (mResponseTextException || mResponseText.IsVoid()) {
|
if (!mResponseException) {
|
||||||
state.mResponseText = JSVAL_VOID;
|
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 {
|
else {
|
||||||
JSString* responseText = JS_NewUCStringCopyN(aCx, mResponseText.get(),
|
state.mResponseText = JSVAL_VOID;
|
||||||
mResponseText.Length());
|
|
||||||
if (!responseText) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mResponseText.Truncate();
|
|
||||||
state.mResponseText = STRING_TO_JSVAL(responseText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state.mStatusException = mStatusException;
|
state.mStatusException = mStatusException;
|
||||||
|
@ -631,6 +679,19 @@ public:
|
||||||
JS_ReportPendingException(aCx);
|
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;
|
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
|
class AbortRunnable : public WorkerThreadProxySyncRunnable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -911,10 +1000,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
mProxy->mInnerChannelId++;
|
mProxy->mInnerChannelId++;
|
||||||
mProxy->mPreviousResponseText.Truncate();
|
|
||||||
mProxy->mPreviousStatusText.Truncate();
|
mProxy->mPreviousStatusText.Truncate();
|
||||||
|
|
||||||
rv = mProxy->mXHR->Open(mMethod, mURL, true, mUser, mPassword, 1);
|
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);
|
return GetDOMExceptionCodeFromResult(rv);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1175,7 +1266,10 @@ Proxy::HandleEvent(nsIDOMEvent* aEvent)
|
||||||
runnable = new EventRunnable(this, !!uploadTarget, type);
|
runnable = new EventRunnable(this, !!uploadTarget, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
runnable->Dispatch(nsnull);
|
{
|
||||||
|
RuntimeService::AutoSafeJSContext cx;
|
||||||
|
runnable->Dispatch(cx);
|
||||||
|
}
|
||||||
|
|
||||||
if (!uploadTarget) {
|
if (!uploadTarget) {
|
||||||
if (type.EqualsASCII(sEventStrings[STRING_loadstart])) {
|
if (type.EqualsASCII(sEventStrings[STRING_loadstart])) {
|
||||||
|
@ -1276,7 +1370,7 @@ XMLHttpRequestPrivate::Notify(JSContext* aCx, Status aStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
XMLHttpRequestPrivate::SetMultipart(JSContext* aCx, jsval *aVp)
|
XMLHttpRequestPrivate::SetMultipart(JSContext* aCx, jsval aOldVal, jsval *aVp)
|
||||||
{
|
{
|
||||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||||
|
|
||||||
|
@ -1306,7 +1400,8 @@ XMLHttpRequestPrivate::SetMultipart(JSContext* aCx, jsval *aVp)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
XMLHttpRequestPrivate::SetMozBackgroundRequest(JSContext* aCx, jsval *aVp)
|
XMLHttpRequestPrivate::SetMozBackgroundRequest(JSContext* aCx, jsval aOldVal,
|
||||||
|
jsval *aVp)
|
||||||
{
|
{
|
||||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||||
|
|
||||||
|
@ -1336,7 +1431,8 @@ XMLHttpRequestPrivate::SetMozBackgroundRequest(JSContext* aCx, jsval *aVp)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
XMLHttpRequestPrivate::SetWithCredentials(JSContext* aCx, jsval *aVp)
|
XMLHttpRequestPrivate::SetWithCredentials(JSContext* aCx, jsval aOldVal,
|
||||||
|
jsval *aVp)
|
||||||
{
|
{
|
||||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||||
|
|
||||||
|
@ -1365,6 +1461,68 @@ XMLHttpRequestPrivate::SetWithCredentials(JSContext* aCx, jsval *aVp)
|
||||||
return true;
|
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
|
bool
|
||||||
XMLHttpRequestPrivate::Abort(JSContext* aCx)
|
XMLHttpRequestPrivate::Abort(JSContext* aCx)
|
||||||
{
|
{
|
||||||
|
@ -1680,8 +1838,8 @@ XMLHttpRequestPrivate::MaybeDispatchPrematureAbortEvents(JSContext* aCx,
|
||||||
NS_ASSERTION(mProxy, "Must have a proxy here!");
|
NS_ASSERTION(mProxy, "Must have a proxy here!");
|
||||||
|
|
||||||
xhr::StateData state = {
|
xhr::StateData state = {
|
||||||
JSVAL_VOID, JSVAL_VOID, JSVAL_VOID, INT_TO_JSVAL(4),
|
JSVAL_VOID, JSVAL_VOID, JSVAL_VOID, INT_TO_JSVAL(4), JSVAL_VOID,
|
||||||
false, false, false, false
|
false, false, false, false, false
|
||||||
};
|
};
|
||||||
|
|
||||||
if (mProxy->mSeenUploadLoadStart) {
|
if (mProxy->mSeenUploadLoadStart) {
|
||||||
|
|
|
@ -111,13 +111,16 @@ public:
|
||||||
Notify(JSContext* aCx, Status aStatus);
|
Notify(JSContext* aCx, Status aStatus);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SetMultipart(JSContext* aCx, jsval *aVp);
|
SetMultipart(JSContext* aCx, jsval aOldVal, jsval *aVp);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SetMozBackgroundRequest(JSContext* aCx, jsval *aVp);
|
SetMozBackgroundRequest(JSContext* aCx, jsval aOldVal, jsval *aVp);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SetWithCredentials(JSContext* aCx, jsval *aVp);
|
SetWithCredentials(JSContext* aCx, jsval aOldVal, jsval *aVp);
|
||||||
|
|
||||||
|
bool
|
||||||
|
SetResponseType(JSContext* aCx, jsval aOldVal, jsval *aVp);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Abort(JSContext* aCx);
|
Abort(JSContext* aCx);
|
||||||
|
|
|
@ -104,6 +104,8 @@ _TEST_FILES = \
|
||||||
throwingOnerror_worker.js \
|
throwingOnerror_worker.js \
|
||||||
test_xhr.html \
|
test_xhr.html \
|
||||||
xhr_worker.js \
|
xhr_worker.js \
|
||||||
|
test_xhr2.html \
|
||||||
|
xhr2_worker.js \
|
||||||
test_xhrAbort.html \
|
test_xhrAbort.html \
|
||||||
xhrAbort_worker.js \
|
xhrAbort_worker.js \
|
||||||
testXHR.txt \
|
testXHR.txt \
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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()");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1647,6 +1647,16 @@ extern JS_PUBLIC_DATA(jsid) JSID_EMPTY;
|
||||||
# define JSID_EMPTY ((jsid)JSID_TYPE_OBJECT)
|
# define JSID_EMPTY ((jsid)JSID_TYPE_OBJECT)
|
||||||
#endif
|
#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. */
|
/* Lock and unlock the GC thing held by a jsval. */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче