js/src/jswrapper.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jswrapper.h"
michael@0 8
michael@0 9 #include "jscntxt.h"
michael@0 10 #include "jscompartment.h"
michael@0 11 #include "jsexn.h"
michael@0 12 #include "jsgc.h"
michael@0 13 #include "jsiter.h"
michael@0 14
michael@0 15 #include "vm/ErrorObject.h"
michael@0 16 #include "vm/WrapperObject.h"
michael@0 17
michael@0 18 #include "jsobjinlines.h"
michael@0 19
michael@0 20 using namespace js;
michael@0 21 using namespace js::gc;
michael@0 22
michael@0 23 const char js::sWrapperFamily = 0;
michael@0 24
michael@0 25 /*
michael@0 26 * Wrapper forwards this call directly to the wrapped object for efficiency
michael@0 27 * and transparency. In particular, the hint is needed to properly stringify
michael@0 28 * Date objects in certain cases - see bug 646129. Note also the
michael@0 29 * SecurityWrapper overrides this trap to avoid information leaks. See bug
michael@0 30 * 720619.
michael@0 31 */
michael@0 32 bool
michael@0 33 Wrapper::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp)
michael@0 34 {
michael@0 35 vp.set(ObjectValue(*proxy->as<ProxyObject>().target()));
michael@0 36 if (hint == JSTYPE_VOID)
michael@0 37 return ToPrimitive(cx, vp);
michael@0 38 return ToPrimitive(cx, hint, vp);
michael@0 39 }
michael@0 40
michael@0 41 JSObject *
michael@0 42 Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler,
michael@0 43 const WrapperOptions *options)
michael@0 44 {
michael@0 45 JS_ASSERT(parent);
michael@0 46
michael@0 47 AutoMarkInDeadZone amd(cx->zone());
michael@0 48
michael@0 49 RootedValue priv(cx, ObjectValue(*obj));
michael@0 50 mozilla::Maybe<WrapperOptions> opts;
michael@0 51 if (!options) {
michael@0 52 opts.construct();
michael@0 53 opts.ref().selectDefaultClass(obj->isCallable());
michael@0 54 options = opts.addr();
michael@0 55 }
michael@0 56 return NewProxyObject(cx, handler, priv, options->proto(), parent, *options);
michael@0 57 }
michael@0 58
michael@0 59 JSObject *
michael@0 60 Wrapper::Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler)
michael@0 61 {
michael@0 62 JS_ASSERT(!obj->isCallable());
michael@0 63 existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj));
michael@0 64 return existing;
michael@0 65 }
michael@0 66
michael@0 67 Wrapper *
michael@0 68 Wrapper::wrapperHandler(JSObject *wrapper)
michael@0 69 {
michael@0 70 JS_ASSERT(wrapper->is<WrapperObject>());
michael@0 71 return static_cast<Wrapper*>(wrapper->as<ProxyObject>().handler());
michael@0 72 }
michael@0 73
michael@0 74 JSObject *
michael@0 75 Wrapper::wrappedObject(JSObject *wrapper)
michael@0 76 {
michael@0 77 JS_ASSERT(wrapper->is<WrapperObject>());
michael@0 78 return wrapper->as<ProxyObject>().target();
michael@0 79 }
michael@0 80
michael@0 81 JS_FRIEND_API(JSObject *)
michael@0 82 js::UncheckedUnwrap(JSObject *wrapped, bool stopAtOuter, unsigned *flagsp)
michael@0 83 {
michael@0 84 unsigned flags = 0;
michael@0 85 while (true) {
michael@0 86 if (!wrapped->is<WrapperObject>() ||
michael@0 87 MOZ_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject))
michael@0 88 {
michael@0 89 break;
michael@0 90 }
michael@0 91 flags |= Wrapper::wrapperHandler(wrapped)->flags();
michael@0 92 wrapped = wrapped->as<ProxyObject>().private_().toObjectOrNull();
michael@0 93 }
michael@0 94 if (flagsp)
michael@0 95 *flagsp = flags;
michael@0 96 return wrapped;
michael@0 97 }
michael@0 98
michael@0 99 JS_FRIEND_API(JSObject *)
michael@0 100 js::CheckedUnwrap(JSObject *obj, bool stopAtOuter)
michael@0 101 {
michael@0 102 while (true) {
michael@0 103 JSObject *wrapper = obj;
michael@0 104 obj = UnwrapOneChecked(obj, stopAtOuter);
michael@0 105 if (!obj || obj == wrapper)
michael@0 106 return obj;
michael@0 107 }
michael@0 108 }
michael@0 109
michael@0 110 JS_FRIEND_API(JSObject *)
michael@0 111 js::UnwrapOneChecked(JSObject *obj, bool stopAtOuter)
michael@0 112 {
michael@0 113 if (!obj->is<WrapperObject>() ||
michael@0 114 MOZ_UNLIKELY(!!obj->getClass()->ext.innerObject && stopAtOuter))
michael@0 115 {
michael@0 116 return obj;
michael@0 117 }
michael@0 118
michael@0 119 Wrapper *handler = Wrapper::wrapperHandler(obj);
michael@0 120 return handler->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj);
michael@0 121 }
michael@0 122
michael@0 123 bool
michael@0 124 js::IsCrossCompartmentWrapper(JSObject *obj)
michael@0 125 {
michael@0 126 return IsWrapper(obj) &&
michael@0 127 !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
michael@0 128 }
michael@0 129
michael@0 130 Wrapper::Wrapper(unsigned flags, bool hasPrototype) : DirectProxyHandler(&sWrapperFamily)
michael@0 131 , mFlags(flags)
michael@0 132 {
michael@0 133 setHasPrototype(hasPrototype);
michael@0 134 }
michael@0 135
michael@0 136 Wrapper::~Wrapper()
michael@0 137 {
michael@0 138 }
michael@0 139
michael@0 140 Wrapper Wrapper::singleton((unsigned)0);
michael@0 141 Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
michael@0 142 JSObject *Wrapper::defaultProto = TaggedProto::LazyProto;
michael@0 143
michael@0 144 /* Compartments. */
michael@0 145
michael@0 146 extern JSObject *
michael@0 147 js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
michael@0 148 HandleObject wrappedProto, HandleObject parent,
michael@0 149 unsigned flags)
michael@0 150 {
michael@0 151 // Allow wrapping outer window proxies.
michael@0 152 JS_ASSERT(!obj->is<WrapperObject>() || obj->getClass()->ext.innerObject);
michael@0 153 JS_ASSERT(wrappedProto == TaggedProto::LazyProto);
michael@0 154 return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton);
michael@0 155 }
michael@0 156
michael@0 157 ErrorCopier::~ErrorCopier()
michael@0 158 {
michael@0 159 JSContext *cx = ac.ref().context()->asJSContext();
michael@0 160 if (ac.ref().origin() != cx->compartment() && cx->isExceptionPending()) {
michael@0 161 RootedValue exc(cx);
michael@0 162 if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
michael@0 163 cx->clearPendingException();
michael@0 164 ac.destroy();
michael@0 165 Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
michael@0 166 JSObject *copyobj = js_CopyErrorObject(cx, errObj, scope);
michael@0 167 if (copyobj)
michael@0 168 cx->setPendingException(ObjectValue(*copyobj));
michael@0 169 }
michael@0 170 }
michael@0 171 }
michael@0 172
michael@0 173 /* Cross compartment wrappers. */
michael@0 174
michael@0 175 CrossCompartmentWrapper::CrossCompartmentWrapper(unsigned flags, bool hasPrototype)
michael@0 176 : Wrapper(CROSS_COMPARTMENT | flags, hasPrototype)
michael@0 177 {
michael@0 178 }
michael@0 179
michael@0 180 CrossCompartmentWrapper::~CrossCompartmentWrapper()
michael@0 181 {
michael@0 182 }
michael@0 183
michael@0 184 bool Wrapper::finalizeInBackground(Value priv)
michael@0 185 {
michael@0 186 if (!priv.isObject())
michael@0 187 return true;
michael@0 188
michael@0 189 /*
michael@0 190 * Make the 'background-finalized-ness' of the wrapper the same as the
michael@0 191 * wrapped object, to allow transplanting between them.
michael@0 192 *
michael@0 193 * If the wrapped object is in the nursery then we know it doesn't have a
michael@0 194 * finalizer, and so background finalization is ok.
michael@0 195 */
michael@0 196 if (IsInsideNursery(priv.toObject().runtimeFromMainThread(), &priv.toObject()))
michael@0 197 return true;
michael@0 198 return IsBackgroundFinalized(priv.toObject().tenuredGetAllocKind());
michael@0 199 }
michael@0 200
michael@0 201 #define PIERCE(cx, wrapper, pre, op, post) \
michael@0 202 JS_BEGIN_MACRO \
michael@0 203 bool ok; \
michael@0 204 { \
michael@0 205 AutoCompartment call(cx, wrappedObject(wrapper)); \
michael@0 206 ok = (pre) && (op); \
michael@0 207 } \
michael@0 208 return ok && (post); \
michael@0 209 JS_END_MACRO
michael@0 210
michael@0 211 #define NOTHING (true)
michael@0 212
michael@0 213 bool
michael@0 214 CrossCompartmentWrapper::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible)
michael@0 215 {
michael@0 216 PIERCE(cx, wrapper,
michael@0 217 NOTHING,
michael@0 218 Wrapper::isExtensible(cx, wrapper, extensible),
michael@0 219 NOTHING);
michael@0 220 }
michael@0 221
michael@0 222 bool
michael@0 223 CrossCompartmentWrapper::preventExtensions(JSContext *cx, HandleObject wrapper)
michael@0 224 {
michael@0 225 PIERCE(cx, wrapper,
michael@0 226 NOTHING,
michael@0 227 Wrapper::preventExtensions(cx, wrapper),
michael@0 228 NOTHING);
michael@0 229 }
michael@0 230
michael@0 231 bool
michael@0 232 CrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
michael@0 233 MutableHandle<PropertyDescriptor> desc)
michael@0 234 {
michael@0 235 RootedId idCopy(cx, id);
michael@0 236 PIERCE(cx, wrapper,
michael@0 237 cx->compartment()->wrapId(cx, idCopy.address()),
michael@0 238 Wrapper::getPropertyDescriptor(cx, wrapper, idCopy, desc),
michael@0 239 cx->compartment()->wrap(cx, desc));
michael@0 240 }
michael@0 241
michael@0 242 bool
michael@0 243 CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
michael@0 244 MutableHandle<PropertyDescriptor> desc)
michael@0 245 {
michael@0 246 RootedId idCopy(cx, id);
michael@0 247 PIERCE(cx, wrapper,
michael@0 248 cx->compartment()->wrapId(cx, idCopy.address()),
michael@0 249 Wrapper::getOwnPropertyDescriptor(cx, wrapper, idCopy, desc),
michael@0 250 cx->compartment()->wrap(cx, desc));
michael@0 251 }
michael@0 252
michael@0 253 bool
michael@0 254 CrossCompartmentWrapper::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
michael@0 255 MutableHandle<PropertyDescriptor> desc)
michael@0 256 {
michael@0 257 RootedId idCopy(cx, id);
michael@0 258 Rooted<PropertyDescriptor> desc2(cx, desc);
michael@0 259 PIERCE(cx, wrapper,
michael@0 260 cx->compartment()->wrapId(cx, idCopy.address()) && cx->compartment()->wrap(cx, &desc2),
michael@0 261 Wrapper::defineProperty(cx, wrapper, idCopy, &desc2),
michael@0 262 NOTHING);
michael@0 263 }
michael@0 264
michael@0 265 bool
michael@0 266 CrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, HandleObject wrapper,
michael@0 267 AutoIdVector &props)
michael@0 268 {
michael@0 269 PIERCE(cx, wrapper,
michael@0 270 NOTHING,
michael@0 271 Wrapper::getOwnPropertyNames(cx, wrapper, props),
michael@0 272 cx->compartment()->wrap(cx, props));
michael@0 273 }
michael@0 274
michael@0 275 bool
michael@0 276 CrossCompartmentWrapper::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
michael@0 277 {
michael@0 278 RootedId idCopy(cx, id);
michael@0 279 PIERCE(cx, wrapper,
michael@0 280 cx->compartment()->wrapId(cx, idCopy.address()),
michael@0 281 Wrapper::delete_(cx, wrapper, idCopy, bp),
michael@0 282 NOTHING);
michael@0 283 }
michael@0 284
michael@0 285 bool
michael@0 286 CrossCompartmentWrapper::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props)
michael@0 287 {
michael@0 288 PIERCE(cx, wrapper,
michael@0 289 NOTHING,
michael@0 290 Wrapper::enumerate(cx, wrapper, props),
michael@0 291 cx->compartment()->wrap(cx, props));
michael@0 292 }
michael@0 293
michael@0 294 bool
michael@0 295 CrossCompartmentWrapper::has(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
michael@0 296 {
michael@0 297 RootedId idCopy(cx, id);
michael@0 298 PIERCE(cx, wrapper,
michael@0 299 cx->compartment()->wrapId(cx, idCopy.address()),
michael@0 300 Wrapper::has(cx, wrapper, idCopy, bp),
michael@0 301 NOTHING);
michael@0 302 }
michael@0 303
michael@0 304 bool
michael@0 305 CrossCompartmentWrapper::hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
michael@0 306 {
michael@0 307 RootedId idCopy(cx, id);
michael@0 308 PIERCE(cx, wrapper,
michael@0 309 cx->compartment()->wrapId(cx, idCopy.address()),
michael@0 310 Wrapper::hasOwn(cx, wrapper, idCopy, bp),
michael@0 311 NOTHING);
michael@0 312 }
michael@0 313
michael@0 314 bool
michael@0 315 CrossCompartmentWrapper::get(JSContext *cx, HandleObject wrapper, HandleObject receiver,
michael@0 316 HandleId id, MutableHandleValue vp)
michael@0 317 {
michael@0 318 RootedObject receiverCopy(cx, receiver);
michael@0 319 RootedId idCopy(cx, id);
michael@0 320 {
michael@0 321 AutoCompartment call(cx, wrappedObject(wrapper));
michael@0 322 if (!cx->compartment()->wrap(cx, &receiverCopy) ||
michael@0 323 !cx->compartment()->wrapId(cx, idCopy.address()))
michael@0 324 {
michael@0 325 return false;
michael@0 326 }
michael@0 327
michael@0 328 if (!Wrapper::get(cx, wrapper, receiverCopy, idCopy, vp))
michael@0 329 return false;
michael@0 330 }
michael@0 331 return cx->compartment()->wrap(cx, vp);
michael@0 332 }
michael@0 333
michael@0 334 bool
michael@0 335 CrossCompartmentWrapper::set(JSContext *cx, HandleObject wrapper, HandleObject receiver,
michael@0 336 HandleId id, bool strict, MutableHandleValue vp)
michael@0 337 {
michael@0 338 RootedObject receiverCopy(cx, receiver);
michael@0 339 RootedId idCopy(cx, id);
michael@0 340 PIERCE(cx, wrapper,
michael@0 341 cx->compartment()->wrap(cx, &receiverCopy) &&
michael@0 342 cx->compartment()->wrapId(cx, idCopy.address()) &&
michael@0 343 cx->compartment()->wrap(cx, vp),
michael@0 344 Wrapper::set(cx, wrapper, receiverCopy, idCopy, strict, vp),
michael@0 345 NOTHING);
michael@0 346 }
michael@0 347
michael@0 348 bool
michael@0 349 CrossCompartmentWrapper::keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props)
michael@0 350 {
michael@0 351 PIERCE(cx, wrapper,
michael@0 352 NOTHING,
michael@0 353 Wrapper::keys(cx, wrapper, props),
michael@0 354 cx->compartment()->wrap(cx, props));
michael@0 355 }
michael@0 356
michael@0 357 /*
michael@0 358 * We can reify non-escaping iterator objects instead of having to wrap them. This
michael@0 359 * allows fast iteration over objects across a compartment boundary.
michael@0 360 */
michael@0 361 static bool
michael@0 362 CanReify(HandleValue vp)
michael@0 363 {
michael@0 364 JSObject *obj;
michael@0 365 return vp.isObject() &&
michael@0 366 (obj = &vp.toObject())->is<PropertyIteratorObject>() &&
michael@0 367 (obj->as<PropertyIteratorObject>().getNativeIterator()->flags & JSITER_ENUMERATE);
michael@0 368 }
michael@0 369
michael@0 370 struct AutoCloseIterator
michael@0 371 {
michael@0 372 AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(cx, obj) {}
michael@0 373
michael@0 374 ~AutoCloseIterator() { if (obj) CloseIterator(cx, obj); }
michael@0 375
michael@0 376 void clear() { obj = nullptr; }
michael@0 377
michael@0 378 private:
michael@0 379 JSContext *cx;
michael@0 380 RootedObject obj;
michael@0 381 };
michael@0 382
michael@0 383 static bool
michael@0 384 Reify(JSContext *cx, JSCompartment *origin, MutableHandleValue vp)
michael@0 385 {
michael@0 386 Rooted<PropertyIteratorObject*> iterObj(cx, &vp.toObject().as<PropertyIteratorObject>());
michael@0 387 NativeIterator *ni = iterObj->getNativeIterator();
michael@0 388
michael@0 389 AutoCloseIterator close(cx, iterObj);
michael@0 390
michael@0 391 /* Wrap the iteratee. */
michael@0 392 RootedObject obj(cx, ni->obj);
michael@0 393 if (!origin->wrap(cx, &obj))
michael@0 394 return false;
michael@0 395
michael@0 396 /*
michael@0 397 * Wrap the elements in the iterator's snapshot.
michael@0 398 * N.B. the order of closing/creating iterators is important due to the
michael@0 399 * implicit cx->enumerators state.
michael@0 400 */
michael@0 401 size_t length = ni->numKeys();
michael@0 402 bool isKeyIter = ni->isKeyIter();
michael@0 403 AutoIdVector keys(cx);
michael@0 404 if (length > 0) {
michael@0 405 if (!keys.reserve(length))
michael@0 406 return false;
michael@0 407 for (size_t i = 0; i < length; ++i) {
michael@0 408 RootedId id(cx);
michael@0 409 RootedValue v(cx, StringValue(ni->begin()[i]));
michael@0 410 if (!ValueToId<CanGC>(cx, v, &id))
michael@0 411 return false;
michael@0 412 keys.infallibleAppend(id);
michael@0 413 if (!origin->wrapId(cx, &keys[i]))
michael@0 414 return false;
michael@0 415 }
michael@0 416 }
michael@0 417
michael@0 418 close.clear();
michael@0 419 if (!CloseIterator(cx, iterObj))
michael@0 420 return false;
michael@0 421
michael@0 422 if (isKeyIter) {
michael@0 423 if (!VectorToKeyIterator(cx, obj, ni->flags, keys, vp))
michael@0 424 return false;
michael@0 425 } else {
michael@0 426 if (!VectorToValueIterator(cx, obj, ni->flags, keys, vp))
michael@0 427 return false;
michael@0 428 }
michael@0 429 return true;
michael@0 430 }
michael@0 431
michael@0 432 bool
michael@0 433 CrossCompartmentWrapper::iterate(JSContext *cx, HandleObject wrapper, unsigned flags,
michael@0 434 MutableHandleValue vp)
michael@0 435 {
michael@0 436 {
michael@0 437 AutoCompartment call(cx, wrappedObject(wrapper));
michael@0 438 if (!Wrapper::iterate(cx, wrapper, flags, vp))
michael@0 439 return false;
michael@0 440 }
michael@0 441
michael@0 442 if (CanReify(vp))
michael@0 443 return Reify(cx, cx->compartment(), vp);
michael@0 444 return cx->compartment()->wrap(cx, vp);
michael@0 445 }
michael@0 446
michael@0 447 bool
michael@0 448 CrossCompartmentWrapper::call(JSContext *cx, HandleObject wrapper, const CallArgs &args)
michael@0 449 {
michael@0 450 RootedObject wrapped(cx, wrappedObject(wrapper));
michael@0 451
michael@0 452 {
michael@0 453 AutoCompartment call(cx, wrapped);
michael@0 454
michael@0 455 args.setCallee(ObjectValue(*wrapped));
michael@0 456 if (!cx->compartment()->wrap(cx, args.mutableThisv()))
michael@0 457 return false;
michael@0 458
michael@0 459 for (size_t n = 0; n < args.length(); ++n) {
michael@0 460 if (!cx->compartment()->wrap(cx, args[n]))
michael@0 461 return false;
michael@0 462 }
michael@0 463
michael@0 464 if (!Wrapper::call(cx, wrapper, args))
michael@0 465 return false;
michael@0 466 }
michael@0 467
michael@0 468 return cx->compartment()->wrap(cx, args.rval());
michael@0 469 }
michael@0 470
michael@0 471 bool
michael@0 472 CrossCompartmentWrapper::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args)
michael@0 473 {
michael@0 474 RootedObject wrapped(cx, wrappedObject(wrapper));
michael@0 475 {
michael@0 476 AutoCompartment call(cx, wrapped);
michael@0 477
michael@0 478 for (size_t n = 0; n < args.length(); ++n) {
michael@0 479 if (!cx->compartment()->wrap(cx, args[n]))
michael@0 480 return false;
michael@0 481 }
michael@0 482 if (!Wrapper::construct(cx, wrapper, args))
michael@0 483 return false;
michael@0 484 }
michael@0 485 return cx->compartment()->wrap(cx, args.rval());
michael@0 486 }
michael@0 487
michael@0 488 bool
michael@0 489 CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
michael@0 490 CallArgs srcArgs)
michael@0 491 {
michael@0 492 RootedObject wrapper(cx, &srcArgs.thisv().toObject());
michael@0 493 JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
michael@0 494 !UncheckedUnwrap(wrapper)->is<CrossCompartmentWrapperObject>());
michael@0 495
michael@0 496 RootedObject wrapped(cx, wrappedObject(wrapper));
michael@0 497 {
michael@0 498 AutoCompartment call(cx, wrapped);
michael@0 499 InvokeArgs dstArgs(cx);
michael@0 500 if (!dstArgs.init(srcArgs.length()))
michael@0 501 return false;
michael@0 502
michael@0 503 Value *src = srcArgs.base();
michael@0 504 Value *srcend = srcArgs.array() + srcArgs.length();
michael@0 505 Value *dst = dstArgs.base();
michael@0 506
michael@0 507 RootedValue source(cx);
michael@0 508 for (; src < srcend; ++src, ++dst) {
michael@0 509 source = *src;
michael@0 510 if (!cx->compartment()->wrap(cx, &source))
michael@0 511 return false;
michael@0 512 *dst = source.get();
michael@0 513
michael@0 514 // Handle |this| specially. When we rewrap on the other side of the
michael@0 515 // membrane, we might apply a same-compartment security wrapper that
michael@0 516 // will stymie this whole process. If that happens, unwrap the wrapper.
michael@0 517 // This logic can go away when same-compartment security wrappers go away.
michael@0 518 if ((src == srcArgs.base() + 1) && dst->isObject()) {
michael@0 519 RootedObject thisObj(cx, &dst->toObject());
michael@0 520 if (thisObj->is<WrapperObject>() &&
michael@0 521 Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy())
michael@0 522 {
michael@0 523 JS_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>());
michael@0 524 *dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
michael@0 525 }
michael@0 526 }
michael@0 527 }
michael@0 528
michael@0 529 if (!CallNonGenericMethod(cx, test, impl, dstArgs))
michael@0 530 return false;
michael@0 531
michael@0 532 srcArgs.rval().set(dstArgs.rval());
michael@0 533 }
michael@0 534 return cx->compartment()->wrap(cx, srcArgs.rval());
michael@0 535 }
michael@0 536
michael@0 537 bool
michael@0 538 CrossCompartmentWrapper::hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v,
michael@0 539 bool *bp)
michael@0 540 {
michael@0 541 AutoCompartment call(cx, wrappedObject(wrapper));
michael@0 542 if (!cx->compartment()->wrap(cx, v))
michael@0 543 return false;
michael@0 544 return Wrapper::hasInstance(cx, wrapper, v, bp);
michael@0 545 }
michael@0 546
michael@0 547 const char *
michael@0 548 CrossCompartmentWrapper::className(JSContext *cx, HandleObject wrapper)
michael@0 549 {
michael@0 550 AutoCompartment call(cx, wrappedObject(wrapper));
michael@0 551 return Wrapper::className(cx, wrapper);
michael@0 552 }
michael@0 553
michael@0 554 JSString *
michael@0 555 CrossCompartmentWrapper::fun_toString(JSContext *cx, HandleObject wrapper, unsigned indent)
michael@0 556 {
michael@0 557 RootedString str(cx);
michael@0 558 {
michael@0 559 AutoCompartment call(cx, wrappedObject(wrapper));
michael@0 560 str = Wrapper::fun_toString(cx, wrapper, indent);
michael@0 561 if (!str)
michael@0 562 return nullptr;
michael@0 563 }
michael@0 564 if (!cx->compartment()->wrap(cx, str.address()))
michael@0 565 return nullptr;
michael@0 566 return str;
michael@0 567 }
michael@0 568
michael@0 569 bool
michael@0 570 CrossCompartmentWrapper::regexp_toShared(JSContext *cx, HandleObject wrapper, RegExpGuard *g)
michael@0 571 {
michael@0 572 AutoCompartment call(cx, wrappedObject(wrapper));
michael@0 573 return Wrapper::regexp_toShared(cx, wrapper, g);
michael@0 574 }
michael@0 575
michael@0 576 bool
michael@0 577 CrossCompartmentWrapper::defaultValue(JSContext *cx, HandleObject wrapper, JSType hint,
michael@0 578 MutableHandleValue vp)
michael@0 579 {
michael@0 580 PIERCE(cx, wrapper,
michael@0 581 NOTHING,
michael@0 582 Wrapper::defaultValue(cx, wrapper, hint, vp),
michael@0 583 cx->compartment()->wrap(cx, vp));
michael@0 584 }
michael@0 585
michael@0 586 bool
michael@0 587 CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper,
michael@0 588 MutableHandleObject protop)
michael@0 589 {
michael@0 590 {
michael@0 591 RootedObject wrapped(cx, wrappedObject(wrapper));
michael@0 592 AutoCompartment call(cx, wrapped);
michael@0 593 if (!JSObject::getProto(cx, wrapped, protop))
michael@0 594 return false;
michael@0 595 if (protop)
michael@0 596 protop->setDelegate(cx);
michael@0 597 }
michael@0 598
michael@0 599 return cx->compartment()->wrap(cx, protop);
michael@0 600 }
michael@0 601
michael@0 602 bool
michael@0 603 CrossCompartmentWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper,
michael@0 604 HandleObject proto, bool *bp)
michael@0 605 {
michael@0 606 RootedObject protoCopy(cx, proto);
michael@0 607 PIERCE(cx, wrapper,
michael@0 608 cx->compartment()->wrap(cx, &protoCopy),
michael@0 609 Wrapper::setPrototypeOf(cx, wrapper, protoCopy, bp),
michael@0 610 NOTHING);
michael@0 611 }
michael@0 612
michael@0 613 CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
michael@0 614
michael@0 615 /* Security wrappers. */
michael@0 616
michael@0 617 template <class Base>
michael@0 618 SecurityWrapper<Base>::SecurityWrapper(unsigned flags)
michael@0 619 : Base(flags)
michael@0 620 {
michael@0 621 BaseProxyHandler::setHasSecurityPolicy(true);
michael@0 622 }
michael@0 623
michael@0 624 template <class Base>
michael@0 625 bool
michael@0 626 SecurityWrapper<Base>::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible)
michael@0 627 {
michael@0 628 // Just like BaseProxyHandler, SecurityWrappers claim by default to always
michael@0 629 // be extensible, so as not to leak information about the state of the
michael@0 630 // underlying wrapped thing.
michael@0 631 *extensible = true;
michael@0 632 return true;
michael@0 633 }
michael@0 634
michael@0 635 template <class Base>
michael@0 636 bool
michael@0 637 SecurityWrapper<Base>::preventExtensions(JSContext *cx, HandleObject wrapper)
michael@0 638 {
michael@0 639 // See above.
michael@0 640 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
michael@0 641 return false;
michael@0 642 }
michael@0 643
michael@0 644 template <class Base>
michael@0 645 bool
michael@0 646 SecurityWrapper<Base>::enter(JSContext *cx, HandleObject wrapper, HandleId id,
michael@0 647 Wrapper::Action act, bool *bp)
michael@0 648 {
michael@0 649 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
michael@0 650 *bp = false;
michael@0 651 return false;
michael@0 652 }
michael@0 653
michael@0 654 template <class Base>
michael@0 655 bool
michael@0 656 SecurityWrapper<Base>::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
michael@0 657 CallArgs args)
michael@0 658 {
michael@0 659 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
michael@0 660 return false;
michael@0 661 }
michael@0 662
michael@0 663 template <class Base>
michael@0 664 bool
michael@0 665 SecurityWrapper<Base>::setPrototypeOf(JSContext *cx, HandleObject wrapper,
michael@0 666 HandleObject proto, bool *bp)
michael@0 667 {
michael@0 668 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
michael@0 669 return false;
michael@0 670 }
michael@0 671
michael@0 672 // For security wrappers, we run the DefaultValue algorithm on the wrapper
michael@0 673 // itself, which means that the existing security policy on operations like
michael@0 674 // toString() will take effect and do the right thing here.
michael@0 675 template <class Base>
michael@0 676 bool
michael@0 677 SecurityWrapper<Base>::defaultValue(JSContext *cx, HandleObject wrapper,
michael@0 678 JSType hint, MutableHandleValue vp)
michael@0 679 {
michael@0 680 return DefaultValue(cx, wrapper, hint, vp);
michael@0 681 }
michael@0 682
michael@0 683 template <class Base>
michael@0 684 bool
michael@0 685 SecurityWrapper<Base>::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
michael@0 686 {
michael@0 687 return false;
michael@0 688 }
michael@0 689
michael@0 690 template <class Base>
michael@0 691 bool
michael@0 692 SecurityWrapper<Base>::regexp_toShared(JSContext *cx, HandleObject obj, RegExpGuard *g)
michael@0 693 {
michael@0 694 return Base::regexp_toShared(cx, obj, g);
michael@0 695 }
michael@0 696
michael@0 697 template <class Base>
michael@0 698 bool
michael@0 699 SecurityWrapper<Base>::defineProperty(JSContext *cx, HandleObject wrapper,
michael@0 700 HandleId id, MutableHandle<PropertyDescriptor> desc)
michael@0 701 {
michael@0 702 if (desc.getter() || desc.setter()) {
michael@0 703 JSString *str = IdToString(cx, id);
michael@0 704 const jschar *prop = str ? str->getCharsZ(cx) : nullptr;
michael@0 705 JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr,
michael@0 706 JSMSG_ACCESSOR_DEF_DENIED, prop);
michael@0 707 return false;
michael@0 708 }
michael@0 709
michael@0 710 return Base::defineProperty(cx, wrapper, id, desc);
michael@0 711 }
michael@0 712
michael@0 713 template <class Base>
michael@0 714 bool
michael@0 715 SecurityWrapper<Base>::watch(JSContext *cx, HandleObject proxy,
michael@0 716 HandleId id, HandleObject callable)
michael@0 717 {
michael@0 718 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
michael@0 719 return false;
michael@0 720 }
michael@0 721
michael@0 722 template <class Base>
michael@0 723 bool
michael@0 724 SecurityWrapper<Base>::unwatch(JSContext *cx, HandleObject proxy,
michael@0 725 HandleId id)
michael@0 726 {
michael@0 727 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
michael@0 728 return false;
michael@0 729 }
michael@0 730
michael@0 731
michael@0 732 template class js::SecurityWrapper<Wrapper>;
michael@0 733 template class js::SecurityWrapper<CrossCompartmentWrapper>;
michael@0 734
michael@0 735 DeadObjectProxy::DeadObjectProxy()
michael@0 736 : BaseProxyHandler(&sDeadObjectFamily)
michael@0 737 {
michael@0 738 }
michael@0 739
michael@0 740 bool
michael@0 741 DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
michael@0 742 {
michael@0 743 // This is kind of meaningless, but dead-object semantics aside,
michael@0 744 // [[Extensible]] always being true is consistent with other proxy types.
michael@0 745 *extensible = true;
michael@0 746 return true;
michael@0 747 }
michael@0 748
michael@0 749 bool
michael@0 750 DeadObjectProxy::preventExtensions(JSContext *cx, HandleObject proxy)
michael@0 751 {
michael@0 752 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 753 return false;
michael@0 754 }
michael@0 755
michael@0 756 bool
michael@0 757 DeadObjectProxy::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
michael@0 758 MutableHandle<PropertyDescriptor> desc)
michael@0 759 {
michael@0 760 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 761 return false;
michael@0 762 }
michael@0 763
michael@0 764 bool
michael@0 765 DeadObjectProxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
michael@0 766 MutableHandle<PropertyDescriptor> desc)
michael@0 767 {
michael@0 768 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 769 return false;
michael@0 770 }
michael@0 771
michael@0 772 bool
michael@0 773 DeadObjectProxy::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
michael@0 774 MutableHandle<PropertyDescriptor> desc)
michael@0 775 {
michael@0 776 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 777 return false;
michael@0 778 }
michael@0 779
michael@0 780 bool
michael@0 781 DeadObjectProxy::getOwnPropertyNames(JSContext *cx, HandleObject wrapper,
michael@0 782 AutoIdVector &props)
michael@0 783 {
michael@0 784 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 785 return false;
michael@0 786 }
michael@0 787
michael@0 788 bool
michael@0 789 DeadObjectProxy::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
michael@0 790 {
michael@0 791 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 792 return false;
michael@0 793 }
michael@0 794
michael@0 795 bool
michael@0 796 DeadObjectProxy::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props)
michael@0 797 {
michael@0 798 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 799 return false;
michael@0 800 }
michael@0 801
michael@0 802 bool
michael@0 803 DeadObjectProxy::call(JSContext *cx, HandleObject wrapper, const CallArgs &args)
michael@0 804 {
michael@0 805 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 806 return false;
michael@0 807 }
michael@0 808
michael@0 809 bool
michael@0 810 DeadObjectProxy::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args)
michael@0 811 {
michael@0 812 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 813 return false;
michael@0 814 }
michael@0 815
michael@0 816 bool
michael@0 817 DeadObjectProxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
michael@0 818 {
michael@0 819 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 820 return false;
michael@0 821 }
michael@0 822
michael@0 823 bool
michael@0 824 DeadObjectProxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
michael@0 825 {
michael@0 826 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 827 return false;
michael@0 828 }
michael@0 829
michael@0 830 bool
michael@0 831 DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
michael@0 832 {
michael@0 833 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 834 return false;
michael@0 835 }
michael@0 836
michael@0 837 const char *
michael@0 838 DeadObjectProxy::className(JSContext *cx, HandleObject wrapper)
michael@0 839 {
michael@0 840 return "DeadObject";
michael@0 841 }
michael@0 842
michael@0 843 JSString *
michael@0 844 DeadObjectProxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
michael@0 845 {
michael@0 846 return nullptr;
michael@0 847 }
michael@0 848
michael@0 849 bool
michael@0 850 DeadObjectProxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g)
michael@0 851 {
michael@0 852 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 853 return false;
michael@0 854 }
michael@0 855
michael@0 856 bool
michael@0 857 DeadObjectProxy::defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
michael@0 858 {
michael@0 859 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
michael@0 860 return false;
michael@0 861 }
michael@0 862
michael@0 863 bool
michael@0 864 DeadObjectProxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop)
michael@0 865 {
michael@0 866 protop.set(nullptr);
michael@0 867 return true;
michael@0 868 }
michael@0 869
michael@0 870 DeadObjectProxy DeadObjectProxy::singleton;
michael@0 871 const char DeadObjectProxy::sDeadObjectFamily = 0;
michael@0 872
michael@0 873 bool
michael@0 874 js::IsDeadProxyObject(JSObject *obj)
michael@0 875 {
michael@0 876 return obj->is<ProxyObject>() &&
michael@0 877 obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton;
michael@0 878 }
michael@0 879
michael@0 880 void
michael@0 881 js::NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper)
michael@0 882 {
michael@0 883 JS_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
michael@0 884
michael@0 885 NotifyGCNukeWrapper(wrapper);
michael@0 886
michael@0 887 wrapper->as<ProxyObject>().nuke(&DeadObjectProxy::singleton);
michael@0 888
michael@0 889 JS_ASSERT(IsDeadProxyObject(wrapper));
michael@0 890 }
michael@0 891
michael@0 892 /*
michael@0 893 * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
michael@0 894 * all of the cross-compartment wrappers that point to objects parented to
michael@0 895 * obj's global. The snag here is that we need to avoid cutting wrappers that
michael@0 896 * point to the window object on page navigation (inner window destruction)
michael@0 897 * and only do that on tab close (outer window destruction). Thus the
michael@0 898 * option of how to handle the global object.
michael@0 899 */
michael@0 900 JS_FRIEND_API(bool)
michael@0 901 js::NukeCrossCompartmentWrappers(JSContext* cx,
michael@0 902 const CompartmentFilter& sourceFilter,
michael@0 903 const CompartmentFilter& targetFilter,
michael@0 904 js::NukeReferencesToWindow nukeReferencesToWindow)
michael@0 905 {
michael@0 906 CHECK_REQUEST(cx);
michael@0 907 JSRuntime *rt = cx->runtime();
michael@0 908
michael@0 909 // Iterate through scopes looking for system cross compartment wrappers
michael@0 910 // that point to an object that shares a global with obj.
michael@0 911
michael@0 912 for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
michael@0 913 if (!sourceFilter.match(c))
michael@0 914 continue;
michael@0 915
michael@0 916 // Iterate the wrappers looking for anything interesting.
michael@0 917 for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
michael@0 918 // Some cross-compartment wrappers are for strings. We're not
michael@0 919 // interested in those.
michael@0 920 const CrossCompartmentKey &k = e.front().key();
michael@0 921 if (k.kind != CrossCompartmentKey::ObjectWrapper)
michael@0 922 continue;
michael@0 923
michael@0 924 AutoWrapperRooter wobj(cx, WrapperValue(e));
michael@0 925 JSObject *wrapped = UncheckedUnwrap(wobj);
michael@0 926
michael@0 927 if (nukeReferencesToWindow == DontNukeWindowReferences &&
michael@0 928 wrapped->getClass()->ext.innerObject)
michael@0 929 continue;
michael@0 930
michael@0 931 if (targetFilter.match(wrapped->compartment())) {
michael@0 932 // We found a wrapper to nuke.
michael@0 933 e.removeFront();
michael@0 934 NukeCrossCompartmentWrapper(cx, wobj);
michael@0 935 }
michael@0 936 }
michael@0 937 }
michael@0 938
michael@0 939 return true;
michael@0 940 }
michael@0 941
michael@0 942 // Given a cross-compartment wrapper |wobj|, update it to point to
michael@0 943 // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
michael@0 944 // useful even if wrapper already points to newTarget.
michael@0 945 bool
michael@0 946 js::RemapWrapper(JSContext *cx, JSObject *wobjArg, JSObject *newTargetArg)
michael@0 947 {
michael@0 948 RootedObject wobj(cx, wobjArg);
michael@0 949 RootedObject newTarget(cx, newTargetArg);
michael@0 950 JS_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
michael@0 951 JS_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
michael@0 952 JSObject *origTarget = Wrapper::wrappedObject(wobj);
michael@0 953 JS_ASSERT(origTarget);
michael@0 954 Value origv = ObjectValue(*origTarget);
michael@0 955 JSCompartment *wcompartment = wobj->compartment();
michael@0 956
michael@0 957 AutoDisableProxyCheck adpc(cx->runtime());
michael@0 958
michael@0 959 // If we're mapping to a different target (as opposed to just recomputing
michael@0 960 // for the same target), we must not have an existing wrapper for the new
michael@0 961 // target, otherwise this will break.
michael@0 962 JS_ASSERT_IF(origTarget != newTarget,
michael@0 963 !wcompartment->lookupWrapper(ObjectValue(*newTarget)));
michael@0 964
michael@0 965 // The old value should still be in the cross-compartment wrapper map, and
michael@0 966 // the lookup should return wobj.
michael@0 967 WrapperMap::Ptr p = wcompartment->lookupWrapper(origv);
michael@0 968 JS_ASSERT(&p->value().unsafeGet()->toObject() == wobj);
michael@0 969 wcompartment->removeWrapper(p);
michael@0 970
michael@0 971 // When we remove origv from the wrapper map, its wrapper, wobj, must
michael@0 972 // immediately cease to be a cross-compartment wrapper. Neuter it.
michael@0 973 NukeCrossCompartmentWrapper(cx, wobj);
michael@0 974
michael@0 975 // First, we wrap it in the new compartment. We try to use the existing
michael@0 976 // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
michael@0 977 // the choice to reuse |wobj| or not.
michael@0 978 RootedObject tobj(cx, newTarget);
michael@0 979 AutoCompartment ac(cx, wobj);
michael@0 980 if (!wcompartment->wrap(cx, &tobj, wobj))
michael@0 981 MOZ_CRASH();
michael@0 982
michael@0 983 // If wrap() reused |wobj|, it will have overwritten it and returned with
michael@0 984 // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
michael@0 985 // will still be nuked. In the latter case, we replace |wobj| with the
michael@0 986 // contents of the new wrapper in |tobj|.
michael@0 987 if (tobj != wobj) {
michael@0 988 // Now, because we need to maintain object identity, we do a brain
michael@0 989 // transplant on the old object so that it contains the contents of the
michael@0 990 // new one.
michael@0 991 if (!JSObject::swap(cx, wobj, tobj))
michael@0 992 MOZ_CRASH();
michael@0 993 }
michael@0 994
michael@0 995 // Before swapping, this wrapper came out of wrap(), which enforces the
michael@0 996 // invariant that the wrapper in the map points directly to the key.
michael@0 997 JS_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
michael@0 998
michael@0 999 // Update the entry in the compartment's wrapper map to point to the old
michael@0 1000 // wrapper, which has now been updated (via reuse or swap).
michael@0 1001 JS_ASSERT(wobj->is<WrapperObject>());
michael@0 1002 wcompartment->putWrapper(cx, ObjectValue(*newTarget), ObjectValue(*wobj));
michael@0 1003 return true;
michael@0 1004 }
michael@0 1005
michael@0 1006 // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
michael@0 1007 // |newTarget|. All wrappers are recomputed.
michael@0 1008 JS_FRIEND_API(bool)
michael@0 1009 js::RemapAllWrappersForObject(JSContext *cx, JSObject *oldTargetArg,
michael@0 1010 JSObject *newTargetArg)
michael@0 1011 {
michael@0 1012 RootedValue origv(cx, ObjectValue(*oldTargetArg));
michael@0 1013 RootedObject newTarget(cx, newTargetArg);
michael@0 1014
michael@0 1015 AutoWrapperVector toTransplant(cx);
michael@0 1016 if (!toTransplant.reserve(cx->runtime()->numCompartments))
michael@0 1017 return false;
michael@0 1018
michael@0 1019 for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
michael@0 1020 if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
michael@0 1021 // We found a wrapper. Remember and root it.
michael@0 1022 toTransplant.infallibleAppend(WrapperValue(wp));
michael@0 1023 }
michael@0 1024 }
michael@0 1025
michael@0 1026 for (WrapperValue *begin = toTransplant.begin(), *end = toTransplant.end();
michael@0 1027 begin != end; ++begin)
michael@0 1028 {
michael@0 1029 if (!RemapWrapper(cx, &begin->toObject(), newTarget))
michael@0 1030 MOZ_CRASH();
michael@0 1031 }
michael@0 1032
michael@0 1033 return true;
michael@0 1034 }
michael@0 1035
michael@0 1036 JS_FRIEND_API(bool)
michael@0 1037 js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter,
michael@0 1038 const CompartmentFilter &targetFilter)
michael@0 1039 {
michael@0 1040 AutoMaybeTouchDeadZones agc(cx);
michael@0 1041
michael@0 1042 AutoWrapperVector toRecompute(cx);
michael@0 1043
michael@0 1044 for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
michael@0 1045 // Filter by source compartment.
michael@0 1046 if (!sourceFilter.match(c))
michael@0 1047 continue;
michael@0 1048
michael@0 1049 // Iterate over the wrappers, filtering appropriately.
michael@0 1050 for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
michael@0 1051 // Filter out non-objects.
michael@0 1052 const CrossCompartmentKey &k = e.front().key();
michael@0 1053 if (k.kind != CrossCompartmentKey::ObjectWrapper)
michael@0 1054 continue;
michael@0 1055
michael@0 1056 // Filter by target compartment.
michael@0 1057 if (!targetFilter.match(static_cast<JSObject *>(k.wrapped)->compartment()))
michael@0 1058 continue;
michael@0 1059
michael@0 1060 // Add it to the list.
michael@0 1061 if (!toRecompute.append(WrapperValue(e)))
michael@0 1062 return false;
michael@0 1063 }
michael@0 1064 }
michael@0 1065
michael@0 1066 // Recompute all the wrappers in the list.
michael@0 1067 for (WrapperValue *begin = toRecompute.begin(), *end = toRecompute.end(); begin != end; ++begin)
michael@0 1068 {
michael@0 1069 JSObject *wrapper = &begin->toObject();
michael@0 1070 JSObject *wrapped = Wrapper::wrappedObject(wrapper);
michael@0 1071 if (!RemapWrapper(cx, wrapper, wrapped))
michael@0 1072 MOZ_CRASH();
michael@0 1073 }
michael@0 1074
michael@0 1075 return true;
michael@0 1076 }

mercurial