/src/mozilla-central/js/ipc/WrapperOwner.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 "WrapperOwner.h" |
9 | | #include "JavaScriptLogging.h" |
10 | | #include "mozilla/Unused.h" |
11 | | #include "mozilla/dom/BindingUtils.h" |
12 | | #include "jsfriendapi.h" |
13 | | #include "js/CharacterEncoding.h" |
14 | | #include "xpcprivate.h" |
15 | | #include "CPOWTimer.h" |
16 | | #include "WrapperFactory.h" |
17 | | |
18 | | #include "nsIDocShellTreeItem.h" |
19 | | #include "nsIDocument.h" |
20 | | |
21 | | using namespace js; |
22 | | using namespace JS; |
23 | | using namespace mozilla; |
24 | | using namespace mozilla::jsipc; |
25 | | |
26 | | struct AuxCPOWData |
27 | | { |
28 | | ObjectId id; |
29 | | bool isCallable; |
30 | | bool isConstructor; |
31 | | bool isDOMObject; |
32 | | |
33 | | // The object tag is just some auxilliary information that clients can use |
34 | | // however they see fit. |
35 | | nsCString objectTag; |
36 | | |
37 | | // The class name for WrapperOwner::className, below. |
38 | | nsCString className; |
39 | | |
40 | | AuxCPOWData(ObjectId id, |
41 | | bool isCallable, |
42 | | bool isConstructor, |
43 | | bool isDOMObject, |
44 | | const nsACString& objectTag) |
45 | | : id(id), |
46 | | isCallable(isCallable), |
47 | | isConstructor(isConstructor), |
48 | | isDOMObject(isDOMObject), |
49 | | objectTag(objectTag) |
50 | 0 | {} |
51 | | }; |
52 | | |
53 | | WrapperOwner::WrapperOwner() |
54 | | : inactive_(false) |
55 | 0 | { |
56 | 0 | } |
57 | | |
58 | | static inline AuxCPOWData* |
59 | | AuxCPOWDataOf(JSObject* obj) |
60 | 0 | { |
61 | 0 | MOZ_ASSERT(IsCPOW(obj)); |
62 | 0 | return static_cast<AuxCPOWData*>(GetProxyReservedSlot(obj, 1).toPrivate()); |
63 | 0 | } |
64 | | |
65 | | static inline WrapperOwner* |
66 | | OwnerOf(JSObject* obj) |
67 | 0 | { |
68 | 0 | MOZ_ASSERT(IsCPOW(obj)); |
69 | 0 | return reinterpret_cast<WrapperOwner*>(GetProxyReservedSlot(obj, 0).toPrivate()); |
70 | 0 | } |
71 | | |
72 | | ObjectId |
73 | | WrapperOwner::idOfUnchecked(JSObject* obj) |
74 | 0 | { |
75 | 0 | MOZ_ASSERT(IsCPOW(obj)); |
76 | 0 |
|
77 | 0 | AuxCPOWData* aux = AuxCPOWDataOf(obj); |
78 | 0 | MOZ_ASSERT(!aux->id.isNull()); |
79 | 0 | return aux->id; |
80 | 0 | } |
81 | | |
82 | | ObjectId |
83 | | WrapperOwner::idOf(JSObject* obj) |
84 | 0 | { |
85 | 0 | ObjectId objId = idOfUnchecked(obj); |
86 | 0 | MOZ_ASSERT(hasCPOW(objId, obj)); |
87 | 0 | return objId; |
88 | 0 | } |
89 | | |
90 | | class CPOWProxyHandler : public BaseProxyHandler |
91 | | { |
92 | | public: |
93 | | constexpr CPOWProxyHandler() |
94 | 0 | : BaseProxyHandler(&family) {} |
95 | | |
96 | 0 | virtual bool finalizeInBackground(const Value& priv) const override { |
97 | 0 | return false; |
98 | 0 | } |
99 | | |
100 | | virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, |
101 | | MutableHandle<PropertyDescriptor> desc) const override; |
102 | | virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, |
103 | | Handle<PropertyDescriptor> desc, |
104 | | ObjectOpResult& result) const override; |
105 | | virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy, |
106 | | AutoIdVector& props) const override; |
107 | | virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id, |
108 | | ObjectOpResult& result) const override; |
109 | | virtual JSObject* enumerate(JSContext* cx, HandleObject proxy) const override; |
110 | | virtual bool preventExtensions(JSContext* cx, HandleObject proxy, |
111 | | ObjectOpResult& result) const override; |
112 | | virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override; |
113 | | virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override; |
114 | | virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, |
115 | | HandleId id, MutableHandleValue vp) const override; |
116 | | virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v, |
117 | | JS::HandleValue receiver, JS::ObjectOpResult& result) const override; |
118 | | virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; |
119 | | virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; |
120 | | |
121 | | virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override; |
122 | | virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, |
123 | | AutoIdVector& props) const override; |
124 | | virtual bool hasInstance(JSContext* cx, HandleObject proxy, |
125 | | MutableHandleValue v, bool* bp) const override; |
126 | | virtual bool getBuiltinClass(JSContext* cx, HandleObject obj, js::ESClass* cls) const override; |
127 | | virtual bool isArray(JSContext* cx, HandleObject obj, |
128 | | IsArrayAnswer* answer) const override; |
129 | | virtual const char* className(JSContext* cx, HandleObject proxy) const override; |
130 | | virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override; |
131 | | virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override; |
132 | | virtual size_t objectMoved(JSObject* proxy, JSObject* old) const override; |
133 | | virtual bool isCallable(JSObject* obj) const override; |
134 | | virtual bool isConstructor(JSObject* obj) const override; |
135 | | virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override; |
136 | | virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, |
137 | | MutableHandleObject protop) const override; |
138 | | |
139 | | static const char family; |
140 | | static const CPOWProxyHandler singleton; |
141 | | }; |
142 | | |
143 | | const char CPOWProxyHandler::family = 0; |
144 | | const CPOWProxyHandler CPOWProxyHandler::singleton; |
145 | | |
146 | | #define FORWARD(call, args, failRetVal) \ |
147 | 0 | AUTO_PROFILER_LABEL(__func__, JS); \ |
148 | 0 | WrapperOwner* owner = OwnerOf(proxy); \ |
149 | 0 | if (!owner->active()) { \ |
150 | 0 | JS_ReportErrorASCII(cx, "cannot use a CPOW whose process is gone"); \ |
151 | 0 | return failRetVal; \ |
152 | 0 | } \ |
153 | 0 | if (!owner->allowMessage(cx)) { \ |
154 | 0 | return failRetVal; \ |
155 | 0 | } \ |
156 | 0 | { \ |
157 | 0 | CPOWTimer timer(cx); \ |
158 | 0 | return owner->call args; \ |
159 | 0 | } |
160 | | |
161 | | bool |
162 | | CPOWProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, |
163 | | MutableHandle<PropertyDescriptor> desc) const |
164 | 0 | { |
165 | 0 | FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc), false); |
166 | 0 | } |
167 | | |
168 | | bool |
169 | | WrapperOwner::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, |
170 | | MutableHandle<PropertyDescriptor> desc) |
171 | 0 | { |
172 | 0 | ObjectId objId = idOf(proxy); |
173 | 0 |
|
174 | 0 | JSIDVariant idVar; |
175 | 0 | if (!toJSIDVariant(cx, id, &idVar)) { |
176 | 0 | return false; |
177 | 0 | } |
178 | 0 | |
179 | 0 | ReturnStatus status; |
180 | 0 | PPropertyDescriptor result; |
181 | 0 | if (!SendGetOwnPropertyDescriptor(objId, idVar, &status, &result)) { |
182 | 0 | return ipcfail(cx); |
183 | 0 | } |
184 | 0 | |
185 | 0 | LOG_STACK(); |
186 | 0 |
|
187 | 0 | if (!ok(cx, status)) { |
188 | 0 | return false; |
189 | 0 | } |
190 | 0 | |
191 | 0 | return toDescriptor(cx, result, desc); |
192 | 0 | } |
193 | | |
194 | | bool |
195 | | CPOWProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, |
196 | | Handle<PropertyDescriptor> desc, |
197 | | ObjectOpResult& result) const |
198 | 0 | { |
199 | 0 | FORWARD(defineProperty, (cx, proxy, id, desc, result), false); |
200 | 0 | } |
201 | | |
202 | | bool |
203 | | WrapperOwner::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, |
204 | | Handle<PropertyDescriptor> desc, |
205 | | ObjectOpResult& result) |
206 | 0 | { |
207 | 0 | ObjectId objId = idOf(proxy); |
208 | 0 |
|
209 | 0 | JSIDVariant idVar; |
210 | 0 | if (!toJSIDVariant(cx, id, &idVar)) { |
211 | 0 | return false; |
212 | 0 | } |
213 | 0 | |
214 | 0 | PPropertyDescriptor descriptor; |
215 | 0 | if (!fromDescriptor(cx, desc, &descriptor)) { |
216 | 0 | return false; |
217 | 0 | } |
218 | 0 | |
219 | 0 | ReturnStatus status; |
220 | 0 | if (!SendDefineProperty(objId, idVar, descriptor, &status)) { |
221 | 0 | return ipcfail(cx); |
222 | 0 | } |
223 | 0 | |
224 | 0 | LOG_STACK(); |
225 | 0 |
|
226 | 0 | return ok(cx, status, result); |
227 | 0 | } |
228 | | |
229 | | bool |
230 | | CPOWProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, |
231 | | AutoIdVector& props) const |
232 | 0 | { |
233 | 0 | FORWARD(ownPropertyKeys, (cx, proxy, props), false); |
234 | 0 | } |
235 | | |
236 | | bool |
237 | | WrapperOwner::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) |
238 | 0 | { |
239 | 0 | return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); |
240 | 0 | } |
241 | | |
242 | | bool |
243 | | CPOWProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id, |
244 | | ObjectOpResult& result) const |
245 | 0 | { |
246 | 0 | FORWARD(delete_, (cx, proxy, id, result), false); |
247 | 0 | } |
248 | | |
249 | | bool |
250 | | WrapperOwner::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result) |
251 | 0 | { |
252 | 0 | ObjectId objId = idOf(proxy); |
253 | 0 |
|
254 | 0 | JSIDVariant idVar; |
255 | 0 | if (!toJSIDVariant(cx, id, &idVar)) { |
256 | 0 | return false; |
257 | 0 | } |
258 | 0 | |
259 | 0 | ReturnStatus status; |
260 | 0 | if (!SendDelete(objId, idVar, &status)) { |
261 | 0 | return ipcfail(cx); |
262 | 0 | } |
263 | 0 | |
264 | 0 | LOG_STACK(); |
265 | 0 |
|
266 | 0 | return ok(cx, status, result); |
267 | 0 | } |
268 | | |
269 | | JSObject* |
270 | | CPOWProxyHandler::enumerate(JSContext* cx, HandleObject proxy) const |
271 | 0 | { |
272 | 0 | // Using a CPOW for the Iterator would slow down for .. in performance, instead |
273 | 0 | // call the base hook, that will use our implementation of getOwnEnumerablePropertyKeys |
274 | 0 | // and follow the proto chain. |
275 | 0 | return BaseProxyHandler::enumerate(cx, proxy); |
276 | 0 | } |
277 | | |
278 | | bool |
279 | | CPOWProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const |
280 | 0 | { |
281 | 0 | FORWARD(has, (cx, proxy, id, bp), false); |
282 | 0 | } |
283 | | |
284 | | bool |
285 | | WrapperOwner::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) |
286 | 0 | { |
287 | 0 | ObjectId objId = idOf(proxy); |
288 | 0 |
|
289 | 0 | JSIDVariant idVar; |
290 | 0 | if (!toJSIDVariant(cx, id, &idVar)) { |
291 | 0 | return false; |
292 | 0 | } |
293 | 0 | |
294 | 0 | ReturnStatus status; |
295 | 0 | if (!SendHas(objId, idVar, &status, bp)) { |
296 | 0 | return ipcfail(cx); |
297 | 0 | } |
298 | 0 | |
299 | 0 | LOG_STACK(); |
300 | 0 |
|
301 | 0 | return ok(cx, status); |
302 | 0 | } |
303 | | |
304 | | bool |
305 | | CPOWProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const |
306 | 0 | { |
307 | 0 | FORWARD(hasOwn, (cx, proxy, id, bp), false); |
308 | 0 | } |
309 | | |
310 | | bool |
311 | | WrapperOwner::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) |
312 | 0 | { |
313 | 0 | ObjectId objId = idOf(proxy); |
314 | 0 |
|
315 | 0 | JSIDVariant idVar; |
316 | 0 | if (!toJSIDVariant(cx, id, &idVar)) { |
317 | 0 | return false; |
318 | 0 | } |
319 | 0 | |
320 | 0 | ReturnStatus status; |
321 | 0 | if (!SendHasOwn(objId, idVar, &status, bp)) { |
322 | 0 | return ipcfail(cx); |
323 | 0 | } |
324 | 0 | |
325 | 0 | LOG_STACK(); |
326 | 0 |
|
327 | 0 | return !!ok(cx, status); |
328 | 0 | } |
329 | | |
330 | | bool |
331 | | CPOWProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver, |
332 | | HandleId id, MutableHandleValue vp) const |
333 | 0 | { |
334 | 0 | FORWARD(get, (cx, proxy, receiver, id, vp), false); |
335 | 0 | } |
336 | | |
337 | | static bool |
338 | | CPOWDOMQI(JSContext* cx, unsigned argc, Value* vp) |
339 | 0 | { |
340 | 0 | CallArgs args = CallArgsFromVp(argc, vp); |
341 | 0 | if (!args.thisv().isObject() || !IsCPOW(&args.thisv().toObject())) { |
342 | 0 | JS_ReportErrorASCII(cx, "bad this object passed to special QI"); |
343 | 0 | return false; |
344 | 0 | } |
345 | 0 | |
346 | 0 | RootedObject proxy(cx, &args.thisv().toObject()); |
347 | 0 | FORWARD(DOMQI, (cx, proxy, args), false); |
348 | 0 | } |
349 | | |
350 | | static bool |
351 | | CPOWToString(JSContext* cx, unsigned argc, Value* vp) |
352 | 0 | { |
353 | 0 | CallArgs args = CallArgsFromVp(argc, vp); |
354 | 0 | RootedObject callee(cx, &args.callee()); |
355 | 0 | RootedValue cpowValue(cx); |
356 | 0 | if (!JS_GetProperty(cx, callee, "__cpow__", &cpowValue)) { |
357 | 0 | return false; |
358 | 0 | } |
359 | 0 | |
360 | 0 | if (!cpowValue.isObject() || !IsCPOW(&cpowValue.toObject())) { |
361 | 0 | JS_ReportErrorASCII(cx, "CPOWToString called on an incompatible object"); |
362 | 0 | return false; |
363 | 0 | } |
364 | 0 | |
365 | 0 | RootedObject proxy(cx, &cpowValue.toObject()); |
366 | 0 | FORWARD(toString, (cx, proxy, args), false); |
367 | 0 | } |
368 | | |
369 | | bool |
370 | | WrapperOwner::toString(JSContext* cx, HandleObject cpow, JS::CallArgs& args) |
371 | 0 | { |
372 | 0 | // Ask the other side to call its toString method. Update the callee so that |
373 | 0 | // it points to the CPOW and not to the synthesized CPOWToString function. |
374 | 0 | args.setCallee(ObjectValue(*cpow)); |
375 | 0 | if (!callOrConstruct(cx, cpow, args, false)) { |
376 | 0 | return false; |
377 | 0 | } |
378 | 0 | |
379 | 0 | if (!args.rval().isString()) { |
380 | 0 | return true; |
381 | 0 | } |
382 | 0 | |
383 | 0 | RootedString cpowResult(cx, args.rval().toString()); |
384 | 0 | nsAutoJSString toStringResult; |
385 | 0 | if (!toStringResult.init(cx, cpowResult)) { |
386 | 0 | return false; |
387 | 0 | } |
388 | 0 | |
389 | 0 | // We don't want to wrap toString() results for things like the location |
390 | 0 | // object, where toString() is supposed to return a URL and nothing else. |
391 | 0 | nsAutoString result; |
392 | 0 | if (toStringResult[0] == '[') { |
393 | 0 | result.AppendLiteral("[object CPOW "); |
394 | 0 | result += toStringResult; |
395 | 0 | result.AppendLiteral("]"); |
396 | 0 | } else { |
397 | 0 | result += toStringResult; |
398 | 0 | } |
399 | 0 |
|
400 | 0 | JSString* str = JS_NewUCStringCopyN(cx, result.get(), result.Length()); |
401 | 0 | if (!str) { |
402 | 0 | return false; |
403 | 0 | } |
404 | 0 | |
405 | 0 | args.rval().setString(str); |
406 | 0 | return true; |
407 | 0 | } |
408 | | |
409 | | bool |
410 | | WrapperOwner::DOMQI(JSContext* cx, JS::HandleObject proxy, JS::CallArgs& args) |
411 | 0 | { |
412 | 0 | // Someone's calling us, handle nsISupports specially to avoid unnecessary |
413 | 0 | // CPOW traffic. |
414 | 0 | HandleValue id = args[0]; |
415 | 0 | if (id.isObject()) { |
416 | 0 | RootedObject idobj(cx, &id.toObject()); |
417 | 0 | nsCOMPtr<nsIJSID> jsid; |
418 | 0 |
|
419 | 0 | nsresult rv = UnwrapArg<nsIJSID>(cx, idobj, getter_AddRefs(jsid)); |
420 | 0 | if (NS_SUCCEEDED(rv)) { |
421 | 0 | MOZ_ASSERT(jsid, "bad wrapJS"); |
422 | 0 | const nsID* idptr = jsid->GetID(); |
423 | 0 | if (idptr->Equals(NS_GET_IID(nsISupports))) { |
424 | 0 | args.rval().set(args.thisv()); |
425 | 0 | return true; |
426 | 0 | } |
427 | 0 | |
428 | 0 | // Webidl-implemented DOM objects never have nsIClassInfo. |
429 | 0 | if (idptr->Equals(NS_GET_IID(nsIClassInfo))) { |
430 | 0 | return Throw(cx, NS_ERROR_NO_INTERFACE); |
431 | 0 | } |
432 | 0 | } |
433 | 0 | } |
434 | 0 | |
435 | 0 | // It wasn't nsISupports, call into the other process to do the QI for us |
436 | 0 | // (since we don't know what other interfaces our object supports). Note |
437 | 0 | // that we have to use JS_GetPropertyDescriptor here to avoid infinite |
438 | 0 | // recursion back into CPOWDOMQI via WrapperOwner::get(). |
439 | 0 | // We could stash the actual QI function on our own function object to avoid |
440 | 0 | // if we're called multiple times, but since we're transient, there's no |
441 | 0 | // point right now. |
442 | 0 | JS::Rooted<PropertyDescriptor> propDesc(cx); |
443 | 0 | if (!JS_GetPropertyDescriptor(cx, proxy, "QueryInterface", &propDesc)) { |
444 | 0 | return false; |
445 | 0 | } |
446 | 0 | |
447 | 0 | if (!propDesc.value().isObject()) { |
448 | 0 | MOZ_ASSERT_UNREACHABLE("We didn't get QueryInterface off a node"); |
449 | 0 | return Throw(cx, NS_ERROR_UNEXPECTED); |
450 | 0 | } |
451 | 0 | return JS_CallFunctionValue(cx, proxy, propDesc.value(), args, args.rval()); |
452 | 0 | } |
453 | | |
454 | | bool |
455 | | WrapperOwner::get(JSContext* cx, HandleObject proxy, HandleValue receiver, |
456 | | HandleId id, MutableHandleValue vp) |
457 | 0 | { |
458 | 0 | ObjectId objId = idOf(proxy); |
459 | 0 |
|
460 | 0 | JSVariant receiverVar; |
461 | 0 | if (!toVariant(cx, receiver, &receiverVar)) { |
462 | 0 | return false; |
463 | 0 | } |
464 | 0 | |
465 | 0 | JSIDVariant idVar; |
466 | 0 | if (!toJSIDVariant(cx, id, &idVar)) { |
467 | 0 | return false; |
468 | 0 | } |
469 | 0 | |
470 | 0 | AuxCPOWData* data = AuxCPOWDataOf(proxy); |
471 | 0 | if (data->isDOMObject && |
472 | 0 | idVar.type() == JSIDVariant::TnsString && |
473 | 0 | idVar.get_nsString().EqualsLiteral("QueryInterface")) |
474 | 0 | { |
475 | 0 | // Handle QueryInterface on DOM Objects specially since we can assume |
476 | 0 | // certain things about their implementation. |
477 | 0 | RootedFunction qi(cx, JS_NewFunction(cx, CPOWDOMQI, 1, 0, |
478 | 0 | "QueryInterface")); |
479 | 0 | if (!qi) { |
480 | 0 | return false; |
481 | 0 | } |
482 | 0 | |
483 | 0 | vp.set(ObjectValue(*JS_GetFunctionObject(qi))); |
484 | 0 | return true; |
485 | 0 | } |
486 | 0 | |
487 | 0 | JSVariant val; |
488 | 0 | ReturnStatus status; |
489 | 0 | if (!SendGet(objId, receiverVar, idVar, &status, &val)) { |
490 | 0 | return ipcfail(cx); |
491 | 0 | } |
492 | 0 | |
493 | 0 | LOG_STACK(); |
494 | 0 |
|
495 | 0 | if (!ok(cx, status)) { |
496 | 0 | return false; |
497 | 0 | } |
498 | 0 | |
499 | 0 | if (!fromVariant(cx, val, vp)) { |
500 | 0 | return false; |
501 | 0 | } |
502 | 0 | |
503 | 0 | if (idVar.type() == JSIDVariant::TnsString && |
504 | 0 | idVar.get_nsString().EqualsLiteral("toString")) { |
505 | 0 | RootedFunction toString(cx, JS_NewFunction(cx, CPOWToString, 0, 0, |
506 | 0 | "toString")); |
507 | 0 | if (!toString) { |
508 | 0 | return false; |
509 | 0 | } |
510 | 0 | |
511 | 0 | RootedObject toStringObj(cx, JS_GetFunctionObject(toString)); |
512 | 0 |
|
513 | 0 | if (!JS_DefineProperty(cx, toStringObj, "__cpow__", vp, JSPROP_PERMANENT | JSPROP_READONLY)) { |
514 | 0 | return false; |
515 | 0 | } |
516 | 0 | |
517 | 0 | vp.set(ObjectValue(*toStringObj)); |
518 | 0 | } |
519 | 0 |
|
520 | 0 | return true; |
521 | 0 | } |
522 | | |
523 | | bool |
524 | | CPOWProxyHandler::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v, |
525 | | JS::HandleValue receiver, JS::ObjectOpResult& result) const |
526 | 0 | { |
527 | 0 | FORWARD(set, (cx, proxy, id, v, receiver, result), false); |
528 | 0 | } |
529 | | |
530 | | bool |
531 | | WrapperOwner::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v, |
532 | | JS::HandleValue receiver, JS::ObjectOpResult& result) |
533 | 0 | { |
534 | 0 | ObjectId objId = idOf(proxy); |
535 | 0 |
|
536 | 0 | JSIDVariant idVar; |
537 | 0 | if (!toJSIDVariant(cx, id, &idVar)) { |
538 | 0 | return false; |
539 | 0 | } |
540 | 0 | |
541 | 0 | JSVariant val; |
542 | 0 | if (!toVariant(cx, v, &val)) { |
543 | 0 | return false; |
544 | 0 | } |
545 | 0 | |
546 | 0 | JSVariant receiverVar; |
547 | 0 | if (!toVariant(cx, receiver, &receiverVar)) { |
548 | 0 | return false; |
549 | 0 | } |
550 | 0 | |
551 | 0 | ReturnStatus status; |
552 | 0 | if (!SendSet(objId, idVar, val, receiverVar, &status)) { |
553 | 0 | return ipcfail(cx); |
554 | 0 | } |
555 | 0 | |
556 | 0 | LOG_STACK(); |
557 | 0 |
|
558 | 0 | return ok(cx, status, result); |
559 | 0 | } |
560 | | |
561 | | bool |
562 | | CPOWProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, |
563 | | AutoIdVector& props) const |
564 | 0 | { |
565 | 0 | FORWARD(getOwnEnumerablePropertyKeys, (cx, proxy, props), false); |
566 | 0 | } |
567 | | |
568 | | bool |
569 | | WrapperOwner::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) |
570 | 0 | { |
571 | 0 | return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props); |
572 | 0 | } |
573 | | |
574 | | bool |
575 | | CPOWProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) const |
576 | 0 | { |
577 | 0 | FORWARD(preventExtensions, (cx, proxy, result), false); |
578 | 0 | } |
579 | | |
580 | | bool |
581 | | WrapperOwner::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) |
582 | 0 | { |
583 | 0 | ObjectId objId = idOf(proxy); |
584 | 0 |
|
585 | 0 | ReturnStatus status; |
586 | 0 | if (!SendPreventExtensions(objId, &status)) { |
587 | 0 | return ipcfail(cx); |
588 | 0 | } |
589 | 0 | |
590 | 0 | LOG_STACK(); |
591 | 0 |
|
592 | 0 | return ok(cx, status, result); |
593 | 0 | } |
594 | | |
595 | | bool |
596 | | CPOWProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const |
597 | 0 | { |
598 | 0 | FORWARD(isExtensible, (cx, proxy, extensible), false); |
599 | 0 | } |
600 | | |
601 | | bool |
602 | | WrapperOwner::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) |
603 | 0 | { |
604 | 0 | ObjectId objId = idOf(proxy); |
605 | 0 |
|
606 | 0 | ReturnStatus status; |
607 | 0 | if (!SendIsExtensible(objId, &status, extensible)) { |
608 | 0 | return ipcfail(cx); |
609 | 0 | } |
610 | 0 | |
611 | 0 | LOG_STACK(); |
612 | 0 |
|
613 | 0 | return ok(cx, status); |
614 | 0 | } |
615 | | |
616 | | bool |
617 | | CPOWProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const |
618 | 0 | { |
619 | 0 | FORWARD(callOrConstruct, (cx, proxy, args, false), false); |
620 | 0 | } |
621 | | |
622 | | bool |
623 | | CPOWProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const |
624 | 0 | { |
625 | 0 | FORWARD(callOrConstruct, (cx, proxy, args, true), false); |
626 | 0 | } |
627 | | |
628 | | bool |
629 | | WrapperOwner::callOrConstruct(JSContext* cx, HandleObject proxy, const CallArgs& args, |
630 | | bool construct) |
631 | 0 | { |
632 | 0 | ObjectId objId = idOf(proxy); |
633 | 0 |
|
634 | 0 | InfallibleTArray<JSParam> vals; |
635 | 0 | AutoValueVector outobjects(cx); |
636 | 0 |
|
637 | 0 | RootedValue v(cx); |
638 | 0 | for (size_t i = 0; i < args.length() + 2; i++) { |
639 | 0 | // The |this| value for constructors is a magic value that we won't be |
640 | 0 | // able to convert, so skip it. |
641 | 0 | if (i == 1 && construct) { |
642 | 0 | v = UndefinedValue(); |
643 | 0 | } else { |
644 | 0 | v = args.base()[i]; |
645 | 0 | } |
646 | 0 | if (v.isObject()) { |
647 | 0 | RootedObject obj(cx, &v.toObject()); |
648 | 0 | if (xpc::IsOutObject(cx, obj)) { |
649 | 0 | // Make sure it is not an in-out object. |
650 | 0 | bool found; |
651 | 0 | if (!JS_HasProperty(cx, obj, "value", &found)) { |
652 | 0 | return false; |
653 | 0 | } |
654 | 0 | if (found) { |
655 | 0 | JS_ReportErrorASCII(cx, "in-out objects cannot be sent via CPOWs yet"); |
656 | 0 | return false; |
657 | 0 | } |
658 | 0 | |
659 | 0 | vals.AppendElement(JSParam(void_t())); |
660 | 0 | if (!outobjects.append(ObjectValue(*obj))) { |
661 | 0 | return false; |
662 | 0 | } |
663 | 0 | continue; |
664 | 0 | } |
665 | 0 | } |
666 | 0 | JSVariant val; |
667 | 0 | if (!toVariant(cx, v, &val)) { |
668 | 0 | return false; |
669 | 0 | } |
670 | 0 | vals.AppendElement(JSParam(val)); |
671 | 0 | } |
672 | 0 |
|
673 | 0 | JSVariant result; |
674 | 0 | ReturnStatus status; |
675 | 0 | InfallibleTArray<JSParam> outparams; |
676 | 0 | if (!SendCallOrConstruct(objId, vals, construct, &status, &result, &outparams)) { |
677 | 0 | return ipcfail(cx); |
678 | 0 | } |
679 | 0 | |
680 | 0 | LOG_STACK(); |
681 | 0 |
|
682 | 0 | if (!ok(cx, status)) { |
683 | 0 | return false; |
684 | 0 | } |
685 | 0 | |
686 | 0 | if (outparams.Length() != outobjects.length()) { |
687 | 0 | return ipcfail(cx); |
688 | 0 | } |
689 | 0 | |
690 | 0 | RootedObject obj(cx); |
691 | 0 | for (size_t i = 0; i < outparams.Length(); i++) { |
692 | 0 | // Don't bother doing anything for outparams that weren't set. |
693 | 0 | if (outparams[i].type() == JSParam::Tvoid_t) { |
694 | 0 | continue; |
695 | 0 | } |
696 | 0 | |
697 | 0 | // Take the value the child process returned, and set it on the XPC |
698 | 0 | // object. |
699 | 0 | if (!fromVariant(cx, outparams[i], &v)) { |
700 | 0 | return false; |
701 | 0 | } |
702 | 0 | |
703 | 0 | obj = &outobjects[i].toObject(); |
704 | 0 | if (!JS_SetProperty(cx, obj, "value", v)) { |
705 | 0 | return false; |
706 | 0 | } |
707 | 0 | } |
708 | 0 |
|
709 | 0 | if (!fromVariant(cx, result, args.rval())) { |
710 | 0 | return false; |
711 | 0 | } |
712 | 0 | |
713 | 0 | return true; |
714 | 0 | } |
715 | | |
716 | | bool |
717 | | CPOWProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const |
718 | 0 | { |
719 | 0 | FORWARD(hasInstance, (cx, proxy, v, bp), false); |
720 | 0 | } |
721 | | |
722 | | bool |
723 | | WrapperOwner::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) |
724 | 0 | { |
725 | 0 | ObjectId objId = idOf(proxy); |
726 | 0 |
|
727 | 0 | JSVariant vVar; |
728 | 0 | if (!toVariant(cx, v, &vVar)) { |
729 | 0 | return false; |
730 | 0 | } |
731 | 0 | |
732 | 0 | ReturnStatus status; |
733 | 0 | if (!SendHasInstance(objId, vVar, &status, bp)) { |
734 | 0 | return ipcfail(cx); |
735 | 0 | } |
736 | 0 | |
737 | 0 | LOG_STACK(); |
738 | 0 |
|
739 | 0 | return ok(cx, status); |
740 | 0 | } |
741 | | |
742 | | bool |
743 | | CPOWProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const |
744 | 0 | { |
745 | 0 | FORWARD(getBuiltinClass, (cx, proxy, cls), false); |
746 | 0 | } |
747 | | |
748 | | bool |
749 | | WrapperOwner::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) |
750 | 0 | { |
751 | 0 | ObjectId objId = idOf(proxy); |
752 | 0 |
|
753 | 0 | uint32_t classValue = uint32_t(ESClass::Other); |
754 | 0 | ReturnStatus status; |
755 | 0 | if (!SendGetBuiltinClass(objId, &status, &classValue)) { |
756 | 0 | return ipcfail(cx); |
757 | 0 | } |
758 | 0 | *cls = ESClass(classValue); |
759 | 0 |
|
760 | 0 | LOG_STACK(); |
761 | 0 |
|
762 | 0 | return ok(cx, status); |
763 | 0 | } |
764 | | |
765 | | bool |
766 | | CPOWProxyHandler::isArray(JSContext* cx, HandleObject proxy, |
767 | | IsArrayAnswer* answer) const |
768 | 0 | { |
769 | 0 | FORWARD(isArray, (cx, proxy, answer), false); |
770 | 0 | } |
771 | | |
772 | | bool |
773 | | WrapperOwner::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer) |
774 | 0 | { |
775 | 0 | ObjectId objId = idOf(proxy); |
776 | 0 |
|
777 | 0 | uint32_t ans; |
778 | 0 | ReturnStatus status; |
779 | 0 | if (!SendIsArray(objId, &status, &ans)) { |
780 | 0 | return ipcfail(cx); |
781 | 0 | } |
782 | 0 | |
783 | 0 | LOG_STACK(); |
784 | 0 |
|
785 | 0 | *answer = IsArrayAnswer(ans); |
786 | 0 | MOZ_ASSERT(*answer == IsArrayAnswer::Array || |
787 | 0 | *answer == IsArrayAnswer::NotArray || |
788 | 0 | *answer == IsArrayAnswer::RevokedProxy); |
789 | 0 |
|
790 | 0 | return ok(cx, status); |
791 | 0 | } |
792 | | |
793 | | const char* |
794 | | CPOWProxyHandler::className(JSContext* cx, HandleObject proxy) const |
795 | 0 | { |
796 | 0 | WrapperOwner* parent = OwnerOf(proxy); |
797 | 0 | if (!parent->active()) { |
798 | 0 | return "<dead CPOW>"; |
799 | 0 | } |
800 | 0 | return parent->className(cx, proxy); |
801 | 0 | } |
802 | | |
803 | | const char* |
804 | | WrapperOwner::className(JSContext* cx, HandleObject proxy) |
805 | 0 | { |
806 | 0 | AuxCPOWData* data = AuxCPOWDataOf(proxy); |
807 | 0 | if (data->className.IsEmpty()) { |
808 | 0 | ObjectId objId = idOf(proxy); |
809 | 0 |
|
810 | 0 | if (!SendClassName(objId, &data->className)) { |
811 | 0 | return "<error>"; |
812 | 0 | } |
813 | 0 | |
814 | 0 | LOG_STACK(); |
815 | 0 | } |
816 | 0 |
|
817 | 0 | return data->className.get(); |
818 | 0 | } |
819 | | |
820 | | bool |
821 | | CPOWProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const |
822 | 0 | { |
823 | 0 | FORWARD(getPrototype, (cx, proxy, objp), false); |
824 | 0 | } |
825 | | |
826 | | bool |
827 | | WrapperOwner::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp) |
828 | 0 | { |
829 | 0 | ObjectId objId = idOf(proxy); |
830 | 0 |
|
831 | 0 | ObjectOrNullVariant val; |
832 | 0 | ReturnStatus status; |
833 | 0 | if (!SendGetPrototype(objId, &status, &val)) { |
834 | 0 | return ipcfail(cx); |
835 | 0 | } |
836 | 0 | |
837 | 0 | LOG_STACK(); |
838 | 0 |
|
839 | 0 | if (!ok(cx, status)) { |
840 | 0 | return false; |
841 | 0 | } |
842 | 0 | |
843 | 0 | objp.set(fromObjectOrNullVariant(cx, val)); |
844 | 0 |
|
845 | 0 | return true; |
846 | 0 | } |
847 | | |
848 | | bool |
849 | | CPOWProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, |
850 | | MutableHandleObject objp) const |
851 | 0 | { |
852 | 0 | FORWARD(getPrototypeIfOrdinary, (cx, proxy, isOrdinary, objp), false); |
853 | 0 | } |
854 | | |
855 | | bool |
856 | | WrapperOwner::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary, |
857 | | MutableHandleObject objp) |
858 | 0 | { |
859 | 0 | ObjectId objId = idOf(proxy); |
860 | 0 |
|
861 | 0 | ObjectOrNullVariant val; |
862 | 0 | ReturnStatus status; |
863 | 0 | if (!SendGetPrototypeIfOrdinary(objId, &status, isOrdinary, &val)) { |
864 | 0 | return ipcfail(cx); |
865 | 0 | } |
866 | 0 | |
867 | 0 | LOG_STACK(); |
868 | 0 |
|
869 | 0 | if (!ok(cx, status)) { |
870 | 0 | return false; |
871 | 0 | } |
872 | 0 | |
873 | 0 | objp.set(fromObjectOrNullVariant(cx, val)); |
874 | 0 |
|
875 | 0 | return true; |
876 | 0 | } |
877 | | |
878 | | RegExpShared* |
879 | | CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const |
880 | 0 | { |
881 | 0 | FORWARD(regexp_toShared, (cx, proxy), nullptr); |
882 | 0 | } |
883 | | |
884 | | RegExpShared* |
885 | | WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy) |
886 | 0 | { |
887 | 0 | ObjectId objId = idOf(proxy); |
888 | 0 |
|
889 | 0 | ReturnStatus status; |
890 | 0 | nsString source; |
891 | 0 | unsigned flags = 0; |
892 | 0 | if (!SendRegExpToShared(objId, &status, &source, &flags)) { |
893 | 0 | MOZ_ALWAYS_FALSE(ipcfail(cx)); |
894 | 0 | return nullptr; |
895 | 0 | } |
896 | 0 | LOG_STACK(); |
897 | 0 |
|
898 | 0 | if (!ok(cx, status)) { |
899 | 0 | return nullptr; |
900 | 0 | } |
901 | 0 | |
902 | 0 | RootedObject regexp(cx); |
903 | 0 | regexp = JS_NewUCRegExpObject(cx, source.get(), source.Length(), flags); |
904 | 0 | if (!regexp) { |
905 | 0 | return nullptr; |
906 | 0 | } |
907 | 0 | |
908 | 0 | return js::RegExpToSharedNonInline(cx, regexp); |
909 | 0 | } |
910 | | |
911 | | void |
912 | | CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const |
913 | 0 | { |
914 | 0 | AuxCPOWData* aux = AuxCPOWDataOf(proxy); |
915 | 0 |
|
916 | 0 | OwnerOf(proxy)->drop(proxy); |
917 | 0 |
|
918 | 0 | if (aux) { |
919 | 0 | delete aux; |
920 | 0 | } |
921 | 0 | } |
922 | | |
923 | | size_t |
924 | | CPOWProxyHandler::objectMoved(JSObject* proxy, JSObject* old) const |
925 | 0 | { |
926 | 0 | OwnerOf(proxy)->updatePointer(proxy, old); |
927 | 0 | return 0; |
928 | 0 | } |
929 | | |
930 | | bool |
931 | | CPOWProxyHandler::isCallable(JSObject* proxy) const |
932 | 0 | { |
933 | 0 | AuxCPOWData* aux = AuxCPOWDataOf(proxy); |
934 | 0 | return aux->isCallable; |
935 | 0 | } |
936 | | |
937 | | bool |
938 | | CPOWProxyHandler::isConstructor(JSObject* proxy) const |
939 | 0 | { |
940 | 0 | AuxCPOWData* aux = AuxCPOWDataOf(proxy); |
941 | 0 | return aux->isConstructor; |
942 | 0 | } |
943 | | |
944 | | void |
945 | | WrapperOwner::drop(JSObject* obj) |
946 | 0 | { |
947 | 0 | // The association may have already been swept from the table but if it's |
948 | 0 | // there then remove it. |
949 | 0 | ObjectId objId = idOfUnchecked(obj); |
950 | 0 | if (cpows_.findPreserveColor(objId) == obj) { |
951 | 0 | cpows_.remove(objId); |
952 | 0 | } |
953 | 0 |
|
954 | 0 | if (active()) { |
955 | 0 | Unused << SendDropObject(objId); |
956 | 0 | } |
957 | 0 | decref(); |
958 | 0 | } |
959 | | |
960 | | void |
961 | | WrapperOwner::updatePointer(JSObject* obj, const JSObject* old) |
962 | 0 | { |
963 | 0 | ObjectId objId = idOfUnchecked(obj); |
964 | 0 | MOZ_ASSERT(hasCPOW(objId, old)); |
965 | 0 | cpows_.add(objId, obj); |
966 | 0 | } |
967 | | |
968 | | bool |
969 | | WrapperOwner::getPropertyKeys(JSContext* cx, HandleObject proxy, uint32_t flags, AutoIdVector& props) |
970 | 0 | { |
971 | 0 | ObjectId objId = idOf(proxy); |
972 | 0 |
|
973 | 0 | ReturnStatus status; |
974 | 0 | InfallibleTArray<JSIDVariant> ids; |
975 | 0 | if (!SendGetPropertyKeys(objId, flags, &status, &ids)) { |
976 | 0 | return ipcfail(cx); |
977 | 0 | } |
978 | 0 | |
979 | 0 | LOG_STACK(); |
980 | 0 |
|
981 | 0 | if (!ok(cx, status)) { |
982 | 0 | return false; |
983 | 0 | } |
984 | 0 | |
985 | 0 | for (size_t i = 0; i < ids.Length(); i++) { |
986 | 0 | RootedId id(cx); |
987 | 0 | if (!fromJSIDVariant(cx, ids[i], &id)) { |
988 | 0 | return false; |
989 | 0 | } |
990 | 0 | if (!props.append(id)) { |
991 | 0 | return false; |
992 | 0 | } |
993 | 0 | } |
994 | 0 |
|
995 | 0 | return true; |
996 | 0 | } |
997 | | |
998 | | namespace mozilla { |
999 | | namespace jsipc { |
1000 | | |
1001 | | bool |
1002 | | IsCPOW(JSObject* obj) |
1003 | 1.62M | { |
1004 | 1.62M | return IsProxy(obj) && GetProxyHandler(obj) == &CPOWProxyHandler::singleton; |
1005 | 1.62M | } |
1006 | | |
1007 | | bool |
1008 | | IsWrappedCPOW(JSObject* obj) |
1009 | 1.62M | { |
1010 | 1.62M | JSObject* unwrapped = js::UncheckedUnwrap(obj, true); |
1011 | 1.62M | if (!unwrapped) { |
1012 | 0 | return false; |
1013 | 0 | } |
1014 | 1.62M | return IsCPOW(unwrapped); |
1015 | 1.62M | } |
1016 | | |
1017 | | void |
1018 | | GetWrappedCPOWTag(JSObject* obj, nsACString& out) |
1019 | 0 | { |
1020 | 0 | JSObject* unwrapped = js::UncheckedUnwrap(obj, true); |
1021 | 0 | MOZ_ASSERT(IsCPOW(unwrapped)); |
1022 | 0 |
|
1023 | 0 | AuxCPOWData* aux = AuxCPOWDataOf(unwrapped); |
1024 | 0 | if (aux) { |
1025 | 0 | out = aux->objectTag; |
1026 | 0 | } |
1027 | 0 | } |
1028 | | |
1029 | | nsresult |
1030 | | InstanceOf(JSObject* proxy, const nsID* id, bool* bp) |
1031 | 0 | { |
1032 | 0 | WrapperOwner* parent = OwnerOf(proxy); |
1033 | 0 | if (!parent->active()) { |
1034 | 0 | return NS_ERROR_UNEXPECTED; |
1035 | 0 | } |
1036 | 0 | return parent->instanceOf(proxy, id, bp); |
1037 | 0 | } |
1038 | | |
1039 | | bool |
1040 | | DOMInstanceOf(JSContext* cx, JSObject* proxyArg, int prototypeID, int depth, bool* bp) |
1041 | 0 | { |
1042 | 0 | RootedObject proxy(cx, proxyArg); |
1043 | 0 | FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp), false); |
1044 | 0 | } |
1045 | | |
1046 | | } /* namespace jsipc */ |
1047 | | } /* namespace mozilla */ |
1048 | | |
1049 | | nsresult |
1050 | | WrapperOwner::instanceOf(JSObject* obj, const nsID* id, bool* bp) |
1051 | 0 | { |
1052 | 0 | ObjectId objId = idOf(obj); |
1053 | 0 |
|
1054 | 0 | JSIID iid; |
1055 | 0 | ConvertID(*id, &iid); |
1056 | 0 |
|
1057 | 0 | ReturnStatus status; |
1058 | 0 | if (!SendInstanceOf(objId, iid, &status, bp)) { |
1059 | 0 | return NS_ERROR_UNEXPECTED; |
1060 | 0 | } |
1061 | 0 | |
1062 | 0 | if (status.type() != ReturnStatus::TReturnSuccess) { |
1063 | 0 | return NS_ERROR_UNEXPECTED; |
1064 | 0 | } |
1065 | 0 | |
1066 | 0 | return NS_OK; |
1067 | 0 | } |
1068 | | |
1069 | | bool |
1070 | | WrapperOwner::domInstanceOf(JSContext* cx, JSObject* obj, int prototypeID, int depth, bool* bp) |
1071 | 0 | { |
1072 | 0 | ObjectId objId = idOf(obj); |
1073 | 0 |
|
1074 | 0 | ReturnStatus status; |
1075 | 0 | if (!SendDOMInstanceOf(objId, prototypeID, depth, &status, bp)) { |
1076 | 0 | return ipcfail(cx); |
1077 | 0 | } |
1078 | 0 | |
1079 | 0 | LOG_STACK(); |
1080 | 0 |
|
1081 | 0 | return ok(cx, status); |
1082 | 0 | } |
1083 | | |
1084 | | void |
1085 | | WrapperOwner::ActorDestroy(ActorDestroyReason why) |
1086 | 0 | { |
1087 | 0 | inactive_ = true; |
1088 | 0 |
|
1089 | 0 | objects_.clear(); |
1090 | 0 | unwaivedObjectIds_.clear(); |
1091 | 0 | waivedObjectIds_.clear(); |
1092 | 0 | } |
1093 | | |
1094 | | bool |
1095 | | WrapperOwner::ipcfail(JSContext* cx) |
1096 | 0 | { |
1097 | 0 | JS_ReportErrorASCII(cx, "cross-process JS call failed"); |
1098 | 0 | return false; |
1099 | 0 | } |
1100 | | |
1101 | | bool |
1102 | | WrapperOwner::ok(JSContext* cx, const ReturnStatus& status) |
1103 | 0 | { |
1104 | 0 | if (status.type() == ReturnStatus::TReturnSuccess) { |
1105 | 0 | return true; |
1106 | 0 | } |
1107 | 0 | |
1108 | 0 | if (status.type() == ReturnStatus::TReturnDeadCPOW) { |
1109 | 0 | JS_ReportErrorASCII(cx, "operation not possible on dead CPOW"); |
1110 | 0 | return false; |
1111 | 0 | } |
1112 | 0 | |
1113 | 0 | RootedValue exn(cx); |
1114 | 0 | if (!fromVariant(cx, status.get_ReturnException().exn(), &exn)) { |
1115 | 0 | return false; |
1116 | 0 | } |
1117 | 0 | |
1118 | 0 | JS_SetPendingException(cx, exn); |
1119 | 0 | return false; |
1120 | 0 | } |
1121 | | |
1122 | | bool |
1123 | | WrapperOwner::ok(JSContext* cx, const ReturnStatus& status, ObjectOpResult& result) |
1124 | 0 | { |
1125 | 0 | if (status.type() == ReturnStatus::TReturnObjectOpResult) { |
1126 | 0 | return result.fail(status.get_ReturnObjectOpResult().code()); |
1127 | 0 | } |
1128 | 0 | if (!ok(cx, status)) { |
1129 | 0 | return false; |
1130 | 0 | } |
1131 | 0 | return result.succeed(); |
1132 | 0 | } |
1133 | | |
1134 | | // CPOWs can have a tag string attached to them, originating in the local |
1135 | | // process from this function. It's sent with the CPOW to the remote process, |
1136 | | // where it can be fetched with Components.utils.getCrossProcessWrapperTag. |
1137 | | static nsCString |
1138 | | GetRemoteObjectTag(JS::Handle<JSObject*> obj) |
1139 | 0 | { |
1140 | 0 | if (nsCOMPtr<nsISupports> supports = xpc::UnwrapReflectorToISupports(obj)) { |
1141 | 0 | nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(supports)); |
1142 | 0 | if (treeItem) { |
1143 | 0 | return NS_LITERAL_CSTRING("ContentDocShellTreeItem"); |
1144 | 0 | } |
1145 | 0 |
|
1146 | 0 | nsCOMPtr<nsIDocument> doc(do_QueryInterface(supports)); |
1147 | 0 | if (doc) { |
1148 | 0 | return NS_LITERAL_CSTRING("ContentDocument"); |
1149 | 0 | } |
1150 | 0 | } |
1151 | 0 |
|
1152 | 0 | return NS_LITERAL_CSTRING("generic"); |
1153 | 0 | } |
1154 | | |
1155 | | static RemoteObject |
1156 | | MakeRemoteObject(JSContext* cx, ObjectId id, HandleObject obj) |
1157 | 0 | { |
1158 | 0 | return RemoteObject(id.serialize(), |
1159 | 0 | JS::IsCallable(obj), |
1160 | 0 | JS::IsConstructor(obj), |
1161 | 0 | dom::IsDOMObject(obj), |
1162 | 0 | GetRemoteObjectTag(obj)); |
1163 | 0 | } |
1164 | | |
1165 | | bool |
1166 | | WrapperOwner::toObjectVariant(JSContext* cx, JSObject* objArg, ObjectVariant* objVarp) |
1167 | 0 | { |
1168 | 0 | RootedObject obj(cx, objArg); |
1169 | 0 | MOZ_ASSERT(obj); |
1170 | 0 |
|
1171 | 0 | // We always save objects unwrapped in the CPOW table. If we stored |
1172 | 0 | // wrappers, then the wrapper might be GCed while the target remained alive. |
1173 | 0 | // Whenever operating on an object that comes from the table, we wrap it |
1174 | 0 | // in findObjectById. |
1175 | 0 | unsigned wrapperFlags = 0; |
1176 | 0 | obj = js::UncheckedUnwrap(obj, true, &wrapperFlags); |
1177 | 0 | if (obj && IsCPOW(obj) && OwnerOf(obj) == this) { |
1178 | 0 | *objVarp = LocalObject(idOf(obj).serialize()); |
1179 | 0 | return true; |
1180 | 0 | } |
1181 | 0 | bool waiveXray = wrapperFlags & xpc::WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG; |
1182 | 0 |
|
1183 | 0 | ObjectId id = objectIdMap(waiveXray).find(obj); |
1184 | 0 | if (!id.isNull()) { |
1185 | 0 | MOZ_ASSERT(id.hasXrayWaiver() == waiveXray); |
1186 | 0 | *objVarp = MakeRemoteObject(cx, id, obj); |
1187 | 0 | return true; |
1188 | 0 | } |
1189 | 0 |
|
1190 | 0 | // Need to call PreserveWrapper on |obj| in case it's a reflector. |
1191 | 0 | // FIXME: What if it's an XPCWrappedNative? |
1192 | 0 | if (mozilla::dom::IsDOMObject(obj)) { |
1193 | 0 | mozilla::dom::TryPreserveWrapper(obj); |
1194 | 0 | } |
1195 | 0 |
|
1196 | 0 | id = ObjectId(nextSerialNumber_++, waiveXray); |
1197 | 0 | if (!objects_.add(id, obj)) { |
1198 | 0 | return false; |
1199 | 0 | } |
1200 | 0 | if (!objectIdMap(waiveXray).add(cx, obj, id)) { |
1201 | 0 | return false; |
1202 | 0 | } |
1203 | 0 | |
1204 | 0 | *objVarp = MakeRemoteObject(cx, id, obj); |
1205 | 0 | return true; |
1206 | 0 | } |
1207 | | |
1208 | | JSObject* |
1209 | | WrapperOwner::fromObjectVariant(JSContext* cx, const ObjectVariant& objVar) |
1210 | 0 | { |
1211 | 0 | if (objVar.type() == ObjectVariant::TRemoteObject) { |
1212 | 0 | return fromRemoteObjectVariant(cx, objVar.get_RemoteObject()); |
1213 | 0 | } else { |
1214 | 0 | return fromLocalObjectVariant(cx, objVar.get_LocalObject()); |
1215 | 0 | } |
1216 | 0 | } |
1217 | | |
1218 | | JSObject* |
1219 | | WrapperOwner::fromRemoteObjectVariant(JSContext* cx, const RemoteObject& objVar) |
1220 | 0 | { |
1221 | 0 | Maybe<ObjectId> maybeObjId(ObjectId::deserialize(objVar.serializedId())); |
1222 | 0 | if (maybeObjId.isNothing()) { |
1223 | 0 | return nullptr; |
1224 | 0 | } |
1225 | 0 | |
1226 | 0 | ObjectId objId = maybeObjId.value(); |
1227 | 0 | RootedObject obj(cx, findCPOWById(objId)); |
1228 | 0 | if (!obj) { |
1229 | 0 |
|
1230 | 0 | // All CPOWs live in the privileged junk scope. |
1231 | 0 | RootedObject junkScope(cx, xpc::PrivilegedJunkScope()); |
1232 | 0 | JSAutoRealm ar(cx, junkScope); |
1233 | 0 | RootedValue v(cx, UndefinedValue()); |
1234 | 0 | // We need to setLazyProto for the getPrototype/getPrototypeIfOrdinary |
1235 | 0 | // hooks. |
1236 | 0 | ProxyOptions options; |
1237 | 0 | options.setLazyProto(true); |
1238 | 0 | obj = NewProxyObject(cx, |
1239 | 0 | &CPOWProxyHandler::singleton, |
1240 | 0 | v, |
1241 | 0 | nullptr, |
1242 | 0 | options); |
1243 | 0 | if (!obj) { |
1244 | 0 | return nullptr; |
1245 | 0 | } |
1246 | 0 | |
1247 | 0 | if (!cpows_.add(objId, obj)) { |
1248 | 0 | return nullptr; |
1249 | 0 | } |
1250 | 0 | |
1251 | 0 | nextCPOWNumber_ = objId.serialNumber() + 1; |
1252 | 0 |
|
1253 | 0 | // Incref once we know the decref will be called. |
1254 | 0 | incref(); |
1255 | 0 |
|
1256 | 0 | AuxCPOWData* aux = new AuxCPOWData(objId, |
1257 | 0 | objVar.isCallable(), |
1258 | 0 | objVar.isConstructor(), |
1259 | 0 | objVar.isDOMObject(), |
1260 | 0 | objVar.objectTag()); |
1261 | 0 |
|
1262 | 0 | SetProxyReservedSlot(obj, 0, PrivateValue(this)); |
1263 | 0 | SetProxyReservedSlot(obj, 1, PrivateValue(aux)); |
1264 | 0 | } |
1265 | 0 |
|
1266 | 0 | if (!JS_WrapObject(cx, &obj)) { |
1267 | 0 | return nullptr; |
1268 | 0 | } |
1269 | 0 | return obj; |
1270 | 0 | } |
1271 | | |
1272 | | JSObject* |
1273 | | WrapperOwner::fromLocalObjectVariant(JSContext* cx, const LocalObject& objVar) |
1274 | 0 | { |
1275 | 0 | Maybe<ObjectId> id(ObjectId::deserialize(objVar.serializedId())); |
1276 | 0 | if (id.isNothing()) { |
1277 | 0 | return nullptr; |
1278 | 0 | } |
1279 | 0 | Rooted<JSObject*> obj(cx, findObjectById(cx, id.value())); |
1280 | 0 | if (!obj) { |
1281 | 0 | return nullptr; |
1282 | 0 | } |
1283 | 0 | if (!JS_WrapObject(cx, &obj)) { |
1284 | 0 | return nullptr; |
1285 | 0 | } |
1286 | 0 | return obj; |
1287 | 0 | } |