Rewrite CPOWs to use one actor per process (bug 853209, r=billm,bholley,smaug).

This commit is contained in:
David Anderson 2013-07-03 00:24:32 -07:00
Родитель e9dc650368
Коммит 71e7bc4fc9
55 изменённых файлов: 2376 добавлений и 2628 удалений

Просмотреть файл

@ -25,7 +25,6 @@
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/TestShellChild.h"
#include "mozilla/ipc/XPCShellEnvironment.h"
#include "mozilla/jsipc/PContextWrapperChild.h"
#include "mozilla/layers/CompositorChild.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/PCompositorChild.h"
@ -50,6 +49,7 @@
#include "nsDebugImpl.h"
#include "nsHashPropertyBag.h"
#include "nsLayoutStylesheetCache.h"
#include "nsIJSRuntimeService.h"
#include "IHistory.h"
#include "nsDocShellCID.h"
@ -64,6 +64,7 @@
#include "nsFrameMessageManager.h"
#include "nsIGeolocationProvider.h"
#include "JavaScriptParent.h"
#include "mozilla/dom/PMemoryReportRequestChild.h"
#ifdef MOZ_PERMISSIONS
@ -109,6 +110,7 @@
#include "nsIPrincipal.h"
#include "nsDeviceStorage.h"
#include "AudioChannelService.h"
#include "JavaScriptChild.h"
#include "ProcessPriorityManager.h"
using namespace base;
@ -123,6 +125,7 @@ using namespace mozilla::hal_sandbox;
using namespace mozilla::ipc;
using namespace mozilla::layers;
using namespace mozilla::net;
using namespace mozilla::jsipc;
#if defined(MOZ_WIDGET_GONK)
using namespace mozilla::system;
#endif
@ -552,6 +555,31 @@ static void FirstIdle(void)
ContentChild::GetSingleton()->SendFirstIdle();
}
mozilla::jsipc::PJavaScriptChild *
ContentChild::AllocPJavaScript()
{
nsCOMPtr<nsIJSRuntimeService> svc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
NS_ENSURE_TRUE(svc, NULL);
JSRuntime *rt;
svc->GetRuntime(&rt);
NS_ENSURE_TRUE(svc, NULL);
mozilla::jsipc::JavaScriptChild *child = new mozilla::jsipc::JavaScriptChild(rt);
if (!child->init()) {
delete child;
return NULL;
}
return child;
}
bool
ContentChild::DeallocPJavaScript(PJavaScriptChild *child)
{
delete child;
return true;
}
PBrowserChild*
ContentChild::AllocPBrowser(const IPCTabContext& aContext,
const uint32_t& aChromeFlags)
@ -760,10 +788,19 @@ ContentChild::DeallocPTestShell(PTestShellChild* shell)
return true;
}
jsipc::JavaScriptChild *
ContentChild::GetCPOWManager()
{
if (ManagedPJavaScriptChild().Length()) {
return static_cast<JavaScriptChild*>(ManagedPJavaScriptChild()[0]);
}
JavaScriptChild* actor = static_cast<JavaScriptChild*>(SendPJavaScriptConstructor());
return actor;
}
bool
ContentChild::RecvPTestShellConstructor(PTestShellChild* actor)
{
actor->SendPContextWrapperConstructor()->SendPObjectWrapperConstructor(true);
return true;
}

Просмотреть файл

@ -28,6 +28,10 @@ class OptionalURIParams;
class URIParams;
}// namespace ipc
namespace jsipc {
class JavaScriptChild;
}
namespace layers {
class PCompositorChild;
} // namespace layers
@ -126,6 +130,7 @@ public:
virtual PTestShellChild* AllocPTestShell();
virtual bool DeallocPTestShell(PTestShellChild*);
virtual bool RecvPTestShellConstructor(PTestShellChild*);
jsipc::JavaScriptChild *GetCPOWManager();
virtual PNeckoChild* AllocPNecko();
virtual bool DeallocPNecko(PNeckoChild*);
@ -156,6 +161,9 @@ public:
const InfallibleTArray<OverrideMapping>& overrides,
const nsCString& locale);
virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScript();
virtual bool DeallocPJavaScript(mozilla::jsipc::PJavaScriptChild*);
virtual bool RecvSetOffline(const bool& offline);
virtual bool RecvNotifyVisited(const URIParams& aURI);

Просмотреть файл

@ -119,6 +119,7 @@ using namespace mozilla::system;
#include "BluetoothService.h"
#endif
#include "JavaScriptParent.h"
#include "Crypto.h"
#ifdef MOZ_WEBSPEECH
@ -140,6 +141,7 @@ using namespace mozilla::idl;
using namespace mozilla::ipc;
using namespace mozilla::layers;
using namespace mozilla::net;
using namespace mozilla::jsipc;
namespace mozilla {
namespace dom {
@ -1008,6 +1010,16 @@ ContentParent::NotifyTabDestroyed(PBrowserParent* aTab,
}
}
jsipc::JavaScriptParent*
ContentParent::GetCPOWManager()
{
if (ManagedPJavaScriptParent().Length()) {
return static_cast<JavaScriptParent*>(ManagedPJavaScriptParent()[0]);
}
JavaScriptParent* actor = static_cast<JavaScriptParent*>(SendPJavaScriptConstructor());
return actor;
}
TestShellParent*
ContentParent::CreateTestShell()
{
@ -1578,6 +1590,24 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline)
return true;
}
mozilla::jsipc::PJavaScriptParent *
ContentParent::AllocPJavaScript()
{
mozilla::jsipc::JavaScriptParent *parent = new mozilla::jsipc::JavaScriptParent();
if (!parent->init()) {
delete parent;
return NULL;
}
return parent;
}
bool
ContentParent::DeallocPJavaScript(PJavaScriptParent *parent)
{
static_cast<mozilla::jsipc::JavaScriptParent *>(parent)->destroyFromContent();
return true;
}
PBrowserParent*
ContentParent::AllocPBrowser(const IPCTabContext& aContext,
const uint32_t &aChromeFlags)

Просмотреть файл

@ -44,6 +44,10 @@ class URIParams;
class TestShellParent;
} // namespace ipc
namespace jsipc {
class JavaScriptParent;
}
namespace layers {
class PCompositorParent;
} // namespace layers
@ -126,6 +130,7 @@ public:
TestShellParent* CreateTestShell();
bool DestroyTestShell(TestShellParent* aTestShell);
TestShellParent* GetTestShellSingleton();
jsipc::JavaScriptParent *GetCPOWManager();
void ReportChildAlreadyBlocked();
bool RequestRunToCompletion();
@ -194,6 +199,7 @@ private:
// using them.
using PContentParent::SendPBrowserConstructor;
using PContentParent::SendPTestShellConstructor;
using PContentParent::SendPJavaScriptConstructor;
// No more than one of !!aApp, aIsForBrowser, and aIsForPreallocated may be
// true.
@ -251,6 +257,9 @@ private:
bool* aIsForBrowser) MOZ_OVERRIDE;
virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline) MOZ_OVERRIDE;
virtual mozilla::jsipc::PJavaScriptParent* AllocPJavaScript();
virtual bool DeallocPJavaScript(mozilla::jsipc::PJavaScriptParent*);
virtual PBrowserParent* AllocPBrowser(const IPCTabContext& aContext,
const uint32_t& aChromeFlags);
virtual bool DeallocPBrowser(PBrowserParent* frame);

Просмотреть файл

@ -42,6 +42,7 @@ LOCAL_INCLUDES += \
-I$(topsrcdir)/dom/bluetooth \
-I$(topsrcdir)/dom/bluetooth/ipc \
-I$(topsrcdir)/content/media/webspeech/synth/ipc \
-I$(topsrcdir)/js/ipc \
$(NULL)
DEFINES += -DBIN_SUFFIX='"$(BIN_SUFFIX)"'

Просмотреть файл

@ -20,6 +20,7 @@ include protocol PSms;
include protocol PSpeechSynthesis;
include protocol PStorage;
include protocol PTestShell;
include protocol PJavaScript;
include DOMTypes;
include InputStreamParams;
include URIParams;
@ -252,6 +253,7 @@ rpc protocol PContent
manages PSpeechSynthesis;
manages PStorage;
manages PTestShell;
manages PJavaScript;
both:
// Depending on exactly how the new browser is being created, it might be
@ -281,6 +283,8 @@ both:
async PBlob(BlobConstructorParams params);
PJavaScript();
child:
/**
* Update OS process privileges to |privs|. Can usually only be

Просмотреть файл

@ -5,7 +5,6 @@
include protocol PContent;
include protocol PTestShellCommand;
include protocol PContextWrapper;
namespace mozilla {
namespace ipc {
@ -15,7 +14,6 @@ rpc protocol PTestShell
manager PContent;
manages PTestShellCommand;
manages PContextWrapper;
child:
__delete__();
@ -23,10 +21,6 @@ child:
ExecuteCommand(nsString aCommand);
PTestShellCommand(nsString aCommand);
parent:
PContextWrapper();
};
} // namespace ipc

Просмотреть файл

@ -3,13 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TestShellChild.h"
#include "mozilla/jsipc/ContextWrapperChild.h"
using mozilla::ipc::TestShellChild;
using mozilla::ipc::PTestShellCommandChild;
using mozilla::ipc::XPCShellEnvironment;
using mozilla::jsipc::PContextWrapperChild;
using mozilla::jsipc::ContextWrapperChild;
TestShellChild::TestShellChild()
: mXPCShell(XPCShellEnvironment::CreateEnvironment())
@ -57,19 +54,3 @@ TestShellChild::RecvPTestShellCommandConstructor(PTestShellCommandChild* aActor,
return PTestShellCommandChild::Send__delete__(aActor, response);
}
PContextWrapperChild*
TestShellChild::AllocPContextWrapper()
{
JSContext* cx;
if (mXPCShell && (cx = mXPCShell->GetContext())) {
return new ContextWrapperChild(cx);
}
return NULL;
}
bool
TestShellChild::DeallocPContextWrapper(PContextWrapperChild* actor)
{
delete actor;
return true;
}

Просмотреть файл

@ -13,10 +13,6 @@
namespace mozilla {
namespace jsipc {
class PContextWrapperChild;
}
namespace ipc {
class XPCShellEnvironment;
@ -39,9 +35,6 @@ public:
bool
DeallocPTestShellCommand(PTestShellCommandChild* aCommand);
PContextWrapperChild* AllocPContextWrapper();
bool DeallocPContextWrapper(PContextWrapperChild* actor);
private:
nsAutoPtr<XPCShellEnvironment> mXPCShell;
};

Просмотреть файл

@ -8,7 +8,6 @@
#include "mozilla/Util.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/jsipc/ContextWrapperParent.h"
#include "nsAutoPtr.h"
@ -17,8 +16,6 @@ using mozilla::ipc::TestShellParent;
using mozilla::ipc::TestShellCommandParent;
using mozilla::ipc::PTestShellCommandParent;
using mozilla::dom::ContentParent;
using mozilla::jsipc::PContextWrapperParent;
using mozilla::jsipc::ContextWrapperParent;
PTestShellCommandParent*
TestShellParent::AllocPTestShellCommand(const nsString& aCommand)
@ -44,32 +41,6 @@ TestShellParent::CommandDone(TestShellCommandParent* command,
return true;
}
PContextWrapperParent*
TestShellParent::AllocPContextWrapper()
{
return new ContextWrapperParent();
}
bool
TestShellParent::DeallocPContextWrapper(PContextWrapperParent* actor)
{
delete actor;
return true;
}
JSBool
TestShellParent::GetGlobalJSObject(JSContext* cx, JSObject** globalp)
{
// TODO Unify this code with TabParent::GetGlobalJSObject.
InfallibleTArray<PContextWrapperParent*> cwps(1);
ManagedPContextWrapperParent(cwps);
if (cwps.Length() < 1)
return JS_FALSE;
NS_ASSERTION(cwps.Length() == 1, "More than one PContextWrapper?");
ContextWrapperParent* cwp = static_cast<ContextWrapperParent*>(cwps[0]);
return cwp->GetGlobalJSObject(cx, globalp);
}
JSBool
TestShellCommandParent::SetCallback(JSContext* aCx,
JS::Value aCallback)

Просмотреть файл

@ -20,10 +20,6 @@ class JSObject;
namespace mozilla {
namespace jsipc {
class PContextWrapperParent;
}
namespace ipc {
class TestShellCommandParent;
@ -39,11 +35,6 @@ public:
bool
CommandDone(TestShellCommandParent* aActor, const nsString& aResponse);
PContextWrapperParent* AllocPContextWrapper();
bool DeallocPContextWrapper(PContextWrapperParent* actor);
JSBool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
};

Просмотреть файл

@ -1,64 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef mozilla_jsipc_CPOWTypes_h
#define mozilla_jsipc_CPOWTypes_h
#include "jsapi.h"
#include "jspubtd.h"
using mozilla::void_t;
namespace mozilla {
namespace jsipc {
template <typename P>
struct CPOWSingleton
{
static void Write(IPC::Message*, const P&) {}
static bool Read(const IPC::Message*, void**, P*) { return true; }
};
template <typename Type, typename As>
struct CPOWConvertible
{
static void Write(IPC::Message* m, const Type& t) {
WriteParam(m, As(t));
}
static bool Read(const IPC::Message* m, void** iter, Type* tp) {
As a;
return (ReadParam(m, iter, &a) &&
(*tp = Type(a), true));
}
};
} // namespace jsipc
} // namespace mozilla
namespace IPC {
template <> struct ParamTraits<JSType> : public mozilla::jsipc::CPOWConvertible<JSType, int> {};
}
// TODO Use a more standard logging mechanism.
#ifdef LOGGING
#define CPOW_LOG(PRINTF_ARGS) \
JS_BEGIN_MACRO \
printf("CPOW | "); \
printf PRINTF_ARGS ; \
printf("\n"); \
JS_END_MACRO
#define JSVAL_TO_CSTR(CX, V) \
NS_ConvertUTF16toUTF8(nsString(JS_GetStringChars(JS_ValueToString(CX, V)))).get()
#else
#define CPOW_LOG(_) JS_BEGIN_MACRO JS_END_MACRO
#define JSVAL_TO_CSTR(CX, V) ((char*)0)
#endif
#endif /* mozilla_jsipc_CPOWTypes_h */

Просмотреть файл

@ -1,80 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef mozilla_jsipc_ContextWrapperChild_h
#define mozilla_jsipc_ContextWrapperChild_h
#include "mozilla/jsipc/PContextWrapperChild.h"
#include "mozilla/jsipc/ObjectWrapperChild.h"
#include "jsapi.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
namespace mozilla {
namespace jsipc {
class ContextWrapperChild
: public PContextWrapperChild
{
public:
ContextWrapperChild(JSContext* cx)
: mContext(cx)
{
mResidentObjectTable.Init();
}
JSContext* GetContext() { return mContext; }
PObjectWrapperChild* GetOrCreateWrapper(JSObject* obj_,
bool makeGlobal = false)
{
if (!obj_) // Don't wrap nothin'!
return NULL;
JS::RootedObject obj(mContext, obj_);
PObjectWrapperChild* wrapper;
while (!mResidentObjectTable.Get(obj, &wrapper)) {
wrapper = SendPObjectWrapperConstructor(AllocPObjectWrapper(obj),
makeGlobal);
if (wrapper)
mResidentObjectTable.Put(obj, wrapper);
else
return NULL;
}
return wrapper;
}
protected:
PObjectWrapperChild* AllocPObjectWrapper(JSObject* obj) {
return new ObjectWrapperChild(mContext, obj);
}
PObjectWrapperChild* AllocPObjectWrapper(const bool&) {
// This stuff is unused and billm has a patch to delete it.
JSAutoRequest ar(mContext);
return AllocPObjectWrapper(JS_GetGlobalForScopeChain(mContext));
}
bool DeallocPObjectWrapper(PObjectWrapperChild* actor) {
ObjectWrapperChild* owc = static_cast<ObjectWrapperChild*>(actor);
mResidentObjectTable.Remove(owc->GetJSObject());
return true;
}
private:
JSContext* const mContext;
nsClassHashtable<nsPtrHashKey<JSObject>,
PObjectWrapperChild> mResidentObjectTable;
};
}}
#endif /* mozilla_jsipc_ContextWrapperChild_h */

Просмотреть файл

@ -1,83 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef mozilla_jsipc_ContextWrapperParent_h
#define mozilla_jsipc_ContextWrapperParent_h
#include "mozilla/jsipc/PContextWrapperParent.h"
#include "mozilla/jsipc/ObjectWrapperParent.h"
#include "mozilla/jsipc/CPOWTypes.h"
#include "mozilla/dom/ContentParent.h"
#include "jsapi.h"
#include "nsAutoJSValHolder.h"
namespace mozilla {
namespace jsipc {
using mozilla::dom::ContentParent;
class ContextWrapperParent
: public PContextWrapperParent
{
public:
ContextWrapperParent()
: mGlobal(NULL)
{}
JSBool GetGlobalJSObject(JSContext* cx, JSObject** globalp) {
if (!mGlobal)
return JS_FALSE;
mGlobalHolder.Hold(cx);
mGlobalHolder = *globalp = mGlobal->GetJSObject(cx);
return JS_TRUE;
}
ObjectWrapperParent* GetGlobalObjectWrapper() const {
return mGlobal;
}
bool RequestRunToCompletion() {
return false;
}
private:
ObjectWrapperParent* mGlobal;
nsAutoJSValHolder mGlobalHolder;
PObjectWrapperParent* AllocPObjectWrapper(const bool&) {
return new ObjectWrapperParent();
}
bool RecvPObjectWrapperConstructor(PObjectWrapperParent* actor,
const bool& makeGlobal)
{
if (makeGlobal) {
mGlobalHolder.Release();
mGlobal = static_cast<ObjectWrapperParent*>(actor);
}
return true;
}
bool DeallocPObjectWrapper(PObjectWrapperParent* actor)
{
if (mGlobal &&
mGlobal == static_cast<ObjectWrapperParent*>(actor)) {
mGlobalHolder.Release();
mGlobal = NULL;
}
delete actor;
return true;
}
};
}}
#endif /* mozilla_jsipc_ContextWrapperParent_h */

605
js/ipc/JavaScriptChild.cpp Normal file
Просмотреть файл

