import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

43
js/ipc/CPOWTimer.cpp Normal file
View file

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jsfriendapi.h"
#include "nsContentUtils.h"
#include "CPOWTimer.h"
#include "jsapi.h"
CPOWTimer::CPOWTimer(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: cx_(nullptr)
, startInterval_(0)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
if (!js::GetStopwatchIsMonitoringCPOW(cx))
return;
cx_ = cx;
startInterval_ = JS_Now();
}
CPOWTimer::~CPOWTimer()
{
if (!cx_) {
// Monitoring was off when we started the timer.
return;
}
if (!js::GetStopwatchIsMonitoringCPOW(cx_)) {
// Monitoring has been deactivated while we were in the timer.
return;
}
const int64_t endInterval = JS_Now();
if (endInterval <= startInterval_) {
// Do not assume monotonicity.
return;
}
js::AddCPOWPerformanceDelta(cx_, endInterval - startInterval_);
}

44
js/ipc/CPOWTimer.h Normal file
View file

@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CPOWTIMER_H
#define CPOWTIMER_H
#include "prinrval.h"
#include "jsapi.h"
/**
* A stopwatch measuring the duration of a CPOW call.
*
* As the process is consuming neither user time nor system time
* during a CPOW call, we measure such durations using wallclock time.
*
* This stopwatch is active iff JSRuntime::stopwatch.isActive is set.
* Upon destruction, update JSRuntime::stopwatch.data.totalCPOWTime.
*/
class MOZ_RAII CPOWTimer final {
public:
explicit inline CPOWTimer(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
~CPOWTimer();
private:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
/**
* The context in which this timer was created, or `nullptr` if
* CPOW monitoring was off when the timer was created.
*/
JSContext* cx_;
/**
* The instant at which the stopwatch was started. Undefined
* if CPOW monitoring was off when the timer was created.
*/
int64_t startInterval_;
};
#endif

25
js/ipc/CpowHolder.h Normal file
View file

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_jsipc_CpowHolder_h__
#define mozilla_jsipc_CpowHolder_h__
#include "js/TypeDecls.h"
namespace mozilla {
namespace jsipc {
class CpowHolder
{
public:
virtual bool ToObject(JSContext* cx, JS::MutableHandle<JSObject*> objp) = 0;
};
} // namespace jsipc
} // namespace mozilla
#endif // mozilla_jsipc_CpowHolder_h__

View file

@ -0,0 +1,101 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_jsipc_CrossProcessObjectWrappers_h__
#define mozilla_jsipc_CrossProcessObjectWrappers_h__
#include "js/TypeDecls.h"
#include "mozilla/jsipc/CpowHolder.h"
#include "mozilla/jsipc/JavaScriptTypes.h"
#include "nsID.h"
#include "nsString.h"
#include "nsTArray.h"
#ifdef XP_WIN
#undef GetClassName
#undef GetClassInfo
#endif
namespace mozilla {
namespace dom {
class CPOWManagerGetter;
} // namespace dom
namespace jsipc {
class PJavaScriptParent;
class PJavaScriptChild;
class CPOWManager
{
public:
virtual bool Unwrap(JSContext* cx,
const InfallibleTArray<CpowEntry>& aCpows,
JS::MutableHandleObject objp) = 0;
virtual bool Wrap(JSContext* cx,
JS::HandleObject aObj,
InfallibleTArray<CpowEntry>* outCpows) = 0;
};
class CrossProcessCpowHolder : public CpowHolder
{
public:
CrossProcessCpowHolder(dom::CPOWManagerGetter* managerGetter,
const InfallibleTArray<CpowEntry>& cpows);
~CrossProcessCpowHolder();
bool ToObject(JSContext* cx, JS::MutableHandleObject objp);
private:
CPOWManager* js_;
const InfallibleTArray<CpowEntry>& cpows_;
bool unwrapped_;
};
CPOWManager*
CPOWManagerFor(PJavaScriptParent* aParent);
CPOWManager*
CPOWManagerFor(PJavaScriptChild* aChild);
bool
IsCPOW(JSObject* obj);
bool
IsWrappedCPOW(JSObject* obj);
nsresult
InstanceOf(JSObject* obj, const nsID* id, bool* bp);
bool
DOMInstanceOf(JSContext* cx, JSObject* obj, int prototypeID, int depth, bool* bp);
void
GetWrappedCPOWTag(JSObject* obj, nsACString& out);
PJavaScriptParent*
NewJavaScriptParent();
void
ReleaseJavaScriptParent(PJavaScriptParent* parent);
PJavaScriptChild*
NewJavaScriptChild();
void
ReleaseJavaScriptChild(PJavaScriptChild* child);
void
AfterProcessTask();
} // namespace jsipc
} // namespace mozilla
#endif // mozilla_jsipc_CrossProcessObjectWrappers_h__

230
js/ipc/JavaScriptBase.h Normal file
View file

@ -0,0 +1,230 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_jsipc_JavaScriptBase_h__
#define mozilla_jsipc_JavaScriptBase_h__
#include "WrapperAnswer.h"
#include "WrapperOwner.h"
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/jsipc/PJavaScript.h"
namespace mozilla {
namespace jsipc {
template<class Base>
class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
{
typedef WrapperAnswer Answer;
public:
virtual ~JavaScriptBase() {}
virtual void ActorDestroy(WrapperOwner::ActorDestroyReason why) {
WrapperOwner::ActorDestroy(why);
}
/*** IPC handlers ***/
bool RecvPreventExtensions(const uint64_t& objId, ReturnStatus* rs) {
return Answer::RecvPreventExtensions(ObjectId::deserialize(objId), rs);
}
bool RecvGetPropertyDescriptor(const uint64_t& objId, const JSIDVariant& id,
ReturnStatus* rs,
PPropertyDescriptor* out) {
return Answer::RecvGetPropertyDescriptor(ObjectId::deserialize(objId), id, rs, out);
}
bool RecvGetOwnPropertyDescriptor(const uint64_t& objId,
const JSIDVariant& id,
ReturnStatus* rs,
PPropertyDescriptor* out) {
return Answer::RecvGetOwnPropertyDescriptor(ObjectId::deserialize(objId), id, rs, out);
}
bool RecvDefineProperty(const uint64_t& objId, const JSIDVariant& id,
const PPropertyDescriptor& flags, ReturnStatus* rs) {
return Answer::RecvDefineProperty(ObjectId::deserialize(objId), id, flags, rs);
}
bool RecvDelete(const uint64_t& objId, const JSIDVariant& id,
ReturnStatus* rs) {
return Answer::RecvDelete(ObjectId::deserialize(objId), id, rs);
}
bool RecvHas(const uint64_t& objId, const JSIDVariant& id,
ReturnStatus* rs, bool* bp) {
return Answer::RecvHas(ObjectId::deserialize(objId), id, rs, bp);
}
bool RecvHasOwn(const uint64_t& objId, const JSIDVariant& id,
ReturnStatus* rs, bool* bp) {
return Answer::RecvHasOwn(ObjectId::deserialize(objId), id, rs, bp);
}
bool RecvGet(const uint64_t& objId, const JSVariant& receiverVar, const JSIDVariant& id,
ReturnStatus* rs, JSVariant* result) {
return Answer::RecvGet(ObjectId::deserialize(objId), receiverVar, id, rs, result);
}
bool RecvSet(const uint64_t& objId, const JSIDVariant& id, const JSVariant& value,
const JSVariant& receiverVar, ReturnStatus* rs) {
return Answer::RecvSet(ObjectId::deserialize(objId), id, value, receiverVar, rs);
}
bool RecvIsExtensible(const uint64_t& objId, ReturnStatus* rs,
bool* result) {
return Answer::RecvIsExtensible(ObjectId::deserialize(objId), rs, result);
}
bool RecvCallOrConstruct(const uint64_t& objId, InfallibleTArray<JSParam>&& argv,
const bool& construct, ReturnStatus* rs, JSVariant* result,
nsTArray<JSParam>* outparams) {
return Answer::RecvCallOrConstruct(ObjectId::deserialize(objId), Move(argv), construct, rs, result, outparams);
}
bool RecvHasInstance(const uint64_t& objId, const JSVariant& v, ReturnStatus* rs, bool* bp) {
return Answer::RecvHasInstance(ObjectId::deserialize(objId), v, rs, bp);
}
bool RecvGetBuiltinClass(const uint64_t& objId, ReturnStatus* rs, uint32_t* classValue) {
return Answer::RecvGetBuiltinClass(ObjectId::deserialize(objId), rs, classValue);
}
bool RecvIsArray(const uint64_t& objId, ReturnStatus* rs, uint32_t* answer) {
return Answer::RecvIsArray(ObjectId::deserialize(objId), rs, answer);
}
bool RecvClassName(const uint64_t& objId, nsCString* result) {
return Answer::RecvClassName(ObjectId::deserialize(objId), result);
}
bool RecvGetPrototype(const uint64_t& objId, ReturnStatus* rs, ObjectOrNullVariant* result) {
return Answer::RecvGetPrototype(ObjectId::deserialize(objId), rs, result);
}
bool RecvGetPrototypeIfOrdinary(const uint64_t& objId, ReturnStatus* rs, bool* isOrdinary,
ObjectOrNullVariant* result)
{
return Answer::RecvGetPrototypeIfOrdinary(ObjectId::deserialize(objId), rs, isOrdinary, result);
}
bool RecvRegExpToShared(const uint64_t& objId, ReturnStatus* rs, nsString* source, uint32_t* flags) {
return Answer::RecvRegExpToShared(ObjectId::deserialize(objId), rs, source, flags);
}
bool RecvGetPropertyKeys(const uint64_t& objId, const uint32_t& flags,
ReturnStatus* rs, nsTArray<JSIDVariant>* ids) {
return Answer::RecvGetPropertyKeys(ObjectId::deserialize(objId), flags, rs, ids);
}
bool RecvInstanceOf(const uint64_t& objId, const JSIID& iid,
ReturnStatus* rs, bool* instanceof) {
return Answer::RecvInstanceOf(ObjectId::deserialize(objId), iid, rs, instanceof);
}
bool RecvDOMInstanceOf(const uint64_t& objId, const int& prototypeID, const int& depth,
ReturnStatus* rs, bool* instanceof) {
return Answer::RecvDOMInstanceOf(ObjectId::deserialize(objId), prototypeID, depth, rs, instanceof);
}
bool RecvDropObject(const uint64_t& objId) {
return Answer::RecvDropObject(ObjectId::deserialize(objId));
}
/*** Dummy call handlers ***/
bool SendDropObject(const ObjectId& objId) {
return Base::SendDropObject(objId.serialize());
}
bool SendPreventExtensions(const ObjectId& objId, ReturnStatus* rs) {
return Base::SendPreventExtensions(objId.serialize(), rs);
}
bool SendGetPropertyDescriptor(const ObjectId& objId, const JSIDVariant& id,
ReturnStatus* rs,
PPropertyDescriptor* out) {
return Base::SendGetPropertyDescriptor(objId.serialize(), id, rs, out);
}
bool SendGetOwnPropertyDescriptor(const ObjectId& objId,
const JSIDVariant& id,
ReturnStatus* rs,
PPropertyDescriptor* out) {
return Base::SendGetOwnPropertyDescriptor(objId.serialize(), id, rs, out);
}
bool SendDefineProperty(const ObjectId& objId, const JSIDVariant& id,
const PPropertyDescriptor& flags,
ReturnStatus* rs) {
return Base::SendDefineProperty(objId.serialize(), id, flags, rs);
}
bool SendDelete(const ObjectId& objId, const JSIDVariant& id, ReturnStatus* rs) {
return Base::SendDelete(objId.serialize(), id, rs);
}
bool SendHas(const ObjectId& objId, const JSIDVariant& id,
ReturnStatus* rs, bool* bp) {
return Base::SendHas(objId.serialize(), id, rs, bp);
}
bool SendHasOwn(const ObjectId& objId, const JSIDVariant& id,
ReturnStatus* rs, bool* bp) {
return Base::SendHasOwn(objId.serialize(), id, rs, bp);
}
bool SendGet(const ObjectId& objId, const JSVariant& receiverVar, const JSIDVariant& id,
ReturnStatus* rs, JSVariant* result) {
return Base::SendGet(objId.serialize(), receiverVar, id, rs, result);
}
bool SendSet(const ObjectId& objId, const JSIDVariant& id, const JSVariant& value,
const JSVariant& receiverVar, ReturnStatus* rs) {
return Base::SendSet(objId.serialize(), id, value, receiverVar, rs);
}
bool SendIsExtensible(const ObjectId& objId, ReturnStatus* rs,
bool* result) {
return Base::SendIsExtensible(objId.serialize(), rs, result);
}
bool SendCallOrConstruct(const ObjectId& objId, const nsTArray<JSParam>& argv,
const bool& construct, ReturnStatus* rs, JSVariant* result,
nsTArray<JSParam>* outparams) {
return Base::SendCallOrConstruct(objId.serialize(), argv, construct, rs, result, outparams);
}
bool SendHasInstance(const ObjectId& objId, const JSVariant& v, ReturnStatus* rs, bool* bp) {
return Base::SendHasInstance(objId.serialize(), v, rs, bp);
}
bool SendGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs, uint32_t* classValue) {
return Base::SendGetBuiltinClass(objId.serialize(), rs, classValue);
}
bool SendIsArray(const ObjectId& objId, ReturnStatus* rs, uint32_t* answer)
{
return Base::SendIsArray(objId.serialize(), rs, answer);
}
bool SendClassName(const ObjectId& objId, nsCString* result) {
return Base::SendClassName(objId.serialize(), result);
}
bool SendGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result) {
return Base::SendGetPrototype(objId.serialize(), rs, result);
}
bool SendGetPrototypeIfOrdinary(const ObjectId& objId, ReturnStatus* rs, bool* isOrdinary,
ObjectOrNullVariant* result)
{
return Base::SendGetPrototypeIfOrdinary(objId.serialize(), rs, isOrdinary, result);
}
bool SendRegExpToShared(const ObjectId& objId, ReturnStatus* rs,
nsString* source, uint32_t* flags) {
return Base::SendRegExpToShared(objId.serialize(), rs, source, flags);
}
bool SendGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
ReturnStatus* rs, nsTArray<JSIDVariant>* ids) {
return Base::SendGetPropertyKeys(objId.serialize(), flags, rs, ids);
}
bool SendInstanceOf(const ObjectId& objId, const JSIID& iid,
ReturnStatus* rs, bool* instanceof) {
return Base::SendInstanceOf(objId.serialize(), iid, rs, instanceof);
}
bool SendDOMInstanceOf(const ObjectId& objId, const int& prototypeID, const int& depth,
ReturnStatus* rs, bool* instanceof) {
return Base::SendDOMInstanceOf(objId.serialize(), prototypeID, depth, rs, instanceof);
}
/* The following code is needed to suppress a bogus MSVC warning (C4250). */
virtual bool toObjectVariant(JSContext* cx, JSObject* obj, ObjectVariant* objVarp) {
return WrapperOwner::toObjectVariant(cx, obj, objVarp);
}
virtual JSObject* fromObjectVariant(JSContext* cx, const ObjectVariant& objVar) {
return WrapperOwner::fromObjectVariant(cx, objVar);
}
};
} // namespace jsipc
} // namespace mozilla
#endif

