1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=80:
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "JavaScriptShared.h"
9 #include "mozilla/dom/BindingUtils.h"
10 #include "mozilla/dom/CPOWManagerGetter.h"
11 #include "mozilla/dom/TabChild.h"
12 #include "jsfriendapi.h"
13 #include "xpcprivate.h"
14 #include "WrapperFactory.h"
15 #include "mozilla/Preferences.h"
19 using namespace mozilla
;
20 using namespace mozilla::jsipc
;
22 IdToObjectMap::IdToObjectMap()
23 : table_(SystemAllocPolicy())
30 if (table_
.initialized())
32 return table_
.init(32);
36 IdToObjectMap::trace(JSTracer
* trc
)
38 for (Table::Range
r(table_
.all()); !r
.empty(); r
.popFront()) {
39 DebugOnly
<JSObject
*> prior
= r
.front().value().get();
40 JS_CallObjectTracer(trc
, &r
.front().value(), "ipc-object");
45 IdToObjectMap::sweep()
47 for (Table::Enum
e(table_
); !e
.empty(); e
.popFront()) {
48 JS::Heap
<JSObject
*>* objp
= &e
.front().value();
49 JS_UpdateWeakPointerAfterGC(objp
);
56 IdToObjectMap::find(ObjectId id
)
58 Table::Ptr p
= table_
.lookup(id
);
65 IdToObjectMap::add(ObjectId id
, JSObject
* obj
)
67 return table_
.put(id
, obj
);
71 IdToObjectMap::remove(ObjectId id
)
77 IdToObjectMap::clear()
83 IdToObjectMap::empty() const
85 return table_
.empty();
88 ObjectToIdMap::ObjectToIdMap()
93 ObjectToIdMap::~ObjectToIdMap()
96 dom::AddForDeferredFinalization
<Table
, nsAutoPtr
>(table_
);
102 ObjectToIdMap::init()
107 table_
= new Table(SystemAllocPolicy());
108 return table_
&& table_
->init(32);
112 ObjectToIdMap::trace(JSTracer
* trc
)
114 for (Table::Enum
e(*table_
); !e
.empty(); e
.popFront()) {
115 JSObject
* obj
= e
.front().key();
116 JS_CallUnbarrieredObjectTracer(trc
, &obj
, "ipc-object");
117 if (obj
!= e
.front().key())
123 ObjectToIdMap::sweep()
125 for (Table::Enum
e(*table_
); !e
.empty(); e
.popFront()) {
126 JSObject
* obj
= e
.front().key();
127 JS_UpdateWeakPointerAfterGCUnbarriered(&obj
);
130 else if (obj
!= e
.front().key())
136 ObjectToIdMap::find(JSObject
* obj
)
138 Table::Ptr p
= table_
->lookup(obj
);
140 return ObjectId::nullId();
145 ObjectToIdMap::add(JSContext
* cx
, JSObject
* obj
, ObjectId id
)
147 if (!table_
->put(obj
, id
))
149 JS_StoreObjectPostBarrierCallback(cx
, keyMarkCallback
, obj
, table_
);
154 * This function is called during minor GCs for each key in the HashMap that has
158 ObjectToIdMap::keyMarkCallback(JSTracer
* trc
, JSObject
* key
, void* data
)
160 Table
* table
= static_cast<Table
*>(data
);
161 JSObject
* prior
= key
;
162 JS_CallUnbarrieredObjectTracer(trc
, &key
, "ObjectIdCache::table_ key");
163 table
->rekeyIfMoved(prior
, key
);
167 ObjectToIdMap::remove(JSObject
* obj
)
173 ObjectToIdMap::clear()
178 bool JavaScriptShared::sLoggingInitialized
;
179 bool JavaScriptShared::sLoggingEnabled
;
180 bool JavaScriptShared::sStackLoggingEnabled
;
182 JavaScriptShared::JavaScriptShared(JSRuntime
* rt
)
187 if (!sLoggingInitialized
) {
188 sLoggingInitialized
= true;
190 if (PR_GetEnv("MOZ_CPOW_LOG")) {
191 sLoggingEnabled
= true;
192 sStackLoggingEnabled
= strstr(PR_GetEnv("MOZ_CPOW_LOG"), "stacks");
194 Preferences::AddBoolVarCache(&sLoggingEnabled
,
195 "dom.ipc.cpows.log.enabled", false);
196 Preferences::AddBoolVarCache(&sStackLoggingEnabled
,
197 "dom.ipc.cpows.log.stack", false);
202 JavaScriptShared::~JavaScriptShared()
204 MOZ_RELEASE_ASSERT(cpows_
.empty());
208 JavaScriptShared::init()
210 if (!objects_
.init())
214 if (!unwaivedObjectIds_
.init())
216 if (!waivedObjectIds_
.init())
223 JavaScriptShared::decref()
231 JavaScriptShared::incref()
237 JavaScriptShared::convertIdToGeckoString(JSContext
* cx
, JS::HandleId id
, nsString
* to
)
239 RootedValue
idval(cx
);
240 if (!JS_IdToValue(cx
, id
, &idval
))
243 RootedString
str(cx
, ToString(cx
, idval
));
247 return AssignJSString(cx
, *to
, str
);
251 JavaScriptShared::convertGeckoStringToId(JSContext
* cx
, const nsString
& from
, JS::MutableHandleId to
)
253 RootedString
str(cx
, JS_NewUCStringCopyN(cx
, from
.BeginReading(), from
.Length()));
257 return JS_StringToId(cx
, str
, to
);
261 JavaScriptShared::toVariant(JSContext
* cx
, JS::HandleValue from
, JSVariant
* to
)
263 switch (JS_TypeOfValue(cx
, from
)) {
265 *to
= UndefinedVariant();
269 case JSTYPE_FUNCTION
:
271 RootedObject
obj(cx
, from
.toObjectOrNull());
273 MOZ_ASSERT(from
== JSVAL_NULL
);
278 if (xpc_JSObjectIsID(cx
, obj
)) {
280 const nsID
* id
= xpc_JSObjectToID(cx
, obj
);
281 ConvertID(*id
, &iid
);
286 ObjectVariant objVar
;
287 if (!toObjectVariant(cx
, obj
, &objVar
))
295 RootedSymbol
sym(cx
, from
.toSymbol());
297 SymbolVariant symVar
;
298 if (!toSymbolVariant(cx
, sym
, &symVar
))
306 nsAutoJSString autoStr
;
307 if (!autoStr
.init(cx
, from
))
315 *to
= double(from
.toInt32());
317 *to
= from
.toDouble();
321 *to
= from
.toBoolean();
331 JavaScriptShared::fromVariant(JSContext
* cx
, const JSVariant
& from
, MutableHandleValue to
)
333 switch (from
.type()) {
334 case JSVariant::TUndefinedVariant
:
335 to
.set(UndefinedValue());
338 case JSVariant::TNullVariant
:
342 case JSVariant::TObjectVariant
:
344 JSObject
* obj
= fromObjectVariant(cx
, from
.get_ObjectVariant());
347 to
.set(ObjectValue(*obj
));
351 case JSVariant::TSymbolVariant
:
353 Symbol
* sym
= fromSymbolVariant(cx
, from
.get_SymbolVariant());
360 case JSVariant::Tdouble
:
361 to
.set(JS_NumberValue(from
.get_double()));
364 case JSVariant::Tbool
:
365 to
.set(BOOLEAN_TO_JSVAL(from
.get_bool()));
368 case JSVariant::TnsString
:
370 const nsString
& old
= from
.get_nsString();
371 JSString
* str
= JS_NewUCStringCopyN(cx
, old
.BeginReading(), old
.Length());
374 to
.set(StringValue(str
));
378 case JSVariant::TJSIID
:
381 const JSIID
& id
= from
.get_JSIID();
384 JSCompartment
* compartment
= GetContextCompartment(cx
);
385 RootedObject
global(cx
, JS_GetGlobalForCompartmentOrNull(cx
, compartment
));
386 JSObject
* obj
= xpc_NewIDObject(cx
, global
, iid
);
389 to
.set(ObjectValue(*obj
));
400 JavaScriptShared::toJSIDVariant(JSContext
* cx
, HandleId from
, JSIDVariant
* to
)
402 if (JSID_IS_STRING(from
)) {
403 nsAutoJSString autoStr
;
404 if (!autoStr
.init(cx
, JSID_TO_STRING(from
)))
409 if (JSID_IS_INT(from
)) {
410 *to
= JSID_TO_INT(from
);
413 if (JSID_IS_SYMBOL(from
)) {
414 SymbolVariant symVar
;
415 if (!toSymbolVariant(cx
, JSID_TO_SYMBOL(from
), &symVar
))
425 JavaScriptShared::fromJSIDVariant(JSContext
* cx
, const JSIDVariant
& from
, MutableHandleId to
)
427 switch (from
.type()) {
428 case JSIDVariant::TSymbolVariant
: {
429 Symbol
* sym
= fromSymbolVariant(cx
, from
.get_SymbolVariant());
432 to
.set(SYMBOL_TO_JSID(sym
));
436 case JSIDVariant::TnsString
:
437 return convertGeckoStringToId(cx
, from
.get_nsString(), to
);
439 case JSIDVariant::Tint32_t
:
440 to
.set(INT_TO_JSID(from
.get_int32_t()));
449 JavaScriptShared::toSymbolVariant(JSContext
* cx
, JS::Symbol
* symArg
, SymbolVariant
* symVarp
)
451 RootedSymbol
sym(cx
, symArg
);
454 SymbolCode code
= GetSymbolCode(sym
);
455 if (static_cast<uint32_t>(code
) < WellKnownSymbolLimit
) {
456 *symVarp
= WellKnownSymbol(static_cast<uint32_t>(code
));
459 if (code
== SymbolCode::InSymbolRegistry
) {
460 nsAutoJSString autoStr
;
461 if (!autoStr
.init(cx
, GetSymbolDescription(sym
)))
463 *symVarp
= RegisteredSymbol(autoStr
);
467 JS_ReportError(cx
, "unique symbol can't be used with CPOW");
472 JavaScriptShared::fromSymbolVariant(JSContext
* cx
, SymbolVariant symVar
)
474 switch (symVar
.type()) {
475 case SymbolVariant::TWellKnownSymbol
: {
476 uint32_t which
= symVar
.get_WellKnownSymbol().which();
477 if (which
< WellKnownSymbolLimit
)
478 return GetWellKnownSymbol(cx
, static_cast<SymbolCode
>(which
));
479 MOZ_ASSERT(false, "bad child data");
483 case SymbolVariant::TRegisteredSymbol
: {
484 nsString key
= symVar
.get_RegisteredSymbol().key();
485 RootedString
str(cx
, JS_NewUCStringCopyN(cx
, key
.get(), key
.Length()));
488 return GetSymbolFor(cx
, str
);
497 JavaScriptShared::ConvertID(const nsID
& from
, JSIID
* to
)
502 to
->m3_0() = from
.m3
[0];
503 to
->m3_1() = from
.m3
[1];
504 to
->m3_2() = from
.m3
[2];
505 to
->m3_3() = from
.m3
[3];
506 to
->m3_4() = from
.m3
[4];
507 to
->m3_5() = from
.m3
[5];
508 to
->m3_6() = from
.m3
[6];
509 to
->m3_7() = from
.m3
[7];
513 JavaScriptShared::ConvertID(const JSIID
& from
, nsID
* to
)
518 to
->m3
[0] = from
.m3_0();
519 to
->m3
[1] = from
.m3_1();
520 to
->m3
[2] = from
.m3_2();
521 to
->m3
[3] = from
.m3_3();
522 to
->m3
[4] = from
.m3_4();
523 to
->m3
[5] = from
.m3_5();
524 to
->m3
[6] = from
.m3_6();
525 to
->m3
[7] = from
.m3_7();
529 JavaScriptShared::findObjectById(JSContext
* cx
, const ObjectId
& objId
)
531 RootedObject
obj(cx
, objects_
.find(objId
));
533 JS_ReportError(cx
, "operation not possible on dead CPOW");
537 // Each process has a dedicated compartment for CPOW targets. All CPOWs
538 // from the other process point to objects in this scope. From there, they
539 // can access objects in other compartments using cross-compartment
541 JSAutoCompartment
ac(cx
, scopeForTargetObjects());
542 if (objId
.hasXrayWaiver()) {
544 JSAutoCompartment
ac2(cx
, obj
);
545 obj
= JS_ObjectToOuterObject(cx
, obj
);
549 if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx
, &obj
))
552 if (!JS_WrapObject(cx
, &obj
))
558 static const uint64_t UnknownPropertyOp
= 1;
561 JavaScriptShared::fromDescriptor(JSContext
* cx
, Handle
<JSPropertyDescriptor
> desc
,
562 PPropertyDescriptor
* out
)
564 out
->attrs() = desc
.attributes();
565 if (!toVariant(cx
, desc
.value(), &out
->value()))
568 if (!toObjectOrNullVariant(cx
, desc
.object(), &out
->obj()))
571 if (!desc
.getter()) {
573 } else if (desc
.hasGetterObject()) {
574 JSObject
* getter
= desc
.getterObject();
575 ObjectVariant objVar
;
576 if (!toObjectVariant(cx
, getter
, &objVar
))
578 out
->getter() = objVar
;
580 MOZ_ASSERT(desc
.getter() != JS_PropertyStub
);
581 out
->getter() = UnknownPropertyOp
;
584 if (!desc
.setter()) {
586 } else if (desc
.hasSetterObject()) {
587 JSObject
* setter
= desc
.setterObject();
588 ObjectVariant objVar
;
589 if (!toObjectVariant(cx
, setter
, &objVar
))
591 out
->setter() = objVar
;
593 MOZ_ASSERT(desc
.setter() != JS_StrictPropertyStub
);
594 out
->setter() = UnknownPropertyOp
;
601 UnknownPropertyStub(JSContext
* cx
, HandleObject obj
, HandleId id
, MutableHandleValue vp
)
603 JS_ReportError(cx
, "getter could not be wrapped via CPOWs");
608 UnknownStrictPropertyStub(JSContext
* cx
, HandleObject obj
, HandleId id
, bool strict
, MutableHandleValue vp
)
610 JS_ReportError(cx
, "setter could not be wrapped via CPOWs");
615 JavaScriptShared::toDescriptor(JSContext
* cx
, const PPropertyDescriptor
& in
,
616 MutableHandle
<JSPropertyDescriptor
> out
)
618 out
.setAttributes(in
.attrs());
619 if (!fromVariant(cx
, in
.value(), out
.value()))
621 out
.object().set(fromObjectOrNullVariant(cx
, in
.obj()));
623 if (in
.getter().type() == GetterSetter::Tuint64_t
&& !in
.getter().get_uint64_t()) {
624 out
.setGetter(nullptr);
625 } else if (in
.attrs() & JSPROP_GETTER
) {
626 Rooted
<JSObject
*> getter(cx
);
627 getter
= fromObjectVariant(cx
, in
.getter().get_ObjectVariant());
630 out
.setGetter(JS_DATA_TO_FUNC_PTR(JSPropertyOp
, getter
.get()));
632 out
.setGetter(UnknownPropertyStub
);
635 if (in
.setter().type() == GetterSetter::Tuint64_t
&& !in
.setter().get_uint64_t()) {
636 out
.setSetter(nullptr);
637 } else if (in
.attrs() & JSPROP_SETTER
) {
638 Rooted
<JSObject
*> setter(cx
);
639 setter
= fromObjectVariant(cx
, in
.setter().get_ObjectVariant());
642 out
.setSetter(JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp
, setter
.get()));
644 out
.setSetter(UnknownStrictPropertyStub
);
651 JavaScriptShared::toObjectOrNullVariant(JSContext
* cx
, JSObject
* obj
, ObjectOrNullVariant
* objVarp
)
654 *objVarp
= NullVariant();
658 ObjectVariant objVar
;
659 if (!toObjectVariant(cx
, obj
, &objVar
))
667 JavaScriptShared::fromObjectOrNullVariant(JSContext
* cx
, ObjectOrNullVariant objVar
)
669 if (objVar
.type() == ObjectOrNullVariant::TNullVariant
)
672 return fromObjectVariant(cx
, objVar
.get_ObjectVariant());
675 CpowIdHolder::CpowIdHolder(dom::CPOWManagerGetter
* managerGetter
, const InfallibleTArray
<CpowEntry
>& cpows
)
679 // Only instantiate the CPOW manager if we might need it later.
681 js_
= managerGetter
->GetCPOWManager();
685 CpowIdHolder::ToObject(JSContext
* cx
, JS::MutableHandleObject objp
)
687 if (!cpows_
.Length())
690 return js_
->Unwrap(cx
, cpows_
, objp
);
694 JavaScriptShared::Unwrap(JSContext
* cx
, const InfallibleTArray
<CpowEntry
>& aCpows
,
695 JS::MutableHandleObject objp
)
699 if (!aCpows
.Length())
702 RootedObject
obj(cx
, JS_NewObject(cx
, nullptr, JS::NullPtr(), JS::NullPtr()));
707 RootedString
str(cx
);
708 for (size_t i
= 0; i
< aCpows
.Length(); i
++) {
709 const nsString
& name
= aCpows
[i
].name();
711 if (!fromVariant(cx
, aCpows
[i
].value(), &v
))
714 if (!JS_DefineUCProperty(cx
,
730 JavaScriptShared::Wrap(JSContext
* cx
, HandleObject aObj
, InfallibleTArray
<CpowEntry
>* outCpows
)
735 AutoIdArray
ids(cx
, JS_Enumerate(cx
, aObj
));
741 for (size_t i
= 0; i
< ids
.length(); i
++) {
745 if (!convertIdToGeckoString(cx
, id
, &str
))
748 if (!JS_GetPropertyById(cx
, aObj
, id
, &v
))
752 if (!toVariant(cx
, v
, &var
))
755 outCpows
->AppendElement(CpowEntry(str
, var
));