@ -0,0 +1,605 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* 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 "JavaScriptChild.h"
#include "mozilla/dom/ContentChild.h"
#include "nsContentUtils.h"
#include "xpcprivate.h"
#include "jsfriendapi.h"
#include "nsCxPusher.h"
using namespace JS;
using namespace mozilla;
using namespace mozilla::jsipc;
using mozilla::AutoSafeJSContext;
JavaScriptChild::JavaScriptChild(JSRuntime *rt)
: lastId_(0),
rt_(rt)
{
}
static void
Trace(JSTracer *trc, void *data)
{
reinterpret_cast<JavaScriptChild *>(data)->trace(trc);
}
JavaScriptChild::~JavaScriptChild()
{
JS_RemoveExtraGCRootsTracer(rt_, Trace, this);
}
void
JavaScriptChild::trace(JSTracer *trc)
{
objects_.trace(trc);
ids_.trace(trc);
}
bool
JavaScriptChild::init()
{
if (!JavaScriptShared::init())
return false;
if (!ids_.init())
return false;
JS_AddExtraGCRootsTracer(rt_, Trace, this);
return true;
}
bool
JavaScriptChild::RecvDropObject(const ObjectId &objId)
{
JSObject *obj = findObject(objId);
if (obj) {
ids_.remove(obj);
objects_.remove(objId);
}
return true;
}
bool
JavaScriptChild::makeId(JSContext *cx, JSObject *obj, ObjectId *idp)
{
if (!obj) {
*idp = 0;
return true;
}
ObjectId id = ids_.find(obj);
if (id) {
*idp = id;
return true;
}
id = ++lastId_;
if (id > MAX_CPOW_IDS) {
JS_ReportError(cx, "CPOW id limit reached");
return false;
}
id <<= OBJECT_EXTRA_BITS;
if (JS_ObjectIsCallable(cx, obj))
id |= OBJECT_IS_CALLABLE;
if (!objects_.add(id, obj))
return false;
if (!ids_.add(obj, id))
return false;
*idp = id;
return true;
}
JSObject *
JavaScriptChild::unwrap(JSContext *cx, ObjectId id)
{
JSObject *obj = findObject(id);
MOZ_ASSERT(obj);
return obj;
}
bool
JavaScriptChild::fail(JSContext *cx, ReturnStatus *rs)
{
// By default, we set |undefined| unless we can get a more meaningful
// exception.
*rs = ReturnStatus(false, JSVariant(void_t()));
// Note we always return true from this function, since this propagates
// to the IPC code, and we don't want a JS failure to cause the death
// of the child process.
jsval exn;
if (!JS_GetPendingException(cx, &exn))
return true;
// If we don't clear the pending exception, JS will try to wrap it as it
// leaves the current compartment. Since there is no previous compartment,
// that would crash.
JS_ClearPendingException(cx);
if (!toVariant(cx, exn, &rs->exn()))
return true;
return true;
}
bool
JavaScriptChild::ok(ReturnStatus *rs)
{
*rs = ReturnStatus(true, JSVariant(void_t()));
return true;
}
bool
JavaScriptChild::AnswerHas(const ObjectId &objId, const nsString &id,
ReturnStatus *rs, bool *bp)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSAutoCompartment comp(cx, obj);
RootedId internedId(cx);
if (!convertGeckoStringToId(cx, id, &internedId))
return fail(cx, rs);
JSBool found;
if (!JS_HasPropertyById(cx, obj, internedId, &found))
return fail(cx, rs);
*bp = !!found;
return ok(rs);
}
bool
JavaScriptChild::AnswerHasOwn(const ObjectId &objId, const nsString &id,
ReturnStatus *rs, bool *bp)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSAutoCompartment comp(cx, obj);
RootedId internedId(cx);
if (!convertGeckoStringToId(cx, id, &internedId))
return fail(cx, rs);
JSPropertyDescriptor desc;
if (!JS_GetPropertyDescriptorById(cx, obj, internedId, 0, &desc))
return fail(cx, rs);
*bp = (desc.obj == obj);
return ok(rs);
}
bool
JavaScriptChild::AnswerGet(const ObjectId &objId, const ObjectId &receiverId,
const nsString &id,
ReturnStatus *rs, JSVariant *result)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
RootedObject receiver(cx, findObject(receiverId));
if (!receiver)
return false;
JSAutoCompartment comp(cx, obj);
RootedId internedId(cx);
if (!convertGeckoStringToId(cx, id, &internedId))
return fail(cx, rs);
JS::Value val;
if (!JS_ForwardGetPropertyTo(cx, obj, internedId, receiver, &val))
return fail(cx, rs);
if (!toVariant(cx, val, result))
return fail(cx, rs);
return ok(rs);
}
bool
JavaScriptChild::AnswerSet(const ObjectId &objId, const ObjectId &receiverId,
const nsString &id, const bool &strict,
const JSVariant &value,
ReturnStatus *rs, JSVariant *result)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
// The outparam will be written to the buffer, so it must be set even if
// the parent won't read it.
*result = void_t();
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
RootedObject receiver(cx, findObject(receiverId));
if (!receiver)
return false;
JSAutoCompartment comp(cx, obj);
RootedId internedId(cx);
if (!convertGeckoStringToId(cx, id, &internedId))
return fail(cx, rs);
MOZ_ASSERT(obj == receiver);
RootedValue val(cx);
if (!toValue(cx, value, &val))
return fail(cx, rs);
if (!JS_SetPropertyById(cx, obj, internedId, val.address()))
return fail(cx, rs);
if (!toVariant(cx, val, result))
return fail(cx, rs);
return ok(rs);
}
bool
JavaScriptChild::AnswerCall(const ObjectId &objId,
const nsTArray<JSParam> &argv,
ReturnStatus *rs,
JSVariant *result,
nsTArray<JSParam> *outparams)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
// The outparam will be written to the buffer, so it must be set even if
// the parent won't read it.
*result = void_t();
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
MOZ_ASSERT(argv.Length() >= 2);
RootedValue objv(cx);
if (!toValue(cx, argv[0], &objv))
return fail(cx, rs);
JSAutoCompartment comp(cx, &objv.toObject());
*result = JSVariant(void_t());
JS::AutoValueVector vals(cx);
JS::AutoValueVector outobjects(cx);
for (size_t i = 0; i < argv.Length(); i++) {
if (argv[i].type() == JSParam::Tvoid_t) {
// This is an outparam.
JSCompartment *compartment = js::GetContextCompartment(cx);
RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, compartment));
RootedObject obj(cx, xpc::NewOutObject(cx, global));
if (!obj)
return fail(cx, rs);
if (!outobjects.append(ObjectValue(*obj)))
return fail(cx, rs);
if (!vals.append(ObjectValue(*obj)))
return fail(cx, rs);
} else {
RootedValue v(cx);
if (!toValue(cx, argv[i].get_JSVariant(), &v))
return fail(cx, rs);
if (!vals.append(v))
return fail(cx, rs);
}
}
uint32_t oldOpts =
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);
jsval rval;
bool success = JS::Call(cx, vals[1], vals[0], vals.length() - 2, vals.begin() + 2, &rval);
JS_SetOptions(cx, oldOpts);
if (!success)
return fail(cx, rs);
if (!toVariant(cx, rval, result))
return fail(cx, rs);
// Prefill everything with a dummy jsval.
for (size_t i = 0; i < outobjects.length(); i++)
outparams->AppendElement(JSParam(void_t()));
// Go through each argument that was an outparam, retrieve the "value"
// field, and add it to a temporary list. We need to do this separately
// because the outparams vector is not rooted.
vals.clear();
for (size_t i = 0; i < outobjects.length(); i++) {
RootedObject obj(cx, &outobjects[i].toObject());
jsval v;
JSBool found;
if (JS_HasProperty(cx, obj, "value", &found)) {
if (!JS_GetProperty(cx, obj, "value", &v))
return fail(cx, rs);
} else {
v = UndefinedValue();
}
if (!vals.append(v))
return fail(cx, rs);
}
// Copy the outparams. If any outparam is already set to a void_t, we
// treat this as the outparam never having been set.
for (size_t i = 0; i < vals.length(); i++) {
JSVariant variant;
if (!toVariant(cx, vals[i], &variant))
return fail(cx, rs);
outparams->ReplaceElementAt(i, JSParam(variant));
}
return ok(rs);
}
bool
JavaScriptChild::AnswerInstanceOf(const ObjectId &objId,
const JSIID &iid,
ReturnStatus *rs,
bool *instanceof)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSAutoCompartment comp(cx, obj);
nsID nsiid;
ConvertID(iid, &nsiid);
nsresult rv = xpc::HasInstance(cx, obj, &nsiid, instanceof);
if (rv != NS_OK)
return fail(cx, rs);
return ok(rs);
}
void
EmptyDesc(PPropertyDescriptor *desc)
{
desc->objId() = 0;
desc->attrs() = 0;
desc->shortid() = 0;
desc->value() = void_t();
desc->getter() = 0;
desc->setter() = 0;
}
bool
JavaScriptChild::AnswerGetPropertyDescriptor(const ObjectId &objId,
const nsString &id,
const uint32_t &flags,
ReturnStatus *rs,
PPropertyDescriptor *out)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSAutoCompartment comp(cx, obj);
RootedId internedId(cx);
if (!convertGeckoStringToId(cx, id, &internedId))
return fail(cx, rs);
JSPropertyDescriptor desc;
if (!JS_GetPropertyDescriptorById(cx, obj, internedId, flags, &desc))
return fail(cx, rs);
if (!desc.obj) {
EmptyDesc(out);
return ok(rs);
}
if (!fromDescriptor(cx, desc, out))
return fail(cx, rs);
return ok(rs);
}
bool
JavaScriptChild::AnswerGetOwnPropertyDescriptor(const ObjectId &objId,
const nsString &id,
const uint32_t &flags,
ReturnStatus *rs,
PPropertyDescriptor *out)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSAutoCompartment comp(cx, obj);
RootedId internedId(cx);
if (!convertGeckoStringToId(cx, id, &internedId))
return fail(cx, rs);
JSPropertyDescriptor desc;
if (!JS_GetPropertyDescriptorById(cx, obj, internedId, flags, &desc))
return fail(cx, rs);
if (desc.obj != obj) {
EmptyDesc(out);
return ok(rs);
}
if (!fromDescriptor(cx, desc, out))
return fail(cx, rs);
return ok(rs);
}
bool
JavaScriptChild::AnswerGetOwnPropertyNames(const ObjectId &objId,
ReturnStatus *rs,
nsTArray<nsString> *names)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSAutoCompartment comp(cx, obj);
AutoIdVector props(cx);
if (!js::GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &props))
return fail(cx, rs);
for (size_t i = 0; i < props.length(); i++) {
nsString name;
if (!convertIdToGeckoString(cx, props.handleAt(i), &name))
return false;
names->AppendElement(name);
}
return ok(rs);
}
bool
JavaScriptChild::AnswerKeys(const ObjectId &objId,
ReturnStatus *rs,
nsTArray<nsString> *names)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSAutoCompartment comp(cx, obj);
AutoIdVector props(cx);
if (!js::GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
return fail(cx, rs);
for (size_t i = 0; i < props.length(); i++) {
nsString name;
if (!convertIdToGeckoString(cx, props.handleAt(i), &name))
return false;
names->AppendElement(name);
}
return ok(rs);
}
bool
JavaScriptChild::AnswerObjectClassIs(const ObjectId &objId,
const uint32_t &classValue,
bool *result)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSAutoCompartment comp(cx, obj);
*result = js_ObjectClassIs(cx, obj, (js::ESClassValue)classValue);
return true;
}
bool
JavaScriptChild::AnswerClassName(const ObjectId &objId,
nsString *name)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSAutoCompartment comp(cx, obj);
*name = NS_ConvertASCIItoUTF16(js_ObjectClassName(cx, obj));
return true;
}
bool
JavaScriptChild::AnswerIsExtensible(const ObjectId &objId,
ReturnStatus *rs,
bool *result)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
*result = false;
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSBool extensible;
if (!JS_IsExtensible(cx, obj, &extensible))
return fail(cx, rs);
*result = !!extensible;
return true;
}
bool
JavaScriptChild::AnswerPreventExtensions(const ObjectId &objId,
ReturnStatus *rs)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
RootedObject obj(cx, findObject(objId));
if (!obj)
return false;
JSAutoCompartment comp(cx, obj);
if (!JS_PreventExtensions(cx, obj))
return fail(cx, rs);
return ok(rs);
}

95
js/ipc/JavaScriptChild.h Normal file
Просмотреть файл

@ -0,0 +1,95 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* 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/. */
#ifndef mozilla_jsipc_JavaScriptChild_h_
#define mozilla_jsipc_JavaScriptChild_h_
#include "JavaScriptShared.h"
#include "mozilla/jsipc/PJavaScriptChild.h"
namespace mozilla {
namespace jsipc {
class JavaScriptChild
: public PJavaScriptChild,
public JavaScriptShared
{
public:
JavaScriptChild(JSRuntime *rt);
~JavaScriptChild();
bool init();
void trace(JSTracer *trc);
bool RecvDropObject(const ObjectId &objId);
bool AnswerHas(const ObjectId &objId, const nsString &id,
ReturnStatus *rs, bool *bp);
bool AnswerHasOwn(const ObjectId &objId, const nsString &id,
ReturnStatus *rs, bool *bp);
bool AnswerGet(const ObjectId &objId, const ObjectId &receiverId,
const nsString &id,
ReturnStatus *rs, JSVariant *result);
bool AnswerSet(const ObjectId &objId, const ObjectId &receiverId,
const nsString &id, const bool &strict,
const JSVariant &value,
ReturnStatus *rs, JSVariant *result);
bool AnswerCall(const ObjectId &objId,
const nsTArray<JSParam> &argv,
ReturnStatus *rs,
JSVariant *result,
nsTArray<JSParam> *outparams);
bool AnswerInstanceOf(const ObjectId &objId,
const JSIID &iid,
ReturnStatus *rs,
bool *instanceof);
bool AnswerGetPropertyDescriptor(const ObjectId &objId,
const nsString &id,
const uint32_t &flags,
ReturnStatus *rs,
PPropertyDescriptor *out);
bool AnswerGetOwnPropertyDescriptor(const ObjectId &objId,
const nsString &id,
const uint32_t &flags,
ReturnStatus *rs,
PPropertyDescriptor *out);
bool AnswerGetOwnPropertyNames(const ObjectId &objId,
ReturnStatus *rs,
nsTArray<nsString> *names);
bool AnswerKeys(const ObjectId &objId,
ReturnStatus *rs,
nsTArray<nsString> *names);
bool AnswerObjectClassIs(const ObjectId &objId,
const uint32_t &classValue,
bool *result);
bool AnswerClassName(const ObjectId &objId,
nsString *result);
bool AnswerIsExtensible(const ObjectId &objId,
ReturnStatus *rs,
bool *result);
bool AnswerPreventExtensions(const ObjectId &objId,
ReturnStatus *rs);
protected:
JSObject *unwrap(JSContext *cx, ObjectId id);
private:
bool makeId(JSContext *cx, JSObject *obj, ObjectId *idp);
bool fail(JSContext *cx, ReturnStatus *rs);
bool ok(ReturnStatus *rs);
private:
ObjectId lastId_;
JSRuntime *rt_;
ObjectIdCache ids_;
};
} // mozilla
} // jsipc
#endif

635
js/ipc/JavaScriptParent.cpp Normal file
Просмотреть файл