100
js/ipc/JavaScriptChild.cpp Normal file
View file

@ -0,0 +1,100 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "JavaScriptChild.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/ipc/MessageChannel.h"
#include "nsContentUtils.h"
#include "xpcprivate.h"
#include "jsfriendapi.h"
#include "AccessCheck.h"
using namespace JS;
using namespace mozilla;
using namespace mozilla::jsipc;
using mozilla::AutoSafeJSContext;
static void
UpdateChildWeakPointersBeforeSweepingZoneGroup(JSContext* cx, void* data)
{
static_cast<JavaScriptChild*>(data)->updateWeakPointers();
}
static void
TraceChild(JSTracer* trc, void* data)
{
static_cast<JavaScriptChild*>(data)->trace(trc);
}
JavaScriptChild::~JavaScriptChild()
{
JSContext* cx = dom::danger::GetJSContext();
JS_RemoveWeakPointerZoneGroupCallback(cx, UpdateChildWeakPointersBeforeSweepingZoneGroup);
JS_RemoveExtraGCRootsTracer(cx, TraceChild, this);
}
bool
JavaScriptChild::init()
{
if (!WrapperOwner::init())
return false;
if (!WrapperAnswer::init())
return false;
JSContext* cx = dom::danger::GetJSContext();
JS_AddWeakPointerZoneGroupCallback(cx, UpdateChildWeakPointersBeforeSweepingZoneGroup, this);
JS_AddExtraGCRootsTracer(cx, TraceChild, this);
return true;
}
void
JavaScriptChild::trace(JSTracer* trc)
{
objects_.trace(trc, strongReferenceObjIdMinimum_);
}
void
JavaScriptChild::updateWeakPointers()
{
objects_.sweep();
unwaivedObjectIds_.sweep();
waivedObjectIds_.sweep();
}
JSObject*
JavaScriptChild::scopeForTargetObjects()
{
// CPOWs from the parent need to point into the child's privileged junk
// scope so that they can benefit from XrayWrappers in the child.
return xpc::PrivilegedJunkScope();
}
bool
JavaScriptChild::RecvDropTemporaryStrongReferences(const uint64_t& upToObjId)
{
strongReferenceObjIdMinimum_ = upToObjId + 1;
return true;
}
PJavaScriptChild*
mozilla::jsipc::NewJavaScriptChild()
{
JavaScriptChild* child = new JavaScriptChild();
if (!child->init()) {
delete child;
return nullptr;
}
return child;
}
void
mozilla::jsipc::ReleaseJavaScriptChild(PJavaScriptChild* child)
{
static_cast<JavaScriptChild*>(child)->decref();
}

50
js/ipc/JavaScriptChild.h Normal file
View file

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_jsipc_JavaScriptChild_h_
#define mozilla_jsipc_JavaScriptChild_h_
#include "JavaScriptBase.h"
#include "mozilla/jsipc/PJavaScriptChild.h"
namespace mozilla {
namespace jsipc {
class JavaScriptChild : public JavaScriptBase<PJavaScriptChild>
{
public:
JavaScriptChild() : strongReferenceObjIdMinimum_(0) {}
virtual ~JavaScriptChild();
bool init();
void trace(JSTracer* trc);
void updateWeakPointers();
void drop(JSObject* obj);
bool allowMessage(JSContext* cx) override { return true; }
protected:
virtual bool isParent() override { return false; }
virtual JSObject* scopeForTargetObjects() override;
bool RecvDropTemporaryStrongReferences(const uint64_t& upToObjId) override;
private:
bool fail(JSContext* cx, ReturnStatus* rs);
bool ok(ReturnStatus* rs);
// JavaScriptChild will keep strong references to JS objects that are
// referenced by the parent only if their ID is >=
// strongReferenceObjIdMinimum_.
uint64_t strongReferenceObjIdMinimum_;
};
} // namespace jsipc
} // namespace mozilla
#endif

231
js/ipc/JavaScriptLogging.h Normal file
View file

@ -0,0 +1,231 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_jsipc_JavaScriptLogging__
#define mozilla_jsipc_JavaScriptLogging__
#include "nsString.h"
#include "nsPrintfCString.h"
#include "jsfriendapi.h"
#include "jswrapper.h"
namespace mozilla {
namespace jsipc {
#define LOG(...) \
PR_BEGIN_MACRO \
if (LoggingEnabled()) { \
Logging log(this, cx); \
log.print(__VA_ARGS__); \
} \
PR_END_MACRO
#define LOG_STACK() \
PR_BEGIN_MACRO \
if (StackLoggingEnabled()) { \
js::DumpBacktrace(cx); \
} \
PR_END_MACRO
struct ReceiverObj
{
ObjectId id;
explicit ReceiverObj(ObjectId id) : id(id) {}
};
struct InVariant
{
JSVariant variant;
explicit InVariant(const JSVariant& variant) : variant(variant) {}
};
struct OutVariant
{
JSVariant variant;
explicit OutVariant(const JSVariant& variant) : variant(variant) {}
};
struct Identifier
{
JSIDVariant variant;
explicit Identifier(const JSIDVariant& variant) : variant(variant) {}
};
class Logging
{
public:
Logging(JavaScriptShared* shared, JSContext* cx) : shared(shared), cx(cx) {}
void print(const nsCString& str) {
const char* side = shared->isParent() ? "from child" : "from parent";
printf("CPOW %s: %s\n", side, str.get());
}
void print(const char* str) {
print(nsCString(str));
}
template<typename T1>
void print(const char* fmt, const T1& a1) {
nsAutoCString tmp1;
format(a1, tmp1);
print(nsPrintfCString(fmt, tmp1.get()));
}
template<typename T1, typename T2>
void print(const char* fmt, const T1& a1, const T2& a2) {
nsAutoCString tmp1;
nsAutoCString tmp2;
format(a1, tmp1);
format(a2, tmp2);
print(nsPrintfCString(fmt, tmp1.get(), tmp2.get()));
}
template<typename T1, typename T2, typename T3>
void print(const char* fmt, const T1& a1, const T2& a2, const T3& a3) {
nsAutoCString tmp1;
nsAutoCString tmp2;
nsAutoCString tmp3;
format(a1, tmp1);
format(a2, tmp2);
format(a3, tmp3);
print(nsPrintfCString(fmt, tmp1.get(), tmp2.get(), tmp3.get()));
}
void format(const nsString& str, nsCString& out) {
out = NS_ConvertUTF16toUTF8(str);
}
void formatObject(bool incoming, bool local, ObjectId id, nsCString& out) {
const char* side;
const char* objDesc;
void* ptr;
if (local == incoming) {
JS::RootedObject obj(cx);
obj = shared->objects_.find(id);
if (obj) {
JSAutoCompartment ac(cx, obj);
objDesc = js::ObjectClassName(cx, obj);
} else {
objDesc = "<dead object>";
}
side = shared->isParent() ? "parent" : "child";
ptr = js::UncheckedUnwrap(obj, true);
} else {
objDesc = "<cpow>";
side = shared->isParent() ? "child" : "parent";
ptr = nullptr;
}
out = nsPrintfCString("<%s %s:%d:%p>", side, objDesc, id.serialNumber(), ptr);
}
void format(const ReceiverObj& obj, nsCString& out) {
formatObject(true, true, obj.id, out);
}
void format(const nsTArray<JSParam>& values, nsCString& out) {
nsAutoCString tmp;
out.Truncate();
for (size_t i = 0; i < values.Length(); i++) {
if (i)
out.AppendLiteral(", ");
if (values[i].type() == JSParam::Tvoid_t) {
out.AppendLiteral("<void>");
} else {
format(InVariant(values[i].get_JSVariant()), tmp);
out += tmp;
}
}
}
void format(const InVariant& value, nsCString& out) {
format(true, value.variant, out);
}
void format(const OutVariant& value, nsCString& out) {
format(false, value.variant, out);
}
void format(bool incoming, const JSVariant& value, nsCString& out) {
switch (value.type()) {
case JSVariant::TUndefinedVariant: {
out = "undefined";
break;
}
case JSVariant::TNullVariant: {
out = "null";
break;
}
case JSVariant::TnsString: {
nsAutoCString tmp;
format(value.get_nsString(), tmp);
out = nsPrintfCString("\"%s\"", tmp.get());
break;
}
case JSVariant::TObjectVariant: {
const ObjectVariant& ovar = value.get_ObjectVariant();
if (ovar.type() == ObjectVariant::TLocalObject)
formatObject(incoming, true, ObjectId::deserialize(ovar.get_LocalObject().serializedId()), out);
else
formatObject(incoming, false, ObjectId::deserialize(ovar.get_RemoteObject().serializedId()), out);
break;
}
case JSVariant::TSymbolVariant: {
out = "<Symbol>";
break;
}
case JSVariant::Tdouble: {
out = nsPrintfCString("%.0f", value.get_double());
break;
}
case JSVariant::Tbool: {
out = value.get_bool() ? "true" : "false";
break;
}
case JSVariant::TJSIID: {
out = "<JSIID>";
break;
}
default: {
out = "<JSIID>";
break;
}
}
}
void format(const Identifier& id, nsCString& out) {
switch (id.variant.type()) {
case JSIDVariant::TSymbolVariant: {
out = "<Symbol>";
break;
}
case JSIDVariant::TnsString: {
nsAutoCString tmp;
format(id.variant.get_nsString(), tmp);
out = nsPrintfCString("\"%s\"", tmp.get());
break;
}
case JSIDVariant::Tint32_t: {
out = nsPrintfCString("%d", id.variant.get_int32_t());
break;
}
default: {
out = "Unknown";
break;
}
}
}
private:
JavaScriptShared* shared;
JSContext* cx;
};
} // namespace jsipc
} // namespace mozilla
#endif

