Bumping manifests a=b2g-bump
[gecko.git] / js / ipc / JavaScriptShared.cpp
blobdcbd8fbebb480be7b75ea34bf00307f7e091df60
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"
17 using namespace js;
18 using namespace JS;
19 using namespace mozilla;
20 using namespace mozilla::jsipc;
22 IdToObjectMap::IdToObjectMap()
23 : table_(SystemAllocPolicy())
27 bool
28 IdToObjectMap::init()
30 if (table_.initialized())
31 return true;
32 return table_.init(32);
35 void
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");
44 void
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);
50 if (!*objp)
51 e.removeFront();
55 JSObject*
56 IdToObjectMap::find(ObjectId id)
58 Table::Ptr p = table_.lookup(id);
59 if (!p)
60 return nullptr;
61 return p->value();
64 bool
65 IdToObjectMap::add(ObjectId id, JSObject* obj)
67 return table_.put(id, obj);
70 void
71 IdToObjectMap::remove(ObjectId id)
73 table_.remove(id);
76 void
77 IdToObjectMap::clear()
79 table_.clear();
82 bool
83 IdToObjectMap::empty() const
85 return table_.empty();
88 ObjectToIdMap::ObjectToIdMap()
89 : table_(nullptr)
93 ObjectToIdMap::~ObjectToIdMap()
95 if (table_) {
96 dom::AddForDeferredFinalization<Table, nsAutoPtr>(table_);
97 table_ = nullptr;
101 bool
102 ObjectToIdMap::init()
104 if (table_)
105 return true;
107 table_ = new Table(SystemAllocPolicy());
108 return table_ && table_->init(32);
111 void
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())
118 e.rekeyFront(obj);
122 void
123 ObjectToIdMap::sweep()
125 for (Table::Enum e(*table_); !e.empty(); e.popFront()) {
126 JSObject* obj = e.front().key();
127 JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
128 if (!obj)
129 e.removeFront();
130 else if (obj != e.front().key())
131 e.rekeyFront(obj);
135 ObjectId
136 ObjectToIdMap::find(JSObject* obj)
138 Table::Ptr p = table_->lookup(obj);
139 if (!p)
140 return ObjectId::nullId();
141 return p->value();
144 bool
145 ObjectToIdMap::add(JSContext* cx, JSObject* obj, ObjectId id)
147 if (!table_->put(obj, id))
148 return false;
149 JS_StoreObjectPostBarrierCallback(cx, keyMarkCallback, obj, table_);
150 return true;
154 * This function is called during minor GCs for each key in the HashMap that has
155 * been moved.
157 /* static */ void
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);
166 void
167 ObjectToIdMap::remove(JSObject* obj)
169 table_->remove(obj);
172 void
173 ObjectToIdMap::clear()
175 table_->clear();
178 bool JavaScriptShared::sLoggingInitialized;
179 bool JavaScriptShared::sLoggingEnabled;
180 bool JavaScriptShared::sStackLoggingEnabled;
182 JavaScriptShared::JavaScriptShared(JSRuntime* rt)
183 : rt_(rt),
184 refcount_(1),
185 nextSerialNumber_(1)
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");
193 } else {
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());
207 bool
208 JavaScriptShared::init()
210 if (!objects_.init())
211 return false;
212 if (!cpows_.init())
213 return false;
214 if (!unwaivedObjectIds_.init())
215 return false;
216 if (!waivedObjectIds_.init())
217 return false;
219 return true;
222 void
223 JavaScriptShared::decref()
225 refcount_--;
226 if (!refcount_)
227 delete this;
230 void
231 JavaScriptShared::incref()
233 refcount_++;
236 bool
237 JavaScriptShared::convertIdToGeckoString(JSContext* cx, JS::HandleId id, nsString* to)
239 RootedValue idval(cx);
240 if (!JS_IdToValue(cx, id, &idval))
241 return false;
243 RootedString str(cx, ToString(cx, idval));
244 if (!str)
245 return false;
247 return AssignJSString(cx, *to, str);
250 bool
251 JavaScriptShared::convertGeckoStringToId(JSContext* cx, const nsString& from, JS::MutableHandleId to)
253 RootedString str(cx, JS_NewUCStringCopyN(cx, from.BeginReading(), from.Length()));
254 if (!str)
255 return false;
257 return JS_StringToId(cx, str, to);
260 bool
261 JavaScriptShared::toVariant(JSContext* cx, JS::HandleValue from, JSVariant* to)
263 switch (JS_TypeOfValue(cx, from)) {
264 case JSTYPE_VOID:
265 *to = UndefinedVariant();
266 return true;
268 case JSTYPE_OBJECT:
269 case JSTYPE_FUNCTION:
271 RootedObject obj(cx, from.toObjectOrNull());
272 if (!obj) {
273 MOZ_ASSERT(from == JSVAL_NULL);
274 *to = NullVariant();
275 return true;
278 if (xpc_JSObjectIsID(cx, obj)) {
279 JSIID iid;
280 const nsID* id = xpc_JSObjectToID(cx, obj);
281 ConvertID(*id, &iid);
282 *to = iid;
283 return true;
286 ObjectVariant objVar;
287 if (!toObjectVariant(cx, obj, &objVar))
288 return false;
289 *to = objVar;
290 return true;
293 case JSTYPE_SYMBOL:
295 RootedSymbol sym(cx, from.toSymbol());
297 SymbolVariant symVar;
298 if (!toSymbolVariant(cx, sym, &symVar))
299 return false;
300 *to = symVar;
301 return true;
304 case JSTYPE_STRING:
306 nsAutoJSString autoStr;
307 if (!autoStr.init(cx, from))
308 return false;
309 *to = autoStr;
310 return true;
313 case JSTYPE_NUMBER:
314 if (from.isInt32())
315 *to = double(from.toInt32());
316 else
317 *to = from.toDouble();
318 return true;
320 case JSTYPE_BOOLEAN:
321 *to = from.toBoolean();
322 return true;
324 default:
325 MOZ_ASSERT(false);
326 return false;
330 bool
331 JavaScriptShared::fromVariant(JSContext* cx, const JSVariant& from, MutableHandleValue to)
333 switch (from.type()) {
334 case JSVariant::TUndefinedVariant:
335 to.set(UndefinedValue());
336 return true;
338 case JSVariant::TNullVariant:
339 to.set(NullValue());
340 return true;
342 case JSVariant::TObjectVariant:
344 JSObject* obj = fromObjectVariant(cx, from.get_ObjectVariant());
345 if (!obj)
346 return false;
347 to.set(ObjectValue(*obj));
348 return true;
351 case JSVariant::TSymbolVariant:
353 Symbol* sym = fromSymbolVariant(cx, from.get_SymbolVariant());
354 if (!sym)
355 return false;
356 to.setSymbol(sym);
357 return true;
360 case JSVariant::Tdouble:
361 to.set(JS_NumberValue(from.get_double()));
362 return true;
364 case JSVariant::Tbool:
365 to.set(BOOLEAN_TO_JSVAL(from.get_bool()));
366 return true;
368 case JSVariant::TnsString:
370 const nsString& old = from.get_nsString();
371 JSString* str = JS_NewUCStringCopyN(cx, old.BeginReading(), old.Length());
372 if (!str)
373 return false;
374 to.set(StringValue(str));
375 return true;
378 case JSVariant::TJSIID:
380 nsID iid;
381 const JSIID& id = from.get_JSIID();
382 ConvertID(id, &iid);
384 JSCompartment* compartment = GetContextCompartment(cx);
385 RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, compartment));
386 JSObject* obj = xpc_NewIDObject(cx, global, iid);
387 if (!obj)
388 return false;
389 to.set(ObjectValue(*obj));
390 return true;
393 default:
394 MOZ_CRASH("NYI");
395 return false;
399 bool
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)))
405 return false;
406 *to = autoStr;
407 return true;
409 if (JSID_IS_INT(from)) {
410 *to = JSID_TO_INT(from);
411 return true;
413 if (JSID_IS_SYMBOL(from)) {
414 SymbolVariant symVar;
415 if (!toSymbolVariant(cx, JSID_TO_SYMBOL(from), &symVar))
416 return false;
417 *to = symVar;
418 return true;
420 MOZ_ASSERT(false);
421 return false;
424 bool
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());
430 if (!sym)
431 return false;
432 to.set(SYMBOL_TO_JSID(sym));
433 return true;
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()));
441 return true;
443 default:
444 return false;
448 bool
449 JavaScriptShared::toSymbolVariant(JSContext* cx, JS::Symbol* symArg, SymbolVariant* symVarp)
451 RootedSymbol sym(cx, symArg);
452 MOZ_ASSERT(sym);
454 SymbolCode code = GetSymbolCode(sym);
455 if (static_cast<uint32_t>(code) < WellKnownSymbolLimit) {
456 *symVarp = WellKnownSymbol(static_cast<uint32_t>(code));
457 return true;
459 if (code == SymbolCode::InSymbolRegistry) {
460 nsAutoJSString autoStr;
461 if (!autoStr.init(cx, GetSymbolDescription(sym)))
462 return false;
463 *symVarp = RegisteredSymbol(autoStr);
464 return true;
467 JS_ReportError(cx, "unique symbol can't be used with CPOW");
468 return false;
471 JS::Symbol*
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");
480 return nullptr;
483 case SymbolVariant::TRegisteredSymbol: {
484 nsString key = symVar.get_RegisteredSymbol().key();
485 RootedString str(cx, JS_NewUCStringCopyN(cx, key.get(), key.Length()));
486 if (!str)
487 return nullptr;
488 return GetSymbolFor(cx, str);
491 default:
492 return nullptr;
496 /* static */ void
497 JavaScriptShared::ConvertID(const nsID& from, JSIID* to)
499 to->m0() = from.m0;
500 to->m1() = from.m1;
501 to->m2() = from.m2;
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];
512 /* static */ void
513 JavaScriptShared::ConvertID(const JSIID& from, nsID* to)
515 to->m0 = from.m0();
516 to->m1 = from.m1();
517 to->m2 = from.m2();
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();
528 JSObject*
529 JavaScriptShared::findObjectById(JSContext* cx, const ObjectId& objId)
531 RootedObject obj(cx, objects_.find(objId));
532 if (!obj) {
533 JS_ReportError(cx, "operation not possible on dead CPOW");
534 return nullptr;
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
540 // wrappers.
541 JSAutoCompartment ac(cx, scopeForTargetObjects());
542 if (objId.hasXrayWaiver()) {
544 JSAutoCompartment ac2(cx, obj);
545 obj = JS_ObjectToOuterObject(cx, obj);
546 if (!obj)
547 return nullptr;
549 if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, &obj))
550 return nullptr;
551 } else {
552 if (!JS_WrapObject(cx, &obj))
553 return nullptr;
555 return obj;
558 static const uint64_t UnknownPropertyOp = 1;
560 bool
561 JavaScriptShared::fromDescriptor(JSContext* cx, Handle<JSPropertyDescriptor> desc,
562 PPropertyDescriptor* out)
564 out->attrs() = desc.attributes();
565 if (!toVariant(cx, desc.value(), &out->value()))
566 return false;
568 if (!toObjectOrNullVariant(cx, desc.object(), &out->obj()))
569 return false;
571 if (!desc.getter()) {
572 out->getter() = 0;
573 } else if (desc.hasGetterObject()) {
574 JSObject* getter = desc.getterObject();
575 ObjectVariant objVar;
576 if (!toObjectVariant(cx, getter, &objVar))
577 return false;
578 out->getter() = objVar;
579 } else {
580 MOZ_ASSERT(desc.getter() != JS_PropertyStub);
581 out->getter() = UnknownPropertyOp;
584 if (!desc.setter()) {
585 out->setter() = 0;
586 } else if (desc.hasSetterObject()) {
587 JSObject* setter = desc.setterObject();
588 ObjectVariant objVar;
589 if (!toObjectVariant(cx, setter, &objVar))
590 return false;
591 out->setter() = objVar;
592 } else {
593 MOZ_ASSERT(desc.setter() != JS_StrictPropertyStub);
594 out->setter() = UnknownPropertyOp;
597 return true;
600 bool
601 UnknownPropertyStub(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
603 JS_ReportError(cx, "getter could not be wrapped via CPOWs");
604 return false;
607 bool
608 UnknownStrictPropertyStub(JSContext* cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
610 JS_ReportError(cx, "setter could not be wrapped via CPOWs");
611 return false;
614 bool
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()))
620 return false;
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());
628 if (!getter)
629 return false;
630 out.setGetter(JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter.get()));
631 } else {
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());
640 if (!setter)
641 return false;
642 out.setSetter(JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter.get()));
643 } else {
644 out.setSetter(UnknownStrictPropertyStub);
647 return true;
650 bool
651 JavaScriptShared::toObjectOrNullVariant(JSContext* cx, JSObject* obj, ObjectOrNullVariant* objVarp)
653 if (!obj) {
654 *objVarp = NullVariant();
655 return true;
658 ObjectVariant objVar;
659 if (!toObjectVariant(cx, obj, &objVar))
660 return false;
662 *objVarp = objVar;
663 return true;
666 JSObject*
667 JavaScriptShared::fromObjectOrNullVariant(JSContext* cx, ObjectOrNullVariant objVar)
669 if (objVar.type() == ObjectOrNullVariant::TNullVariant)
670 return nullptr;
672 return fromObjectVariant(cx, objVar.get_ObjectVariant());
675 CpowIdHolder::CpowIdHolder(dom::CPOWManagerGetter* managerGetter, const InfallibleTArray<CpowEntry>& cpows)
676 : js_(nullptr),
677 cpows_(cpows)
679 // Only instantiate the CPOW manager if we might need it later.
680 if (cpows.Length())
681 js_ = managerGetter->GetCPOWManager();
684 bool
685 CpowIdHolder::ToObject(JSContext* cx, JS::MutableHandleObject objp)
687 if (!cpows_.Length())
688 return true;
690 return js_->Unwrap(cx, cpows_, objp);
693 bool
694 JavaScriptShared::Unwrap(JSContext* cx, const InfallibleTArray<CpowEntry>& aCpows,
695 JS::MutableHandleObject objp)
697 objp.set(nullptr);
699 if (!aCpows.Length())
700 return true;
702 RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
703 if (!obj)
704 return false;
706 RootedValue v(cx);
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))
712 return false;
714 if (!JS_DefineUCProperty(cx,
715 obj,
716 name.BeginReading(),
717 name.Length(),
719 JSPROP_ENUMERATE))
721 return false;
725 objp.set(obj);
726 return true;
729 bool
730 JavaScriptShared::Wrap(JSContext* cx, HandleObject aObj, InfallibleTArray<CpowEntry>* outCpows)
732 if (!aObj)
733 return true;
735 AutoIdArray ids(cx, JS_Enumerate(cx, aObj));
736 if (!ids)
737 return false;
739 RootedId id(cx);
740 RootedValue v(cx);
741 for (size_t i = 0; i < ids.length(); i++) {
742 id = ids[i];
744 nsString str;
745 if (!convertIdToGeckoString(cx, id, &str))
746 return false;
748 if (!JS_GetPropertyById(cx, aObj, id, &v))
749 return false;
751 JSVariant var;
752 if (!toVariant(cx, v, &var))
753 return false;
755 outCpows->AppendElement(CpowEntry(str, var));
758 return true;