Implement the multi-process Jetpack API (bug 556846). r=bsmedberg,bent

This commit is contained in:
Ben Newman 2010-06-25 08:00:37 -07:00
Родитель 17048d28ab
Коммит efe16a69c1
17 изменённых файлов: 2113 добавлений и 26 удалений

305
js/jetpack/Handle.h Normal file
Просмотреть файл

@ -0,0 +1,305 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Newman <mozilla@benjamn.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla_jetpack_HandleParent_h
#define mozilla_jetpack_HandleParent_h
#include "mozilla/jetpack/PHandleParent.h"
#include "mozilla/jetpack/PHandleChild.h"
#include "jsapi.h"
#include "jsobj.h"
#include "jscntxt.h"
namespace mozilla {
namespace jetpack {
/**
* BaseType should be one of PHandleParent or PHandleChild; see the
* HandleParent and HandleChild typedefs at the bottom of this file.
*/
template <class BaseType>
class Handle
: public BaseType
{
Handle(Handle* parent)
: mParent(parent)
, mObj(NULL)
, mRuntime(NULL)
{}
BaseType* AllocPHandle() {
return new Handle(this);
}
bool DeallocPHandle(BaseType* actor) {
delete actor;
return true;
}
public:
Handle()
: mParent(NULL)
, mObj(NULL)
, mRuntime(NULL)
{}
~Handle() { TearDown(); }
static Handle* FromJSObject(JSContext* cx, JSObject* obj) {
// TODO Convert non-Handles to Handles somehow?
return Unwrap(cx, obj);
}
static Handle* FromJSVal(JSContext* cx, jsval val) {
if (!JSVAL_IS_OBJECT(val))
return NULL;
return Unwrap(cx, JSVAL_TO_OBJECT(val));
}
JSObject* ToJSObject(JSContext* cx) const {
if (!mObj && !mRuntime) {
JSAutoRequest request(cx);
JSClass* clasp = const_cast<JSClass*>(&sHandle_JSClass);
JSObject* obj = JS_NewObject(cx, clasp, NULL, NULL);
if (!obj)
return NULL;
js::AutoObjectRooter root(cx, obj);
JSPropertySpec* ps = const_cast<JSPropertySpec*>(sHandle_Properties);
JSFunctionSpec* fs = const_cast<JSFunctionSpec*>(sHandle_Functions);
JSRuntime* rt;
const char* name = IsParent(this)
? "mozilla::jetpack::Handle<PHandleParent>::mObj"
: "mozilla::jetpack::Handle<PHandleChild>::mObj";
if (JS_SetPrivate(cx, obj, (void*)this) &&
JS_DefineProperties(cx, obj, ps) &&
JS_DefineFunctions(cx, obj, fs) &&
JS_AddNamedRootRT(rt = JS_GetRuntime(cx), (void*)&mObj, name))
{
mObj = obj;
mRuntime = rt;
}
}
return mObj;
}
protected:
void ActorDestroy(typename Handle::ActorDestroyReason why) {
TearDown();
}
private:
static bool IsParent(const PHandleParent* handle) { return true; }
static bool IsParent(const PHandleChild* handle) { return false; }
void TearDown() {
if (mObj) {
mObj->setPrivate(NULL);
mObj = NULL;
// Nulling out mObj effectively unroots the object, but we still
// need to remove the root, else the JS engine will complain at
// shutdown.
NS_ASSERTION(mRuntime, "Should have a JSRuntime if we had an object");
JS_RemoveRootRT(mRuntime, (void*)&mObj);
// By not nulling out mRuntime, we prevent ToJSObject from
// reviving an invalidated/destroyed handle.
}
}
static const JSClass sHandle_JSClass;
static const JSPropertySpec sHandle_Properties[];
static const JSFunctionSpec sHandle_Functions[];
Handle* const mParent;
// Used to cache the JSObject returned by ToJSObject, which is
// otherwise a const method.
mutable JSObject* mObj;
mutable JSRuntime* mRuntime;
static Handle*
Unwrap(JSContext* cx, JSObject* obj) {
while (obj && obj->getClass() != &sHandle_JSClass)
obj = obj->getProto();
if (!obj)
return NULL;
Handle* self = static_cast<Handle*>(JS_GetPrivate(cx, obj));
NS_ASSERTION(!self || self->ToJSObject(cx) == obj,
"Wrapper and wrapped object disagree?");
return self;
}
static JSBool
GetParent(JSContext* cx, JSObject* obj, jsval, jsval* vp) {
JS_SET_RVAL(cx, vp, JSVAL_NULL);
Handle* self = Unwrap(cx, obj);
if (!self)
return JS_TRUE;
Handle* parent = self->mParent;
if (!parent)
return JS_TRUE;
JSObject* pobj = parent->ToJSObject(cx);
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(pobj));
return JS_TRUE;
}
static JSBool
GetIsValid(JSContext* cx, JSObject* obj, jsval, jsval* vp) {
Handle* self = Unwrap(cx, obj);
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(!!self));
return JS_TRUE;
}
static JSBool
Invalidate(JSContext* cx, uintN argc, jsval* vp) {
if (argc > 0) {
JS_ReportError(cx, "invalidate takes zero arguments");
return JS_FALSE;
}
Handle* self = Unwrap(cx, JS_THIS_OBJECT(cx, vp));
if (self) {
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_TRUE));
if (!Send__delete__(self)) {
JS_ReportError(cx, "Failed to send __delete__ while invalidating");
return JS_FALSE;
}
} else {
JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_FALSE));
}
return JS_TRUE;
}
static JSBool
CreateHandle(JSContext* cx, uintN argc, jsval* vp) {
if (argc > 0) {
JS_ReportError(cx, "createHandle takes zero arguments");
return JS_FALSE;
}
Handle* self = Unwrap(cx, JS_THIS_OBJECT(cx, vp));
if (!self) {
JS_ReportError(cx, "Tried to create child from invalid handle");
return JS_FALSE;
}
BaseType* child = self->SendPHandleConstructor();
if (!child) {
JS_ReportError(cx, "Failed to construct child");
return JS_FALSE;
}
JSObject* obj = static_cast<Handle*>(child)->ToJSObject(cx);
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
return JS_TRUE;
}
static void
Finalize(JSContext* cx, JSObject* obj) {
Handle* self = Unwrap(cx, obj);
// Avoid warnings about unused return values:
self && Send__delete__(self);
}
};
template <class BaseType>
const JSClass
Handle<BaseType>::sHandle_JSClass = {
"IPDL Handle", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, Handle::Finalize,
JSCLASS_NO_OPTIONAL_MEMBERS
};
#define HANDLE_PROP_FLAGS (JSPROP_READONLY | JSPROP_PERMANENT)
template <class BaseType>
const JSPropertySpec
Handle<BaseType>::sHandle_Properties[] = {
{ "parent", 0, HANDLE_PROP_FLAGS, GetParent, NULL },
{ "isValid", 0, HANDLE_PROP_FLAGS, GetIsValid, NULL },
{ 0, 0, 0, NULL, NULL }
};
#undef HANDLE_PROP_FLAGS
#define HANDLE_FUN_FLAGS (JSFUN_FAST_NATIVE | \
JSPROP_READONLY | \
JSPROP_PERMANENT)
template <class BaseType>
const JSFunctionSpec
Handle<BaseType>::sHandle_Functions[] = {
JS_FN("invalidate", Invalidate, 0, HANDLE_FUN_FLAGS),
JS_FN("createHandle", CreateHandle, 0, HANDLE_FUN_FLAGS),
JS_FS_END
};
#undef HANDLE_FUN_FLAGS
// The payoff for using templates is that these two implementations are
// guaranteed to be perfectly symmetric:
typedef Handle<PHandleParent> HandleParent;
typedef Handle<PHandleChild> HandleChild;
} // namespace jetpack
} // namespace mozilla
#endif

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