212
js/ipc/JavaScriptParent.cpp Normal file
View file

@ -0,0 +1,212 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "JavaScriptParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsJSUtils.h"
#include "jsfriendapi.h"
#include "jswrapper.h"
#include "js/Proxy.h"
#include "js/HeapAPI.h"
#include "xpcprivate.h"
#include "mozilla/Casting.h"
#include "mozilla/Telemetry.h"
#include "nsAutoPtr.h"
using namespace js;
using namespace JS;
using namespace mozilla;
using namespace mozilla::jsipc;
using namespace mozilla::dom;
static void
TraceParent(JSTracer* trc, void* data)
{
static_cast<JavaScriptParent*>(data)->trace(trc);
}
JavaScriptParent::~JavaScriptParent()
{
JS_RemoveExtraGCRootsTracer(danger::GetJSContext(), TraceParent, this);
}
bool
JavaScriptParent::init()
{
if (!WrapperOwner::init())
return false;
JS_AddExtraGCRootsTracer(danger::GetJSContext(), TraceParent, this);
return true;
}
static bool
ForbidUnsafeBrowserCPOWs()
{
static bool result;
static bool cached = false;
if (!cached) {
cached = true;
Preferences::AddBoolVarCache(&result, "dom.ipc.cpows.forbid-unsafe-from-browser", false);
}
return result;
}
// Should we allow CPOWs in aAddonId, even though it's marked as multiprocess
// compatible? This is controlled by two prefs:
// If dom.ipc.cpows.forbid-cpows-in-compat-addons is false, then we allow the CPOW.
// If dom.ipc.cpows.forbid-cpows-in-compat-addons is true:
// We check if aAddonId is listed in dom.ipc.cpows.allow-cpows-in-compat-addons
// (which should be a comma-separated string). If it's present there, we allow
// the CPOW. Otherwise we forbid the CPOW.
static bool
ForbidCPOWsInCompatibleAddon(const nsACString& aAddonId)
{
bool forbid = Preferences::GetBool("dom.ipc.cpows.forbid-cpows-in-compat-addons", false);
if (!forbid) {
return false;
}
nsCString allow;
allow.Assign(',');
allow.Append(Preferences::GetCString("dom.ipc.cpows.allow-cpows-in-compat-addons"));
allow.Append(',');
nsCString searchString(",");
searchString.Append(aAddonId);
searchString.Append(',');
return allow.Find(searchString) == kNotFound;
}
bool
JavaScriptParent::allowMessage(JSContext* cx)
{
// If we're running browser code, then we allow all safe CPOWs and forbid
// unsafe CPOWs based on a pref (which defaults to forbidden). We also allow
// CPOWs unconditionally in selected globals (based on
// Cu.permitCPOWsInScope).
//
// If we're running add-on code, then we check if the add-on is multiprocess
// compatible (which eventually translates to a given setting of allowCPOWs
// on the scopw). If it's not compatible, then we allow the CPOW but
// warn. If it is marked as compatible, then we check the
// ForbidCPOWsInCompatibleAddon; see the comment there.
MessageChannel* channel = GetIPCChannel();
bool isSafe = channel->IsInTransaction();
bool warn = !isSafe;
nsIGlobalObject* global = dom::GetIncumbentGlobal();
JSObject* jsGlobal = global ? global->GetGlobalJSObject() : nullptr;
if (jsGlobal) {
JSAutoCompartment ac(cx, jsGlobal);
JSAddonId* addonId = JS::AddonIdOfObject(jsGlobal);
if (!xpc::CompartmentPrivate::Get(jsGlobal)->allowCPOWs) {
if (!addonId && ForbidUnsafeBrowserCPOWs() && !isSafe) {
Telemetry::Accumulate(Telemetry::BROWSER_SHIM_USAGE_BLOCKED, 1);
JS_ReportErrorASCII(cx, "unsafe CPOW usage forbidden");
return false;
}
if (addonId) {
JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(addonId));
nsString addonIdString;
AssignJSFlatString(addonIdString, flat);
NS_ConvertUTF16toUTF8 addonIdCString(addonIdString);
Telemetry::Accumulate(Telemetry::ADDON_FORBIDDEN_CPOW_USAGE, addonIdCString);
if (ForbidCPOWsInCompatibleAddon(addonIdCString)) {
JS_ReportErrorASCII(cx, "CPOW usage forbidden in this add-on");
return false;
}
warn = true;
}
}
}
if (!warn)
return true;
static bool disableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS");
if (!disableUnsafeCPOWWarnings) {
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
if (console && cx) {
nsAutoString filename;
uint32_t lineno = 0, column = 0;
nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column);
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
error->Init(NS_LITERAL_STRING("unsafe/forbidden CPOW usage"), filename,
EmptyString(), lineno, column,
nsIScriptError::warningFlag, "chrome javascript");
console->LogMessage(error);
} else {
NS_WARNING("Unsafe synchronous IPC message");
}
}
return true;
}
void
JavaScriptParent::trace(JSTracer* trc)
{
objects_.trace(trc);
unwaivedObjectIds_.trace(trc);
waivedObjectIds_.trace(trc);
}
JSObject*
JavaScriptParent::scopeForTargetObjects()
{
// CPWOWs from the child need to point into the parent's unprivileged junk
// scope so that a compromised child cannot compromise the parent. In
// practice, this means that a child process can only (a) hold parent
// objects alive and (b) invoke them if they are callable.
return xpc::UnprivilegedJunkScope();
}
void
JavaScriptParent::afterProcessTask()
{
if (savedNextCPOWNumber_ == nextCPOWNumber_)
return;
savedNextCPOWNumber_ = nextCPOWNumber_;
MOZ_ASSERT(nextCPOWNumber_ > 0);
if (active())
Unused << SendDropTemporaryStrongReferences(nextCPOWNumber_ - 1);
}
PJavaScriptParent*
mozilla::jsipc::NewJavaScriptParent()
{
JavaScriptParent* parent = new JavaScriptParent();
if (!parent->init()) {
delete parent;
return nullptr;
}
return parent;
}
void
mozilla::jsipc::ReleaseJavaScriptParent(PJavaScriptParent* parent)
{
static_cast<JavaScriptParent*>(parent)->decref();
}
void
mozilla::jsipc::AfterProcessTask()
{
for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
if (PJavaScriptParent* p = LoneManagedOrNullAsserts(cp->ManagedPJavaScriptParent()))
static_cast<JavaScriptParent*>(p)->afterProcessTask();
}
}

43
js/ipc/JavaScriptParent.h Normal file
View file

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_jsipc_JavaScriptParent__
#define mozilla_jsipc_JavaScriptParent__
#include "JavaScriptBase.h"
#include "mozilla/jsipc/PJavaScriptParent.h"
namespace mozilla {
namespace jsipc {
class JavaScriptParent : public JavaScriptBase<PJavaScriptParent>
{
public:
JavaScriptParent() : savedNextCPOWNumber_(1) {}
virtual ~JavaScriptParent();
bool init();
void trace(JSTracer* trc);
void drop(JSObject* obj);
bool allowMessage(JSContext* cx) override;
void afterProcessTask();
protected:
virtual bool isParent() override { return true; }
virtual JSObject* scopeForTargetObjects() override;
private:
uint64_t savedNextCPOWNumber_;
};
} // namespace jsipc
} // namespace mozilla
#endif // mozilla_jsipc_JavaScriptWrapper_h__

760
js/ipc/JavaScriptShared.cpp Normal file
View file

