mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
224115946f
In the new setup, all per-interface DOM binding files are exported into mozilla/dom. General files not specific to an interface are also exported into mozilla/dom. In terms of namespaces, most things now live in mozilla::dom. Each interface Foo that has generated code has a mozilla::dom::FooBinding namespace for said generated code (and possibly a mozilla::bindings::FooBinding_workers if there's separate codegen for workers). IDL enums are a bit weird: since the name of the enum and the names of its entries all end up in the same namespace, we still generate a C++ namespace with the name of the IDL enum type with "Values" appended to it, with a ::valuelist inside for the actual C++ enum. We then typedef EnumFooValues::valuelist to EnumFoo. That makes it a bit more difficult to refer to the values, but means that values from different enums don't collide with each other. The enums with the proto and constructor IDs in them now live under the mozilla::dom::prototypes and mozilla::dom::constructors namespaces respectively. Again, this lets us deal sanely with the whole "enum value names are flattened into the namespace the enum is in" deal. The main benefit of this setup (and the reason "Binding" got appended to the per-interface namespaces) is that this way "using mozilla::dom" should Just Work for consumers and still allow C++ code to sanely use the IDL interface names for concrete classes, which is fairly desirable. --HG-- rename : dom/bindings/Utils.cpp => dom/bindings/BindingUtils.cpp rename : dom/bindings/Utils.h => dom/bindings/BindingUtils.h
2172 lines
54 KiB
C++
2172 lines
54 KiB
C++
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "XMLHttpRequest.h"
|
|
|
|
#include "nsIDOMEvent.h"
|
|
#include "nsIDOMEventListener.h"
|
|
#include "nsIDOMProgressEvent.h"
|
|
#include "nsIRunnable.h"
|
|
#include "nsIVariant.h"
|
|
#include "nsIXMLHttpRequest.h"
|
|
#include "nsIXPConnect.h"
|
|
|
|
#include "jsfriendapi.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsJSUtils.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "nsXMLHttpRequest.h"
|
|
|
|
#include "Events.h"
|
|
#include "EventTarget.h"
|
|
#include "Exceptions.h"
|
|
#include "File.h"
|
|
#include "RuntimeService.h"
|
|
#include "WorkerPrivate.h"
|
|
#include "XMLHttpRequestUpload.h"
|
|
|
|
#include "DOMBindingInlines.h"
|
|
|
|
USING_WORKERS_NAMESPACE
|
|
|
|
using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
|
|
|
|
// XXX Need to figure this out...
|
|
#define UNCATCHABLE_EXCEPTION NS_ERROR_OUT_OF_MEMORY
|
|
|
|
/**
|
|
* XMLHttpRequest in workers
|
|
*
|
|
* XHR in workers is implemented by proxying calls/events/etc between the
|
|
* worker thread and an nsXMLHttpRequest on the main thread. The glue
|
|
* object here is the Proxy, which lives on both threads. All other objects
|
|
* live on either the main thread (the nsXMLHttpRequest) or the worker thread
|
|
* (the worker and XHR private objects).
|
|
*
|
|
* The main thread XHR is always operated in async mode, even for sync XHR
|
|
* in workers. Calls made on the worker thread are proxied to the main thread
|
|
* synchronously (meaning the worker thread is blocked until the call
|
|
* returns). Each proxied call spins up a sync queue, which captures any
|
|
* synchronously dispatched events and ensures that they run synchronously
|
|
* on the worker as well. Asynchronously dispatched events are posted to the
|
|
* worker thread to run asynchronously. Some of the XHR state is mirrored on
|
|
* the worker thread to avoid needing a cross-thread call on every property
|
|
* access.
|
|
*
|
|
* The XHR private is stored in the private slot of the XHR JSObject on the
|
|
* worker thread. It is destroyed when that JSObject is GCd. The private
|
|
* roots its JSObject while network activity is in progress. It also
|
|
* adds itself as a feature to the worker to give itself a chance to clean up
|
|
* if the worker goes away during an XHR call. It is important that the
|
|
* rooting and feature registration (collectively called pinning) happens at
|
|
* the proper times. If we pin for too long we can cause memory leaks or even
|
|
* shutdown hangs. If we don't pin for long enough we introduce a GC hazard.
|
|
*
|
|
* The XHR is pinned from the time Send is called to roughly the time loadend
|
|
* is received. There are some complications involved with Abort and XHR
|
|
* reuse. We maintain a counter on the main thread of how many times Send was
|
|
* called on this XHR, and we decrement the counter every time we receive a
|
|
* loadend event. When the counter reaches zero we dispatch a runnable to the
|
|
* worker thread to unpin the XHR. We only decrement the counter if the
|
|
* dispatch was successful, because the worker may no longer be accepting
|
|
* regular runnables. In the event that we reach Proxy::Teardown and there
|
|
* the outstanding Send count is still non-zero, we dispatch a control
|
|
* runnable which is guaranteed to run.
|
|
*
|
|
* NB: Some of this could probably be simplified now that we have the
|
|
* inner/outer channel ids.
|
|
*/
|
|
|
|
BEGIN_WORKERS_NAMESPACE
|
|
|
|
class Proxy : public nsIDOMEventListener
|
|
{
|
|
public:
|
|
// Read on multiple threads.
|
|
WorkerPrivate* mWorkerPrivate;
|
|
XMLHttpRequest* mXMLHttpRequestPrivate;
|
|
|
|
// Only touched on the main thread.
|
|
nsRefPtr<nsXMLHttpRequest> mXHR;
|
|
nsCOMPtr<nsIXMLHttpRequestUpload> mXHRUpload;
|
|
PRUint32 mInnerEventStreamId;
|
|
PRUint32 mInnerChannelId;
|
|
PRUint32 mOutstandingSendCount;
|
|
|
|
// Only touched on the worker thread.
|
|
PRUint32 mOuterEventStreamId;
|
|
PRUint32 mOuterChannelId;
|
|
PRUint64 mLastLoaded;
|
|
PRUint64 mLastTotal;
|
|
PRUint64 mLastUploadLoaded;
|
|
PRUint64 mLastUploadTotal;
|
|
bool mIsSyncXHR;
|
|
bool mLastLengthComputable;
|
|
bool mLastUploadLengthComputable;
|
|
bool mSeenLoadStart;
|
|
bool mSeenUploadLoadStart;
|
|
|
|
// Only touched on the main thread.
|
|
PRUint32 mSyncQueueKey;
|
|
PRUint32 mSyncEventResponseSyncQueueKey;
|
|
bool mUploadEventListenersAttached;
|
|
bool mMainThreadSeenLoadStart;
|
|
bool mInOpen;
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIDOMEVENTLISTENER
|
|
|
|
Proxy(XMLHttpRequest* aXHRPrivate)
|
|
: mWorkerPrivate(nsnull), mXMLHttpRequestPrivate(aXHRPrivate),
|
|
mInnerEventStreamId(0), mInnerChannelId(0), mOutstandingSendCount(0),
|
|
mOuterEventStreamId(0), mOuterChannelId(0), mLastLoaded(0), mLastTotal(0),
|
|
mLastUploadLoaded(0), mLastUploadTotal(0), mIsSyncXHR(false),
|
|
mLastLengthComputable(false), mLastUploadLengthComputable(false),
|
|
mSeenLoadStart(false), mSeenUploadLoadStart(false),
|
|
mSyncQueueKey(PR_UINT32_MAX),
|
|
mSyncEventResponseSyncQueueKey(PR_UINT32_MAX),
|
|
mUploadEventListenersAttached(false), mMainThreadSeenLoadStart(false),
|
|
mInOpen(false)
|
|
{ }
|
|
|
|
~Proxy()
|
|
{
|
|
NS_ASSERTION(!mXHR, "Still have an XHR object attached!");
|
|
NS_ASSERTION(!mXHRUpload, "Still have an XHR upload object attached!");
|
|
NS_ASSERTION(!mOutstandingSendCount, "We're dying too early!");
|
|
}
|
|
|
|
bool
|
|
Init()
|
|
{
|
|
AssertIsOnMainThread();
|
|
NS_ASSERTION(mWorkerPrivate, "Must have a worker here!");
|
|
|
|
if (!mXHR) {
|
|
mXHR = new nsXMLHttpRequest();
|
|
|
|
if (NS_FAILED(mXHR->Init(mWorkerPrivate->GetPrincipal(),
|
|
mWorkerPrivate->GetScriptContext(),
|
|
mWorkerPrivate->GetWindow(),
|
|
mWorkerPrivate->GetBaseURI()))) {
|
|
mXHR = nsnull;
|
|
return false;
|
|
}
|
|
|
|
if (NS_FAILED(mXHR->GetUpload(getter_AddRefs(mXHRUpload)))) {
|
|
mXHR = nsnull;
|
|
return false;
|
|
}
|
|
|
|
if (!AddRemoveEventListeners(false, true)) {
|
|
mXHRUpload = nsnull;
|
|
mXHR = nsnull;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
Teardown();
|
|
|
|
bool
|
|
AddRemoveEventListeners(bool aUpload, bool aAdd);
|
|
|
|
void
|
|
Reset()
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
if (mUploadEventListenersAttached) {
|
|
AddRemoveEventListeners(true, false);
|
|
}
|
|
}
|
|
|
|
PRUint32
|
|
GetSyncQueueKey()
|
|
{
|
|
AssertIsOnMainThread();
|
|
return mSyncEventResponseSyncQueueKey == PR_UINT32_MAX ?
|
|
mSyncQueueKey :
|
|
mSyncEventResponseSyncQueueKey;
|
|
}
|
|
|
|
bool
|
|
EventsBypassSyncQueue()
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
return mSyncQueueKey == PR_UINT32_MAX &&
|
|
mSyncEventResponseSyncQueueKey == PR_UINT32_MAX;
|
|
}
|
|
};
|
|
|
|
END_WORKERS_NAMESPACE
|
|
|
|
|
|
namespace {
|
|
|
|
inline void
|
|
ConvertResponseTypeToString(XMLHttpRequestResponseType aType,
|
|
nsString& aString)
|
|
{
|
|
using namespace
|
|
mozilla::dom::XMLHttpRequestResponseTypeValues;
|
|
|
|
size_t index = static_cast<size_t>(aType);
|
|
MOZ_ASSERT(index < ArrayLength(strings), "Codegen gave us a bad value!");
|
|
|
|
aString.AssignASCII(strings[index].value, strings[index].length);
|
|
}
|
|
|
|
inline XMLHttpRequestResponseType
|
|
ConvertStringToResponseType(const nsAString& aString)
|
|
{
|
|
using namespace
|
|
mozilla::dom::XMLHttpRequestResponseTypeValues;
|
|
|
|
for (size_t index = 0; index < ArrayLength(strings) - 1; index++) {
|
|
if (aString.EqualsASCII(strings[index].value, strings[index].length)) {
|
|
return static_cast<XMLHttpRequestResponseType>(index);
|
|
}
|
|
}
|
|
|
|
MOZ_NOT_REACHED("Don't know anything about this response type!");
|
|
return _empty;
|
|
}
|
|
|
|
enum
|
|
{
|
|
STRING_abort = 0,
|
|
STRING_error,
|
|
STRING_load,
|
|
STRING_loadstart,
|
|
STRING_progress,
|
|
STRING_timeout,
|
|
STRING_readystatechange,
|
|
STRING_loadend,
|
|
|
|
STRING_COUNT,
|
|
|
|
STRING_LAST_XHR = STRING_loadend,
|
|
STRING_LAST_EVENTTARGET = STRING_timeout
|
|
};
|
|
|
|
JS_STATIC_ASSERT(STRING_LAST_XHR >= STRING_LAST_EVENTTARGET);
|
|
JS_STATIC_ASSERT(STRING_LAST_XHR == STRING_COUNT - 1);
|
|
|
|
const char* const sEventStrings[] = {
|
|
// nsIXMLHttpRequestEventTarget event types, supported by both XHR and Upload.
|
|
"abort",
|
|
"error",
|
|
"load",
|
|
"loadstart",
|
|
"progress",
|
|
"timeout",
|
|
|
|
// nsIXMLHttpRequest event types, supported only by XHR.
|
|
"readystatechange",
|
|
"loadend",
|
|
};
|
|
|
|
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(sEventStrings) == STRING_COUNT);
|
|
|
|
class MainThreadSyncRunnable : public WorkerSyncRunnable
|
|
{
|
|
public:
|
|
MainThreadSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
|
ClearingBehavior aClearingBehavior,
|
|
PRUint32 aSyncQueueKey,
|
|
bool aBypassSyncEventQueue)
|
|
: WorkerSyncRunnable(aWorkerPrivate, aSyncQueueKey, aBypassSyncEventQueue,
|
|
aClearingBehavior)
|
|
{
|
|
AssertIsOnMainThread();
|
|
}
|
|
|
|
bool
|
|
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
{
|
|
AssertIsOnMainThread();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
|
bool aDispatchResult)
|
|
{
|
|
AssertIsOnMainThread();
|
|
}
|
|
};
|
|
|
|
class MainThreadProxyRunnable : public MainThreadSyncRunnable
|
|
{
|
|
protected:
|
|
nsRefPtr<Proxy> mProxy;
|
|
|
|
public:
|
|
MainThreadProxyRunnable(WorkerPrivate* aWorkerPrivate,
|
|
ClearingBehavior aClearingBehavior, Proxy* aProxy)
|
|
: MainThreadSyncRunnable(aWorkerPrivate, aClearingBehavior,
|
|
aProxy->GetSyncQueueKey(),
|
|
aProxy->EventsBypassSyncQueue()),
|
|
mProxy(aProxy)
|
|
{ }
|
|
};
|
|
|
|
class XHRUnpinRunnable : public WorkerControlRunnable
|
|
{
|
|
XMLHttpRequest* mXMLHttpRequestPrivate;
|
|
|
|
public:
|
|
XHRUnpinRunnable(WorkerPrivate* aWorkerPrivate,
|
|
XMLHttpRequest* aXHRPrivate)
|
|
: WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
|
|
mXMLHttpRequestPrivate(aXHRPrivate)
|
|
{ }
|
|
|
|
bool
|
|
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
{
|
|
AssertIsOnMainThread();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
|
bool aDispatchResult)
|
|
{
|
|
AssertIsOnMainThread();
|
|
}
|
|
|
|
bool
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
{
|
|
mXMLHttpRequestPrivate->Unpin();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class AsyncTeardownRunnable : public nsRunnable
|
|
{
|
|
nsRefPtr<Proxy> mProxy;
|
|
|
|
public:
|
|
AsyncTeardownRunnable(Proxy* aProxy)
|
|
{
|
|
mProxy = aProxy;
|
|
NS_ASSERTION(mProxy, "Null proxy!");
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
mProxy->Teardown();
|
|
mProxy = nsnull;
|
|
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class LoadStartDetectionRunnable : public nsIRunnable,
|
|
public nsIDOMEventListener
|
|
{
|
|
WorkerPrivate* mWorkerPrivate;
|
|
nsRefPtr<Proxy> mProxy;
|
|
nsRefPtr<nsXMLHttpRequest> mXHR;
|
|
XMLHttpRequest* mXMLHttpRequestPrivate;
|
|
nsString mEventType;
|
|
bool mReceivedLoadStart;
|
|
PRUint32 mChannelId;
|
|
|
|
class ProxyCompleteRunnable : public MainThreadProxyRunnable
|
|
{
|
|
XMLHttpRequest* mXMLHttpRequestPrivate;
|
|
PRUint32 mChannelId;
|
|
|
|
public:
|
|
ProxyCompleteRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
XMLHttpRequest* aXHRPrivate, PRUint32 aChannelId)
|
|
: MainThreadProxyRunnable(aWorkerPrivate, RunWhenClearing, aProxy),
|
|
mXMLHttpRequestPrivate(aXHRPrivate), mChannelId(aChannelId)
|
|
{ }
|
|
|
|
bool
|
|
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
{
|
|
AssertIsOnMainThread();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
|
bool aDispatchResult)
|
|
{
|
|
AssertIsOnMainThread();
|
|
}
|
|
|
|
bool
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
{
|
|
if (mChannelId != mProxy->mOuterChannelId) {
|
|
// Threads raced, this event is now obsolete.
|
|
return true;
|
|
}
|
|
|
|
if (mSyncQueueKey != PR_UINT32_MAX) {
|
|
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, true);
|
|
}
|
|
|
|
mXMLHttpRequestPrivate->Unpin();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
LoadStartDetectionRunnable(Proxy* aProxy, XMLHttpRequest* aXHRPrivate)
|
|
: mWorkerPrivate(aProxy->mWorkerPrivate), mProxy(aProxy), mXHR(aProxy->mXHR),
|
|
mXMLHttpRequestPrivate(aXHRPrivate), mReceivedLoadStart(false),
|
|
mChannelId(mProxy->mInnerChannelId)
|
|
{
|
|
AssertIsOnMainThread();
|
|
mEventType.AssignWithConversion(sEventStrings[STRING_loadstart]);
|
|
}
|
|
|
|
~LoadStartDetectionRunnable()
|
|
{
|
|
AssertIsOnMainThread();
|
|
}
|
|
|
|
bool
|
|
RegisterAndDispatch()
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
if (NS_FAILED(mXHR->AddEventListener(mEventType, this, false, false, 2))) {
|
|
NS_WARNING("Failed to add event listener!");
|
|
return false;
|
|
}
|
|
|
|
return NS_SUCCEEDED(NS_DispatchToCurrentThread(this));
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Run()
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
if (NS_FAILED(mXHR->RemoveEventListener(mEventType, this, false))) {
|
|
NS_WARNING("Failed to remove event listener!");
|
|
}
|
|
|
|
if (!mReceivedLoadStart) {
|
|
if (mProxy->mOutstandingSendCount > 1) {
|
|
mProxy->mOutstandingSendCount--;
|
|
} else if (mProxy->mOutstandingSendCount == 1) {
|
|
mProxy->Reset();
|
|
|
|
nsRefPtr<ProxyCompleteRunnable> runnable =
|
|
new ProxyCompleteRunnable(mWorkerPrivate, mProxy,
|
|
mXMLHttpRequestPrivate,
|
|
mChannelId);
|
|
if (runnable->Dispatch(nsnull)) {
|
|
mProxy->mWorkerPrivate = nsnull;
|
|
mProxy->mOutstandingSendCount--;
|
|
}
|
|
}
|
|
}
|
|
|
|
mProxy = nsnull;
|
|
mXHR = nsnull;
|
|
mXMLHttpRequestPrivate = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD
|
|
HandleEvent(nsIDOMEvent* aEvent)
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
nsString type;
|
|
if (NS_SUCCEEDED(aEvent->GetType(type))) {
|
|
NS_ASSERTION(type == mEventType, "Unexpected event type!");
|
|
}
|
|
else {
|
|
NS_WARNING("Failed to get event type!");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
mReceivedLoadStart = true;
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS2(LoadStartDetectionRunnable, nsIRunnable, nsIDOMEventListener)
|
|
|
|
class EventRunnable : public MainThreadProxyRunnable
|
|
{
|
|
nsString mType;
|
|
nsString mResponseType;
|
|
JSAutoStructuredCloneBuffer mResponseBuffer;
|
|
nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
|
|
jsval mResponse;
|
|
nsString mResponseText;
|
|
nsString mStatusText;
|
|
PRUint64 mLoaded;
|
|
PRUint64 mTotal;
|
|
PRUint32 mEventStreamId;
|
|
PRUint32 mStatus;
|
|
PRUint16 mReadyState;
|
|
bool mUploadEvent;
|
|
bool mProgressEvent;
|
|
bool mLengthComputable;
|
|
nsresult mResponseTextResult;
|
|
nsresult mStatusResult;
|
|
nsresult mResponseResult;
|
|
|
|
public:
|
|
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
|
|
bool aLengthComputable, PRUint64 aLoaded, PRUint64 aTotal)
|
|
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, SkipWhenClearing, aProxy),
|
|
mType(aType), mResponse(JSVAL_VOID), mLoaded(aLoaded), mTotal(aTotal),
|
|
mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
|
|
mUploadEvent(aUploadEvent), mProgressEvent(true),
|
|
mLengthComputable(aLengthComputable), mResponseTextResult(NS_OK),
|
|
mStatusResult(NS_OK), mResponseResult(NS_OK)
|
|
{ }
|
|
|
|
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
|
|
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, SkipWhenClearing, aProxy),
|
|
mType(aType), mResponse(JSVAL_VOID), mLoaded(0), mTotal(0),
|
|
mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
|
|
mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
|
|
mResponseTextResult(NS_OK), mStatusResult(NS_OK), mResponseResult(NS_OK)
|
|
{ }
|
|
|
|
bool
|
|
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
{
|
|
nsRefPtr<nsXMLHttpRequest>& xhr = mProxy->mXHR;
|
|
NS_ASSERTION(xhr, "Must have an XHR here!");
|
|
|
|
if (NS_FAILED(xhr->GetResponseType(mResponseType))) {
|
|
NS_ERROR("This should never fail!");
|
|
}
|
|
|
|
mResponseTextResult = xhr->GetResponseText(mResponseText);
|
|
if (NS_SUCCEEDED(mResponseTextResult)) {
|
|
mResponseResult = mResponseTextResult;
|
|
if (mResponseText.IsVoid()) {
|
|
mResponse = JSVAL_NULL;
|
|
}
|
|
}
|
|
else {
|
|
jsval response;
|
|
mResponseResult = xhr->GetResponse(aCx, &response);
|
|
if (NS_SUCCEEDED(mResponseResult)) {
|
|
if (JSVAL_IS_UNIVERSAL(response)) {
|
|
mResponse = response;
|
|
}
|
|
else {
|
|
// 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_WARNING("Failed to clone response!");
|
|
mResponseResult = NS_ERROR_DOM_DATA_CLONE_ERR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mStatusResult = xhr->GetStatus(&mStatus);
|
|
|
|
xhr->GetStatusText(mStatusText);
|
|
|
|
mReadyState = xhr->GetReadyState();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
{
|
|
if (mEventStreamId != mProxy->mOuterEventStreamId) {
|
|
// Threads raced, this event is now obsolete.
|
|
return true;
|
|
}
|
|
|
|
if (!mProxy->mXMLHttpRequestPrivate) {
|
|
// Object was finalized, bail.
|
|
return true;
|
|
}
|
|
|
|
if (mType.EqualsASCII(sEventStrings[STRING_loadstart])) {
|
|
if (mUploadEvent) {
|
|
mProxy->mSeenUploadLoadStart = true;
|
|
}
|
|
else {
|
|
mProxy->mSeenLoadStart = true;
|
|
}
|
|
}
|
|
else if (mType.EqualsASCII(sEventStrings[STRING_loadend])) {
|
|
if (mUploadEvent) {
|
|
mProxy->mSeenUploadLoadStart = false;
|
|
}
|
|
else {
|
|
mProxy->mSeenLoadStart = false;
|
|
}
|
|
}
|
|
else if (mType.EqualsASCII(sEventStrings[STRING_abort])) {
|
|
if ((mUploadEvent && !mProxy->mSeenUploadLoadStart) ||
|
|
(!mUploadEvent && !mProxy->mSeenLoadStart)) {
|
|
// We've already dispatched premature abort events.
|
|
return true;
|
|
}
|
|
}
|
|
else if (mType.EqualsASCII(sEventStrings[STRING_readystatechange])) {
|
|
if (mReadyState == 4 && !mUploadEvent && !mProxy->mSeenLoadStart) {
|
|
// We've already dispatched premature abort events.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (mProgressEvent) {
|
|
// Cache these for premature abort events.
|
|
if (mUploadEvent) {
|
|
mProxy->mLastUploadLengthComputable = mLengthComputable;
|
|
mProxy->mLastUploadLoaded = mLoaded;
|
|
mProxy->mLastUploadTotal = mTotal;
|
|
}
|
|
else {
|
|
mProxy->mLastLengthComputable = mLengthComputable;
|
|
mProxy->mLastLoaded = mLoaded;
|
|
mProxy->mLastTotal = mTotal;
|
|
}
|
|
}
|
|
|
|
XMLHttpRequest::StateData state;
|
|
|
|
state.mResponseTextResult = mResponseTextResult;
|
|
state.mResponseText = mResponseText;
|
|
|
|
if (NS_SUCCEEDED(mResponseTextResult)) {
|
|
MOZ_ASSERT(JSVAL_IS_VOID(mResponse) || JSVAL_IS_NULL(mResponse));
|
|
state.mResponseResult = mResponseTextResult;
|
|
state.mResponse = mResponse;
|
|
}
|
|
else {
|
|
state.mResponseResult = mResponseResult;
|
|
|
|
if (NS_SUCCEEDED(mResponseResult)) {
|
|
if (mResponseBuffer.data()) {
|
|
MOZ_ASSERT(JSVAL_IS_VOID(mResponse));
|
|
|
|
JSAutoStructuredCloneBuffer responseBuffer;
|
|
mResponseBuffer.swap(responseBuffer);
|
|
|
|
JSStructuredCloneCallbacks* callbacks =
|
|
aWorkerPrivate->IsChromeWorker() ?
|
|
ChromeWorkerStructuredCloneCallbacks(false) :
|
|
WorkerStructuredCloneCallbacks(false);
|
|
|
|
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
|
|
clonedObjects.SwapElements(mClonedObjects);
|
|
|
|
jsval response;
|
|
if (!responseBuffer.read(aCx, &response, callbacks, &clonedObjects)) {
|
|
return false;
|
|
}
|
|
|
|
state.mResponse = response;
|
|
}
|
|
else {
|
|
state.mResponse = mResponse;
|
|
}
|
|
}
|
|
}
|
|
|
|
state.mStatusResult = mStatusResult;
|
|
state.mStatus = mStatus;
|
|
|
|
state.mStatusText = mStatusText;
|
|
|
|
state.mReadyState = mReadyState;
|
|
|
|
XMLHttpRequest* xhr = mProxy->mXMLHttpRequestPrivate;
|
|
xhr->UpdateState(state);
|
|
|
|
if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
|
|
return true;
|
|
}
|
|
|
|
JSString* type = JS_NewUCStringCopyN(aCx, mType.get(), mType.Length());
|
|
if (!type) {
|
|
return false;
|
|
}
|
|
|
|
JSObject* event = mProgressEvent ?
|
|
events::CreateProgressEvent(aCx, type, mLengthComputable,
|
|
mLoaded, mTotal) :
|
|
events::CreateGenericEvent(aCx, type, false, false,
|
|
false);
|
|
if (!event) {
|
|
return false;
|
|
}
|
|
|
|
JSObject* target = mUploadEvent ?
|
|
xhr->GetUploadObjectNoCreate()->GetJSObject() :
|
|
xhr->GetJSObject();
|
|
MOZ_ASSERT(target);
|
|
|
|
bool dummy;
|
|
if (!events::DispatchEventToTarget(aCx, target, event, &dummy)) {
|
|
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->NullResponseText();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class WorkerThreadProxySyncRunnable : public nsRunnable
|
|
{
|
|
protected:
|
|
WorkerPrivate* mWorkerPrivate;
|
|
nsRefPtr<Proxy> mProxy;
|
|
PRUint32 mSyncQueueKey;
|
|
|
|
private:
|
|
class ResponseRunnable : public MainThreadProxyRunnable
|
|
{
|
|
PRUint32 mSyncQueueKey;
|
|
nsresult mErrorCode;
|
|
|
|
public:
|
|
ResponseRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
PRUint32 aSyncQueueKey, nsresult aErrorCode)
|
|
: MainThreadProxyRunnable(aWorkerPrivate, SkipWhenClearing, aProxy),
|
|
mSyncQueueKey(aSyncQueueKey), mErrorCode(aErrorCode)
|
|
{
|
|
NS_ASSERTION(aProxy, "Don't hand me a null proxy!");
|
|
}
|
|
|
|
bool
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
{
|
|
if (NS_FAILED(mErrorCode)) {
|
|
ThrowDOMExceptionForNSResult(aCx, mErrorCode);
|
|
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, false);
|
|
}
|
|
else {
|
|
aWorkerPrivate->StopSyncLoop(mSyncQueueKey, true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
public:
|
|
WorkerThreadProxySyncRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
|
|
: mWorkerPrivate(aWorkerPrivate), mProxy(aProxy), mSyncQueueKey(0)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
NS_ASSERTION(aProxy, "Don't hand me a null proxy!");
|
|
}
|
|
|
|
bool
|
|
Dispatch(JSContext* aCx)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
mSyncQueueKey = mWorkerPrivate->CreateNewSyncLoop();
|
|
|
|
if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
|
|
JS_ReportError(aCx, "Failed to dispatch to main thread!");
|
|
return false;
|
|
}
|
|
|
|
if (!mWorkerPrivate->RunSyncLoop(aCx, mSyncQueueKey)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual nsresult
|
|
MainThreadRun() = 0;
|
|
|
|
NS_IMETHOD
|
|
Run()
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
PRUint32 oldSyncQueueKey = mProxy->mSyncEventResponseSyncQueueKey;
|
|
mProxy->mSyncEventResponseSyncQueueKey = mSyncQueueKey;
|
|
|
|
nsresult rv = MainThreadRun();
|
|
|
|
nsRefPtr<ResponseRunnable> response =
|
|
new ResponseRunnable(mWorkerPrivate, mProxy, mSyncQueueKey, rv);
|
|
if (!response->Dispatch(nsnull)) {
|
|
NS_WARNING("Failed to dispatch response!");
|
|
}
|
|
|
|
mProxy->mSyncEventResponseSyncQueueKey = oldSyncQueueKey;
|
|
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class SyncTeardownRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
public:
|
|
SyncTeardownRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
|
|
{
|
|
MOZ_ASSERT(aWorkerPrivate);
|
|
MOZ_ASSERT(aProxy);
|
|
}
|
|
|
|
virtual nsresult
|
|
MainThreadRun()
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
mProxy->Teardown();
|
|
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class SetMultipartRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
bool mValue;
|
|
|
|
public:
|
|
SetMultipartRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
bool aValue)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
return mProxy->mXHR->SetMultipart(mValue);
|
|
}
|
|
};
|
|
|
|
class SetBackgroundRequestRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
bool mValue;
|
|
|
|
public:
|
|
SetBackgroundRequestRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
bool aValue)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
return mProxy->mXHR->SetMozBackgroundRequest(mValue);
|
|
}
|
|
};
|
|
|
|
class SetWithCredentialsRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
bool mValue;
|
|
|
|
public:
|
|
SetWithCredentialsRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
bool aValue)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
return mProxy->mXHR->SetWithCredentials(mValue);
|
|
}
|
|
};
|
|
|
|
class SetResponseTypeRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
nsString mResponseType;
|
|
|
|
public:
|
|
SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
const nsAString& aResponseType)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
|
|
mResponseType(aResponseType)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
nsresult rv = mProxy->mXHR->SetResponseType(mResponseType);
|
|
mResponseType.Truncate();
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = mProxy->mXHR->GetResponseType(mResponseType);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
GetResponseType(nsAString& aResponseType) {
|
|
aResponseType.Assign(mResponseType);
|
|
}
|
|
};
|
|
|
|
class SetTimeoutRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
PRUint32 mTimeout;
|
|
|
|
public:
|
|
SetTimeoutRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
PRUint32 aTimeout)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
|
|
mTimeout(aTimeout)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
return mProxy->mXHR->SetTimeout(mTimeout);
|
|
}
|
|
};
|
|
|
|
class AbortRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
public:
|
|
AbortRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
mProxy->mInnerEventStreamId++;
|
|
|
|
WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
|
|
mProxy->mWorkerPrivate = mWorkerPrivate;
|
|
|
|
mProxy->mXHR->Abort();
|
|
|
|
mProxy->mWorkerPrivate = oldWorker;
|
|
|
|
mProxy->Reset();
|
|
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class GetAllResponseHeadersRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
nsString& mResponseHeaders;
|
|
|
|
public:
|
|
GetAllResponseHeadersRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
nsString& aResponseHeaders)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
|
|
mResponseHeaders(aResponseHeaders)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
mProxy->mXHR->GetAllResponseHeaders(mResponseHeaders);
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class GetResponseHeaderRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
const nsCString mHeader;
|
|
nsCString& mValue;
|
|
|
|
public:
|
|
GetResponseHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
const nsCString& aHeader, nsCString& aValue)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mHeader(aHeader),
|
|
mValue(aValue)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
return mProxy->mXHR->GetResponseHeader(mHeader, mValue);
|
|
}
|
|
};
|
|
|
|
class OpenRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
nsCString mMethod;
|
|
nsCString mURL;
|
|
nsString mUser;
|
|
nsString mPassword;
|
|
bool mMultipart;
|
|
bool mBackgroundRequest;
|
|
bool mWithCredentials;
|
|
PRUint32 mTimeout;
|
|
|
|
public:
|
|
OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
const nsACString& aMethod, const nsACString& aURL,
|
|
const nsAString& aUser, const nsAString& aPassword,
|
|
bool aMultipart, bool aBackgroundRequest, bool aWithCredentials,
|
|
PRUint32 aTimeout)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMethod(aMethod),
|
|
mURL(aURL), mUser(aUser), mPassword(aPassword), mMultipart(aMultipart),
|
|
mBackgroundRequest(aBackgroundRequest), mWithCredentials(aWithCredentials),
|
|
mTimeout(aTimeout)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
|
|
mProxy->mWorkerPrivate = mWorkerPrivate;
|
|
|
|
nsresult rv = MainThreadRunInternal();
|
|
|
|
mProxy->mWorkerPrivate = oldWorker;
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
MainThreadRunInternal()
|
|
{
|
|
if (!mProxy->Init()) {
|
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
}
|
|
|
|
nsresult rv;
|
|
|
|
if (mMultipart) {
|
|
rv = mProxy->mXHR->SetMultipart(mMultipart);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (mBackgroundRequest) {
|
|
rv = mProxy->mXHR->SetMozBackgroundRequest(mBackgroundRequest);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (mWithCredentials) {
|
|
rv = mProxy->mXHR->SetWithCredentials(mWithCredentials);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (mTimeout) {
|
|
rv = mProxy->mXHR->SetTimeout(mTimeout);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
NS_ASSERTION(!mProxy->mInOpen, "Reentrancy is bad!");
|
|
mProxy->mInOpen = true;
|
|
|
|
rv = mProxy->mXHR->Open(mMethod, mURL, true, mUser, mPassword, 1);
|
|
|
|
NS_ASSERTION(mProxy->mInOpen, "Reentrancy is bad!");
|
|
mProxy->mInOpen = false;
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = mProxy->mXHR->SetResponseType(NS_LITERAL_STRING("text"));
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
};
|
|
|
|
class SendRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
nsString mStringBody;
|
|
JSAutoStructuredCloneBuffer mBody;
|
|
nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
|
|
PRUint32 mSyncQueueKey;
|
|
bool mHasUploadListeners;
|
|
|
|
public:
|
|
SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
const nsAString& aStringBody, JSAutoStructuredCloneBuffer& aBody,
|
|
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
|
|
PRUint32 aSyncQueueKey, bool aHasUploadListeners)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
|
|
mStringBody(aStringBody), mSyncQueueKey(aSyncQueueKey),
|
|
mHasUploadListeners(aHasUploadListeners)
|
|
{
|
|
mBody.swap(aBody);
|
|
mClonedObjects.SwapElements(aClonedObjects);
|
|
}
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
nsCOMPtr<nsIVariant> variant;
|
|
RuntimeService::AutoSafeJSContext cx;
|
|
|
|
if (mBody.data()) {
|
|
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
|
NS_ASSERTION(xpc, "This should never be null!");
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
JSStructuredCloneCallbacks* callbacks =
|
|
mWorkerPrivate->IsChromeWorker() ?
|
|
ChromeWorkerStructuredCloneCallbacks(true) :
|
|
WorkerStructuredCloneCallbacks(true);
|
|
|
|
jsval body;
|
|
if (mBody.read(cx, &body, callbacks, &mClonedObjects)) {
|
|
if (NS_FAILED(xpc->JSValToVariant(cx, &body,
|
|
getter_AddRefs(variant)))) {
|
|
rv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
}
|
|
}
|
|
else {
|
|
rv = NS_ERROR_DOM_DATA_CLONE_ERR;
|
|
}
|
|
|
|
mBody.clear();
|
|
mClonedObjects.Clear();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
else {
|
|
nsCOMPtr<nsIWritableVariant> wvariant =
|
|
do_CreateInstance(NS_VARIANT_CONTRACTID);
|
|
NS_ENSURE_TRUE(wvariant, NS_ERROR_UNEXPECTED);
|
|
|
|
if (NS_FAILED(wvariant->SetAsAString(mStringBody))) {
|
|
NS_ERROR("This should never fail!");
|
|
}
|
|
|
|
variant = wvariant;
|
|
}
|
|
|
|
NS_ASSERTION(!mProxy->mWorkerPrivate, "Should be null!");
|
|
mProxy->mWorkerPrivate = mWorkerPrivate;
|
|
|
|
NS_ASSERTION(mProxy->mSyncQueueKey == PR_UINT32_MAX, "Should be unset!");
|
|
mProxy->mSyncQueueKey = mSyncQueueKey;
|
|
|
|
if (mHasUploadListeners) {
|
|
NS_ASSERTION(!mProxy->mUploadEventListenersAttached, "Huh?!");
|
|
if (!mProxy->AddRemoveEventListeners(true, true)) {
|
|
NS_ERROR("This should never fail!");
|
|
}
|
|
}
|
|
|
|
mProxy->mInnerChannelId++;
|
|
|
|
nsresult rv = mProxy->mXHR->Send(variant, cx);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
mProxy->mOutstandingSendCount++;
|
|
|
|
if (!mHasUploadListeners) {
|
|
NS_ASSERTION(!mProxy->mUploadEventListenersAttached, "Huh?!");
|
|
if (!mProxy->AddRemoveEventListeners(true, true)) {
|
|
NS_ERROR("This should never fail!");
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
};
|
|
|
|
class SetRequestHeaderRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
nsCString mHeader;
|
|
nsCString mValue;
|
|
|
|
public:
|
|
SetRequestHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
const nsCString& aHeader, const nsCString& aValue)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mHeader(aHeader),
|
|
mValue(aValue)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
return mProxy->mXHR->SetRequestHeader(mHeader, mValue);
|
|
}
|
|
};
|
|
|
|
class OverrideMimeTypeRunnable : public WorkerThreadProxySyncRunnable
|
|
{
|
|
nsString mMimeType;
|
|
|
|
public:
|
|
OverrideMimeTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
|
|
const nsAString& aMimeType)
|
|
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMimeType(aMimeType)
|
|
{ }
|
|
|
|
nsresult
|
|
MainThreadRun()
|
|
{
|
|
mProxy->mXHR->OverrideMimeType(mMimeType);
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class AutoUnpinXHR
|
|
{
|
|
public:
|
|
AutoUnpinXHR(XMLHttpRequest* aXMLHttpRequestPrivate)
|
|
: mXMLHttpRequestPrivate(aXMLHttpRequestPrivate)
|
|
{
|
|
MOZ_ASSERT(aXMLHttpRequestPrivate);
|
|
}
|
|
|
|
~AutoUnpinXHR()
|
|
{
|
|
if (mXMLHttpRequestPrivate) {
|
|
mXMLHttpRequestPrivate->Unpin();
|
|
}
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
mXMLHttpRequestPrivate = NULL;
|
|
}
|
|
|
|
private:
|
|
XMLHttpRequest* mXMLHttpRequestPrivate;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
void
|
|
Proxy::Teardown()
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
if (mXHR) {
|
|
Reset();
|
|
|
|
// NB: We are intentionally dropping events coming from xhr.abort on the
|
|
// floor.
|
|
AddRemoveEventListeners(false, false);
|
|
mXHR->Abort();
|
|
|
|
if (mOutstandingSendCount) {
|
|
nsRefPtr<XHRUnpinRunnable> runnable =
|
|
new XHRUnpinRunnable(mWorkerPrivate, mXMLHttpRequestPrivate);
|
|
if (!runnable->Dispatch(nsnull)) {
|
|
NS_RUNTIMEABORT("We're going to hang at shutdown anyways.");
|
|
}
|
|
|
|
mWorkerPrivate = nsnull;
|
|
mOutstandingSendCount = 0;
|
|
}
|
|
|
|
mXHRUpload = nsnull;
|
|
mXHR = nsnull;
|
|
}
|
|
}
|
|
|
|
bool
|
|
Proxy::AddRemoveEventListeners(bool aUpload, bool aAdd)
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
NS_ASSERTION(!aUpload ||
|
|
(mUploadEventListenersAttached && !aAdd) ||
|
|
(!mUploadEventListenersAttached && aAdd),
|
|
"Messed up logic for upload listeners!");
|
|
|
|
nsCOMPtr<nsIDOMEventTarget> target =
|
|
aUpload ?
|
|
do_QueryInterface(mXHRUpload) :
|
|
do_QueryInterface(static_cast<nsIXMLHttpRequest*>(mXHR.get()));
|
|
NS_ASSERTION(target, "This should never fail!");
|
|
|
|
PRUint32 lastEventType = aUpload ? STRING_LAST_EVENTTARGET : STRING_LAST_XHR;
|
|
|
|
nsAutoString eventType;
|
|
for (PRUint32 index = 0; index <= lastEventType; index++) {
|
|
eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]);
|
|
if (aAdd) {
|
|
if (NS_FAILED(target->AddEventListener(eventType, this, false))) {
|
|
return false;
|
|
}
|
|
}
|
|
else if (NS_FAILED(target->RemoveEventListener(eventType, this, false))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (aUpload) {
|
|
mUploadEventListenersAttached = aAdd;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS1(Proxy, nsIDOMEventListener)
|
|
|
|
NS_IMETHODIMP
|
|
Proxy::HandleEvent(nsIDOMEvent* aEvent)
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
if (!mWorkerPrivate || !mXMLHttpRequestPrivate) {
|
|
NS_ERROR("Shouldn't get here!");
|
|
return NS_OK;
|
|
}
|
|
|
|
nsString type;
|
|
if (NS_FAILED(aEvent->GetType(type))) {
|
|
NS_WARNING("Failed to get event type!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMEventTarget> target;
|
|
if (NS_FAILED(aEvent->GetTarget(getter_AddRefs(target)))) {
|
|
NS_WARNING("Failed to get target!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsIXMLHttpRequestUpload> uploadTarget = do_QueryInterface(target);
|
|
nsCOMPtr<nsIDOMProgressEvent> progressEvent = do_QueryInterface(aEvent);
|
|
|
|
nsRefPtr<EventRunnable> runnable;
|
|
|
|
if (mInOpen && type.EqualsASCII(sEventStrings[STRING_readystatechange])) {
|
|
PRUint16 readyState = 0;
|
|
if (NS_SUCCEEDED(mXHR->GetReadyState(&readyState)) &&
|
|
readyState == nsIXMLHttpRequest::OPENED) {
|
|
mInnerEventStreamId++;
|
|
}
|
|
}
|
|
|
|
if (progressEvent) {
|
|
bool lengthComputable;
|
|
PRUint64 loaded, total;
|
|
if (NS_FAILED(progressEvent->GetLengthComputable(&lengthComputable)) ||
|
|
NS_FAILED(progressEvent->GetLoaded(&loaded)) ||
|
|
NS_FAILED(progressEvent->GetTotal(&total))) {
|
|
NS_WARNING("Bad progress event!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
runnable = new EventRunnable(this, !!uploadTarget, type, lengthComputable,
|
|
loaded, total);
|
|
}
|
|
else {
|
|
runnable = new EventRunnable(this, !!uploadTarget, type);
|
|
}
|
|
|
|
{
|
|
RuntimeService::AutoSafeJSContext cx;
|
|
runnable->Dispatch(cx);
|
|
}
|
|
|
|
if (!uploadTarget) {
|
|
if (type.EqualsASCII(sEventStrings[STRING_loadstart])) {
|
|
NS_ASSERTION(!mMainThreadSeenLoadStart, "Huh?!");
|
|
mMainThreadSeenLoadStart = true;
|
|
}
|
|
else if (mMainThreadSeenLoadStart &&
|
|
type.EqualsASCII(sEventStrings[STRING_loadend])) {
|
|
mMainThreadSeenLoadStart = false;
|
|
|
|
nsRefPtr<LoadStartDetectionRunnable> runnable =
|
|
new LoadStartDetectionRunnable(this, mXMLHttpRequestPrivate);
|
|
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
|
|
NS_WARNING("Failed to dispatch LoadStartDetectionRunnable!");
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
XMLHttpRequest::XMLHttpRequest(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|
: XMLHttpRequestEventTarget(aCx), mJSObject(NULL), mUpload(NULL),
|
|
mWorkerPrivate(aWorkerPrivate),
|
|
mResponseType(XMLHttpRequestResponseTypeValues::text), mTimeout(0),
|
|
mJSObjectRooted(false), mMultipart(false), mBackgroundRequest(false),
|
|
mWithCredentials(false), mCanceled(false)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
}
|
|
|
|
XMLHttpRequest::~XMLHttpRequest()
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
MOZ_ASSERT(!mJSObjectRooted);
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::_Trace(JSTracer* aTrc)
|
|
{
|
|
if (mUpload) {
|
|
JS_CALL_OBJECT_TRACER(aTrc, mUpload->GetJSObject(), "mUpload");
|
|
}
|
|
JS_CALL_VALUE_TRACER(aTrc, mStateData.mResponse, "mResponse");
|
|
XMLHttpRequestEventTarget::_Trace(aTrc);
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::_Finalize(JSFreeOp* aFop)
|
|
{
|
|
ReleaseProxy(XHRIsGoingAway);
|
|
XMLHttpRequestEventTarget::_Finalize(aFop);
|
|
}
|
|
|
|
// static
|
|
XMLHttpRequest*
|
|
XMLHttpRequest::_Constructor(JSContext* aCx, JSObject* aGlobal, nsresult& aRv)
|
|
{
|
|
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
|
MOZ_ASSERT(workerPrivate);
|
|
|
|
nsRefPtr<XMLHttpRequest> xhr = new XMLHttpRequest(aCx, workerPrivate);
|
|
|
|
if (!Wrap(aCx, aGlobal, xhr)) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return NULL;
|
|
}
|
|
|
|
xhr->mJSObject = xhr->GetJSObject();
|
|
return xhr;
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::ReleaseProxy(ReleaseType aType)
|
|
{
|
|
// Can't assert that we're on the worker thread here because mWorkerPrivate
|
|
// may be gone.
|
|
|
|
if (mProxy) {
|
|
if (aType == XHRIsGoingAway) {
|
|
// We're in a GC finalizer, so we can't do a sync call here (and we don't
|
|
// need to).
|
|
nsRefPtr<AsyncTeardownRunnable> runnable =
|
|
new AsyncTeardownRunnable(mProxy);
|
|
mProxy = nsnull;
|
|
|
|
if (NS_DispatchToMainThread(runnable)) {
|
|
NS_ERROR("Failed to dispatch teardown runnable!");
|
|
}
|
|
} else {
|
|
// This isn't necessary if the worker is going away or the XHR is going
|
|
// away.
|
|
if (aType == Default) {
|
|
// Don't let any more events run.
|
|
mProxy->mOuterEventStreamId++;
|
|
}
|
|
|
|
// We need to make a sync call here.
|
|
nsRefPtr<SyncTeardownRunnable> runnable =
|
|
new SyncTeardownRunnable(mWorkerPrivate, mProxy);
|
|
mProxy = nsnull;
|
|
|
|
if (!runnable->Dispatch(nsnull)) {
|
|
NS_ERROR("Failed to dispatch teardown runnable!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::MaybePin(nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mJSObjectRooted) {
|
|
return;
|
|
}
|
|
|
|
JSContext* cx = GetJSContext();
|
|
|
|
if (!JS_AddNamedObjectRoot(cx, &mJSObject, "XMLHttpRequest mJSObject")) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
|
|
if (!mWorkerPrivate->AddFeature(cx, this)) {
|
|
JS_RemoveObjectRoot(cx, &mJSObject);
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
|
|
mJSObjectRooted = true;
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::MaybeDispatchPrematureAbortEvents(nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
MOZ_ASSERT(mProxy);
|
|
|
|
mStateData.mReadyState = 4;
|
|
|
|
if (mProxy->mSeenUploadLoadStart) {
|
|
MOZ_ASSERT(mUpload);
|
|
|
|
JSObject* target = mUpload->GetJSObject();
|
|
MOZ_ASSERT(target);
|
|
|
|
DispatchPrematureAbortEvent(target, STRING_abort, true, aRv);
|
|
if (NS_FAILED(aRv)) {
|
|
return;
|
|
}
|
|
|
|
DispatchPrematureAbortEvent(target, STRING_loadend, true, aRv);
|
|
if (NS_FAILED(aRv)) {
|
|
return;
|
|
}
|
|
|
|
mProxy->mSeenUploadLoadStart = false;
|
|
}
|
|
|
|
if (mProxy->mSeenLoadStart) {
|
|
JSObject* target = GetJSObject();
|
|
MOZ_ASSERT(target);
|
|
|
|
DispatchPrematureAbortEvent(target, STRING_readystatechange, false, aRv);
|
|
if (NS_FAILED(aRv)) {
|
|
return;
|
|
}
|
|
|
|
DispatchPrematureAbortEvent(target, STRING_abort, false, aRv);
|
|
if (NS_FAILED(aRv)) {
|
|
return;
|
|
}
|
|
|
|
DispatchPrematureAbortEvent(target, STRING_loadend, false, aRv);
|
|
if (NS_FAILED(aRv)) {
|
|
return;
|
|
}
|
|
|
|
mProxy->mSeenLoadStart = false;
|
|
}
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::DispatchPrematureAbortEvent(JSObject* aTarget,
|
|
uint8_t aEventType,
|
|
bool aUploadTarget, nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
MOZ_ASSERT(mProxy);
|
|
MOZ_ASSERT(aTarget);
|
|
MOZ_ASSERT(aEventType <= STRING_COUNT);
|
|
|
|
JSContext* cx = GetJSContext();
|
|
|
|
JSString* type = JS_NewStringCopyZ(cx, sEventStrings[aEventType]);
|
|
if (!type) {
|
|
aRv = NS_ERROR_OUT_OF_MEMORY;
|
|
return;
|
|
}
|
|
|
|
JSObject* event;
|
|
if (aEventType == STRING_readystatechange) {
|
|
event = events::CreateGenericEvent(cx, type, false, false, false);
|
|
}
|
|
else if (aUploadTarget) {
|
|
event = events::CreateProgressEvent(cx, type,
|
|
mProxy->mLastUploadLengthComputable,
|
|
mProxy->mLastUploadLoaded,
|
|
mProxy->mLastUploadTotal);
|
|
}
|
|
else {
|
|
event = events::CreateProgressEvent(cx, type,
|
|
mProxy->mLastLengthComputable,
|
|
mProxy->mLastLoaded,
|
|
mProxy->mLastTotal);
|
|
}
|
|
|
|
if (!event) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
|
|
bool dummy;
|
|
if (!events::DispatchEventToTarget(cx, aTarget, event, &dummy)) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::Unpin()
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
NS_ASSERTION(mJSObjectRooted, "Mismatched calls to Unpin!");
|
|
|
|
JSContext* cx = GetJSContext();
|
|
|
|
JS_RemoveObjectRoot(cx, &mJSObject);
|
|
|
|
mWorkerPrivate->RemoveFeature(cx, this);
|
|
|
|
mJSObjectRooted = false;
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::SendInternal(const nsAString& aStringBody,
|
|
JSAutoStructuredCloneBuffer& aBody,
|
|
nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
|
|
nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false;
|
|
|
|
MaybePin(aRv);
|
|
if (NS_FAILED(aRv)) {
|
|
return;
|
|
}
|
|
|
|
AutoUnpinXHR autoUnpin(this);
|
|
|
|
PRUint32 syncQueueKey = PR_UINT32_MAX;
|
|
if (mProxy->mIsSyncXHR) {
|
|
syncQueueKey = mWorkerPrivate->CreateNewSyncLoop();
|
|
}
|
|
|
|
mProxy->mOuterChannelId++;
|
|
|
|
JSContext* cx = GetJSContext();
|
|
|
|
nsRefPtr<SendRunnable> runnable =
|
|
new SendRunnable(mWorkerPrivate, mProxy, aStringBody, aBody,
|
|
aClonedObjects, syncQueueKey, hasUploadListeners);
|
|
if (!runnable->Dispatch(cx)) {
|
|
return;
|
|
}
|
|
|
|
autoUnpin.Clear();
|
|
|
|
// The event loop was spun above, make sure we aren't canceled already.
|
|
if (mCanceled) {
|
|
return;
|
|
}
|
|
|
|
if (mProxy->mIsSyncXHR && !mWorkerPrivate->RunSyncLoop(cx, syncQueueKey)) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool
|
|
XMLHttpRequest::Notify(JSContext* aCx, Status aStatus)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
MOZ_ASSERT(GetJSContext() == aCx);
|
|
|
|
if (aStatus >= Canceling && !mCanceled) {
|
|
mCanceled = true;
|
|
ReleaseProxy(WorkerIsGoingAway);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::Open(const nsAString& aMethod, const nsAString& aUrl,
|
|
bool aAsync, const nsAString& aUser,
|
|
const nsAString& aPassword, nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
if (mProxy) {
|
|
MaybeDispatchPrematureAbortEvents(aRv);
|
|
if (NS_FAILED(aRv)) {
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
mProxy = new Proxy(this);
|
|
}
|
|
|
|
mProxy->mOuterEventStreamId++;
|
|
|
|
nsRefPtr<OpenRunnable> runnable =
|
|
new OpenRunnable(mWorkerPrivate, mProxy, NS_ConvertUTF16toUTF8(aMethod),
|
|
NS_ConvertUTF16toUTF8(aUrl), aUser, aPassword, mMultipart,
|
|
mBackgroundRequest, mWithCredentials, mTimeout);
|
|
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
ReleaseProxy();
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
|
|
mProxy->mIsSyncXHR = !aAsync;
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::SetRequestHeader(const nsAString& aHeader,
|
|
const nsAString& aValue, nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
if (!mProxy) {
|
|
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
return;
|
|
}
|
|
|
|
nsRefPtr<SetRequestHeaderRunnable> runnable =
|
|
new SetRequestHeaderRunnable(mWorkerPrivate, mProxy,
|
|
NS_ConvertUTF16toUTF8(aHeader),
|
|
NS_ConvertUTF16toUTF8(aValue));
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::SetTimeout(uint32_t aTimeout, nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
mTimeout = aTimeout;
|
|
|
|
if (!mProxy) {
|
|
// Open may not have been called yet, in which case we'll handle the
|
|
// timeout in OpenRunnable.
|
|
return;
|
|
}
|
|
|
|
nsRefPtr<SetTimeoutRunnable> runnable =
|
|
new SetTimeoutRunnable(mWorkerPrivate, mProxy, aTimeout);
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::SetWithCredentials(bool aWithCredentials, nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
mWithCredentials = aWithCredentials;
|
|
|
|
if (!mProxy) {
|
|
// Open may not have been called yet, in which case we'll handle the
|
|
// credentials in OpenRunnable.
|
|
return;
|
|
}
|
|
|
|
nsRefPtr<SetWithCredentialsRunnable> runnable =
|
|
new SetWithCredentialsRunnable(mWorkerPrivate, mProxy, aWithCredentials);
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::SetMultipart(bool aMultipart, nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
mMultipart = aMultipart;
|
|
|
|
if (!mProxy) {
|
|
// Open may not have been called yet, in which case we'll handle the
|
|
// multipart in OpenRunnable.
|
|
return;
|
|
}
|
|
|
|
nsRefPtr<SetMultipartRunnable> runnable =
|
|
new SetMultipartRunnable(mWorkerPrivate, mProxy, aMultipart);
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::SetMozBackgroundRequest(bool aBackgroundRequest,
|
|
nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
mBackgroundRequest = aBackgroundRequest;
|
|
|
|
if (!mProxy) {
|
|
// Open may not have been called yet, in which case we'll handle the
|
|
// background request in OpenRunnable.
|
|
return;
|
|
}
|
|
|
|
nsRefPtr<SetBackgroundRequestRunnable> runnable =
|
|
new SetBackgroundRequestRunnable(mWorkerPrivate, mProxy,
|
|
aBackgroundRequest);
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
XMLHttpRequestUpload*
|
|
XMLHttpRequest::GetUpload(nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return NULL;
|
|
}
|
|
|
|
if (!mUpload) {
|
|
XMLHttpRequestUpload* upload =
|
|
XMLHttpRequestUpload::Create(GetJSContext(), this);
|
|
|
|
if (!upload) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return NULL;
|
|
}
|
|
|
|
mUpload = upload;
|
|
}
|
|
|
|
return mUpload;
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::Send(nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
if (!mProxy) {
|
|
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
return;
|
|
}
|
|
|
|
// Nothing to clone.
|
|
JSAutoStructuredCloneBuffer buffer;
|
|
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
|
|
|
|
SendInternal(NullString(), buffer, clonedObjects, aRv);
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::Send(const nsAString& aBody, nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
if (!mProxy) {
|
|
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
return;
|
|
}
|
|
|
|
// Nothing to clone.
|
|
JSAutoStructuredCloneBuffer buffer;
|
|
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
|
|
|
|
SendInternal(aBody, buffer, clonedObjects, aRv);
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::Send(JSObject* aBody, nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
MOZ_ASSERT(aBody);
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
if (!mProxy) {
|
|
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
return;
|
|
}
|
|
|
|
JSContext* cx = GetJSContext();
|
|
|
|
jsval valToClone;
|
|
if (JS_IsArrayBufferObject(aBody, cx) || file::GetDOMBlobFromJSObject(aBody)) {
|
|
valToClone = OBJECT_TO_JSVAL(aBody);
|
|
}
|
|
else {
|
|
JSString* bodyStr = JS_ValueToString(cx, OBJECT_TO_JSVAL(aBody));
|
|
if (!bodyStr) {
|
|
aRv = NS_ERROR_OUT_OF_MEMORY;
|
|
return;
|
|
}
|
|
valToClone = STRING_TO_JSVAL(bodyStr);
|
|
}
|
|
|
|
JSStructuredCloneCallbacks* callbacks =
|
|
mWorkerPrivate->IsChromeWorker() ?
|
|
ChromeWorkerStructuredCloneCallbacks(false) :
|
|
WorkerStructuredCloneCallbacks(false);
|
|
|
|
nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
|
|
|
|
JSAutoStructuredCloneBuffer buffer;
|
|
if (!buffer.write(cx, valToClone, callbacks, &clonedObjects)) {
|
|
aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
|
|
return;
|
|
}
|
|
|
|
SendInternal(EmptyString(), buffer, clonedObjects, aRv);
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::SendAsBinary(const nsAString& aBody, nsresult& aRv)
|
|
{
|
|
NS_NOTYETIMPLEMENTED("Implement me!");
|
|
aRv = NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::Abort(nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
if (!mProxy) {
|
|
return;
|
|
}
|
|
|
|
MaybeDispatchPrematureAbortEvents(aRv);
|
|
if (NS_FAILED(aRv)) {
|
|
return;
|
|
}
|
|
|
|
mProxy->mOuterEventStreamId++;
|
|
|
|
nsRefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::GetResponseHeader(const nsAString& aHeader,
|
|
nsAString& aResponseHeader, nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
if (!mProxy) {
|
|
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
return;
|
|
}
|
|
|
|
nsCString value;
|
|
nsRefPtr<GetResponseHeaderRunnable> runnable =
|
|
new GetResponseHeaderRunnable(mWorkerPrivate, mProxy,
|
|
NS_ConvertUTF16toUTF8(aHeader), value);
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
|
|
aResponseHeader = NS_ConvertUTF8toUTF16(value);
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::GetAllResponseHeaders(nsAString& aResponseHeaders,
|
|
nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
if (!mProxy) {
|
|
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
return;
|
|
}
|
|
|
|
nsString responseHeaders;
|
|
nsRefPtr<GetAllResponseHeadersRunnable> runnable =
|
|
new GetAllResponseHeadersRunnable(mWorkerPrivate, mProxy, responseHeaders);
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
|
|
aResponseHeaders = responseHeaders;
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::OverrideMimeType(const nsAString& aMimeType, nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
// We're supposed to throw if the state is not OPENED or HEADERS_RECEIVED. We
|
|
// can detect OPENED really easily but we can't detect HEADERS_RECEIVED in a
|
|
// non-racy way until the XHR state machine actually runs on this thread
|
|
// (bug 671047). For now we're going to let this work only if the Send()
|
|
// method has not been called.
|
|
if (!mProxy || SendInProgress()) {
|
|
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
return;
|
|
}
|
|
|
|
nsRefPtr<OverrideMimeTypeRunnable> runnable =
|
|
new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aResponseType,
|
|
nsresult& aRv)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
if (mCanceled) {
|
|
aRv = UNCATCHABLE_EXCEPTION;
|
|
return;
|
|
}
|
|
|
|
if (!mProxy || SendInProgress()) {
|
|
aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
return;
|
|
}
|
|
|
|
// "document" is fine for the main thread but not for a worker. Short-circuit
|
|
// that here.
|
|
if (aResponseType == XMLHttpRequestResponseTypeValues::document) {
|
|
return;
|
|
}
|
|
|
|
nsString responseType;
|
|
ConvertResponseTypeToString(aResponseType, responseType);
|
|
|
|
nsRefPtr<SetResponseTypeRunnable> runnable =
|
|
new SetResponseTypeRunnable(mWorkerPrivate, mProxy, responseType);
|
|
if (!runnable->Dispatch(GetJSContext())) {
|
|
aRv = NS_ERROR_FAILURE;
|
|
return;
|
|
}
|
|
|
|
nsString acceptedResponseTypeString;
|
|
runnable->GetResponseType(acceptedResponseTypeString);
|
|
|
|
mResponseType = ConvertStringToResponseType(acceptedResponseTypeString);
|
|
}
|
|
|
|
jsval
|
|
XMLHttpRequest::GetResponse(nsresult& aRv)
|
|
{
|
|
if (NS_SUCCEEDED(mStateData.mResponseTextResult) &&
|
|
JSVAL_IS_VOID(mStateData.mResponse)) {
|
|
MOZ_ASSERT(mStateData.mResponseText.Length());
|
|
MOZ_ASSERT(NS_SUCCEEDED(mStateData.mResponseResult));
|
|
|
|
JSString* str =
|
|
JS_NewUCStringCopyN(GetJSContext(), mStateData.mResponseText.get(),
|
|
mStateData.mResponseText.Length());
|
|
if (!str) {
|
|
aRv = NS_ERROR_OUT_OF_MEMORY;
|
|
return JSVAL_VOID;
|
|
}
|
|
|
|
mStateData.mResponse = STRING_TO_JSVAL(str);
|
|
}
|
|
|
|
aRv = mStateData.mResponseResult;
|
|
return mStateData.mResponse;
|
|
}
|
|
|
|
void
|
|
XMLHttpRequest::GetResponseText(nsAString& aResponseText, nsresult& aRv)
|
|
{
|
|
aRv = mStateData.mResponseTextResult;
|
|
aResponseText = mStateData.mResponseText;
|
|
}
|