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
JS_FRIEND_API void js::NukeCrossCompartmentWrapper(JSContext* cx,
376
JSObject* wrapper) {
377
JS::Compartment* comp = wrapper->compartment();
378
auto ptr = comp->lookupWrapper(Wrapper::wrappedObject(wrapper));
379
if (ptr) {
380
comp->removeWrapper(ptr);
381
}
382
NukeRemovedCrossCompartmentWrapper(cx, wrapper);
383
}
384
385
JS_FRIEND_API void js::NukeCrossCompartmentWrapperIfExists(
386
JSContext* cx, JS::Compartment* source, JSObject* target) {
387
MOZ_ASSERT(source != target->compartment());
388
MOZ_ASSERT(!target->is<CrossCompartmentWrapperObject>());
389
auto ptr = source->lookupWrapper(target);
390
if (ptr) {
391
JSObject* wrapper = ptr->value().get();
392
NukeCrossCompartmentWrapper(cx, wrapper);
393
}
394
}
395
396
// Returns true iff all realms in the compartment have been nuked.
397
static bool NukedAllRealms(JS::Compartment* comp) {
398
for (RealmsInCompartmentIter realm(comp); !realm.done(); realm.next()) {
399
if (!realm->nukedIncomingWrappers) {
400
return false;
401
}
402
}
403
return true;
404
}
405
406
/*
407
* NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
408
* all of the cross-compartment wrappers that point to an object in the |target|
409
* realm. The snag here is that we need to avoid cutting wrappers that point to
410
* the window object on page navigation (inner window destruction) and only do
411
* that on tab close (outer window destruction). Thus the option of how to
412
* handle the global object.
413
*/
414
JS_FRIEND_API bool js::NukeCrossCompartmentWrappers(
415
JSContext* cx, const CompartmentFilter& sourceFilter, JS::Realm* target,
416
js::NukeReferencesToWindow nukeReferencesToWindow,
417
js::NukeReferencesFromTarget nukeReferencesFromTarget) {
418
CHECK_THREAD(cx);
419
JSRuntime* rt = cx->runtime();
420
421
// If we're nuking all wrappers into the target realm, prevent us from
422
// creating new wrappers for it in the future.
423
if (nukeReferencesFromTarget == NukeAllReferences) {
424
target->nukedIncomingWrappers = true;
425
}
426
427
for (CompartmentsIter c(rt); !c.done(); c.next()) {
428
if (!sourceFilter.match(c)) {
429
continue;
430
}
431
432
// If the realm matches both the source and target filter, we may want to
433
// cut outgoing wrappers too, if we nuked all realms in the compartment.
434
bool nukeAll =
435
(nukeReferencesFromTarget == NukeAllReferences &&
436
target->compartment() == c.get() && NukedAllRealms(c.get()));
437
438
// Iterate only the wrappers that have target compartment matched unless
439
// |nukeAll| is true. Use Maybe to avoid copying from conditionally
440
// initializing ObjectWrapperEnum.
441
mozilla::Maybe<Compartment::ObjectWrapperEnum> e;
442
if (MOZ_LIKELY(!nukeAll)) {
443
e.emplace(c, target->compartment());
444
} else {
445
e.emplace(c);
446
c.get()->nukedOutgoingWrappers = true;
447
}
448
for (; !e->empty(); e->popFront()) {
449
JSObject* key = e->front().key();
450
451
AutoWrapperRooter wobj(cx, WrapperValue(*e));
452
453
// Unwrap from the wrapped object in key instead of the wrapper, this
454
// could save us a bit of time.
455
JSObject* wrapped = UncheckedUnwrap(key);
456
457
// Don't nuke wrappers for objects in other realms in the target
458
// compartment unless nukeAll is set because in that case we want to nuke
459
// all outgoing wrappers for the current compartment.
460
if (!nukeAll && wrapped->nonCCWRealm() != target) {
461
continue;
462
}
463
464
// We never nuke ScriptSourceObjects, since they are only ever used
465
// internally by the JS engine, and are expected to remain valid
466
// throughout a script's lifetime.
467
if (MOZ_UNLIKELY(wrapped->is<ScriptSourceObject>())) {
468
continue;
469
}
470
471
// We only skip nuking window references that point to a target
472
// compartment, not the ones that belong to it.
473
if (nukeReferencesToWindow == DontNukeWindowReferences &&
474
MOZ_LIKELY(!nukeAll) && IsWindowProxy(wrapped)) {
475
continue;
476
}
477
478
// Now this is the wrapper we want to nuke.
479
e->removeFront();
480
NukeRemovedCrossCompartmentWrapper(cx, wobj);
481
}
482
}
483
484
return true;
485
}
486
487
JS_FRIEND_API bool js::AllowNewWrapper(JS::Compartment* target, JSObject* obj) {
488
// Disallow creating new wrappers if we nuked the object realm or target
489
// compartment. However, we always need to provide live wrappers for
490
// ScriptSourceObjects, since they're used for cross-compartment cloned
491
// scripts, and need to remain accessible even after the original realm has
492
// been nuked.
493
494
MOZ_ASSERT(obj->compartment() != target);
495
496
if (obj->is<ScriptSourceObject>()) {
497
return true;
498
}
499
500
if (target->nukedOutgoingWrappers ||
501
obj->nonCCWRealm()->nukedIncomingWrappers) {
502
return false;
503
}
504
505
return true;
506
}
507
508
JS_FRIEND_API bool js::NukedObjectRealm(JSObject* obj) {
509
return obj->nonCCWRealm()->nukedIncomingWrappers;
510
}
511
512
// Given a cross-compartment wrapper |wobj|, update it to point to
513
// |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
514
// useful even if wrapper already points to newTarget.
515
// This operation crashes on failure rather than leaving the heap in an
516
// inconsistent state.
517
void js::RemapWrapper(JSContext* cx, JSObject* wobjArg,
518
JSObject* newTargetArg) {
519
MOZ_ASSERT(!IsInsideNursery(wobjArg));
520
MOZ_ASSERT(!IsInsideNursery(newTargetArg));
521
522
RootedObject wobj(cx, wobjArg);
523
RootedObject newTarget(cx, newTargetArg);
524
MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
525
MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
526
JSObject* origTarget = Wrapper::wrappedObject(wobj);
527
MOZ_ASSERT(origTarget);
528
MOZ_ASSERT(!JS_IsDeadWrapper(origTarget),
529
"We don't want a dead proxy in the wrapper map");
530
JS::Compartment* wcompartment = wobj->compartment();
531
MOZ_ASSERT(wcompartment != newTarget->compartment());
532
533
AutoDisableProxyCheck adpc;
534
535
// If we're mapping to a different target (as opposed to just recomputing
536
// for the same target), we must not have an existing wrapper for the new
537
// target, otherwise this will break.
538
MOZ_ASSERT_IF(origTarget != newTarget,
539
!wcompartment->lookupWrapper(newTarget));
540
541
// The old value should still be in the cross-compartment wrapper map, and
542
// the lookup should return wobj.
543
ObjectWrapperMap::Ptr p = wcompartment->lookupWrapper(origTarget);
544
MOZ_ASSERT(*p->value().unsafeGet() == wobj);
545
wcompartment->removeWrapper(p);
546
547
// When we remove origv from the wrapper map, its wrapper, wobj, must
548
// immediately cease to be a cross-compartment wrapper. Nuke it.
549
NukeCrossCompartmentWrapper(cx, wobj);
550
js::RemapDeadWrapper(cx, wobj, newTarget);
551
}
552
553
// Given a dead proxy object |wobj|, turn it into a cross-compartment wrapper
554
// pointing at |newTarget|.
555
// This operation crashes on failure rather than leaving the heap in an
556
// inconsistent state.
557
void js::RemapDeadWrapper(JSContext* cx, HandleObject wobj,
558
HandleObject newTarget) {
559
MOZ_ASSERT(IsDeadProxyObject(wobj));
560
MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
561
562
AutoDisableProxyCheck adpc;
563
564
// wobj is not a cross-compartment wrapper, so we can use nonCCWRealm.
565
Realm* wrealm = wobj->nonCCWRealm();
566
567
// First, we wrap it in the new compartment. We try to use the existing
568
// wrapper, |wobj|, since it's been nuked anyway. The rewrap() function has
569
// the choice to reuse |wobj| or not.
570
RootedObject tobj(cx, newTarget);
571
AutoRealmUnchecked ar(cx, wrealm);
572
AutoEnterOOMUnsafeRegion oomUnsafe;
573
JS::Compartment* wcompartment = wobj->compartment();
574
if (!wcompartment->rewrap(cx, &tobj, wobj)) {
575
oomUnsafe.crash("js::RemapWrapper");
576
}
577
578
// If rewrap() reused |wobj|, it will have overwritten it and returned with
579
// |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
580
// will still be nuked. In the latter case, we replace |wobj| with the
581
// contents of the new wrapper in |tobj|.
582
if (tobj != wobj) {
583
// Now, because we need to maintain object identity, we do a brain
584
// transplant on the old object so that it contains the contents of the
585
// new one.
586
JSObject::swap(cx, wobj, tobj);
587
}
588
589
if (!wobj->is<WrapperObject>()) {
590
MOZ_ASSERT(js::IsDOMRemoteProxyObject(wobj));
591
return;
592
}
593
594
// Before swapping, this wrapper came out of rewrap(), which enforces the
595
// invariant that the wrapper in the map points directly to the key.
596
MOZ_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
597
598
// Update the entry in the compartment's wrapper map to point to the old
599
// wrapper, which has now been updated (via reuse or swap).
600
if (!wcompartment->putWrapper(cx, newTarget, wobj)) {
601
oomUnsafe.crash("js::RemapWrapper");
602
}
603
}
604
605
// Remap all cross-compartment wrappers pointing to |oldTarget| to point to
606
// |newTarget|. All wrappers are recomputed.
607
JS_FRIEND_API bool js::RemapAllWrappersForObject(JSContext* cx,
608
HandleObject oldTarget,
609
HandleObject newTarget) {
610
MOZ_ASSERT(!IsInsideNursery(oldTarget));
611
MOZ_ASSERT(!IsInsideNursery(newTarget));
612
613
AutoWrapperVector toTransplant(cx);
614
615
for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
616
if (ObjectWrapperMap::Ptr wp = c->lookupWrapper(oldTarget)) {
617
// We found a wrapper. Remember and root it.
618
if (!toTransplant.append(WrapperValue(wp))) {
619
return false;
620
}
621
}
622
}
623
624
for (const WrapperValue& v : toTransplant) {
625
RemapWrapper(cx, v, newTarget);
626
}
627
628
return true;
629
}
630
631
JS_FRIEND_API bool js::RecomputeWrappers(
632
JSContext* cx, const CompartmentFilter& sourceFilter,
633
const CompartmentFilter& targetFilter) {
634
bool evictedNursery = false;
635
636
AutoWrapperVector toRecompute(cx);
637
for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
638
// Filter by source compartment.
639
if (!sourceFilter.match(c)) {
640
continue;
641
}
642
643
if (!evictedNursery &&
644
c->hasNurseryAllocatedObjectWrapperEntries(targetFilter)) {
645
cx->runtime()->gc.evictNursery();
646
evictedNursery = true;
647
}
648
649
// Iterate over object wrappers, filtering appropriately.
650
for (Compartment::ObjectWrapperEnum e(c, targetFilter); !e.empty();
651
e.popFront()) {
652
// Add the wrapper to the list.
653
if (!toRecompute.append(WrapperValue(e))) {
654
return false;
655
}
656
}
657
}
658
659
// Recompute all the wrappers in the list.
660
for (const WrapperValue& wrapper : toRecompute) {
661
JSObject* wrapped = Wrapper::wrappedObject(wrapper);
662
RemapWrapper(cx, wrapper, wrapped);
663
}
664
665
return true;
666
}