@ -0,0 +1,760 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "JavaScriptShared.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/CPOWManagerGetter.h"
#include "mozilla/dom/TabChild.h"
#include "jsfriendapi.h"
#include "xpcprivate.h"
#include "WrapperFactory.h"
#include "mozilla/Preferences.h"
using namespace js;
using namespace JS;
using namespace mozilla;
using namespace mozilla::jsipc;
IdToObjectMap::IdToObjectMap()
: table_(SystemAllocPolicy())
{
}
bool
IdToObjectMap::init()
{
if (table_.initialized())
return true;
return table_.init(32);
}
void
IdToObjectMap::trace(JSTracer* trc, uint64_t minimimId)
{
for (Table::Range r(table_.all()); !r.empty(); r.popFront()) {
if (r.front().key().serialNumber() >= minimimId)
JS::TraceEdge(trc, &r.front().value(), "ipc-object");
}
}
void
IdToObjectMap::sweep()
{
for (Table::Enum e(table_); !e.empty(); e.popFront()) {
JS::Heap<JSObject*>* objp = &e.front().value();
JS_UpdateWeakPointerAfterGC(objp);
if (!*objp)
e.removeFront();
}
}
JSObject*
IdToObjectMap::find(ObjectId id)
{
Table::Ptr p = table_.lookup(id);
if (!p)
return nullptr;
return p->value();
}
bool
IdToObjectMap::add(ObjectId id, JSObject* obj)
{
return table_.put(id, obj);
}
void
IdToObjectMap::remove(ObjectId id)
{
table_.remove(id);
}
void
IdToObjectMap::clear()
{
table_.clear();
}
bool
IdToObjectMap::empty() const
{
return table_.empty();
}
#ifdef DEBUG
bool
IdToObjectMap::has(const ObjectId& id, const JSObject* obj) const
{
auto p = table_.lookup(id);
if (!p)
return false;
return p->value().unbarrieredGet() == obj;
}
#endif
bool
ObjectToIdMap::init()
{
return table_.initialized() || table_.init(32);
}
void
ObjectToIdMap::trace(JSTracer* trc)
{
table_.trace(trc);
}
void
ObjectToIdMap::sweep()
{
table_.sweep();
}
ObjectId
ObjectToIdMap::find(JSObject* obj)
{
Table::Ptr p = table_.lookup(obj);
if (!p)
return ObjectId::nullId();
return p->value();
}
bool
ObjectToIdMap::add(JSContext* cx, JSObject* obj, ObjectId id)
{
return table_.put(obj, id);
}
void
ObjectToIdMap::remove(JSObject* obj)
{
table_.remove(obj);
}
void
ObjectToIdMap::clear()
{
table_.clear();
}
bool JavaScriptShared::sLoggingInitialized;
bool JavaScriptShared::sLoggingEnabled;
bool JavaScriptShared::sStackLoggingEnabled;
JavaScriptShared::JavaScriptShared()
: refcount_(1),
nextSerialNumber_(1),
nextCPOWNumber_(1)
{
if (!sLoggingInitialized) {
sLoggingInitialized = true;
if (PR_GetEnv("MOZ_CPOW_LOG")) {
sLoggingEnabled = true;
sStackLoggingEnabled = strstr(PR_GetEnv("MOZ_CPOW_LOG"), "stacks");
} else {
Preferences::AddBoolVarCache(&sLoggingEnabled,
"dom.ipc.cpows.log.enabled", false);
Preferences::AddBoolVarCache(&sStackLoggingEnabled,
"dom.ipc.cpows.log.stack", false);
}
}
}
JavaScriptShared::~JavaScriptShared()
{
MOZ_RELEASE_ASSERT(cpows_.empty());
}
bool
JavaScriptShared::init()
{
if (!objects_.init())
return false;
if (!cpows_.init())
return false;
if (!unwaivedObjectIds_.init())
return false;
if (!waivedObjectIds_.init())
return false;
return true;
}
void
JavaScriptShared::decref()
{
refcount_--;
if (!refcount_)
delete this;
}
void
JavaScriptShared::incref()
{
refcount_++;
}
bool
JavaScriptShared::convertIdToGeckoString(JSContext* cx, JS::HandleId id, nsString* to)
{
RootedValue idval(cx);
if (!JS_IdToValue(cx, id, &idval))
return false;
RootedString str(cx, ToString(cx, idval));
if (!str)
return false;
return AssignJSString(cx, *to, str);
}
bool
JavaScriptShared::convertGeckoStringToId(JSContext* cx, const nsString& from, JS::MutableHandleId to)
{
RootedString str(cx, JS_NewUCStringCopyN(cx, from.BeginReading(), from.Length()));
if (!str)
return false;
return JS_StringToId(cx, str, to);
}
bool
JavaScriptShared::toVariant(JSContext* cx, JS::HandleValue from, JSVariant* to)
{
switch (JS_TypeOfValue(cx, from)) {
case JSTYPE_VOID:
*to = UndefinedVariant();
return true;
case JSTYPE_OBJECT:
case JSTYPE_FUNCTION:
{
RootedObject obj(cx, from.toObjectOrNull());
if (!obj) {
MOZ_ASSERT(from.isNull());
*to = NullVariant();
return true;
}
if (xpc_JSObjectIsID(cx, obj)) {
JSIID iid;
const nsID* id = xpc_JSObjectToID(cx, obj);
ConvertID(*id, &iid);
*to = iid;
return true;
}
ObjectVariant objVar;
if (!toObjectVariant(cx, obj, &objVar))
return false;
*to = objVar;
return true;
}
case JSTYPE_SYMBOL:
{
RootedSymbol sym(cx, from.toSymbol());
SymbolVariant symVar;
if (!toSymbolVariant(cx, sym, &symVar))
return false;
*to = symVar;
return true;
}
case JSTYPE_STRING:
{
nsAutoJSString autoStr;
if (!autoStr.init(cx, from))
return false;
*to = autoStr;
return true;
}
case JSTYPE_NUMBER:
if (from.isInt32())
*to = double(from.toInt32());
else
*to = from.toDouble();
return true;
case JSTYPE_BOOLEAN:
*to = from.toBoolean();
return true;
default:
MOZ_ASSERT(false);
return false;
}
}
bool
JavaScriptShared::fromVariant(JSContext* cx, const JSVariant& from, MutableHandleValue to)
{
switch (from.type()) {
case JSVariant::TUndefinedVariant:
to.set(UndefinedValue());
return true;
case JSVariant::TNullVariant:
to.set(NullValue());
return true;
case JSVariant::TObjectVariant:
{
JSObject* obj = fromObjectVariant(cx, from.get_ObjectVariant());
if (!obj)
return false;
to.set(ObjectValue(*obj));
return true;
}
case JSVariant::TSymbolVariant:
{
Symbol* sym = fromSymbolVariant(cx, from.get_SymbolVariant());
if (!sym)
return false;
to.setSymbol(sym);
return true;
}
case JSVariant::Tdouble:
to.set(JS_NumberValue(from.get_double()));
return true;
case JSVariant::Tbool:
to.setBoolean(from.get_bool());
return true;
case JSVariant::TnsString:
{
const nsString& old = from.get_nsString();
JSString* str = JS_NewUCStringCopyN(cx, old.BeginReading(), old.Length());
if (!str)
return false;
to.set(StringValue(str));
return true;
}
case JSVariant::TJSIID:
{
nsID iid;
const JSIID& id = from.get_JSIID();
ConvertID(id, &iid);
JSCompartment* compartment = GetContextCompartment(cx);
RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, compartment));
JSObject* obj = xpc_NewIDObject(cx, global, iid);
if (!obj)
return false;
to.set(ObjectValue(*obj));
return true;
}
default:
MOZ_CRASH("NYI");
return false;
}
}
bool
JavaScriptShared::toJSIDVariant(JSContext* cx, HandleId from, JSIDVariant* to)
{
if (JSID_IS_STRING(from)) {
nsAutoJSString autoStr;
if (!autoStr.init(cx, JSID_TO_STRING(from)))
return false;
*to = autoStr;
return true;
}
if (JSID_IS_INT(from)) {
*to = JSID_TO_INT(from);
return true;
}
if (JSID_IS_SYMBOL(from)) {
SymbolVariant symVar;
if (!toSymbolVariant(cx, JSID_TO_SYMBOL(from), &symVar))
return false;
*to = symVar;
return true;
}
MOZ_ASSERT(false);
return false;
}
bool
JavaScriptShared::fromJSIDVariant(JSContext* cx, const JSIDVariant& from, MutableHandleId to)
{
switch (from.type()) {
case JSIDVariant::TSymbolVariant: {
Symbol* sym = fromSymbolVariant(cx, from.get_SymbolVariant());
if (!sym)
return false;
to.set(SYMBOL_TO_JSID(sym));
return true;
}
case JSIDVariant::TnsString:
return convertGeckoStringToId(cx, from.get_nsString(), to);
case JSIDVariant::Tint32_t:
to.set(INT_TO_JSID(from.get_int32_t()));
return true;
default:
return false;
}
}
bool
JavaScriptShared::toSymbolVariant(JSContext* cx, JS::Symbol* symArg, SymbolVariant* symVarp)
{
RootedSymbol sym(cx, symArg);
MOZ_ASSERT(sym);
SymbolCode code = GetSymbolCode(sym);
if (static_cast<uint32_t>(code) < WellKnownSymbolLimit) {
*symVarp = WellKnownSymbol(static_cast<uint32_t>(code));
return true;
}
if (code == SymbolCode::InSymbolRegistry) {
nsAutoJSString autoStr;
if (!autoStr.init(cx, GetSymbolDescription(sym)))
return false;
*symVarp = RegisteredSymbol(autoStr);
return true;
}
JS_ReportErrorASCII(cx, "unique symbol can't be used with CPOW");
return false;
}
JS::Symbol*
JavaScriptShared::fromSymbolVariant(JSContext* cx, const SymbolVariant& symVar)
{
switch (symVar.type()) {
case SymbolVariant::TWellKnownSymbol: {
uint32_t which = symVar.get_WellKnownSymbol().which();
if (which < WellKnownSymbolLimit)
return GetWellKnownSymbol(cx, static_cast<SymbolCode>(which));
MOZ_ASSERT(false, "bad child data");
return nullptr;
}
case SymbolVariant::TRegisteredSymbol: {
nsString key = symVar.get_RegisteredSymbol().key();
RootedString str(cx, JS_NewUCStringCopyN(cx, key.get(), key.Length()));
if (!str)
return nullptr;
return GetSymbolFor(cx, str);
}
default:
return nullptr;
}
}
/* static */ void
JavaScriptShared::ConvertID(const nsID& from, JSIID* to)
{
to->m0() = from.m0;
to->m1() = from.m1;
to->m2() = from.m2;
to->m3_0() = from.m3[0];
to->m3_1() = from.m3[1];
to->m3_2() = from.m3[2];
to->m3_3() = from.m3[3];
to->m3_4() = from.m3[4];
to->m3_5() = from.m3[5];
to->m3_6() = from.m3[6];
to->m3_7() = from.m3[7];
}
/* static */ void
JavaScriptShared::ConvertID(const JSIID& from, nsID* to)
{
to->m0 = from.m0();
to->m1 = from.m1();
to->m2 = from.m2();
to->m3[0] = from.m3_0();
to->m3[1] = from.m3_1();
to->m3[2] = from.m3_2();
to->m3[3] = from.m3_3();
to->m3[4] = from.m3_4();
to->m3[5] = from.m3_5();
to->m3[6] = from.m3_6();
to->m3[7] = from.m3_7();
}
JSObject*
JavaScriptShared::findObjectById(JSContext* cx, const ObjectId& objId)
{
RootedObject obj(cx, objects_.find(objId));
if (!obj) {
JS_ReportErrorASCII(cx, "operation not possible on dead CPOW");
return nullptr;
}
// Each process has a dedicated compartment for CPOW targets. All CPOWs
// from the other process point to objects in this scope. From there, they
// can access objects in other compartments using cross-compartment
// wrappers.
JSAutoCompartment ac(cx, scopeForTargetObjects());
if (objId.hasXrayWaiver()) {
{
JSAutoCompartment ac2(cx, obj);
obj = js::ToWindowProxyIfWindow(obj);
MOZ_ASSERT(obj);
}
if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, &obj))
return nullptr;
} else {
if (!JS_WrapObject(cx, &obj))
return nullptr;
}
return obj;
}
static const uint64_t UnknownPropertyOp = 1;
bool
JavaScriptShared::fromDescriptor(JSContext* cx, Handle<PropertyDescriptor> desc,
PPropertyDescriptor* out)
{
out->attrs() = desc.attributes();
if (!toVariant(cx, desc.value(), &out->value()))
return false;
if (!toObjectOrNullVariant(cx, desc.object(), &out->obj()))
return false;
if (!desc.getter()) {
out->getter() = 0;
} else if (desc.hasGetterObject()) {
JSObject* getter = desc.getterObject();
ObjectVariant objVar;
if (!toObjectVariant(cx, getter, &objVar))
return false;
out->getter() = objVar;
} else {
MOZ_ASSERT(desc.getter() != JS_PropertyStub);
out->getter() = UnknownPropertyOp;
}
if (!desc.setter()) {
out->setter() = 0;
} else if (desc.hasSetterObject()) {
JSObject* setter = desc.setterObject();
ObjectVariant objVar;
if (!toObjectVariant(cx, setter, &objVar))
return false;
out->setter() = objVar;
} else {
MOZ_ASSERT(desc.setter() != JS_StrictPropertyStub);
out->setter() = UnknownPropertyOp;
}
return true;
}
bool
UnknownPropertyStub(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
{
JS_ReportErrorASCII(cx, "getter could not be wrapped via CPOWs");
return false;
}
bool
UnknownStrictPropertyStub(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
ObjectOpResult& result)
{
JS_ReportErrorASCII(cx, "setter could not be wrapped via CPOWs");
return false;
}
bool
JavaScriptShared::toDescriptor(JSContext* cx, const PPropertyDescriptor& in,
MutableHandle<PropertyDescriptor> out)
{
out.setAttributes(in.attrs());
if (!fromVariant(cx, in.value(), out.value()))
return false;
out.object().set(fromObjectOrNullVariant(cx, in.obj()));
if (in.getter().type() == GetterSetter::Tuint64_t && !in.getter().get_uint64_t()) {
out.setGetter(nullptr);
} else if (in.attrs() & JSPROP_GETTER) {
Rooted<JSObject*> getter(cx);
getter = fromObjectVariant(cx, in.getter().get_ObjectVariant());
if (!getter)
return false;
out.setGetter(JS_DATA_TO_FUNC_PTR(JSGetterOp, getter.get()));
} else {
out.setGetter(UnknownPropertyStub);
}
if (in.setter().type() == GetterSetter::Tuint64_t && !in.setter().get_uint64_t()) {
out.setSetter(nullptr);
} else if (in.attrs() & JSPROP_SETTER) {
Rooted<JSObject*> setter(cx);
setter = fromObjectVariant(cx, in.setter().get_ObjectVariant());
if (!setter)
return false;
out.setSetter(JS_DATA_TO_FUNC_PTR(JSSetterOp, setter.get()));
} else {
out.setSetter(UnknownStrictPropertyStub);
}
return true;
}
bool
JavaScriptShared::toObjectOrNullVariant(JSContext* cx, JSObject* obj, ObjectOrNullVariant* objVarp)
{
if (!obj) {
*objVarp = NullVariant();
return true;
}
ObjectVariant objVar;
if (!toObjectVariant(cx, obj, &objVar))
return false;
*objVarp = objVar;
return true;
}
JSObject*
JavaScriptShared::fromObjectOrNullVariant(JSContext* cx, const ObjectOrNullVariant& objVar)
{
if (objVar.type() == ObjectOrNullVariant::TNullVariant)
return nullptr;
return fromObjectVariant(cx, objVar.get_ObjectVariant());
}
CrossProcessCpowHolder::CrossProcessCpowHolder(dom::CPOWManagerGetter* managerGetter,
const InfallibleTArray<CpowEntry>& cpows)
: js_(nullptr),
cpows_(cpows),
unwrapped_(false)
{
// Only instantiate the CPOW manager if we might need it later.
if (cpows.Length())
js_ = managerGetter->GetCPOWManager();
}
CrossProcessCpowHolder::~CrossProcessCpowHolder()
{
if (cpows_.Length() && !unwrapped_) {
// This should only happen if a message manager message
// containing CPOWs gets ignored for some reason. We need to
// unwrap every incoming CPOW in this process to ensure that
// the corresponding part of the CPOW in the other process
// will eventually be collected. The scope for this object
// doesn't really matter, because it immediately becomes
// garbage.
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::PrivilegedJunkScope()))
return;
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> cpows(cx);
js_->Unwrap(cx, cpows_, &cpows);
}
}
bool
CrossProcessCpowHolder::ToObject(JSContext* cx, JS::MutableHandleObject objp)
{
unwrapped_ = true;
if (!cpows_.Length())
return true;
return js_->Unwrap(cx, cpows_, objp);
}
bool
JavaScriptShared::Unwrap(JSContext* cx, const InfallibleTArray<CpowEntry>& aCpows,
JS::MutableHandleObject objp)
{
objp.set(nullptr);
if (!aCpows.Length())
return true;
RootedObject obj(cx, JS_NewPlainObject(cx));
if (!obj)
return false;
RootedValue v(cx);
RootedString str(cx);
for (size_t i = 0; i < aCpows.Length(); i++) {
const nsString& name = aCpows[i].name();
if (!fromVariant(cx, aCpows[i].value(), &v))
return false;
if (!JS_DefineUCProperty(cx,
obj,
name.BeginReading(),
name.Length(),
v,
JSPROP_ENUMERATE))
{
return false;
}
}
objp.set(obj);
return true;
}
bool
JavaScriptShared::Wrap(JSContext* cx, HandleObject aObj, InfallibleTArray<CpowEntry>* outCpows)
{
if (!aObj)
return true;
Rooted<IdVector> ids(cx, IdVector(cx));
if (!JS_Enumerate(cx, aObj, &ids))
return false;
RootedId id(cx);
RootedValue v(cx);
for (size_t i = 0; i < ids.length(); i++) {
id = ids[i];
nsString str;
if (!convertIdToGeckoString(cx, id, &str))
return false;
if (!JS_GetPropertyById(cx, aObj, id, &v))
return false;
JSVariant var;
if (!toVariant(cx, v, &var))
return false;
outCpows->AppendElement(CpowEntry(str, var));
}
return true;
}
CPOWManager*
mozilla::jsipc::CPOWManagerFor(PJavaScriptParent* aParent)
{
return static_cast<JavaScriptParent*>(aParent);
}
CPOWManager*
mozilla::jsipc::CPOWManagerFor(PJavaScriptChild* aChild)
{
return static_cast<JavaScriptChild*>(aChild);
}

