Bug 1065811 - Track Xray waivers with CPOWs. r=billm

This commit is contained in:
Bobby Holley 2014-09-25 13:13:29 +02:00
parent 8b65a1f10b
commit 8e8a7d28c3
8 changed files with 56 additions and 25 deletions

View File

@ -223,7 +223,7 @@
// (the privileged junk scope, but we don't have a good way to test for that
// specifically).
is(unprivilegedObject.expando, undefined, "parent->child references should get Xrays");
todo_is(unprivilegedObject.wrappedJSObject.expando, 42, "parent->child references should get waivable Xrays - see bug 1065811");
is(unprivilegedObject.wrappedJSObject.expando, 42, "parent->child references should get waivable Xrays");
// Send an object to the child to let it verify invariants in the other direction.
function passMe() { return 42; };

View File

@ -53,7 +53,8 @@ void
JavaScriptChild::updateWeakPointers()
{
objects_.sweep();
objectIds_.sweep();
unwaivedObjectIds_.sweep();
waivedObjectIds_.sweep();
}
JSObject *

View File

@ -96,7 +96,7 @@ class Logging
if (local == incoming) {
JS::RootedObject obj(cx);
obj = shared->findObjectById(id);
obj = shared->objects_.find(id);
if (obj) {
JSAutoCompartment ac(cx, obj);
objDesc = js_ObjectClassName(cx, obj);

View File

@ -53,7 +53,8 @@ JavaScriptParent::trace(JSTracer *trc)
{
if (active()) {
objects_.trace(trc);
objectIds_.trace(trc);
unwaivedObjectIds_.trace(trc);
waivedObjectIds_.trace(trc);
}
}

View File

@ -10,6 +10,7 @@
#include "mozilla/dom/TabChild.h"
#include "jsfriendapi.h"
#include "xpcprivate.h"
#include "WrapperFactory.h"
#include "mozilla/Preferences.h"
using namespace js;
@ -186,7 +187,9 @@ JavaScriptShared::init()
return false;
if (!cpows_.init())
return false;
if (!objectIds_.init())
if (!unwaivedObjectIds_.init())
return false;
if (!waivedObjectIds_.init())
return false;
return true;
@ -383,7 +386,7 @@ JavaScriptShared::ConvertID(const JSIID &from, nsID *to)
JSObject *
JavaScriptShared::findObjectById(JSContext *cx, const ObjectId &objId)
{
RootedObject obj(cx, findObjectById(objId));
RootedObject obj(cx, objects_.find(objId));
if (!obj) {
JS_ReportError(cx, "operation not possible on dead CPOW");
return nullptr;
@ -394,8 +397,13 @@ JavaScriptShared::findObjectById(JSContext *cx, const ObjectId &objId)
// can access objects in other compartments using cross-compartment
// wrappers.
JSAutoCompartment ac(cx, scopeForTargetObjects());
if (!JS_WrapObject(cx, &obj))
return nullptr;
if (objId.hasXrayWaiver()) {
if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, &obj))
return nullptr;
} else {
if (!JS_WrapObject(cx, &obj))
return nullptr;
}
return obj;
}

View File

@ -24,8 +24,8 @@ class ObjectId {
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 isCallable)
: serialNumber_(serialNumber), isCallable_(isCallable)
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");
@ -33,17 +33,17 @@ class ObjectId {
bool operator==(const ObjectId &other) const {
bool equal = serialNumber() == other.serialNumber();
MOZ_ASSERT_IF(equal, isCallable() == other.isCallable());
MOZ_ASSERT_IF(equal, hasXrayWaiver() == other.hasXrayWaiver());
return equal;
}
bool isNull() { return !serialNumber_; }
uint64_t serialNumber() const { return serialNumber_; }
bool isCallable() const { return isCallable_; }
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) | ((isCallable() ? 1 : 0) << 0));
return uint64_t((serialNumber() << FLAG_BITS) | ((hasXrayWaiver() ? 1 : 0) << 0));
}
static ObjectId nullId() { return ObjectId(); }
@ -52,10 +52,10 @@ class ObjectId {
}
private:
ObjectId() : serialNumber_(0), isCallable_(false) {}
ObjectId() : serialNumber_(0), hasXrayWaiver_(false) {}
uint64_t serialNumber_ : SERIAL_NUMBER_BITS;
bool isCallable_ : 1;
bool hasXrayWaiver_ : 1;
};
class JavaScriptShared;
@ -174,9 +174,6 @@ class JavaScriptShared
JSObject *findCPOWById(const ObjectId &objId) {
return cpows_.find(objId);
}
JSObject *findObjectById(const ObjectId &objId) {
return objects_.find(objId);
}
JSObject *findObjectById(JSContext *cx, const ObjectId &objId);
static bool LoggingEnabled() { return sLoggingEnabled; }
@ -196,7 +193,27 @@ class JavaScriptShared
IdToObjectMap cpows_;
uint64_t nextSerialNumber_;
ObjectToIdMap objectIds_;
// 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;

View File

@ -679,9 +679,9 @@ WrapperAnswer::AnswerIsConstructor(const ObjectId &objId, bool *result)
bool
WrapperAnswer::RecvDropObject(const ObjectId &objId)
{
JSObject *obj = findObjectById(objId);
JSObject *obj = objects_.find(objId);
if (obj) {
objectIds_.remove(obj);
objectIdMap(objId.hasXrayWaiver()).remove(obj);
objects_.remove(objId);
}
return true;

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/BindingUtils.h"
#include "jsfriendapi.h"
#include "xpcprivate.h"
#include "WrapperFactory.h"
using namespace js;
using namespace JS;
@ -870,14 +871,17 @@ WrapperOwner::toObjectVariant(JSContext *cx, JSObject *objArg, ObjectVariant *ob
// wrappers, then the wrapper might be GCed while the target remained alive.
// Whenever operating on an object that comes from the table, we wrap it
// in findObjectById.
obj = js::UncheckedUnwrap(obj, false);
unsigned wrapperFlags = 0;
obj = js::UncheckedUnwrap(obj, false, &wrapperFlags);
if (obj && IsCPOW(obj) && OwnerOf(obj) == this) {
*objVarp = LocalObject(idOf(obj).serialize());
return true;
}
bool waiveXray = wrapperFlags & xpc::WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG;
ObjectId id = objectIds_.find(obj);
ObjectId id = objectIdMap(waiveXray).find(obj);
if (!id.isNull()) {
MOZ_ASSERT(id.hasXrayWaiver() == waiveXray);
*objVarp = RemoteObject(id.serialize());
return true;
}
@ -887,10 +891,10 @@ WrapperOwner::toObjectVariant(JSContext *cx, JSObject *objArg, ObjectVariant *ob
if (mozilla::dom::IsDOMObject(obj))
mozilla::dom::TryPreserveWrapper(obj);
id = ObjectId(nextSerialNumber_++, JS::IsCallable(obj));
id = ObjectId(nextSerialNumber_++, waiveXray);
if (!objects_.add(id, obj))
return false;
if (!objectIds_.add(cx, obj, id))
if (!objectIdMap(waiveXray).add(cx, obj, id))
return false;
*objVarp = RemoteObject(id.serialize());