@ -0,0 +1,560 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Firefox.
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation <http://www.mozilla.org>.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Newman <mozilla@benjamn.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "base/basictypes.h"
#include "jscntxt.h"
#include "mozilla/jetpack/JetpackActorCommon.h"
#include "mozilla/jetpack/PJetpack.h"
#include "mozilla/jetpack/PHandleParent.h"
#include "mozilla/jetpack/PHandleChild.h"
#include "mozilla/jetpack/Handle.h"
#include "jsapi.h"
#include "jstl.h"
#include "jshashtable.h"
using mozilla::jetpack::JetpackActorCommon;
using mozilla::jetpack::PHandleParent;
using mozilla::jetpack::HandleParent;
using mozilla::jetpack::PHandleChild;
using mozilla::jetpack::HandleChild;
using mozilla::jetpack::KeyValue;
using mozilla::jetpack::PrimVariant;
using mozilla::jetpack::CompVariant;
using mozilla::jetpack::Variant;
class JetpackActorCommon::OpaqueSeenType
{
public:
typedef JSObject* KeyType;
typedef size_t IdType;
typedef js::HashMap<
KeyType, IdType,
js::DefaultHasher<KeyType>,
js::SystemAllocPolicy
> MapType;
OpaqueSeenType() {
NS_ASSERTION(map.init(1), "Failed to initialize map");
}
bool ok() { return map.initialized(); }
// Reserving 0 as an invalid ID means starting the valid IDs at 1. We
// could have reserved a dummy first element of rmap, but that would
// have wasted space.
static const IdType kInvalidId = 0;
bool add(KeyType obj, IdType* id) {
MapType::AddPtr ap = map.lookupForAdd(obj);
if (!ap) {
if (!rmap.AppendElement(obj) ||
!map.add(ap, obj, *id = rmap.Length()))
*id = kInvalidId;
return true;
}
*id = ap->value;
return false;
}
KeyType reverseLookup(IdType id) {
return rmap.SafeElementAt(id - 1, NULL);
}
private:
MapType map;
nsAutoTArray<KeyType, 4> rmap;
};
bool
JetpackActorCommon::jsval_to_PrimVariant(JSContext* cx, JSType type, jsval from,
PrimVariant* to)
{
// A false return from this function just means the value couldn't be
// converted to a PrimVariant (i.e., it wasn't a primitive value).
switch (type) {
case JSTYPE_VOID:
*to = void_t();
return true;
case JSTYPE_NULL:
*to = null_t();
return true;
case JSTYPE_FUNCTION:
return false;
case JSTYPE_OBJECT: {
HandleParent* hp = HandleParent::FromJSVal(cx, from);
HandleChild* hc = HandleChild::FromJSVal(cx, from);
NS_ASSERTION(!hc || !hp, "Can't be both a parent and a child");
if (hp) {
*to = hp;
return true;
}
if (hc) {
*to = hc;
return true;
}
return false;
}
case JSTYPE_STRING:
*to = nsDependentString((PRUnichar*)JS_GetStringChars(JSVAL_TO_STRING(from)),
JS_GetStringLength(JSVAL_TO_STRING(from)));
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;
case JSTYPE_XML:
return false;
default:
return false;
}
}
bool
JetpackActorCommon::jsval_to_CompVariant(JSContext* cx, JSType type, jsval from,
CompVariant* to, OpaqueSeenType* seen)
{
if (type != JSTYPE_OBJECT)
return false;
js::LazilyConstructed<OpaqueSeenType> lost;
if (!seen) {
lost.construct();
seen = lost.addr();
if (!seen->ok())
return false;
}
OpaqueSeenType::KeyType obj = JSVAL_TO_OBJECT(from);
OpaqueSeenType::IdType id;
if (!seen->add(obj, &id)) {
if (OpaqueSeenType::kInvalidId == id)
return false;
*to = CompVariant(id);
return true;
}
if (JS_IsArrayObject(cx, obj)) {
nsTArray<Variant> elems;
jsuint len;
if (!JS_GetArrayLength(cx, obj, &len) ||
!elems.SetCapacity(len))
return false;
for (jsuint i = 0; i < len; ++i) {
jsval val;
Variant* vp = elems.AppendElement();
if (!JS_GetElement(cx, obj, i, &val) ||
!jsval_to_Variant(cx, val, vp, seen))
*vp = void_t();
}
*to = elems;
return true;
}
js::AutoIdArray ida(cx, JS_Enumerate(cx, obj));
if (!ida)
return false;
nsTArray<KeyValue> kvs;
for (size_t i = 0; i < ida.length(); ++i) {
jsval val; // reused for both key and value
if (!JS_IdToValue(cx, ida[i], &val))
return false;
JSString* idStr = JS_ValueToString(cx, val);
if (!idStr)
return false;
if (!JS_GetPropertyById(cx, obj, ida[i], &val))
return false;
KeyValue kv;
// Silently drop properties that can't be converted.
if (jsval_to_Variant(cx, val, &kv.value(), seen)) {
kv.key() = nsDependentString((PRUnichar*)JS_GetStringChars(idStr),
JS_GetStringLength(idStr));
// If AppendElement fails, we lose this property, no big deal.
kvs.AppendElement(kv);
}
}
*to = kvs;
return true;
}
bool
JetpackActorCommon::jsval_to_Variant(JSContext* cx, jsval from, Variant* to,
OpaqueSeenType* seen)
{
JSType type = JS_TypeOfValue(cx, from);
if (JSVAL_IS_NULL(from))
type = JSTYPE_NULL;
PrimVariant pv;
if (jsval_to_PrimVariant(cx, type, from, &pv)) {
*to = pv;
return true;
}
CompVariant cv;
if (jsval_to_CompVariant(cx, type, from, &cv, seen)) {
*to = cv;
return true;
}
return false;
}
bool
JetpackActorCommon::jsval_from_PrimVariant(JSContext* cx,
const PrimVariant& from,
jsval* to)
{
switch (from.type()) {
case PrimVariant::Tvoid_t:
*to = JSVAL_VOID;
return true;
case PrimVariant::Tnull_t:
*to = JSVAL_NULL;
return true;
case PrimVariant::Tbool:
*to = from.get_bool() ? JSVAL_TRUE : JSVAL_FALSE;
return true;
case PrimVariant::Tint:
*to = INT_TO_JSVAL(from.get_int());
return true;
case PrimVariant::Tdouble:
return !!JS_NewDoubleValue(cx, from.get_double(), to);
case PrimVariant::TnsString: {
const nsString& str = from.get_nsString();
// TODO Use some sort of sharedstring/stringbuffer abstraction to
// exploit sharing opportunities more generally.
if (!str.Length()) {
*to = JS_GetEmptyStringValue(cx);
return true;
}
JSString* s =
JS_NewUCStringCopyN(cx, str.get(), str.Length());
if (!s)
return false;
*to = STRING_TO_JSVAL(s);
return true;
}
case PrimVariant::TPHandleParent: {
JSObject* hobj =
static_cast<const HandleParent*>(from.get_PHandleParent())->ToJSObject(cx);
if (!hobj)
return false;
*to = OBJECT_TO_JSVAL(hobj);
return true;
}
case PrimVariant::TPHandleChild: {
JSObject* hobj =
static_cast<const HandleChild*>(from.get_PHandleChild())->ToJSObject(cx);
if (!hobj)
return false;
*to = OBJECT_TO_JSVAL(hobj);
return true;
}
default:
return false;
}
}
bool
JetpackActorCommon::jsval_from_CompVariant(JSContext* cx,
const CompVariant& from,
jsval* to,
OpaqueSeenType* seen)
{
js::LazilyConstructed<OpaqueSeenType> lost;
if (!seen) {
lost.construct();
seen = lost.addr();
if (!seen->ok())
return false;
}
JSObject* obj = NULL;
switch (from.type()) {
case CompVariant::TArrayOfKeyValue: {
if (!(obj = JS_NewObject(cx, NULL, NULL, NULL)))
return false;
js::AutoObjectRooter root(cx, obj);
OpaqueSeenType::IdType ignored;
if (!seen->add(obj, &ignored))
return false;
const nsTArray<KeyValue>& kvs = from.get_ArrayOfKeyValue();
for (PRUint32 i = 0; i < kvs.Length(); ++i) {
const KeyValue& kv = kvs.ElementAt(i);
js::AutoValueRooter toSet(cx);
if (!jsval_from_Variant(cx, kv.value(), toSet.addr(), seen) ||
!JS_SetUCProperty(cx, obj,
kv.key().get(),
kv.key().Length(),
toSet.addr()))
return false;
}
break;
}
case CompVariant::TArrayOfVariant: {
const nsTArray<Variant>& vs = from.get_ArrayOfVariant();
nsAutoTArray<jsval, 8> jsvals;
jsval* elems = jsvals.AppendElements(vs.Length());
if (!elems)
return false;
for (PRUint32 i = 0; i < vs.Length(); ++i)
elems[i] = JSVAL_VOID;
js::AutoArrayRooter root(cx, vs.Length(), elems);
OpaqueSeenType::IdType ignored;
if (!seen->add(obj, &ignored))
return false;
for (PRUint32 i = 0; i < vs.Length(); ++i)
if (!jsval_from_Variant(cx, vs.ElementAt(i), elems + i, seen))
return false;
if (!(obj = JS_NewArrayObject(cx, vs.Length(), elems)))
return false;
break;
}
case CompVariant::Tsize_t:
if (!(obj = seen->reverseLookup(from.get_size_t())))
return false;
break;
default:
return false;
}
*to = OBJECT_TO_JSVAL(obj);
return true;
}
bool
JetpackActorCommon::jsval_from_Variant(JSContext* cx, const Variant& from,
jsval* to, OpaqueSeenType* seen)
{
switch (from.type()) {
case Variant::TPrimVariant:
return jsval_from_PrimVariant(cx, from, to);
case Variant::TCompVariant:
return jsval_from_CompVariant(cx, from, to, seen);
default:
return false;
}
}
bool
JetpackActorCommon::RecvMessage(JSContext* cx,
const nsString& messageName,
const nsTArray<Variant>& data,
nsTArray<Variant>* results)
{
if (results)
results->Clear();
RecList* list;
if (!mReceivers.Get(messageName, &list))
return true;
nsAutoTArray<jsval, 4> snapshot;
list->copyTo(snapshot);
if (!snapshot.Length())
return true;
nsAutoTArray<jsval, 4> args;
PRUint32 argc = data.Length() + 1;
jsval* argv = args.AppendElements(argc);
if (!argv)
return false;
for (PRUint32 i = 0; i < argc; ++i)
argv[i] = JSVAL_VOID;
js::AutoArrayRooter argvRooter(cx, argc, argv);
JSString* msgNameStr =
JS_NewUCStringCopyN(cx,
messageName.get(),
messageName.Length());
if (!msgNameStr)
return false;
argv[0] = STRING_TO_JSVAL(msgNameStr);
for (PRUint32 i = 0; i < data.Length(); ++i)
if (!jsval_from_Variant(cx, data.ElementAt(i), argv + i + 1))
return false;
JSObject* implGlobal = JS_GetGlobalObject(cx);
js::AutoValueRooter rval(cx);
const uint32 savedOptions =
JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);
for (PRUint32 i = 0; i < snapshot.Length(); ++i) {
Variant* vp = results ? results->AppendElement() : NULL;
rval.set(JSVAL_VOID);
if (!JS_CallFunctionValue(cx, implGlobal, snapshot[i], argc, argv,
rval.addr())) {
// If a receiver throws, we drop the exception on the floor.
JS_ClearPendingException(cx);
if (vp)
*vp = void_t();
} else if (vp && !jsval_to_Variant(cx, rval.value(), vp))
*vp = void_t();
}
JS_SetOptions(cx, savedOptions);
return true;
}
JetpackActorCommon::RecList::~RecList()
{
while (mHead) {
RecNode* old = mHead;
mHead = mHead->down;
delete old;
}
}
void
JetpackActorCommon::RecList::add(jsval v)
{
RecNode* node = mHead, *tail = NULL;
while (node) {
if (node->value() == v)
return;
node = (tail = node)->down;
}
node = new RecNode(mCx, v);
if (tail)
tail->down = node;
else
mHead = node;
}
void
JetpackActorCommon::RecList::remove(jsval v)
{
while (mHead && mHead->value() == v) {
RecNode* old = mHead;
mHead = mHead->down;
delete old;
}
if (!mHead)
return;
RecNode* prev = mHead, *node = prev->down;
while (node) {
if (node->value() == v) {
prev->down = node->down;
delete node;
}
node = (prev = node)->down;
}
}
void
JetpackActorCommon::RecList::copyTo(nsTArray<jsval>& dst) const
{
dst.Clear();
for (RecNode* node = mHead; node; node = node->down)
dst.AppendElement(node->value());
}
nsresult
JetpackActorCommon::RegisterReceiver(JSContext* cx,
const nsString& messageName,
jsval receiver)
{
if (!JSVAL_IS_OBJECT(receiver) ||
!JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(receiver)))
return NS_ERROR_INVALID_ARG;
RecList* list;
if (!mReceivers.Get(messageName, &list)) {
list = new RecList(cx);
if (!list || !mReceivers.Put(messageName, list)) {
delete list;
return NS_ERROR_OUT_OF_MEMORY;
}
}
list->add(receiver);
return NS_OK;
}
void
JetpackActorCommon::UnregisterReceiver(const nsString& messageName,
jsval receiver)
{
RecList* list;
if (!mReceivers.Get(messageName, &list))
return;
list->remove(receiver);
}

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