236
js/ipc/JavaScriptShared.h Normal file
View file

@ -0,0 +1,236 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_jsipc_JavaScriptShared_h__
#define mozilla_jsipc_JavaScriptShared_h__
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/jsipc/PJavaScript.h"
#include "js/GCHashTable.h"
#include "nsJSUtils.h"
namespace mozilla {
namespace jsipc {
class ObjectId {
public:
// Use 47 bits at most, to be safe, since jsval privates are encoded as
// doubles. See bug 1065811 comment 12 for an explanation.
static const size_t SERIAL_NUMBER_BITS = 47;
static const size_t FLAG_BITS = 1;
static const uint64_t SERIAL_NUMBER_MAX = (uint64_t(1) << SERIAL_NUMBER_BITS) - 1;
explicit ObjectId(uint64_t serialNumber, bool hasXrayWaiver)
: serialNumber_(serialNumber), hasXrayWaiver_(hasXrayWaiver)
{
if (MOZ_UNLIKELY(serialNumber == 0 || serialNumber > SERIAL_NUMBER_MAX))
MOZ_CRASH("Bad CPOW Id");
}
bool operator==(const ObjectId& other) const {
bool equal = serialNumber() == other.serialNumber();
MOZ_ASSERT_IF(equal, hasXrayWaiver() == other.hasXrayWaiver());
return equal;
}
bool isNull() { return !serialNumber_; }
uint64_t serialNumber() const { return serialNumber_; }
bool hasXrayWaiver() const { return hasXrayWaiver_; }
uint64_t serialize() const {
MOZ_ASSERT(serialNumber(), "Don't send a null ObjectId over IPC");
return uint64_t((serialNumber() << FLAG_BITS) | ((hasXrayWaiver() ? 1 : 0) << 0));
}
static ObjectId nullId() { return ObjectId(); }
static ObjectId deserialize(uint64_t data) {
return ObjectId(data >> FLAG_BITS, data & 1);
}
// For use with StructGCPolicy.
void trace(JSTracer*) const {}
bool needsSweep() const { return false; }
private:
ObjectId() : serialNumber_(0), hasXrayWaiver_(false) {}
uint64_t serialNumber_ : SERIAL_NUMBER_BITS;
bool hasXrayWaiver_ : 1;
};
class JavaScriptShared;
// DefaultHasher<T> requires that T coerce to an integral type. We could make
// ObjectId do that, but doing so would weaken our type invariants, so we just
// reimplement it manually.
struct ObjectIdHasher
{
typedef ObjectId Lookup;
static js::HashNumber hash(const Lookup& l) {
return l.serialize();
}
static bool match(const ObjectId& k, const ObjectId& l) {
return k == l;
}
static void rekey(ObjectId& k, const ObjectId& newKey) {
k = newKey;
}
};
// Map ids -> JSObjects
class IdToObjectMap
{
typedef js::HashMap<ObjectId, JS::Heap<JSObject*>, ObjectIdHasher, js::SystemAllocPolicy> Table;
public:
IdToObjectMap();
bool init();
void trace(JSTracer* trc, uint64_t minimumId = 0);
void sweep();
bool add(ObjectId id, JSObject* obj);
JSObject* find(ObjectId id);
void remove(ObjectId id);
void clear();
bool empty() const;
#ifdef DEBUG
bool has(const ObjectId& id, const JSObject* obj) const;
#endif
private:
Table table_;
};
// Map JSObjects -> ids
class ObjectToIdMap
{
using Hasher = js::MovableCellHasher<JS::Heap<JSObject*>>;
using Table = JS::GCHashMap<JS::Heap<JSObject*>, ObjectId, Hasher, js::SystemAllocPolicy>;
public:
bool init();
void trace(JSTracer* trc);
void sweep();
bool add(JSContext* cx, JSObject* obj, ObjectId id);
ObjectId find(JSObject* obj);
void remove(JSObject* obj);
void clear();
private:
Table table_;
};
class Logging;
class JavaScriptShared : public CPOWManager
{
public:
JavaScriptShared();
virtual ~JavaScriptShared();
bool init();
void decref();
void incref();
bool Unwrap(JSContext* cx, const InfallibleTArray<CpowEntry>& aCpows, JS::MutableHandleObject objp);
bool Wrap(JSContext* cx, JS::HandleObject aObj, InfallibleTArray<CpowEntry>* outCpows);
protected:
bool toVariant(JSContext* cx, JS::HandleValue from, JSVariant* to);
bool fromVariant(JSContext* cx, const JSVariant& from, JS::MutableHandleValue to);
bool toJSIDVariant(JSContext* cx, JS::HandleId from, JSIDVariant* to);
bool fromJSIDVariant(JSContext* cx, const JSIDVariant& from, JS::MutableHandleId to);
bool toSymbolVariant(JSContext* cx, JS::Symbol* sym, SymbolVariant* symVarp);
JS::Symbol* fromSymbolVariant(JSContext* cx, const SymbolVariant& symVar);
bool fromDescriptor(JSContext* cx, JS::Handle<JS::PropertyDescriptor> desc,
PPropertyDescriptor* out);
bool toDescriptor(JSContext* cx, const PPropertyDescriptor& in,
JS::MutableHandle<JS::PropertyDescriptor> out);
bool toObjectOrNullVariant(JSContext* cx, JSObject* obj, ObjectOrNullVariant* objVarp);
JSObject* fromObjectOrNullVariant(JSContext* cx, const ObjectOrNullVariant& objVar);
bool convertIdToGeckoString(JSContext* cx, JS::HandleId id, nsString* to);
bool convertGeckoStringToId(JSContext* cx, const nsString& from, JS::MutableHandleId id);
virtual bool toObjectVariant(JSContext* cx, JSObject* obj, ObjectVariant* objVarp) = 0;
virtual JSObject* fromObjectVariant(JSContext* cx, const ObjectVariant& objVar) = 0;
static void ConvertID(const nsID& from, JSIID* to);
static void ConvertID(const JSIID& from, nsID* to);
JSObject* findCPOWById(const ObjectId& objId) {
return cpows_.find(objId);
}
JSObject* findObjectById(JSContext* cx, const ObjectId& objId);
#ifdef DEBUG
bool hasCPOW(const ObjectId& objId, const JSObject* obj) {
return cpows_.has(objId, obj);
}
#endif
static bool LoggingEnabled() { return sLoggingEnabled; }
static bool StackLoggingEnabled() { return sStackLoggingEnabled; }
friend class Logging;
virtual bool isParent() = 0;
virtual JSObject* scopeForTargetObjects() = 0;
protected:
uintptr_t refcount_;
IdToObjectMap objects_;
IdToObjectMap cpows_;
uint64_t nextSerialNumber_;
// nextCPOWNumber_ should be the value of nextSerialNumber_ in the other
// process. The next new CPOW we get should have this serial number.
uint64_t nextCPOWNumber_;
// CPOW references can be weak, and any object we store in a map may be
// GCed (at which point the CPOW will report itself "dead" to the owner).
// This means that we don't want to store any js::Wrappers in the CPOW map,
// because CPOW will die if the wrapper is GCed, even if the underlying
// object is still alive.
//
// This presents a tricky situation for Xray waivers, since they're normally
// represented as a special same-compartment wrapper. We have to strip them
// off before putting them in the id-to-object and object-to-id maps, so we
// need a way of distinguishing them at lookup-time.
//
// For the id-to-object map, we encode waiver-or-not information into the id
// itself, which lets us do the right thing when accessing the object.
//
// For the object-to-id map, we just keep two maps, one for each type.
ObjectToIdMap unwaivedObjectIds_;
ObjectToIdMap waivedObjectIds_;
ObjectToIdMap& objectIdMap(bool waiver) {
return waiver ? waivedObjectIds_ : unwaivedObjectIds_;
}
static bool sLoggingInitialized;
static bool sLoggingEnabled;
static bool sStackLoggingEnabled;
};
} // namespace jsipc
} // namespace mozilla
#endif