@ -0,0 +1,635 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* 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 "JavaScriptParent.h"
#include "mozilla/dom/ContentParent.h"
#include "nsJSUtils.h"
#include "jsfriendapi.h"
#include "jsproxy.h"
#include "HeapAPI.h"
#include "xpcprivate.h"
#include "mozilla/Casting.h"
using namespace js;
using namespace JS;
using namespace mozilla;
using namespace mozilla::jsipc;
JavaScriptParent::JavaScriptParent()
: refcount_(1),
inactive_(false)
{
}
static inline JavaScriptParent *
ParentOf(JSObject *obj)
{
MOZ_ASSERT(JavaScriptParent::IsCPOW(obj));
return reinterpret_cast<JavaScriptParent *>(GetProxyExtra(obj, 0).toPrivate());
}
ObjectId
JavaScriptParent::idOf(JSObject *obj)
{
MOZ_ASSERT(JavaScriptParent::IsCPOW(obj));
Value v = GetProxyExtra(obj, 1);
MOZ_ASSERT(v.isDouble());
ObjectId objId = BitwiseCast<uint64_t>(v.toDouble());
MOZ_ASSERT(findObject(objId) == obj);
MOZ_ASSERT(objId);
return objId;
}
int sCPOWProxyHandler;
class CPOWProxyHandler : public BaseProxyHandler
{
public:
CPOWProxyHandler()
: BaseProxyHandler(&sCPOWProxyHandler) {}
virtual ~CPOWProxyHandler() {}
virtual bool finalizeInBackground(Value priv) MOZ_OVERRIDE {
return false;
}
virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
PropertyDescriptor *desc, unsigned flags) MOZ_OVERRIDE;
virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy,
HandleId id, PropertyDescriptor *desc, unsigned flags) MOZ_OVERRIDE;
virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
PropertyDescriptor *desc) MOZ_OVERRIDE;
virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy,
AutoIdVector &props) MOZ_OVERRIDE;
virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
HandleId id, MutableHandleValue vp) MOZ_OVERRIDE;
virtual bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, bool strict, JS::MutableHandleValue vp) MOZ_OVERRIDE;
virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
MutableHandleValue vp) MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE;
virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE;
virtual const char* className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
static CPOWProxyHandler singleton;
};
CPOWProxyHandler CPOWProxyHandler::singleton;
bool
CPOWProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
PropertyDescriptor *desc, unsigned flags)
{
return ParentOf(proxy)->getPropertyDescriptor(cx, proxy, id, desc, flags);
}
bool
JavaScriptParent::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
PropertyDescriptor *desc, unsigned flags)
{
ObjectId objId = idOf(proxy);
nsString idstr;
if (!convertIdToGeckoString(cx, id, &idstr))
return false;
ReturnStatus status;
PPropertyDescriptor result;
if (!CallGetPropertyDescriptor(objId, idstr, flags, &status, &result))
return ipcfail(cx);
if (!ok(cx, status))
return false;
return toDescriptor(cx, result, desc);
}
bool
CPOWProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy,
HandleId id, PropertyDescriptor *desc, unsigned flags)
{
return ParentOf(proxy)->getOwnPropertyDescriptor(cx, proxy, id, desc, flags);
}
bool
JavaScriptParent::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
PropertyDescriptor *desc, unsigned flags)
{
ObjectId objId = idOf(proxy);
nsString idstr;
if (!convertIdToGeckoString(cx, id, &idstr))
return false;
ReturnStatus status;
PPropertyDescriptor result;
if (!CallGetOwnPropertyDescriptor(objId, idstr, flags, &status, &result))
return ipcfail(cx);
if (!ok(cx, status))
return false;
return toDescriptor(cx, result, desc);
}
bool
CPOWProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
PropertyDescriptor *desc)
{
MOZ_CRASH("unimplemented");
}
bool
CPOWProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props)
{
return ParentOf(proxy)->getOwnPropertyNames(cx, proxy, props);
}
bool
JavaScriptParent::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props)
{
ObjectId objId = idOf(proxy);
ReturnStatus status;
InfallibleTArray<nsString> names;
if (!CallGetOwnPropertyNames(objId, &status, &names))
return ipcfail(cx);
if (!ok(cx, status))
return false;
RootedId name(cx);
for (size_t i = 0; i < names.Length(); i++) {
if (!convertGeckoStringToId(cx, names[i], &name))
return false;
if (!props.append(name))
return false;
}
return true;
}
bool
CPOWProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
{
return ParentOf(proxy)->keys(cx, proxy, props);
}
bool
JavaScriptParent::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
{
ObjectId objId = idOf(proxy);
ReturnStatus status;
InfallibleTArray<nsString> names;
if (!CallKeys(objId, &status, &names))
return ipcfail(cx);
if (!ok(cx, status))
return false;
RootedId name(cx);
for (size_t i = 0; i < names.Length(); i++) {
if (!convertGeckoStringToId(cx, names[i], &name))
return false;
if (!props.append(name))
return false;
}
return true;
}
bool
CPOWProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
{
MOZ_CRASH("unimplemented");
}
bool
CPOWProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
{
MOZ_CRASH("unimplemented");
}
bool
CPOWProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy)
{
return ParentOf(proxy)->preventExtensions(cx, proxy);
}
bool
JavaScriptParent::preventExtensions(JSContext *cx, HandleObject proxy)
{
ObjectId objId = idOf(proxy);
ReturnStatus status;
if (!CallPreventExtensions(objId, &status))
return ipcfail(cx);
return ok(cx, status);
}
bool
CPOWProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
{
return ParentOf(proxy)->isExtensible(cx, proxy, extensible);
}
bool
JavaScriptParent::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
{
ObjectId objId = idOf(proxy);
ReturnStatus status;
if (!CallIsExtensible(objId, &status, extensible))
return ipcfail(cx);
return ok(cx, status);
}
bool
CPOWProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
{
return ParentOf(proxy)->has(cx, proxy, id, bp);
}
bool
JavaScriptParent::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
{
ObjectId objId = idOf(proxy);
nsString idstr;
if (!convertIdToGeckoString(cx, id, &idstr))
return false;
ReturnStatus status;
if (!CallHas(objId, idstr, &status, bp))
return ipcfail(cx);
return ok(cx, status);
}
bool
CPOWProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
{
return ParentOf(proxy)->hasOwn(cx, proxy, id, bp);
}
bool
JavaScriptParent::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
{
ObjectId objId = idOf(proxy);
nsString idstr;
if (!convertIdToGeckoString(cx, id, &idstr))
return false;
ReturnStatus status;
if (!CallHasOwn(objId, idstr, &status, bp))
return ipcfail(cx);
return !!ok(cx, status);
}
bool
CPOWProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
HandleId id, MutableHandleValue vp)
{
return ParentOf(proxy)->get(cx, proxy, receiver, id, vp);
}
bool
JavaScriptParent::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
HandleId id, MutableHandleValue vp)
{
ObjectId objId = idOf(proxy);
ObjectId receiverId = idOf(receiver);
nsString idstr;
if (!convertIdToGeckoString(cx, id, &idstr))
return false;
JSVariant val;
ReturnStatus status;
if (!CallGet(objId, receiverId, idstr, &status, &val))
return ipcfail(cx);
if (!ok(cx, status))
return false;
return toValue(cx, val, vp);
}
bool
CPOWProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, bool strict, JS::MutableHandleValue vp)
{
return ParentOf(proxy)->set(cx, proxy, receiver, id, strict, vp);
}
bool
JavaScriptParent::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, bool strict, JS::MutableHandleValue vp)
{
ObjectId objId = idOf(proxy);
ObjectId receiverId = idOf(receiver);
nsString idstr;
if (!convertIdToGeckoString(cx, id, &idstr))
return false;
JSVariant val;
if (!toVariant(cx, vp, &val))
return false;
ReturnStatus status;
JSVariant result;
if (!CallSet(objId, receiverId, idstr, strict, val, &status, &result))
return ipcfail(cx);
if (!ok(cx, status))
return false;
return toValue(cx, result, vp);
}
bool
CPOWProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
MutableHandleValue vp)
{
MOZ_CRASH("unimplemented");
}
bool
CPOWProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
{
return ParentOf(proxy)->call(cx, proxy, args);
}
bool
JavaScriptParent::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
{
ObjectId objId = idOf(proxy);
InfallibleTArray<JSParam> vals;
AutoValueVector outobjects(cx);
RootedValue v(cx);
for (size_t i = 0; i < args.length() + 2; i++) {
v = args.base()[i];
if (v.isObject()) {
JSObject *obj = &v.toObject();
if (xpc::IsOutObject(cx, obj)) {
// Make sure it is not an in-out object.
JSBool found;
if (!JS_HasProperty(cx, obj, "value", &found))
return false;
if (found) {
JS_ReportError(cx, "in-out objects cannot be sent via CPOWs yet");
return false;
}
vals.AppendElement(JSParam(void_t()));
if (!outobjects.append(ObjectValue(*obj)))
return false;
continue;
}
}
JSVariant val;
if (!toVariant(cx, v, &val))
return false;
vals.AppendElement(JSParam(val));
}
JSVariant result;
ReturnStatus status;
InfallibleTArray<JSParam> outparams;
if (!CallCall(objId, vals, &status, &result, &outparams))
return ipcfail(cx);
if (!ok(cx, status))
return false;
if (outparams.Length() != outobjects.length())
return ipcfail(cx);
for (size_t i = 0; i < outparams.Length(); i++) {
// Don't bother doing anything for outparams that weren't set.
if (outparams[i].type() == JSParam::Tvoid_t)
continue;
// Take the value the child process returned, and set it on the XPC
// object.
if (!toValue(cx, outparams[i], &v))
return false;
JSObject *obj = &outobjects[i].toObject();
if (!JS_SetProperty(cx, obj, "value", v.address()))
return false;
}
if (!toValue(cx, result, args.rval()))
return false;
return true;
}
void
CPOWProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy)
{
ParentOf(proxy)->drop(proxy);
}
void
JavaScriptParent::drop(JSObject *obj)
{
if (inactive_)
return;
ObjectId objId = idOf(obj);
objects_.remove(objId);
if (!SendDropObject(objId))
MOZ_CRASH();
decref();
}
bool
CPOWProxyHandler::objectClassIs(HandleObject proxy, js::ESClassValue classValue, JSContext *cx)
{
return ParentOf(proxy)->objectClassIs(cx, proxy, classValue);
}
bool
JavaScriptParent::objectClassIs(JSContext *cx, HandleObject proxy, js::ESClassValue classValue)
{
ObjectId objId = idOf(proxy);
// This function is assumed infallible, so we just return false if the IPC
// channel fails.
bool result;
if (!CallObjectClassIs(objId, classValue, &result))
return false;
return result;
}
const char *
CPOWProxyHandler::className(JSContext *cx, HandleObject proxy)
{
return ParentOf(proxy)->className(cx, proxy);
}
const char *
JavaScriptParent::className(JSContext *cx, HandleObject proxy)
{
ObjectId objId = idOf(proxy);
nsString name;
if (!CallClassName(objId, &name))
return NULL;
return ToNewCString(name);
}
bool
JavaScriptParent::init()
{
if (!JavaScriptShared::init())
return false;
return true;
}
bool
JavaScriptParent::makeId(JSContext *cx, JSObject *obj, ObjectId *idp)
{
if (!IsProxy(obj) || GetProxyHandler(obj) != &CPOWProxyHandler::singleton) {
JS_ReportError(cx, "cannot ipc non-cpow object");
return false;
}
*idp = idOf(obj);
return true;
}
JSObject *
JavaScriptParent::unwrap(JSContext *cx, ObjectId objId)
{
if (JSObject *obj = findObject(objId)) {
if (!JS_WrapObject(cx, &obj))
return NULL;
return obj;
}
if (objId > MAX_CPOW_IDS) {
JS_ReportError(cx, "unusable CPOW id");
return NULL;
}
bool callable = !!(objId & OBJECT_IS_CALLABLE);
RootedValue v(cx, UndefinedValue());
JSObject *obj = NewProxyObject(cx,
&CPOWProxyHandler::singleton,
v,
NULL,
NULL,
callable ? ProxyIsCallable : ProxyNotCallable);
if (!obj)
return NULL;
if (!objects_.add(objId, obj))
return NULL;
// Incref once we know the decref will be called.
incref();
SetProxyExtra(obj, 0, PrivateValue(this));
SetProxyExtra(obj, 1, DoubleValue(BitwiseCast<double>(objId)));
return obj;
}
bool
JavaScriptParent::ipcfail(JSContext *cx)
{
JS_ReportError(cx, "catastrophic IPC failure");
return false;
}
bool
JavaScriptParent::ok(JSContext *cx, const ReturnStatus &status)
{
if (status.ok())
return true;
RootedValue exn(cx);
if (!toValue(cx, status.exn(), &exn))
return false;
JS_SetPendingException(cx, exn);
return false;
}
void
JavaScriptParent::decref()
{
refcount_--;
if (!refcount_)
delete this;
}
void
JavaScriptParent::incref()
{
refcount_++;
}
void
JavaScriptParent::destroyFromContent()
{
inactive_ = true;
decref();
}
/* static */ bool
JavaScriptParent::IsCPOW(JSObject *obj)
{
return IsProxy(obj) && GetProxyHandler(obj) == &CPOWProxyHandler::singleton;
}
/* static */ nsresult
JavaScriptParent::InstanceOf(JSObject *obj, const nsID *id, bool *bp)
{
return ParentOf(obj)->instanceOf(obj, id, bp);
}
nsresult
JavaScriptParent::instanceOf(JSObject *obj, const nsID *id, bool *bp)
{
ObjectId objId = idOf(obj);
JSIID iid;
ConvertID(*id, &iid);
ReturnStatus status;
if (!CallInstanceOf(objId, iid, &status, bp))
return NS_ERROR_UNEXPECTED;
if (!status.ok())
return NS_ERROR_UNEXPECTED;
return NS_OK;
}

84
js/ipc/JavaScriptParent.h Normal file
Просмотреть файл

@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* 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/. */
#ifndef mozilla_jsipc_JavaScriptParent__
#define mozilla_jsipc_JavaScriptParent__
#include "JavaScriptShared.h"
#include "mozilla/jsipc/PJavaScriptParent.h"
#include "jsclass.h"
#ifdef XP_WIN
#undef GetClassName
#undef GetClassInfo
#endif
namespace mozilla {
namespace jsipc {
class JavaScriptParent
: public PJavaScriptParent,
public JavaScriptShared
{
public:
JavaScriptParent();
bool init();
public:
bool has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
bool get(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, JS::MutableHandleValue vp);
bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
JS::HandleId id, bool strict, JS::MutableHandleValue vp);
bool call(JSContext *cx, JS::HandleObject proxy, const JS::CallArgs &args);
bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JSPropertyDescriptor *desc, unsigned flags);
bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JSPropertyDescriptor *desc, unsigned flags);
bool getOwnPropertyNames(JSContext *cx, JS::HandleObject proxy, js::AutoIdVector &props);
bool keys(JSContext *cx, JS::HandleObject proxy, js::AutoIdVector &props);
bool objectClassIs(JSContext *cx, JS::HandleObject obj, js::ESClassValue classValue);
const char* className(JSContext *cx, JS::HandleObject proxy);
bool preventExtensions(JSContext *cx, JS::HandleObject proxy);
bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
void decref();
void incref();
void destroyFromContent();
void drop(JSObject *obj);
static bool IsCPOW(JSObject *obj);
static nsresult InstanceOf(JSObject *obj, const nsID *id, bool *bp);
nsresult instanceOf(JSObject *obj, const nsID *id, bool *bp);
protected:
JSObject *unwrap(JSContext *cx, ObjectId objId);
private:
bool makeId(JSContext *cx, JSObject *obj, ObjectId *idp);
ObjectId idOf(JSObject *obj);
// Catastrophic IPC failure.
bool ipcfail(JSContext *cx);
// Check whether a return status is okay, and if not, propagate its error.
bool ok(JSContext *cx, const ReturnStatus &status);
private:
uintptr_t refcount_;
bool inactive_;
};
} // jsipc
} // mozilla
#endif // mozilla_jsipc_JavaScriptWrapper_h__

397
js/ipc/JavaScriptShared.cpp Normal file
Просмотреть файл

@ -0,0 +1,397 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* 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 "JavaScriptShared.h"
#include "jsfriendapi.h"
#include "xpcprivate.h"
using namespace js;
using namespace JS;
using namespace mozilla;
using namespace mozilla::jsipc;
ObjectStore::ObjectStore()
: table_(SystemAllocPolicy())
{
}
bool
ObjectStore::init()
{
return table_.init(32);
}
void
ObjectStore::trace(JSTracer *trc)
{
for (ObjectTable::Range r(table_.all()); !r.empty(); r.popFront()) {
JSObject *obj = r.front().value;
JS_CallObjectTracer(trc, &obj, "ipc-object");
MOZ_ASSERT(obj == r.front().value);
}
}
JSObject *
ObjectStore::find(ObjectId id)
{
ObjectTable::Ptr p = table_.lookup(id);
if (!p)
return NULL;
return p->value;
}
bool
ObjectStore::add(ObjectId id, JSObject *obj)
{
return table_.put(id, obj);
}
void
ObjectStore::remove(ObjectId id)
{
table_.remove(id);
}
ObjectIdCache::ObjectIdCache()
: table_(SystemAllocPolicy())
{
}
bool
ObjectIdCache::init()
{
return table_.init(32);
}
void
ObjectIdCache::trace(JSTracer *trc)
{
for (ObjectIdTable::Range r(table_.all()); !r.empty(); r.popFront()) {
JSObject *obj = r.front().key;
JS_CallObjectTracer(trc, &obj, "ipc-id");
MOZ_ASSERT(obj == r.front().key);
}
}
ObjectId
ObjectIdCache::find(JSObject *obj)
{
ObjectIdTable::Ptr p = table_.lookup(obj);
if (!p)
return 0;
return p->value;
}
bool
ObjectIdCache::add(JSObject *obj, ObjectId id)
{
return table_.put(obj, id);
}
void
ObjectIdCache::remove(JSObject *obj)
{
table_.remove(obj);
}
bool
JavaScriptShared::init()
{
if (!objects_.init())
return false;
return true;
}
bool
JavaScriptShared::convertIdToGeckoString(JSContext *cx, JS::HandleId id, nsString *to)
{
RootedValue idval(cx);
if (!JS_IdToValue(cx, id, idval.address()))
return false;
RootedString str(cx, JS_ValueToString(cx, idval));
if (!str)
return false;
const jschar *chars = JS_GetStringCharsZ(cx, str);
if (!chars)
return false;
*to = chars;
return true;
}
bool
JavaScriptShared::convertGeckoStringToId(JSContext *cx, const nsString &from, JS::MutableHandleId to)
{
RootedString str(cx, JS_NewUCStringCopyN(cx, from.BeginReading(), from.Length()));
if (!str)
return false;
return JS_ValueToId(cx, StringValue(str), to.address());
}
bool
JavaScriptShared::toVariant(JSContext *cx, jsval from, JSVariant *to)
{
switch (JS_TypeOfValue(cx, from)) {
case JSTYPE_VOID:
*to = void_t();
return true;
case JSTYPE_NULL:
{
*to = uint64_t(0);
return true;
}
case JSTYPE_OBJECT:
case JSTYPE_FUNCTION:
{
JSObject *obj = from.toObjectOrNull();
if (!obj) {
JS_ASSERT(from == JSVAL_NULL);
*to = uint64_t(0);
return true;
}
if (xpc_JSObjectIsID(cx, obj)) {
JSIID iid;
const nsID *id = xpc_JSObjectToID(cx, obj);
ConvertID(*id, &iid);
*to = iid;
return true;
}
ObjectId id;
if (!makeId(cx, obj, &id))
return false;
*to = uint64_t(id);
return true;
}
case JSTYPE_STRING:
{
nsDependentJSString dep;
if (!dep.init(cx, from))
return false;
*to = dep;
return true;
}
case JSTYPE_NUMBER:
if (JSVAL_IS_INT(from))
*to = double(from.toInt32());
else
*to = from.toDouble();
return true;
case JSTYPE_BOOLEAN:
*to = from.toBoolean();
return true;
default:
MOZ_ASSERT(false);
return false;
}
}
bool
JavaScriptShared::toValue(JSContext *cx, const JSVariant &from, MutableHandleValue to)
{
switch (from.type()) {
case JSVariant::Tvoid_t:
to.set(UndefinedValue());
return true;
case JSVariant::Tuint64_t:
{
ObjectId id = from.get_uint64_t();
if (id) {
JSObject *obj = unwrap(cx, id);
if (!obj)
return false;
to.set(ObjectValue(*obj));
} else {
to.set(JSVAL_NULL);
}
return true;
}
case JSVariant::Tdouble:
to.set(JS_NumberValue(from.get_double()));
return true;
case JSVariant::Tbool:
to.set(BOOLEAN_TO_JSVAL(from.get_bool()));
return true;
case JSVariant::TnsString:
{
const nsString &old = from.get_nsString();
JSString *str = JS_NewUCStringCopyN(cx, old.BeginReading(), old.Length());
if (!str)
return false;
to.set(StringValue(str));
return true;
}
case JSVariant::TJSIID:
{
nsID iid;
const JSIID &id = from.get_JSIID();
ConvertID(id, &iid);
JSCompartment *compartment = GetContextCompartment(cx);
RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, compartment));
JSObject *obj = xpc_NewIDObject(cx, global, iid);
if (!obj)
return false;
to.set(ObjectValue(*obj));
return true;
}
default:
return false;
}
}
/* static */ void
JavaScriptShared::ConvertID(const nsID &from, JSIID *to)
{
to->m0() = from.m0;
to->m1() = from.m1;
to->m2() = from.m2;
to->m3_0() = from.m3[0];
to->m3_1() = from.m3[1];
to->m3_2() = from.m3[2];
to->m3_3() = from.m3[3];
to->m3_4() = from.m3[4];
to->m3_5() = from.m3[5];
to->m3_6() = from.m3[6];
to->m3_7() = from.m3[7];
}
/* static */ void
JavaScriptShared::ConvertID(const JSIID &from, nsID *to)
{
to->m0 = from.m0();
to->m1 = from.m1();
to->m2 = from.m2();
to->m3[0] = from.m3_0();
to->m3[1] = from.m3_1();
to->m3[2] = from.m3_2();
to->m3[3] = from.m3_3();
to->m3[4] = from.m3_4();
to->m3[5] = from.m3_5();
to->m3[6] = from.m3_6();
to->m3[7] = from.m3_7();
}
static const uint32_t DefaultPropertyOp = 1;
static const uint32_t GetterOnlyPropertyStub = 2;
static const uint32_t UnknownPropertyOp = 3;
bool
JavaScriptShared::fromDescriptor(JSContext *cx, const JSPropertyDescriptor &desc, PPropertyDescriptor *out)
{
out->attrs() = desc.attrs;
out->shortid() = desc.shortid;
if (!toVariant(cx, desc.value, &out->value()))
return false;
if (!makeId(cx, desc.obj, &out->objId()))
return false;
if (!desc.getter) {
out->getter() = 0;
} else if (desc.attrs & JSPROP_GETTER) {
JSObject *getter = JS_FUNC_TO_DATA_PTR(JSObject *, desc.getter);
if (!makeId(cx, getter, &out->getter()))
return false;
} else {
if (desc.getter == JS_PropertyStub)
out->getter() = DefaultPropertyOp;
else
out->getter() = UnknownPropertyOp;
}
if (!desc.setter) {
out->setter() = 0;
} else if (desc.attrs & JSPROP_SETTER) {
JSObject *setter = JS_FUNC_TO_DATA_PTR(JSObject *, desc.setter);
if (!makeId(cx, setter, &out->setter()))
return false;
} else {
if (desc.setter == JS_StrictPropertyStub)
out->setter() = DefaultPropertyOp;
else if (desc.setter == js_GetterOnlyPropertyStub)
out->setter() = GetterOnlyPropertyStub;
else
out->setter() = UnknownPropertyOp;
}
return true;
}
JSBool
UnknownPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
{
JS_ReportError(cx, "getter could not be wrapped via CPOWs");
return JS_FALSE;
}
JSBool
UnknownStrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, JSBool strict, MutableHandleValue vp)
{
JS_ReportError(cx, "setter could not be wrapped via CPOWs");
return JS_FALSE;
}
bool
JavaScriptShared::toDescriptor(JSContext *cx, const PPropertyDescriptor &in, JSPropertyDescriptor *out)
{
out->attrs = in.attrs();
out->shortid = in.shortid();
if (!toValue(cx, in.value(), &out->value))
return false;
if (!unwrap(cx, in.objId(), &out->obj))
return false;
if (!in.getter()) {
out->getter = NULL;
} else if (in.attrs() & JSPROP_GETTER) {
JSObject *getter;
if (!unwrap(cx, in.getter(), &getter))
return false;
out->getter = JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter);
} else {
if (in.getter() == DefaultPropertyOp)
out->getter = JS_PropertyStub;
else
out->getter = UnknownPropertyStub;
}
if (!in.setter()) {
out->setter = NULL;
} else if (in.attrs() & JSPROP_SETTER) {
JSObject *setter;
if (!unwrap(cx, in.setter(), &setter))
return false;
out->setter = JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter);
} else {
if (in.setter() == DefaultPropertyOp)
out->setter = JS_StrictPropertyStub;
else if (in.setter() == GetterOnlyPropertyStub)
out->setter = js_GetterOnlyPropertyStub;
else
out->setter = UnknownStrictPropertyStub;
}
return true;
}

