Source code

Revision control

Other Tools

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