js/src/jswrapper.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial