зеркало из https://github.com/mozilla/gecko-dev.git
Implement the multi-process Jetpack API (bug 556846). r=bsmedberg,bent
This commit is contained in:
Родитель
17048d28ab
Коммит
efe16a69c1
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
Загрузка…
Ссылка в новой задаче