зеркало из https://github.com/mozilla/gecko-dev.git
Bug 618484 - 'Allow ChromeWorkers access to XPCOM objects'. r=jst, a=blocking.
This commit is contained in:
Родитель
0ce6811105
Коммит
a7ff51c2c7
|
@ -4071,7 +4071,8 @@ static JSObject*
|
|||
DOMReadStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32 tag,
|
||||
uint32 data)
|
||||
uint32 data,
|
||||
void* closure)
|
||||
{
|
||||
// We don't currently support any extensions to structured cloning.
|
||||
nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
|
@ -4081,7 +4082,8 @@ DOMReadStructuredClone(JSContext* cx,
|
|||
static JSBool
|
||||
DOMWriteStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneWriter* writer,
|
||||
JSObject* obj)
|
||||
JSObject* obj,
|
||||
void *closure)
|
||||
{
|
||||
// We don't currently support any extensions to structured cloning.
|
||||
nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
|
|
|
@ -528,7 +528,7 @@ IDBObjectStore::GetKeyPathValueFromStructuredData(const PRUint8* aData,
|
|||
jsval clone;
|
||||
if (!JS_ReadStructuredClone(cx, reinterpret_cast<const uint64*>(aData),
|
||||
aDataLength, JS_STRUCTURED_CLONE_VERSION,
|
||||
&clone)) {
|
||||
&clone, NULL, NULL)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
|
|
|
@ -755,6 +755,9 @@ nsDOMThreadService::Init()
|
|||
success = mPools.Init();
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
success = mThreadsafeContractIDs.Init();
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
success = mJSContexts.SetCapacity(THREADPOOL_THREAD_CAP);
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
|
@ -1214,6 +1217,43 @@ nsDOMThreadService::ChangeThreadPoolMaxThreads(PRInt16 aDelta)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMThreadService::NoteThreadsafeContractId(const nsACString& aContractId,
|
||||
PRBool aIsThreadsafe)
|
||||
{
|
||||
NS_ASSERTION(!aContractId.IsEmpty(), "Empty contract id!");
|
||||
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
PRBool isThreadsafe;
|
||||
if (mThreadsafeContractIDs.Get(aContractId, &isThreadsafe)) {
|
||||
NS_ASSERTION(aIsThreadsafe == isThreadsafe, "Inconsistent threadsafety!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!mThreadsafeContractIDs.Put(aContractId, aIsThreadsafe)) {
|
||||
NS_WARNING("Out of memory!");
|
||||
}
|
||||
}
|
||||
|
||||
ThreadsafeStatus
|
||||
nsDOMThreadService::GetContractIdThreadsafeStatus(const nsACString& aContractId)
|
||||
{
|
||||
NS_ASSERTION(!aContractId.IsEmpty(), "Empty contract id!");
|
||||
|
||||
nsAutoMonitor mon(mMonitor);
|
||||
|
||||
PRBool isThreadsafe;
|
||||
if (mThreadsafeContractIDs.Get(aContractId, &isThreadsafe)) {
|
||||
return isThreadsafe ? Threadsafe : NotThreadsafe;
|
||||
}
|
||||
|
||||
return Unknown;
|
||||
}
|
||||
|
||||
// static
|
||||
nsIJSRuntimeService*
|
||||
nsDOMThreadService::JSRuntimeService()
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "jsapi.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsTPtrArray.h"
|
||||
|
@ -69,6 +70,13 @@ class nsIThreadJSContextStack;
|
|||
class nsIXPConnect;
|
||||
class nsIXPCSecurityManager;
|
||||
|
||||
enum ThreadsafeStatus
|
||||
{
|
||||
Threadsafe,
|
||||
NotThreadsafe,
|
||||
Unknown
|
||||
};
|
||||
|
||||
class nsDOMThreadService : public nsIEventTarget,
|
||||
public nsIObserver,
|
||||
public nsIThreadPoolListener
|
||||
|
@ -114,6 +122,11 @@ public:
|
|||
|
||||
nsresult ChangeThreadPoolMaxThreads(PRInt16 aDelta);
|
||||
|
||||
void NoteThreadsafeContractId(const nsACString& aContractId,
|
||||
PRBool aIsThreadsafe);
|
||||
|
||||
ThreadsafeStatus GetContractIdThreadsafeStatus(const nsACString& aContractId);
|
||||
|
||||
private:
|
||||
nsDOMThreadService();
|
||||
~nsDOMThreadService();
|
||||
|
@ -185,6 +198,9 @@ private:
|
|||
// suspended. Always protected with mMonitor.
|
||||
nsTArray<nsDOMWorkerRunnable*> mSuspendedWorkers;
|
||||
|
||||
// Always protected with mMonitor.
|
||||
nsDataHashtable<nsCStringHashKey, PRBool> mThreadsafeContractIDs;
|
||||
|
||||
nsString mAppName;
|
||||
nsString mAppVersion;
|
||||
nsString mPlatform;
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "nsAutoLock.h"
|
||||
#include "nsAXPCNativeCallContext.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsDOMClassInfoID.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsJSON.h"
|
||||
|
@ -69,6 +70,57 @@
|
|||
#include "nsDOMWorkerTimeout.h"
|
||||
#include "nsDOMWorkerXHR.h"
|
||||
|
||||
class TestComponentThreadsafetyRunnable : public nsIRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
TestComponentThreadsafetyRunnable(const nsACString& aContractId,
|
||||
PRBool aService)
|
||||
: mContractId(aContractId),
|
||||
mService(aService),
|
||||
mIsThreadsafe(PR_FALSE)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsISupports> instance;
|
||||
if (mService) {
|
||||
instance = do_GetService(mContractId.get(), &rv);
|
||||
}
|
||||
else {
|
||||
instance = do_CreateInstance(mContractId.get(), &rv);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(instance, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 flags;
|
||||
rv = classInfo->GetFlags(&flags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mIsThreadsafe = !!(flags & nsIClassInfo::THREADSAFE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool IsThreadsafe()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
return mIsThreadsafe;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCString mContractId;
|
||||
PRBool mService;
|
||||
PRBool mIsThreadsafe;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(TestComponentThreadsafetyRunnable, nsIRunnable)
|
||||
|
||||
class nsDOMWorkerFunctions
|
||||
{
|
||||
public:
|
||||
|
@ -115,6 +167,19 @@ public:
|
|||
static JSBool
|
||||
NewChromeWorker(JSContext* aCx, uintN aArgc, jsval* aVp);
|
||||
|
||||
static JSBool
|
||||
XPCOMLazyGetter(JSContext* aCx, JSObject* aObj, jsid aId, jsval* aVp);
|
||||
|
||||
static JSBool
|
||||
CreateInstance(JSContext* aCx, uintN aArgc, jsval* aVp) {
|
||||
return GetInstanceCommon(aCx, aArgc, aVp, PR_FALSE);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
GetService(JSContext* aCx, uintN aArgc, jsval* aVp) {
|
||||
return GetInstanceCommon(aCx, aArgc, aVp, PR_TRUE);
|
||||
}
|
||||
|
||||
#ifdef BUILD_CTYPES
|
||||
static JSBool
|
||||
CTypesLazyGetter(JSContext* aCx, JSObject* aObj, jsid aId, jsval* aVp);
|
||||
|
@ -128,6 +193,15 @@ private:
|
|||
static JSBool
|
||||
MakeNewWorker(JSContext* aCx, uintN aArgc, jsval* aVp,
|
||||
WorkerPrivilegeModel aPrivilegeModel);
|
||||
|
||||
static JSBool
|
||||
GetInstanceCommon(JSContext* aCx, uintN aArgc, jsval* aVp, PRBool aService);
|
||||
};
|
||||
|
||||
JSFunctionSpec gDOMWorkerXPCOMFunctions[] = {
|
||||
{"createInstance", nsDOMWorkerFunctions::CreateInstance, 1, JSPROP_ENUMERATE},
|
||||
{"getService", nsDOMWorkerFunctions::GetService, 1, JSPROP_ENUMERATE},
|
||||
{ nsnull, nsnull, 0, 0 }
|
||||
};
|
||||
|
||||
JSBool
|
||||
|
@ -408,6 +482,170 @@ nsDOMWorkerFunctions::NewChromeWorker(JSContext* aCx,
|
|||
return MakeNewWorker(aCx, aArgc, aVp, nsDOMWorker::CHROME);
|
||||
}
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerFunctions::XPCOMLazyGetter(JSContext* aCx,
|
||||
JSObject* aObj,
|
||||
jsid aId,
|
||||
jsval* aVp)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
{
|
||||
NS_ASSERTION(JS_GetGlobalForObject(aCx, aObj) == aObj, "Bad object!");
|
||||
NS_ASSERTION(JSID_IS_STRING(aId), "Not a string!");
|
||||
JSString* str = JSID_TO_STRING(aId);
|
||||
NS_ASSERTION(nsDependentJSString(str).EqualsLiteral("XPCOM"), "Bad id!");
|
||||
}
|
||||
#endif
|
||||
nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
|
||||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
PRUint16 dummy;
|
||||
nsCOMPtr<nsIXPCSecurityManager> secMan;
|
||||
nsContentUtils::XPConnect()->
|
||||
GetSecurityManagerForJSContext(aCx, getter_AddRefs(secMan), &dummy);
|
||||
if (!secMan) {
|
||||
JS_ReportError(aCx, "Could not get security manager!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsCID dummyCID;
|
||||
if (NS_FAILED(secMan->CanGetService(aCx, dummyCID))) {
|
||||
JS_ReportError(aCx, "Access to the XPCOM object is denied!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSObject* xpcom = JS_NewObject(aCx, nsnull, nsnull, nsnull);
|
||||
NS_ENSURE_TRUE(xpcom, JS_FALSE);
|
||||
|
||||
JSBool ok = JS_DefineFunctions(aCx, xpcom, gDOMWorkerXPCOMFunctions);
|
||||
NS_ENSURE_TRUE(ok, JS_FALSE);
|
||||
|
||||
ok = JS_DeletePropertyById(aCx, aObj, aId);
|
||||
NS_ENSURE_TRUE(ok, JS_FALSE);
|
||||
|
||||
jsval xpcomVal = OBJECT_TO_JSVAL(xpcom);
|
||||
ok = JS_SetPropertyById(aCx, aObj, aId, &xpcomVal);
|
||||
NS_ENSURE_TRUE(ok, JS_FALSE);
|
||||
|
||||
JS_SET_RVAL(aCx, aVp, xpcomVal);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerFunctions::GetInstanceCommon(JSContext* aCx,
|
||||
uintN aArgc,
|
||||
jsval* aVp,
|
||||
PRBool aService)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
|
||||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
if (worker->IsCanceled()) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (!aArgc) {
|
||||
JS_ReportError(aCx, "Function requires at least 1 parameter");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
|
||||
if (!str) {
|
||||
NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSAutoByteString strBytes(aCx, str);
|
||||
if (!strBytes) {
|
||||
NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsDependentCString contractId(strBytes.ptr(), JS_GetStringLength(str));
|
||||
|
||||
nsDOMThreadService* threadService = nsDOMThreadService::get();
|
||||
|
||||
ThreadsafeStatus status =
|
||||
threadService->GetContractIdThreadsafeStatus(contractId);
|
||||
|
||||
if (status == Unknown) {
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
if (NS_FAILED(rv)) {
|
||||
JS_ReportError(aCx, "Failed to get main thread!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsRefPtr<TestComponentThreadsafetyRunnable> runnable =
|
||||
new TestComponentThreadsafetyRunnable(contractId, aService);
|
||||
|
||||
rv = mainThread->Dispatch(runnable, NS_DISPATCH_SYNC);
|
||||
if (NS_FAILED(rv)) {
|
||||
JS_ReportError(aCx, "Failed to check threadsafety!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
// The worker may have been canceled while waiting above. Check again.
|
||||
if (worker->IsCanceled()) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (runnable->IsThreadsafe()) {
|
||||
threadService->NoteThreadsafeContractId(contractId, PR_TRUE);
|
||||
status = Threadsafe;
|
||||
}
|
||||
else {
|
||||
threadService->NoteThreadsafeContractId(contractId, PR_FALSE);
|
||||
status = NotThreadsafe;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == NotThreadsafe) {
|
||||
JS_ReportError(aCx, "ChromeWorker may not create an XPCOM object that is "
|
||||
"not threadsafe!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> instance;
|
||||
if (aService) {
|
||||
instance = do_GetService(contractId.get());
|
||||
if (!instance) {
|
||||
JS_ReportError(aCx, "Could not get the service!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
instance = do_CreateInstance(contractId.get());
|
||||
if (!instance) {
|
||||
JS_ReportError(aCx, "Could not create the instance!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx));
|
||||
if (!global) {
|
||||
NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
jsval val;
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
|
||||
if (NS_FAILED(nsContentUtils::WrapNative(aCx, global, instance, &val,
|
||||
getter_AddRefs(wrapper)))) {
|
||||
JS_ReportError(aCx, "Failed to wrap object!");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JS_SET_RVAL(aCx, aVp, val);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
nsDOMWorkerFunctions::MakeNewWorker(JSContext* aCx,
|
||||
uintN aArgc,
|
||||
|
@ -549,6 +787,70 @@ JSFunctionSpec gDOMWorkerChromeFunctions[] = {
|
|||
{ nsnull, nsnull, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
enum DOMWorkerStructuredDataType
|
||||
{
|
||||
// We have a special tag for XPCWrappedNatives that are being passed between
|
||||
// threads. This will not work across processes and cannot be persisted. Only
|
||||
// for ChromeWorker use at present.
|
||||
DOMWORKER_SCTAG_WRAPPEDNATIVE = JS_SCTAG_USER_MIN + 0x1000,
|
||||
|
||||
DOMWORKER_SCTAG_END
|
||||
};
|
||||
|
||||
PR_STATIC_ASSERT(DOMWORKER_SCTAG_END <= JS_SCTAG_USER_MAX);
|
||||
|
||||
// static
|
||||
JSBool
|
||||
WriteStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter,
|
||||
JSObject* aObj,
|
||||
void* aClosure)
|
||||
{
|
||||
NS_ASSERTION(aClosure, "Null pointer!");
|
||||
|
||||
// We'll stash any nsISupports pointers that need to be AddRef'd here.
|
||||
nsTArray<nsCOMPtr<nsISupports> >* wrappedNatives =
|
||||
static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
|
||||
|
||||
// See if this is a wrapped native.
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
|
||||
nsContentUtils::XPConnect()->
|
||||
GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
|
||||
if (wrappedNative) {
|
||||
// Get the raw nsISupports out of it.
|
||||
nsISupports* wrappedObject = wrappedNative->Native();
|
||||
NS_ASSERTION(wrappedObject, "Null pointer?!");
|
||||
|
||||
// See if this nsISupports is threadsafe.
|
||||
nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(wrappedObject);
|
||||
if (classInfo) {
|
||||
PRUint32 flags;
|
||||
if (NS_SUCCEEDED(classInfo->GetFlags(&flags)) &&
|
||||
(flags & nsIClassInfo::THREADSAFE)) {
|
||||
// Write the raw pointer into the stream, and add it to the list we're
|
||||
return JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_WRAPPEDNATIVE, 0) &&
|
||||
JS_WriteBytes(aWriter, &wrappedObject, sizeof(wrappedObject)) &&
|
||||
wrappedNatives->AppendElement(wrappedObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Something failed above, try using the runtime callbacks instead.
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
aCx->runtime->structuredCloneCallbacks;
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->write(aCx, aWriter, aObj, nsnull);
|
||||
}
|
||||
|
||||
// We can't handle this object, throw an exception if one hasn't been thrown
|
||||
// already.
|
||||
if (!JS_IsExceptionPending(aCx)) {
|
||||
nsDOMClassInfo::ThrowJSException(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
}
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
nsDOMWorkerScope::nsDOMWorkerScope(nsDOMWorker* aWorker)
|
||||
: mWorker(aWorker),
|
||||
mWrappedNative(nsnull),
|
||||
|
@ -1656,11 +1958,19 @@ nsDOMWorker::PostMessageInternal(PRBool aToInner)
|
|||
rv = cc->GetJSContext(&cx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If we're a ChromeWorker then we allow wrapped natives to be passed via
|
||||
// structured cloning by supplying a custom write callback. To do that we need
|
||||
// to make sure they stay alive while the message is being sent, so we collect
|
||||
// the wrapped natives in an array to be packaged with the message.
|
||||
JSStructuredCloneCallbacks callbacks = {
|
||||
nsnull, IsPrivileged() ? WriteStructuredClone : nsnull, nsnull
|
||||
};
|
||||
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
JSAutoStructuredCloneBuffer buffer;
|
||||
|
||||
if (!buffer.write(cx, argv[0])) {
|
||||
nsTArray<nsCOMPtr<nsISupports> > wrappedNatives;
|
||||
if (!buffer.write(cx, argv[0], &callbacks, &wrappedNatives)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
|
@ -1672,7 +1982,7 @@ nsDOMWorker::PostMessageInternal(PRBool aToInner)
|
|||
nsnull);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = message->SetJSData(cx, buffer);
|
||||
rv = message->SetJSData(cx, buffer, wrappedNatives);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRefPtr<nsDOMFireEventRunnable> runnable =
|
||||
|
@ -1796,6 +2106,11 @@ nsDOMWorker::CompileGlobalObject(JSContext* aCx, nsLazyAutoRequest *aRequest,
|
|||
success = JS_DefineFunctions(aCx, global, gDOMWorkerChromeFunctions);
|
||||
NS_ENSURE_TRUE(success, PR_FALSE);
|
||||
|
||||
success = JS_DefineProperty(aCx, global, "XPCOM", JSVAL_VOID,
|
||||
nsDOMWorkerFunctions::XPCOMLazyGetter, nsnull,
|
||||
0);
|
||||
NS_ENSURE_TRUE(success, PR_FALSE);
|
||||
|
||||
#ifdef BUILD_CTYPES
|
||||
// Add the lazy getter for ctypes.
|
||||
success = JS_DefineProperty(aCx, global, "ctypes", JSVAL_VOID,
|
||||
|
@ -2192,6 +2507,53 @@ nsDOMWorker::GetExpirationTime()
|
|||
}
|
||||
#endif
|
||||
|
||||
// static
|
||||
JSObject*
|
||||
nsDOMWorker::ReadStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32 aTag,
|
||||
uint32 aData,
|
||||
void* aClosure)
|
||||
{
|
||||
NS_ASSERTION(aCx, "Null context!");
|
||||
NS_ASSERTION(aReader, "Null reader!");
|
||||
NS_ASSERTION(!aClosure, "Shouldn't have a closure here!");
|
||||
|
||||
if (aTag == DOMWORKER_SCTAG_WRAPPEDNATIVE) {
|
||||
NS_ASSERTION(!aData, "Huh?");
|
||||
|
||||
nsISupports* wrappedNative;
|
||||
if (JS_ReadBytes(aReader, &wrappedNative, sizeof(wrappedNative))) {
|
||||
NS_ASSERTION(wrappedNative, "Null pointer?!");
|
||||
|
||||
JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx));
|
||||
if (global) {
|
||||
jsval val;
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
|
||||
if (NS_SUCCEEDED(nsContentUtils::WrapNative(aCx, global, wrappedNative,
|
||||
&val,
|
||||
getter_AddRefs(wrapper)))) {
|
||||
return JSVAL_TO_OBJECT(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Something failed above, try using the runtime callbacks instead.
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
aCx->runtime->structuredCloneCallbacks;
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->read(aCx, aReader, aTag, aData, nsnull);
|
||||
}
|
||||
|
||||
// We can't handle this object, throw an exception if one hasn't been thrown
|
||||
// already.
|
||||
if (!JS_IsExceptionPending(aCx)) {
|
||||
nsDOMClassInfo::ThrowJSException(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsDOMWorker::QueueSuspendedRunnable(nsIRunnable* aRunnable)
|
||||
{
|
||||
|
|
|
@ -234,6 +234,12 @@ public:
|
|||
return mPrivilegeModel == CHROME;
|
||||
}
|
||||
|
||||
static JSObject* ReadStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32 aTag,
|
||||
uint32 aData,
|
||||
void* aClosure);
|
||||
|
||||
/**
|
||||
* Use this chart to help figure out behavior during each of the closing
|
||||
* statuses. Details below.
|
||||
|
|
|
@ -279,8 +279,10 @@ NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerMessageEvent, nsIDOMEvent,
|
|||
NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerMessageEvent)
|
||||
|
||||
nsresult
|
||||
nsDOMWorkerMessageEvent::SetJSData(JSContext* aCx,
|
||||
JSAutoStructuredCloneBuffer& aBuffer)
|
||||
nsDOMWorkerMessageEvent::SetJSData(
|
||||
JSContext* aCx,
|
||||
JSAutoStructuredCloneBuffer& aBuffer,
|
||||
nsTArray<nsCOMPtr<nsISupports> >& aWrappedNatives)
|
||||
{
|
||||
NS_ASSERTION(aCx, "Null context!");
|
||||
|
||||
|
@ -289,6 +291,10 @@ nsDOMWorkerMessageEvent::SetJSData(JSContext* aCx,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mWrappedNatives.SwapElements(aWrappedNatives)) {
|
||||
NS_ERROR("This should never fail!");
|
||||
}
|
||||
|
||||
aBuffer.steal(&mData, &mDataLen);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -315,7 +321,17 @@ nsDOMWorkerMessageEvent::GetData(nsAString& aData)
|
|||
mData = nsnull;
|
||||
mDataLen = 0;
|
||||
|
||||
if (!buffer.read(mDataVal.ToJSValPtr())) {
|
||||
JSStructuredCloneCallbacks callbacks = {
|
||||
nsDOMWorker::ReadStructuredClone, nsnull, nsnull
|
||||
};
|
||||
|
||||
JSBool ok = buffer.read(mDataVal.ToJSValPtr(), cx, &callbacks);
|
||||
|
||||
// Release wrapped natives now, regardless of whether or not the deserialize
|
||||
// succeeded.
|
||||
mWrappedNatives.Clear();
|
||||
|
||||
if (!ok) {
|
||||
NS_WARNING("Failed to deserialize!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -215,7 +215,8 @@ public:
|
|||
~nsDOMWorkerMessageEvent();
|
||||
|
||||
nsresult SetJSData(JSContext* aCx,
|
||||
JSAutoStructuredCloneBuffer& aBuffer);
|
||||
JSAutoStructuredCloneBuffer& aBuffer,
|
||||
nsTArray<nsCOMPtr<nsISupports> >& aWrappedNatives);
|
||||
|
||||
protected:
|
||||
nsString mOrigin;
|
||||
|
@ -224,6 +225,7 @@ protected:
|
|||
nsAutoJSValHolder mDataVal;
|
||||
uint64* mData;
|
||||
size_t mDataLen;
|
||||
nsTArray<nsCOMPtr<nsISupports> > mWrappedNatives;
|
||||
};
|
||||
|
||||
class nsDOMWorkerProgressEvent : public nsDOMWorkerEvent,
|
||||
|
|
|
@ -43,9 +43,12 @@
|
|||
|
||||
// Other includes
|
||||
#include "jsapi.h"
|
||||
#include "nsDOMError.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
// DOMWorker includes
|
||||
#include "nsDOMThreadService.h"
|
||||
#include "nsDOMWorker.h"
|
||||
|
||||
#define LOG(_args) PR_LOG(gDOMThreadsLog, PR_LOG_DEBUG, _args)
|
||||
|
||||
|
@ -73,7 +76,7 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerSecurityManager,
|
|||
nsIXPCSecurityManager)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerSecurityManager::CanCreateWrapper(JSContext* aJSContext,
|
||||
nsDOMWorkerSecurityManager::CanCreateWrapper(JSContext* aCx,
|
||||
const nsIID& aIID,
|
||||
nsISupports* aObj,
|
||||
nsIClassInfo* aClassInfo,
|
||||
|
@ -83,19 +86,22 @@ nsDOMWorkerSecurityManager::CanCreateWrapper(JSContext* aJSContext,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerSecurityManager::CanCreateInstance(JSContext* aJSContext,
|
||||
nsDOMWorkerSecurityManager::CanCreateInstance(JSContext* aCx,
|
||||
const nsCID& aCID)
|
||||
{
|
||||
NS_NOTREACHED("Should not call this!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
return CanGetService(aCx, aCID);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWorkerSecurityManager::CanGetService(JSContext* aJSContext,
|
||||
nsDOMWorkerSecurityManager::CanGetService(JSContext* aCx,
|
||||
const nsCID& aCID)
|
||||
{
|
||||
NS_NOTREACHED("Should not call this!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
|
||||
NS_ASSERTION(worker, "This should be set by the DOM thread service!");
|
||||
|
||||
return worker->IsPrivileged() ? NS_OK : NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -119,6 +119,8 @@ _TEST_FILES = \
|
|||
newError_worker.js \
|
||||
test_chromeWorker.html \
|
||||
WorkerTest_badworker.js \
|
||||
test_xpcom.html \
|
||||
xpcom_worker.js \
|
||||
$(NULL)
|
||||
|
||||
_SUBDIR_TEST_FILES = \
|
||||
|
|
|
@ -35,8 +35,34 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
let worker = new ChromeWorker("chromeWorker_subworker.js");
|
||||
worker.onmessage = function(event) {
|
||||
postMessage(event.data);
|
||||
// Test XPCOM.getService()
|
||||
let threadMan = XPCOM.getService("@mozilla.org/thread-manager;1");
|
||||
let mainThread = threadMan.mainThread;
|
||||
if (mainThread.isOnCurrentThread()) {
|
||||
throw "Thread manager is lying to us!";
|
||||
}
|
||||
|
||||
// Test XPCOM.createInstance
|
||||
let threadPool = XPCOM.createInstance("@mozilla.org/thread-pool;1");
|
||||
threadPool.shutdown();
|
||||
|
||||
let notThreadsafe;
|
||||
try {
|
||||
notThreadsafe = XPCOM.createInstance("@mozilla.org/supports-PRBool;1");
|
||||
}
|
||||
catch(e) { }
|
||||
|
||||
if (notThreadsafe) {
|
||||
throw "Shouldn't be able to create non-threadsafe component!";
|
||||
}
|
||||
|
||||
function onmessage(event) {
|
||||
// Test passing wrapped natives from the main thread.
|
||||
event.data.shutdown();
|
||||
|
||||
let worker = new ChromeWorker("chromeWorker_subworker.js");
|
||||
worker.onmessage = function(event) {
|
||||
postMessage(event.data);
|
||||
}
|
||||
worker.postMessage("Go");
|
||||
}
|
||||
worker.postMessage("Go");
|
||||
|
|
|
@ -64,6 +64,25 @@
|
|||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// Test passing a non-threadsafe wrapped native to the worker.
|
||||
var isupports =
|
||||
Components.classes["@mozilla.org/supports-PRBool;1"]
|
||||
.createInstance(Components.interfaces.nsISupportsPRBool);
|
||||
|
||||
try {
|
||||
worker.postMessage(isupports);
|
||||
ok(false, "Passing non-threadsafe thing should throw!");
|
||||
}
|
||||
catch (e) {
|
||||
ok(true, "Passing non-threadsafe thing threw");
|
||||
}
|
||||
|
||||
// Test passing a wrapped native to the worker.
|
||||
var thread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().newThread(0);
|
||||
|
||||
worker.postMessage(thread);
|
||||
}
|
||||
|
||||
]]>
|
||||
|
|
|
@ -68,6 +68,12 @@
|
|||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// Test passing a wrapped native to the worker.
|
||||
var thread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().newThread(0);
|
||||
|
||||
worker.postMessage(thread);
|
||||
}
|
||||
|
||||
]]>
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for DOM Worker Threads</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var worker = new Worker("xpcom_worker.js");
|
||||
worker.onmessage = function(event) {
|
||||
is(event.data, "Done", "Correct message");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
worker.onerror = function(event) {
|
||||
ok(false, "Worker had an error: " + event.message);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
// Test passing a wrapped native to the worker.
|
||||
var thread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().newThread(0);
|
||||
|
||||
try {
|
||||
worker.postMessage(thread);
|
||||
ok(false, "postMessage with a wrapped native should fail!");
|
||||
}
|
||||
catch(e) {
|
||||
ok(true, "postMessage with a wrapped native failed");
|
||||
}
|
||||
|
||||
worker.postMessage("Hi");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
var exception;
|
||||
try {
|
||||
var xpcom = XPCOM;
|
||||
}
|
||||
catch(e) {
|
||||
exception = e;
|
||||
}
|
||||
|
||||
if (!exception) {
|
||||
throw "Worker shouldn't be able to access the XPCOM object!";
|
||||
}
|
||||
|
||||
onmessage = function(event) {
|
||||
if (event.data != "Hi") {
|
||||
throw "Bad message!";
|
||||
}
|
||||
postMessage("Done");
|
||||
}
|
|
@ -5481,26 +5481,48 @@ JS_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver)
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_ReadStructuredClone(JSContext *cx, const uint64 *buf, size_t nbytes, uint32 version, jsval *vp)
|
||||
JS_ReadStructuredClone(JSContext *cx, const uint64 *buf, size_t nbytes,
|
||||
uint32 version, jsval *vp,
|
||||
const JSStructuredCloneCallbacks *optionalCallbacks,
|
||||
void *closure)
|
||||
{
|
||||
if (version > JS_STRUCTURED_CLONE_VERSION) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CLONE_VERSION);
|
||||
return false;
|
||||
}
|
||||
return ReadStructuredClone(cx, buf, nbytes, Valueify(vp));
|
||||
const JSStructuredCloneCallbacks *callbacks =
|
||||
optionalCallbacks ?
|
||||
optionalCallbacks :
|
||||
cx->runtime->structuredCloneCallbacks;
|
||||
return ReadStructuredClone(cx, buf, nbytes, Valueify(vp), callbacks, closure);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **bufp, size_t *nbytesp)
|
||||
JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **bufp, size_t *nbytesp,
|
||||
const JSStructuredCloneCallbacks *optionalCallbacks,
|
||||
void *closure)
|
||||
{
|
||||
return WriteStructuredClone(cx, Valueify(v), (uint64_t **) bufp, nbytesp);
|
||||
const JSStructuredCloneCallbacks *callbacks =
|
||||
optionalCallbacks ?
|
||||
optionalCallbacks :
|
||||
cx->runtime->structuredCloneCallbacks;
|
||||
return WriteStructuredClone(cx, Valueify(v), (uint64_t **) bufp, nbytesp,
|
||||
callbacks, closure);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_StructuredClone(JSContext *cx, jsval v, jsval *vp)
|
||||
JS_StructuredClone(JSContext *cx, jsval v, jsval *vp,
|
||||
ReadStructuredCloneOp optionalReadOp,
|
||||
const JSStructuredCloneCallbacks *optionalCallbacks,
|
||||
void *closure)
|
||||
{
|
||||
const JSStructuredCloneCallbacks *callbacks =
|
||||
optionalCallbacks ?
|
||||
optionalCallbacks :
|
||||
cx->runtime->structuredCloneCallbacks;
|
||||
JSAutoStructuredCloneBuffer buf;
|
||||
return buf.write(cx, v) && buf.read(vp);
|
||||
return buf.write(cx, v, callbacks, closure) &&
|
||||
buf.read(vp, cx, callbacks, closure);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
|
|
@ -3207,15 +3207,28 @@ JS_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver);
|
|||
/* The maximum supported structured-clone serialization format version. */
|
||||
#define JS_STRUCTURED_CLONE_VERSION 1
|
||||
|
||||
struct JSStructuredCloneCallbacks {
|
||||
ReadStructuredCloneOp read;
|
||||
WriteStructuredCloneOp write;
|
||||
StructuredCloneErrorOp reportError;
|
||||
};
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_ReadStructuredClone(JSContext *cx, const uint64 *data, size_t nbytes, uint32 version, jsval *vp);
|
||||
JS_ReadStructuredClone(JSContext *cx, const uint64 *data, size_t nbytes,
|
||||
uint32 version, jsval *vp,
|
||||
const JSStructuredCloneCallbacks *optionalCallbacks,
|
||||
void *closure);
|
||||
|
||||
/* Note: On success, the caller is responsible for calling js_free(*datap). */
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **datap, size_t *nbytesp);
|
||||
JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **datap, size_t *nbytesp,
|
||||
const JSStructuredCloneCallbacks *optionalCallbacks,
|
||||
void *closure);
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_StructuredClone(JSContext *cx, jsval v, jsval *vp);
|
||||
JS_StructuredClone(JSContext *cx, jsval v, jsval *vp,
|
||||
const JSStructuredCloneCallbacks *optionalCallbacks,
|
||||
void *closure);
|
||||
|
||||
#ifdef __cplusplus
|
||||
/* RAII sugar for JS_WriteStructuredClone. */
|
||||
|
@ -3280,18 +3293,24 @@ class JSAutoStructuredCloneBuffer {
|
|||
version_ = 0;
|
||||
}
|
||||
|
||||
bool read(jsval *vp, JSContext *cx=NULL) const {
|
||||
bool read(jsval *vp, JSContext *cx=NULL,
|
||||
const JSStructuredCloneCallbacks *optionalCallbacks=NULL,
|
||||
void *closure=NULL) const {
|
||||
if (!cx)
|
||||
cx = cx_;
|
||||
JS_ASSERT(cx);
|
||||
JS_ASSERT(data_);
|
||||
return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp);
|
||||
return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp,
|
||||
optionalCallbacks, closure);
|
||||
}
|
||||
|
||||
bool write(JSContext *cx, jsval v) {
|
||||
bool write(JSContext *cx, jsval v,
|
||||
const JSStructuredCloneCallbacks *optionalCallbacks=NULL,
|
||||
void *closure=NULL) {
|
||||
clear(cx);
|
||||
cx_ = cx;
|
||||
bool ok = !!JS_WriteStructuredClone(cx, v, &data_, &nbytes_);
|
||||
bool ok = !!JS_WriteStructuredClone(cx, v, &data_, &nbytes_,
|
||||
optionalCallbacks, closure);
|
||||
if (!ok) {
|
||||
data_ = NULL;
|
||||
nbytes_ = 0;
|
||||
|
@ -3335,12 +3354,6 @@ class JSAutoStructuredCloneBuffer {
|
|||
|
||||
#define JS_SCERR_RECURSION 0
|
||||
|
||||
struct JSStructuredCloneCallbacks {
|
||||
ReadStructuredCloneOp read;
|
||||
WriteStructuredCloneOp write;
|
||||
StructuredCloneErrorOp reportError;
|
||||
};
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks);
|
||||
|
||||
|
|
|
@ -49,18 +49,20 @@ namespace js
|
|||
{
|
||||
|
||||
bool
|
||||
WriteStructuredClone(JSContext *cx, const Value &v, uint64 **bufp, size_t *nbytesp)
|
||||
WriteStructuredClone(JSContext *cx, const Value &v, uint64 **bufp, size_t *nbytesp,
|
||||
const JSStructuredCloneCallbacks *cb, void *cbClosure)
|
||||
{
|
||||
SCOutput out(cx);
|
||||
JSStructuredCloneWriter w(out);
|
||||
JSStructuredCloneWriter w(out, cb, cbClosure);
|
||||
return w.init() && w.write(v) && out.extractBuffer(bufp, nbytesp);
|
||||
}
|
||||
|
||||
bool
|
||||
ReadStructuredClone(JSContext *cx, const uint64_t *data, size_t nbytes, Value *vp)
|
||||
ReadStructuredClone(JSContext *cx, const uint64_t *data, size_t nbytes, Value *vp,
|
||||
const JSStructuredCloneCallbacks *cb, void *cbClosure)
|
||||
{
|
||||
SCInput in(cx, data, nbytes);
|
||||
JSStructuredCloneReader r(in);
|
||||
JSStructuredCloneReader r(in, cb, cbClosure);
|
||||
return r.read(vp);
|
||||
}
|
||||
|
||||
|
@ -465,9 +467,8 @@ JSStructuredCloneWriter::startObject(JSObject *obj)
|
|||
HashSet<JSObject *>::AddPtr p = memory.lookupForAdd(obj);
|
||||
if (p) {
|
||||
JSContext *cx = context();
|
||||
const JSStructuredCloneCallbacks *cb = cx->runtime->structuredCloneCallbacks;
|
||||
if (cb)
|
||||
cb->reportError(cx, JS_SCERR_RECURSION);
|
||||
if (callbacks && callbacks->reportError)
|
||||
callbacks->reportError(cx, JS_SCERR_RECURSION);
|
||||
else
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SC_RECURSION);
|
||||
return false;
|
||||
|
@ -532,9 +533,8 @@ JSStructuredCloneWriter::startWrite(const js::Value &v)
|
|||
return writeString(SCTAG_STRING_OBJECT, obj->getPrimitiveThis().toString());
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks *cb = context()->runtime->structuredCloneCallbacks;
|
||||
if (cb)
|
||||
return cb->write(context(), this, obj);
|
||||
if (callbacks && callbacks->write)
|
||||
return callbacks->write(context(), this, obj, closure);
|
||||
/* else fall through */
|
||||
}
|
||||
|
||||
|
@ -786,13 +786,12 @@ JSStructuredCloneReader::startRead(Value *vp)
|
|||
if (SCTAG_TYPED_ARRAY_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_MAX)
|
||||
return readTypedArray(tag, data, vp);
|
||||
|
||||
const JSStructuredCloneCallbacks *cb = context()->runtime->structuredCloneCallbacks;
|
||||
if (!cb) {
|
||||
if (!callbacks || !callbacks->read) {
|
||||
JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
|
||||
"unsupported type");
|
||||
return false;
|
||||
}
|
||||
JSObject *obj = cb->read(context(), this, tag, data);
|
||||
JSObject *obj = callbacks->read(context(), this, tag, data, closure);
|
||||
if (!obj)
|
||||
return false;
|
||||
vp->setObject(*obj);
|
||||
|
|
|
@ -49,10 +49,12 @@
|
|||
namespace js {
|
||||
|
||||
bool
|
||||
WriteStructuredClone(JSContext *cx, const Value &v, uint64_t **bufp, size_t *nbytesp);
|
||||
WriteStructuredClone(JSContext *cx, const Value &v, uint64_t **bufp, size_t *nbytesp,
|
||||
const JSStructuredCloneCallbacks *cb, void *cbClosure);
|
||||
|
||||
bool
|
||||
ReadStructuredClone(JSContext *cx, const uint64_t *data, size_t nbytes, Value *vp);
|
||||
ReadStructuredClone(JSContext *cx, const uint64_t *data, size_t nbytes, Value *vp,
|
||||
const JSStructuredCloneCallbacks *cb, void *cbClosure);
|
||||
|
||||
struct SCOutput {
|
||||
public:
|
||||
|
@ -109,8 +111,9 @@ struct SCInput {
|
|||
|
||||
struct JSStructuredCloneReader {
|
||||
public:
|
||||
explicit JSStructuredCloneReader(js::SCInput &in)
|
||||
: in(in), objs(in.context()) {}
|
||||
explicit JSStructuredCloneReader(js::SCInput &in, const JSStructuredCloneCallbacks *cb,
|
||||
void *cbClosure)
|
||||
: in(in), objs(in.context()), callbacks(cb), closure(cbClosure) { }
|
||||
|
||||
js::SCInput &input() { return in; }
|
||||
bool read(js::Value *vp);
|
||||
|
@ -129,13 +132,20 @@ struct JSStructuredCloneReader {
|
|||
|
||||
// Stack of objects with properties remaining to be read.
|
||||
js::AutoValueVector objs;
|
||||
|
||||
// The user defined callbacks that will be used for cloning.
|
||||
const JSStructuredCloneCallbacks *callbacks;
|
||||
|
||||
// Any value passed to JS_ReadStructuredClone.
|
||||
void *closure;
|
||||
};
|
||||
|
||||
struct JSStructuredCloneWriter {
|
||||
public:
|
||||
explicit JSStructuredCloneWriter(js::SCOutput &out)
|
||||
explicit JSStructuredCloneWriter(js::SCOutput &out, const JSStructuredCloneCallbacks *cb,
|
||||
void *cbClosure)
|
||||
: out(out), objs(out.context()), counts(out.context()), ids(out.context()),
|
||||
memory(out.context()) {}
|
||||
memory(out.context()), callbacks(cb), closure(cbClosure) { }
|
||||
|
||||
bool init() { return memory.init(); }
|
||||
|
||||
|
@ -170,6 +180,12 @@ struct JSStructuredCloneWriter {
|
|||
// The "memory" list described in the HTML5 internal structured cloning algorithm.
|
||||
// memory has the same elements as objs.
|
||||
js::HashSet<JSObject *> memory;
|
||||
|
||||
// The user defined callbacks that will be used for cloning.
|
||||
const JSStructuredCloneCallbacks *callbacks;
|
||||
|
||||
// Any value passed to JS_WriteStructuredClone.
|
||||
void *closure;
|
||||
};
|
||||
|
||||
#endif /* jsclone_h___ */
|
||||
|
|
|
@ -584,10 +584,11 @@ typedef JSBool
|
|||
*
|
||||
* tag and data are the pair of uint32 values from the header. The callback may
|
||||
* use the JS_Read* APIs to read any other relevant parts of the object from
|
||||
* the reader r. Return the new object on success, NULL on error/exception.
|
||||
* the reader r. closure is any value passed to the JS_ReadStructuredClone
|
||||
* function. Return the new object on success, NULL on error/exception.
|
||||
*/
|
||||
typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReader *r,
|
||||
uint32 tag, uint32 data);
|
||||
uint32 tag, uint32 data, void *closure);
|
||||
|
||||
/*
|
||||
* Structured data serialization hook. The engine can write primitive values,
|
||||
|
@ -596,11 +597,12 @@ typedef JSObject *(*ReadStructuredCloneOp)(JSContext *cx, JSStructuredCloneReade
|
|||
* the JS_WriteUint32Pair API to write an object header, passing a value
|
||||
* greater than JS_SCTAG_USER to the tag parameter. Then it can use the
|
||||
* JS_Write* APIs to write any other relevant parts of the value v to the
|
||||
* writer w.
|
||||
* writer w. closure is any value passed to the JS_WriteStructuredCLone function.
|
||||
*
|
||||
* Return true on success, false on error/exception.
|
||||
*/
|
||||
typedef JSBool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w, JSObject *obj);
|
||||
typedef JSBool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w,
|
||||
JSObject *obj, void *closure);
|
||||
|
||||
/*
|
||||
* This is called when JS_WriteStructuredClone finds that the object to be
|
||||
|
|
|
@ -4169,7 +4169,7 @@ Serialize(JSContext *cx, uintN argc, jsval *vp)
|
|||
jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
|
||||
uint64 *datap;
|
||||
size_t nbytes;
|
||||
if (!JS_WriteStructuredClone(cx, v, &datap, &nbytes))
|
||||
if (!JS_WriteStructuredClone(cx, v, &datap, &nbytes, NULL, NULL))
|
||||
return false;
|
||||
|
||||
JSObject *arrayobj = js_CreateTypedArray(cx, TypedArray::TYPE_UINT8, nbytes);
|
||||
|
@ -4201,7 +4201,7 @@ Deserialize(JSContext *cx, uintN argc, jsval *vp)
|
|||
}
|
||||
|
||||
if (!JS_ReadStructuredClone(cx, (uint64 *) array->data, array->byteLength,
|
||||
JS_STRUCTURED_CLONE_VERSION, &v)) {
|
||||
JS_STRUCTURED_CLONE_VERSION, &v, NULL, NULL)) {
|
||||
return false;
|
||||
}
|
||||
JS_SET_RVAL(cx, vp, v);
|
||||
|
|
|
@ -294,7 +294,8 @@ class Event
|
|||
}
|
||||
|
||||
bool deserializeData(JSContext *cx, jsval *vp) {
|
||||
return !!JS_ReadStructuredClone(cx, data, nbytes, JS_STRUCTURED_CLONE_VERSION, vp);
|
||||
return !!JS_ReadStructuredClone(cx, data, nbytes, JS_STRUCTURED_CLONE_VERSION, vp,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
virtual Result process(JSContext *cx) = 0;
|
||||
|
@ -307,7 +308,7 @@ class Event
|
|||
{
|
||||
uint64 *data;
|
||||
size_t nbytes;
|
||||
if (!JS_WriteStructuredClone(cx, v, &data, &nbytes))
|
||||
if (!JS_WriteStructuredClone(cx, v, &data, &nbytes, NULL, NULL))
|
||||
return NULL;
|
||||
|
||||
EventType *event = new EventType;
|
||||
|
|
Загрузка…
Ссылка в новой задаче