|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=4 sw=4 et tw=80: |
|
3 * |
|
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/. */ |
|
7 |
|
8 #include "JavaScriptShared.h" |
|
9 #include "mozilla/dom/BindingUtils.h" |
|
10 #include "jsfriendapi.h" |
|
11 #include "xpcprivate.h" |
|
12 |
|
13 using namespace js; |
|
14 using namespace JS; |
|
15 using namespace mozilla; |
|
16 using namespace mozilla::jsipc; |
|
17 |
|
18 ObjectStore::ObjectStore() |
|
19 : table_(SystemAllocPolicy()) |
|
20 { |
|
21 } |
|
22 |
|
23 bool |
|
24 ObjectStore::init() |
|
25 { |
|
26 return table_.init(32); |
|
27 } |
|
28 |
|
29 void |
|
30 ObjectStore::trace(JSTracer *trc) |
|
31 { |
|
32 for (ObjectTable::Range r(table_.all()); !r.empty(); r.popFront()) { |
|
33 DebugOnly<JSObject *> prior = r.front().value().get(); |
|
34 JS_CallHeapObjectTracer(trc, &r.front().value(), "ipc-object"); |
|
35 MOZ_ASSERT(r.front().value() == prior); |
|
36 } |
|
37 } |
|
38 |
|
39 JSObject * |
|
40 ObjectStore::find(ObjectId id) |
|
41 { |
|
42 ObjectTable::Ptr p = table_.lookup(id); |
|
43 if (!p) |
|
44 return nullptr; |
|
45 return p->value(); |
|
46 } |
|
47 |
|
48 bool |
|
49 ObjectStore::add(ObjectId id, JSObject *obj) |
|
50 { |
|
51 return table_.put(id, obj); |
|
52 } |
|
53 |
|
54 void |
|
55 ObjectStore::remove(ObjectId id) |
|
56 { |
|
57 table_.remove(id); |
|
58 } |
|
59 |
|
60 ObjectIdCache::ObjectIdCache() |
|
61 : table_(nullptr) |
|
62 { |
|
63 } |
|
64 |
|
65 ObjectIdCache::~ObjectIdCache() |
|
66 { |
|
67 if (table_) { |
|
68 dom::AddForDeferredFinalization<ObjectIdTable, nsAutoPtr>(table_); |
|
69 table_ = nullptr; |
|
70 } |
|
71 } |
|
72 |
|
73 bool |
|
74 ObjectIdCache::init() |
|
75 { |
|
76 MOZ_ASSERT(!table_); |
|
77 table_ = new ObjectIdTable(SystemAllocPolicy()); |
|
78 return table_ && table_->init(32); |
|
79 } |
|
80 |
|
81 void |
|
82 ObjectIdCache::trace(JSTracer *trc) |
|
83 { |
|
84 for (ObjectIdTable::Range r(table_->all()); !r.empty(); r.popFront()) { |
|
85 JSObject *obj = r.front().key(); |
|
86 JS_CallObjectTracer(trc, &obj, "ipc-id"); |
|
87 MOZ_ASSERT(obj == r.front().key()); |
|
88 } |
|
89 } |
|
90 |
|
91 ObjectId |
|
92 ObjectIdCache::find(JSObject *obj) |
|
93 { |
|
94 ObjectIdTable::Ptr p = table_->lookup(obj); |
|
95 if (!p) |
|
96 return 0; |
|
97 return p->value(); |
|
98 } |
|
99 |
|
100 bool |
|
101 ObjectIdCache::add(JSContext *cx, JSObject *obj, ObjectId id) |
|
102 { |
|
103 if (!table_->put(obj, id)) |
|
104 return false; |
|
105 JS_StoreObjectPostBarrierCallback(cx, keyMarkCallback, obj, table_); |
|
106 return true; |
|
107 } |
|
108 |
|
109 /* |
|
110 * This function is called during minor GCs for each key in the HashMap that has |
|
111 * been moved. |
|
112 */ |
|
113 /* static */ void |
|
114 ObjectIdCache::keyMarkCallback(JSTracer *trc, JSObject *key, void *data) { |
|
115 ObjectIdTable* table = static_cast<ObjectIdTable*>(data); |
|
116 JSObject *prior = key; |
|
117 JS_CallObjectTracer(trc, &key, "ObjectIdCache::table_ key"); |
|
118 table->rekeyIfMoved(prior, key); |
|
119 } |
|
120 |
|
121 void |
|
122 ObjectIdCache::remove(JSObject *obj) |
|
123 { |
|
124 table_->remove(obj); |
|
125 } |
|
126 |
|
127 bool |
|
128 JavaScriptShared::init() |
|
129 { |
|
130 if (!objects_.init()) |
|
131 return false; |
|
132 return true; |
|
133 } |
|
134 |
|
135 bool |
|
136 JavaScriptShared::convertIdToGeckoString(JSContext *cx, JS::HandleId id, nsString *to) |
|
137 { |
|
138 RootedValue idval(cx); |
|
139 if (!JS_IdToValue(cx, id, &idval)) |
|
140 return false; |
|
141 |
|
142 RootedString str(cx, ToString(cx, idval)); |
|
143 if (!str) |
|
144 return false; |
|
145 |
|
146 const jschar *chars = JS_GetStringCharsZ(cx, str); |
|
147 if (!chars) |
|
148 return false; |
|
149 |
|
150 *to = chars; |
|
151 return true; |
|
152 } |
|
153 |
|
154 bool |
|
155 JavaScriptShared::convertGeckoStringToId(JSContext *cx, const nsString &from, JS::MutableHandleId to) |
|
156 { |
|
157 RootedString str(cx, JS_NewUCStringCopyN(cx, from.BeginReading(), from.Length())); |
|
158 if (!str) |
|
159 return false; |
|
160 |
|
161 return JS_StringToId(cx, str, to); |
|
162 } |
|
163 |
|
164 bool |
|
165 JavaScriptShared::toVariant(JSContext *cx, JS::HandleValue from, JSVariant *to) |
|
166 { |
|
167 switch (JS_TypeOfValue(cx, from)) { |
|
168 case JSTYPE_VOID: |
|
169 *to = void_t(); |
|
170 return true; |
|
171 |
|
172 case JSTYPE_NULL: |
|
173 { |
|
174 *to = uint64_t(0); |
|
175 return true; |
|
176 } |
|
177 |
|
178 case JSTYPE_OBJECT: |
|
179 case JSTYPE_FUNCTION: |
|
180 { |
|
181 RootedObject obj(cx, from.toObjectOrNull()); |
|
182 if (!obj) { |
|
183 MOZ_ASSERT(from == JSVAL_NULL); |
|
184 *to = uint64_t(0); |
|
185 return true; |
|
186 } |
|
187 |
|
188 if (xpc_JSObjectIsID(cx, obj)) { |
|
189 JSIID iid; |
|
190 const nsID *id = xpc_JSObjectToID(cx, obj); |
|
191 ConvertID(*id, &iid); |
|
192 *to = iid; |
|
193 return true; |
|
194 } |
|
195 |
|
196 ObjectId id; |
|
197 if (!makeId(cx, obj, &id)) |
|
198 return false; |
|
199 *to = uint64_t(id); |
|
200 return true; |
|
201 } |
|
202 |
|
203 case JSTYPE_STRING: |
|
204 { |
|
205 nsDependentJSString dep; |
|
206 if (!dep.init(cx, from)) |
|
207 return false; |
|
208 *to = dep; |
|
209 return true; |
|
210 } |
|
211 |
|
212 case JSTYPE_NUMBER: |
|
213 if (JSVAL_IS_INT(from)) |
|
214 *to = double(from.toInt32()); |
|
215 else |
|
216 *to = from.toDouble(); |
|
217 return true; |
|
218 |
|
219 case JSTYPE_BOOLEAN: |
|
220 *to = from.toBoolean(); |
|
221 return true; |
|
222 |
|
223 default: |
|
224 MOZ_ASSERT(false); |
|
225 return false; |
|
226 } |
|
227 } |
|
228 |
|
229 bool |
|
230 JavaScriptShared::toValue(JSContext *cx, const JSVariant &from, MutableHandleValue to) |
|
231 { |
|
232 switch (from.type()) { |
|
233 case JSVariant::Tvoid_t: |
|
234 to.set(UndefinedValue()); |
|
235 return true; |
|
236 |
|
237 case JSVariant::Tuint64_t: |
|
238 { |
|
239 ObjectId id = from.get_uint64_t(); |
|
240 if (id) { |
|
241 JSObject *obj = unwrap(cx, id); |
|
242 if (!obj) |
|
243 return false; |
|
244 to.set(ObjectValue(*obj)); |
|
245 } else { |
|
246 to.set(JSVAL_NULL); |
|
247 } |
|
248 return true; |
|
249 } |
|
250 |
|
251 case JSVariant::Tdouble: |
|
252 to.set(JS_NumberValue(from.get_double())); |
|
253 return true; |
|
254 |
|
255 case JSVariant::Tbool: |
|
256 to.set(BOOLEAN_TO_JSVAL(from.get_bool())); |
|
257 return true; |
|
258 |
|
259 case JSVariant::TnsString: |
|
260 { |
|
261 const nsString &old = from.get_nsString(); |
|
262 JSString *str = JS_NewUCStringCopyN(cx, old.BeginReading(), old.Length()); |
|
263 if (!str) |
|
264 return false; |
|
265 to.set(StringValue(str)); |
|
266 return true; |
|
267 } |
|
268 |
|
269 case JSVariant::TJSIID: |
|
270 { |
|
271 nsID iid; |
|
272 const JSIID &id = from.get_JSIID(); |
|
273 ConvertID(id, &iid); |
|
274 |
|
275 JSCompartment *compartment = GetContextCompartment(cx); |
|
276 RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, compartment)); |
|
277 JSObject *obj = xpc_NewIDObject(cx, global, iid); |
|
278 if (!obj) |
|
279 return false; |
|
280 to.set(ObjectValue(*obj)); |
|
281 return true; |
|
282 } |
|
283 |
|
284 default: |
|
285 return false; |
|
286 } |
|
287 } |
|
288 |
|
289 /* static */ void |
|
290 JavaScriptShared::ConvertID(const nsID &from, JSIID *to) |
|
291 { |
|
292 to->m0() = from.m0; |
|
293 to->m1() = from.m1; |
|
294 to->m2() = from.m2; |
|
295 to->m3_0() = from.m3[0]; |
|
296 to->m3_1() = from.m3[1]; |
|
297 to->m3_2() = from.m3[2]; |
|
298 to->m3_3() = from.m3[3]; |
|
299 to->m3_4() = from.m3[4]; |
|
300 to->m3_5() = from.m3[5]; |
|
301 to->m3_6() = from.m3[6]; |
|
302 to->m3_7() = from.m3[7]; |
|
303 } |
|
304 |
|
305 /* static */ void |
|
306 JavaScriptShared::ConvertID(const JSIID &from, nsID *to) |
|
307 { |
|
308 to->m0 = from.m0(); |
|
309 to->m1 = from.m1(); |
|
310 to->m2 = from.m2(); |
|
311 to->m3[0] = from.m3_0(); |
|
312 to->m3[1] = from.m3_1(); |
|
313 to->m3[2] = from.m3_2(); |
|
314 to->m3[3] = from.m3_3(); |
|
315 to->m3[4] = from.m3_4(); |
|
316 to->m3[5] = from.m3_5(); |
|
317 to->m3[6] = from.m3_6(); |
|
318 to->m3[7] = from.m3_7(); |
|
319 } |
|
320 |
|
321 static const uint32_t DefaultPropertyOp = 1; |
|
322 static const uint32_t GetterOnlyPropertyStub = 2; |
|
323 static const uint32_t UnknownPropertyOp = 3; |
|
324 |
|
325 bool |
|
326 JavaScriptShared::fromDescriptor(JSContext *cx, Handle<JSPropertyDescriptor> desc, |
|
327 PPropertyDescriptor *out) |
|
328 { |
|
329 out->attrs() = desc.attributes(); |
|
330 if (!toVariant(cx, desc.value(), &out->value())) |
|
331 return false; |
|
332 |
|
333 if (!makeId(cx, desc.object(), &out->objId())) |
|
334 return false; |
|
335 |
|
336 if (!desc.getter()) { |
|
337 out->getter() = 0; |
|
338 } else if (desc.hasGetterObject()) { |
|
339 JSObject *getter = desc.getterObject(); |
|
340 if (!makeId(cx, getter, &out->getter())) |
|
341 return false; |
|
342 } else { |
|
343 if (desc.getter() == JS_PropertyStub) |
|
344 out->getter() = DefaultPropertyOp; |
|
345 else |
|
346 out->getter() = UnknownPropertyOp; |
|
347 } |
|
348 |
|
349 if (!desc.setter()) { |
|
350 out->setter() = 0; |
|
351 } else if (desc.hasSetterObject()) { |
|
352 JSObject *setter = desc.setterObject(); |
|
353 if (!makeId(cx, setter, &out->setter())) |
|
354 return false; |
|
355 } else { |
|
356 if (desc.setter() == JS_StrictPropertyStub) |
|
357 out->setter() = DefaultPropertyOp; |
|
358 else if (desc.setter() == js_GetterOnlyPropertyStub) |
|
359 out->setter() = GetterOnlyPropertyStub; |
|
360 else |
|
361 out->setter() = UnknownPropertyOp; |
|
362 } |
|
363 |
|
364 return true; |
|
365 } |
|
366 |
|
367 bool |
|
368 UnknownPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) |
|
369 { |
|
370 JS_ReportError(cx, "getter could not be wrapped via CPOWs"); |
|
371 return false; |
|
372 } |
|
373 |
|
374 bool |
|
375 UnknownStrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) |
|
376 { |
|
377 JS_ReportError(cx, "setter could not be wrapped via CPOWs"); |
|
378 return false; |
|
379 } |
|
380 |
|
381 bool |
|
382 JavaScriptShared::toDescriptor(JSContext *cx, const PPropertyDescriptor &in, |
|
383 MutableHandle<JSPropertyDescriptor> out) |
|
384 { |
|
385 out.setAttributes(in.attrs()); |
|
386 if (!toValue(cx, in.value(), out.value())) |
|
387 return false; |
|
388 Rooted<JSObject*> obj(cx); |
|
389 if (!unwrap(cx, in.objId(), &obj)) |
|
390 return false; |
|
391 out.object().set(obj); |
|
392 |
|
393 if (!in.getter()) { |
|
394 out.setGetter(nullptr); |
|
395 } else if (in.attrs() & JSPROP_GETTER) { |
|
396 Rooted<JSObject*> getter(cx); |
|
397 if (!unwrap(cx, in.getter(), &getter)) |
|
398 return false; |
|
399 out.setGetter(JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter.get())); |
|
400 } else { |
|
401 if (in.getter() == DefaultPropertyOp) |
|
402 out.setGetter(JS_PropertyStub); |
|
403 else |
|
404 out.setGetter(UnknownPropertyStub); |
|
405 } |
|
406 |
|
407 if (!in.setter()) { |
|
408 out.setSetter(nullptr); |
|
409 } else if (in.attrs() & JSPROP_SETTER) { |
|
410 Rooted<JSObject*> setter(cx); |
|
411 if (!unwrap(cx, in.setter(), &setter)) |
|
412 return false; |
|
413 out.setSetter(JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter.get())); |
|
414 } else { |
|
415 if (in.setter() == DefaultPropertyOp) |
|
416 out.setSetter(JS_StrictPropertyStub); |
|
417 else if (in.setter() == GetterOnlyPropertyStub) |
|
418 out.setSetter(js_GetterOnlyPropertyStub); |
|
419 else |
|
420 out.setSetter(UnknownStrictPropertyStub); |
|
421 } |
|
422 |
|
423 return true; |
|
424 } |
|
425 |
|
426 bool |
|
427 CpowIdHolder::ToObject(JSContext *cx, JS::MutableHandleObject objp) |
|
428 { |
|
429 return js_->Unwrap(cx, cpows_, objp); |
|
430 } |
|
431 |
|
432 bool |
|
433 JavaScriptShared::Unwrap(JSContext *cx, const InfallibleTArray<CpowEntry> &aCpows, |
|
434 JS::MutableHandleObject objp) |
|
435 { |
|
436 objp.set(nullptr); |
|
437 |
|
438 if (!aCpows.Length()) |
|
439 return true; |
|
440 |
|
441 RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); |
|
442 if (!obj) |
|
443 return false; |
|
444 |
|
445 RootedValue v(cx); |
|
446 RootedString str(cx); |
|
447 for (size_t i = 0; i < aCpows.Length(); i++) { |
|
448 const nsString &name = aCpows[i].name(); |
|
449 |
|
450 if (!toValue(cx, aCpows[i].value(), &v)) |
|
451 return false; |
|
452 |
|
453 if (!JS_DefineUCProperty(cx, |
|
454 obj, |
|
455 name.BeginReading(), |
|
456 name.Length(), |
|
457 v, |
|
458 nullptr, |
|
459 nullptr, |
|
460 JSPROP_ENUMERATE)) |
|
461 { |
|
462 return false; |
|
463 } |
|
464 } |
|
465 |
|
466 objp.set(obj); |
|
467 return true; |
|
468 } |
|
469 |
|
470 bool |
|
471 JavaScriptShared::Wrap(JSContext *cx, HandleObject aObj, InfallibleTArray<CpowEntry> *outCpows) |
|
472 { |
|
473 if (!aObj) |
|
474 return true; |
|
475 |
|
476 AutoIdArray ids(cx, JS_Enumerate(cx, aObj)); |
|
477 if (!ids) |
|
478 return false; |
|
479 |
|
480 RootedId id(cx); |
|
481 RootedValue v(cx); |
|
482 for (size_t i = 0; i < ids.length(); i++) { |
|
483 id = ids[i]; |
|
484 |
|
485 nsString str; |
|
486 if (!convertIdToGeckoString(cx, id, &str)) |
|
487 return false; |
|
488 |
|
489 if (!JS_GetPropertyById(cx, aObj, id, &v)) |
|
490 return false; |
|
491 |
|
492 JSVariant var; |
|
493 if (!toVariant(cx, v, &var)) |
|
494 return false; |
|
495 |
|
496 outCpows->AppendElement(CpowEntry(str, var)); |
|
497 } |
|
498 |
|
499 return true; |
|
500 } |
|
501 |