119
js/ipc/JavaScriptShared.h Normal file
Просмотреть файл

@ -0,0 +1,119 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* 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/. */
#ifndef mozilla_jsipc_JavaScriptShared_h__
#define mozilla_jsipc_JavaScriptShared_h__
#include "jsapi.h"
#include "jspubtd.h"
#include "js/HashTable.h"
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/jsipc/PJavaScript.h"
#include "nsJSUtils.h"
#include "nsFrameMessageManager.h"
namespace mozilla {
namespace jsipc {
typedef uint64_t ObjectId;
// Map ids -> JSObjects
class ObjectStore
{
typedef js::DefaultHasher<ObjectId> TableKeyHasher;
typedef js::HashMap<ObjectId, JSObject *, TableKeyHasher, js::SystemAllocPolicy> ObjectTable;
public:
ObjectStore();
bool init();
void trace(JSTracer *trc);
bool add(ObjectId id, JSObject *obj);
JSObject *find(ObjectId id);
void remove(ObjectId id);
private:
ObjectTable table_;
};
// Map JSObjects -> ids
class ObjectIdCache
{
typedef js::PointerHasher<JSObject *, 3> Hasher;
typedef js::HashMap<JSObject *, ObjectId, Hasher, js::SystemAllocPolicy> ObjectIdTable;
public:
ObjectIdCache();
bool init();
void trace(JSTracer *trc);
bool add(JSObject *, ObjectId id);
ObjectId find(JSObject *obj);
void remove(JSObject *obj);
private:
ObjectIdTable table_;
};
class JavaScriptShared
{
public:
bool init();
static const uint32_t OBJECT_EXTRA_BITS = 1;
static const uint32_t OBJECT_IS_CALLABLE = (1 << 0);
protected:
bool toVariant(JSContext *cx, jsval from, JSVariant *to);
bool toValue(JSContext *cx, const JSVariant &from, JS::MutableHandleValue to);
bool fromDescriptor(JSContext *cx, const JSPropertyDescriptor &desc, PPropertyDescriptor *out);
bool toDescriptor(JSContext *cx, const PPropertyDescriptor &in, JSPropertyDescriptor *out);
bool convertIdToGeckoString(JSContext *cx, JS::HandleId id, nsString *to);
bool convertGeckoStringToId(JSContext *cx, const nsString &from, JS::MutableHandleId id);
bool toValue(JSContext *cx, const JSVariant &from, jsval *to) {
JS::RootedValue v(cx);
if (!toValue(cx, from, &v))
return false;
*to = v;
return true;
}
virtual bool makeId(JSContext *cx, JSObject *obj, ObjectId *idp) = 0;
virtual JSObject *unwrap(JSContext *cx, ObjectId id) = 0;
bool unwrap(JSContext *cx, ObjectId id, JSObject **objp) {
if (!id) {
*objp = NULL;
return true;
}
*objp = unwrap(cx, id);
return !!*objp;
}
static void ConvertID(const nsID &from, JSIID *to);
static void ConvertID(const JSIID &from, nsID *to);
JSObject *findObject(uint32_t objId) {
return objects_.find(objId);
}
protected:
ObjectStore objects_;
};
// Use 47 at most, to be safe, since jsval privates are encoded as doubles.
static const uint64_t MAX_CPOW_IDS = (uint64_t(1) << 47) - 1;
} // namespace jsipc
} // namespace mozilla
#endif

Просмотреть файл

@ -0,0 +1,78 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* 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 DOMTypes;
using mozilla::void_t;
namespace mozilla {
namespace jsipc {
struct JSIID
{
uint32_t m0;
uint16_t m1;
uint16_t m2;
uint8_t m3_0;
uint8_t m3_1;
uint8_t m3_2;
uint8_t m3_3;
uint8_t m3_4;
uint8_t m3_5;
uint8_t m3_6;
uint8_t m3_7;
};
union JSVariant
{
void_t; /* |undefined| */
nsString; /* StringValue(x) */
uint64_t; /* ID that maps to a JSObject (cpow on parent, original on child). */
double; /* NumberValue(x) */
bool; /* BooleanValue(x) */
JSIID; /* XPC nsIID */
};
struct ReturnStatus
{
bool ok; /* True for success, false for failure. */
JSVariant exn; /* Exception, if |ok| is false. */
};
union JSParam
{
void_t; /* value is strictly an xpc out param */
JSVariant; /* actual value to pass through */
};
struct PPropertyDescriptor
{
uint64_t objId;
uint32_t attrs;
uint32_t shortid;
JSVariant value;
// How to interpret these values depends on whether JSPROP_GETTER/SETTER
// are set. If set, the corresponding value is a CPOW or 0 for NULL.
// Otherwise, the following table is used:
//
// 0 - NULL
// 1 - Default getter or setter.
// 2 - js_GetterOnlyPropertyStub (setter only)
// 3 - Unknown
uint64_t getter;
uint64_t setter;
};
struct CpowEntry
{
nsString name;
JSVariant value;
};
}
}

Просмотреть файл

@ -15,9 +15,15 @@ FORCE_STATIC_LIB = 1
EXPORT_LIBRARY = 1
FAIL_ON_WARNINGS = 1
# For nsDependentJSString
EXPORTS_mozilla/jsipc = \
JavaScriptParent.h \
$(NULL)
LOCAL_INCLUDES += \
-I$(topsrcdir)/dom/base \
-I$(topsrcdir)/js/ipc \
-I$(topsrcdir)/js/public \
-I$(topsrcdir)/js/xpconnect/src \
$(NULL)
include $(topsrcdir)/config/config.mk

Просмотреть файл

@ -1,621 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 "mozilla/GuardObjects.h"
#include "base/basictypes.h"
#include "mozilla/jsipc/ContextWrapperChild.h"
#include "mozilla/jsipc/ObjectWrapperChild.h"
#include "mozilla/jsipc/CPOWTypes.h"
#include "jsapi.h"
#include "nsAutoPtr.h"
#include "nsTArray.h"
#include "nsContentUtils.h"
#include "nsCxPusher.h"
#include "nsJSUtils.h"
using namespace mozilla::jsipc;
using namespace js;
namespace {
class MOZ_STACK_CLASS AutoContextPusher {
nsCxPusher mStack;
JSContext* const mContext;
const uint32_t mSavedOptions;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
AutoContextPusher(JSContext* cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mContext(cx)
, mSavedOptions(JS_SetOptions(cx, (JS_GetOptions(cx) |
JSOPTION_DONT_REPORT_UNCAUGHT)))
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
mStack.Push(cx);
}
~AutoContextPusher() {
mStack.Pop();
JS_SetOptions(mContext, mSavedOptions);
}
};
class StatusPtrOwner
{
OperationStatus* mStatusPtr;
public:
StatusPtrOwner() : mStatusPtr(NULL) {}
void SetStatusPtr(OperationStatus* statusPtr) {
mStatusPtr = statusPtr;
// By default, initialize mStatusPtr to failure without an
// exception. Doing so only when the union is uninitialized
// allows AutoCheckOperation classes to be nested on the
// stack, just in case AnswerConstruct, for example, calls
// AnswerCall (as it once did, before there were unrelated
// problems with that approach).
if (mStatusPtr->type() == OperationStatus::T__None)
*mStatusPtr = JS_FALSE;
}
OperationStatus* StatusPtr() {
NS_ASSERTION(mStatusPtr, "Should have called SetStatusPtr by now.");
return mStatusPtr;
}
};
typedef AutoCheckOperationBase<StatusPtrOwner> ACOBase;
class AutoCheckOperation : public ACOBase
{
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
AutoCheckOperation(ObjectWrapperChild* owc,
OperationStatus* statusPtr
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: ACOBase(NULL, owc)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
SetStatusPtr(statusPtr);
}
};
}
void
ObjectWrapperChild::CheckOperation(JSContext*,
OperationStatus* status)
{
NS_PRECONDITION(status->type() != OperationStatus::T__None,
"Checking an uninitialized operation.");
JSContext* cx = Manager()->GetContext();
jsval thrown;
if (JS_GetPendingException(cx, &thrown)) {
NS_ASSERTION(!(status->type() == OperationStatus::TJSBool &&
status->get_JSBool()),
"Operation succeeded but exception was thrown?");
JSVariant exception;
if (!jsval_to_JSVariant(cx, thrown, &exception))
exception = void_t(); // XXX Useful?
*status = exception;
JS_ClearPendingException(cx);
}
}
ObjectWrapperChild::ObjectWrapperChild(JSContext* cx, JSObject* obj)
: mObj(obj)
{
AutoContextPusher acp(cx);
#ifdef DEBUG
bool added =
#endif
JS_AddObjectRoot(cx, &mObj);
NS_ASSERTION(added, "ObjectWrapperChild constructor failed to root JSObject*");
}
void
ObjectWrapperChild::ActorDestroy(ActorDestroyReason why)
{
JSContext* cx = Manager()->GetContext();
AutoContextPusher acp(cx);
JS_RemoveObjectRoot(cx, &mObj);
}
bool
ObjectWrapperChild::JSObject_to_JSVariant(JSContext* cx, JSObject* from,
JSVariant* to)
{
*to = Manager()->GetOrCreateWrapper(from);
return true;
}
bool
ObjectWrapperChild::jsval_to_JSVariant(JSContext* cx, jsval from, JSVariant* to)
{
switch (JS_TypeOfValue(cx, from)) {
case JSTYPE_VOID:
*to = void_t();
return true;
case JSTYPE_NULL:
if (from != JSVAL_NULL)
return false;
// fall through
case JSTYPE_FUNCTION:
// fall through
case JSTYPE_OBJECT:
return JSObject_to_JSVariant(cx, JSVAL_TO_OBJECT(from), to);
case JSTYPE_STRING:
{
nsDependentJSString depStr;
if (!depStr.init(cx, from))
return false;
*to = depStr;
}
return true;
case JSTYPE_NUMBER:
if (JSVAL_IS_INT(from))
*to = JSVAL_TO_INT(from);
else if (JSVAL_IS_DOUBLE(from))
*to = JSVAL_TO_DOUBLE(from);
else return false;
return true;
case JSTYPE_BOOLEAN:
*to = !!JSVAL_TO_BOOLEAN(from);
return true;
default:
return false;
}
}
/*static*/ bool
ObjectWrapperChild::
JSObject_from_PObjectWrapperChild(JSContext*,
const PObjectWrapperChild* from,
JSObject** to)
{
const ObjectWrapperChild* owc =
static_cast<const ObjectWrapperChild*>(from);
*to = owc ? owc->mObj : NULL;
return true;
}
/*static*/ bool
ObjectWrapperChild::JSObject_from_JSVariant(JSContext* cx,
const JSVariant& from,
JSObject** to)
{
if (from.type() != JSVariant::TPObjectWrapperChild)
return false;
return JSObject_from_PObjectWrapperChild(cx,
from.get_PObjectWrapperChild(),
to);
}
/*static*/ bool
ObjectWrapperChild::jsval_from_JSVariant(JSContext* cx, const JSVariant& from,
jsval* to)
{
switch (from.type()) {
case JSVariant::Tvoid_t:
*to = JSVAL_VOID;
return true;
case JSVariant::TPObjectWrapperChild:
{
JSObject* obj;
if (!JSObject_from_JSVariant(cx, from, &obj))
return false;
*to = OBJECT_TO_JSVAL(obj);
return true;
}
case JSVariant::TnsString:
{
const nsString& str = from.get_nsString();
JSString* s = JS_NewUCStringCopyN(cx,
str.BeginReading(),
str.Length());
if (!s)
return false;
*to = STRING_TO_JSVAL(s);
}
return true;
case JSVariant::Tint:
*to = INT_TO_JSVAL(from.get_int());
return true;
case JSVariant::Tdouble:
*to = JS_NumberValue(from.get_double());
return true;
case JSVariant::Tbool:
*to = BOOLEAN_TO_JSVAL(from.get_bool());
return true;
default:
return false;
}
}
ContextWrapperChild*
ObjectWrapperChild::Manager()
{
PContextWrapperChild* pcwc = PObjectWrapperChild::Manager();
return static_cast<ContextWrapperChild*>(pcwc);
}
static bool
jsid_to_nsString(JSContext* cx, jsid from, nsString* to)
{
if (JSID_IS_STRING(from)) {
size_t length;
const jschar* chars = JS_GetInternedStringCharsAndLength(JSID_TO_STRING(from), &length);
*to = nsDependentString(chars, length);
return true;
}
return false;
}
static bool
jsid_from_nsString(JSContext* cx, const nsString& from, jsid* to)
{
JSString* str = JS_NewUCStringCopyN(cx, from.BeginReading(), from.Length());
if (!str)
return false;
return JS_ValueToId(cx, STRING_TO_JSVAL(str), to);
}
#if 0
// The general schema for ObjectWrapperChild::Answer* methods:
bool
ObjectWrapperChild::AnswerSomething(/* in-parameters */
/* out-parameters */)
{
// initialize out-parameters for failure
JSContext* cx = Manager()->GetContext();
AutoContextPusher acp(cx);
// validate in-parameters, else return false
// successfully perform local JS operations, else return true
// perform out-parameter conversions, else return false
return true;
}
// There's an important subtlety here: though a local JS operation may
// fail, leaving out-parameters uninitialized, we must initialize all
// out-parameters when reporting success (returning true) to the IPC
// messaging system. See AnswerGetProperty for illustration.
#endif
bool
ObjectWrapperChild::AnswerAddProperty(const nsString& id,
OperationStatus* status)
{
JSContext* cx = Manager()->GetContext();
JS::RootedId interned_id(cx);
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
if (!jsid_from_nsString(cx, id, interned_id.address()))
return false;
*status = JS_DefinePropertyById(cx, mObj, interned_id, JSVAL_VOID,
NULL, NULL, 0);
return true;
}
bool
ObjectWrapperChild::AnswerGetProperty(const nsString& id,
OperationStatus* status, JSVariant* vp)
{
JSContext* cx = Manager()->GetContext();
JS::RootedId interned_id(cx);
JS::RootedValue val(cx);
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
if (!jsid_from_nsString(cx, id, interned_id.address()))
return false;
*status = JS_GetPropertyById(cx, mObj, interned_id, val.address());
// Since we fully expect this call to jsval_to_JSVariant to return
// true, we can't just leave vp uninitialized when JS_GetPropertyById
// returns JS_FALSE. This pitfall could be avoided in general if IPDL
// ensured that outparams were pre-initialized to some default value
// (XXXfixme cjones?).
return jsval_to_JSVariant(cx, aco.Ok() ? val : JSVAL_VOID, vp);
}
bool
ObjectWrapperChild::AnswerSetProperty(const nsString& id, const JSVariant& v,
OperationStatus* status, JSVariant* vp)
{
*vp = v;
JSContext* cx = Manager()->GetContext();
JS::RootedId interned_id(cx);
JS::RootedValue val(cx);
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
if (!jsid_from_nsString(cx, id, interned_id.address()) ||
!jsval_from_JSVariant(cx, v, val.address())) {
return false;
}
*status = JS_SetPropertyById(cx, mObj, interned_id, val.address());
return jsval_to_JSVariant(cx, aco.Ok() ? val : JSVAL_VOID, vp);
}
bool
ObjectWrapperChild::AnswerDelProperty(const nsString& id,
OperationStatus* status, JSVariant* vp)
{
JSContext* cx = Manager()->GetContext();
JS::RootedId interned_id(cx);
JS::RootedValue val(cx);
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
if (!jsid_from_nsString(cx, id, interned_id.address()))
return false;
*status = JS_DeletePropertyById2(cx, mObj, interned_id, val.address());
return jsval_to_JSVariant(cx, aco.Ok() ? val : JSVAL_VOID, vp);
}
static const uint32_t sNextIdIndexSlot = 0;
static const uint32_t sNumNewEnumerateStateSlots = 1;
static void
CPOW_NewEnumerateState_FreeIds(JSObject* state)
{
nsTArray<nsString>* strIds =
static_cast<nsTArray<nsString>*>(JS_GetPrivate(state));
if (strIds) {
delete strIds;
JS_SetPrivate(state, NULL);
}
}
static void
CPOW_NewEnumerateState_Finalize(JSFreeOp* fop, JSObject* state)
{
CPOW_NewEnumerateState_FreeIds(state);
}
// Similar to IteratorClass in XPCWrapper.cpp
static const JSClass sCPOW_NewEnumerateState_JSClass = {
"CPOW NewEnumerate State",
JSCLASS_HAS_PRIVATE |
JSCLASS_HAS_RESERVED_SLOTS(sNumNewEnumerateStateSlots),
JS_PropertyStub, JS_DeletePropertyStub,
JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, CPOW_NewEnumerateState_Finalize
};
bool
ObjectWrapperChild::AnswerNewEnumerateInit(/* no in-parameters */
OperationStatus* status, JSVariant* statep, int* idp)
{
*idp = 0;
JSContext* cx = Manager()->GetContext();
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
JSClass* clasp = const_cast<JSClass*>(&sCPOW_NewEnumerateState_JSClass);
JS::Rooted<JSObject*> state(cx, JS_NewObjectWithGivenProto(cx, clasp, NULL, NULL));
if (!state)
return false;
for (JS::RootedObject proto(cx, mObj); proto; ) {
AutoIdArray ids(cx, JS_Enumerate(cx, proto));
for (size_t i = 0; i < ids.length(); ++i)
JS_DefinePropertyById(cx, state, ids[i], JSVAL_VOID,
NULL, NULL, JSPROP_ENUMERATE | JSPROP_SHARED);
if (!JS_GetPrototype(cx, proto, proto.address()))
return false;
}
InfallibleTArray<nsString>* strIds;
{
AutoIdArray ids(cx, JS_Enumerate(cx, state));
if (!ids)
return false;
strIds = new InfallibleTArray<nsString>(ids.length());
for (size_t i = 0; i < ids.length(); ++i)
if (!jsid_to_nsString(cx, ids[i], strIds->AppendElement())) {
delete strIds;
return false;
}
}
*idp = strIds->Length();
JS_SetPrivate(state, strIds);
JS_SetReservedSlot(state, sNextIdIndexSlot, JSVAL_ZERO);
*status = JSObject_to_JSVariant(cx, state, statep);
return true;
}
bool
ObjectWrapperChild::AnswerNewEnumerateNext(const JSVariant& in_state,
OperationStatus* status, JSVariant* statep, nsString* idp)
{
JSObject* state;
*statep = in_state;
idp->Truncate();
JSContext* cx = Manager()->GetContext();
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
if (!JSObject_from_JSVariant(cx, in_state, &state))
return false;
InfallibleTArray<nsString>* strIds =
static_cast<InfallibleTArray<nsString>*>(JS_GetPrivate(state));
if (!strIds)
return false;
jsval v = JS_GetReservedSlot(state, sNextIdIndexSlot);
int32_t i = JSVAL_TO_INT(v);
NS_ASSERTION(i >= 0, "Index of next jsid negative?");
NS_ASSERTION(size_t(i) <= strIds->Length(), "Index of next jsid too large?");
if (size_t(i) == strIds->Length()) {
*status = JS_TRUE;
return JSObject_to_JSVariant(cx, NULL, statep);
}
*idp = strIds->ElementAt(i);
JS_SetReservedSlot(state, sNextIdIndexSlot, INT_TO_JSVAL(i + 1));
*status = JS_TRUE;
return true;
}
bool
ObjectWrapperChild::RecvNewEnumerateDestroy(const JSVariant& in_state)
{
JSObject* state;
JSContext* cx = Manager()->GetContext();
AutoContextPusher acp(cx);
if (!JSObject_from_JSVariant(cx, in_state, &state))
return false;
CPOW_NewEnumerateState_FreeIds(state);
return true;
}
bool
ObjectWrapperChild::AnswerNewResolve(const nsString& id, const int& flags,
OperationStatus* status, PObjectWrapperChild** obj2)
{
*obj2 = NULL;
JSContext* cx = Manager()->GetContext();
JS::RootedId interned_id(cx);
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
if (!jsid_from_nsString(cx, id, interned_id.address()))
return false;
CPOW_LOG(("new-resolving \"%s\"...",
NS_ConvertUTF16toUTF8(id).get()));
JS::Rooted<JSPropertyDescriptor> desc(cx);
if (!JS_GetPropertyDescriptorById(cx, mObj, interned_id, flags, desc.address()))
return true;
*status = JS_TRUE;
if (desc.get().obj)
*obj2 = Manager()->GetOrCreateWrapper(desc.get().obj);
return true;
}
bool
ObjectWrapperChild::AnswerConvert(const JSType& type,
OperationStatus* status, JSVariant* vp)
{
JSContext* cx = Manager()->GetContext();
JS::RootedValue v(cx);
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
*status = JS_ConvertValue(cx, OBJECT_TO_JSVAL(mObj), type, v.address());
return jsval_to_JSVariant(cx, aco.Ok() ? v : JSVAL_VOID, vp);
}
namespace {
// Should be an overestimate of typical JS function arity.
typedef nsAutoTArray<jsval, 5> AutoJSArgs;
}
bool
ObjectWrapperChild::AnswerCall(PObjectWrapperChild* receiver, const InfallibleTArray<JSVariant>& argv,
OperationStatus* status, JSVariant* rval)
{
JSContext* cx = Manager()->GetContext();
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
JS::RootedObject obj(cx);
if (!JSObject_from_PObjectWrapperChild(cx, receiver, obj.address()))
return false;
AutoJSArgs args;
uint32_t argc = argv.Length();
jsval *jsargs = args.AppendElements(argc);
if (!jsargs)
return false;
AutoArrayRooter tvr(cx, argc, jsargs);
for (uint32_t i = 0; i < argc; ++i)
if (!jsval_from_JSVariant(cx, argv.ElementAt(i), jsargs + i))
return false;
JS::RootedValue rv(cx);
*status = JS_CallFunctionValue(cx, obj, OBJECT_TO_JSVAL(mObj),
argv.Length(), jsargs, rv.address());
return jsval_to_JSVariant(cx, aco.Ok() ? rv : JSVAL_VOID, rval);
}
bool
ObjectWrapperChild::AnswerConstruct(const InfallibleTArray<JSVariant>& argv,
OperationStatus* status, PObjectWrapperChild** rval)
{
JSContext* cx = Manager()->GetContext();
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
AutoJSArgs args;
uint32_t argc = argv.Length();
jsval* jsargs = args.AppendElements(argc);
if (!jsargs)
return false;
AutoArrayRooter tvr(cx, argc, jsargs);
for (uint32_t i = 0; i < argc; ++i)
if (!jsval_from_JSVariant(cx, argv.ElementAt(i), jsargs + i))
return false;
JSObject* obj = JS_New(cx, mObj, argc, jsargs);
*status = !!obj;
*rval = Manager()->GetOrCreateWrapper(obj);
return true;
}
bool
ObjectWrapperChild::AnswerHasInstance(const JSVariant& v,
OperationStatus* status, JSBool* bp)
{
JSContext* cx = Manager()->GetContext();
JS::RootedValue candidate(cx);
AutoContextPusher acp(cx);
AutoCheckOperation aco(this, status);
if (!jsval_from_JSVariant(cx, v, candidate.address()))
return false;
*status = JS_HasInstance(cx, mObj, candidate, bp);
return true;
}

