/src/mozilla-central/js/ipc/WrapperAnswer.cpp
Line | Count | Source (jump to first uncovered line) |
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 "WrapperAnswer.h" |
9 | | #include "JavaScriptLogging.h" |
10 | | #include "mozilla/dom/ContentChild.h" |
11 | | #include "mozilla/dom/BindingUtils.h" |
12 | | #include "mozilla/dom/ScriptSettings.h" |
13 | | #include "xpcprivate.h" |
14 | | #include "js/Class.h" |
15 | | #include "jsfriendapi.h" |
16 | | |
17 | | using namespace JS; |
18 | | using namespace mozilla; |
19 | | using namespace mozilla::jsipc; |
20 | | |
21 | | // Note - Using AutoJSAPI (rather than AutoEntryScript) for a trap means |
22 | | // that we don't expect it to run script. For most of these traps that will only |
23 | | // happen if the target is a scripted proxy, which is probably something that we |
24 | | // don't want to support over CPOWs. When enough code is fixed up, the long-term |
25 | | // plan is to have the JS engine throw if it encounters script when it isn't |
26 | | // expecting it. |
27 | | using mozilla::dom::AutoJSAPI; |
28 | | using mozilla::dom::AutoEntryScript; |
29 | | |
30 | | using xpc::IsInAutomation; |
31 | | |
32 | | static void |
33 | | MaybeForceDebugGC() |
34 | 0 | { |
35 | 0 | static bool sEnvVarInitialized = false; |
36 | 0 | static bool sDebugGCs = false; |
37 | 0 |
|
38 | 0 | if (!sEnvVarInitialized) { |
39 | 0 | sDebugGCs = !!PR_GetEnv("MOZ_DEBUG_DEAD_CPOWS"); |
40 | 0 | } |
41 | 0 |
|
42 | 0 | if (sDebugGCs) { |
43 | 0 | JSContext* cx = XPCJSContext::Get()->Context(); |
44 | 0 | PrepareForFullGC(cx); |
45 | 0 | NonIncrementalGC(cx, GC_NORMAL, gcreason::COMPONENT_UTILS); |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | | bool |
50 | | WrapperAnswer::fail(AutoJSAPI& jsapi, ReturnStatus* rs) |
51 | 0 | { |
52 | 0 | // By default, we set |undefined| unless we can get a more meaningful |
53 | 0 | // exception. |
54 | 0 | *rs = ReturnStatus(ReturnException(JSVariant(UndefinedVariant()))); |
55 | 0 |
|
56 | 0 | // Note we always return true from this function, since this propagates |
57 | 0 | // to the IPC code, and we don't want a JS failure to cause the death |
58 | 0 | // of the child process. |
59 | 0 |
|
60 | 0 | JSContext* cx = jsapi.cx(); |
61 | 0 | RootedValue exn(cx); |
62 | 0 | if (!jsapi.HasException()) { |
63 | 0 | return true; |
64 | 0 | } |
65 | 0 | |
66 | 0 | if (!jsapi.StealException(&exn)) { |
67 | 0 | return true; |
68 | 0 | } |
69 | 0 | |
70 | 0 | // If this fails, we still don't want to exit. Just return an invalid |
71 | 0 | // exception. |
72 | 0 | (void) toVariant(cx, exn, &rs->get_ReturnException().exn()); |
73 | 0 | return true; |
74 | 0 | } |
75 | | |
76 | | bool |
77 | | WrapperAnswer::ok(ReturnStatus* rs) |
78 | 0 | { |
79 | 0 | *rs = ReturnStatus(ReturnSuccess()); |
80 | 0 | return true; |
81 | 0 | } |
82 | | |
83 | | bool |
84 | | WrapperAnswer::ok(ReturnStatus* rs, const JS::ObjectOpResult& result) |
85 | 0 | { |
86 | 0 | *rs = result |
87 | 0 | ? ReturnStatus(ReturnSuccess()) |
88 | 0 | : ReturnStatus(ReturnObjectOpResult(result.failureCode())); |
89 | 0 | return true; |
90 | 0 | } |
91 | | |
92 | | bool |
93 | | WrapperAnswer::deadCPOW(AutoJSAPI& jsapi, ReturnStatus* rs) |
94 | 0 | { |
95 | 0 | JSContext* cx = jsapi.cx(); |
96 | 0 | JS_ClearPendingException(cx); |
97 | 0 | *rs = ReturnStatus(ReturnDeadCPOW()); |
98 | 0 | return true; |
99 | 0 | } |
100 | | |
101 | | bool |
102 | | WrapperAnswer::RecvPreventExtensions(const ObjectId& objId, ReturnStatus* rs) |
103 | 0 | { |
104 | 0 | if (!IsInAutomation()) { |
105 | 0 | return false; |
106 | 0 | } |
107 | 0 | |
108 | 0 | MaybeForceDebugGC(); |
109 | 0 |
|
110 | 0 | AutoJSAPI jsapi; |
111 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
112 | 0 | return false; |
113 | 0 | } |
114 | 0 | JSContext* cx = jsapi.cx(); |
115 | 0 |
|
116 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
117 | 0 | if (!obj) { |
118 | 0 | return deadCPOW(jsapi, rs); |
119 | 0 | } |
120 | 0 | |
121 | 0 | ObjectOpResult success; |
122 | 0 | if (!JS_PreventExtensions(cx, obj, success)) { |
123 | 0 | return fail(jsapi, rs); |
124 | 0 | } |
125 | 0 | |
126 | 0 | LOG("%s.preventExtensions()", ReceiverObj(objId)); |
127 | 0 | return ok(rs, success); |
128 | 0 | } |
129 | | |
130 | | static void |
131 | | EmptyDesc(PPropertyDescriptor* desc) |
132 | 0 | { |
133 | 0 | desc->obj() = LocalObject(0); |
134 | 0 | desc->attrs() = 0; |
135 | 0 | desc->value() = UndefinedVariant(); |
136 | 0 | desc->getter() = 0; |
137 | 0 | desc->setter() = 0; |
138 | 0 | } |
139 | | |
140 | | bool |
141 | | WrapperAnswer::RecvGetOwnPropertyDescriptor(const ObjectId& objId, const JSIDVariant& idVar, |
142 | | ReturnStatus* rs, PPropertyDescriptor* out) |
143 | 0 | { |
144 | 0 | if (!IsInAutomation()) { |
145 | 0 | return false; |
146 | 0 | } |
147 | 0 | |
148 | 0 | MaybeForceDebugGC(); |
149 | 0 |
|
150 | 0 | AutoJSAPI jsapi; |
151 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
152 | 0 | return false; |
153 | 0 | } |
154 | 0 | JSContext* cx = jsapi.cx(); |
155 | 0 | EmptyDesc(out); |
156 | 0 |
|
157 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
158 | 0 | if (!obj) { |
159 | 0 | return deadCPOW(jsapi, rs); |
160 | 0 | } |
161 | 0 | |
162 | 0 | LOG("%s.getOwnPropertyDescriptor(%s)", ReceiverObj(objId), Identifier(idVar)); |
163 | 0 |
|
164 | 0 | RootedId id(cx); |
165 | 0 | if (!fromJSIDVariant(cx, idVar, &id)) { |
166 | 0 | return fail(jsapi, rs); |
167 | 0 | } |
168 | 0 | |
169 | 0 | Rooted<PropertyDescriptor> desc(cx); |
170 | 0 | if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) { |
171 | 0 | return fail(jsapi, rs); |
172 | 0 | } |
173 | 0 | |
174 | 0 | if (!fromDescriptor(cx, desc, out)) { |
175 | 0 | return fail(jsapi, rs); |
176 | 0 | } |
177 | 0 | |
178 | 0 | return ok(rs); |
179 | 0 | } |
180 | | |
181 | | bool |
182 | | WrapperAnswer::RecvDefineProperty(const ObjectId& objId, const JSIDVariant& idVar, |
183 | | const PPropertyDescriptor& descriptor, ReturnStatus* rs) |
184 | 0 | { |
185 | 0 | if (!IsInAutomation()) { |
186 | 0 | return false; |
187 | 0 | } |
188 | 0 | |
189 | 0 | MaybeForceDebugGC(); |
190 | 0 |
|
191 | 0 | AutoJSAPI jsapi; |
192 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
193 | 0 | return false; |
194 | 0 | } |
195 | 0 | JSContext* cx = jsapi.cx(); |
196 | 0 |
|
197 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
198 | 0 | if (!obj) { |
199 | 0 | return deadCPOW(jsapi, rs); |
200 | 0 | } |
201 | 0 | |
202 | 0 | LOG("define %s[%s]", ReceiverObj(objId), Identifier(idVar)); |
203 | 0 |
|
204 | 0 | RootedId id(cx); |
205 | 0 | if (!fromJSIDVariant(cx, idVar, &id)) { |
206 | 0 | return fail(jsapi, rs); |
207 | 0 | } |
208 | 0 | |
209 | 0 | Rooted<PropertyDescriptor> desc(cx); |
210 | 0 | if (!toDescriptor(cx, descriptor, &desc)) { |
211 | 0 | return fail(jsapi, rs); |
212 | 0 | } |
213 | 0 | |
214 | 0 | ObjectOpResult success; |
215 | 0 | if (!JS_DefinePropertyById(cx, obj, id, desc, success)) { |
216 | 0 | return fail(jsapi, rs); |
217 | 0 | } |
218 | 0 | return ok(rs, success); |
219 | 0 | } |
220 | | |
221 | | bool |
222 | | WrapperAnswer::RecvDelete(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs) |
223 | 0 | { |
224 | 0 | if (!IsInAutomation()) { |
225 | 0 | return false; |
226 | 0 | } |
227 | 0 | |
228 | 0 | MaybeForceDebugGC(); |
229 | 0 |
|
230 | 0 | AutoJSAPI jsapi; |
231 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
232 | 0 | return false; |
233 | 0 | } |
234 | 0 | JSContext* cx = jsapi.cx(); |
235 | 0 |
|
236 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
237 | 0 | if (!obj) { |
238 | 0 | return deadCPOW(jsapi, rs); |
239 | 0 | } |
240 | 0 | |
241 | 0 | LOG("delete %s[%s]", ReceiverObj(objId), Identifier(idVar)); |
242 | 0 |
|
243 | 0 | RootedId id(cx); |
244 | 0 | if (!fromJSIDVariant(cx, idVar, &id)) { |
245 | 0 | return fail(jsapi, rs); |
246 | 0 | } |
247 | 0 | |
248 | 0 | ObjectOpResult success; |
249 | 0 | if (!JS_DeletePropertyById(cx, obj, id, success)) { |
250 | 0 | return fail(jsapi, rs); |
251 | 0 | } |
252 | 0 | return ok(rs, success); |
253 | 0 | } |
254 | | |
255 | | bool |
256 | | WrapperAnswer::RecvHas(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs, |
257 | | bool* foundp) |
258 | 0 | { |
259 | 0 | if (!IsInAutomation()) { |
260 | 0 | return false; |
261 | 0 | } |
262 | 0 | |
263 | 0 | MaybeForceDebugGC(); |
264 | 0 |
|
265 | 0 | AutoJSAPI jsapi; |
266 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
267 | 0 | return false; |
268 | 0 | } |
269 | 0 | JSContext* cx = jsapi.cx(); |
270 | 0 | *foundp = false; |
271 | 0 |
|
272 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
273 | 0 | if (!obj) { |
274 | 0 | return deadCPOW(jsapi, rs); |
275 | 0 | } |
276 | 0 | |
277 | 0 | LOG("%s.has(%s)", ReceiverObj(objId), Identifier(idVar)); |
278 | 0 |
|
279 | 0 | RootedId id(cx); |
280 | 0 | if (!fromJSIDVariant(cx, idVar, &id)) { |
281 | 0 | return fail(jsapi, rs); |
282 | 0 | } |
283 | 0 | |
284 | 0 | if (!JS_HasPropertyById(cx, obj, id, foundp)) { |
285 | 0 | return fail(jsapi, rs); |
286 | 0 | } |
287 | 0 | return ok(rs); |
288 | 0 | } |
289 | | |
290 | | bool |
291 | | WrapperAnswer::RecvHasOwn(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs, |
292 | | bool* foundp) |
293 | 0 | { |
294 | 0 | if (!IsInAutomation()) { |
295 | 0 | return false; |
296 | 0 | } |
297 | 0 | |
298 | 0 | MaybeForceDebugGC(); |
299 | 0 |
|
300 | 0 | AutoJSAPI jsapi; |
301 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
302 | 0 | return false; |
303 | 0 | } |
304 | 0 | JSContext* cx = jsapi.cx(); |
305 | 0 | *foundp = false; |
306 | 0 |
|
307 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
308 | 0 | if (!obj) { |
309 | 0 | return deadCPOW(jsapi, rs); |
310 | 0 | } |
311 | 0 | |
312 | 0 | LOG("%s.hasOwn(%s)", ReceiverObj(objId), Identifier(idVar)); |
313 | 0 |
|
314 | 0 | RootedId id(cx); |
315 | 0 | if (!fromJSIDVariant(cx, idVar, &id)) { |
316 | 0 | return fail(jsapi, rs); |
317 | 0 | } |
318 | 0 | |
319 | 0 | if (!JS_HasOwnPropertyById(cx, obj, id, foundp)) { |
320 | 0 | return fail(jsapi, rs); |
321 | 0 | } |
322 | 0 | return ok(rs); |
323 | 0 | } |
324 | | |
325 | | bool |
326 | | WrapperAnswer::RecvGet(const ObjectId& objId, const JSVariant& receiverVar, |
327 | | const JSIDVariant& idVar, ReturnStatus* rs, JSVariant* result) |
328 | 0 | { |
329 | 0 | if (!IsInAutomation()) { |
330 | 0 | return false; |
331 | 0 | } |
332 | 0 | |
333 | 0 | MaybeForceDebugGC(); |
334 | 0 |
|
335 | 0 | // We may run scripted getters. |
336 | 0 | AutoEntryScript aes(scopeForTargetObjects(), |
337 | 0 | "Cross-Process Object Wrapper 'get'"); |
338 | 0 | JSContext* cx = aes.cx(); |
339 | 0 |
|
340 | 0 | // The outparam will be written to the buffer, so it must be set even if |
341 | 0 | // the parent won't read it. |
342 | 0 | *result = UndefinedVariant(); |
343 | 0 |
|
344 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
345 | 0 | if (!obj) { |
346 | 0 | return deadCPOW(aes, rs); |
347 | 0 | } |
348 | 0 | |
349 | 0 | RootedValue receiver(cx); |
350 | 0 | if (!fromVariant(cx, receiverVar, &receiver)) { |
351 | 0 | return fail(aes, rs); |
352 | 0 | } |
353 | 0 | |
354 | 0 | RootedId id(cx); |
355 | 0 | if (!fromJSIDVariant(cx, idVar, &id)) { |
356 | 0 | return fail(aes, rs); |
357 | 0 | } |
358 | 0 | |
359 | 0 | JS::RootedValue val(cx); |
360 | 0 | if (!JS_ForwardGetPropertyTo(cx, obj, id, receiver, &val)) { |
361 | 0 | return fail(aes, rs); |
362 | 0 | } |
363 | 0 | |
364 | 0 | if (!toVariant(cx, val, result)) { |
365 | 0 | return fail(aes, rs); |
366 | 0 | } |
367 | 0 | |
368 | 0 | LOG("get %s.%s = %s", ReceiverObj(objId), Identifier(idVar), OutVariant(*result)); |
369 | 0 |
|
370 | 0 | return ok(rs); |
371 | 0 | } |
372 | | |
373 | | bool |
374 | | WrapperAnswer::RecvSet(const ObjectId& objId, const JSIDVariant& idVar, const JSVariant& value, |
375 | | const JSVariant& receiverVar, ReturnStatus* rs) |
376 | 0 | { |
377 | 0 | if (!IsInAutomation()) { |
378 | 0 | return false; |
379 | 0 | } |
380 | 0 | |
381 | 0 | MaybeForceDebugGC(); |
382 | 0 |
|
383 | 0 | // We may run scripted setters. |
384 | 0 | AutoEntryScript aes(scopeForTargetObjects(), |
385 | 0 | "Cross-Process Object Wrapper 'set'"); |
386 | 0 | JSContext* cx = aes.cx(); |
387 | 0 |
|
388 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
389 | 0 | if (!obj) { |
390 | 0 | return deadCPOW(aes, rs); |
391 | 0 | } |
392 | 0 | |
393 | 0 | LOG("set %s[%s] = %s", ReceiverObj(objId), Identifier(idVar), InVariant(value)); |
394 | 0 |
|
395 | 0 | RootedId id(cx); |
396 | 0 | if (!fromJSIDVariant(cx, idVar, &id)) { |
397 | 0 | return fail(aes, rs); |
398 | 0 | } |
399 | 0 | |
400 | 0 | RootedValue val(cx); |
401 | 0 | if (!fromVariant(cx, value, &val)) { |
402 | 0 | return fail(aes, rs); |
403 | 0 | } |
404 | 0 | |
405 | 0 | RootedValue receiver(cx); |
406 | 0 | if (!fromVariant(cx, receiverVar, &receiver)) { |
407 | 0 | return fail(aes, rs); |
408 | 0 | } |
409 | 0 | |
410 | 0 | ObjectOpResult result; |
411 | 0 | if (!JS_ForwardSetPropertyTo(cx, obj, id, val, receiver, result)) { |
412 | 0 | return fail(aes, rs); |
413 | 0 | } |
414 | 0 | |
415 | 0 | return ok(rs, result); |
416 | 0 | } |
417 | | |
418 | | bool |
419 | | WrapperAnswer::RecvIsExtensible(const ObjectId& objId, ReturnStatus* rs, bool* result) |
420 | 0 | { |
421 | 0 | if (!IsInAutomation()) { |
422 | 0 | return false; |
423 | 0 | } |
424 | 0 | |
425 | 0 | MaybeForceDebugGC(); |
426 | 0 |
|
427 | 0 | AutoJSAPI jsapi; |
428 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
429 | 0 | return false; |
430 | 0 | } |
431 | 0 | JSContext* cx = jsapi.cx(); |
432 | 0 | *result = false; |
433 | 0 |
|
434 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
435 | 0 | if (!obj) { |
436 | 0 | return deadCPOW(jsapi, rs); |
437 | 0 | } |
438 | 0 | |
439 | 0 | LOG("%s.isExtensible()", ReceiverObj(objId)); |
440 | 0 |
|
441 | 0 | bool extensible; |
442 | 0 | if (!JS_IsExtensible(cx, obj, &extensible)) { |
443 | 0 | return fail(jsapi, rs); |
444 | 0 | } |
445 | 0 | |
446 | 0 | *result = !!extensible; |
447 | 0 | return ok(rs); |
448 | 0 | } |
449 | | |
450 | | bool |
451 | | WrapperAnswer::RecvCallOrConstruct(const ObjectId& objId, |
452 | | InfallibleTArray<JSParam>&& argv, |
453 | | const bool& construct, |
454 | | ReturnStatus* rs, |
455 | | JSVariant* result, |
456 | | nsTArray<JSParam>* outparams) |
457 | 0 | { |
458 | 0 | if (!IsInAutomation()) { |
459 | 0 | return false; |
460 | 0 | } |
461 | 0 | |
462 | 0 | MaybeForceDebugGC(); |
463 | 0 |
|
464 | 0 | AutoEntryScript aes(scopeForTargetObjects(), |
465 | 0 | "Cross-Process Object Wrapper call/construct"); |
466 | 0 | JSContext* cx = aes.cx(); |
467 | 0 |
|
468 | 0 | // The outparam will be written to the buffer, so it must be set even if |
469 | 0 | // the parent won't read it. |
470 | 0 | *result = UndefinedVariant(); |
471 | 0 |
|
472 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
473 | 0 | if (!obj) { |
474 | 0 | return deadCPOW(aes, rs); |
475 | 0 | } |
476 | 0 | |
477 | 0 | MOZ_ASSERT(argv.Length() >= 2); |
478 | 0 |
|
479 | 0 | RootedValue objv(cx); |
480 | 0 | if (!fromVariant(cx, argv[0], &objv)) { |
481 | 0 | return fail(aes, rs); |
482 | 0 | } |
483 | 0 | |
484 | 0 | *result = JSVariant(UndefinedVariant()); |
485 | 0 |
|
486 | 0 | AutoValueVector vals(cx); |
487 | 0 | AutoValueVector outobjects(cx); |
488 | 0 | for (size_t i = 0; i < argv.Length(); i++) { |
489 | 0 | if (argv[i].type() == JSParam::Tvoid_t) { |
490 | 0 | // This is an outparam. |
491 | 0 | RootedObject obj(cx, xpc::NewOutObject(cx)); |
492 | 0 | if (!obj) { |
493 | 0 | return fail(aes, rs); |
494 | 0 | } |
495 | 0 | if (!outobjects.append(ObjectValue(*obj))) { |
496 | 0 | return fail(aes, rs); |
497 | 0 | } |
498 | 0 | if (!vals.append(ObjectValue(*obj))) { |
499 | 0 | return fail(aes, rs); |
500 | 0 | } |
501 | 0 | } else { |
502 | 0 | RootedValue v(cx); |
503 | 0 | if (!fromVariant(cx, argv[i].get_JSVariant(), &v)) { |
504 | 0 | return fail(aes, rs); |
505 | 0 | } |
506 | 0 | if (!vals.append(v)) { |
507 | 0 | return fail(aes, rs); |
508 | 0 | } |
509 | 0 | } |
510 | 0 | } |
511 | 0 |
|
512 | 0 | RootedValue rval(cx); |
513 | 0 | { |
514 | 0 | HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2); |
515 | 0 | if (construct) { |
516 | 0 | RootedObject obj(cx); |
517 | 0 | if (!JS::Construct(cx, vals[0], args, &obj)) { |
518 | 0 | return fail(aes, rs); |
519 | 0 | } |
520 | 0 | rval.setObject(*obj); |
521 | 0 | } else { |
522 | 0 | if(!JS::Call(cx, vals[1], vals[0], args, &rval)) |
523 | 0 | return fail(aes, rs); |
524 | 0 | } |
525 | 0 | } |
526 | 0 | |
527 | 0 | if (!toVariant(cx, rval, result)) { |
528 | 0 | return fail(aes, rs); |
529 | 0 | } |
530 | 0 | |
531 | 0 | // Prefill everything with a dummy jsval. |
532 | 0 | for (size_t i = 0; i < outobjects.length(); i++) { |
533 | 0 | outparams->AppendElement(JSParam(void_t())); |
534 | 0 | } |
535 | 0 |
|
536 | 0 | // Go through each argument that was an outparam, retrieve the "value" |
537 | 0 | // field, and add it to a temporary list. We need to do this separately |
538 | 0 | // because the outparams vector is not rooted. |
539 | 0 | vals.clear(); |
540 | 0 | for (size_t i = 0; i < outobjects.length(); i++) { |
541 | 0 | RootedObject obj(cx, &outobjects[i].toObject()); |
542 | 0 |
|
543 | 0 | RootedValue v(cx); |
544 | 0 | bool found; |
545 | 0 | if (JS_HasProperty(cx, obj, "value", &found)) { |
546 | 0 | if (!JS_GetProperty(cx, obj, "value", &v)) { |
547 | 0 | return fail(aes, rs); |
548 | 0 | } |
549 | 0 | } else { |
550 | 0 | v = UndefinedValue(); |
551 | 0 | } |
552 | 0 | if (!vals.append(v)) { |
553 | 0 | return fail(aes, rs); |
554 | 0 | } |
555 | 0 | } |
556 | 0 |
|
557 | 0 | // Copy the outparams. If any outparam is already set to a void_t, we |
558 | 0 | // treat this as the outparam never having been set. |
559 | 0 | for (size_t i = 0; i < vals.length(); i++) { |
560 | 0 | JSVariant variant; |
561 | 0 | if (!toVariant(cx, vals[i], &variant)) { |
562 | 0 | return fail(aes, rs); |
563 | 0 | } |
564 | 0 | outparams->ReplaceElementAt(i, JSParam(variant)); |
565 | 0 | } |
566 | 0 |
|
567 | 0 | LOG("%s.call(%s) = %s", ReceiverObj(objId), argv, OutVariant(*result)); |
568 | 0 |
|
569 | 0 | return ok(rs); |
570 | 0 | } |
571 | | |
572 | | bool |
573 | | WrapperAnswer::RecvHasInstance(const ObjectId& objId, const JSVariant& vVar, ReturnStatus* rs, bool* bp) |
574 | 0 | { |
575 | 0 | if (!IsInAutomation()) { |
576 | 0 | return false; |
577 | 0 | } |
578 | 0 | |
579 | 0 | MaybeForceDebugGC(); |
580 | 0 |
|
581 | 0 | AutoJSAPI jsapi; |
582 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
583 | 0 | return false; |
584 | 0 | } |
585 | 0 | JSContext* cx = jsapi.cx(); |
586 | 0 |
|
587 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
588 | 0 | if (!obj) { |
589 | 0 | return deadCPOW(jsapi, rs); |
590 | 0 | } |
591 | 0 | |
592 | 0 | LOG("%s.hasInstance(%s)", ReceiverObj(objId), InVariant(vVar)); |
593 | 0 |
|
594 | 0 | RootedValue val(cx); |
595 | 0 | if (!fromVariant(cx, vVar, &val)) { |
596 | 0 | return fail(jsapi, rs); |
597 | 0 | } |
598 | 0 | |
599 | 0 | if (!JS_HasInstance(cx, obj, val, bp)) { |
600 | 0 | return fail(jsapi, rs); |
601 | 0 | } |
602 | 0 | |
603 | 0 | return ok(rs); |
604 | 0 | } |
605 | | |
606 | | bool |
607 | | WrapperAnswer::RecvGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs, |
608 | | uint32_t* classValue) |
609 | 0 | { |
610 | 0 | if (!IsInAutomation()) { |
611 | 0 | return false; |
612 | 0 | } |
613 | 0 | |
614 | 0 | MaybeForceDebugGC(); |
615 | 0 |
|
616 | 0 | *classValue = uint32_t(js::ESClass::Other); |
617 | 0 |
|
618 | 0 | AutoJSAPI jsapi; |
619 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
620 | 0 | return false; |
621 | 0 | } |
622 | 0 | JSContext* cx = jsapi.cx(); |
623 | 0 |
|
624 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
625 | 0 | if (!obj) { |
626 | 0 | return deadCPOW(jsapi, rs); |
627 | 0 | } |
628 | 0 | |
629 | 0 | LOG("%s.getBuiltinClass()", ReceiverObj(objId)); |
630 | 0 |
|
631 | 0 | js::ESClass cls; |
632 | 0 | if (!js::GetBuiltinClass(cx, obj, &cls)) { |
633 | 0 | return fail(jsapi, rs); |
634 | 0 | } |
635 | 0 | |
636 | 0 | *classValue = uint32_t(cls); |
637 | 0 | return ok(rs); |
638 | 0 | } |
639 | | |
640 | | bool |
641 | | WrapperAnswer::RecvIsArray(const ObjectId& objId, ReturnStatus* rs, |
642 | | uint32_t* ans) |
643 | 0 | { |
644 | 0 | if (!IsInAutomation()) { |
645 | 0 | return false; |
646 | 0 | } |
647 | 0 | |
648 | 0 | MaybeForceDebugGC(); |
649 | 0 |
|
650 | 0 | *ans = uint32_t(IsArrayAnswer::NotArray); |
651 | 0 |
|
652 | 0 | AutoJSAPI jsapi; |
653 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
654 | 0 | return false; |
655 | 0 | } |
656 | 0 | JSContext* cx = jsapi.cx(); |
657 | 0 |
|
658 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
659 | 0 | if (!obj) { |
660 | 0 | return deadCPOW(jsapi, rs); |
661 | 0 | } |
662 | 0 | |
663 | 0 | LOG("%s.isArray()", ReceiverObj(objId)); |
664 | 0 |
|
665 | 0 | IsArrayAnswer answer; |
666 | 0 | if (!JS::IsArray(cx, obj, &answer)) { |
667 | 0 | return fail(jsapi, rs); |
668 | 0 | } |
669 | 0 | |
670 | 0 | *ans = uint32_t(answer); |
671 | 0 | return ok(rs); |
672 | 0 | } |
673 | | |
674 | | bool |
675 | | WrapperAnswer::RecvClassName(const ObjectId& objId, nsCString* name) |
676 | 0 | { |
677 | 0 | if (!IsInAutomation()) { |
678 | 0 | return false; |
679 | 0 | } |
680 | 0 | |
681 | 0 | MaybeForceDebugGC(); |
682 | 0 |
|
683 | 0 | AutoJSAPI jsapi; |
684 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
685 | 0 | return false; |
686 | 0 | } |
687 | 0 | JSContext* cx = jsapi.cx(); |
688 | 0 |
|
689 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
690 | 0 | if (!obj) { |
691 | 0 | // This is very unfortunate, but we have no choice. |
692 | 0 | *name = "<dead CPOW>"; |
693 | 0 | return true; |
694 | 0 | } |
695 | 0 | |
696 | 0 | LOG("%s.className()", ReceiverObj(objId)); |
697 | 0 |
|
698 | 0 | *name = js::ObjectClassName(cx, obj); |
699 | 0 | return true; |
700 | 0 | } |
701 | | |
702 | | bool |
703 | | WrapperAnswer::RecvGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result) |
704 | 0 | { |
705 | 0 | if (!IsInAutomation()) { |
706 | 0 | return false; |
707 | 0 | } |
708 | 0 | |
709 | 0 | MaybeForceDebugGC(); |
710 | 0 |
|
711 | 0 | *result = NullVariant(); |
712 | 0 |
|
713 | 0 | AutoJSAPI jsapi; |
714 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
715 | 0 | return false; |
716 | 0 | } |
717 | 0 | JSContext* cx = jsapi.cx(); |
718 | 0 |
|
719 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
720 | 0 | if (!obj) { |
721 | 0 | return deadCPOW(jsapi, rs); |
722 | 0 | } |
723 | 0 | |
724 | 0 | JS::RootedObject proto(cx); |
725 | 0 | if (!JS_GetPrototype(cx, obj, &proto)) { |
726 | 0 | return fail(jsapi, rs); |
727 | 0 | } |
728 | 0 | |
729 | 0 | if (!toObjectOrNullVariant(cx, proto, result)) { |
730 | 0 | return fail(jsapi, rs); |
731 | 0 | } |
732 | 0 | |
733 | 0 | LOG("getPrototype(%s)", ReceiverObj(objId)); |
734 | 0 |
|
735 | 0 | return ok(rs); |
736 | 0 | } |
737 | | |
738 | | bool |
739 | | WrapperAnswer::RecvGetPrototypeIfOrdinary(const ObjectId& objId, ReturnStatus* rs, bool* isOrdinary, |
740 | | ObjectOrNullVariant* result) |
741 | 0 | { |
742 | 0 | if (!IsInAutomation()) { |
743 | 0 | return false; |
744 | 0 | } |
745 | 0 | |
746 | 0 | MaybeForceDebugGC(); |
747 | 0 |
|
748 | 0 | *result = NullVariant(); |
749 | 0 | *isOrdinary = false; |
750 | 0 |
|
751 | 0 | AutoJSAPI jsapi; |
752 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
753 | 0 | return false; |
754 | 0 | } |
755 | 0 | JSContext* cx = jsapi.cx(); |
756 | 0 |
|
757 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
758 | 0 | if (!obj) { |
759 | 0 | return deadCPOW(jsapi, rs); |
760 | 0 | } |
761 | 0 | |
762 | 0 | JS::RootedObject proto(cx); |
763 | 0 | if (!JS_GetPrototypeIfOrdinary(cx, obj, isOrdinary, &proto)) { |
764 | 0 | return fail(jsapi, rs); |
765 | 0 | } |
766 | 0 | |
767 | 0 | if (!toObjectOrNullVariant(cx, proto, result)) { |
768 | 0 | return fail(jsapi, rs); |
769 | 0 | } |
770 | 0 | |
771 | 0 | LOG("getPrototypeIfOrdinary(%s)", ReceiverObj(objId)); |
772 | 0 |
|
773 | 0 | return ok(rs); |
774 | 0 | } |
775 | | |
776 | | bool |
777 | | WrapperAnswer::RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs, |
778 | | nsString* source, uint32_t* flags) |
779 | 0 | { |
780 | 0 | if (!IsInAutomation()) { |
781 | 0 | return false; |
782 | 0 | } |
783 | 0 | |
784 | 0 | MaybeForceDebugGC(); |
785 | 0 |
|
786 | 0 | AutoJSAPI jsapi; |
787 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
788 | 0 | return false; |
789 | 0 | } |
790 | 0 | JSContext* cx = jsapi.cx(); |
791 | 0 |
|
792 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
793 | 0 | if (!obj) { |
794 | 0 | return deadCPOW(jsapi, rs); |
795 | 0 | } |
796 | 0 | |
797 | 0 | RootedString sourceJSStr(cx, JS_GetRegExpSource(cx, obj)); |
798 | 0 | if (!sourceJSStr) { |
799 | 0 | return fail(jsapi, rs); |
800 | 0 | } |
801 | 0 | nsAutoJSString sourceStr; |
802 | 0 | if (!sourceStr.init(cx, sourceJSStr)) { |
803 | 0 | return fail(jsapi, rs); |
804 | 0 | } |
805 | 0 | source->Assign(sourceStr); |
806 | 0 |
|
807 | 0 | *flags = JS_GetRegExpFlags(cx, obj); |
808 | 0 |
|
809 | 0 | return ok(rs); |
810 | 0 | } |
811 | | |
812 | | bool |
813 | | WrapperAnswer::RecvGetPropertyKeys(const ObjectId& objId, const uint32_t& flags, |
814 | | ReturnStatus* rs, nsTArray<JSIDVariant>* ids) |
815 | 0 | { |
816 | 0 | if (!IsInAutomation()) { |
817 | 0 | return false; |
818 | 0 | } |
819 | 0 | |
820 | 0 | MaybeForceDebugGC(); |
821 | 0 |
|
822 | 0 | AutoJSAPI jsapi; |
823 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
824 | 0 | return false; |
825 | 0 | } |
826 | 0 | JSContext* cx = jsapi.cx(); |
827 | 0 |
|
828 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
829 | 0 | if (!obj) { |
830 | 0 | return deadCPOW(jsapi, rs); |
831 | 0 | } |
832 | 0 | |
833 | 0 | LOG("%s.getPropertyKeys()", ReceiverObj(objId)); |
834 | 0 |
|
835 | 0 | AutoIdVector props(cx); |
836 | 0 | if (!js::GetPropertyKeys(cx, obj, flags, &props)) { |
837 | 0 | return fail(jsapi, rs); |
838 | 0 | } |
839 | 0 | |
840 | 0 | for (size_t i = 0; i < props.length(); i++) { |
841 | 0 | JSIDVariant id; |
842 | 0 | if (!toJSIDVariant(cx, props[i], &id)) { |
843 | 0 | return fail(jsapi, rs); |
844 | 0 | } |
845 | 0 | |
846 | 0 | ids->AppendElement(id); |
847 | 0 | } |
848 | 0 |
|
849 | 0 | return ok(rs); |
850 | 0 | } |
851 | | |
852 | | bool |
853 | | WrapperAnswer::RecvInstanceOf(const ObjectId& objId, const JSIID& iid, ReturnStatus* rs, |
854 | | bool* instanceof) |
855 | 0 | { |
856 | 0 | if (!IsInAutomation()) { |
857 | 0 | return false; |
858 | 0 | } |
859 | 0 | |
860 | 0 | MaybeForceDebugGC(); |
861 | 0 |
|
862 | 0 | AutoJSAPI jsapi; |
863 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
864 | 0 | return false; |
865 | 0 | } |
866 | 0 | JSContext* cx = jsapi.cx(); |
867 | 0 |
|
868 | 0 | *instanceof = false; |
869 | 0 |
|
870 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
871 | 0 | if (!obj) { |
872 | 0 | return deadCPOW(jsapi, rs); |
873 | 0 | } |
874 | 0 | |
875 | 0 | LOG("%s.instanceOf()", ReceiverObj(objId)); |
876 | 0 |
|
877 | 0 | nsID nsiid; |
878 | 0 | ConvertID(iid, &nsiid); |
879 | 0 |
|
880 | 0 | nsresult rv = xpc::HasInstance(cx, obj, &nsiid, instanceof); |
881 | 0 | if (rv != NS_OK) { |
882 | 0 | return fail(jsapi, rs); |
883 | 0 | } |
884 | 0 | |
885 | 0 | return ok(rs); |
886 | 0 | } |
887 | | |
888 | | bool |
889 | | WrapperAnswer::RecvDOMInstanceOf(const ObjectId& objId, const int& prototypeID, |
890 | | const int& depth, ReturnStatus* rs, bool* instanceof) |
891 | 0 | { |
892 | 0 | if (!IsInAutomation()) { |
893 | 0 | return false; |
894 | 0 | } |
895 | 0 | |
896 | 0 | MaybeForceDebugGC(); |
897 | 0 |
|
898 | 0 | AutoJSAPI jsapi; |
899 | 0 | if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) { |
900 | 0 | return false; |
901 | 0 | } |
902 | 0 | JSContext* cx = jsapi.cx(); |
903 | 0 | *instanceof = false; |
904 | 0 |
|
905 | 0 | RootedObject obj(cx, findObjectById(cx, objId)); |
906 | 0 | if (!obj) { |
907 | 0 | return deadCPOW(jsapi, rs); |
908 | 0 | } |
909 | 0 | |
910 | 0 | LOG("%s.domInstanceOf()", ReceiverObj(objId)); |
911 | 0 |
|
912 | 0 | bool tmp; |
913 | 0 | if (!mozilla::dom::InterfaceHasInstance(cx, prototypeID, depth, obj, &tmp)) { |
914 | 0 | return fail(jsapi, rs); |
915 | 0 | } |
916 | 0 | *instanceof = tmp; |
917 | 0 |
|
918 | 0 | return ok(rs); |
919 | 0 | } |
920 | | |
921 | | bool |
922 | | WrapperAnswer::RecvDropObject(const ObjectId& objId) |
923 | 0 | { |
924 | 0 | JSObject* obj = objects_.findPreserveColor(objId); |
925 | 0 | if (obj) { |
926 | 0 | objectIdMap(objId.hasXrayWaiver()).remove(obj); |
927 | 0 | objects_.remove(objId); |
928 | 0 | } |
929 | 0 | return true; |
930 | 0 | } |