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 "jsiter.h"
8 : #include "jswrapper.h"
9 :
10 : #include "proxy/DeadObjectProxy.h"
11 : #include "vm/WrapperObject.h"
12 :
13 : #include "jscompartmentinlines.h"
14 : #include "jsobjinlines.h"
15 :
16 : #include "gc/Nursery-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 : AutoCompartment 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 12328 : MarkAtoms(JSContext* cx, jsid id)
34 : {
35 12328 : cx->markId(id);
36 12328 : return true;
37 : }
38 :
39 : static bool
40 55 : MarkAtoms(JSContext* cx, const AutoIdVector& ids)
41 : {
42 333 : for (size_t i = 0; i < ids.length(); i++)
43 278 : cx->markId(ids[i]);
44 55 : 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 97 : CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
59 : MutableHandle<PropertyDescriptor> desc) const
60 : {
61 97 : 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 1787 : CrossCompartmentWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
69 : Handle<PropertyDescriptor> desc,
70 : ObjectOpResult& result) const
71 : {
72 3574 : Rooted<PropertyDescriptor> desc2(cx, desc);
73 3574 : 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 8 : CrossCompartmentWrapper::ownPropertyKeys(JSContext* cx, HandleObject wrapper,
81 : AutoIdVector& props) const
82 : {
83 8 : PIERCE(cx, wrapper,
84 : NOTHING,
85 : Wrapper::ownPropertyKeys(cx, wrapper, props),
86 : MarkAtoms(cx, props));
87 : }
88 :
89 : bool
90 370 : CrossCompartmentWrapper::delete_(JSContext* cx, HandleObject wrapper, HandleId id,
91 : ObjectOpResult& result) const
92 : {
93 370 : PIERCE(cx, wrapper,
94 : MarkAtoms(cx, id),
95 : Wrapper::delete_(cx, wrapper, id, result),
96 : NOTHING);
97 : }
98 :
99 : bool
100 53 : CrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject wrapper,
101 : MutableHandleObject protop) const
102 : {
103 : {
104 106 : RootedObject wrapped(cx, wrappedObject(wrapper));
105 106 : AutoCompartment call(cx, wrapped);
106 53 : if (!GetPrototype(cx, wrapped, protop))
107 0 : return false;
108 53 : if (protop) {
109 39 : if (!JSObject::setDelegate(cx, protop))
110 0 : return false;
111 : }
112 : }
113 :
114 53 : 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 5 : CrossCompartmentWrapper::getPrototypeIfOrdinary(JSContext* cx, HandleObject wrapper,
130 : bool* isOrdinary, MutableHandleObject protop) const
131 : {
132 : {
133 10 : RootedObject wrapped(cx, wrappedObject(wrapper));
134 10 : AutoCompartment call(cx, wrapped);
135 5 : if (!GetPrototypeIfOrdinary(cx, wrapped, isOrdinary, protop))
136 0 : return false;
137 :
138 5 : if (!*isOrdinary)
139 0 : return true;
140 :
141 5 : if (protop) {
142 3 : if (!JSObject::setDelegate(cx, protop))
143 0 : return false;
144 : }
145 : }
146 :
147 5 : 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 1 : CrossCompartmentWrapper::preventExtensions(JSContext* cx, HandleObject wrapper,
161 : ObjectOpResult& result) const
162 : {
163 1 : 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 355 : CrossCompartmentWrapper::has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
180 : {
181 355 : PIERCE(cx, wrapper,
182 : MarkAtoms(cx, id),
183 : Wrapper::has(cx, wrapper, id, bp),
184 : NOTHING);
185 : }
186 :
187 : bool
188 44 : CrossCompartmentWrapper::hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const
189 : {
190 44 : PIERCE(cx, wrapper,
191 : MarkAtoms(cx, id),
192 : Wrapper::hasOwn(cx, wrapper, id, bp),
193 : NOTHING);
194 : }
195 :
196 : static bool
197 9675 : 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 9675 : if (ObjectValue(*wrapper) == receiver) {
204 9484 : JSObject* wrapped = Wrapper::wrappedObject(wrapper);
205 9484 : if (!IsWrapper(wrapped)) {
206 9340 : MOZ_ASSERT(wrapped->compartment() == cx->compartment());
207 9340 : MOZ_ASSERT(!IsWindow(wrapped));
208 9340 : receiver.setObject(*wrapped);
209 9340 : return true;
210 : }
211 : }
212 :
213 335 : return cx->compartment()->wrap(cx, receiver);
214 : }
215 :
216 : bool
217 9314 : CrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
218 : HandleId id, MutableHandleValue vp) const
219 : {
220 18628 : RootedValue receiverCopy(cx, receiver);
221 : {
222 18628 : AutoCompartment call(cx, wrappedObject(wrapper));
223 9314 : if (!MarkAtoms(cx, id) || !WrapReceiver(cx, wrapper, &receiverCopy))
224 0 : return false;
225 :
226 9314 : if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp))
227 0 : return false;
228 : }
229 9314 : return cx->compartment()->wrap(cx, vp);
230 : }
231 :
232 : bool
233 361 : CrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
234 : HandleValue receiver, ObjectOpResult& result) const
235 : {
236 722 : RootedValue valCopy(cx, v);
237 722 : RootedValue receiverCopy(cx, receiver);
238 722 : 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 47 : CrossCompartmentWrapper::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
248 : AutoIdVector& props) const
249 : {
250 47 : 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 5 : CanReify(HandleObject obj)
262 : {
263 10 : return obj->is<PropertyIteratorObject>() &&
264 10 : (obj->as<PropertyIteratorObject>().getNativeIterator()->flags & JSITER_ENUMERATE);
265 : }
266 :
267 : struct AutoCloseIterator
268 : {
269 0 : AutoCloseIterator(JSContext* cx, PropertyIteratorObject* obj) : cx(cx), obj(cx, obj) {}
270 :
271 0 : ~AutoCloseIterator() {
272 0 : if (obj)
273 0 : MOZ_ALWAYS_TRUE(CloseIterator(cx, obj));
274 0 : }
275 :
276 0 : void clear() { obj = nullptr; }
277 :
278 : private:
279 : JSContext* cx;
280 : Rooted<PropertyIteratorObject*> obj;
281 : };
282 :
283 : static JSObject*
284 0 : Reify(JSContext* cx, JSCompartment* origin, HandleObject objp)
285 : {
286 0 : Rooted<PropertyIteratorObject*> iterObj(cx, &objp->as<PropertyIteratorObject>());
287 0 : NativeIterator* ni = iterObj->getNativeIterator();
288 :
289 0 : RootedObject obj(cx, ni->obj);
290 : {
291 0 : AutoCloseIterator close(cx, iterObj);
292 :
293 : /* Wrap the iteratee. */
294 0 : if (!origin->wrap(cx, &obj))
295 0 : return nullptr;
296 :
297 : /*
298 : * Wrap the elements in the iterator's snapshot.
299 : * N.B. the order of closing/creating iterators is important due to the
300 : * implicit cx->enumerators state.
301 : */
302 0 : size_t length = ni->numKeys();
303 0 : AutoIdVector keys(cx);
304 0 : if (length > 0) {
305 0 : if (!keys.reserve(length))
306 0 : return nullptr;
307 0 : for (size_t i = 0; i < length; ++i) {
308 0 : RootedId id(cx);
309 0 : RootedValue v(cx, StringValue(ni->begin()[i]));
310 0 : if (!ValueToId<CanGC>(cx, v, &id))
311 0 : return nullptr;
312 0 : keys.infallibleAppend(id);
313 : }
314 : }
315 :
316 0 : close.clear();
317 0 : MOZ_ALWAYS_TRUE(CloseIterator(cx, iterObj));
318 :
319 0 : obj = EnumeratedIdVectorToIterator(cx, obj, ni->flags, keys);
320 : }
321 0 : return obj;
322 : }
323 :
324 : JSObject*
325 5 : CrossCompartmentWrapper::enumerate(JSContext* cx, HandleObject wrapper) const
326 : {
327 10 : RootedObject res(cx);
328 : {
329 10 : AutoCompartment call(cx, wrappedObject(wrapper));
330 5 : res = Wrapper::enumerate(cx, wrapper);
331 5 : if (!res)
332 0 : return nullptr;
333 : }
334 :
335 5 : if (CanReify(res))
336 0 : return Reify(cx, cx->compartment(), res);
337 5 : if (!cx->compartment()->wrap(cx, &res))
338 0 : return nullptr;
339 5 : return res;
340 : }
341 :
342 : bool
343 4925 : CrossCompartmentWrapper::call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
344 : {
345 9850 : RootedObject wrapped(cx, wrappedObject(wrapper));
346 :
347 : {
348 9763 : AutoCompartment call(cx, wrapped);
349 :
350 4925 : args.setCallee(ObjectValue(*wrapped));
351 4925 : if (!cx->compartment()->wrap(cx, args.mutableThisv()))
352 0 : return false;
353 :
354 12948 : for (size_t n = 0; n < args.length(); ++n) {
355 8023 : if (!cx->compartment()->wrap(cx, args[n]))
356 0 : return false;
357 : }
358 :
359 4925 : if (!Wrapper::call(cx, wrapper, args))
360 87 : return false;
361 : }
362 :
363 4838 : return cx->compartment()->wrap(cx, args.rval());
364 : }
365 :
366 : bool
367 175 : CrossCompartmentWrapper::construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const
368 : {
369 350 : RootedObject wrapped(cx, wrappedObject(wrapper));
370 : {
371 350 : AutoCompartment call(cx, wrapped);
372 :
373 326 : for (size_t n = 0; n < args.length(); ++n) {
374 151 : if (!cx->compartment()->wrap(cx, args[n]))
375 0 : return false;
376 : }
377 175 : if (!cx->compartment()->wrap(cx, args.newTarget()))
378 0 : return false;
379 175 : if (!Wrapper::construct(cx, wrapper, args))
380 0 : return false;
381 : }
382 175 : 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 : AutoCompartment 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 0 : 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 24 : CrossCompartmentWrapper::hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
436 : bool* bp) const
437 : {
438 48 : AutoCompartment call(cx, wrappedObject(wrapper));
439 24 : if (!cx->compartment()->wrap(cx, v))
440 0 : return false;
441 24 : return Wrapper::hasInstance(cx, wrapper, v, bp);
442 : }
443 :
444 : const char*
445 0 : CrossCompartmentWrapper::className(JSContext* cx, HandleObject wrapper) const
446 : {
447 0 : AutoCompartment call(cx, wrappedObject(wrapper));
448 0 : return Wrapper::className(cx, wrapper);
449 : }
450 :
451 : JSString*
452 0 : CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, unsigned indent) const
453 : {
454 0 : RootedString str(cx);
455 : {
456 0 : AutoCompartment call(cx, wrappedObject(wrapper));
457 0 : str = Wrapper::fun_toString(cx, wrapper, indent);
458 0 : if (!str)
459 0 : return nullptr;
460 : }
461 0 : if (!cx->compartment()->wrap(cx, &str))
462 0 : 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 : AutoCompartment 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 34748 : js::IsCrossCompartmentWrapper(JSObject* obj)
496 : {
497 62481 : return IsWrapper(obj) &&
498 62481 : !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
499 : }
500 :
501 : static void
502 44 : NukeRemovedCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
503 : {
504 44 : MOZ_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
505 :
506 44 : NotifyGCNukeWrapper(wrapper);
507 :
508 44 : wrapper->as<ProxyObject>().nuke();
509 :
510 44 : MOZ_ASSERT(IsDeadProxyObject(wrapper));
511 44 : }
512 :
513 : JS_FRIEND_API(void)
514 10 : js::NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
515 : {
516 10 : JSCompartment* comp = wrapper->compartment();
517 10 : auto ptr = comp->lookupWrapper(Wrapper::wrappedObject(wrapper));
518 10 : if (ptr)
519 0 : comp->removeWrapper(ptr);
520 10 : NukeRemovedCrossCompartmentWrapper(cx, wrapper);
521 10 : }
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 3 : js::NukeCrossCompartmentWrappers(JSContext* cx,
533 : const CompartmentFilter& sourceFilter,
534 : JSCompartment* target,
535 : js::NukeReferencesToWindow nukeReferencesToWindow,
536 : js::NukeReferencesFromTarget nukeReferencesFromTarget)
537 : {
538 6 : CHECK_REQUEST(cx);
539 3 : JSRuntime* rt = cx->runtime();
540 :
541 440 : for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
542 437 : if (!sourceFilter.match(c))
543 40 : 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 397 : bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences &&
548 397 : 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 794 : mozilla::Maybe<JSCompartment::NonStringWrapperEnum> e;
556 397 : if (MOZ_LIKELY(!nukeAll))
557 397 : e.emplace(c, target);
558 : else
559 0 : e.emplace(c);
560 471 : 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 37 : const CrossCompartmentKey& k = e->front().key();
565 37 : if (!k.is<JSObject*>())
566 3 : continue;
567 :
568 71 : 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 37 : 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 37 : if (MOZ_UNLIKELY(wrapped->is<ScriptSourceObject>())) {
577 0 : 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 71 : if (nukeReferencesToWindow == DontNukeWindowReferences &&
583 71 : MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped))
584 : {
585 3 : continue;
586 : }
587 :
588 : // Now this is the wrapper we want to nuke.
589 34 : e->removeFront();
590 34 : NukeRemovedCrossCompartmentWrapper(cx, wobj);
591 : }
592 : }
593 :
594 6 : 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 10 : js::RemapWrapper(JSContext* cx, JSObject* wobjArg, JSObject* newTargetArg)
604 : {
605 10 : MOZ_ASSERT(!IsInsideNursery(wobjArg));
606 10 : MOZ_ASSERT(!IsInsideNursery(newTargetArg));
607 :
608 20 : RootedObject wobj(cx, wobjArg);
609 20 : RootedObject newTarget(cx, newTargetArg);
610 10 : MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
611 10 : MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
612 10 : JSObject* origTarget = Wrapper::wrappedObject(wobj);
613 10 : MOZ_ASSERT(origTarget);
614 10 : Value origv = ObjectValue(*origTarget);
615 10 : JSCompartment* wcompartment = wobj->compartment();
616 :
617 20 : AutoDisableProxyCheck adpc;
618 :
619 : // If we're mapping to a different target (as opposed to just recomputing
620 : // for the same target), we must not have an existing wrapper for the new
621 : // target, otherwise this will break.
622 10 : MOZ_ASSERT_IF(origTarget != newTarget,
623 : !wcompartment->lookupWrapper(ObjectValue(*newTarget)));
624 :
625 : // The old value should still be in the cross-compartment wrapper map, and
626 : // the lookup should return wobj.
627 10 : WrapperMap::Ptr p = wcompartment->lookupWrapper(origv);
628 10 : MOZ_ASSERT(&p->value().unsafeGet()->toObject() == wobj);
629 10 : wcompartment->removeWrapper(p);
630 :
631 : // When we remove origv from the wrapper map, its wrapper, wobj, must
632 : // immediately cease to be a cross-compartment wrapper. Nuke it.
633 10 : NukeCrossCompartmentWrapper(cx, wobj);
634 :
635 : // First, we wrap it in the new compartment. We try to use the existing
636 : // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
637 : // the choice to reuse |wobj| or not.
638 20 : RootedObject tobj(cx, newTarget);
639 20 : AutoCompartmentUnchecked ac(cx, wcompartment);
640 10 : if (!wcompartment->rewrap(cx, &tobj, wobj))
641 0 : MOZ_CRASH();
642 :
643 : // If wrap() reused |wobj|, it will have overwritten it and returned with
644 : // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
645 : // will still be nuked. In the latter case, we replace |wobj| with the
646 : // contents of the new wrapper in |tobj|.
647 10 : if (tobj != wobj) {
648 : // Now, because we need to maintain object identity, we do a brain
649 : // transplant on the old object so that it contains the contents of the
650 : // new one.
651 0 : if (!JSObject::swap(cx, wobj, tobj))
652 0 : MOZ_CRASH();
653 : }
654 :
655 : // Before swapping, this wrapper came out of wrap(), which enforces the
656 : // invariant that the wrapper in the map points directly to the key.
657 10 : MOZ_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
658 :
659 : // Update the entry in the compartment's wrapper map to point to the old
660 : // wrapper, which has now been updated (via reuse or swap).
661 10 : MOZ_ASSERT(wobj->is<WrapperObject>());
662 10 : if (!wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj)))
663 0 : MOZ_CRASH();
664 10 : }
665 :
666 : // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
667 : // |newTarget|. All wrappers are recomputed.
668 : JS_FRIEND_API(bool)
669 3 : js::RemapAllWrappersForObject(JSContext* cx, JSObject* oldTargetArg,
670 : JSObject* newTargetArg)
671 : {
672 3 : MOZ_ASSERT(!IsInsideNursery(oldTargetArg));
673 3 : MOZ_ASSERT(!IsInsideNursery(newTargetArg));
674 :
675 6 : RootedValue origv(cx, ObjectValue(*oldTargetArg));
676 6 : RootedObject newTarget(cx, newTargetArg);
677 :
678 6 : AutoWrapperVector toTransplant(cx);
679 3 : if (!toTransplant.reserve(cx->runtime()->numCompartments))
680 0 : return false;
681 :
682 329 : for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
683 326 : if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
684 : // We found a wrapper. Remember and root it.
685 10 : toTransplant.infallibleAppend(WrapperValue(wp));
686 : }
687 : }
688 :
689 13 : for (const WrapperValue& v : toTransplant)
690 10 : RemapWrapper(cx, &v.toObject(), newTarget);
691 :
692 3 : return true;
693 : }
694 :
695 : JS_FRIEND_API(bool)
696 3 : js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
697 : const CompartmentFilter& targetFilter)
698 : {
699 3 : bool evictedNursery = false;
700 :
701 6 : AutoWrapperVector toRecompute(cx);
702 499 : for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
703 : // Filter by source compartment.
704 496 : if (!sourceFilter.match(c))
705 480 : continue;
706 :
707 16 : if (!evictedNursery && c->hasNurseryAllocatedWrapperEntries(targetFilter)) {
708 0 : EvictAllNurseries(cx->runtime());
709 0 : evictedNursery = true;
710 : }
711 :
712 : // Iterate over the wrappers, filtering appropriately.
713 16 : for (JSCompartment::NonStringWrapperEnum e(c, targetFilter); !e.empty(); e.popFront()) {
714 : // Filter out non-objects.
715 0 : CrossCompartmentKey& k = e.front().mutableKey();
716 0 : if (!k.is<JSObject*>())
717 0 : continue;
718 :
719 : // Add it to the list.
720 0 : if (!toRecompute.append(WrapperValue(e)))
721 0 : return false;
722 : }
723 : }
724 :
725 : // Recompute all the wrappers in the list.
726 3 : for (const WrapperValue& v : toRecompute) {
727 0 : JSObject* wrapper = &v.toObject();
728 0 : JSObject* wrapped = Wrapper::wrappedObject(wrapper);
729 0 : RemapWrapper(cx, wrapper, wrapped);
730 : }
731 :
732 3 : return true;
733 : }
|