Просмотреть файл

@ -1,93 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef mozilla_jsipc_ObjectWrapperChild_h
#define mozilla_jsipc_ObjectWrapperChild_h
#include "mozilla/jsipc/PObjectWrapperChild.h"
// For OperationChecker and AutoCheckOperationBase.
#include "mozilla/jsipc/ObjectWrapperParent.h"
using mozilla::jsipc::JSVariant;
namespace mozilla {
namespace jsipc {
class ContextWrapperChild;
class ObjectWrapperChild
: public PObjectWrapperChild
, public OperationChecker
{
public:
ObjectWrapperChild(JSContext* cx, JSObject* obj);
JSObject* GetJSObject() const { return mObj; }
void CheckOperation(JSContext* cx, OperationStatus* status);
private:
JSObject* mObj;
bool JSObject_to_JSVariant(JSContext* cx, JSObject* from, JSVariant* to);
bool jsval_to_JSVariant(JSContext* cx, jsval from, JSVariant* to);
static bool JSObject_from_PObjectWrapperChild(JSContext* cx,
const PObjectWrapperChild* from,
JSObject** to);
static bool JSObject_from_JSVariant(JSContext* cx, const JSVariant& from,
JSObject** to);
static bool jsval_from_JSVariant(JSContext* cx, const JSVariant& from,
jsval* to);
ContextWrapperChild* Manager();
protected:
void ActorDestroy(ActorDestroyReason why);
bool AnswerAddProperty(const nsString& id,
OperationStatus* status);
bool AnswerGetProperty(const nsString& id,
OperationStatus* status, JSVariant* vp);
bool AnswerSetProperty(const nsString& id, const JSVariant& v,
OperationStatus* status, JSVariant* vp);
bool AnswerDelProperty(const nsString& id,
OperationStatus* status, JSVariant* vp);
bool AnswerNewEnumerateInit(/* no in-parameters */
OperationStatus* status, JSVariant* statep, int* idp);
bool AnswerNewEnumerateNext(const JSVariant& in_state,
OperationStatus* status, JSVariant* statep, nsString* idp);
bool RecvNewEnumerateDestroy(const JSVariant& in_state);
bool AnswerNewResolve(const nsString& id, const int& flags,
OperationStatus* status, PObjectWrapperChild** obj2);
bool AnswerConvert(const JSType& type,
OperationStatus* status, JSVariant* vp);
bool AnswerCall(PObjectWrapperChild* receiver, const InfallibleTArray<JSVariant>& argv,
OperationStatus* status, JSVariant* rval);
bool AnswerConstruct(const InfallibleTArray<JSVariant>& argv,
OperationStatus* status, PObjectWrapperChild** rval);
bool AnswerHasInstance(const JSVariant& v,
OperationStatus* status, JSBool* bp);
};
}}
#endif /* mozilla_jsipc_ObjectWrapperChild_h */

Просмотреть файл