View file

@ -0,0 +1,161 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include DOMTypes;
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace jsipc {
struct JSIID
{
uint32_t m0;
uint16_t m1;
uint16_t m2;
uint8_t m3_0;
uint8_t m3_1;
uint8_t m3_2;
uint8_t m3_3;
uint8_t m3_4;
uint8_t m3_5;
uint8_t m3_6;
uint8_t m3_7;
};
struct LocalObject
{
uint64_t serializedId;
};
struct RemoteObject
{
uint64_t serializedId;
bool isCallable;
bool isConstructor;
bool isDOMObject;
nsCString objectTag;
};
union ObjectVariant
{
LocalObject;
RemoteObject;
};
struct WellKnownSymbol
{
uint32_t which;
};
struct RegisteredSymbol
{
nsString key;
};
union SymbolVariant
{
WellKnownSymbol;
RegisteredSymbol;
};
struct UndefinedVariant {};
struct NullVariant {};
union ObjectOrNullVariant
{
ObjectVariant;
NullVariant;
};
union JSVariant
{
UndefinedVariant;
NullVariant;
ObjectVariant;
SymbolVariant;
nsString; /* StringValue(x) */
double; /* NumberValue(x) */
bool; /* BooleanValue(x) */
JSIID; /* XPC nsIID */
};
union JSIDVariant
{
SymbolVariant;
nsString;
int32_t;
};
struct ReturnSuccess
{
};
struct ReturnStopIteration
{
};
struct ReturnDeadCPOW
{
};
struct ReturnException
{
JSVariant exn;
};
struct ReturnObjectOpResult
{
uint32_t code;
};
union ReturnStatus
{
ReturnSuccess;
ReturnStopIteration;
ReturnDeadCPOW;
ReturnException;
ReturnObjectOpResult;
};
union JSParam
{
void_t; /* value is strictly an xpc out param */
JSVariant; /* actual value to pass through */
};
union GetterSetter
{
uint64_t;
ObjectVariant;
};
struct PPropertyDescriptor
{
ObjectOrNullVariant obj;
uint32_t attrs;
JSVariant value;
// How to interpret these values depends on whether JSPROP_GETTER/SETTER
// are set. If set, the corresponding value is a CPOW or 0 for NULL.
// Otherwise, the following table is used:
//
// 0 - NULL
// 1 - Default getter or setter.
// 2 - Unknown
GetterSetter getter;
GetterSetter setter;
};
struct CpowEntry
{
nsString name;
JSVariant value;
};
}
}

60
js/ipc/PJavaScript.ipdl Normal file
View file

