1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jswrapper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1076 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jswrapper.h" 1.11 + 1.12 +#include "jscntxt.h" 1.13 +#include "jscompartment.h" 1.14 +#include "jsexn.h" 1.15 +#include "jsgc.h" 1.16 +#include "jsiter.h" 1.17 + 1.18 +#include "vm/ErrorObject.h" 1.19 +#include "vm/WrapperObject.h" 1.20 + 1.21 +#include "jsobjinlines.h" 1.22 + 1.23 +using namespace js; 1.24 +using namespace js::gc; 1.25 + 1.26 +const char js::sWrapperFamily = 0; 1.27 + 1.28 +/* 1.29 + * Wrapper forwards this call directly to the wrapped object for efficiency 1.30 + * and transparency. In particular, the hint is needed to properly stringify 1.31 + * Date objects in certain cases - see bug 646129. Note also the 1.32 + * SecurityWrapper overrides this trap to avoid information leaks. See bug 1.33 + * 720619. 1.34 + */ 1.35 +bool 1.36 +Wrapper::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp) 1.37 +{ 1.38 + vp.set(ObjectValue(*proxy->as<ProxyObject>().target())); 1.39 + if (hint == JSTYPE_VOID) 1.40 + return ToPrimitive(cx, vp); 1.41 + return ToPrimitive(cx, hint, vp); 1.42 +} 1.43 + 1.44 +JSObject * 1.45 +Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler, 1.46 + const WrapperOptions *options) 1.47 +{ 1.48 + JS_ASSERT(parent); 1.49 + 1.50 + AutoMarkInDeadZone amd(cx->zone()); 1.51 + 1.52 + RootedValue priv(cx, ObjectValue(*obj)); 1.53 + mozilla::Maybe<WrapperOptions> opts; 1.54 + if (!options) { 1.55 + opts.construct(); 1.56 + opts.ref().selectDefaultClass(obj->isCallable()); 1.57 + options = opts.addr(); 1.58 + } 1.59 + return NewProxyObject(cx, handler, priv, options->proto(), parent, *options); 1.60 +} 1.61 + 1.62 +JSObject * 1.63 +Wrapper::Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler) 1.64 +{ 1.65 + JS_ASSERT(!obj->isCallable()); 1.66 + existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj)); 1.67 + return existing; 1.68 +} 1.69 + 1.70 +Wrapper * 1.71 +Wrapper::wrapperHandler(JSObject *wrapper) 1.72 +{ 1.73 + JS_ASSERT(wrapper->is<WrapperObject>()); 1.74 + return static_cast<Wrapper*>(wrapper->as<ProxyObject>().handler()); 1.75 +} 1.76 + 1.77 +JSObject * 1.78 +Wrapper::wrappedObject(JSObject *wrapper) 1.79 +{ 1.80 + JS_ASSERT(wrapper->is<WrapperObject>()); 1.81 + return wrapper->as<ProxyObject>().target(); 1.82 +} 1.83 + 1.84 +JS_FRIEND_API(JSObject *) 1.85 +js::UncheckedUnwrap(JSObject *wrapped, bool stopAtOuter, unsigned *flagsp) 1.86 +{ 1.87 + unsigned flags = 0; 1.88 + while (true) { 1.89 + if (!wrapped->is<WrapperObject>() || 1.90 + MOZ_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject)) 1.91 + { 1.92 + break; 1.93 + } 1.94 + flags |= Wrapper::wrapperHandler(wrapped)->flags(); 1.95 + wrapped = wrapped->as<ProxyObject>().private_().toObjectOrNull(); 1.96 + } 1.97 + if (flagsp) 1.98 + *flagsp = flags; 1.99 + return wrapped; 1.100 +} 1.101 + 1.102 +JS_FRIEND_API(JSObject *) 1.103 +js::CheckedUnwrap(JSObject *obj, bool stopAtOuter) 1.104 +{ 1.105 + while (true) { 1.106 + JSObject *wrapper = obj; 1.107 + obj = UnwrapOneChecked(obj, stopAtOuter); 1.108 + if (!obj || obj == wrapper) 1.109 + return obj; 1.110 + } 1.111 +} 1.112 + 1.113 +JS_FRIEND_API(JSObject *) 1.114 +js::UnwrapOneChecked(JSObject *obj, bool stopAtOuter) 1.115 +{ 1.116 + if (!obj->is<WrapperObject>() || 1.117 + MOZ_UNLIKELY(!!obj->getClass()->ext.innerObject && stopAtOuter)) 1.118 + { 1.119 + return obj; 1.120 + } 1.121 + 1.122 + Wrapper *handler = Wrapper::wrapperHandler(obj); 1.123 + return handler->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj); 1.124 +} 1.125 + 1.126 +bool 1.127 +js::IsCrossCompartmentWrapper(JSObject *obj) 1.128 +{ 1.129 + return IsWrapper(obj) && 1.130 + !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT); 1.131 +} 1.132 + 1.133 +Wrapper::Wrapper(unsigned flags, bool hasPrototype) : DirectProxyHandler(&sWrapperFamily) 1.134 + , mFlags(flags) 1.135 +{ 1.136 + setHasPrototype(hasPrototype); 1.137 +} 1.138 + 1.139 +Wrapper::~Wrapper() 1.140 +{ 1.141 +} 1.142 + 1.143 +Wrapper Wrapper::singleton((unsigned)0); 1.144 +Wrapper Wrapper::singletonWithPrototype((unsigned)0, true); 1.145 +JSObject *Wrapper::defaultProto = TaggedProto::LazyProto; 1.146 + 1.147 +/* Compartments. */ 1.148 + 1.149 +extern JSObject * 1.150 +js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj, 1.151 + HandleObject wrappedProto, HandleObject parent, 1.152 + unsigned flags) 1.153 +{ 1.154 + // Allow wrapping outer window proxies. 1.155 + JS_ASSERT(!obj->is<WrapperObject>() || obj->getClass()->ext.innerObject); 1.156 + JS_ASSERT(wrappedProto == TaggedProto::LazyProto); 1.157 + return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton); 1.158 +} 1.159 + 1.160 +ErrorCopier::~ErrorCopier() 1.161 +{ 1.162 + JSContext *cx = ac.ref().context()->asJSContext(); 1.163 + if (ac.ref().origin() != cx->compartment() && cx->isExceptionPending()) { 1.164 + RootedValue exc(cx); 1.165 + if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) { 1.166 + cx->clearPendingException(); 1.167 + ac.destroy(); 1.168 + Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>()); 1.169 + JSObject *copyobj = js_CopyErrorObject(cx, errObj, scope); 1.170 + if (copyobj) 1.171 + cx->setPendingException(ObjectValue(*copyobj)); 1.172 + } 1.173 + } 1.174 +} 1.175 + 1.176 +/* Cross compartment wrappers. */ 1.177 + 1.178 +CrossCompartmentWrapper::CrossCompartmentWrapper(unsigned flags, bool hasPrototype) 1.179 + : Wrapper(CROSS_COMPARTMENT | flags, hasPrototype) 1.180 +{ 1.181 +} 1.182 + 1.183 +CrossCompartmentWrapper::~CrossCompartmentWrapper() 1.184 +{ 1.185 +} 1.186 + 1.187 +bool Wrapper::finalizeInBackground(Value priv) 1.188 +{ 1.189 + if (!priv.isObject()) 1.190 + return true; 1.191 + 1.192 + /* 1.193 + * Make the 'background-finalized-ness' of the wrapper the same as the 1.194 + * wrapped object, to allow transplanting between them. 1.195 + * 1.196 + * If the wrapped object is in the nursery then we know it doesn't have a 1.197 + * finalizer, and so background finalization is ok. 1.198 + */ 1.199 + if (IsInsideNursery(priv.toObject().runtimeFromMainThread(), &priv.toObject())) 1.200 + return true; 1.201 + return IsBackgroundFinalized(priv.toObject().tenuredGetAllocKind()); 1.202 +} 1.203 + 1.204 +#define PIERCE(cx, wrapper, pre, op, post) \ 1.205 + JS_BEGIN_MACRO \ 1.206 + bool ok; \ 1.207 + { \ 1.208 + AutoCompartment call(cx, wrappedObject(wrapper)); \ 1.209 + ok = (pre) && (op); \ 1.210 + } \ 1.211 + return ok && (post); \ 1.212 + JS_END_MACRO 1.213 + 1.214 +#define NOTHING (true) 1.215 + 1.216 +bool 1.217 +CrossCompartmentWrapper::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) 1.218 +{ 1.219 + PIERCE(cx, wrapper, 1.220 + NOTHING, 1.221 + Wrapper::isExtensible(cx, wrapper, extensible), 1.222 + NOTHING); 1.223 +} 1.224 + 1.225 +bool 1.226 +CrossCompartmentWrapper::preventExtensions(JSContext *cx, HandleObject wrapper) 1.227 +{ 1.228 + PIERCE(cx, wrapper, 1.229 + NOTHING, 1.230 + Wrapper::preventExtensions(cx, wrapper), 1.231 + NOTHING); 1.232 +} 1.233 + 1.234 +bool 1.235 +CrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, 1.236 + MutableHandle<PropertyDescriptor> desc) 1.237 +{ 1.238 + RootedId idCopy(cx, id); 1.239 + PIERCE(cx, wrapper, 1.240 + cx->compartment()->wrapId(cx, idCopy.address()), 1.241 + Wrapper::getPropertyDescriptor(cx, wrapper, idCopy, desc), 1.242 + cx->compartment()->wrap(cx, desc)); 1.243 +} 1.244 + 1.245 +bool 1.246 +CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, 1.247 + MutableHandle<PropertyDescriptor> desc) 1.248 +{ 1.249 + RootedId idCopy(cx, id); 1.250 + PIERCE(cx, wrapper, 1.251 + cx->compartment()->wrapId(cx, idCopy.address()), 1.252 + Wrapper::getOwnPropertyDescriptor(cx, wrapper, idCopy, desc), 1.253 + cx->compartment()->wrap(cx, desc)); 1.254 +} 1.255 + 1.256 +bool 1.257 +CrossCompartmentWrapper::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, 1.258 + MutableHandle<PropertyDescriptor> desc) 1.259 +{ 1.260 + RootedId idCopy(cx, id); 1.261 + Rooted<PropertyDescriptor> desc2(cx, desc); 1.262 + PIERCE(cx, wrapper, 1.263 + cx->compartment()->wrapId(cx, idCopy.address()) && cx->compartment()->wrap(cx, &desc2), 1.264 + Wrapper::defineProperty(cx, wrapper, idCopy, &desc2), 1.265 + NOTHING); 1.266 +} 1.267 + 1.268 +bool 1.269 +CrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, 1.270 + AutoIdVector &props) 1.271 +{ 1.272 + PIERCE(cx, wrapper, 1.273 + NOTHING, 1.274 + Wrapper::getOwnPropertyNames(cx, wrapper, props), 1.275 + cx->compartment()->wrap(cx, props)); 1.276 +} 1.277 + 1.278 +bool 1.279 +CrossCompartmentWrapper::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) 1.280 +{ 1.281 + RootedId idCopy(cx, id); 1.282 + PIERCE(cx, wrapper, 1.283 + cx->compartment()->wrapId(cx, idCopy.address()), 1.284 + Wrapper::delete_(cx, wrapper, idCopy, bp), 1.285 + NOTHING); 1.286 +} 1.287 + 1.288 +bool 1.289 +CrossCompartmentWrapper::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) 1.290 +{ 1.291 + PIERCE(cx, wrapper, 1.292 + NOTHING, 1.293 + Wrapper::enumerate(cx, wrapper, props), 1.294 + cx->compartment()->wrap(cx, props)); 1.295 +} 1.296 + 1.297 +bool 1.298 +CrossCompartmentWrapper::has(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) 1.299 +{ 1.300 + RootedId idCopy(cx, id); 1.301 + PIERCE(cx, wrapper, 1.302 + cx->compartment()->wrapId(cx, idCopy.address()), 1.303 + Wrapper::has(cx, wrapper, idCopy, bp), 1.304 + NOTHING); 1.305 +} 1.306 + 1.307 +bool 1.308 +CrossCompartmentWrapper::hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) 1.309 +{ 1.310 + RootedId idCopy(cx, id); 1.311 + PIERCE(cx, wrapper, 1.312 + cx->compartment()->wrapId(cx, idCopy.address()), 1.313 + Wrapper::hasOwn(cx, wrapper, idCopy, bp), 1.314 + NOTHING); 1.315 +} 1.316 + 1.317 +bool 1.318 +CrossCompartmentWrapper::get(JSContext *cx, HandleObject wrapper, HandleObject receiver, 1.319 + HandleId id, MutableHandleValue vp) 1.320 +{ 1.321 + RootedObject receiverCopy(cx, receiver); 1.322 + RootedId idCopy(cx, id); 1.323 + { 1.324 + AutoCompartment call(cx, wrappedObject(wrapper)); 1.325 + if (!cx->compartment()->wrap(cx, &receiverCopy) || 1.326 + !cx->compartment()->wrapId(cx, idCopy.address())) 1.327 + { 1.328 + return false; 1.329 + } 1.330 + 1.331 + if (!Wrapper::get(cx, wrapper, receiverCopy, idCopy, vp)) 1.332 + return false; 1.333 + } 1.334 + return cx->compartment()->wrap(cx, vp); 1.335 +} 1.336 + 1.337 +bool 1.338 +CrossCompartmentWrapper::set(JSContext *cx, HandleObject wrapper, HandleObject receiver, 1.339 + HandleId id, bool strict, MutableHandleValue vp) 1.340 +{ 1.341 + RootedObject receiverCopy(cx, receiver); 1.342 + RootedId idCopy(cx, id); 1.343 + PIERCE(cx, wrapper, 1.344 + cx->compartment()->wrap(cx, &receiverCopy) && 1.345 + cx->compartment()->wrapId(cx, idCopy.address()) && 1.346 + cx->compartment()->wrap(cx, vp), 1.347 + Wrapper::set(cx, wrapper, receiverCopy, idCopy, strict, vp), 1.348 + NOTHING); 1.349 +} 1.350 + 1.351 +bool 1.352 +CrossCompartmentWrapper::keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) 1.353 +{ 1.354 + PIERCE(cx, wrapper, 1.355 + NOTHING, 1.356 + Wrapper::keys(cx, wrapper, props), 1.357 + cx->compartment()->wrap(cx, props)); 1.358 +} 1.359 + 1.360 +/* 1.361 + * We can reify non-escaping iterator objects instead of having to wrap them. This 1.362 + * allows fast iteration over objects across a compartment boundary. 1.363 + */ 1.364 +static bool 1.365 +CanReify(HandleValue vp) 1.366 +{ 1.367 + JSObject *obj; 1.368 + return vp.isObject() && 1.369 + (obj = &vp.toObject())->is<PropertyIteratorObject>() && 1.370 + (obj->as<PropertyIteratorObject>().getNativeIterator()->flags & JSITER_ENUMERATE); 1.371 +} 1.372 + 1.373 +struct AutoCloseIterator 1.374 +{ 1.375 + AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(cx, obj) {} 1.376 + 1.377 + ~AutoCloseIterator() { if (obj) CloseIterator(cx, obj); } 1.378 + 1.379 + void clear() { obj = nullptr; } 1.380 + 1.381 + private: 1.382 + JSContext *cx; 1.383 + RootedObject obj; 1.384 +}; 1.385 + 1.386 +static bool 1.387 +Reify(JSContext *cx, JSCompartment *origin, MutableHandleValue vp) 1.388 +{ 1.389 + Rooted<PropertyIteratorObject*> iterObj(cx, &vp.toObject().as<PropertyIteratorObject>()); 1.390 + NativeIterator *ni = iterObj->getNativeIterator(); 1.391 + 1.392 + AutoCloseIterator close(cx, iterObj); 1.393 + 1.394 + /* Wrap the iteratee. */ 1.395 + RootedObject obj(cx, ni->obj); 1.396 + if (!origin->wrap(cx, &obj)) 1.397 + return false; 1.398 + 1.399 + /* 1.400 + * Wrap the elements in the iterator's snapshot. 1.401 + * N.B. the order of closing/creating iterators is important due to the 1.402 + * implicit cx->enumerators state. 1.403 + */ 1.404 + size_t length = ni->numKeys(); 1.405 + bool isKeyIter = ni->isKeyIter(); 1.406 + AutoIdVector keys(cx); 1.407 + if (length > 0) { 1.408 + if (!keys.reserve(length)) 1.409 + return false; 1.410 + for (size_t i = 0; i < length; ++i) { 1.411 + RootedId id(cx); 1.412 + RootedValue v(cx, StringValue(ni->begin()[i])); 1.413 + if (!ValueToId<CanGC>(cx, v, &id)) 1.414 + return false; 1.415 + keys.infallibleAppend(id); 1.416 + if (!origin->wrapId(cx, &keys[i])) 1.417 + return false; 1.418 + } 1.419 + } 1.420 + 1.421 + close.clear(); 1.422 + if (!CloseIterator(cx, iterObj)) 1.423 + return false; 1.424 + 1.425 + if (isKeyIter) { 1.426 + if (!VectorToKeyIterator(cx, obj, ni->flags, keys, vp)) 1.427 + return false; 1.428 + } else { 1.429 + if (!VectorToValueIterator(cx, obj, ni->flags, keys, vp)) 1.430 + return false; 1.431 + } 1.432 + return true; 1.433 +} 1.434 + 1.435 +bool 1.436 +CrossCompartmentWrapper::iterate(JSContext *cx, HandleObject wrapper, unsigned flags, 1.437 + MutableHandleValue vp) 1.438 +{ 1.439 + { 1.440 + AutoCompartment call(cx, wrappedObject(wrapper)); 1.441 + if (!Wrapper::iterate(cx, wrapper, flags, vp)) 1.442 + return false; 1.443 + } 1.444 + 1.445 + if (CanReify(vp)) 1.446 + return Reify(cx, cx->compartment(), vp); 1.447 + return cx->compartment()->wrap(cx, vp); 1.448 +} 1.449 + 1.450 +bool 1.451 +CrossCompartmentWrapper::call(JSContext *cx, HandleObject wrapper, const CallArgs &args) 1.452 +{ 1.453 + RootedObject wrapped(cx, wrappedObject(wrapper)); 1.454 + 1.455 + { 1.456 + AutoCompartment call(cx, wrapped); 1.457 + 1.458 + args.setCallee(ObjectValue(*wrapped)); 1.459 + if (!cx->compartment()->wrap(cx, args.mutableThisv())) 1.460 + return false; 1.461 + 1.462 + for (size_t n = 0; n < args.length(); ++n) { 1.463 + if (!cx->compartment()->wrap(cx, args[n])) 1.464 + return false; 1.465 + } 1.466 + 1.467 + if (!Wrapper::call(cx, wrapper, args)) 1.468 + return false; 1.469 + } 1.470 + 1.471 + return cx->compartment()->wrap(cx, args.rval()); 1.472 +} 1.473 + 1.474 +bool 1.475 +CrossCompartmentWrapper::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) 1.476 +{ 1.477 + RootedObject wrapped(cx, wrappedObject(wrapper)); 1.478 + { 1.479 + AutoCompartment call(cx, wrapped); 1.480 + 1.481 + for (size_t n = 0; n < args.length(); ++n) { 1.482 + if (!cx->compartment()->wrap(cx, args[n])) 1.483 + return false; 1.484 + } 1.485 + if (!Wrapper::construct(cx, wrapper, args)) 1.486 + return false; 1.487 + } 1.488 + return cx->compartment()->wrap(cx, args.rval()); 1.489 +} 1.490 + 1.491 +bool 1.492 +CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, 1.493 + CallArgs srcArgs) 1.494 +{ 1.495 + RootedObject wrapper(cx, &srcArgs.thisv().toObject()); 1.496 + JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) || 1.497 + !UncheckedUnwrap(wrapper)->is<CrossCompartmentWrapperObject>()); 1.498 + 1.499 + RootedObject wrapped(cx, wrappedObject(wrapper)); 1.500 + { 1.501 + AutoCompartment call(cx, wrapped); 1.502 + InvokeArgs dstArgs(cx); 1.503 + if (!dstArgs.init(srcArgs.length())) 1.504 + return false; 1.505 + 1.506 + Value *src = srcArgs.base(); 1.507 + Value *srcend = srcArgs.array() + srcArgs.length(); 1.508 + Value *dst = dstArgs.base(); 1.509 + 1.510 + RootedValue source(cx); 1.511 + for (; src < srcend; ++src, ++dst) { 1.512 + source = *src; 1.513 + if (!cx->compartment()->wrap(cx, &source)) 1.514 + return false; 1.515 + *dst = source.get(); 1.516 + 1.517 + // Handle |this| specially. When we rewrap on the other side of the 1.518 + // membrane, we might apply a same-compartment security wrapper that 1.519 + // will stymie this whole process. If that happens, unwrap the wrapper. 1.520 + // This logic can go away when same-compartment security wrappers go away. 1.521 + if ((src == srcArgs.base() + 1) && dst->isObject()) { 1.522 + RootedObject thisObj(cx, &dst->toObject()); 1.523 + if (thisObj->is<WrapperObject>() && 1.524 + Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy()) 1.525 + { 1.526 + JS_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>()); 1.527 + *dst = ObjectValue(*Wrapper::wrappedObject(thisObj)); 1.528 + } 1.529 + } 1.530 + } 1.531 + 1.532 + if (!CallNonGenericMethod(cx, test, impl, dstArgs)) 1.533 + return false; 1.534 + 1.535 + srcArgs.rval().set(dstArgs.rval()); 1.536 + } 1.537 + return cx->compartment()->wrap(cx, srcArgs.rval()); 1.538 +} 1.539 + 1.540 +bool 1.541 +CrossCompartmentWrapper::hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, 1.542 + bool *bp) 1.543 +{ 1.544 + AutoCompartment call(cx, wrappedObject(wrapper)); 1.545 + if (!cx->compartment()->wrap(cx, v)) 1.546 + return false; 1.547 + return Wrapper::hasInstance(cx, wrapper, v, bp); 1.548 +} 1.549 + 1.550 +const char * 1.551 +CrossCompartmentWrapper::className(JSContext *cx, HandleObject wrapper) 1.552 +{ 1.553 + AutoCompartment call(cx, wrappedObject(wrapper)); 1.554 + return Wrapper::className(cx, wrapper); 1.555 +} 1.556 + 1.557 +JSString * 1.558 +CrossCompartmentWrapper::fun_toString(JSContext *cx, HandleObject wrapper, unsigned indent) 1.559 +{ 1.560 + RootedString str(cx); 1.561 + { 1.562 + AutoCompartment call(cx, wrappedObject(wrapper)); 1.563 + str = Wrapper::fun_toString(cx, wrapper, indent); 1.564 + if (!str) 1.565 + return nullptr; 1.566 + } 1.567 + if (!cx->compartment()->wrap(cx, str.address())) 1.568 + return nullptr; 1.569 + return str; 1.570 +} 1.571 + 1.572 +bool 1.573 +CrossCompartmentWrapper::regexp_toShared(JSContext *cx, HandleObject wrapper, RegExpGuard *g) 1.574 +{ 1.575 + AutoCompartment call(cx, wrappedObject(wrapper)); 1.576 + return Wrapper::regexp_toShared(cx, wrapper, g); 1.577 +} 1.578 + 1.579 +bool 1.580 +CrossCompartmentWrapper::defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, 1.581 + MutableHandleValue vp) 1.582 +{ 1.583 + PIERCE(cx, wrapper, 1.584 + NOTHING, 1.585 + Wrapper::defaultValue(cx, wrapper, hint, vp), 1.586 + cx->compartment()->wrap(cx, vp)); 1.587 +} 1.588 + 1.589 +bool 1.590 +CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper, 1.591 + MutableHandleObject protop) 1.592 +{ 1.593 + { 1.594 + RootedObject wrapped(cx, wrappedObject(wrapper)); 1.595 + AutoCompartment call(cx, wrapped); 1.596 + if (!JSObject::getProto(cx, wrapped, protop)) 1.597 + return false; 1.598 + if (protop) 1.599 + protop->setDelegate(cx); 1.600 + } 1.601 + 1.602 + return cx->compartment()->wrap(cx, protop); 1.603 +} 1.604 + 1.605 +bool 1.606 +CrossCompartmentWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper, 1.607 + HandleObject proto, bool *bp) 1.608 +{ 1.609 + RootedObject protoCopy(cx, proto); 1.610 + PIERCE(cx, wrapper, 1.611 + cx->compartment()->wrap(cx, &protoCopy), 1.612 + Wrapper::setPrototypeOf(cx, wrapper, protoCopy, bp), 1.613 + NOTHING); 1.614 +} 1.615 + 1.616 +CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u); 1.617 + 1.618 +/* Security wrappers. */ 1.619 + 1.620 +template <class Base> 1.621 +SecurityWrapper<Base>::SecurityWrapper(unsigned flags) 1.622 + : Base(flags) 1.623 +{ 1.624 + BaseProxyHandler::setHasSecurityPolicy(true); 1.625 +} 1.626 + 1.627 +template <class Base> 1.628 +bool 1.629 +SecurityWrapper<Base>::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) 1.630 +{ 1.631 + // Just like BaseProxyHandler, SecurityWrappers claim by default to always 1.632 + // be extensible, so as not to leak information about the state of the 1.633 + // underlying wrapped thing. 1.634 + *extensible = true; 1.635 + return true; 1.636 +} 1.637 + 1.638 +template <class Base> 1.639 +bool 1.640 +SecurityWrapper<Base>::preventExtensions(JSContext *cx, HandleObject wrapper) 1.641 +{ 1.642 + // See above. 1.643 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); 1.644 + return false; 1.645 +} 1.646 + 1.647 +template <class Base> 1.648 +bool 1.649 +SecurityWrapper<Base>::enter(JSContext *cx, HandleObject wrapper, HandleId id, 1.650 + Wrapper::Action act, bool *bp) 1.651 +{ 1.652 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); 1.653 + *bp = false; 1.654 + return false; 1.655 +} 1.656 + 1.657 +template <class Base> 1.658 +bool 1.659 +SecurityWrapper<Base>::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, 1.660 + CallArgs args) 1.661 +{ 1.662 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); 1.663 + return false; 1.664 +} 1.665 + 1.666 +template <class Base> 1.667 +bool 1.668 +SecurityWrapper<Base>::setPrototypeOf(JSContext *cx, HandleObject wrapper, 1.669 + HandleObject proto, bool *bp) 1.670 +{ 1.671 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); 1.672 + return false; 1.673 +} 1.674 + 1.675 +// For security wrappers, we run the DefaultValue algorithm on the wrapper 1.676 +// itself, which means that the existing security policy on operations like 1.677 +// toString() will take effect and do the right thing here. 1.678 +template <class Base> 1.679 +bool 1.680 +SecurityWrapper<Base>::defaultValue(JSContext *cx, HandleObject wrapper, 1.681 + JSType hint, MutableHandleValue vp) 1.682 +{ 1.683 + return DefaultValue(cx, wrapper, hint, vp); 1.684 +} 1.685 + 1.686 +template <class Base> 1.687 +bool 1.688 +SecurityWrapper<Base>::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) 1.689 +{ 1.690 + return false; 1.691 +} 1.692 + 1.693 +template <class Base> 1.694 +bool 1.695 +SecurityWrapper<Base>::regexp_toShared(JSContext *cx, HandleObject obj, RegExpGuard *g) 1.696 +{ 1.697 + return Base::regexp_toShared(cx, obj, g); 1.698 +} 1.699 + 1.700 +template <class Base> 1.701 +bool 1.702 +SecurityWrapper<Base>::defineProperty(JSContext *cx, HandleObject wrapper, 1.703 + HandleId id, MutableHandle<PropertyDescriptor> desc) 1.704 +{ 1.705 + if (desc.getter() || desc.setter()) { 1.706 + JSString *str = IdToString(cx, id); 1.707 + const jschar *prop = str ? str->getCharsZ(cx) : nullptr; 1.708 + JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr, 1.709 + JSMSG_ACCESSOR_DEF_DENIED, prop); 1.710 + return false; 1.711 + } 1.712 + 1.713 + return Base::defineProperty(cx, wrapper, id, desc); 1.714 +} 1.715 + 1.716 +template <class Base> 1.717 +bool 1.718 +SecurityWrapper<Base>::watch(JSContext *cx, HandleObject proxy, 1.719 + HandleId id, HandleObject callable) 1.720 +{ 1.721 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); 1.722 + return false; 1.723 +} 1.724 + 1.725 +template <class Base> 1.726 +bool 1.727 +SecurityWrapper<Base>::unwatch(JSContext *cx, HandleObject proxy, 1.728 + HandleId id) 1.729 +{ 1.730 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); 1.731 + return false; 1.732 +} 1.733 + 1.734 + 1.735 +template class js::SecurityWrapper<Wrapper>; 1.736 +template class js::SecurityWrapper<CrossCompartmentWrapper>; 1.737 + 1.738 +DeadObjectProxy::DeadObjectProxy() 1.739 + : BaseProxyHandler(&sDeadObjectFamily) 1.740 +{ 1.741 +} 1.742 + 1.743 +bool 1.744 +DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) 1.745 +{ 1.746 + // This is kind of meaningless, but dead-object semantics aside, 1.747 + // [[Extensible]] always being true is consistent with other proxy types. 1.748 + *extensible = true; 1.749 + return true; 1.750 +} 1.751 + 1.752 +bool 1.753 +DeadObjectProxy::preventExtensions(JSContext *cx, HandleObject proxy) 1.754 +{ 1.755 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.756 + return false; 1.757 +} 1.758 + 1.759 +bool 1.760 +DeadObjectProxy::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, 1.761 + MutableHandle<PropertyDescriptor> desc) 1.762 +{ 1.763 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.764 + return false; 1.765 +} 1.766 + 1.767 +bool 1.768 +DeadObjectProxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, 1.769 + MutableHandle<PropertyDescriptor> desc) 1.770 +{ 1.771 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.772 + return false; 1.773 +} 1.774 + 1.775 +bool 1.776 +DeadObjectProxy::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, 1.777 + MutableHandle<PropertyDescriptor> desc) 1.778 +{ 1.779 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.780 + return false; 1.781 +} 1.782 + 1.783 +bool 1.784 +DeadObjectProxy::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, 1.785 + AutoIdVector &props) 1.786 +{ 1.787 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.788 + return false; 1.789 +} 1.790 + 1.791 +bool 1.792 +DeadObjectProxy::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) 1.793 +{ 1.794 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.795 + return false; 1.796 +} 1.797 + 1.798 +bool 1.799 +DeadObjectProxy::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) 1.800 +{ 1.801 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.802 + return false; 1.803 +} 1.804 + 1.805 +bool 1.806 +DeadObjectProxy::call(JSContext *cx, HandleObject wrapper, const CallArgs &args) 1.807 +{ 1.808 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.809 + return false; 1.810 +} 1.811 + 1.812 +bool 1.813 +DeadObjectProxy::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) 1.814 +{ 1.815 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.816 + return false; 1.817 +} 1.818 + 1.819 +bool 1.820 +DeadObjectProxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) 1.821 +{ 1.822 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.823 + return false; 1.824 +} 1.825 + 1.826 +bool 1.827 +DeadObjectProxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) 1.828 +{ 1.829 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.830 + return false; 1.831 +} 1.832 + 1.833 +bool 1.834 +DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) 1.835 +{ 1.836 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.837 + return false; 1.838 +} 1.839 + 1.840 +const char * 1.841 +DeadObjectProxy::className(JSContext *cx, HandleObject wrapper) 1.842 +{ 1.843 + return "DeadObject"; 1.844 +} 1.845 + 1.846 +JSString * 1.847 +DeadObjectProxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) 1.848 +{ 1.849 + return nullptr; 1.850 +} 1.851 + 1.852 +bool 1.853 +DeadObjectProxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) 1.854 +{ 1.855 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.856 + return false; 1.857 +} 1.858 + 1.859 +bool 1.860 +DeadObjectProxy::defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) 1.861 +{ 1.862 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 1.863 + return false; 1.864 +} 1.865 + 1.866 +bool 1.867 +DeadObjectProxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) 1.868 +{ 1.869 + protop.set(nullptr); 1.870 + return true; 1.871 +} 1.872 + 1.873 +DeadObjectProxy DeadObjectProxy::singleton; 1.874 +const char DeadObjectProxy::sDeadObjectFamily = 0; 1.875 + 1.876 +bool 1.877 +js::IsDeadProxyObject(JSObject *obj) 1.878 +{ 1.879 + return obj->is<ProxyObject>() && 1.880 + obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton; 1.881 +} 1.882 + 1.883 +void 1.884 +js::NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper) 1.885 +{ 1.886 + JS_ASSERT(wrapper->is<CrossCompartmentWrapperObject>()); 1.887 + 1.888 + NotifyGCNukeWrapper(wrapper); 1.889 + 1.890 + wrapper->as<ProxyObject>().nuke(&DeadObjectProxy::singleton); 1.891 + 1.892 + JS_ASSERT(IsDeadProxyObject(wrapper)); 1.893 +} 1.894 + 1.895 +/* 1.896 + * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts 1.897 + * all of the cross-compartment wrappers that point to objects parented to 1.898 + * obj's global. The snag here is that we need to avoid cutting wrappers that 1.899 + * point to the window object on page navigation (inner window destruction) 1.900 + * and only do that on tab close (outer window destruction). Thus the 1.901 + * option of how to handle the global object. 1.902 + */ 1.903 +JS_FRIEND_API(bool) 1.904 +js::NukeCrossCompartmentWrappers(JSContext* cx, 1.905 + const CompartmentFilter& sourceFilter, 1.906 + const CompartmentFilter& targetFilter, 1.907 + js::NukeReferencesToWindow nukeReferencesToWindow) 1.908 +{ 1.909 + CHECK_REQUEST(cx); 1.910 + JSRuntime *rt = cx->runtime(); 1.911 + 1.912 + // Iterate through scopes looking for system cross compartment wrappers 1.913 + // that point to an object that shares a global with obj. 1.914 + 1.915 + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { 1.916 + if (!sourceFilter.match(c)) 1.917 + continue; 1.918 + 1.919 + // Iterate the wrappers looking for anything interesting. 1.920 + for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { 1.921 + // Some cross-compartment wrappers are for strings. We're not 1.922 + // interested in those. 1.923 + const CrossCompartmentKey &k = e.front().key(); 1.924 + if (k.kind != CrossCompartmentKey::ObjectWrapper) 1.925 + continue; 1.926 + 1.927 + AutoWrapperRooter wobj(cx, WrapperValue(e)); 1.928 + JSObject *wrapped = UncheckedUnwrap(wobj); 1.929 + 1.930 + if (nukeReferencesToWindow == DontNukeWindowReferences && 1.931 + wrapped->getClass()->ext.innerObject) 1.932 + continue; 1.933 + 1.934 + if (targetFilter.match(wrapped->compartment())) { 1.935 + // We found a wrapper to nuke. 1.936 + e.removeFront(); 1.937 + NukeCrossCompartmentWrapper(cx, wobj); 1.938 + } 1.939 + } 1.940 + } 1.941 + 1.942 + return true; 1.943 +} 1.944 + 1.945 +// Given a cross-compartment wrapper |wobj|, update it to point to 1.946 +// |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be 1.947 +// useful even if wrapper already points to newTarget. 1.948 +bool 1.949 +js::RemapWrapper(JSContext *cx, JSObject *wobjArg, JSObject *newTargetArg) 1.950 +{ 1.951 + RootedObject wobj(cx, wobjArg); 1.952 + RootedObject newTarget(cx, newTargetArg); 1.953 + JS_ASSERT(wobj->is<CrossCompartmentWrapperObject>()); 1.954 + JS_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>()); 1.955 + JSObject *origTarget = Wrapper::wrappedObject(wobj); 1.956 + JS_ASSERT(origTarget); 1.957 + Value origv = ObjectValue(*origTarget); 1.958 + JSCompartment *wcompartment = wobj->compartment(); 1.959 + 1.960 + AutoDisableProxyCheck adpc(cx->runtime()); 1.961 + 1.962 + // If we're mapping to a different target (as opposed to just recomputing 1.963 + // for the same target), we must not have an existing wrapper for the new 1.964 + // target, otherwise this will break. 1.965 + JS_ASSERT_IF(origTarget != newTarget, 1.966 + !wcompartment->lookupWrapper(ObjectValue(*newTarget))); 1.967 + 1.968 + // The old value should still be in the cross-compartment wrapper map, and 1.969 + // the lookup should return wobj. 1.970 + WrapperMap::Ptr p = wcompartment->lookupWrapper(origv); 1.971 + JS_ASSERT(&p->value().unsafeGet()->toObject() == wobj); 1.972 + wcompartment->removeWrapper(p); 1.973 + 1.974 + // When we remove origv from the wrapper map, its wrapper, wobj, must 1.975 + // immediately cease to be a cross-compartment wrapper. Neuter it. 1.976 + NukeCrossCompartmentWrapper(cx, wobj); 1.977 + 1.978 + // First, we wrap it in the new compartment. We try to use the existing 1.979 + // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has 1.980 + // the choice to reuse |wobj| or not. 1.981 + RootedObject tobj(cx, newTarget); 1.982 + AutoCompartment ac(cx, wobj); 1.983 + if (!wcompartment->wrap(cx, &tobj, wobj)) 1.984 + MOZ_CRASH(); 1.985 + 1.986 + // If wrap() reused |wobj|, it will have overwritten it and returned with 1.987 + // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj| 1.988 + // will still be nuked. In the latter case, we replace |wobj| with the 1.989 + // contents of the new wrapper in |tobj|. 1.990 + if (tobj != wobj) { 1.991 + // Now, because we need to maintain object identity, we do a brain 1.992 + // transplant on the old object so that it contains the contents of the 1.993 + // new one. 1.994 + if (!JSObject::swap(cx, wobj, tobj)) 1.995 + MOZ_CRASH(); 1.996 + } 1.997 + 1.998 + // Before swapping, this wrapper came out of wrap(), which enforces the 1.999 + // invariant that the wrapper in the map points directly to the key. 1.1000 + JS_ASSERT(Wrapper::wrappedObject(wobj) == newTarget); 1.1001 + 1.1002 + // Update the entry in the compartment's wrapper map to point to the old 1.1003 + // wrapper, which has now been updated (via reuse or swap). 1.1004 + JS_ASSERT(wobj->is<WrapperObject>()); 1.1005 + wcompartment->putWrapper(cx, ObjectValue(*newTarget), ObjectValue(*wobj)); 1.1006 + return true; 1.1007 +} 1.1008 + 1.1009 +// Remap all cross-compartment wrappers pointing to |oldTarget| to point to 1.1010 +// |newTarget|. All wrappers are recomputed. 1.1011 +JS_FRIEND_API(bool) 1.1012 +js::RemapAllWrappersForObject(JSContext *cx, JSObject *oldTargetArg, 1.1013 + JSObject *newTargetArg) 1.1014 +{ 1.1015 + RootedValue origv(cx, ObjectValue(*oldTargetArg)); 1.1016 + RootedObject newTarget(cx, newTargetArg); 1.1017 + 1.1018 + AutoWrapperVector toTransplant(cx); 1.1019 + if (!toTransplant.reserve(cx->runtime()->numCompartments)) 1.1020 + return false; 1.1021 + 1.1022 + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { 1.1023 + if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) { 1.1024 + // We found a wrapper. Remember and root it. 1.1025 + toTransplant.infallibleAppend(WrapperValue(wp)); 1.1026 + } 1.1027 + } 1.1028 + 1.1029 + for (WrapperValue *begin = toTransplant.begin(), *end = toTransplant.end(); 1.1030 + begin != end; ++begin) 1.1031 + { 1.1032 + if (!RemapWrapper(cx, &begin->toObject(), newTarget)) 1.1033 + MOZ_CRASH(); 1.1034 + } 1.1035 + 1.1036 + return true; 1.1037 +} 1.1038 + 1.1039 +JS_FRIEND_API(bool) 1.1040 +js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, 1.1041 + const CompartmentFilter &targetFilter) 1.1042 +{ 1.1043 + AutoMaybeTouchDeadZones agc(cx); 1.1044 + 1.1045 + AutoWrapperVector toRecompute(cx); 1.1046 + 1.1047 + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { 1.1048 + // Filter by source compartment. 1.1049 + if (!sourceFilter.match(c)) 1.1050 + continue; 1.1051 + 1.1052 + // Iterate over the wrappers, filtering appropriately. 1.1053 + for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { 1.1054 + // Filter out non-objects. 1.1055 + const CrossCompartmentKey &k = e.front().key(); 1.1056 + if (k.kind != CrossCompartmentKey::ObjectWrapper) 1.1057 + continue; 1.1058 + 1.1059 + // Filter by target compartment. 1.1060 + if (!targetFilter.match(static_cast<JSObject *>(k.wrapped)->compartment())) 1.1061 + continue; 1.1062 + 1.1063 + // Add it to the list. 1.1064 + if (!toRecompute.append(WrapperValue(e))) 1.1065 + return false; 1.1066 + } 1.1067 + } 1.1068 + 1.1069 + // Recompute all the wrappers in the list. 1.1070 + for (WrapperValue *begin = toRecompute.begin(), *end = toRecompute.end(); begin != end; ++begin) 1.1071 + { 1.1072 + JSObject *wrapper = &begin->toObject(); 1.1073 + JSObject *wrapped = Wrapper::wrappedObject(wrapper); 1.1074 + if (!RemapWrapper(cx, wrapper, wrapped)) 1.1075 + MOZ_CRASH(); 1.1076 + } 1.1077 + 1.1078 + return true; 1.1079 +}