Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "gc/PublicIterators.h"
8 : #include "js/Wrapper.h"
9 : #include "proxy/DeadObjectProxy.h"
10 : #include "vm/Iteration.h"
11 : #include "vm/WrapperObject.h"
12 :
13 : #include "gc/Nursery-inl.h"
14 : #include "vm/Compartment-inl.h"
15 : #include "vm/JSObject-inl.h"
16 : #include "vm/Realm-inl.h"
17 :
18 : using namespace js;
19 :
20 : #define PIERCE(cx, wrapper, pre, op, post) \
21 : JS_BEGIN_MACRO \
22 : bool ok; \
23 : { \
24 : AutoRealm call(cx, wrappedObject(wrapper)); \
25 : ok = (pre) && (op); \
26 : } \
27 : return ok && (post); \
28 : JS_END_MACRO
29 :
30 : #define NOTHING (true)
31 :
32 : static bool
33 : MarkAtoms(JSContext* cx, jsid id)
34 : {
35 29425 : cx->markId(id);
36 : return true;
37 : }
38 :
39 : static bool
40 185 : MarkAtoms(JSContext* cx, const AutoIdVector& ids)
41 : {
42 0 : for (size_t i = 0; i < ids.length(); i++)
43 0 : cx->markId(ids[i]);
44 185 : return true;
45 : }
46 :
47 : bool
48 0 : CrossCompartmentWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
49 : MutableHandle<PropertyDescriptor> desc) const
50 : {
51 0 : PIERCE(cx, wrapper,
52 : MarkAtoms(cx, id),
53 : Wrapper::getPropertyDescriptor(cx, wrapper, id, desc),
54 : cx->compartment()->wrap(cx, desc));
55 : }
56 :
57 : bool
58 233 : CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
59 : MutableHandle<PropertyDescriptor> desc) const
60 : {
61 1398 : PIERCE(cx, wrapper,
62 : MarkAtoms(cx, id),
63 : Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc),
64 : cx->compartment()->wrap(cx, desc));
65 : }
66 :
67 : bool
68 497 : CrossCompartmentWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
69 : Handle<PropertyDescriptor> desc,
70 : ObjectOpResult& result) const
71 : {
72 0 : Rooted<PropertyDescriptor> desc2(cx, desc);
73 4473 : PIERCE(cx, wrapper,
74 : MarkAtoms(cx, id) && cx->compartment()->wrap(cx, &desc2),
75 : Wrapper::defineProperty(cx, wrapper, id, desc2, result),
76 : NOTHING);
77 : }
78 :
79 : bool
80 22 : CrossCompartmentWrapper::ownPropertyKeys(JSContext* cx, HandleObject wrapper,
81 : AutoIdVector& props) const
82 : {
83 66 : PIERCE(cx, wrapper,
84 : NOTHING,
85 : Wrapper::ownPropertyKeys(cx, wrapper, props),
86 : MarkAtoms(cx, props));
87 : }
88 :
89 : bool
90 14 : CrossCompartmentWrapper::delete_(JSContext* cx, HandleObject wrapper, HandleId id,
91 : ObjectOpResult& result) const
92 : {
93 70 : PIERCE(cx, wrapper,
94 : MarkAtoms(cx, id),
95 : Wrapper::delete_(cx, wrapper, id, result),
96 : NOTHING);
97 : }
98 :
99 : bool
100 0 : CrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject wrapper,
101 : MutableHandleObject protop) const
102 : {
103 : {
104 0 : RootedObject wrapped(cx, wrappedObject(wrapper));
105 0 : AutoRealm call(cx, wrapped);
106 0 : if (!GetPrototype(cx, wrapped, protop))
107 0 : return false;
108 0 : if (protop) {
109 0 : if (!JSObject::setDelegate(cx, protop))
110 : return false;
111 : }
112 : }
113 :
114 0 : return cx->compartment()->wrap(cx, protop);
115 : }
116 :
117 : bool
118 0 : CrossCompartmentWrapper::setPrototype(JSContext* cx, HandleObject wrapper,
119 : HandleObject proto, ObjectOpResult& result) const
120 : {
121 0 : RootedObject protoCopy(cx, proto);
122 0 : PIERCE(cx, wrapper,
123 : cx->compartment()->wrap(cx, &protoCopy),
124 : Wrapper::setPrototype(cx, wrapper, protoCopy, result),
125 : NOTHING);
126 : }
127 :
128 : bool
129 2 : CrossCompartmentWrapper::getPrototypeIfOrdinary(JSContext* cx, HandleObject wrapper,
130 : bool* isOrdinary, MutableHandleObject protop) const
131 : {
132 : {
133 0 : RootedObject wrapped(cx, wrappedObject(wrapper));
134 0 : AutoRealm call(cx, wrapped);
135 0 : if (!GetPrototypeIfOrdinary(cx, wrapped, isOrdinary, protop))
136 0 : return false;
137 :
138 2 : if (!*isOrdinary)
139 : return true;
140 :
141 0 : if (protop) {
142 2 : if (!JSObject::setDelegate(cx, protop))
143 : return false;
144 : }
145 : }
146 :
147 4 : return cx->compartment()->wrap(cx, protop);
148 : }
149 :
150 : bool
151 0 : CrossCompartmentWrapper::setImmutablePrototype(JSContext* cx, HandleObject wrapper, bool* succeeded) const
152 : {
153 0 : PIERCE(cx, wrapper,
154 : NOTHING,
155 : Wrapper::setImmutablePrototype(cx, wrapper, succeeded),
156 : NOTHING);
157 : }
158 :
159 : bool
160 0 : CrossCompartmentWrapper::preventExtensions(JSContext* cx, HandleObject wrapper,
161 : ObjectOpResult& result) const
162 : {
163 0 : PIERCE(cx, wrapper,
164 : NOTHING,
165 : Wrapper::preventExtensions(cx, wrapper, result),
166 : NOTHING);
167 : }
168 :
169 : bool
170 0 : CrossCompartmentWrapper::isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const
171 : {
172 0 : PIERCE(cx, wrapper,
173 : NOTHING,
174 : Wrapper::isExtensible(cx, wrapper, extensible),
175 : NOTHING);
176 : }
177 :
178 : bool
179 24 : CrossCompartmentWrapper::has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
180 : {
181 120 : PIERCE(cx, wrapper,
182 : MarkAtoms(cx, id),
183 : Wrapper::has(cx, wrapper, id, bp),
184 : NOTHING);
185 : }
186 :
187 : bool
188 274 : CrossCompartmentWrapper::hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
189 : {
190 1370 : PIERCE(cx, wrapper,
191 : MarkAtoms(cx, id),
192 : Wrapper::hasOwn(cx, wrapper, id, bp),
193 : NOTHING);
194 : }
195 :
196 : static bool
197 28383 : WrapReceiver(JSContext* cx, HandleObject wrapper, MutableHandleValue receiver)
198 : {
199 : // Usually the receiver is the wrapper and we can just unwrap it. If the
200 : // wrapped object is also a wrapper, things are more complicated and we
201 : // fall back to the slow path (it calls UncheckedUnwrap to unwrap all
202 : // wrappers).
203 0 : if (ObjectValue(*wrapper) == receiver) {
204 0 : JSObject* wrapped = Wrapper::wrappedObject(wrapper);
205 0 : if (!IsWrapper(wrapped)) {
206 0 : MOZ_ASSERT(wrapped->compartment() == cx->compartment());
207 0 : MOZ_ASSERT(!IsWindow(wrapped));
208 0 : receiver.setObject(*wrapped);
209 27936 : return true;
210 : }
211 : }
212 :
213 447 : return cx->compartment()->wrap(cx, receiver);
214 : }
215 :
216 : bool
217 27975 : CrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
218 : HandleId id, MutableHandleValue vp) const
219 : {
220 55950 : RootedValue receiverCopy(cx, receiver);
221 : {
222 0 : AutoRealm call(cx, wrappedObject(wrapper));
223 0 : if (!MarkAtoms(cx, id) || !WrapReceiver(cx, wrapper, &receiverCopy))
224 0 : return false;
225 :
226 27975 : if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp))
227 : return false;
228 : }
229 55950 : return cx->compartment()->wrap(cx, vp);
230 : }
231 :
232 : bool
233 408 : CrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
234 : HandleValue receiver, ObjectOpResult& result) const
235 : {
236 0 : RootedValue valCopy(cx, v);
237 0 : RootedValue receiverCopy(cx, receiver);
238 4488 : PIERCE(cx, wrapper,
239 : MarkAtoms(cx, id) &&
240 : cx->compartment()->wrap(cx, &valCopy) &&
241 : WrapReceiver(cx, wrapper, &receiverCopy),
242 : Wrapper::set(cx, wrapper, id, valCopy, receiverCopy, result),
243 : NOTHING);
244 : }
245 :
246 : bool
247 163 : CrossCompartmentWrapper::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
248 : AutoIdVector& props) const
249 : {
250 489 : PIERCE(cx, wrapper,
251 : NOTHING,
252 : Wrapper::getOwnEnumerablePropertyKeys(cx, wrapper, props),
253 : MarkAtoms(cx, props));
254 : }
255 :
256 : /*
257 : * We can reify non-escaping iterator objects instead of having to wrap them. This
258 : * allows fast iteration over objects across a compartment boundary.
259 : */
260 : static bool
261 : CanReify(HandleObject obj)
262 : {
263 48 : return obj->is<PropertyIteratorObject>();
264 : }
265 :
266 : struct AutoCloseIterator
267 : {
268 24 : AutoCloseIterator(JSContext* cx, PropertyIteratorObject* obj) : obj(cx, obj) {}
269 :
270 0 : ~AutoCloseIterator() {
271 1 : if (obj)
272 0 : CloseIterator(obj);
273 24 : }
274 :
275 48 : void clear() { obj = nullptr; }
276 :
277 : private:
278 : Rooted<PropertyIteratorObject*> obj;
279 : };
280 :
281 : static JSObject*
282 24 : Reify(JSContext* cx, JS::Compartment* origin, HandleObject objp)
283 : {
284 0 : Rooted<PropertyIteratorObject*> iterObj(cx, &objp->as<PropertyIteratorObject>());
285 48 : NativeIterator* ni = iterObj->getNativeIterator();
286 :
287 48 : RootedObject obj(cx, ni->objectBeingIterated());
288 : {
289 72 : AutoCloseIterator close(cx, iterObj);
290 :
291 : /* Wrap the iteratee. */
292 1 : if (!origin->wrap(cx, &obj))
293 0 : return nullptr;
294 :
295 : /*
296 : * Wrap the elements in the iterator's snapshot.
297 : * N.B. the order of closing/creating iterators is important due to the
298 : * implicit cx->enumerators state.
299 : */
300 0 : size_t length = ni->numKeys();
301 0 : AutoIdVector keys(cx);
302 0 : if (length > 0) {
303 1 : if (!keys.reserve(length))
304 0 : return nullptr;
305 0 : RootedId id(cx);
306 0 : RootedValue v(cx);
307 0 : for (size_t i = 0; i < length; ++i) {
308 0 : v.setString(ni->propertiesBegin()[i]);
309 1 : if (!ValueToId<CanGC>(cx, v, &id))
310 0 : return nullptr;
311 0 : cx->markId(id);
312 117 : keys.infallibleAppend(id);
313 : }
314 : }
315 :
316 0 : close.clear();
317 24 : CloseIterator(iterObj);
318 :
319 48 : obj = EnumeratedIdVectorToIterator(cx, obj, keys);
320 : }
321 24 : return obj;
322 : }
323 :
324 : JSObject*
325 24 : CrossCompartmentWrapper::enumerate(JSContext* cx, HandleObject wrapper) const
326 : {
327 48 : RootedObject res(cx);
328 : {
329 0 : AutoRealm call(cx, wrappedObject(wrapper));
330 0 : res = Wrapper::enumerate(cx, wrapper);
331 1 : if (!res)
332 0 : return nullptr;
333 : }
334 :
335 0 : if (CanReify(res))
336 1 : return Reify(cx, cx->compartment(), res);
337 0 : if (!cx->compartment()->wrap(cx, &res))
338 : return nullptr;
339 0 : return res;
340 : }
341 :
342 : bool
343 15158 : CrossCompartmentWrapper::call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
344 : {
345 30316 : RootedObject wrapped(cx, wrappedObject(wrapper));
346 :
347 : {
348 45474 : AutoRealm call(cx, wrapped);
349 :
350 0 : args.setCallee(ObjectValue(*wrapped));
351 0 : if (!cx->compartment()->wrap(cx, args.mutableThisv()))
352 0 : return false;
353 :
354 0 : for (size_t n = 0; n < args.length(); ++n) {
355 31334 : if (!cx->compartment()->wrap(cx, args[n]))
356 : return false;
357 : }
358 :
359 15158 : if (!Wrapper::call(cx, wrapper, args))
360 : return false;
361 : }
362 :
363 45474 : return cx->compartment()->wrap(cx, args.rval());
364 : }
365 :
366 : bool
367 52 : CrossCompartmentWrapper::construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
368 : {
369 104 : RootedObject wrapped(cx, wrappedObject(wrapper));
370 : {
371 156 : AutoRealm call(cx, wrapped);
372 :
373 0 : for (size_t n = 0; n < args.length(); ++n) {
374 0 : if (!cx->compartment()->wrap(cx, args[n]))
375 0 : return false;
376 : }
377 104 : if (!cx->compartment()->wrap(cx, args.newTarget()))
378 : return false;
379 52 : if (!Wrapper::construct(cx, wrapper, args))
380 : return false;
381 : }
382 156 : return cx->compartment()->wrap(cx, args.rval());
383 : }
384 :
385 : bool
386 0 : CrossCompartmentWrapper::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
387 : const CallArgs& srcArgs) const
388 : {
389 0 : RootedObject wrapper(cx, &srcArgs.thisv().toObject());
390 0 : MOZ_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
391 : !UncheckedUnwrap(wrapper)->is<CrossCompartmentWrapperObject>());
392 :
393 0 : RootedObject wrapped(cx, wrappedObject(wrapper));
394 : {
395 0 : AutoRealm call(cx, wrapped);
396 0 : InvokeArgs dstArgs(cx);
397 0 : if (!dstArgs.init(cx, srcArgs.length()))
398 0 : return false;
399 :
400 0 : Value* src = srcArgs.base();
401 0 : Value* srcend = srcArgs.array() + srcArgs.length();
402 0 : Value* dst = dstArgs.base();
403 :
404 0 : RootedValue source(cx);
405 0 : for (; src < srcend; ++src, ++dst) {
406 0 : source = *src;
407 0 : if (!cx->compartment()->wrap(cx, &source))
408 0 : return false;
409 0 : *dst = source.get();
410 :
411 : // Handle |this| specially. When we rewrap on the other side of the
412 : // membrane, we might apply a same-compartment security wrapper that
413 : // will stymie this whole process. If that happens, unwrap the wrapper.
414 : // This logic can go away when same-compartment security wrappers go away.
415 0 : if ((src == srcArgs.base() + 1) && dst->isObject()) {
416 0 : RootedObject thisObj(cx, &dst->toObject());
417 0 : if (thisObj->is<WrapperObject>() &&
418 0 : Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy())
419 : {
420 0 : MOZ_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>());
421 0 : *dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
422 : }
423 : }
424 : }
425 :
426 0 : if (!CallNonGenericMethod(cx, test, impl, dstArgs))
427 : return false;
428 :
429 0 : srcArgs.rval().set(dstArgs.rval());
430 : }
431 0 : return cx->compartment()->wrap(cx, srcArgs.rval());
432 : }
433 :
434 : bool
435 0 : CrossCompartmentWrapper::hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
436 : bool* bp) const
437 : {
438 0 : AutoRealm call(cx, wrappedObject(wrapper));
439 0 : if (!cx->compartment()->wrap(cx, v))
440 : return false;
441 0 : return Wrapper::hasInstance(cx, wrapper, v, bp);
442 : }
443 :
444 : const char*
445 0 : CrossCompartmentWrapper::className(JSContext* cx, HandleObject wrapper) const
446 : {
447 0 : AutoRealm call(cx, wrappedObject(wrapper));
448 0 : return Wrapper::className(cx, wrapper);
449 : }
450 :
451 : JSString*
452 0 : CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, bool isToSource) const
453 : {
454 0 : RootedString str(cx);
455 : {
456 0 : AutoRealm call(cx, wrappedObject(wrapper));
457 0 : str = Wrapper::fun_toString(cx, wrapper, isToSource);
458 0 : if (!str)
459 0 : return nullptr;
460 : }
461 0 : if (!cx->compartment()->wrap(cx, &str))
462 : return nullptr;
463 0 : return str;
464 : }
465 :
466 : RegExpShared*
467 0 : CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper) const
468 : {
469 0 : RootedRegExpShared re(cx);
470 : {
471 0 : AutoRealm call(cx, wrappedObject(wrapper));
472 0 : re = Wrapper::regexp_toShared(cx, wrapper);
473 0 : if (!re)
474 0 : return nullptr;
475 : }
476 :
477 : // Get an equivalent RegExpShared associated with the current compartment.
478 0 : RootedAtom source(cx, re->getSource());
479 0 : cx->markAtom(source);
480 0 : return cx->zone()->regExps.get(cx, source, re->getFlags());
481 : }
482 :
483 : bool
484 0 : CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx, HandleObject wrapper, MutableHandleValue vp) const
485 : {
486 0 : PIERCE(cx, wrapper,
487 : NOTHING,
488 : Wrapper::boxedValue_unbox(cx, wrapper, vp),
489 : cx->compartment()->wrap(cx, vp));
490 : }
491 :
492 : const CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
493 :
494 : bool
495 40259 : js::IsCrossCompartmentWrapper(const JSObject* obj)
496 : {
497 0 : return IsWrapper(obj) &&
498 64543 : !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
499 : }
500 :
501 : static void
502 43 : NukeRemovedCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
503 : {
504 43 : MOZ_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
505 :
506 43 : NotifyGCNukeWrapper(wrapper);
507 :
508 43 : wrapper->as<ProxyObject>().nuke();
509 :
510 0 : MOZ_ASSERT(IsDeadProxyObject(wrapper));
511 43 : }
512 :
513 : JS_FRIEND_API(void)
514 12 : js::NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
515 : {
516 0 : JS::Compartment* comp = wrapper->compartment();
517 0 : auto ptr = comp->lookupWrapper(Wrapper::wrappedObject(wrapper));
518 12 : if (ptr)
519 : comp->removeWrapper(ptr);
520 0 : NukeRemovedCrossCompartmentWrapper(cx, wrapper);
521 12 : }
522 :
523 : /*
524 : * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
525 : * all of the cross-compartment wrappers that point to objects parented to
526 : * obj's global. The snag here is that we need to avoid cutting wrappers that
527 : * point to the window object on page navigation (inner window destruction)
528 : * and only do that on tab close (outer window destruction). Thus the
529 : * option of how to handle the global object.
530 : */
531 : JS_FRIEND_API(bool)
532 5 : js::NukeCrossCompartmentWrappers(JSContext* cx,
533 : const CompartmentFilter& sourceFilter,
534 : JS::Compartment* target,
535 : js::NukeReferencesToWindow nukeReferencesToWindow,
536 : js::NukeReferencesFromTarget nukeReferencesFromTarget)
537 : {
538 0 : CHECK_REQUEST(cx);
539 5 : JSRuntime* rt = cx->runtime();
540 :
541 0 : for (CompartmentsIter c(rt); !c.done(); c.next()) {
542 0 : if (!sourceFilter.match(c))
543 46 : continue;
544 :
545 : // If the compartment matches both the source and target filter, we may
546 : // want to cut both incoming and outgoing wrappers.
547 0 : bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences &&
548 125 : target == c.get());
549 :
550 : // Iterate only the wrappers that have target compartment matched unless
551 : // |nukeAll| is true. The string wrappers that we're not interested in
552 : // won't be iterated, we can exclude them easily because they have
553 : // compartment nullptr. Use Maybe to avoid copying from conditionally
554 : // initializing NonStringWrapperEnum.
555 0 : mozilla::Maybe<Compartment::NonStringWrapperEnum> e;
556 0 : if (MOZ_LIKELY(!nukeAll))
557 125 : e.emplace(c, target);
558 : else
559 0 : e.emplace(c);
560 193 : for (; !e->empty(); e->popFront()) {
561 : // Skip debugger references because NukeCrossCompartmentWrapper()
562 : // doesn't know how to nuke them yet, see bug 1084626 for more
563 : // information.
564 0 : const CrossCompartmentKey& k = e->front().key();
565 0 : if (!k.is<JSObject*>())
566 3 : continue;
567 :
568 133 : AutoWrapperRooter wobj(cx, WrapperValue(*e));
569 :
570 : // Unwrap from the wrapped object in CrossCompartmentKey instead of
571 : // the wrapper, this could save us a bit of time.
572 34 : JSObject* wrapped = UncheckedUnwrap(k.as<JSObject*>());
573 :
574 : // We never nuke script source objects, since only ever used internally by the JS
575 : // engine, and are expected to remain valid throughout a scripts lifetime.
576 34 : if (MOZ_UNLIKELY(wrapped->is<ScriptSourceObject>())) {
577 : continue;
578 : }
579 :
580 : // We only skip nuking window references that point to a target
581 : // compartment, not the ones that belong to it.
582 0 : if (nukeReferencesToWindow == DontNukeWindowReferences &&
583 65 : MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped))
584 : {
585 : continue;
586 : }
587 :
588 : // Now this is the wrapper we want to nuke.
589 0 : e->removeFront();
590 31 : NukeRemovedCrossCompartmentWrapper(cx, wobj);
591 : }
592 : }
593 :
594 5 : return true;
595 : }
596 :
597 : // Given a cross-compartment wrapper |wobj|, update it to point to
598 : // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
599 : // useful even if wrapper already points to newTarget.
600 : // This operation crashes on failure rather than leaving the heap in an
601 : // inconsistent state.
602 : void
603 12 : js::RemapWrapper(JSContext* cx, JSObject* wobjArg, JSObject* newTargetArg)
604 : {
605 0 : MOZ_ASSERT(!IsInsideNursery(wobjArg));
606 12 : MOZ_ASSERT(!IsInsideNursery(newTargetArg));
607 :
608 0 : RootedObject wobj(cx, wobjArg);
609 0 : RootedObject newTarget(cx, newTargetArg);
610 0 : MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
611 0 : MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
612 0 : JSObject* origTarget = Wrapper::wrappedObject(wobj);
613 0 : MOZ_ASSERT(origTarget);
614 12 : MOZ_ASSERT(!JS_IsDeadWrapper(origTarget),
615 : "We don't want a dead proxy in the wrapper map");
616 0 : Value origv = ObjectValue(*origTarget);
617 0 : Realm* wrealm = wobj->realm();
618 24 : JS::Compartment* wcompartment = wobj->compartment();
619 :
620 24 : AutoDisableProxyCheck adpc;
621 :
622 : // If we're mapping to a different target (as opposed to just recomputing
623 : // for the same target), we must not have an existing wrapper for the new
624 : // target, otherwise this will break.
625 57 : MOZ_ASSERT_IF(origTarget != newTarget,
626 : !wcompartment->lookupWrapper(ObjectValue(*newTarget)));
627 :
628 : // The old value should still be in the cross-compartment wrapper map, and
629 : // the lookup should return wobj.
630 0 : WrapperMap::Ptr p = wcompartment->lookupWrapper(origv);
631 0 : MOZ_ASSERT(&p->value().unsafeGet()->toObject() == wobj);
632 12 : wcompartment->removeWrapper(p);
633 :
634 : // When we remove origv from the wrapper map, its wrapper, wobj, must
635 : // immediately cease to be a cross-compartment wrapper. Nuke it.
636 12 : NukeCrossCompartmentWrapper(cx, wobj);
637 :
638 : // First, we wrap it in the new compartment. We try to use the existing
639 : // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
640 : // the choice to reuse |wobj| or not.
641 0 : RootedObject tobj(cx, newTarget);
642 0 : AutoRealmUnchecked ar(cx, wrealm);
643 1 : if (!wcompartment->rewrap(cx, &tobj, wobj))
644 0 : MOZ_CRASH();
645 :
646 : // If wrap() reused |wobj|, it will have overwritten it and returned with
647 : // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
648 : // will still be nuked. In the latter case, we replace |wobj| with the
649 : // contents of the new wrapper in |tobj|.
650 12 : if (tobj != wobj) {
651 : // Now, because we need to maintain object identity, we do a brain
652 : // transplant on the old object so that it contains the contents of the
653 : // new one.
654 0 : if (!JSObject::swap(cx, wobj, tobj))
655 0 : MOZ_CRASH();
656 : }
657 :
658 : // Before swapping, this wrapper came out of wrap(), which enforces the
659 : // invariant that the wrapper in the map points directly to the key.
660 24 : MOZ_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
661 :
662 : // Update the entry in the compartment's wrapper map to point to the old
663 : // wrapper, which has now been updated (via reuse or swap).
664 0 : MOZ_ASSERT(wobj->is<WrapperObject>());
665 1 : if (!wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj)))
666 0 : MOZ_CRASH();
667 12 : }
668 :
669 : // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
670 : // |newTarget|. All wrappers are recomputed.
671 : JS_FRIEND_API(bool)
672 7 : js::RemapAllWrappersForObject(JSContext* cx, JSObject* oldTargetArg,
673 : JSObject* newTargetArg)
674 : {
675 0 : MOZ_ASSERT(!IsInsideNursery(oldTargetArg));
676 7 : MOZ_ASSERT(!IsInsideNursery(newTargetArg));
677 :
678 0 : RootedValue origv(cx, ObjectValue(*oldTargetArg));
679 14 : RootedObject newTarget(cx, newTargetArg);
680 :
681 21 : AutoWrapperVector toTransplant(cx);
682 :
683 0 : for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
684 609 : if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
685 : // We found a wrapper. Remember and root it.
686 1 : if (!toTransplant.append(WrapperValue(wp)))
687 0 : return false;
688 : }
689 : }
690 :
691 0 : for (const WrapperValue& v : toTransplant)
692 24 : RemapWrapper(cx, &v.toObject(), newTarget);
693 :
694 : return true;
695 : }
696 :
697 : JS_FRIEND_API(bool)
698 0 : js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
699 : const CompartmentFilter& targetFilter)
700 : {
701 0 : bool evictedNursery = false;
702 :
703 0 : AutoWrapperVector toRecompute(cx);
704 0 : for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
705 : // Filter by source compartment.
706 0 : if (!sourceFilter.match(c))
707 : continue;
708 :
709 0 : if (!evictedNursery && c->hasNurseryAllocatedWrapperEntries(targetFilter)) {
710 0 : cx->runtime()->gc.evictNursery();
711 0 : evictedNursery = true;
712 : }
713 :
714 : // Iterate over the wrappers, filtering appropriately.
715 0 : for (Compartment::NonStringWrapperEnum e(c, targetFilter); !e.empty(); e.popFront()) {
716 : // Filter out non-objects.
717 0 : CrossCompartmentKey& k = e.front().mutableKey();
718 0 : if (!k.is<JSObject*>())
719 : continue;
720 :
721 : // Add it to the list.
722 0 : if (!toRecompute.append(WrapperValue(e)))
723 0 : return false;
724 : }
725 : }
726 :
727 : // Recompute all the wrappers in the list.
728 0 : for (const WrapperValue& v : toRecompute) {
729 0 : JSObject* wrapper = &v.toObject();
730 0 : JSObject* wrapped = Wrapper::wrappedObject(wrapper);
731 : RemapWrapper(cx, wrapper, wrapped);
732 : }
733 :
734 : return true;
735 : }
|