@ -0,0 +1,143 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Firefox.
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation <http://www.mozilla.org>.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Newman <mozilla@benjamn.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla_jetpack_JetpackActorCommon_h
#define mozilla_jetpack_JetpackActorCommon_h
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include "nsString.h"
#include "nsAutoJSValHolder.h"
struct JSContext;
namespace mozilla {
namespace jetpack {
struct KeyValue;
class PrimVariant;
class CompVariant;
class Variant;
class JetpackActorCommon
{
public:
bool
RecvMessage(JSContext* cx,
const nsString& messageName,
const nsTArray<Variant>& data,
nsTArray<Variant>* results);
nsresult
RegisterReceiver(JSContext* cx,
const nsString& messageName,
jsval receiver);
void
UnregisterReceiver(const nsString& messageName,
jsval receiver);
void
UnregisterReceivers(const nsString& messageName) {
mReceivers.Remove(messageName);
}
void ClearReceivers() {
mReceivers.Clear();
}
class OpaqueSeenType;
static bool jsval_to_Variant(JSContext* cx, jsval from, Variant* to,
OpaqueSeenType* seen = NULL);
static bool jsval_from_Variant(JSContext* cx, const Variant& from, jsval* to,
OpaqueSeenType* seen = NULL);
protected:
JetpackActorCommon() {
mReceivers.Init();
NS_ASSERTION(mReceivers.IsInitialized(),
"Failed to initialize message receiver hash set");
}
private:
static bool jsval_to_PrimVariant(JSContext* cx, JSType type, jsval from,
PrimVariant* to);
static bool jsval_to_CompVariant(JSContext* cx, JSType type, jsval from,
CompVariant* to, OpaqueSeenType* seen);
static bool jsval_from_PrimVariant(JSContext* cx, const PrimVariant& from,
jsval* to);
static bool jsval_from_CompVariant(JSContext* cx, const CompVariant& from,
jsval* to, OpaqueSeenType* seen);
// Don't want to be memcpy'ing nsAutoJSValHolders around, so we need a
// linked list of receivers.
class RecList
{
JSContext* mCx;
class RecNode
{
nsAutoJSValHolder mHolder;
public:
RecNode* down;
RecNode(JSContext* cx, jsval v) : down(NULL) {
mHolder.Hold(cx);
mHolder = v;
}
jsval value() { return mHolder; }
}* mHead;
public:
RecList(JSContext* cx) : mCx(cx), mHead(NULL) {}
~RecList();
void add(jsval v);
void remove(jsval v);
void copyTo(nsTArray<jsval>& dst) const;
};
nsClassHashtable<nsStringHashKey, RecList> mReceivers;
};
} // namespace jetpack
} // namespace mozilla
#endif

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