@ -1,733 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 "mozilla/GuardObjects.h"
#include "mozilla/jsipc/ObjectWrapperParent.h"
#include "mozilla/jsipc/ContextWrapperParent.h"
#include "mozilla/jsipc/CPOWTypes.h"
#include "mozilla/unused.h"
#include "nsJSUtils.h"
#include "jsutil.h"
#include "jsfriendapi.h"
using namespace mozilla::jsipc;
using namespace JS;
namespace {
// Only need one reserved slot because the ObjectWrapperParent* is
// stored in the private slot.
static const unsigned sFlagsSlot = 0;
static const unsigned sNumSlots = 1;
static const unsigned CPOW_FLAG_RESOLVING = 1 << 0;
class AutoResolveFlag
{
Rooted<JSObject*> mObj;
unsigned mOldFlags;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
static unsigned GetFlags(JSObject* obj) {
jsval v = JS_GetReservedSlot(obj, sFlagsSlot);
return JSVAL_TO_INT(v);
}
static unsigned SetFlags(JSObject* obj, unsigned flags) {
unsigned oldFlags = GetFlags(obj);
if (oldFlags != flags)
JS_SetReservedSlot(obj, sFlagsSlot, INT_TO_JSVAL(flags));
return oldFlags;
}
public:
AutoResolveFlag(JSContext *cx, JSObject* obj
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mObj(cx, obj)
, mOldFlags(SetFlags(obj, GetFlags(obj) | CPOW_FLAG_RESOLVING))
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoResolveFlag() {
SetFlags(mObj, mOldFlags);
}
static JSBool IsSet(JSObject* obj) {
return GetFlags(obj) & CPOW_FLAG_RESOLVING;
}
};
class StatusMemberOwner
{
OperationStatus mStatus;
public:
StatusMemberOwner() : mStatus(JS_FALSE) {}
OperationStatus* StatusPtr() {
return &mStatus;
}
};
typedef AutoCheckOperationBase<StatusMemberOwner> ACOBase;
class AutoCheckOperation : public ACOBase
{
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
AutoCheckOperation(JSContext* cx,
ObjectWrapperParent* owp
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: ACOBase(cx, owp)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
};
}
void
ObjectWrapperParent::CheckOperation(JSContext* cx,
OperationStatus* status)
{
NS_PRECONDITION(status->type() != OperationStatus::T__None,
"Checking an uninitialized operation.");
switch (status->type()) {
case OperationStatus::TJSVariant:
{
Rooted<Value> thrown(cx);
if (jsval_from_JSVariant(cx, status->get_JSVariant(), thrown.address()))
JS_SetPendingException(cx, thrown);
*status = JS_FALSE;
}
break;
case OperationStatus::TJSBool:
if (!status->get_JSBool() && !JS_IsExceptionPending(cx)) {
NS_WARNING("CPOW operation failed without setting an exception.");
}
break;
default:
NS_NOTREACHED("Invalid or uninitialized OperationStatus type.");
break;
}
}
template <typename RType>
static RType
with_error(JSContext* cx,
RType rval,
const char* error = NULL)
{
if (!JS_IsExceptionPending(cx))
JS_ReportError(cx, error ? error : "Unspecified CPOW error");
return rval;
}
const js::Class ObjectWrapperParent::sCPOW_JSClass = {
"CrossProcessObjectWrapper",
JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE |
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(sNumSlots),
ObjectWrapperParent::CPOW_AddProperty,
ObjectWrapperParent::CPOW_DelProperty,
ObjectWrapperParent::CPOW_GetProperty,
ObjectWrapperParent::CPOW_SetProperty,
(JSEnumerateOp) ObjectWrapperParent::CPOW_NewEnumerate,
(JSResolveOp) ObjectWrapperParent::CPOW_NewResolve,
ObjectWrapperParent::CPOW_Convert,
ObjectWrapperParent::CPOW_Finalize,
nullptr, // checkAccess
ObjectWrapperParent::CPOW_Call,
ObjectWrapperParent::CPOW_HasInstance,
ObjectWrapperParent::CPOW_Construct,
nullptr // trace
};
void
ObjectWrapperParent::ActorDestroy(ActorDestroyReason)
{
if (mObj) {
JS_SetPrivate(mObj, NULL);
mObj = NULL;
}
}
ContextWrapperParent*
ObjectWrapperParent::Manager()
{
PContextWrapperParent* pcwp = PObjectWrapperParent::Manager();
return static_cast<ContextWrapperParent*>(pcwp);
}
JSObject*
ObjectWrapperParent::GetJSObject(JSContext* cx) const
{
if (!mObj) {
js::Class *clasp = const_cast<js::Class *>(&ObjectWrapperParent::sCPOW_JSClass);
mObj = JS_NewObject(cx, js::Jsvalify(clasp), NULL, NULL);
if (mObj) {
JS_SetPrivate(mObj, (void*)this);
JS_SetReservedSlot(mObj, sFlagsSlot, JSVAL_ZERO);
}
}
return mObj;
}
static ObjectWrapperParent*
Unwrap(JSContext* cx, JSObject* objArg)
{
Rooted<JSObject*> obj(cx, objArg), proto(cx);
while (js::GetObjectClass(obj) != &ObjectWrapperParent::sCPOW_JSClass) {
if (!js::GetObjectProto(cx, obj, &proto) || !proto)
return NULL;
obj = proto;
}
ObjectWrapperParent* self =
static_cast<ObjectWrapperParent*>(JS_GetPrivate(obj));
NS_ASSERTION(!self || self->GetJSObjectOrNull() == obj,
"Wrapper and wrapped object disagree?");
return self;
}
/*static*/ bool
ObjectWrapperParent::jsval_to_JSVariant(JSContext* cx, jsval from,
JSVariant* to)
{
switch (JS_TypeOfValue(cx, from)) {
case JSTYPE_VOID:
*to = void_t();
return true;
case JSTYPE_NULL:
if (from != JSVAL_NULL)
return false;
// fall through
case JSTYPE_FUNCTION:
// CPOWs can fool JS_TypeOfValue into returning JSTYPE_FUNCTION
// because they have a call hook, but CPOWs are really objects, so
// fall through to the JSTYPE_OBJECT case:
case JSTYPE_OBJECT:
{
PObjectWrapperParent* powp;
if (!JSObject_to_PObjectWrapperParent(cx, JSVAL_TO_OBJECT(from), &powp))
return with_error(cx, false, "Cannot pass parent-created object to child");
*to = powp;
}
return true;
case JSTYPE_STRING:
{
nsDependentJSString depStr;
if (!depStr.init(cx, from))
return false;
*to = depStr;
}
return true;
case JSTYPE_NUMBER:
if (JSVAL_IS_INT(from))
*to = JSVAL_TO_INT(from);
else if (JSVAL_IS_DOUBLE(from))
*to = JSVAL_TO_DOUBLE(from);
else return false;
return true;
case JSTYPE_BOOLEAN:
*to = !!JSVAL_TO_BOOLEAN(from);
return true;
default:
return with_error(cx, false, "Bad jsval type");
}
}
/*static*/ bool
ObjectWrapperParent::jsval_from_JSVariant(JSContext* cx, const JSVariant& from,
jsval* to)
{
switch (from.type()) {
case JSVariant::Tvoid_t:
*to = JSVAL_VOID;
return true;
case JSVariant::TPObjectWrapperParent:
return jsval_from_PObjectWrapperParent(cx, from.get_PObjectWrapperParent(), to);
case JSVariant::TnsString:
{
JSString* str = JS_NewUCStringCopyZ(cx, from.get_nsString().BeginReading());
if (!str)
return false;
*to = STRING_TO_JSVAL(str);
return true;
}
case JSVariant::Tint:
*to = INT_TO_JSVAL(from.get_int());
return true;
case JSVariant::Tdouble:
*to = JS_NumberValue(from.get_double());
return true;
case JSVariant::Tbool:
*to = BOOLEAN_TO_JSVAL(from.get_bool());
return true;
default:
return false;
}
}
/*static*/ bool
ObjectWrapperParent::boolean_from_JSVariant(JSContext* cx, const JSVariant& from,
JSBool* to)
{
switch (from.type()) {
case JSVariant::Tvoid_t:
*to = false;
return true;
case JSVariant::TPObjectWrapperParent: {
Rooted<Value> v(cx);
if (!jsval_from_PObjectWrapperParent(cx, from.get_PObjectWrapperParent(), v.address()))
return false;
*to = JS::ToBoolean(v);
return true;
}
case JSVariant::TnsString:
{
JSString* str = JS_NewUCStringCopyZ(cx, from.get_nsString().BeginReading());
if (!str)
return false;
*to = JS::ToBoolean(JS::StringValue(str));
return true;
}
case JSVariant::Tint:
*to = from.get_int() != 0;
return true;
case JSVariant::Tdouble:
*to = JS::ToBoolean(JS::DoubleValue(from.get_double()));
return true;
case JSVariant::Tbool:
*to = from.get_bool();
return true;
default:
return false;
}
}
/*static*/ bool
ObjectWrapperParent::
JSObject_to_PObjectWrapperParent(JSContext* cx, JSObject* from, PObjectWrapperParent** to)
{
if (!from) {
*to = NULL;
return true;
}
ObjectWrapperParent* owp = Unwrap(cx, from);
if (!owp)
return false;
*to = owp;
return true;
}
/*static*/ bool
ObjectWrapperParent::
JSObject_from_PObjectWrapperParent(JSContext* cx,
const PObjectWrapperParent* from,
MutableHandleObject to)
{
const ObjectWrapperParent* owp =
static_cast<const ObjectWrapperParent*>(from);
to.set(owp
? owp->GetJSObject(cx)
: JSVAL_TO_OBJECT(JSVAL_NULL));
return true;
}
/*static*/ bool
ObjectWrapperParent::
jsval_from_PObjectWrapperParent(JSContext* cx,
const PObjectWrapperParent* from,
jsval* to)
{
Rooted<JSObject*> obj(cx);
if (!JSObject_from_PObjectWrapperParent(cx, from, &obj))
return false;
*to = OBJECT_TO_JSVAL(obj);
return true;
}
static bool
jsid_from_int(JSContext* cx, int from, jsid* to)
{
jsval v = INT_TO_JSVAL(from);
return JS_ValueToId(cx, v, to);
}
static bool
jsid_from_nsString(JSContext* cx, const nsString& from, jsid* to)
{
JSString* str = JS_NewUCStringCopyZ(cx, from.BeginReading());
if (!str)
return false;
return JS_ValueToId(cx, STRING_TO_JSVAL(str), to);
}
static bool
jsval_to_nsString(JSContext* cx, jsid from, nsString* to)
{
JSString* str;
const jschar* chars;
jsval idval;
if (JS_IdToValue(cx, from, &idval) &&
(str = JS_ValueToString(cx, idval)) &&
(chars = JS_GetStringCharsZ(cx, str))) {
*to = chars;
return true;
}
return false;
}
/*static*/ JSBool
ObjectWrapperParent::CPOW_AddProperty(JSContext *cx, HandleObject obj, HandleId id,
MutableHandleValue vp)
{
CPOW_LOG(("Calling CPOW_AddProperty (%s)...",
JSVAL_TO_CSTR(cx, id)));
ObjectWrapperParent* self = Unwrap(cx, obj);
if (!self)
return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_AddProperty");
if (AutoResolveFlag::IsSet(obj))
return JS_TRUE;
AutoCheckOperation aco(cx, self);
nsString in_id;
if (!jsval_to_nsString(cx, id, &in_id))
return JS_FALSE;
return (self->Manager()->RequestRunToCompletion() &&
self->CallAddProperty(in_id,
aco.StatusPtr()) &&
aco.Ok());
}
/*static*/ JSBool
ObjectWrapperParent::CPOW_GetProperty(JSContext *cx, HandleObject obj, HandleId id,
MutableHandleValue vp)
{
CPOW_LOG(("Calling CPOW_GetProperty (%s)...",
JSVAL_TO_CSTR(cx, id)));
ObjectWrapperParent* self = Unwrap(cx, obj);
if (!self)
return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_GetProperty");
AutoCheckOperation aco(cx, self);
nsString in_id;
if (!jsval_to_nsString(cx, id, &in_id))
return JS_FALSE;
JSVariant out_v;
return (self->Manager()->RequestRunToCompletion() &&
self->CallGetProperty(in_id,
aco.StatusPtr(), &out_v) &&
aco.Ok() &&
self->jsval_from_JSVariant(cx, out_v, vp.address()));
}
/*static*/ JSBool
ObjectWrapperParent::CPOW_SetProperty(JSContext *cx, HandleObject obj, HandleId id,
JSBool strict, MutableHandleValue vp)
{
CPOW_LOG(("Calling CPOW_SetProperty (%s)...",
JSVAL_TO_CSTR(cx, id)));
ObjectWrapperParent* self = Unwrap(cx, obj);
if (!self)
return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_SetProperty");
AutoCheckOperation aco(cx, self);
nsString in_id;
JSVariant in_v;
if (!jsval_to_nsString(cx, id, &in_id) ||
!self->jsval_to_JSVariant(cx, vp, &in_v))
return JS_FALSE;
JSVariant out_v;
return (self->Manager()->RequestRunToCompletion() &&
self->CallSetProperty(in_id, in_v,
aco.StatusPtr(), &out_v) &&
aco.Ok() &&
self->jsval_from_JSVariant(cx, out_v, vp.address()));
}
/*static*/ JSBool
ObjectWrapperParent::CPOW_DelProperty(JSContext *cx, HandleObject obj, HandleId id,
JSBool *succeeded)
{
CPOW_LOG(("Calling CPOW_DelProperty (%s)...",
JSVAL_TO_CSTR(cx, id)));
ObjectWrapperParent* self = Unwrap(cx, obj);
if (!self)
return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_DelProperty");
AutoCheckOperation aco(cx, self);
nsString in_id;
if (!jsval_to_nsString(cx, id, &in_id))
return JS_FALSE;
JSVariant out_v;
return (self->Manager()->RequestRunToCompletion() &&
self->CallDelProperty(in_id,
aco.StatusPtr(), &out_v) &&
aco.Ok() &&
boolean_from_JSVariant(cx, out_v, succeeded));
}
JSBool
ObjectWrapperParent::NewEnumerateInit(JSContext* cx, jsval* statep, jsid* idp)
{
AutoCheckOperation aco(cx, this);
JSVariant out_state;
int out_id;
return (CallNewEnumerateInit(aco.StatusPtr(), &out_state, &out_id) &&
aco.Ok() &&
jsval_from_JSVariant(cx, out_state, statep) &&
(!idp || jsid_from_int(cx, out_id, idp)));
}
JSBool
ObjectWrapperParent::NewEnumerateNext(JSContext* cx, jsval* statep, jsid* idp)
{
AutoCheckOperation aco(cx, this);
JSVariant in_state;
if (!jsval_to_JSVariant(cx, *statep, &in_state))
return JS_FALSE;
JSVariant out_state;
nsString out_id;
if (CallNewEnumerateNext(in_state,
aco.StatusPtr(), &out_state, &out_id) &&
aco.Ok() &&
jsval_from_JSVariant(cx, out_state, statep) &&
jsid_from_nsString(cx, out_id, idp))
{
JSObject* obj = GetJSObject(cx);
AutoResolveFlag arf(cx, obj);
return JS_DefinePropertyById(cx, obj, *idp, JSVAL_VOID, NULL, NULL,
JSPROP_ENUMERATE);
}
return JS_FALSE;
}
JSBool
ObjectWrapperParent::NewEnumerateDestroy(JSContext* cx, jsval state)
{
AutoCheckOperation aco(cx, this);
JSVariant in_state;
if (!jsval_to_JSVariant(cx, state, &in_state))
return JS_FALSE;
return SendNewEnumerateDestroy(in_state);
}
/*static*/ JSBool
ObjectWrapperParent::CPOW_NewEnumerate(JSContext *cx, HandleObject obj,
JSIterateOp enum_op, jsval *statep,
jsid *idp)
{
CPOW_LOG(("Calling CPOW_NewEnumerate..."));
ObjectWrapperParent* self = Unwrap(cx, obj);
if (!self)
return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_NewEnumerate");
switch (enum_op) {
case JSENUMERATE_INIT:
case JSENUMERATE_INIT_ALL:
self->Manager()->RequestRunToCompletion();
return self->NewEnumerateInit(cx, statep, idp);
case JSENUMERATE_NEXT:
return self->NewEnumerateNext(cx, statep, idp);
case JSENUMERATE_DESTROY:
return self->NewEnumerateDestroy(cx, *statep);
}
NS_NOTREACHED("Unknown enum_op value in CPOW_NewEnumerate");
return JS_FALSE;
}
/*static*/ JSBool
ObjectWrapperParent::CPOW_NewResolve(JSContext *cx, HandleObject obj, HandleId id,
unsigned flags, MutableHandleObject objp)
{
CPOW_LOG(("Calling CPOW_NewResolve (%s)...",
JSVAL_TO_CSTR(cx, id)));
ObjectWrapperParent* self = Unwrap(cx, obj);
if (!self)
return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_NewResolve");
AutoCheckOperation aco(cx, self);
nsString in_id;
if (!jsval_to_nsString(cx, id, &in_id))
return JS_FALSE;
PObjectWrapperParent* out_pobj;
if (!self->Manager()->RequestRunToCompletion() ||
!self->CallNewResolve(in_id, flags,
aco.StatusPtr(), &out_pobj) ||
!aco.Ok() ||
!JSObject_from_PObjectWrapperParent(cx, out_pobj, objp))
return JS_FALSE;
if (objp) {
AutoResolveFlag arf(cx, objp.get());
Rooted<JSObject*> obj2(cx, objp);
JS_DefinePropertyById(cx, obj2, id, JSVAL_VOID, NULL, NULL,
JSPROP_ENUMERATE);
}
return JS_TRUE;
}
/*static*/ JSBool
ObjectWrapperParent::CPOW_Convert(JSContext *cx, HandleObject obj, JSType type,
MutableHandleValue vp)
{
CPOW_LOG(("Calling CPOW_Convert (to %s)...",
JS_GetTypeName(cx, type)));
ObjectWrapperParent* self = Unwrap(cx, obj);
if (!self)
return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_Convert");
vp.set(OBJECT_TO_JSVAL(obj));
return JS_TRUE;
}
/*static*/ void
ObjectWrapperParent::CPOW_Finalize(js::FreeOp* fop, JSObject* obj)
{
CPOW_LOG(("Calling CPOW_Finalize..."));
MOZ_ASSERT(js::GetObjectClass(obj) == &ObjectWrapperParent::sCPOW_JSClass);
ObjectWrapperParent* self =
static_cast<ObjectWrapperParent*>(JS_GetPrivate(obj));
if (self) {
self->mObj = NULL;
unused << ObjectWrapperParent::Send__delete__(self);
}
}
/*static*/ JSBool
ObjectWrapperParent::CPOW_Call(JSContext* cx, unsigned argc, jsval* vp)
{
CPOW_LOG(("Calling CPOW_Call..."));
Rooted<JSObject*> thisobj(cx, JS_THIS_OBJECT(cx, vp));
if (!thisobj)
return JS_FALSE;
ObjectWrapperParent* function =
Unwrap(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
if (!function)
return with_error(cx, JS_FALSE, "Could not unwrap CPOW function");
AutoCheckOperation aco(cx, function);
ObjectWrapperParent* receiver = Unwrap(cx, thisobj);
if (!receiver) {
// Substitute child global for parent global object.
// TODO First make sure we're really replacing the global object?
ContextWrapperParent* manager =
static_cast<ContextWrapperParent*>(function->Manager());
receiver = manager->GetGlobalObjectWrapper();
}
InfallibleTArray<JSVariant> in_argv(argc);
jsval* argv = JS_ARGV(cx, vp);
for (unsigned i = 0; i < argc; i++)
if (!jsval_to_JSVariant(cx, argv[i], in_argv.AppendElement()))
return JS_FALSE;
JSVariant out_rval;
return (function->Manager()->RequestRunToCompletion() &&
function->CallCall(receiver, in_argv,
aco.StatusPtr(), &out_rval) &&
aco.Ok() &&
jsval_from_JSVariant(cx, out_rval, vp));
}
/*static*/ JSBool
ObjectWrapperParent::CPOW_Construct(JSContext* cx, unsigned argc, jsval* vp)
{
CPOW_LOG(("Calling CPOW_Construct..."));
ObjectWrapperParent* constructor = Unwrap(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
if (!constructor)
return with_error(cx, JS_FALSE, "Could not unwrap CPOW constructor function");
AutoCheckOperation aco(cx, constructor);
InfallibleTArray<JSVariant> in_argv(argc);
jsval* argv = JS_ARGV(cx, vp);
for (unsigned i = 0; i < argc; i++)
if (!jsval_to_JSVariant(cx, argv[i], in_argv.AppendElement()))
return JS_FALSE;
PObjectWrapperParent* out_powp;
return (constructor->Manager()->RequestRunToCompletion() &&
constructor->CallConstruct(in_argv, aco.StatusPtr(), &out_powp) &&
aco.Ok() &&
jsval_from_PObjectWrapperParent(cx, out_powp, vp));
}
/*static*/ JSBool
ObjectWrapperParent::CPOW_HasInstance(JSContext *cx, HandleObject obj, MutableHandleValue v,
JSBool *bp)
{
CPOW_LOG(("Calling CPOW_HasInstance..."));
*bp = JS_FALSE;
ObjectWrapperParent* self = Unwrap(cx, obj);
if (!self)
return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_HasInstance");
AutoCheckOperation aco(cx, self);
JSVariant in_v;
if (!jsval_to_JSVariant(cx, v, &in_v))
return JS_FALSE;
return (self->Manager()->RequestRunToCompletion() &&
self->CallHasInstance(in_v,
aco.StatusPtr(), bp) &&
aco.Ok());
}

Просмотреть файл

@ -1,149 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef mozilla_jsipc_ObjectWrapperParent_h
#define mozilla_jsipc_ObjectWrapperParent_h
#include "mozilla/jsipc/PObjectWrapperParent.h"
#include "jsapi.h"
#include "jsclass.h"
#include "nsAutoJSValHolder.h"
namespace mozilla {
namespace jsipc {
class ContextWrapperParent;
class OperationChecker {
public:
virtual void CheckOperation(JSContext* cx,
OperationStatus* status) = 0;
};
class ObjectWrapperParent
: public PObjectWrapperParent
, public OperationChecker
{
public:
ObjectWrapperParent()
: mObj(NULL)
{}
JSObject* GetJSObject(JSContext* cx) const;
JSObject* GetJSObjectOrNull() const {
return mObj;
}
jsval GetJSVal(JSContext* cx) const {
return OBJECT_TO_JSVAL(GetJSObject(cx));
}
void CheckOperation(JSContext* cx,
OperationStatus* status);
static const js::Class sCPOW_JSClass;
protected:
void ActorDestroy(ActorDestroyReason why);
ContextWrapperParent* Manager();
private:
mutable JSObject* mObj;
static JSBool
CPOW_AddProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
JS::MutableHandleValue vp);
static JSBool
CPOW_DelProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JSBool *succeeded);
static JSBool
CPOW_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
JS::MutableHandleValue vp);
static JSBool
CPOW_SetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JSBool strict,
JS::MutableHandleValue vp);
JSBool NewEnumerateInit(JSContext* cx, jsval* statep, jsid* idp);
JSBool NewEnumerateNext(JSContext* cx, jsval* statep, jsid* idp);
JSBool NewEnumerateDestroy(JSContext* cx, jsval state);
static JSBool
CPOW_NewEnumerate(JSContext *cx, JS::HandleObject obj, JSIterateOp enum_op,
jsval *statep, jsid *idp);
static JSBool
CPOW_NewResolve(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned flags,
JS::MutableHandleObject objp);
static JSBool
CPOW_Convert(JSContext *cx, JS::HandleObject obj, JSType type, JS::MutableHandleValue vp);
static void
CPOW_Finalize(js::FreeOp* fop, JSObject* obj);
static JSBool
CPOW_Call(JSContext* cx, unsigned argc, jsval* vp);
static JSBool
CPOW_Construct(JSContext *cx, unsigned argc, jsval *vp);
static JSBool
CPOW_HasInstance(JSContext *cx, JS::HandleObject obj, JS::MutableHandleValue vp, JSBool *bp);
static bool jsval_to_JSVariant(JSContext* cx, jsval from, JSVariant* to);
static bool jsval_from_JSVariant(JSContext* cx, const JSVariant& from,
jsval* to);
static bool boolean_from_JSVariant(JSContext* cx, const JSVariant& from,
JSBool* to);
static bool
JSObject_to_PObjectWrapperParent(JSContext* cx, JSObject* from,
PObjectWrapperParent** to);
static bool
JSObject_from_PObjectWrapperParent(JSContext* cx,
const PObjectWrapperParent* from,
JS::MutableHandleObject to);
static bool
jsval_from_PObjectWrapperParent(JSContext* cx,
const PObjectWrapperParent* from,
jsval* to);
};
template <class StatusOwnerPolicy>
class AutoCheckOperationBase
: public StatusOwnerPolicy
{
JSContext* const mContext;
OperationChecker* const mChecker;
protected:
AutoCheckOperationBase(JSContext* cx,
OperationChecker* checker)
: mContext(cx)
, mChecker(checker)
{}
virtual ~AutoCheckOperationBase() {
mChecker->CheckOperation(mContext, StatusOwnerPolicy::StatusPtr());
}
public:
bool Ok() {
return (StatusOwnerPolicy::StatusPtr()->type() == OperationStatus::TJSBool &&
StatusOwnerPolicy::StatusPtr()->get_JSBool());
}
};
}}
#endif /* mozilla_jsipc_ObjectWrapperParent_h */

Просмотреть файл

@ -1,23 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* 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 protocol PTestShell;
include protocol PObjectWrapper;
namespace mozilla {
namespace jsipc {
rpc protocol PContextWrapper
{
manager PTestShell;
manages PObjectWrapper;
parent:
async __delete__();
async PObjectWrapper(bool makeGlobal);
};
}}

