/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Web Workers. * * The Initial Developer of the Original Code is * Mozilla Corporation. * Portions created by the Initial Developer are Copyright (C) 2008 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Ben Turner (Original Author) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsDOMWorkerEvents.h" #include "nsIXMLHttpRequest.h" #include "nsIXPConnect.h" #include "nsAXPCNativeCallContext.h" #include "nsContentUtils.h" #include "nsThreadUtils.h" #include "nsDOMWorkerMessageHandler.h" #include "nsDOMWorkerXHR.h" #include "nsDOMWorkerXHRProxy.h" NS_DEFINE_STATIC_IID_ACCESSOR(nsIDOMWorkerPrivateEvent, NS_IDOMWORKERPRIVATEEVENT_IID) nsDOMWorkerPrivateEvent::nsDOMWorkerPrivateEvent(nsIDOMEvent* aEvent) : mEvent(aEvent), mProgressEvent(do_QueryInterface(aEvent)), mMessageEvent(do_QueryInterface(aEvent)), mErrorEvent(do_QueryInterface(aEvent)), mPreventDefaultCalled(PR_FALSE) { NS_ASSERTION(aEvent, "Null pointer!"); } NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerPrivateEvent) NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerPrivateEvent) NS_INTERFACE_MAP_BEGIN(nsDOMWorkerPrivateEvent) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWorkerPrivateEvent) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEvent, nsIDOMWorkerPrivateEvent) NS_INTERFACE_MAP_ENTRY(nsIDOMWorkerPrivateEvent) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMProgressEvent, mProgressEvent) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIWorkerMessageEvent, mMessageEvent) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIWorkerErrorEvent, mErrorEvent) NS_INTERFACE_MAP_ENTRY(nsIClassInfo) NS_INTERFACE_MAP_END NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerPrivateEvent, nsIDOMEvent) NS_IMPL_THREADSAFE_DOM_CI_HELPER(nsDOMWorkerPrivateEvent) NS_IMPL_THREADSAFE_DOM_CI_ALL_THE_REST(nsDOMWorkerPrivateEvent) NS_IMETHODIMP nsDOMWorkerPrivateEvent::GetInterfaces(PRUint32* aCount, nsIID*** aArray) { nsCOMPtr ci(do_QueryInterface(mEvent)); if (ci) { return ci->GetInterfaces(aCount, aArray); } return NS_CI_INTERFACE_GETTER_NAME(nsDOMWorkerPrivateEvent)(aCount, aArray); } NS_IMETHODIMP nsDOMWorkerPrivateEvent::PreventDefault() { PRBool cancelable = PR_FALSE; mEvent->GetCancelable(&cancelable); if (cancelable) { mPreventDefaultCalled = PR_TRUE; } return mEvent->PreventDefault(); } NS_IMETHODIMP nsDOMWorkerPrivateEvent::InitEvent(const nsAString& aEventType, PRBool aCanBubble, PRBool aCancelable) { mPreventDefaultCalled = PR_FALSE; return mEvent->InitEvent(aEventType, aCanBubble, aCancelable); } NS_IMETHODIMP nsDOMWorkerPrivateEvent::InitProgressEvent(const nsAString& aTypeArg, PRBool aCanBubbleArg, PRBool aCancelableArg, PRBool aLengthComputableArg, PRUint64 aLoadedArg, PRUint64 aTotalArg) { NS_ASSERTION(mProgressEvent, "Impossible!"); mPreventDefaultCalled = PR_FALSE; return mProgressEvent->InitProgressEvent(aTypeArg, aCanBubbleArg, aCancelableArg, aLengthComputableArg, aLoadedArg, aTotalArg); } NS_IMETHODIMP nsDOMWorkerPrivateEvent::InitMessageEvent(const nsAString& aTypeArg, PRBool aCanBubbleArg, PRBool aCancelableArg, const nsAString& aDataArg, const nsAString& aOriginArg, nsISupports* aSourceArg) { NS_ASSERTION(mMessageEvent, "Impossible!"); mPreventDefaultCalled = PR_FALSE; return mMessageEvent->InitMessageEvent(aTypeArg, aCanBubbleArg, aCancelableArg, aDataArg, aOriginArg, aSourceArg); } NS_IMETHODIMP nsDOMWorkerPrivateEvent::InitErrorEvent(const nsAString& aTypeArg, PRBool aCanBubbleArg, PRBool aCancelableArg, const nsAString& aMessageArg, const nsAString& aFilenameArg, PRUint32 aLinenoArg) { NS_ASSERTION(mErrorEvent, "Impossible!"); mPreventDefaultCalled = PR_FALSE; return mErrorEvent->InitErrorEvent(aTypeArg, aCanBubbleArg, aCancelableArg, aMessageArg, aFilenameArg, aLinenoArg); } PRBool nsDOMWorkerPrivateEvent::PreventDefaultCalled() { return mPreventDefaultCalled; } NS_IMPL_THREADSAFE_ISUPPORTS2(nsDOMWorkerEvent, nsIDOMEvent, nsIClassInfo) NS_IMPL_CI_INTERFACE_GETTER1(nsDOMWorkerEvent, nsIDOMEvent) NS_IMPL_THREADSAFE_DOM_CI(nsDOMWorkerEvent) NS_IMETHODIMP nsDOMWorkerEvent::GetType(nsAString& aType) { aType.Assign(mType); return NS_OK; } NS_IMETHODIMP nsDOMWorkerEvent::GetTarget(nsIDOMEventTarget** aTarget) { NS_ENSURE_ARG_POINTER(aTarget); NS_IF_ADDREF(*aTarget = mTarget); return NS_OK; } NS_IMETHODIMP nsDOMWorkerEvent::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget) { NS_ENSURE_ARG_POINTER(aCurrentTarget); NS_IF_ADDREF(*aCurrentTarget = mTarget); return NS_OK; } NS_IMETHODIMP nsDOMWorkerEvent::GetEventPhase(PRUint16* aEventPhase) { NS_ENSURE_ARG_POINTER(aEventPhase); *aEventPhase = mEventPhase; return NS_OK; } NS_IMETHODIMP nsDOMWorkerEvent::GetBubbles(PRBool* aBubbles) { NS_ENSURE_ARG_POINTER(aBubbles); *aBubbles = mBubbles; return NS_OK; } NS_IMETHODIMP nsDOMWorkerEvent::GetCancelable(PRBool* aCancelable) { NS_ENSURE_ARG_POINTER(aCancelable); *aCancelable = mCancelable; return NS_OK; } NS_IMETHODIMP nsDOMWorkerEvent::GetTimeStamp(DOMTimeStamp* aTimeStamp) { NS_ENSURE_ARG_POINTER(aTimeStamp); *aTimeStamp = mTimeStamp; return NS_OK; } NS_IMETHODIMP nsDOMWorkerEvent::StopPropagation() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDOMWorkerEvent::PreventDefault() { mPreventDefaultCalled = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsDOMWorkerEvent::InitEvent(const nsAString& aEventTypeArg, PRBool aCanBubbleArg, PRBool aCancelableArg) { NS_ENSURE_FALSE(aEventTypeArg.IsEmpty(), NS_ERROR_INVALID_ARG); mType.Assign(aEventTypeArg); mBubbles = aCanBubbleArg; mCancelable = aCancelableArg; mPreventDefaultCalled = PR_FALSE; mTimeStamp = PR_Now(); return NS_OK; } NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerMessageEvent, nsDOMWorkerEvent, nsIWorkerMessageEvent) NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerMessageEvent, nsIDOMEvent, nsIWorkerMessageEvent) NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerMessageEvent) NS_IMETHODIMP nsDOMWorkerMessageEvent::GetData(nsAString& aData) { if (!mIsJSON) { aData.Assign(mData); return NS_OK; } nsIXPConnect* xpc = nsContentUtils::XPConnect(); NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED); nsAXPCNativeCallContext* cc; nsresult rv = xpc->GetCurrentNativeCallContext(&cc); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED); jsval* retval; rv = cc->GetRetValPtr(&retval); NS_ENSURE_SUCCESS(rv, rv); if (mHaveCachedJSVal) { cc->SetReturnValueWasSet(PR_TRUE); *retval = mCachedJSVal; return NS_OK; } if (mHaveAttemptedConversion) { // Don't try to convert again if the first time around we saw an error. return NS_ERROR_FAILURE; } mHaveAttemptedConversion = PR_TRUE; JSContext* cx; rv = cc->GetJSContext(&cx); NS_ENSURE_SUCCESS(rv, rv); JSAutoRequest ar(cx); JSBool ok = mCachedJSVal.Hold(cx); NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); JSONParser* parser = JS_BeginJSONParse(cx, mCachedJSVal.ToJSValPtr()); NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED); // This is slightly sneaky, but now that JS_BeginJSONParse succeeded we always // need call JS_FinishJSONParse even if JS_ConsumeJSONText fails. We'll report // an error if either failed, though. ok = JS_ConsumeJSONText(cx, parser, (jschar*)mData.get(), (uint32)mData.Length()); // Note the '&& ok' after the call here! ok = JS_FinishJSONParse(cx, parser, JSVAL_NULL) && ok; if (!ok) { mCachedJSVal = JSVAL_NULL; return NS_ERROR_UNEXPECTED; } NS_ASSERTION(mCachedJSVal.ToJSObject(), "Bad JSON result!"); if (mIsPrimitive) { jsval primitive; ok = JS_GetProperty(cx, mCachedJSVal.ToJSObject(), JSON_PRIMITIVE_PROPNAME, &primitive); if (!ok) { mCachedJSVal = JSVAL_NULL; return NS_ERROR_UNEXPECTED; } mCachedJSVal = primitive; } // We no longer need to hold this copy of the data around. mData.Truncate(); // Now that everything has succeeded we'll set this flag so that we return the // cached jsval in the future. mHaveCachedJSVal = PR_TRUE; *retval = mCachedJSVal; cc->SetReturnValueWasSet(PR_TRUE); return NS_OK; } NS_IMETHODIMP nsDOMWorkerMessageEvent::GetOrigin(nsAString& aOrigin) { aOrigin.Assign(mOrigin); return NS_OK; } NS_IMETHODIMP nsDOMWorkerMessageEvent::GetSource(nsISupports** aSource) { NS_ENSURE_ARG_POINTER(aSource); NS_IF_ADDREF(*aSource = mSource); return NS_OK; } NS_IMETHODIMP nsDOMWorkerMessageEvent::InitMessageEvent(const nsAString& aTypeArg, PRBool aCanBubbleArg, PRBool aCancelableArg, const nsAString& aDataArg, const nsAString& aOriginArg, nsISupports* aSourceArg) { mData.Assign(aDataArg); mOrigin.Assign(aOriginArg); mSource = aSourceArg; return nsDOMWorkerEvent::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg); } NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerProgressEvent, nsDOMWorkerEvent, nsIDOMProgressEvent) NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerProgressEvent, nsIDOMEvent, nsIDOMProgressEvent) NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerProgressEvent) NS_IMETHODIMP nsDOMWorkerProgressEvent::GetLengthComputable(PRBool* aLengthComputable) { NS_ENSURE_ARG_POINTER(aLengthComputable); *aLengthComputable = mLengthComputable; return NS_OK; } NS_IMETHODIMP nsDOMWorkerProgressEvent::GetLoaded(PRUint64* aLoaded) { NS_ENSURE_ARG_POINTER(aLoaded); *aLoaded = mLoaded; return NS_OK; } NS_IMETHODIMP nsDOMWorkerProgressEvent::GetTotal(PRUint64* aTotal) { NS_ENSURE_ARG_POINTER(aTotal); *aTotal = mTotal; return NS_OK; } NS_IMETHODIMP nsDOMWorkerProgressEvent::InitProgressEvent(const nsAString_internal& aTypeArg, PRBool aCanBubbleArg, PRBool aCancelableArg, PRBool aLengthComputableArg, PRUint64 aLoadedArg, PRUint64 aTotalArg) { mLengthComputable = aLengthComputableArg; mLoaded = aLoadedArg; mTotal = aTotalArg; return nsDOMWorkerEvent::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg); } NS_IMPL_THREADSAFE_ADDREF(nsDOMWorkerXHRState) NS_IMPL_THREADSAFE_RELEASE(nsDOMWorkerXHRState) nsDOMWorkerXHREvent::nsDOMWorkerXHREvent(nsDOMWorkerXHRProxy* aXHRProxy) : mXHRProxy(aXHRProxy), mXHREventType(PR_UINT32_MAX), mChannelID(-1), mUploadEvent(PR_FALSE), mProgressEvent(PR_FALSE) { NS_ASSERTION(aXHRProxy, "Can't be null!"); } NS_IMPL_ADDREF_INHERITED(nsDOMWorkerXHREvent, nsDOMWorkerProgressEvent) NS_IMPL_RELEASE_INHERITED(nsDOMWorkerXHREvent, nsDOMWorkerProgressEvent) NS_INTERFACE_MAP_BEGIN(nsDOMWorkerXHREvent) NS_INTERFACE_MAP_ENTRY(nsIRunnable) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMProgressEvent, mProgressEvent) NS_INTERFACE_MAP_END_INHERITING(nsDOMWorkerEvent) NS_IMETHODIMP nsDOMWorkerXHREvent::GetInterfaces(PRUint32* aCount, nsIID*** aArray) { PRUint32 count = *aCount = mProgressEvent ? 2 : 1; *aArray = (nsIID**)nsMemory::Alloc(sizeof(nsIID*) * count); if (mProgressEvent) { (*aArray)[--count] = (nsIID*)nsMemory::Clone(&NS_GET_IID(nsIDOMProgressEvent), sizeof(nsIID)); } (*aArray)[--count] = (nsIID *)nsMemory::Clone(&NS_GET_IID(nsIDOMEvent), sizeof(nsIID)); NS_ASSERTION(!count, "Bad math!"); return NS_OK; } nsresult nsDOMWorkerXHREvent::Init(PRUint32 aXHREventType, const nsAString& aType, nsIDOMEvent* aEvent, SnapshotChoice aSnapshot) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aEvent, "Don't pass null here!"); mXHREventType = aXHREventType; // Only set a channel id if we're not going to be run immediately. mChannelID = mXHRProxy->mSyncEventQueue ? -1 : mXHRProxy->ChannelID(); mTarget = static_cast(mXHRProxy->mWorkerXHR); NS_ENSURE_TRUE(mTarget, NS_ERROR_UNEXPECTED); mXHRWN = mXHRProxy->mWorkerXHR->GetWrappedNative(); NS_ENSURE_STATE(mXHRWN); nsCOMPtr mainThreadTarget; nsresult rv = aEvent->GetTarget(getter_AddRefs(mainThreadTarget)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_STATE(mainThreadTarget); nsCOMPtr upload(do_QueryInterface(mainThreadTarget)); if (upload) { mUploadEvent = PR_TRUE; mTarget = static_cast(mXHRProxy->mWorkerXHR->mUpload); } else { mUploadEvent = PR_FALSE; mTarget = static_cast(mXHRProxy->mWorkerXHR); } NS_ASSERTION(mTarget, "Null target!"); PRBool bubbles; rv = aEvent->GetBubbles(&bubbles); NS_ENSURE_SUCCESS(rv, rv); PRBool cancelable; rv = aEvent->GetCancelable(&cancelable); NS_ENSURE_SUCCESS(rv, rv); rv = aEvent->GetTimeStamp(&mTimeStamp); NS_ENSURE_SUCCESS(rv, rv); rv = aEvent->GetEventPhase(&mEventPhase); NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(mEventPhase == nsIDOMEvent::AT_TARGET, "Unsupported phase!"); nsCOMPtr progressEvent(do_QueryInterface(aEvent)); if (progressEvent) { mProgressEvent = PR_TRUE; PRBool lengthComputable; rv = progressEvent->GetLengthComputable(&lengthComputable); NS_ENSURE_SUCCESS(rv, rv); PRUint64 loaded; rv = progressEvent->GetLoaded(&loaded); NS_ENSURE_SUCCESS(rv, rv); PRUint64 total; rv = progressEvent->GetTotal(&total); NS_ENSURE_SUCCESS(rv, rv); rv = InitProgressEvent(aType, bubbles, cancelable, lengthComputable, loaded, total); NS_ENSURE_SUCCESS(rv, rv); } else { mProgressEvent = PR_FALSE; rv = InitEvent(aType, bubbles, cancelable); NS_ENSURE_SUCCESS(rv, rv); } mState = new nsDOMWorkerXHRState(); NS_ENSURE_TRUE(mState, NS_ERROR_OUT_OF_MEMORY); if (aSnapshot == SNAPSHOT) { SnapshotXHRState(mXHRProxy->mXHR, mState); } return NS_OK; } /* static */ void nsDOMWorkerXHREvent::SnapshotXHRState(nsIXMLHttpRequest* aXHR, nsDOMWorkerXHRState* aState) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aXHR && aState, "Don't pass null here!"); aState->responseTextResult = aXHR->GetResponseText(aState->responseText); aState->statusTextResult = aXHR->GetStatusText(aState->statusText); aState->statusResult = aXHR->GetStatus(&aState->status); aState->readyStateResult = aXHR->GetReadyState(&aState->readyState); } NS_IMETHODIMP nsDOMWorkerXHREvent::Run() { nsresult rv = mXHRProxy->HandleWorkerEvent(this, mUploadEvent); // Prevent reference cycles by releasing this here. mXHRProxy = nsnull; NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerErrorEvent, nsDOMWorkerEvent, nsIWorkerErrorEvent) NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerErrorEvent, nsIDOMEvent, nsIWorkerErrorEvent) NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerErrorEvent) nsresult nsDOMWorkerErrorEvent::GetMessage(nsAString& aMessage) { aMessage.Assign(mMessage); return NS_OK; } nsresult nsDOMWorkerErrorEvent::GetFilename(nsAString& aFilename) { aFilename.Assign(mFilename); return NS_OK; } nsresult nsDOMWorkerErrorEvent::GetLineno(PRUint32* aLineno) { NS_ENSURE_ARG_POINTER(aLineno); *aLineno = mLineno; return NS_OK; } nsresult nsDOMWorkerErrorEvent::InitErrorEvent(const nsAString& aTypeArg, PRBool aCanBubbleArg, PRBool aCancelableArg, const nsAString& aMessageArg, const nsAString& aFilenameArg, PRUint32 aLinenoArg) { mMessage.Assign(aMessageArg); mFilename.Assign(aFilenameArg); mLineno = aLinenoArg; return InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg); }