@ -35,25 +35,63 @@
*
* ***** END LICENSE BLOCK ***** */
#include "mozilla/jetpack/JetpackChild.h"
#include "base/basictypes.h"
#include "jscntxt.h"
#include <stdio.h>
#include "mozilla/jetpack/JetpackChild.h"
#include "mozilla/jetpack/Handle.h"
#include "jsarray.h"
namespace mozilla {
namespace jetpack {
JetpackChild::JetpackChild()
{
NS_ASSERTION(!gInstance, "Something terribly wrong here!");
gInstance = this;
}
JetpackChild::~JetpackChild()
{
NS_ASSERTION(gInstance == this, "Something terribly wrong here!");
gInstance = nsnull;
}
#define IMPL_PROP_FLAGS (JSPROP_SHARED | \
JSPROP_ENUMERATE | \
JSPROP_READONLY | \
JSPROP_PERMANENT)
const JSPropertySpec
JetpackChild::sImplProperties[] = {
{ "jetpack", 0, IMPL_PROP_FLAGS, UserJetpackGetter, NULL },
{ 0, 0, 0, NULL, NULL }
};
#undef IMPL_PROP_FLAGS
#define IMPL_METHOD_FLAGS (JSFUN_FAST_NATIVE | \
JSPROP_ENUMERATE | \
JSPROP_READONLY | \
JSPROP_PERMANENT)
const JSFunctionSpec
JetpackChild::sImplMethods[] = {
JS_FN("sendMessage", SendMessage, 3, IMPL_METHOD_FLAGS),
JS_FN("callMessage", CallMessage, 2, IMPL_METHOD_FLAGS),
JS_FN("registerReceiver", RegisterReceiver, 2, IMPL_METHOD_FLAGS),
JS_FN("unregisterReceiver", UnregisterReceiver, 2, IMPL_METHOD_FLAGS),
JS_FN("unregisterReceivers", UnregisterReceivers, 1, IMPL_METHOD_FLAGS),
JS_FN("wrap", Wrap, 1, IMPL_METHOD_FLAGS),
JS_FN("createHandle", CreateHandle, 0, IMPL_METHOD_FLAGS),
JS_FS_END
};
#undef IMPL_METHOD_FLAGS
const JSClass
JetpackChild::sGlobalClass = {
"JetpackChild::sGlobalClass", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
bool
JetpackChild::Init(base::ProcessHandle aParentProcessHandle,
MessageLoop* aIOLoop,
@ -62,22 +100,325 @@ JetpackChild::Init(base::ProcessHandle aParentProcessHandle,
if (!Open(aChannel, aParentProcessHandle, aIOLoop))
return false;
if (!(mRuntime = JS_NewRuntime(32L * 1024L * 1024L)) ||
!(mImplCx = JS_NewContext(mRuntime, 8192)) ||
!(mUserCx = JS_NewContext(mRuntime, 8192)))
return false;
{
JSAutoRequest request(mImplCx);
JS_SetContextPrivate(mImplCx, this);
JSObject* implGlobal =
JS_NewGlobalObject(mImplCx, const_cast<JSClass*>(&sGlobalClass));
if (!implGlobal ||
!JS_InitStandardClasses(mImplCx, implGlobal) ||
!JS_DefineProperties(mImplCx, implGlobal,
const_cast<JSPropertySpec*>(sImplProperties)) ||
!JS_DefineFunctions(mImplCx, implGlobal,
const_cast<JSFunctionSpec*>(sImplMethods)))
return false;
}
{
JSAutoRequest request(mUserCx);
JS_SetContextPrivate(mUserCx, this);
JSObject* userGlobal =
JS_NewGlobalObject(mUserCx, const_cast<JSClass*>(&sGlobalClass));
if (!userGlobal ||
!JS_InitStandardClasses(mUserCx, userGlobal))
return false;
}
return true;
}
void
JetpackChild::CleanUp()
{
JS_DestroyContext(mUserCx);
JS_DestroyContext(mImplCx);
JS_DestroyRuntime(mRuntime);
JS_ShutDown();
}
bool
JetpackChild::RecvLoadImplementation(const nsCString& script)
JetpackChild::RecvSendMessage(const nsString& messageName,
const nsTArray<Variant>& data)
{
printf("Received LoadImplementation message: '%s'\n", script.get());
JSAutoRequest request(mImplCx);
return JetpackActorCommon::RecvMessage(mImplCx, messageName, data, NULL);
}
static bool
Evaluate(JSContext* cx, const nsCString& code)
{
JSAutoRequest request(cx);
js::AutoValueRooter ignored(cx);
JS_EvaluateScript(cx, JS_GetGlobalObject(cx), code.get(),
code.Length(), "", 1, ignored.addr());
return true;
}
JetpackChild* JetpackChild::gInstance;
bool
JetpackChild::RecvLoadImplementation(const nsCString& code)
{
return Evaluate(mImplCx, code);
}
bool
JetpackChild::RecvLoadUserScript(const nsCString& code)
{
return Evaluate(mUserCx, code);
}
PHandleChild*
JetpackChild::AllocPHandle()
{
return new HandleChild();
}
bool
JetpackChild::DeallocPHandle(PHandleChild* actor)
{
delete actor;
return true;
}
JetpackChild*
JetpackChild::GetThis(JSContext* cx)
{
JetpackChild* self =
static_cast<JetpackChild*>(JS_GetContextPrivate(cx));
JS_ASSERT(cx == self->mImplCx ||
cx == self->mUserCx);
return self;
}
JSBool
JetpackChild::UserJetpackGetter(JSContext* cx, JSObject* obj, jsval id,
jsval* vp)
{
JSObject* userGlobal = JS_GetGlobalObject(GetThis(cx)->mUserCx);
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(userGlobal));
return JS_TRUE;
}
struct MessageResult {
nsString msgName;
nsTArray<Variant> data;
};
static JSBool
MessageCommon(JSContext* cx, uintN argc, jsval* vp,
MessageResult* result)
{
if (argc < 1) {
JS_ReportError(cx, "Message requires a name, at least");
return JS_FALSE;
}
jsval* argv = JS_ARGV(cx, vp);
JSString* msgNameStr = JS_ValueToString(cx, argv[0]);
if (!msgNameStr) {
JS_ReportError(cx, "Could not convert value to string");
return JS_FALSE;
}
result->msgName.Assign((PRUnichar*)JS_GetStringChars(msgNameStr),
JS_GetStringLength(msgNameStr));
result->data.Clear();
if (!result->data.SetCapacity(argc)) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
for (uintN i = 1; i < argc; ++i) {
Variant* vp = result->data.AppendElement();
if (!JetpackActorCommon::jsval_to_Variant(cx, argv[i], vp)) {
JS_ReportError(cx, "Invalid message argument at position %d", i);
return JS_FALSE;
}
}
return JS_TRUE;
}
JSBool
JetpackChild::SendMessage(JSContext* cx, uintN argc, jsval* vp)
{
MessageResult smr;
if (!MessageCommon(cx, argc, vp, &smr))
return JS_FALSE;
if (!GetThis(cx)->SendSendMessage(smr.msgName, smr.data)) {
JS_ReportError(cx, "Failed to sendMessage");
return JS_FALSE;
}
return JS_TRUE;
}
JSBool
JetpackChild::CallMessage(JSContext* cx, uintN argc, jsval* vp)
{
MessageResult smr;
if (!MessageCommon(cx, argc, vp, &smr))
return JS_FALSE;
nsTArray<Variant> results;
if (!GetThis(cx)->SendCallMessage(smr.msgName, smr.data, &results)) {
JS_ReportError(cx, "Failed to callMessage");
return JS_FALSE;
}
nsAutoTArray<jsval, 4> jsvals;
jsval* rvals = jsvals.AppendElements(results.Length());
if (!rvals) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
for (PRUint32 i = 0; i < results.Length(); ++i)
rvals[i] = JSVAL_VOID;
js::AutoArrayRooter root(cx, results.Length(), rvals);
for (PRUint32 i = 0; i < results.Length(); ++i)
if (!jsval_from_Variant(cx, results.ElementAt(i), rvals + i)) {
JS_ReportError(cx, "Invalid result from handler %d", i);
return JS_FALSE;
}
JSObject* arrObj = JS_NewArrayObject(cx, results.Length(), rvals);
if (!arrObj) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(arrObj));
return JS_TRUE;
}
struct ReceiverResult
{
nsString msgName;
jsval receiver;
};
static JSBool
ReceiverCommon(JSContext* cx, uintN argc, jsval* vp,
const char* methodName, uintN arity,
ReceiverResult* result)
{
if (argc != arity) {
JS_ReportError(cx, "%s requires exactly %d arguments", methodName, arity);
return JS_FALSE;
}
// Not currently possible, but think of the future.
if (arity < 1)
return JS_TRUE;
jsval* argv = JS_ARGV(cx, vp);
JSString* str = JS_ValueToString(cx, argv[0]);
if (!str) {
JS_ReportError(cx, "%s expects a stringifiable value as its first argument",
methodName);
return JS_FALSE;
}
result->msgName.Assign((PRUnichar*)JS_GetStringChars(str),
JS_GetStringLength(str));
if (arity < 2)
return JS_TRUE;
if (!JSVAL_IS_OBJECT(argv[1]) ||
!JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(argv[1])))
{
JS_ReportError(cx, "%s expects a function as its second argument",
methodName);
return JS_FALSE;
}
// GC-safe because argv is rooted.
result->receiver = argv[1];
return JS_TRUE;
}
JSBool
JetpackChild::RegisterReceiver(JSContext* cx, uintN argc, jsval* vp)
{
ReceiverResult rr;
if (!ReceiverCommon(cx, argc, vp, "registerReceiver", 2, &rr))
return JS_FALSE;
JetpackActorCommon* actor = GetThis(cx);
nsresult rv = actor->RegisterReceiver(cx, rr.msgName, rr.receiver);
if (NS_FAILED(rv)) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
return JS_TRUE;
}
JSBool
JetpackChild::UnregisterReceiver(JSContext* cx, uintN argc, jsval* vp)
{
ReceiverResult rr;
if (!ReceiverCommon(cx, argc, vp, "unregisterReceiver", 2, &rr))
return JS_FALSE;
JetpackActorCommon* actor = GetThis(cx);
actor->UnregisterReceiver(rr.msgName, rr.receiver);
return JS_TRUE;
}
JSBool
JetpackChild::UnregisterReceivers(JSContext* cx, uintN argc, jsval* vp)
{
ReceiverResult rr;
if (!ReceiverCommon(cx, argc, vp, "unregisterReceivers", 1, &rr))
return JS_FALSE;
JetpackActorCommon* actor = GetThis(cx);
actor->UnregisterReceivers(rr.msgName);
return JS_TRUE;
}
JSBool
JetpackChild::Wrap(JSContext* cx, uintN argc, jsval* vp)
{
NS_NOTYETIMPLEMENTED("wrap not yet implemented (depends on bug 563010)");
return JS_FALSE;
}
JSBool
JetpackChild::CreateHandle(JSContext* cx, uintN argc, jsval* vp)
{
if (argc > 0) {
JS_ReportError(cx, "createHandle takes zero arguments");
return JS_FALSE;
}
HandleChild* handle;
JSObject* hobj;
PHandleChild* phc = GetThis(cx)->SendPHandleConstructor();
if (!(handle = static_cast<HandleChild*>(phc)) ||
!(hobj = handle->ToJSObject(cx))) {
JS_ReportError(cx, "Failed to construct Handle");
return JS_FALSE;
}
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(hobj));
return JS_TRUE;
}
} // namespace jetpack
} // namespace mozilla

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