47
js/ipc/PJavaScript.ipdl Normal file
Просмотреть файл

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* 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 protocol PContent;
include DOMTypes;
include JavaScriptTypes;
using mozilla::void_t;
namespace mozilla {
namespace jsipc {
rpc protocol PJavaScript
{
manager PContent;
child:
// The parent process no longer holds any references to the child object.
async DropObject(uint64_t objId);
// These roughly map to the ProxyHandler hooks that CPOWs need.
urgent Has(uint64_t objId, nsString id) returns (ReturnStatus rs, bool has);
urgent HasOwn(uint64_t objId, nsString id) returns (ReturnStatus rs, bool has);
urgent Get(uint64_t objId, uint64_t receiverId, nsString id) returns (ReturnStatus rs, JSVariant result);
urgent Set(uint64_t objId, uint64_t receiverId, nsString id, bool strict, JSVariant value) returns (ReturnStatus rs, JSVariant result);
urgent Call(uint64_t objId, JSParam[] argv) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
urgent InstanceOf(uint64_t objId, JSIID iid) returns (ReturnStatus rs, bool instanceof);
urgent GetPropertyDescriptor(uint64_t objId, nsString id, uint32_t flags) returns (ReturnStatus rs, PPropertyDescriptor result);
urgent GetOwnPropertyDescriptor(uint64_t objId, nsString id, uint32_t flags) returns (ReturnStatus rs, PPropertyDescriptor result);
urgent GetOwnPropertyNames(uint64_t objId) returns (ReturnStatus rs, nsString[] names);
urgent Keys(uint64_t objId) returns (ReturnStatus rs, nsString[] names);
urgent ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
urgent ClassName(uint64_t objId) returns (nsString name);
urgent IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
urgent PreventExtensions(uint64_t objId) returns (ReturnStatus rs);
parent:
async __delete__();
};
}
}

Просмотреть файл

@ -1,92 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* 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 protocol PContextWrapper;
include "mozilla/jsipc/CPOWTypes.h";
using mozilla::void_t;
using JSType;
using JSBool;
namespace mozilla {
namespace jsipc {
union JSVariant {
void_t;
nullable PObjectWrapper;
nsString;
int;
double;
bool; // We'd like to use JSBool here, but IPC::ParamTraits would
// treat JSBool as int.
};
union OperationStatus {
JSBool;
JSVariant; // Exception thrown.
};
rpc protocol PObjectWrapper
{
manager PContextWrapper;
child:
__delete__(); // unroot
rpc AddProperty(nsString id)
returns (OperationStatus status);
rpc GetProperty(nsString id)
returns (OperationStatus status,
JSVariant vp);
rpc SetProperty(nsString id,
JSVariant v)
returns (OperationStatus status,
JSVariant vp);
rpc DelProperty(nsString id)
returns (OperationStatus status,
JSVariant vp);
rpc NewEnumerateInit()
returns (OperationStatus status,
JSVariant statep,
int idp);
rpc NewEnumerateNext(JSVariant in_state)
returns (OperationStatus status,
JSVariant statep,
nsString idp);
async NewEnumerateDestroy(JSVariant in_state);
rpc NewResolve(nsString id,
int flags)
returns (OperationStatus status,
nullable PObjectWrapper obj2);
rpc Convert(JSType type)
returns (OperationStatus status,
JSVariant vp);
rpc Call(PObjectWrapper receiver,
JSVariant[] argv)
returns (OperationStatus status,
JSVariant rval);
rpc Construct(JSVariant[] argv)
returns (OperationStatus status,
nullable PObjectWrapper rval);
rpc HasInstance(JSVariant v)
returns (OperationStatus status,
JSBool bp);
};
}}

Просмотреть файл

@ -3,6 +3,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
IPDLSRCS = \
PContextWrapper.ipdl \
PObjectWrapper.ipdl \
JavaScriptTypes.ipdlh \
PJavaScript.ipdl \
$(NULL)

Просмотреть файл

@ -1,7 +0,0 @@
# 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/.
toolkit.jar:
content/global/cpow/test.xul (tests/adhoc/test.xul)
content/global/cpow/child.html (tests/adhoc/child.html)

Просмотреть файл

@ -6,16 +6,9 @@
MODULE = 'js'
EXPORTS.mozilla.jsipc += [
'CPOWTypes.h',
'ContextWrapperChild.h',
'ContextWrapperParent.h',
'ObjectWrapperChild.h',
'ObjectWrapperParent.h',
]
CPP_SOURCES += [
'ObjectWrapperChild.cpp',
'ObjectWrapperParent.cpp',
'JavaScriptChild.cpp',
'JavaScriptParent.cpp',
'JavaScriptShared.cpp',
]

Просмотреть файл

@ -1,30 +0,0 @@
<html>
<head>
</head>
<body>
<script>
window.foo = {
a: 42,
b: 37 * 73,
ctor: function(name, value) {
this[name] = value;
},
fakector: function(name, value) {
window[name] = "oyez";
this[name] = value;
return window;
},
f: function(x) {
document.body.appendChild(document.createElement("div")).innerHTML =
"called f(" + x + ")";
return x + Math.PI;
},
pitch: function(ball) {
throw ball;
}
};
window.foo.self = window.foo;
</script>
oyez
</body>
</html>

Просмотреть файл

@ -1,103 +0,0 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
width="800" height="600" orient="vertical">
<script type="application/javascript">
var cpow_tests = {
resolve: function(obj, keys) {
alert("resolving");
keys = keys.split(" ");
for (var i = 0; i != keys.length; ++i)
alert("has " + keys[i] + "? " + (keys[i] in obj));
alert("done resolving");
},
iterate: function(obj) {
alert("iterating");
for (var k in obj)
alert("key: " + k);
alert("done iterating");
},
navigate: function(child) {
alert("navigating");
child.location = prompt("Where to?");
setTimeout(function() {
alert(child.location.href);
}, 2000);
},
construct: function(foo) {
alert(new foo.ctor("answer", 42).answer);
alert(new foo.fakector("answer", 42).answer);
},
indirect_eval: function(child) {
alert(child.eval("location.href"));
alert(new child.Function("x", "return x+1")(42));
},
funcalls: function(foo) {
var fn = foo.f;
alert(foo.f(2));
alert(fn.call.call(fn, foo, 3));
},
equality: function(child) {
var foo = child.foo,
self = foo.self;
alert("foo == self? " + (foo == self));
alert("foo === self? " + (foo === self));
},
exceptions: function(child) {
var ball = "ball";
try {
child.foo.pitch(ball);
alert("shouldn't reach this point");
} catch (x) {
alert("ball === x? " + (ball === x));
}
}
}
function getCPOW() {
if (!getCPOW.cpow) {
var page = document.getElementById("page"),
owner = page.QueryInterface(Components.interfaces.nsIFrameLoaderOwner);
getCPOW.cpow = owner.crossProcessObjectWrapper;
alert("got fresh CPOW");
}
return getCPOW.cpow;
}
function test_cpow() {
var child = getCPOW();
cpow_tests.construct(child.foo);
cpow_tests.resolve(child.location, "href hostname");
cpow_tests.iterate(child.location);
cpow_tests.iterate(child.foo);
cpow_tests.funcalls(child.foo);
cpow_tests.navigate(child);
cpow_tests.equality(child);
cpow_tests.exceptions(child);
setTimeout(function() {
alert("going back");
child.history.back();
}, 3000);
}
function show_location() {
var child = getCPOW();
child.location += "#fragment";
alert(child.location.href);
alert(child.document.documentURI);
}
</script>
<toolbar id="controls">
<toolbarbutton onclick="test_cpow()" label="Run tests."/>
<toolbarbutton onclick="show_location()" label="Show location."/>
</toolbar>
<browser remote="true" width="200" height="200"
type="content"
src="child.html"
id="page" />
</window>

Просмотреть файл

@ -1,11 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
MODULE = 'test_jsipc'
# FIXME/bug 575918: out-of-process xpcshell is broken on OS X
if CONFIG['OS_ARCH'] != 'Darwin':
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']

Просмотреть файл

@ -1,66 +0,0 @@
var data = {
answer: 42,
nested: { objects: { work: "yes they do" } },
arr: [
"zeroeth",
{ foo: "bar" },
function() { return data },
{ toString: function() { return "last" } }
],
toString: function() {
return "CPOW";
}
};
var empty = function() {
this.try_to_delete = "just try";
};
empty.prototype = {
try_to_delete: "bwahaha",
inherited: "inherited",
method: function() {
return "called"
}
};
data.derived = new empty;
(data.constructor = function(value) {
var self = this;
this.value = value;
this.check = function(that) {
do_check_eq(this.value, that.value);
do_check_eq(this, self);
do_check_eq(this, that);
do_check_false(this.isGlobal());
};
}).prototype = {
isGlobal: function() {
return (function() { return this })() == this;
}
};
function A() {
this.a = A;
this.b = A;
}
function B() {
this.b = B;
this.c = B;
}
B.prototype = new A;
function pitch(ball) {
throw ball;
}
get_set = {
get foo() { return 42; },
get foo_throws() { throw "BAM"; },
set one(val) { this.two = val + 1; }
};
function type(x) {
return typeof x;
}
function run_test() {}

Просмотреть файл

@ -1,277 +0,0 @@
function run_test() {
run_test_in_child("cpow_child.js", run_actual_tests);
}
function run_actual_tests() {
var obj = getChildGlobalObject();
test_properties(obj.data);
test_delete(obj.data);
test_toString(obj.data);
test_inheritance(obj.data.derived);
test_constructor(obj.data.constructor,
obj.Function,
obj.Object,
obj.Date);
test_instanceof(obj.A, obj.B);
test_enumeration(obj.A, obj.B);
test_Array(obj.Array);
test_Function(obj.Function);
test_exceptions(obj.pitch, obj.Object);
test_generators(obj.Function);
test_Iterator(obj.data, obj.Iterator, obj.StopIteration);
test_getters_setters(obj.get_set, obj.Function);
test_forbidden_things(obj);
do_test_finished();
}
function test_properties(data) {
do_check_true("answer" in data);
do_check_false("cnefhasefho" in data.nested);
do_check_eq(data.answer, 42);
do_check_eq(data.nested.objects.work,
"yes they do");
do_check_eq(data.asodfijasdiofj, void(0));
do_check_eq(data.arr.length, 4);
do_check_eq(data.arr[4], void(0));
do_check_eq(data.arr[0], "zeroeth");
do_check_eq(data.arr[1].foo, "bar");
do_check_true(2 in data.arr);
do_check_eq(data.arr[2], data.arr[2]); // ensure reuse
do_check_true(data.arr[2]() === data);
do_check_eq(data.arr[2]().arr[0], "zeroeth");
do_check_true("call" in data.arr[2]);
do_check_eq(data.arr[3], "last");
}
function test_delete(data) {
do_check_eq(data.derived.try_to_delete, "just try");
do_check_true(delete data.derived.try_to_delete);
do_check_eq(data.derived.try_to_delete, "bwahaha");
do_check_true(delete data.derived.try_to_delete);
do_check_eq(data.derived.try_to_delete, "bwahaha");
}
function test_toString(data) {
do_check_eq(data.toString(), "CPOW");
do_check_eq(data + "asdf", "CPOWasdf");
do_check_eq(new String(data), "CPOW");
}
function test_inheritance(derived) {
do_check_true("inherited" in derived);
do_check_false(derived.hasOwnProperty("inherited"));
var base = derived.__proto__;
do_check_true(base.hasOwnProperty("inherited"));
do_check_eq(derived.inherited, "inherited");
do_check_eq(derived.method(), "called");
do_check_eq(base.method, derived.method);
do_check_true(base.method === derived.method);
}
function test_constructor(ctor,
ChildFunction,
ChildObject,
ChildDate) {
var obj = new ctor(42);
obj.check(obj);
// Re-run test_inheritance, creating locally everything that
// test_inheritance expects to be created in the child process:
var empty = new ChildFunction(),
proto = new ChildObject();
proto.inherited = "inherited";
proto.method = new ChildFunction("return 'called'");
empty.prototype = proto;
test_inheritance(new empty);
var cd = new ChildDate,
tolerance_ms = 20000; // Ridiculously large to accommodate gcZeal delays.
do_check_eq(Math.max(Math.abs(cd.getTime() - new Date),
tolerance_ms),
tolerance_ms);
do_check_true(cd instanceof ChildDate);
}
function test_enumeration(A, B) {
function check(obj, nk, s) {
var keys = [];
for (var k in obj)
keys[keys.length] = k;
do_check_eq(keys.length, nk);
do_check_eq(keys.sort().join("~"), s);
}
check(new B, 3, "a~b~c");
check(B.prototype, 2, "a~b");
B.prototype = A.prototype;
A.prototype = new B;
check(new A, 3, "a~b~c");
check(new B, 2, "b~c");
// Put things back the way they were, mostly:
A.prototype = B.prototype;
B.prototype = new A;
}
function test_instanceof(A, B) {
var a = new A, b = new B;
do_check_true(a instanceof A);
do_check_false(a instanceof B);
do_check_true(b instanceof A);
do_check_true(b instanceof B);
}
function test_Array(ChildArray) {
do_check_true(!!ChildArray);
var arr = new ChildArray(1, new ChildArray(2, 3), 4);
do_check_eq(arr.length, 3);
do_check_eq(arr.slice(1).shift()[1], 3);
arr[2] = arr[1];
do_check_eq(arr.pop()[0], 2);
}
function test_Function(ChildFunction) {
var succ = new ChildFunction("x", "return x + 1");
do_check_eq(succ(succ(3)), 5);
do_check_eq(succ + "", "" + new Function("x", "return x + 1"));
}
function test_exceptions(pitch, ChildObject) {
try {
throw "parent-only";
} catch (x) {
do_check_eq(x, "parent-only");
}
var ball = new ChildObject(),
thrown = false;
ball.sport = "baseball";
try {
pitch(ball);
do_throw("Should have thrown.");
} catch (x) {
thrown = true;
do_check_eq(x.sport, "baseball");
do_check_eq(x, ball);
do_check_true(x === ball);
}
do_check_true(thrown);
}
function test_generators(ChildFunction) {
// Run the test with own Function just to keep sane:
if (ChildFunction != Function)
test_generators(Function);
var count = 0, sum = 0,
genFn = new ChildFunction("for(var i = 1; i < 4; i++) yield i");
var gen = genFn();
do try { sum += gen.next(); }
catch (x) { break; }
while ((count += 1));
do_check_eq(count, 3);
do_check_eq(sum, 6);
try {
for (var n in genFn()) {
count += 1;
sum += n;
}
} catch (x) {}
do_check_eq(count, 6);
do_check_eq(sum, 12);
}
function test_Iterator(data, ChildIterator, ChildStopIteration) {
do_check_true(data && ChildIterator && true);
var copy = {},
thrown = null;
try {
for (var kv in ChildIterator(data)) {
do_check_true(kv[0] in data);
do_check_eq(data[kv[0]], kv[1]);
copy[kv[0]] = kv[1];
}
// XXX I shouldn't have to be catching this, should I?
} catch (x) { thrown = x; }
do_check_true(thrown != null);
do_check_true(thrown instanceof ChildStopIteration);
do_check_eq(copy + "", "CPOW");
}
function test_getters_setters(get_set, ChildFunction) {
do_check_eq(get_set.foo, 42);
var thrown = null;
try { get_set.bar = get_set.foo_throws; }
catch (x) { thrown = x; }
do_check_true(thrown != null);
do_check_eq(thrown, "BAM");
do_check_false("bar" in get_set);
get_set.two = 2222;
get_set.one = 1;
do_check_eq(get_set.two, 2);
var getter = new ChildFunction("return 'you got me'");
get_set.__defineGetter__("defined_getter", getter);
do_check_eq(get_set.defined_getter, "you got me");
do_check_eq(get_set.__lookupGetter__("defined_getter"),
getter);
var setter = new ChildFunction("val", "this.side_effect = val");
get_set.__defineSetter__("defined_setter", setter);
get_set.side_effect = "can't touch this";
get_set.defined_setter = "you set me";
do_check_eq(get_set.side_effect, "you set me");
do_check_eq(get_set.__lookupSetter__("defined_setter"),
setter);
}
function test_forbidden_things(child) {
var x_count = 0;
do_check_eq(child.type(42), "number");
try {
child.type(function(){});
do_throw("Should not have been able to pass a parent-created " +
"function to the child");
} catch (x) {
print(x);
x_count += 1;
}
try {
child.type({});
do_throw("Should not have been able to pass a parent-created " +
"object to the child");
} catch (x) {
print(x);
x_count += 1;
}
try {
child.type.prop = {};
do_throw("Should not have been able to set a property of a child " +
"object to a parent-created object value");
} catch (x) {
print(x);
x_count += 1;
}
try {
child.type.prop = function(){};
do_throw("Should not have been able to set a property of a child " +
"object to a parent-created function value");
} catch (x) {
print(x);
x_count += 1;
}
do_check_eq(x_count, 4);
}

Просмотреть файл