@ -0,0 +1,60 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PContent;
include protocol PContentBridge;
include DOMTypes;
include JavaScriptTypes;
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace jsipc {
nested(upto inside_sync) sync protocol PJavaScript
{
manager PContent or PContentBridge;
both:
// Sent when a CPOW has been finalized and table entries can be freed up.
async DropObject(uint64_t objId);
// These roughly map to the ProxyHandler hooks that CPOWs need.
nested(inside_sync) sync PreventExtensions(uint64_t objId) returns (ReturnStatus rs);
nested(inside_sync) sync GetPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
nested(inside_sync) sync GetOwnPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
nested(inside_sync) sync DefineProperty(uint64_t objId, JSIDVariant id, PPropertyDescriptor descriptor) returns (ReturnStatus rs);
nested(inside_sync) sync Delete(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs);
nested(inside_sync) sync Has(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
nested(inside_sync) sync HasOwn(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
nested(inside_sync) sync Get(uint64_t objId, JSVariant receiver, JSIDVariant id) returns (ReturnStatus rs, JSVariant result);
nested(inside_sync) sync Set(uint64_t objId, JSIDVariant id, JSVariant value, JSVariant receiver) returns (ReturnStatus rs);
nested(inside_sync) sync IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
nested(inside_sync) sync CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
nested(inside_sync) sync HasInstance(uint64_t objId, JSVariant v) returns (ReturnStatus rs, bool has);
nested(inside_sync) sync GetBuiltinClass(uint64_t objId) returns (ReturnStatus rs, uint32_t classValue);
nested(inside_sync) sync IsArray(uint64_t objId) returns (ReturnStatus rs, uint32_t ans);
nested(inside_sync) sync ClassName(uint64_t objId) returns (nsCString name);
nested(inside_sync) sync GetPrototype(uint64_t objId) returns (ReturnStatus rs, ObjectOrNullVariant result);
nested(inside_sync) sync GetPrototypeIfOrdinary(uint64_t objId) returns (ReturnStatus rs, bool isOrdinary, ObjectOrNullVariant result);
nested(inside_sync) sync RegExpToShared(uint64_t objId) returns (ReturnStatus rs, nsString source, uint32_t flags);
nested(inside_sync) sync GetPropertyKeys(uint64_t objId, uint32_t flags) returns (ReturnStatus rs, JSIDVariant[] ids);
nested(inside_sync) sync InstanceOf(uint64_t objId, JSIID iid) returns (ReturnStatus rs, bool instanceof);
nested(inside_sync) sync DOMInstanceOf(uint64_t objId, int prototypeID, int depth) returns (ReturnStatus rs, bool instanceof);
parent:
async __delete__();
child:
async DropTemporaryStrongReferences(uint64_t upToObjId);
};
}
}

798
js/ipc/WrapperAnswer.cpp Normal file
View file

@ -0,0 +1,798 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WrapperAnswer.h"
#include "JavaScriptLogging.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ScriptSettings.h"
#include "xpcprivate.h"
#include "js/Class.h"
#include "jsfriendapi.h"
using namespace JS;
using namespace mozilla;
using namespace mozilla::jsipc;
// Note - Using AutoJSAPI (rather than AutoEntryScript) for a trap means
// that we don't expect it to run script. For most of these traps that will only
// happen if the target is a scripted proxy, which is probably something that we
// don't want to support over CPOWs. When enough code is fixed up, the long-term
// plan is to have the JS engine throw if it encounters script when it isn't
// expecting it.
using mozilla::dom::AutoJSAPI;
using mozilla::dom::AutoEntryScript;
static void
MaybeForceDebugGC()
{
static bool sEnvVarInitialized = false;
static bool sDebugGCs = false;
if (!sEnvVarInitialized)
sDebugGCs = !!PR_GetEnv("MOZ_DEBUG_DEAD_CPOWS");
if (sDebugGCs) {
JSContext* cx = nsXPConnect::GetContextInstance()->Context();
PrepareForFullGC(cx);
GCForReason(cx, GC_NORMAL, gcreason::COMPONENT_UTILS);
}
}
bool
WrapperAnswer::fail(AutoJSAPI& jsapi, ReturnStatus* rs)
{
// By default, we set |undefined| unless we can get a more meaningful
// exception.
*rs = ReturnStatus(ReturnException(JSVariant(UndefinedVariant())));
// Note we always return true from this function, since this propagates
// to the IPC code, and we don't want a JS failure to cause the death
// of the child process.
JSContext* cx = jsapi.cx();
RootedValue exn(cx);
if (!jsapi.HasException())
return true;
if (!jsapi.StealException(&exn))
return true;
if (JS_IsStopIteration(exn)) {
*rs = ReturnStatus(ReturnStopIteration());
return true;
}
// If this fails, we still don't want to exit. Just return an invalid
// exception.
(void) toVariant(cx, exn, &rs->get_ReturnException().exn());
return true;
}
bool
WrapperAnswer::ok(ReturnStatus* rs)
{
*rs = ReturnStatus(ReturnSuccess());
return true;
}
bool
WrapperAnswer::ok(ReturnStatus* rs, const JS::ObjectOpResult& result)
{
*rs = result
? ReturnStatus(ReturnSuccess())
: ReturnStatus(ReturnObjectOpResult(result.failureCode()));
return true;
}
bool
WrapperAnswer::deadCPOW(AutoJSAPI& jsapi, ReturnStatus* rs)
{
JSContext* cx = jsapi.cx();
JS_ClearPendingException(cx);
*rs = ReturnStatus(ReturnDeadCPOW());
return true;
}
bool
WrapperAnswer::RecvPreventExtensions(const ObjectId& objId, ReturnStatus* rs)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
ObjectOpResult success;
if (!JS_PreventExtensions(cx, obj, success))
return fail(jsapi, rs);
LOG("%s.preventExtensions()", ReceiverObj(objId));
return ok(rs, success);
}
static void
EmptyDesc(PPropertyDescriptor* desc)
{
desc->obj() = LocalObject(0);
desc->attrs() = 0;
desc->value() = UndefinedVariant();
desc->getter() = 0;
desc->setter() = 0;
}
bool
WrapperAnswer::RecvGetPropertyDescriptor(const ObjectId& objId, const JSIDVariant& idVar,
ReturnStatus* rs, PPropertyDescriptor* out)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
EmptyDesc(out);
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.getPropertyDescriptor(%s)", ReceiverObj(objId), Identifier(idVar));
RootedId id(cx);
if (!fromJSIDVariant(cx, idVar, &id))
return fail(jsapi, rs);
Rooted<PropertyDescriptor> desc(cx);
if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
return fail(jsapi, rs);
if (!fromDescriptor(cx, desc, out))
return fail(jsapi, rs);
return ok(rs);
}
bool
WrapperAnswer::RecvGetOwnPropertyDescriptor(const ObjectId& objId, const JSIDVariant& idVar,
ReturnStatus* rs, PPropertyDescriptor* out)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
EmptyDesc(out);
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.getOwnPropertyDescriptor(%s)", ReceiverObj(objId), Identifier(idVar));
RootedId id(cx);
if (!fromJSIDVariant(cx, idVar, &id))
return fail(jsapi, rs);
Rooted<PropertyDescriptor> desc(cx);
if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc))
return fail(jsapi, rs);
if (!fromDescriptor(cx, desc, out))
return fail(jsapi, rs);
return ok(rs);
}
bool
WrapperAnswer::RecvDefineProperty(const ObjectId& objId, const JSIDVariant& idVar,
const PPropertyDescriptor& descriptor, ReturnStatus* rs)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("define %s[%s]", ReceiverObj(objId), Identifier(idVar));
RootedId id(cx);
if (!fromJSIDVariant(cx, idVar, &id))
return fail(jsapi, rs);
Rooted<PropertyDescriptor> desc(cx);
if (!toDescriptor(cx, descriptor, &desc))
return fail(jsapi, rs);
ObjectOpResult success;
if (!JS_DefinePropertyById(cx, obj, id, desc, success))
return fail(jsapi, rs);
return ok(rs, success);
}
bool
WrapperAnswer::RecvDelete(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("delete %s[%s]", ReceiverObj(objId), Identifier(idVar));
RootedId id(cx);
if (!fromJSIDVariant(cx, idVar, &id))
return fail(jsapi, rs);
ObjectOpResult success;
if (!JS_DeletePropertyById(cx, obj, id, success))
return fail(jsapi, rs);
return ok(rs, success);
}
bool
WrapperAnswer::RecvHas(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs,
bool* foundp)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
*foundp = false;
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.has(%s)", ReceiverObj(objId), Identifier(idVar));
RootedId id(cx);
if (!fromJSIDVariant(cx, idVar, &id))
return fail(jsapi, rs);
if (!JS_HasPropertyById(cx, obj, id, foundp))
return fail(jsapi, rs);
return ok(rs);
}
bool
WrapperAnswer::RecvHasOwn(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs,
bool* foundp)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
*foundp = false;
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.hasOwn(%s)", ReceiverObj(objId), Identifier(idVar));
RootedId id(cx);
if (!fromJSIDVariant(cx, idVar, &id))
return fail(jsapi, rs);
if (!JS_HasOwnPropertyById(cx, obj, id, foundp))
return fail(jsapi, rs);
return ok(rs);
}
bool
WrapperAnswer::RecvGet(const ObjectId& objId, const JSVariant& receiverVar,
const JSIDVariant& idVar, ReturnStatus* rs, JSVariant* result)
{
MaybeForceDebugGC();
// We may run scripted getters.
AutoEntryScript aes(scopeForTargetObjects(),
"Cross-Process Object Wrapper 'get'");
JSContext* cx = aes.cx();
// The outparam will be written to the buffer, so it must be set even if
// the parent won't read it.
*result = UndefinedVariant();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(aes, rs);
RootedValue receiver(cx);
if (!fromVariant(cx, receiverVar, &receiver))
return fail(aes, rs);
RootedId id(cx);
if (!fromJSIDVariant(cx, idVar, &id))
return fail(aes, rs);
JS::RootedValue val(cx);
if (!JS_ForwardGetPropertyTo(cx, obj, id, receiver, &val))
return fail(aes, rs);
if (!toVariant(cx, val, result))
return fail(aes, rs);
LOG("get %s.%s = %s", ReceiverObj(objId), Identifier(idVar), OutVariant(*result));
return ok(rs);
}
bool
WrapperAnswer::RecvSet(const ObjectId& objId, const JSIDVariant& idVar, const JSVariant& value,
const JSVariant& receiverVar, ReturnStatus* rs)
{
MaybeForceDebugGC();
// We may run scripted setters.
AutoEntryScript aes(scopeForTargetObjects(),
"Cross-Process Object Wrapper 'set'");
JSContext* cx = aes.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(aes, rs);
LOG("set %s[%s] = %s", ReceiverObj(objId), Identifier(idVar), InVariant(value));
RootedId id(cx);
if (!fromJSIDVariant(cx, idVar, &id))
return fail(aes, rs);
RootedValue val(cx);
if (!fromVariant(cx, value, &val))
return fail(aes, rs);
RootedValue receiver(cx);
if (!fromVariant(cx, receiverVar, &receiver))
return fail(aes, rs);
ObjectOpResult result;
if (!JS_ForwardSetPropertyTo(cx, obj, id, val, receiver, result))
return fail(aes, rs);
return ok(rs, result);
}
bool
WrapperAnswer::RecvIsExtensible(const ObjectId& objId, ReturnStatus* rs, bool* result)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
*result = false;
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.isExtensible()", ReceiverObj(objId));
bool extensible;
if (!JS_IsExtensible(cx, obj, &extensible))
return fail(jsapi, rs);
*result = !!extensible;
return ok(rs);
}
bool
WrapperAnswer::RecvCallOrConstruct(const ObjectId& objId,
InfallibleTArray<JSParam>&& argv,
const bool& construct,
ReturnStatus* rs,
JSVariant* result,
nsTArray<JSParam>* outparams)
{
MaybeForceDebugGC();
AutoEntryScript aes(scopeForTargetObjects(),
"Cross-Process Object Wrapper call/construct");
JSContext* cx = aes.cx();
// The outparam will be written to the buffer, so it must be set even if
// the parent won't read it.
*result = UndefinedVariant();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(aes, rs);
MOZ_ASSERT(argv.Length() >= 2);
RootedValue objv(cx);
if (!fromVariant(cx, argv[0], &objv))
return fail(aes, rs);
*result = JSVariant(UndefinedVariant());
AutoValueVector vals(cx);
AutoValueVector outobjects(cx);
for (size_t i = 0; i < argv.Length(); i++) {
if (argv[i].type() == JSParam::Tvoid_t) {
// This is an outparam.
RootedObject obj(cx, xpc::NewOutObject(cx));
if (!obj)
return fail(aes, rs);
if (!outobjects.append(ObjectValue(*obj)))
return fail(aes, rs);
if (!vals.append(ObjectValue(*obj)))
return fail(aes, rs);
} else {
RootedValue v(cx);
if (!fromVariant(cx, argv[i].get_JSVariant(), &v))
return fail(aes, rs);
if (!vals.append(v))
return fail(aes, rs);
}
}
RootedValue rval(cx);
{
HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2);
if (construct) {
RootedObject obj(cx);
if (!JS::Construct(cx, vals[0], args, &obj))
return fail(aes, rs);
rval.setObject(*obj);
} else {
if(!JS::Call(cx, vals[1], vals[0], args, &rval))
return fail(aes, rs);
}
}
if (!toVariant(cx, rval, result))
return fail(aes, rs);
// Prefill everything with a dummy jsval.
for (size_t i = 0; i < outobjects.length(); i++)
outparams->AppendElement(JSParam(void_t()));
// Go through each argument that was an outparam, retrieve the "value"
// field, and add it to a temporary list. We need to do this separately
// because the outparams vector is not rooted.
vals.clear();
for (size_t i = 0; i < outobjects.length(); i++) {
RootedObject obj(cx, &outobjects[i].toObject());
RootedValue v(cx);
bool found;
if (JS_HasProperty(cx, obj, "value", &found)) {
if (!JS_GetProperty(cx, obj, "value", &v))
return fail(aes, rs);
} else {
v = UndefinedValue();
}
if (!vals.append(v))
return fail(aes, rs);
}
// Copy the outparams. If any outparam is already set to a void_t, we
// treat this as the outparam never having been set.
for (size_t i = 0; i < vals.length(); i++) {
JSVariant variant;
if (!toVariant(cx, vals[i], &variant))
return fail(aes, rs);
outparams->ReplaceElementAt(i, JSParam(variant));
}
LOG("%s.call(%s) = %s", ReceiverObj(objId), argv, OutVariant(*result));
return ok(rs);
}
bool
WrapperAnswer::RecvHasInstance(const ObjectId& objId, const JSVariant& vVar, ReturnStatus* rs, bool* bp)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.hasInstance(%s)", ReceiverObj(objId), InVariant(vVar));
RootedValue val(cx);
if (!fromVariant(cx, vVar, &val))
return fail(jsapi, rs);
if (!JS_HasInstance(cx, obj, val, bp))
return fail(jsapi, rs);
return ok(rs);
}
bool
WrapperAnswer::RecvGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs,
uint32_t* classValue)
{
MaybeForceDebugGC();
*classValue = uint32_t(js::ESClass::Other);
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.getBuiltinClass()", ReceiverObj(objId));
js::ESClass cls;
if (!js::GetBuiltinClass(cx, obj, &cls))
return fail(jsapi, rs);
*classValue = uint32_t(cls);
return ok(rs);
}
bool
WrapperAnswer::RecvIsArray(const ObjectId& objId, ReturnStatus* rs,
uint32_t* ans)
{
MaybeForceDebugGC();
*ans = uint32_t(IsArrayAnswer::NotArray);
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.isArray()", ReceiverObj(objId));
IsArrayAnswer answer;
if (!JS::IsArray(cx, obj, &answer))
return fail(jsapi, rs);
*ans = uint32_t(answer);
return ok(rs);
}
bool
WrapperAnswer::RecvClassName(const ObjectId& objId, nsCString* name)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj) {
// This is very unfortunate, but we have no choice.
*name = "<dead CPOW>";
return true;
}
LOG("%s.className()", ReceiverObj(objId));
*name = js::ObjectClassName(cx, obj);
return true;
}
bool
WrapperAnswer::RecvGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result)
{
MaybeForceDebugGC();
*result = NullVariant();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
JS::RootedObject proto(cx);
if (!JS_GetPrototype(cx, obj, &proto))
return fail(jsapi, rs);
if (!toObjectOrNullVariant(cx, proto, result))
return fail(jsapi, rs);
LOG("getPrototype(%s)", ReceiverObj(objId));
return ok(rs);
}
bool
WrapperAnswer::RecvGetPrototypeIfOrdinary(const ObjectId& objId, ReturnStatus* rs, bool* isOrdinary,
ObjectOrNullVariant* result)
{
MaybeForceDebugGC();
*result = NullVariant();
*isOrdinary = false;
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
JS::RootedObject proto(cx);
if (!JS_GetPrototypeIfOrdinary(cx, obj, isOrdinary, &proto))
return fail(jsapi, rs);
if (!toObjectOrNullVariant(cx, proto, result))
return fail(jsapi, rs);
LOG("getPrototypeIfOrdinary(%s)", ReceiverObj(objId));
return ok(rs);
}
bool
WrapperAnswer::RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs,
nsString* source, uint32_t* flags)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
RootedString sourceJSStr(cx, JS_GetRegExpSource(cx, obj));
if (!sourceJSStr)
return fail(jsapi, rs);
nsAutoJSString sourceStr;
if (!sourceStr.init(cx, sourceJSStr))
return fail(jsapi, rs);
source->Assign(sourceStr);
*flags = JS_GetRegExpFlags(cx, obj);
return ok(rs);
}
bool
WrapperAnswer::RecvGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
ReturnStatus* rs, nsTArray<JSIDVariant>* ids)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.getPropertyKeys()", ReceiverObj(objId));
AutoIdVector props(cx);
if (!js::GetPropertyKeys(cx, obj, flags, &props))
return fail(jsapi, rs);
for (size_t i = 0; i < props.length(); i++) {
JSIDVariant id;
if (!toJSIDVariant(cx, props[i], &id))
return fail(jsapi, rs);
ids->AppendElement(id);
}
return ok(rs);
}
bool
WrapperAnswer::RecvInstanceOf(const ObjectId& objId, const JSIID& iid, ReturnStatus* rs,
bool* instanceof)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
*instanceof = false;
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.instanceOf()", ReceiverObj(objId));
nsID nsiid;
ConvertID(iid, &nsiid);
nsresult rv = xpc::HasInstance(cx, obj, &nsiid, instanceof);
if (rv != NS_OK)
return fail(jsapi, rs);
return ok(rs);
}
bool
WrapperAnswer::RecvDOMInstanceOf(const ObjectId& objId, const int& prototypeID,
const int& depth, ReturnStatus* rs, bool* instanceof)
{
MaybeForceDebugGC();
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
return false;
JSContext* cx = jsapi.cx();
*instanceof = false;
RootedObject obj(cx, findObjectById(cx, objId));
if (!obj)
return deadCPOW(jsapi, rs);
LOG("%s.domInstanceOf()", ReceiverObj(objId));
bool tmp;
if (!mozilla::dom::InterfaceHasInstance(cx, prototypeID, depth, obj, &tmp))
return fail(jsapi, rs);
*instanceof = tmp;
return ok(rs);
}
bool
WrapperAnswer::RecvDropObject(const ObjectId& objId)
{
JSObject* obj = objects_.find(objId);
if (obj) {
objectIdMap(objId.hasXrayWaiver()).remove(obj);
objects_.remove(objId);
}
return true;
}