@ -39,11 +39,18 @@
#define mozilla_jetpack_JetpackChild_h
#include "mozilla/jetpack/PJetpackChild.h"
#include "mozilla/jetpack/JetpackActorCommon.h"
#include "nsTArray.h"
namespace mozilla {
namespace jetpack {
class JetpackChild : public PJetpackChild
class PHandleChild;
class JetpackChild
: public PJetpackChild
, private JetpackActorCommon
{
public:
JetpackChild();
@ -58,10 +65,34 @@ public:
void CleanUp();
protected:
NS_OVERRIDE virtual bool RecvLoadImplementation(const nsCString& script);
NS_OVERRIDE virtual bool RecvSendMessage(const nsString& messageName,
const nsTArray<Variant>& data);
NS_OVERRIDE virtual bool RecvLoadImplementation(const nsCString& code);
NS_OVERRIDE virtual bool RecvLoadUserScript(const nsCString& code);
NS_OVERRIDE virtual PHandleChild* AllocPHandle();
NS_OVERRIDE virtual bool DeallocPHandle(PHandleChild* actor);
private:
static JetpackChild* gInstance;
JSRuntime* mRuntime;
JSContext *mImplCx, *mUserCx;
static JetpackChild* GetThis(JSContext* cx);
static const JSPropertySpec sImplProperties[];
static JSBool UserJetpackGetter(JSContext* cx, JSObject* obj, jsval idval,
jsval* vp);
static const JSFunctionSpec sImplMethods[];
static JSBool SendMessage(JSContext* cx, uintN argc, jsval *vp);
static JSBool CallMessage(JSContext* cx, uintN argc, jsval *vp);
static JSBool RegisterReceiver(JSContext* cx, uintN argc, jsval *vp);
static JSBool UnregisterReceiver(JSContext* cx, uintN argc, jsval *vp);
static JSBool UnregisterReceivers(JSContext* cx, uintN argc, jsval *vp);
static JSBool Wrap(JSContext* cx, uintN argc, jsval *vp);
static JSBool CreateHandle(JSContext* cx, uintN argc, jsval *vp);
static const JSClass sGlobalClass;
DISALLOW_EVIL_CONSTRUCTORS(JetpackChild);
};

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

@ -36,12 +36,19 @@
* ***** END LICENSE BLOCK ***** */
#include "mozilla/jetpack/JetpackParent.h"
#include "mozilla/jetpack/Handle.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsIVariant.h"
#include "nsIXPConnect.h"
namespace mozilla {
namespace jetpack {
JetpackParent::JetpackParent()
JetpackParent::JetpackParent(JSContext* cx)
: mSubprocess(new JetpackProcessParent())
, mContext(cx)
{
mSubprocess->Launch();
Open(mSubprocess->GetChannel(),
@ -56,15 +63,178 @@ JetpackParent::~JetpackParent()
NS_IMPL_ISUPPORTS1(JetpackParent, nsIJetpack)
NS_IMETHODIMP
JetpackParent::LoadImplementation(const nsAString& aURI)
static nsresult
ReadFromURI(const nsAString& aURI,
nsCString* content)
{
// this is all wrong, load the URI and send the data, but for now...
if (!SendLoadImplementation(NS_ConvertUTF16toUTF8(aURI)))
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri),
NS_ConvertUTF16toUTF8(aURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> channel;
NS_NewChannel(getter_AddRefs(channel), uri);
NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
nsCOMPtr<nsIInputStream> input;
rv = channel->Open(getter_AddRefs(input));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(input, "Channel opened successfully but stream was null?");
char buffer[256];
PRUint32 avail = 0;
input->Available(&avail);
if (avail) {
PRUint32 read = 0;
while (NS_SUCCEEDED(input->Read(buffer, sizeof(buffer), &read)) && read) {
content->Append(buffer, read);
read = 0;
}
}
return NS_OK;
}
NS_IMETHODIMP
JetpackParent::SendMessage(const nsAString& aMessageName)
{
nsresult rv;
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsAXPCNativeCallContext* ncc = NULL;
rv = xpc->GetCurrentNativeCallContext(&ncc);
NS_ENSURE_SUCCESS(rv, rv);
JSContext* cx;
rv = ncc->GetJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 argc;
rv = ncc->GetArgc(&argc);
NS_ENSURE_SUCCESS(rv, rv);
jsval* argv;
rv = ncc->GetArgvPtr(&argv);
NS_ENSURE_SUCCESS(rv, rv);
nsTArray<Variant> data;
NS_ENSURE_TRUE(data.SetCapacity(argc), NS_ERROR_OUT_OF_MEMORY);
JSAutoRequest request(cx);
for (PRUint32 i = 1; i < argc; ++i)
if (!jsval_to_Variant(cx, argv[i], data.AppendElement()))
return NS_ERROR_INVALID_ARG;
if (!SendSendMessage(nsString(aMessageName), data))
return NS_ERROR_FAILURE;
return NS_OK;
}
NS_IMETHODIMP
JetpackParent::RegisterReceiver(const nsAString& aMessageName,
jsval aReceiver)
{
return JetpackActorCommon::RegisterReceiver(mContext,
nsString(aMessageName),
aReceiver);
}
NS_IMETHODIMP
JetpackParent::UnregisterReceiver(const nsAString& aMessageName,
jsval aReceiver)
{
JetpackActorCommon::UnregisterReceiver(nsString(aMessageName),
aReceiver);
return NS_OK;
}
NS_IMETHODIMP
JetpackParent::UnregisterReceivers(const nsAString& aMessageName)
{
JetpackActorCommon::UnregisterReceivers(nsString(aMessageName));
return NS_OK;
}
NS_IMETHODIMP
JetpackParent::LoadImplementation(const nsAString& aURI)
{
nsCString code;
nsresult rv = ReadFromURI(aURI, &code);
NS_ENSURE_SUCCESS(rv, rv);
if (!code.IsEmpty() &&
!SendLoadImplementation(code))
rv = NS_ERROR_FAILURE;
return rv;
}
NS_IMETHODIMP
JetpackParent::LoadUserScript(const nsAString& aURI)
{
nsCString code;
nsresult rv = ReadFromURI(aURI, &code);
NS_ENSURE_SUCCESS(rv, rv);
if (!code.IsEmpty() &&
!SendLoadUserScript(code))
rv = NS_ERROR_FAILURE;
return rv;
}
bool
JetpackParent::RecvSendMessage(const nsString& messageName,
const nsTArray<Variant>& data)
{
JSAutoRequest request(mContext);
return JetpackActorCommon::RecvMessage(mContext, messageName, data, NULL);
}
bool
JetpackParent::RecvCallMessage(const nsString& messageName,
const nsTArray<Variant>& data,
nsTArray<Variant>* results)
{
JSAutoRequest request(mContext);
return JetpackActorCommon::RecvMessage(mContext, messageName, data, results);
}
NS_IMETHODIMP
JetpackParent::CreateHandle(nsIVariant** aResult)
{
HandleParent* handle =
static_cast<HandleParent*>(SendPHandleConstructor());
NS_ENSURE_TRUE(handle, NS_ERROR_OUT_OF_MEMORY);
nsresult rv;
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
NS_ENSURE_SUCCESS(rv, rv);
JSAutoRequest request(mContext);
JSObject* hobj = handle->ToJSObject(mContext);
if (!hobj)
return NS_ERROR_FAILURE;
return xpc->JSToVariant(mContext, OBJECT_TO_JSVAL(hobj), aResult);
}
PHandleParent*
JetpackParent::AllocPHandle()
{
return new HandleParent();
}
bool
JetpackParent::DeallocPHandle(PHandleParent* actor)
{
delete actor;
return true;
}
} // namespace jetpack
} // namespace mozilla

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

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Newman <mozilla@benjamn.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -40,24 +41,43 @@
#include "mozilla/jetpack/PJetpackParent.h"
#include "mozilla/jetpack/JetpackProcessParent.h"
#include "mozilla/jetpack/JetpackActorCommon.h"
#include "nsIJetpack.h"
#include "nsTArray.h"
struct JSContext;
namespace mozilla {
namespace jetpack {
class PHandleParent;
class JetpackParent
: public PJetpackParent
, public nsIJetpack
, private JetpackActorCommon
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIJETPACK
JetpackParent();
JetpackParent(JSContext* cx);
~JetpackParent();
protected:
NS_OVERRIDE virtual bool RecvSendMessage(const nsString& messageName,
const nsTArray<Variant>& data);
NS_OVERRIDE virtual bool RecvCallMessage(const nsString& messageName,
const nsTArray<Variant>& data,
nsTArray<Variant>* results);
NS_OVERRIDE virtual PHandleParent* AllocPHandle();
NS_OVERRIDE virtual bool DeallocPHandle(PHandleParent* actor);
private:
JetpackProcessParent* mSubprocess;
JSContext* mContext;
DISALLOW_EVIL_CONSTRUCTORS(JetpackParent);
};

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

@ -43,6 +43,8 @@
#include "nsIGenericFactory.h"
#include "nsIXPConnect.h"
namespace mozilla {
namespace jetpack {
@ -52,8 +54,21 @@ NS_IMPL_ISUPPORTS1(JetpackService,
NS_IMETHODIMP
JetpackService::CreateJetpack(nsIJetpack** aResult)
{
nsRefPtr<JetpackParent> j = new JetpackParent();
nsresult rv;
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsAXPCNativeCallContext* ncc = NULL;
rv = xpc->GetCurrentNativeCallContext(&ncc);
NS_ENSURE_SUCCESS(rv, rv);
JSContext* cx;
rv = ncc->GetJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<JetpackParent> j = new JetpackParent(cx);
*aResult = j.forget().get();
return NS_OK;
}

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

@ -61,6 +61,8 @@ EXPORTS_mozilla/jetpack = \
JetpackParent.h \
JetpackChild.h \
JetpackService.h \
JetpackActorCommon.h \
Handle.h \
$(NULL)
CPPSRCS = \
@ -69,8 +71,13 @@ CPPSRCS = \
JetpackProcessChild.cpp \
JetpackProcessParent.cpp \
JetpackService.cpp \
JetpackActorCommon.cpp \
$(NULL)
ifdef ENABLE_TESTS
TOOL_DIRS += tests
endif
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk
include $(topsrcdir)/config/rules.mk

56
js/jetpack/PHandle.ipdl Normal file
Просмотреть файл

@ -0,0 +1,56 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Newman <mozilla@benjamn.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
include protocol PJetpack;
namespace mozilla {
namespace jetpack {
async protocol PHandle
{
manager PJetpack or PHandle;
manages PHandle;
both:
PHandle();
__delete__();
};
} // namespace jetpack
} // namespace mozilla

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

@ -35,14 +35,58 @@
*
* ***** END LICENSE BLOCK ***** */
include protocol PHandle;
using mozilla::void_t;
using mozilla::null_t;
namespace mozilla {
namespace jetpack {
async protocol PJetpack
{
child:
async LoadImplementation(nsCString script);
struct KeyValue {
nsString key;
Variant value;
};
}
}
union PrimVariant {
void_t;
null_t;
bool;
int;
double;
nsString;
PHandle;
};
union CompVariant {
KeyValue[];
Variant[];
size_t; // reference
};
union Variant {
PrimVariant;
CompVariant;
};
sync protocol PJetpack
{
manages PHandle;
both:
async SendMessage(nsString messageName,
Variant[] data);
async PHandle();
child:
async LoadImplementation(nsCString code);
async LoadUserScript(nsCString code);
parent:
sync CallMessage(nsString messageName,
Variant[] data)
returns (Variant[] results);
};
} // namespace jetpack
} // namespace mozilla

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

@ -1,3 +1,4 @@
IPDLSRCS = \
PJetpack.ipdl \
PHandle.ipdl \
$(NULL)

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

@ -37,8 +37,24 @@
#include "nsISupports.idl"
[scriptable, uuid(be12cc9f-75d4-4f2d-b062-f549ea13446a)]
interface nsIVariant;
[scriptable, uuid(563afdf8-5d2f-4bb2-9ac0-fb82461d2922)]
interface nsIJetpack : nsISupports
{
void sendMessage(in AString aMessageName
/* [optional] in jsval v1,
[optional] in jsval v2,
... */);
void registerReceiver(in AString aMessageName,
in jsval aReceiver);
void unregisterReceiver(in AString aMessageName,
in jsval aReceiver);
void unregisterReceivers(in AString aMessageName);
void loadImplementation(in AString aURI);
void loadUserScript(in AString aURI);
nsIVariant createHandle();
};

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

@ -0,0 +1,50 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Newman <mozilla@benjamn.com> (original author)
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = test_jetpack
XPCSHELL_TESTS = unit
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/config/rules.mk

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

@ -0,0 +1,99 @@
function run_handle_tests() {
test_sanity();
test_safe_iteration();
test_local_invalidation();
test_long_parent_chain(100);
test_invalid_creation();
}
function test_sanity() {
var parent = createHandle(),
child = parent.createHandle(),
grandchild = child.createHandle();
do_check_neq(child, parent);
do_check_eq(child.parent, parent);
do_check_eq(parent.parent, null);
do_check_eq(grandchild.parent.parent, parent);
do_check_true(child.isValid);
do_check_true(parent.isValid);
parent.invalidate();
}
function test_safe_iteration() {
var handle = createHandle(),
keys = [];
handle.foo = 42;
handle.self = handle;
for (var k in handle)
keys[keys.length] = k;
do_check_eq(keys.sort().join("~"),
"foo~self");
handle.invalidate();
}
function test_local_invalidation() {
var parent = createHandle(),
child = parent.createHandle();
do_check_true(child.invalidate());
do_check_false(child.isValid);
do_check_true(parent.isValid);
child = parent.createHandle();
do_check_true(child.isValid);
do_check_true(parent.invalidate());
do_check_false(parent.invalidate());
do_check_false(child.isValid);
do_check_false(parent.isValid);
parent = createHandle();
child = parent.createHandle();
child = child.createHandle();
var uncle = parent.createHandle(),
sibling = child.parent.createHandle();
do_check_eq(child.parent.parent, parent);
do_check_true(child.parent.isValid);
do_check_true(child.parent.invalidate());
do_check_false(child.isValid);
do_check_true(parent.isValid);
do_check_false(sibling.isValid);
do_check_true(uncle.isValid);
parent.invalidate();
}
function test_long_parent_chain(len) {
const ancestor = createHandle();
for (var handle = ancestor, i = 0; i < len; ++i)
handle = handle.createHandle();
const child = handle;
while (handle != ancestor)
handle = handle.parent;
do_check_true(child.isValid);
do_check_true(ancestor.invalidate());
do_check_false(child.isValid);
}
function test_invalid_creation() {
var parent = createHandle(),
child = parent.createHandle();
parent.invalidate();
do_check_eq(child.parent, null);
var threw = false;
try { child.createHandle(); }
catch (x) { threw = true; }
do_check_true(threw);
}

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

@ -0,0 +1,43 @@
function echo() {
sendMessage.apply(this, arguments);
}
registerReceiver("echo", echo);
registerReceiver("callback",
function(msgName, data, handle) {
sendMessage("sendback",
callMessage("callback", data)[0],
handle);
});
registerReceiver("gimmeHandle",
function(msgName) {
sendMessage("recvHandle", "ok", createHandle());
});
registerReceiver("kthx",
function(msgName, data, child) {
sendMessage("recvHandleAgain", data + data, child.parent);
});
registerReceiver("echo2", echo);
registerReceiver("multireturn begin",
function() {
var results = callMessage("multireturn");
sendMessage.apply(null, ["multireturn check"].concat(results));
});
registerReceiver("testarray",
function(msgName, array) {
sendMessage("testarray", array.reverse());
});
registerReceiver("test primitive types", echo);
registerReceiver("drop methods", echo);
registerReceiver("exception coping", echo);
registerReceiver("duplicate receivers", echo);

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

@ -0,0 +1,186 @@
var jps = jps || Components.classes["@mozilla.org/jetpack/service;1"]
.getService(Components.interfaces.nsIJetpackService),
jetpack = jps.createJetpack();
load("handle_tests.js");
function createHandle() {
return jetpack.createHandle();
}
function run_test() {
run_handle_tests();
jetpack.loadImplementation("file://" + do_get_file("impl.js").path);
var circ1 = {},
circ2 = {},
circ3 = {},
ok = false;
((circ1.next = circ2).next = circ3).next = circ1;
try {
jetpack.sendMessage("ignored", circ3, circ1);
ok = true;
} catch (x) {
do_check_false(x);
}
do_check_true(ok);
var echoHandle = jetpack.createHandle();
echoHandle.payload = { weight: 10 };
jetpack.registerReceiver("echo",
function(msgName, data, handle) {
do_check_eq(arguments.length, 3);
do_check_eq(msgName, "echo");
do_check_eq(data, "echo this");
do_check_true(handle.isValid);
do_check_eq(handle, echoHandle);
do_check_eq(handle.payload.weight, 10);
do_test_finished();
});
jetpack.registerReceiver("callback",
function(msgName, data) {
do_check_eq(msgName, "callback");
return "called back: " + data;
});
var callbackHandle = echoHandle.createHandle();
jetpack.registerReceiver("sendback",
function(msgName, data, handle) {
do_check_eq(msgName, "sendback");
do_check_eq(data, "called back: call me back");
do_check_eq(handle, callbackHandle);
do_test_finished();
});
var obj;
jetpack.registerReceiver("recvHandle",
function(msgName, data, handle) {
handle.mark = obj = {};
jetpack.sendMessage("kthx", data + data, handle.createHandle());
});
jetpack.registerReceiver("recvHandleAgain",
function(msgName, data, handle) {
do_check_eq(data, "okokokok");
do_check_eq(handle.mark, obj);
do_test_finished();
});
var obj1 = {
id: Math.random() + ""
}, obj2 = {
id: Math.random() + "",
obj: obj1
};
jetpack.registerReceiver("echo2",
function(msgName, a, b) {
do_check_neq(obj1, a);
do_check_neq(obj2, b);
do_check_eq(obj1.id, a.id);
do_check_eq(obj2.id, b.id);
do_check_eq(obj1.id, obj2.obj.id);
do_test_finished();
});
jetpack.registerReceiver("multireturn", function() { return obj1 });
jetpack.registerReceiver("multireturn", function() { return circ1 });
jetpack.registerReceiver("multireturn", function() { return obj2 });
jetpack.registerReceiver("multireturn check",
function(msgName, rval1, rval2, rval3) {
do_check_eq(rval1.id, obj1.id);
do_check_eq(rval2.next.next.next, rval2);
do_check_eq(rval3.id, obj2.id);
do_check_eq(rval3.obj.id, obj1.id);
do_test_finished();
});
var testarray = [1, 1, 2, 3, 5, 8, 13];
jetpack.registerReceiver("testarray",
function(msgName, reversed) {
for (var i = 0; i < testarray.length; ++i)
do_check_eq(testarray[i],
reversed[reversed.length - i - 1]);
do_test_finished();
});
var undefined;
jetpack.registerReceiver("test primitive types",
function(msgName,
void_val, null_val,
bool_true, bool_false,
one, two, nine99,
one_quarter,
oyez_str)
{
do_check_true(void_val === undefined);
do_check_true(null_val === null);
do_check_true(bool_true === true);
do_check_true(bool_false === false);
do_check_eq(one, 1);
do_check_eq(two, 2);
do_check_eq(nine99, 999);
do_check_eq(one_quarter, 0.25);
do_check_eq(oyez_str, "oyez");
do_test_finished();
});
var drop = {
nested: {
method: function() { return this.value },
value: 42
}
};
jetpack.registerReceiver("drop methods",
function(msgName, echoed) {
do_check_true(!echoed.nested.method);
do_check_eq(echoed.nested.value, 42);
do_test_finished();
});
var coped = "did not cope";
jetpack.registerReceiver("exception coping",
function(msgName) { throw coped = "did cope" });
jetpack.registerReceiver("exception coping",
function(msgName) {
do_check_eq(coped, "did cope");
do_test_finished();
});
var calls = "";
function countCalls() { calls += "." }
jetpack.registerReceiver("duplicate receivers", countCalls);
jetpack.registerReceiver("duplicate receivers", countCalls);
jetpack.registerReceiver("duplicate receivers",
function() { do_check_eq(calls, ".") });
jetpack.registerReceiver("duplicate receivers", countCalls);
jetpack.registerReceiver("duplicate receivers",
function() {
do_check_eq(calls, ".");
jetpack.unregisterReceivers("duplicate receivers");
});
jetpack.registerReceiver("duplicate receivers",
function() { do_test_finished() });
do_test_pending();
do_test_pending();
do_test_pending();
do_test_pending();
do_test_pending();
do_test_pending();
do_test_pending();
do_test_pending();
do_test_pending();
do_test_pending();
jetpack.sendMessage("echo", "echo this", echoHandle);
jetpack.sendMessage("callback", "call me back", callbackHandle);
jetpack.sendMessage("gimmeHandle");
jetpack.sendMessage("echo2", obj1, obj2);
jetpack.sendMessage("multireturn begin");
jetpack.sendMessage("testarray", testarray);
jetpack.sendMessage("test primitive types",
undefined, null, true, false, 1, 2, 999, 1/4, "oyez");
jetpack.sendMessage("drop methods", drop);
jetpack.sendMessage("exception coping");
jetpack.sendMessage("duplicate receivers");
}