@ -760,13 +760,15 @@ js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots)
}
/* The embedding can register additional roots here. */
if (JSTraceDataOp op = rt->gcBlackRootsTraceOp)
(*op)(trc, rt->gcBlackRootsData);
for (size_t i = 0; i < rt->gcBlackRootTracers.length(); i++) {
const JSRuntime::ExtraTracer &e = rt->gcBlackRootTracers[i];
(*e.op)(trc, e.data);
}
/* During GC, we don't mark gray roots at this stage. */
if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) {
if (JSTraceDataOp op = rt->gcGrayRootTracer.op) {
if (!IS_GC_MARKING_TRACER(trc))
(*op)(trc, rt->gcGrayRootsData);
(*op)(trc, rt->gcGrayRootTracer.data);
}
}
@ -774,9 +776,9 @@ void
js::gc::BufferGrayRoots(GCMarker *gcmarker)
{
JSRuntime *rt = gcmarker->runtime;
if (JSTraceDataOp op = rt->gcGrayRootsTraceOp) {
if (JSTraceDataOp op = rt->gcGrayRootTracer.op) {
gcmarker->startBufferingGrayRoots();
(*op)(gcmarker, rt->gcGrayRootsData);
(*op)(gcmarker, rt->gcGrayRootTracer.data);
gcmarker->endBufferingGrayRoots();
}
}

Просмотреть файл

@ -816,10 +816,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
analysisPurgeCallback(NULL),
analysisPurgeTriggerBytes(0),
gcMallocBytes(0),
gcBlackRootsTraceOp(NULL),
gcBlackRootsData(NULL),
gcGrayRootsTraceOp(NULL),
gcGrayRootsData(NULL),
autoGCRooters(NULL),
scriptAndCountsVector(NULL),
NaNValue(UndefinedValue()),
@ -2292,12 +2288,24 @@ JS_AnchorPtr(void *p)
{
}
JS_PUBLIC_API(void)
JS_SetExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
JS_PUBLIC_API(JSBool)
JS_AddExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
{
AssertHeapIsIdle(rt);
rt->gcBlackRootsTraceOp = traceOp;
rt->gcBlackRootsData = data;
return !!rt->gcBlackRootTracers.append(JSRuntime::ExtraTracer(traceOp, data));
}
JS_PUBLIC_API(void)
JS_RemoveExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
{
AssertHeapIsIdle(rt);
for (size_t i = 0; i < rt->gcBlackRootTracers.length(); i++) {
JSRuntime::ExtraTracer *e = &rt->gcBlackRootTracers[i];
if (e->op == traceOp && e->data == data) {
rt->gcBlackRootTracers.erase(e);
break;
}
}
}
JS_PUBLIC_API(void)
@ -7157,3 +7165,15 @@ JS_GetScriptedGlobal(JSContext *cx)
return &i.scopeChain()->global();
}
JS_PUBLIC_API(JSBool)
JS_PreventExtensions(JSContext *cx, JS::HandleObject obj)
{
JSBool extensible;
if (!JS_IsExtensible(cx, obj, &extensible))
return JS_TRUE;
if (extensible)
return JS_TRUE;
return JSObject::preventExtensions(cx, obj);
}

Просмотреть файл

@ -2373,8 +2373,12 @@ JS_AnchorPtr(void *p);
* JS_CallTracer whenever the root contains a traceable thing.
* data: the data argument to pass to each invocation of traceOp.
*/
extern JS_PUBLIC_API(JSBool)
JS_AddExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
/* Undo a call to JS_AddExtraGCRootsTracer. */
extern JS_PUBLIC_API(void)
JS_SetExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
JS_RemoveExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
/*
* JS_CallTracer API and related macros for implementors of JSTraceOp, to
@ -3217,6 +3221,9 @@ JS_DeepFreezeObject(JSContext *cx, JSObject *obj);
extern JS_PUBLIC_API(JSBool)
JS_FreezeObject(JSContext *cx, JSObject *obj);
extern JS_PUBLIC_API(JSBool)
JS_PreventExtensions(JSContext *cx, JS::HandleObject obj);
extern JS_PUBLIC_API(JSObject *)
JS_New(JSContext *cx, JSObject *ctor, unsigned argc, jsval *argv);

Просмотреть файл

@ -1165,16 +1165,27 @@ struct JSRuntime : public JS::shadow::Runtime,
return needsBarrier_;
}
struct ExtraTracer {
JSTraceDataOp op;
void *data;
ExtraTracer()
: op(NULL), data(NULL)
{}
ExtraTracer(JSTraceDataOp op, void *data)
: op(op), data(data)
{}
};
/*
* The trace operations to trace embedding-specific GC roots. One is for
* tracing through black roots and the other is for tracing through gray
* roots. The black/gray distinction is only relevant to the cycle
* collector.
*/
JSTraceDataOp gcBlackRootsTraceOp;
void *gcBlackRootsData;
JSTraceDataOp gcGrayRootsTraceOp;
void *gcGrayRootsData;
typedef js::Vector<ExtraTracer, 4, js::SystemAllocPolicy> ExtraTracerVector;
ExtraTracerVector gcBlackRootTracers;
ExtraTracer gcGrayRootTracer;
/* Stack of thread-stack-allocated GC roots. */
js::AutoGCRooter *autoGCRooters;

Просмотреть файл

@ -52,8 +52,8 @@ JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook)
JS_FRIEND_API(void)
JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
{
rt->gcGrayRootsTraceOp = traceOp;
rt->gcGrayRootsData = data;
rt->gcGrayRootTracer.op = traceOp;
rt->gcGrayRootTracer.data = data;
}
JS_FRIEND_API(JSString *)
@ -302,6 +302,18 @@ JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *objArg, const JSFunctionSpec
return true;
}
JS_FRIEND_API(bool)
js_ObjectClassIs(JSContext *cx, HandleObject obj, ESClassValue classValue)
{
return ObjectClassIs(obj, classValue, cx);
}
JS_FRIEND_API(const char *)
js_ObjectClassName(JSContext *cx, HandleObject obj)
{
return JSObject::className(cx, obj);
}
AutoSwitchCompartment::AutoSwitchCompartment(JSContext *cx, JSCompartment *newCompartment
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: cx(cx), oldCompartment(cx->compartment())

Просмотреть файл

@ -133,6 +133,12 @@ js_GetterOnlyPropertyStub(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
JS_FRIEND_API(void)
js_ReportOverRecursed(JSContext *maybecx);
JS_FRIEND_API(bool)
js_ObjectClassIs(JSContext *cx, JS::HandleObject obj, js::ESClassValue classValue);
JS_FRIEND_API(const char *)
js_ObjectClassName(JSContext *cx, JS::HandleObject obj);
#ifdef DEBUG
/*

Просмотреть файл

@ -2917,8 +2917,8 @@ MarkGrayReferences(JSRuntime *rt)
gcmarker->markBufferedGrayRoots(zone);
} else {
JS_ASSERT(!rt->gcIsIncremental);
if (JSTraceDataOp op = rt->gcGrayRootsTraceOp)
(*op)(gcmarker, rt->gcGrayRootsData);
if (JSTraceDataOp op = rt->gcGrayRootTracer.op)
(*op)(gcmarker, rt->gcGrayRootTracer.data);
}
SliceBudget budget;
gcmarker->drainMarkStack(budget);

Просмотреть файл

@ -680,19 +680,6 @@ SendCommand(JSContext* cx,
return true;
}
static JSBool
GetChildGlobalObject(JSContext* cx,
unsigned,
jsval* vp)
{
JS::Rooted<JSObject*> global(cx);
if (XRE_GetChildGlobalObject(cx, global.address())) {
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(global));
return true;
}
return false;
}
/*
* JSContext option name to flag map. The option names are in alphabetical
* order for better reporting.
@ -919,7 +906,6 @@ static const JSFunctionSpec glob_functions[] = {
JS_FS("dumpHeap", DumpHeap, 5,0),
#endif
JS_FS("sendCommand", SendCommand, 1,0),
JS_FS("getChildGlobalObject", GetChildGlobalObject, 0,0),
JS_FS("atob", Atob, 1,0),
JS_FS("btoa", Btoa, 1,0),
JS_FS("Blob", Blob, 2,JSFUN_CONSTRUCTOR),

Просмотреть файл

@ -32,8 +32,11 @@ LOCAL_INCLUDES = \
-I$(topsrcdir)/layout/base \
-I$(topsrcdir)/dom/base \
-I$(topsrcdir)/xpcom/ds \
-I$(topsrcdir)/js/ipc \
$(NULL)
include $(topsrcdir)/ipc/chromium/chromium-config.mk
SHARED_LIBRARY_LIBS = \
../loader/$(LIB_PREFIX)jsloader_s.$(LIB_SUFFIX) \
../wrappers/$(LIB_PREFIX)xpcwrappers_s.$(LIB_SUFFIX) \

Просмотреть файл

@ -23,6 +23,7 @@
#include "jsapi.h"
#include "jsfriendapi.h"
#include "JavaScriptParent.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/PrimitiveConversions.h"
@ -62,6 +63,18 @@ XPCConvert::IsMethodReflectable(const XPTMethodDescriptor& info)
return true;
}
static JSObject*
UnwrapNativeCPOW(nsISupports* wrapper)
{
nsCOMPtr<nsIXPConnectWrappedJS> underware = do_QueryInterface(wrapper);
if (underware) {
JSObject* mainObj = underware->GetJSObject();
if (mainObj && mozilla::jsipc::JavaScriptParent::IsCPOW(mainObj))
return mainObj;
}
return nullptr;
}
/***************************************************************************/
// static
@ -793,8 +806,7 @@ XPCConvert::NativeInterface2JSObject(jsval* d,
*d = JSVAL_NULL;
if (dest)
*dest = nullptr;
nsISupports *src = aHelper.Object();
if (!src)
if (!aHelper.Object())
return true;
if (pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
@ -839,6 +851,17 @@ XPCConvert::NativeInterface2JSObject(jsval* d,
flat = nullptr;
}
// Don't double wrap CPOWs. This is a temporary measure for compatibility
// with objects that don't provide necessary QIs (such as objects under
// the new DOM bindings). We expect the other side of the CPOW to have
// the appropriate wrappers in place.
if (JSObject *cpow = UnwrapNativeCPOW(aHelper.Object())) {
if (!JS_WrapObject(cx, &cpow))
return false;
*d = OBJECT_TO_JSVAL(cpow);
return true;
}
// We can't simply construct a slim wrapper. Go ahead and create an
// XPCWrappedNative for this object. At this point, |flat| could be
// non-null, meaning that either we already have a wrapped native from

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/Attributes.h"
#include "XPCWrapper.h"
#include "JavaScriptParent.h"
using namespace mozilla::dom;
using namespace JS;
@ -476,7 +477,10 @@ static JSObject *
FindObjectForHasInstance(JSContext *cx, HandleObject objArg)
{
RootedObject obj(cx, objArg), proto(cx);
while (obj && !IS_WN_REFLECTOR(obj) && !IsDOMObject(obj)) {
while (obj && !IS_WN_REFLECTOR(obj) &&
!IsDOMObject(obj) && !mozilla::jsipc::JavaScriptParent::IsCPOW(obj))
{
if (js::IsWrapper(obj)) {
obj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
continue;
@ -488,6 +492,57 @@ FindObjectForHasInstance(JSContext *cx, HandleObject objArg)
return obj;
}
nsresult
xpc::HasInstance(JSContext *cx, HandleObject objArg, const nsID *iid, bool *bp)
{
*bp = false;
RootedObject obj(cx, FindObjectForHasInstance(cx, objArg));
if (!obj)
return NS_OK;
if (IsDOMObject(obj)) {
// Not all DOM objects implement nsISupports. But if they don't,
// there's nothing to do in this HasInstance hook.
nsISupports *identity = UnwrapDOMObjectToISupports(obj);
if (!identity)
return NS_OK;;
nsCOMPtr<nsISupports> supp;
identity->QueryInterface(*iid, getter_AddRefs(supp));
*bp = supp;
return NS_OK;
}
if (mozilla::jsipc::JavaScriptParent::IsCPOW(obj))
return mozilla::jsipc::JavaScriptParent::InstanceOf(obj, iid, bp);
MOZ_ASSERT(IS_WN_REFLECTOR(obj));
XPCWrappedNative* other_wrapper = XPCWrappedNative::Get(obj);
if (!other_wrapper)
return NS_OK;
// We'll trust the interface set of the wrapper if this is known
// to be an interface that the objects *expects* to be able to
// handle.
if (other_wrapper->HasInterfaceNoQI(*iid)) {
*bp = true;
return NS_OK;
}
// Otherwise, we'll end up Querying the native object to be sure.
XPCCallContext ccx(JS_CALLER, cx);
AutoMarkingNativeInterfacePtr iface(ccx);
iface = XPCNativeInterface::GetNewOrUsed(iid);
nsresult findResult = NS_OK;
if (iface && other_wrapper->FindTearOff(iface, false, &findResult))
*bp = true;
if (NS_FAILED(findResult) && findResult != NS_ERROR_NO_INTERFACE)
return findResult;
return NS_OK;
}
/* bool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out bool bp); */
NS_IMETHODIMP
@ -496,60 +551,16 @@ nsJSIID::HasInstance(nsIXPConnectWrappedNative *wrapper,
const jsval &val, bool *bp, bool *_retval)
{
*bp = false;
nsresult rv = NS_OK;
if (!JSVAL_IS_PRIMITIVE(val)) {
// we have a JSObject
RootedObject obj(cx, JSVAL_TO_OBJECT(val));
if (JSVAL_IS_PRIMITIVE(val))
return NS_OK;
NS_ASSERTION(obj, "when is an object not an object?");
// we have a JSObject
RootedObject obj(cx, JSVAL_TO_OBJECT(val));
// is this really a native xpcom object with a wrapper?
const nsIID* iid;
mInfo->GetIIDShared(&iid);
obj = FindObjectForHasInstance(cx, obj);
if (!obj)
return NS_OK;
if (IsDOMObject(obj)) {
// Not all DOM objects implement nsISupports. But if they don't,
// there's nothing to do in this HasInstance hook.
nsISupports *identity = UnwrapDOMObjectToISupports(obj);
if (!identity)
return NS_OK;
nsCOMPtr<nsISupports> supp;
identity->QueryInterface(*iid, getter_AddRefs(supp));
*bp = supp;
return NS_OK;
}
MOZ_ASSERT(IS_WN_REFLECTOR(obj));
XPCWrappedNative* other_wrapper = XPCWrappedNative::Get(obj);
if (!other_wrapper)
return NS_OK;
// We'll trust the interface set of the wrapper if this is known
// to be an interface that the objects *expects* to be able to
// handle.
if (other_wrapper->HasInterfaceNoQI(*iid)) {
*bp = true;
return NS_OK;
}
// Otherwise, we'll end up Querying the native object to be sure.
XPCCallContext ccx(JS_CALLER, cx);
AutoMarkingNativeInterfacePtr iface(ccx);
iface = XPCNativeInterface::GetNewOrUsed(iid);
nsresult findResult = NS_OK;
if (iface && other_wrapper->FindTearOff(iface, false, &findResult))
*bp = true;
if (NS_FAILED(findResult) && findResult != NS_ERROR_NO_INTERFACE)
rv = findResult;
}
return rv;
const nsIID* iid;
mInfo->GetIIDShared(&iid);
return xpc::HasInstance(cx, obj, iid, bp);
}
/* string canCreateWrapper (in nsIIDPtr iid); */

Просмотреть файл

@ -16,6 +16,7 @@
#include "mozilla/Attributes.h"
#include "jsapi.h"
#include "jsfriendapi.h"
using namespace xpc;
using namespace JS;
@ -1649,8 +1650,37 @@ nsXPCWrappedJSClass::GetInterfaceName()
return mName;
}
static void
FinalizeStub(JSFreeOp *fop, JSObject *obj)
{
}
static JSClass XPCOutParamClass = {
"XPCOutParam",
0,
JS_PropertyStub,
JS_DeletePropertyStub,
JS_PropertyStub,
JS_StrictPropertyStub,
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
FinalizeStub,
NULL, /* checkAccess */
NULL, /* call */
NULL, /* hasInstance */
NULL, /* construct */
NULL /* trace */
};
bool
xpc::IsOutObject(JSContext* cx, JSObject* obj)
{
return js::GetObjectJSClass(obj) == &XPCOutParamClass;
}
JSObject*
nsXPCWrappedJSClass::NewOutObject(JSContext* cx, JSObject* scope)
xpc::NewOutObject(JSContext* cx, JSObject* scope)
{
return JS_NewObject(cx, nullptr, nullptr, JS_GetGlobalForObject(cx, scope));
}

Просмотреть файл

@ -62,6 +62,7 @@ XPC_MSG_DEF(NS_ERROR_XPC_BAD_INITIALIZER_NAME , "Bad initializer name
XPC_MSG_DEF(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN , "Operation failed because the XPConnect subsystem has been shutdown")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN , "Cannot modify properties of a WrappedNative")
XPC_MSG_DEF(NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL , "Could not convert JavaScript argument - 0 was passed, expected object. Did you mean null?")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE , "It's illegal to pass a CPOW to native code")
/* common global codes (from nsError.h) */

Просмотреть файл

@ -2708,8 +2708,6 @@ private:
nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID,
nsIInterfaceInfo* aInfo);
JSObject* NewOutObject(JSContext* cx, JSObject* scope);
JSBool IsReflectable(uint16_t i) const
{return (JSBool)(mDescriptors[i/32] & (1 << (i%32)));}
void SetReflectable(uint16_t i, JSBool b)
@ -3867,6 +3865,11 @@ GetObjectScope(JSObject *obj)
extern JSBool gDebugMode;
extern JSBool gDesiredDebugMode;
JSObject* NewOutObject(JSContext* cx, JSObject* scope);
bool IsOutObject(JSContext* cx, JSObject* obj);
nsresult HasInstance(JSContext *cx, JS::HandleObject objArg, const nsID *iid, bool *bp);
// Internal use only.
bool PushJSContext(JSContext *aCx);
void PopJSContext();

Просмотреть файл

@ -69,8 +69,6 @@
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/jsipc/ContextWrapperParent.h"
#include "mozilla/ipc/TestShellParent.h"
#include "mozilla/ipc/XPCShellEnvironment.h"
@ -96,9 +94,6 @@ using mozilla::dom::ContentProcess;
using mozilla::dom::ContentParent;
using mozilla::dom::ContentChild;
using mozilla::jsipc::PContextWrapperParent;
using mozilla::jsipc::ContextWrapperParent;
using mozilla::ipc::TestShellParent;
using mozilla::ipc::TestShellCommandParent;
using mozilla::ipc::XPCShellEnvironment;
@ -763,13 +758,6 @@ XRE_SendTestShellCommand(JSContext* aCx,
return true;
}
bool
XRE_GetChildGlobalObject(JSContext* aCx, JSObject** aGlobalP)
{
TestShellParent* tsp = GetOrCreateTestShellParent();
return tsp && tsp->GetGlobalJSObject(aCx, aGlobalP);
}
bool
XRE_ShutdownTestShell()
{

Просмотреть файл

@ -464,7 +464,9 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(uint32_t aMaxbytes,
MOZ_CRASH();
}
JS_SetExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this);
if (!JS_AddExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this)) {
MOZ_CRASH();
}
JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
mJSHolders.Init(512);

Просмотреть файл

@ -592,6 +592,7 @@
ERROR(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, FAILURE(51)),
ERROR(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, FAILURE(52)),
ERROR(NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL, FAILURE(53)),
ERROR(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, FAILURE(54)),
/* any new errors here should have an associated entry added in xpc.msg */
ERROR(NS_SUCCESS_I_DID_SOMETHING, SUCCESS(1)),

Просмотреть файл

@ -435,10 +435,6 @@ XRE_API(bool,
void* aCallback))
class JSObject;
XRE_API(bool,
XRE_GetChildGlobalObject, (JSContext* aCx,
JSObject** globalp))
XRE_API(bool,
XRE_ShutdownTestShell, ())