80
js/ipc/WrapperAnswer.h Normal file
View file

@ -0,0 +1,80 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_jsipc_WrapperAnswer_h_
#define mozilla_jsipc_WrapperAnswer_h_
#include "JavaScriptShared.h"
namespace mozilla {
namespace dom {
class AutoJSAPI;
} // namespace dom
namespace jsipc {
class WrapperAnswer : public virtual JavaScriptShared
{
public:
bool RecvPreventExtensions(const ObjectId& objId, ReturnStatus* rs);
bool RecvGetPropertyDescriptor(const ObjectId& objId, const JSIDVariant& id,
ReturnStatus* rs,
PPropertyDescriptor* out);
bool RecvGetOwnPropertyDescriptor(const ObjectId& objId,
const JSIDVariant& id,
ReturnStatus* rs,
PPropertyDescriptor* out);
bool RecvDefineProperty(const ObjectId& objId, const JSIDVariant& id,
const PPropertyDescriptor& flags, ReturnStatus* rs);
bool RecvDelete(const ObjectId& objId, const JSIDVariant& id, ReturnStatus* rs);
bool RecvHas(const ObjectId& objId, const JSIDVariant& id,
ReturnStatus* rs, bool* foundp);
bool RecvHasOwn(const ObjectId& objId, const JSIDVariant& id,
ReturnStatus* rs, bool* foundp);
bool RecvGet(const ObjectId& objId, const JSVariant& receiverVar,
const JSIDVariant& id,
ReturnStatus* rs, JSVariant* result);
bool RecvSet(const ObjectId& objId, const JSIDVariant& id, const JSVariant& value,
const JSVariant& receiverVar, ReturnStatus* rs);
bool RecvIsExtensible(const ObjectId& objId, ReturnStatus* rs,
bool* result);
bool RecvCallOrConstruct(const ObjectId& objId, InfallibleTArray<JSParam>&& argv,
const bool& construct, ReturnStatus* rs, JSVariant* result,
nsTArray<JSParam>* outparams);
bool RecvHasInstance(const ObjectId& objId, const JSVariant& v, ReturnStatus* rs, bool* bp);
bool RecvGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs,
uint32_t* classValue);
bool RecvIsArray(const ObjectId& objId, ReturnStatus* rs, uint32_t* ans);
bool RecvClassName(const ObjectId& objId, nsCString* result);
bool RecvGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result);
bool RecvGetPrototypeIfOrdinary(const ObjectId& objId, ReturnStatus* rs, bool* isOrdinary,
ObjectOrNullVariant* result);
bool RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs, nsString* source, uint32_t* flags);
bool RecvGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
ReturnStatus* rs, nsTArray<JSIDVariant>* ids);
bool RecvInstanceOf(const ObjectId& objId, const JSIID& iid,
ReturnStatus* rs, bool* instanceof);
bool RecvDOMInstanceOf(const ObjectId& objId, const int& prototypeID, const int& depth,
ReturnStatus* rs, bool* instanceof);
bool RecvDropObject(const ObjectId& objId);
private:
bool fail(dom::AutoJSAPI& jsapi, ReturnStatus* rs);
bool ok(ReturnStatus* rs);
bool ok(ReturnStatus* rs, const JS::ObjectOpResult& result);
bool deadCPOW(dom::AutoJSAPI& jsapi, ReturnStatus* rs);
};
} // namespace jsipc
} // namespace mozilla
#endif

1236
js/ipc/WrapperOwner.cpp Normal file

File diff suppressed because it is too large Load diff

167
js/ipc/WrapperOwner.h Normal file
View file

@ -0,0 +1,167 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_jsipc_WrapperOwner_h__
#define mozilla_jsipc_WrapperOwner_h__
#include "JavaScriptShared.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "js/Class.h"
#include "js/Proxy.h"
namespace mozilla {
namespace jsipc {
class WrapperOwner : public virtual JavaScriptShared
{
public:
typedef mozilla::ipc::IProtocol::ActorDestroyReason
ActorDestroyReason;
WrapperOwner();
bool init();
// Standard internal methods.
// (The traps should be in the same order like js/Proxy.h)
bool getOwnPropertyDescriptor(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc);
bool defineProperty(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
JS::Handle<JS::PropertyDescriptor> desc,
JS::ObjectOpResult& result);
bool ownPropertyKeys(JSContext* cx, JS::HandleObject proxy, JS::AutoIdVector& props);
bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
JS::ObjectOpResult& result);
bool preventExtensions(JSContext* cx, JS::HandleObject proxy, JS::ObjectOpResult& result);
bool isExtensible(JSContext* cx, JS::HandleObject proxy, bool* extensible);
bool has(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, bool* bp);
bool get(JSContext* cx, JS::HandleObject proxy, JS::HandleValue receiver,
JS::HandleId id, JS::MutableHandleValue vp);
bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
JS::HandleValue receiver, JS::ObjectOpResult& result);
bool callOrConstruct(JSContext* cx, JS::HandleObject proxy, const JS::CallArgs& args,
bool construct);
// SpiderMonkey extensions.
bool getPropertyDescriptor(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
JS::MutableHandle<JS::PropertyDescriptor> desc);
bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, bool* bp);
bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::HandleObject proxy,
JS::AutoIdVector& props);
bool hasInstance(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool* bp);
bool getBuiltinClass(JSContext* cx, JS::HandleObject proxy, js::ESClass* cls);
bool isArray(JSContext* cx, JS::HandleObject proxy, JS::IsArrayAnswer* answer);
const char* className(JSContext* cx, JS::HandleObject proxy);
bool getPrototype(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleObject protop);
bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy, bool* isOrdinary,
JS::MutableHandleObject protop);
bool regexp_toShared(JSContext* cx, JS::HandleObject proxy, js::RegExpGuard* g);
nsresult instanceOf(JSObject* obj, const nsID* id, bool* bp);
bool toString(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args);
bool DOMQI(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args);
/*
* Check that |obj| is a DOM wrapper whose prototype chain contains
* |prototypeID| at depth |depth|.
*/
bool domInstanceOf(JSContext* cx, JSObject* obj, int prototypeID, int depth, bool* bp);
bool active() { return !inactive_; }
virtual bool allowMessage(JSContext* cx) = 0;
void drop(JSObject* obj);
void updatePointer(JSObject* obj, const JSObject* old);
virtual void ActorDestroy(ActorDestroyReason why);
virtual bool toObjectVariant(JSContext* cx, JSObject* obj, ObjectVariant* objVarp);
virtual JSObject* fromObjectVariant(JSContext* cx, const ObjectVariant& objVar);
JSObject* fromRemoteObjectVariant(JSContext* cx, const RemoteObject& objVar);
JSObject* fromLocalObjectVariant(JSContext* cx, const LocalObject& objVar);
protected:
ObjectId idOf(JSObject* obj);
private:
ObjectId idOfUnchecked(JSObject* obj);
bool getPropertyKeys(JSContext* cx, JS::HandleObject proxy, uint32_t flags,
JS::AutoIdVector& props);
// Catastrophic IPC failure.
bool ipcfail(JSContext* cx);
// Check whether a return status is okay, and if not, propagate its error.
//
// If 'status' might be a ReturnObjectOpResult, which is only possible for
// a subset of the operations below, 'result' must be passed.
bool ok(JSContext* cx, const ReturnStatus& status, JS::ObjectOpResult& result);
bool ok(JSContext* cx, const ReturnStatus& status);
bool inactive_;
/*** Dummy call handlers ***/
public:
virtual bool SendDropObject(const ObjectId& objId) = 0;
virtual bool SendPreventExtensions(const ObjectId& objId, ReturnStatus* rs) = 0;
virtual bool SendGetPropertyDescriptor(const ObjectId& objId, const JSIDVariant& id,
ReturnStatus* rs,
PPropertyDescriptor* out) = 0;
virtual bool SendGetOwnPropertyDescriptor(const ObjectId& objId,
const JSIDVariant& id,
ReturnStatus* rs,
PPropertyDescriptor* out) = 0;
virtual bool SendDefineProperty(const ObjectId& objId, const JSIDVariant& id,
const PPropertyDescriptor& flags,
ReturnStatus* rs) = 0;
virtual bool SendDelete(const ObjectId& objId, const JSIDVariant& id,
ReturnStatus* rs) = 0;
virtual bool SendHas(const ObjectId& objId, const JSIDVariant& id,
ReturnStatus* rs, bool* bp) = 0;
virtual bool SendHasOwn(const ObjectId& objId, const JSIDVariant& id,
ReturnStatus* rs, bool* bp) = 0;
virtual bool SendGet(const ObjectId& objId, const JSVariant& receiverVar,
const JSIDVariant& id,
ReturnStatus* rs, JSVariant* result) = 0;
virtual bool SendSet(const ObjectId& objId, const JSIDVariant& id, const JSVariant& value,
const JSVariant& receiverVar, ReturnStatus* rs) = 0;
virtual bool SendIsExtensible(const ObjectId& objId, ReturnStatus* rs,
bool* result) = 0;
virtual bool SendCallOrConstruct(const ObjectId& objId, const nsTArray<JSParam>& argv,
const bool& construct, ReturnStatus* rs, JSVariant* result,
nsTArray<JSParam>* outparams) = 0;
virtual bool SendHasInstance(const ObjectId& objId, const JSVariant& v,
ReturnStatus* rs, bool* bp) = 0;
virtual bool SendGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs,
uint32_t* classValue) = 0;
virtual bool SendIsArray(const ObjectId& objId, ReturnStatus* rs,
uint32_t* answer) = 0;
virtual bool SendClassName(const ObjectId& objId, nsCString* result) = 0;
virtual bool SendGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result) = 0;
virtual bool SendGetPrototypeIfOrdinary(const ObjectId& objId, ReturnStatus* rs, bool* isOrdinary,
ObjectOrNullVariant* result) = 0;
virtual bool SendRegExpToShared(const ObjectId& objId, ReturnStatus* rs, nsString* source,
uint32_t* flags) = 0;
virtual bool SendGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
ReturnStatus* rs, nsTArray<JSIDVariant>* ids) = 0;
virtual bool SendInstanceOf(const ObjectId& objId, const JSIID& iid,
ReturnStatus* rs, bool* instanceof) = 0;
virtual bool SendDOMInstanceOf(const ObjectId& objId, const int& prototypeID, const int& depth,
ReturnStatus* rs, bool* instanceof) = 0;
};
} // namespace jsipc
} // namespace mozilla
#endif // mozilla_jsipc_WrapperOwner_h__

41
js/ipc/moz.build Normal file
View file

@ -0,0 +1,41 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
'CPOWTimer.cpp',
'JavaScriptChild.cpp',
'JavaScriptParent.cpp',
'JavaScriptShared.cpp',
'WrapperAnswer.cpp',
'WrapperOwner.cpp',
]
IPDL_SOURCES += [
'JavaScriptTypes.ipdlh',
'PJavaScript.ipdl',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG['BIN_SUFFIX']
EXPORTS.mozilla.jsipc = [
'CpowHolder.h',
'CrossProcessObjectWrappers.h',
]
LOCAL_INCLUDES += [
'/dom/base',
'/js/ipc',
'/js/src',
'/js/xpconnect/src',
'/js/xpconnect/wrappers',
]
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-shadow']