LCOV - code coverage report
Current view: top level - js/xpconnect/src - Sandbox.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 386 931 41.5 %
Date: 2017-07-14 16:53:18 Functions: 35 74 47.3 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : /*
       8             :  * The Components.Sandbox object.
       9             :  */
      10             : 
      11             : #include "AccessCheck.h"
      12             : #include "jsfriendapi.h"
      13             : #include "js/Proxy.h"
      14             : #include "js/StructuredClone.h"
      15             : #include "nsContentUtils.h"
      16             : #include "nsGlobalWindow.h"
      17             : #include "nsIScriptContext.h"
      18             : #include "nsIScriptObjectPrincipal.h"
      19             : #include "nsIURI.h"
      20             : #include "nsJSUtils.h"
      21             : #include "nsNetUtil.h"
      22             : #include "NullPrincipal.h"
      23             : #include "ExpandedPrincipal.h"
      24             : #include "WrapperFactory.h"
      25             : #include "xpcprivate.h"
      26             : #include "xpc_make_class.h"
      27             : #include "XPCWrapper.h"
      28             : #include "XrayWrapper.h"
      29             : #include "Crypto.h"
      30             : #include "mozilla/dom/BindingUtils.h"
      31             : #include "mozilla/dom/BlobBinding.h"
      32             : #include "mozilla/dom/cache/CacheStorage.h"
      33             : #include "mozilla/dom/CSSBinding.h"
      34             : #include "mozilla/dom/DirectoryBinding.h"
      35             : #include "mozilla/dom/IndexedDatabaseManager.h"
      36             : #include "mozilla/dom/Fetch.h"
      37             : #include "mozilla/dom/FileBinding.h"
      38             : #include "mozilla/dom/MessageChannelBinding.h"
      39             : #include "mozilla/dom/MessagePortBinding.h"
      40             : #include "mozilla/dom/PromiseBinding.h"
      41             : #include "mozilla/dom/RequestBinding.h"
      42             : #include "mozilla/dom/ResponseBinding.h"
      43             : #ifdef MOZ_WEBRTC
      44             : #include "mozilla/dom/RTCIdentityProviderRegistrar.h"
      45             : #endif
      46             : #include "mozilla/dom/FileReaderBinding.h"
      47             : #include "mozilla/dom/ScriptSettings.h"
      48             : #include "mozilla/dom/TextDecoderBinding.h"
      49             : #include "mozilla/dom/TextEncoderBinding.h"
      50             : #include "mozilla/dom/UnionConversions.h"
      51             : #include "mozilla/dom/URLBinding.h"
      52             : #include "mozilla/dom/URLSearchParamsBinding.h"
      53             : #include "mozilla/dom/XMLHttpRequest.h"
      54             : #include "mozilla/DeferredFinalize.h"
      55             : 
      56             : using namespace mozilla;
      57             : using namespace JS;
      58             : using namespace xpc;
      59             : 
      60             : using mozilla::dom::DestroyProtoAndIfaceCache;
      61             : using mozilla::dom::IndexedDatabaseManager;
      62             : 
      63             : NS_IMPL_CYCLE_COLLECTION_CLASS(SandboxPrivate)
      64             : 
      65           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SandboxPrivate)
      66           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
      67           0 :   tmp->UnlinkHostObjectURIs();
      68           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      69             : 
      70           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SandboxPrivate)
      71           0 :   tmp->TraverseHostObjectURIs(cb);
      72           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      73             : 
      74           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(SandboxPrivate)
      75             : 
      76        2425 : NS_IMPL_CYCLE_COLLECTING_ADDREF(SandboxPrivate)
      77        2332 : NS_IMPL_CYCLE_COLLECTING_RELEASE(SandboxPrivate)
      78        2272 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SandboxPrivate)
      79        2247 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
      80        2247 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptObjectPrincipal)
      81        2247 :   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
      82        2222 :   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
      83         631 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
      84         576 : NS_INTERFACE_MAP_END
      85             : 
      86             : const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID;
      87             : 
      88             : class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox,
      89             :                                       public nsIXPCScriptable
      90             : {
      91             : public:
      92             :     // Aren't macros nice?
      93             :     NS_DECL_ISUPPORTS
      94             :     NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX
      95             :     NS_DECL_NSIXPCSCRIPTABLE
      96             : 
      97             : public:
      98             :     nsXPCComponents_utils_Sandbox();
      99             : 
     100             : private:
     101             :     virtual ~nsXPCComponents_utils_Sandbox();
     102             : 
     103             :     static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
     104             :                                     JSContext* cx, HandleObject obj,
     105             :                                     const CallArgs& args, bool* _retval);
     106             : };
     107             : 
     108             : already_AddRefed<nsIXPCComponents_utils_Sandbox>
     109           2 : xpc::NewSandboxConstructor()
     110             : {
     111             :     nsCOMPtr<nsIXPCComponents_utils_Sandbox> sbConstructor =
     112           4 :         new nsXPCComponents_utils_Sandbox();
     113           4 :     return sbConstructor.forget();
     114             : }
     115             : 
     116             : static bool
     117           0 : SandboxDump(JSContext* cx, unsigned argc, Value* vp)
     118             : {
     119           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     120             : 
     121           0 :     if (args.length() == 0)
     122           0 :         return true;
     123             : 
     124           0 :     RootedString str(cx, ToString(cx, args[0]));
     125           0 :     if (!str)
     126           0 :         return false;
     127             : 
     128           0 :     JSAutoByteString utf8str;
     129           0 :     char* cstr = utf8str.encodeUtf8(cx, str);
     130           0 :     if (!cstr)
     131           0 :         return false;
     132             : 
     133             : #if defined(XP_MACOSX)
     134             :     // Be nice and convert all \r to \n.
     135             :     char* c = cstr;
     136             :     char* cEnd = cstr + strlen(cstr);
     137             :     while (c < cEnd) {
     138             :         if (*c == '\r')
     139             :             *c = '\n';
     140             :         c++;
     141             :     }
     142             : #endif
     143             : #ifdef ANDROID
     144             :     __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
     145             : #endif
     146             : 
     147           0 :     fputs(cstr, stdout);
     148           0 :     fflush(stdout);
     149           0 :     args.rval().setBoolean(true);
     150           0 :     return true;
     151             : }
     152             : 
     153             : static bool
     154           0 : SandboxDebug(JSContext* cx, unsigned argc, Value* vp)
     155             : {
     156             : #ifdef DEBUG
     157           0 :     return SandboxDump(cx, argc, vp);
     158             : #else
     159             :     return true;
     160             : #endif
     161             : }
     162             : 
     163             : static bool
     164           0 : SandboxImport(JSContext* cx, unsigned argc, Value* vp)
     165             : {
     166           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     167             : 
     168           0 :     if (args.length() < 1 || args[0].isPrimitive()) {
     169           0 :         XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
     170           0 :         return false;
     171             :     }
     172             : 
     173           0 :     RootedString funname(cx);
     174           0 :     if (args.length() > 1) {
     175             :         // Use the second parameter as the function name.
     176           0 :         funname = ToString(cx, args[1]);
     177           0 :         if (!funname)
     178           0 :             return false;
     179             :     } else {
     180             :         // NB: funobj must only be used to get the JSFunction out.
     181           0 :         RootedObject funobj(cx, &args[0].toObject());
     182           0 :         if (js::IsProxy(funobj)) {
     183           0 :             funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj);
     184             :         }
     185             : 
     186           0 :         JSAutoCompartment ac(cx, funobj);
     187             : 
     188           0 :         RootedValue funval(cx, ObjectValue(*funobj));
     189           0 :         JSFunction* fun = JS_ValueToFunction(cx, funval);
     190           0 :         if (!fun) {
     191           0 :             XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
     192           0 :             return false;
     193             :         }
     194             : 
     195             :         // Use the actual function name as the name.
     196           0 :         funname = JS_GetFunctionId(fun);
     197           0 :         if (!funname) {
     198           0 :             XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
     199           0 :             return false;
     200             :         }
     201             :     }
     202           0 :     JS_MarkCrossZoneIdValue(cx, StringValue(funname));
     203             : 
     204           0 :     RootedId id(cx);
     205           0 :     if (!JS_StringToId(cx, funname, &id))
     206           0 :         return false;
     207             : 
     208             :     // We need to resolve the this object, because this function is used
     209             :     // unbound and should still work and act on the original sandbox.
     210           0 :     RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp));
     211           0 :     if (!thisObject) {
     212           0 :         XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
     213           0 :         return false;
     214             :     }
     215           0 :     if (!JS_SetPropertyById(cx, thisObject, id, args[0]))
     216           0 :         return false;
     217             : 
     218           0 :     args.rval().setUndefined();
     219           0 :     return true;
     220             : }
     221             : 
     222             : static bool
     223           0 : SandboxCreateCrypto(JSContext* cx, JS::HandleObject obj)
     224             : {
     225           0 :     MOZ_ASSERT(JS_IsGlobalObject(obj));
     226             : 
     227           0 :     nsIGlobalObject* native = xpc::NativeGlobal(obj);
     228           0 :     MOZ_ASSERT(native);
     229             : 
     230           0 :     dom::Crypto* crypto = new dom::Crypto();
     231           0 :     crypto->Init(native);
     232           0 :     JS::RootedObject wrapped(cx, crypto->WrapObject(cx, nullptr));
     233           0 :     return JS_DefineProperty(cx, obj, "crypto", wrapped, JSPROP_ENUMERATE);
     234             : }
     235             : 
     236             : #ifdef MOZ_WEBRTC
     237             : static bool
     238           0 : SandboxCreateRTCIdentityProvider(JSContext* cx, JS::HandleObject obj)
     239             : {
     240           0 :     MOZ_ASSERT(JS_IsGlobalObject(obj));
     241             : 
     242           0 :     nsCOMPtr<nsIGlobalObject> nativeGlobal = xpc::NativeGlobal(obj);
     243           0 :     MOZ_ASSERT(nativeGlobal);
     244             : 
     245             :     dom::RTCIdentityProviderRegistrar* registrar =
     246           0 :             new dom::RTCIdentityProviderRegistrar(nativeGlobal);
     247           0 :     JS::RootedObject wrapped(cx, registrar->WrapObject(cx, nullptr));
     248           0 :     return JS_DefineProperty(cx, obj, "rtcIdentityProvider", wrapped, JSPROP_ENUMERATE);
     249             : }
     250             : #endif
     251             : 
     252             : static bool
     253           1 : SetFetchRequestFromValue(JSContext *cx, RequestOrUSVString& request,
     254             :                          const MutableHandleValue& requestOrUrl)
     255             : {
     256           1 :     RequestOrUSVStringArgument requestHolder(request);
     257           1 :     bool noMatch = true;
     258           1 :     if (requestOrUrl.isObject() &&
     259           0 :         !requestHolder.TrySetToRequest(cx, requestOrUrl, noMatch, false)) {
     260           0 :         return false;
     261             :     }
     262           2 :     if (noMatch &&
     263           1 :         !requestHolder.TrySetToUSVString(cx, requestOrUrl, noMatch)) {
     264           0 :         return false;
     265             :     }
     266           1 :     if (noMatch) {
     267           0 :         return false;
     268             :     }
     269           1 :     return true;
     270             : }
     271             : 
     272             : static bool
     273           1 : SandboxFetch(JSContext* cx, JS::HandleObject scope, const CallArgs& args)
     274             : {
     275           1 :     if (args.length() < 1) {
     276           0 :         JS_ReportErrorASCII(cx, "fetch requires at least 1 argument");
     277           0 :         return false;
     278             :     }
     279             : 
     280           2 :     RequestOrUSVString request;
     281           1 :     if (!SetFetchRequestFromValue(cx, request, args[0])) {
     282           0 :         JS_ReportErrorASCII(cx, "fetch requires a string or Request in argument 1");
     283           0 :         return false;
     284             :     }
     285           2 :     RootedDictionary<dom::RequestInit> options(cx);
     286           1 :     if (!options.Init(cx, args.hasDefined(1) ? args[1] : JS::NullHandleValue,
     287             :                       "Argument 2 of fetch", false)) {
     288           0 :         return false;
     289             :     }
     290           2 :     nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(scope);
     291           1 :     if (!global) {
     292           0 :         return false;
     293             :     }
     294           1 :     dom::CallerType callerType = nsContentUtils::IsSystemCaller(cx) ?
     295           1 :         dom::CallerType::System : dom::CallerType::NonSystem;
     296           2 :     ErrorResult rv;
     297             :     RefPtr<dom::Promise> response =
     298           2 :         FetchRequest(global, Constify(request), Constify(options),
     299           2 :                      callerType, rv);
     300           1 :     if (rv.MaybeSetPendingException(cx)) {
     301           0 :         return false;
     302             :     }
     303             : 
     304           1 :     args.rval().setObject(*response->PromiseObj());
     305           1 :     return true;
     306             : }
     307             : 
     308           1 : static bool SandboxFetchPromise(JSContext* cx, unsigned argc, Value* vp)
     309             : {
     310           1 :     CallArgs args = CallArgsFromVp(argc, vp);
     311           2 :     RootedObject callee(cx, &args.callee());
     312           2 :     RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
     313           1 :     if (SandboxFetch(cx, scope, args)) {
     314           1 :         return true;
     315             :     }
     316           0 :     return ConvertExceptionToPromise(cx, scope, args.rval());
     317             : }
     318             : 
     319             : 
     320             : static bool
     321           3 : SandboxCreateFetch(JSContext* cx, HandleObject obj)
     322             : {
     323           3 :     MOZ_ASSERT(JS_IsGlobalObject(obj));
     324             : 
     325           6 :     return JS_DefineFunction(cx, obj, "fetch", SandboxFetchPromise, 2, 0) &&
     326           6 :         dom::RequestBinding::GetConstructorObject(cx) &&
     327           9 :         dom::ResponseBinding::GetConstructorObject(cx) &&
     328           6 :         dom::HeadersBinding::GetConstructorObject(cx);
     329             : }
     330             : 
     331             : static bool
     332           0 : SandboxIsProxy(JSContext* cx, unsigned argc, Value* vp)
     333             : {
     334           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     335           0 :     if (args.length() < 1) {
     336           0 :         JS_ReportErrorASCII(cx, "Function requires at least 1 argument");
     337           0 :         return false;
     338             :     }
     339           0 :     if (!args[0].isObject()) {
     340           0 :         args.rval().setBoolean(false);
     341           0 :         return true;
     342             :     }
     343             : 
     344           0 :     RootedObject obj(cx, &args[0].toObject());
     345           0 :     obj = js::CheckedUnwrap(obj);
     346           0 :     NS_ENSURE_TRUE(obj, false);
     347             : 
     348           0 :     args.rval().setBoolean(js::IsScriptedProxy(obj));
     349           0 :     return true;
     350             : }
     351             : 
     352             : /*
     353             :  * Expected type of the arguments and the return value:
     354             :  * function exportFunction(function funToExport,
     355             :  *                         object targetScope,
     356             :  *                         [optional] object options)
     357             :  */
     358             : static bool
     359           0 : SandboxExportFunction(JSContext* cx, unsigned argc, Value* vp)
     360             : {
     361           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     362           0 :     if (args.length() < 2) {
     363           0 :         JS_ReportErrorASCII(cx, "Function requires at least 2 arguments");
     364           0 :         return false;
     365             :     }
     366             : 
     367           0 :     RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
     368           0 :     return ExportFunction(cx, args[0], args[1], options, args.rval());
     369             : }
     370             : 
     371             : static bool
     372           0 : SandboxCreateObjectIn(JSContext* cx, unsigned argc, Value* vp)
     373             : {
     374           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     375           0 :     if (args.length() < 1) {
     376           0 :         JS_ReportErrorASCII(cx, "Function requires at least 1 argument");
     377           0 :         return false;
     378             :     }
     379             : 
     380           0 :     RootedObject optionsObj(cx);
     381           0 :     bool calledWithOptions = args.length() > 1;
     382           0 :     if (calledWithOptions) {
     383           0 :         if (!args[1].isObject()) {
     384           0 :             JS_ReportErrorASCII(cx, "Expected the 2nd argument (options) to be an object");
     385           0 :             return false;
     386             :         }
     387           0 :         optionsObj = &args[1].toObject();
     388             :     }
     389             : 
     390           0 :     CreateObjectInOptions options(cx, optionsObj);
     391           0 :     if (calledWithOptions && !options.Parse())
     392           0 :         return false;
     393             : 
     394           0 :     return xpc::CreateObjectIn(cx, args[0], options, args.rval());
     395             : }
     396             : 
     397             : static bool
     398           0 : SandboxCloneInto(JSContext* cx, unsigned argc, Value* vp)
     399             : {
     400           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     401           0 :     if (args.length() < 2) {
     402           0 :         JS_ReportErrorASCII(cx, "Function requires at least 2 arguments");
     403           0 :         return false;
     404             :     }
     405             : 
     406           0 :     RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
     407           0 :     return xpc::CloneInto(cx, args[0], args[1], options, args.rval());
     408             : }
     409             : 
     410             : static void
     411           0 : sandbox_finalize(js::FreeOp* fop, JSObject* obj)
     412             : {
     413             :     nsIScriptObjectPrincipal* sop =
     414           0 :         static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
     415           0 :     if (!sop) {
     416             :         // sop can be null if CreateSandboxObject fails in the middle.
     417           0 :         return;
     418             :     }
     419             : 
     420           0 :     static_cast<SandboxPrivate*>(sop)->ForgetGlobalObject(obj);
     421           0 :     DestroyProtoAndIfaceCache(obj);
     422           0 :     DeferredFinalize(sop);
     423             : }
     424             : 
     425             : static void
     426           0 : sandbox_moved(JSObject* obj, const JSObject* old)
     427             : {
     428             :     // Note that this hook can be called before the private pointer is set. In
     429             :     // this case the SandboxPrivate will not exist yet, so there is nothing to
     430             :     // do.
     431             :     nsIScriptObjectPrincipal* sop =
     432           0 :         static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
     433           0 :     if (sop)
     434           0 :         static_cast<SandboxPrivate*>(sop)->ObjectMoved(obj, old);
     435           0 : }
     436             : 
     437             : static bool
     438           0 : writeToProto_setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
     439             :                          JS::MutableHandleValue vp, JS::ObjectOpResult& result)
     440             : {
     441           0 :     RootedObject proto(cx);
     442           0 :     if (!JS_GetPrototype(cx, obj, &proto))
     443           0 :         return false;
     444             : 
     445           0 :     RootedValue receiver(cx, ObjectValue(*proto));
     446           0 :     return JS_ForwardSetPropertyTo(cx, proto, id, vp, receiver, result);
     447             : }
     448             : 
     449             : static bool
     450           0 : writeToProto_getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
     451             :                     JS::MutableHandleValue vp)
     452             : {
     453           0 :     RootedObject proto(cx);
     454           0 :     if (!JS_GetPrototype(cx, obj, &proto))
     455           0 :         return false;
     456             : 
     457           0 :     return JS_GetPropertyById(cx, proto, id, vp);
     458             : }
     459             : 
     460             : struct AutoSkipPropertyMirroring
     461             : {
     462          50 :     explicit AutoSkipPropertyMirroring(CompartmentPrivate* priv) : priv(priv) {
     463          50 :         MOZ_ASSERT(!priv->skipWriteToGlobalPrototype);
     464          50 :         priv->skipWriteToGlobalPrototype = true;
     465          50 :     }
     466         100 :     ~AutoSkipPropertyMirroring() {
     467          50 :         MOZ_ASSERT(priv->skipWriteToGlobalPrototype);
     468          50 :         priv->skipWriteToGlobalPrototype = false;
     469          50 :     }
     470             : 
     471             :   private:
     472             :     CompartmentPrivate* priv;
     473             : };
     474             : 
     475             : // This hook handles the case when writeToGlobalPrototype is set on the
     476             : // sandbox. This flag asks that any properties defined on the sandbox global
     477             : // also be defined on the sandbox global's prototype. Whenever one of these
     478             : // properties is changed (on either side), the change should be reflected on
     479             : // both sides. We use this functionality to create sandboxes that are
     480             : // essentially "sub-globals" of another global. This is useful for running
     481             : // add-ons in a separate compartment while still giving them access to the
     482             : // chrome window.
     483             : static bool
     484           0 : sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
     485             : {
     486           0 :     CompartmentPrivate* priv = CompartmentPrivate::Get(obj);
     487           0 :     MOZ_ASSERT(priv->writeToGlobalPrototype);
     488             : 
     489             :     // Whenever JS_EnumerateStandardClasses is called, it defines the
     490             :     // "undefined" property, even if it's already defined. We don't want to do
     491             :     // anything in that case.
     492           0 :     if (id == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_UNDEFINED))
     493           0 :         return true;
     494             : 
     495             :     // Avoid recursively triggering sandbox_addProperty in the
     496             :     // JS_DefinePropertyById call below.
     497           0 :     if (priv->skipWriteToGlobalPrototype)
     498           0 :         return true;
     499             : 
     500           0 :     AutoSkipPropertyMirroring askip(priv);
     501             : 
     502           0 :     RootedObject proto(cx);
     503           0 :     if (!JS_GetPrototype(cx, obj, &proto))
     504           0 :         return false;
     505             : 
     506             :     // After bug 1015790 is fixed, we should be able to remove this unwrapping.
     507           0 :     RootedObject unwrappedProto(cx, js::UncheckedUnwrap(proto, /* stopAtWindowProxy = */ false));
     508             : 
     509           0 :     Rooted<JS::PropertyDescriptor> pd(cx);
     510           0 :     if (!JS_GetPropertyDescriptorById(cx, proto, id, &pd))
     511           0 :         return false;
     512             : 
     513             :     // This is a little icky. If the property exists and is not configurable,
     514             :     // then JS_CopyPropertyFrom will throw an exception when we try to do a
     515             :     // normal assignment since it will think we're trying to remove the
     516             :     // non-configurability. So we do JS_SetPropertyById in that case.
     517             :     //
     518             :     // However, in the case of |const x = 3|, we get called once for
     519             :     // JSOP_DEFCONST and once for JSOP_SETCONST. The first one creates the
     520             :     // property as readonly and configurable. The second one changes the
     521             :     // attributes to readonly and not configurable. If we use JS_SetPropertyById
     522             :     // for the second call, it will throw an exception because the property is
     523             :     // readonly. We have to use JS_CopyPropertyFrom since it ignores the
     524             :     // readonly attribute (as it calls JSObject::defineProperty). See bug
     525             :     // 1019181.
     526           0 :     if (pd.object() && !pd.configurable()) {
     527           0 :         if (!JS_SetPropertyById(cx, proto, id, v))
     528           0 :             return false;
     529             :     } else {
     530           0 :         if (!JS_CopyPropertyFrom(cx, id, unwrappedProto, obj,
     531             :                                  MakeNonConfigurableIntoConfigurable))
     532           0 :             return false;
     533             :     }
     534             : 
     535           0 :     if (!JS_GetPropertyDescriptorById(cx, obj, id, &pd))
     536           0 :         return false;
     537           0 :     unsigned attrs = pd.attributes() & ~(JSPROP_GETTER | JSPROP_SETTER);
     538           0 :     if (!JS_DefinePropertyById(cx, obj, id, v,
     539             :                                attrs | JSPROP_PROPOP_ACCESSORS | JSPROP_REDEFINE_NONCONFIGURABLE,
     540             :                                JS_PROPERTYOP_GETTER(writeToProto_getProperty),
     541             :                                JS_PROPERTYOP_SETTER(writeToProto_setProperty)))
     542           0 :         return false;
     543             : 
     544           0 :     return true;
     545             : }
     546             : 
     547             : #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
     548             : 
     549             : static const js::ClassOps SandboxClassOps = {
     550             :     nullptr, nullptr, nullptr, nullptr,
     551             :     nullptr, JS_NewEnumerateStandardClasses, JS_ResolveStandardClass,
     552             :     JS_MayResolveStandardClass,
     553             :     sandbox_finalize,
     554             :     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
     555             : };
     556             : 
     557             : static const js::ClassExtension SandboxClassExtension = {
     558             :     nullptr,      /* weakmapKeyDelegateOp */
     559             :     sandbox_moved /* objectMovedOp */
     560             : };
     561             : 
     562             : static const js::Class SandboxClass = {
     563             :     "Sandbox",
     564             :     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1) |
     565             :     JSCLASS_FOREGROUND_FINALIZE,
     566             :     &SandboxClassOps,
     567             :     JS_NULL_CLASS_SPEC,
     568             :     &SandboxClassExtension,
     569             :     JS_NULL_OBJECT_OPS
     570             : };
     571             : 
     572             : // Note to whomever comes here to remove addProperty hooks: billm has promised
     573             : // to do the work for this class.
     574             : static const js::ClassOps SandboxWriteToProtoClassOps = {
     575             :     sandbox_addProperty, nullptr, nullptr, nullptr,
     576             :     nullptr, JS_NewEnumerateStandardClasses, JS_ResolveStandardClass,
     577             :     JS_MayResolveStandardClass,
     578             :     sandbox_finalize,
     579             :     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
     580             : };
     581             : 
     582             : static const js::Class SandboxWriteToProtoClass = {
     583             :     "Sandbox",
     584             :     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1) |
     585             :     JSCLASS_FOREGROUND_FINALIZE,
     586             :     &SandboxWriteToProtoClassOps,
     587             :     JS_NULL_CLASS_SPEC,
     588             :     &SandboxClassExtension,
     589             :     JS_NULL_OBJECT_OPS
     590             : };
     591             : 
     592             : static const JSFunctionSpec SandboxFunctions[] = {
     593             :     JS_FS("dump",    SandboxDump,    1,0),
     594             :     JS_FS("debug",   SandboxDebug,   1,0),
     595             :     JS_FS("importFunction", SandboxImport, 1,0),
     596             :     JS_FS_END
     597             : };
     598             : 
     599             : bool
     600        1471 : xpc::IsSandbox(JSObject* obj)
     601             : {
     602        1471 :     const js::Class* clasp = js::GetObjectClass(obj);
     603        1471 :     return clasp == &SandboxClass || clasp == &SandboxWriteToProtoClass;
     604             : }
     605             : 
     606             : /***************************************************************************/
     607           2 : nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox()
     608             : {
     609           2 : }
     610             : 
     611           0 : nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox()
     612             : {
     613           0 : }
     614             : 
     615          89 : NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox)
     616          89 :   NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox)
     617          82 :   NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
     618          57 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox)
     619          41 : NS_INTERFACE_MAP_END
     620             : 
     621          88 : NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox)
     622          77 : NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox)
     623             : 
     624             : // We use the nsIXPScriptable macros to generate lots of stuff for us.
     625             : #define XPC_MAP_CLASSNAME         nsXPCComponents_utils_Sandbox
     626             : #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox"
     627             : #define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CALL | \
     628             :                        XPC_SCRIPTABLE_WANT_CONSTRUCT)
     629             : #include "xpc_map_end.h" /* This #undef's the above. */
     630             : 
     631             : const xpc::SandboxProxyHandler xpc::sandboxProxyHandler;
     632             : 
     633             : bool
     634           0 : xpc::IsSandboxPrototypeProxy(JSObject* obj)
     635             : {
     636           0 :     return js::IsProxy(obj) &&
     637           0 :            js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler;
     638             : }
     639             : 
     640             : bool
     641           0 : xpc::SandboxCallableProxyHandler::call(JSContext* cx, JS::Handle<JSObject*> proxy,
     642             :                                        const JS::CallArgs& args) const
     643             : {
     644             :     // We forward the call to our underlying callable.
     645             : 
     646             :     // Get our SandboxProxyHandler proxy.
     647           0 :     RootedObject sandboxProxy(cx, getSandboxProxy(proxy));
     648           0 :     MOZ_ASSERT(js::IsProxy(sandboxProxy) &&
     649             :                js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler);
     650             : 
     651             :     // The global of the sandboxProxy is the sandbox global, and the
     652             :     // target object is the original proto.
     653             :     RootedObject sandboxGlobal(cx,
     654           0 :       js::GetGlobalForObjectCrossCompartment(sandboxProxy));
     655           0 :     MOZ_ASSERT(IsSandbox(sandboxGlobal));
     656             : 
     657             :     // If our this object is the sandbox global, we call with this set to the
     658             :     // original proto instead.
     659             :     //
     660             :     // There are two different ways we can compute |this|. If we use
     661             :     // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the
     662             :     // caller, which may be undefined if a global function was invoked without
     663             :     // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this|
     664             :     // in |vp| will be coerced to the global, which is not the correct
     665             :     // behavior in ES5 strict mode. And we have no way to compute strictness
     666             :     // here.
     667             :     //
     668             :     // The naive approach is simply to use JS_THIS_VALUE here. If |this| was
     669             :     // explicit, we can remap it appropriately. If it was implicit, then we
     670             :     // leave it as undefined, and let the callee sort it out. Since the callee
     671             :     // is generally in the same compartment as its global (eg the Window's
     672             :     // compartment, not the Sandbox's), the callee will generally compute the
     673             :     // correct |this|.
     674             :     //
     675             :     // However, this breaks down in the Xray case. If the sandboxPrototype
     676             :     // is an Xray wrapper, then we'll end up reifying the native methods in
     677             :     // the Sandbox's scope, which means that they'll compute |this| to be the
     678             :     // Sandbox, breaking old-style XPC_WN_CallMethod methods.
     679             :     //
     680             :     // Luckily, the intent of Xrays is to provide a vanilla view of a foreign
     681             :     // DOM interface, which means that we don't care about script-enacted
     682             :     // strictness in the prototype's home compartment. Indeed, since DOM
     683             :     // methods are always non-strict, we can just assume non-strict semantics
     684             :     // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately
     685             :     // remap |this|.
     686           0 :     bool isXray = WrapperFactory::IsXrayWrapper(sandboxProxy);
     687           0 :     RootedValue thisVal(cx, isXray ? args.computeThis(cx) : args.thisv());
     688           0 :     if (thisVal == ObjectValue(*sandboxGlobal)) {
     689           0 :         thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy));
     690             :     }
     691             : 
     692           0 :     RootedValue func(cx, js::GetProxyPrivate(proxy));
     693           0 :     return JS::Call(cx, thisVal, func, args, args.rval());
     694             : }
     695             : 
     696             : const xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler;
     697             : 
     698             : /*
     699             :  * Wrap a callable such that if we're called with oldThisObj as the
     700             :  * "this" we will instead call it with newThisObj as the this.
     701             :  */
     702             : static JSObject*
     703           0 : WrapCallable(JSContext* cx, HandleObject callable, HandleObject sandboxProtoProxy)
     704             : {
     705           0 :     MOZ_ASSERT(JS::IsCallable(callable));
     706             :     // Our proxy is wrapping the callable.  So we need to use the
     707             :     // callable as the private.  We put the given sandboxProtoProxy in
     708             :     // an extra slot, and our call() hook depends on that.
     709           0 :     MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) &&
     710             :                js::GetProxyHandler(sandboxProtoProxy) ==
     711             :                  &xpc::sandboxProxyHandler);
     712             : 
     713           0 :     RootedValue priv(cx, ObjectValue(*callable));
     714             :     // We want to claim to have the same proto as our wrapped callable, so set
     715             :     // ourselves up with a lazy proto.
     716           0 :     js::ProxyOptions options;
     717           0 :     options.setLazyProto(true);
     718           0 :     JSObject* obj = js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler,
     719           0 :                                        priv, nullptr, options);
     720           0 :     if (obj) {
     721             :         js::SetProxyReservedSlot(obj, SandboxCallableProxyHandler::SandboxProxySlot,
     722           0 :                                  ObjectValue(*sandboxProtoProxy));
     723             :     }
     724             : 
     725           0 :     return obj;
     726             : }
     727             : 
     728             : template<typename Op>
     729           0 : bool WrapAccessorFunction(JSContext* cx, Op& op, PropertyDescriptor* desc,
     730             :                           unsigned attrFlag, HandleObject sandboxProtoProxy)
     731             : {
     732           0 :     if (!op) {
     733           0 :         return true;
     734             :     }
     735             : 
     736           0 :     if (!(desc->attrs & attrFlag)) {
     737           0 :         XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
     738           0 :         return false;
     739             :     }
     740             : 
     741           0 :     RootedObject func(cx, JS_FUNC_TO_DATA_PTR(JSObject*, op));
     742           0 :     func = WrapCallable(cx, func, sandboxProtoProxy);
     743           0 :     if (!func)
     744           0 :         return false;
     745           0 :     op = JS_DATA_TO_FUNC_PTR(Op, func.get());
     746           0 :     return true;
     747             : }
     748             : 
     749             : bool
     750           0 : xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext* cx,
     751             :                                                 JS::Handle<JSObject*> proxy,
     752             :                                                 JS::Handle<jsid> id,
     753             :                                                 JS::MutableHandle<PropertyDescriptor> desc) const
     754             : {
     755           0 :     JS::RootedObject obj(cx, wrappedObject(proxy));
     756             : 
     757           0 :     MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy));
     758           0 :     if (!JS_GetPropertyDescriptorById(cx, obj, id, desc))
     759           0 :         return false;
     760             : 
     761           0 :     if (!desc.object())
     762           0 :         return true; // No property, nothing to do
     763             : 
     764             :     // Now fix up the getter/setter/value as needed to be bound to desc->obj.
     765           0 :     if (!WrapAccessorFunction(cx, desc.getter(), desc.address(),
     766             :                               JSPROP_GETTER, proxy))
     767           0 :         return false;
     768           0 :     if (!WrapAccessorFunction(cx, desc.setter(), desc.address(),
     769             :                               JSPROP_SETTER, proxy))
     770           0 :         return false;
     771           0 :     if (desc.value().isObject()) {
     772           0 :         RootedObject val (cx, &desc.value().toObject());
     773           0 :         if (JS::IsCallable(val)) {
     774           0 :             val = WrapCallable(cx, val, proxy);
     775           0 :             if (!val)
     776           0 :                 return false;
     777           0 :             desc.value().setObject(*val);
     778             :         }
     779             :     }
     780             : 
     781           0 :     return true;
     782             : }
     783             : 
     784             : bool
     785           0 : xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext* cx,
     786             :                                                    JS::Handle<JSObject*> proxy,
     787             :                                                    JS::Handle<jsid> id,
     788             :                                                    JS::MutableHandle<PropertyDescriptor> desc)
     789             :                                                    const
     790             : {
     791           0 :     if (!getPropertyDescriptor(cx, proxy, id, desc))
     792           0 :         return false;
     793             : 
     794           0 :     if (desc.object() != wrappedObject(proxy))
     795           0 :         desc.object().set(nullptr);
     796             : 
     797           0 :     return true;
     798             : }
     799             : 
     800             : /*
     801             :  * Reuse the BaseProxyHandler versions of the derived traps that are implemented
     802             :  * in terms of the fundamental traps.
     803             :  */
     804             : 
     805             : bool
     806           0 : xpc::SandboxProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy,
     807             :                               JS::Handle<jsid> id, bool* bp) const
     808             : {
     809             :     // This uses getPropertyDescriptor for backward compatibility with
     810             :     // the old BaseProxyHandler::has implementation.
     811           0 :     Rooted<PropertyDescriptor> desc(cx);
     812           0 :     if (!getPropertyDescriptor(cx, proxy, id, &desc))
     813           0 :         return false;
     814             : 
     815           0 :     *bp = !!desc.object();
     816           0 :     return true;
     817             : }
     818             : bool
     819           0 : xpc::SandboxProxyHandler::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
     820             :                                  JS::Handle<jsid> id, bool* bp) const
     821             : {
     822           0 :     return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
     823             : }
     824             : 
     825             : bool
     826           0 : xpc::SandboxProxyHandler::get(JSContext* cx, JS::Handle<JSObject*> proxy,
     827             :                               JS::Handle<JS::Value> receiver,
     828             :                               JS::Handle<jsid> id,
     829             :                               JS::MutableHandle<Value> vp) const
     830             : {
     831             :     // This uses getPropertyDescriptor for backward compatibility with
     832             :     // the old BaseProxyHandler::get implementation.
     833           0 :     Rooted<PropertyDescriptor> desc(cx);
     834           0 :     if (!getPropertyDescriptor(cx, proxy, id, &desc))
     835           0 :         return false;
     836           0 :     desc.assertCompleteIfFound();
     837             : 
     838           0 :     if (!desc.object()) {
     839           0 :         vp.setUndefined();
     840           0 :         return true;
     841             :     }
     842             : 
     843             :     // Everything after here follows [[Get]] for ordinary objects.
     844           0 :     if (desc.isDataDescriptor()) {
     845           0 :         vp.set(desc.value());
     846           0 :         return true;
     847             :     }
     848             : 
     849           0 :     MOZ_ASSERT(desc.isAccessorDescriptor());
     850           0 :     RootedObject getter(cx, desc.getterObject());
     851             : 
     852           0 :     if (!getter) {
     853           0 :         vp.setUndefined();
     854           0 :         return true;
     855             :     }
     856             : 
     857           0 :     return Call(cx, receiver, getter, HandleValueArray::empty(), vp);
     858             : }
     859             : 
     860             : bool
     861           0 : xpc::SandboxProxyHandler::set(JSContext* cx, JS::Handle<JSObject*> proxy,
     862             :                               JS::Handle<jsid> id,
     863             :                               JS::Handle<Value> v,
     864             :                               JS::Handle<Value> receiver,
     865             :                               JS::ObjectOpResult& result) const
     866             : {
     867           0 :     return BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
     868             : }
     869             : 
     870             : bool
     871           0 : xpc::SandboxProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx,
     872             :                                                        JS::Handle<JSObject*> proxy,
     873             :                                                        AutoIdVector& props) const
     874             : {
     875           0 :     return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
     876             : }
     877             : 
     878             : JSObject*
     879           0 : xpc::SandboxProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy) const
     880             : {
     881           0 :     return BaseProxyHandler::enumerate(cx, proxy);
     882             : }
     883             : 
     884             : bool
     885          36 : xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj)
     886             : {
     887             :     uint32_t length;
     888          36 :     bool ok = JS_GetArrayLength(cx, obj, &length);
     889          36 :     NS_ENSURE_TRUE(ok, false);
     890          71 :     for (uint32_t i = 0; i < length; i++) {
     891          70 :         RootedValue nameValue(cx);
     892          35 :         ok = JS_GetElement(cx, obj, i, &nameValue);
     893          35 :         NS_ENSURE_TRUE(ok, false);
     894          35 :         if (!nameValue.isString()) {
     895           0 :             JS_ReportErrorASCII(cx, "Property names must be strings");
     896           0 :             return false;
     897             :         }
     898          70 :         RootedString nameStr(cx, nameValue.toString());
     899          70 :         JSAutoByteString name;
     900          35 :         if (!name.encodeUtf8(cx, nameStr))
     901           0 :             return false;
     902          35 :         if (!strcmp(name.ptr(), "CSS")) {
     903           0 :             CSS = true;
     904          35 :         } else if (!strcmp(name.ptr(), "indexedDB")) {
     905          14 :             indexedDB = true;
     906          21 :         } else if (!strcmp(name.ptr(), "XMLHttpRequest")) {
     907           4 :             XMLHttpRequest = true;
     908          17 :         } else if (!strcmp(name.ptr(), "TextEncoder")) {
     909           0 :             TextEncoder = true;
     910          17 :         } else if (!strcmp(name.ptr(), "TextDecoder")) {
     911           0 :             TextDecoder = true;
     912          17 :         } else if (!strcmp(name.ptr(), "URL")) {
     913           9 :             URL = true;
     914           8 :         } else if (!strcmp(name.ptr(), "URLSearchParams")) {
     915           2 :             URLSearchParams = true;
     916           6 :         } else if (!strcmp(name.ptr(), "atob")) {
     917           0 :             atob = true;
     918           6 :         } else if (!strcmp(name.ptr(), "btoa")) {
     919           1 :             btoa = true;
     920           5 :         } else if (!strcmp(name.ptr(), "Blob")) {
     921           0 :             Blob = true;
     922           5 :         } else if (!strcmp(name.ptr(), "Directory")) {
     923           0 :             Directory = true;
     924           5 :         } else if (!strcmp(name.ptr(), "File")) {
     925           1 :             File = true;
     926           4 :         } else if (!strcmp(name.ptr(), "crypto")) {
     927           0 :             crypto = true;
     928             : #ifdef MOZ_WEBRTC
     929           4 :         } else if (!strcmp(name.ptr(), "rtcIdentityProvider")) {
     930           0 :             rtcIdentityProvider = true;
     931             : #endif
     932           4 :         } else if (!strcmp(name.ptr(), "fetch")) {
     933           3 :             fetch = true;
     934           1 :         } else if (!strcmp(name.ptr(), "caches")) {
     935           0 :             caches = true;
     936           1 :         } else if (!strcmp(name.ptr(), "FileReader")) {
     937           1 :             fileReader = true;
     938           0 :         } else if (!strcmp(name.ptr(), "MessageChannel")) {
     939           0 :             messageChannel = true;
     940             :         } else {
     941           0 :             JS_ReportErrorUTF8(cx, "Unknown property name: %s", name.ptr());
     942           0 :             return false;
     943             :         }
     944             :     }
     945          36 :     return true;
     946             : }
     947             : 
     948             : bool
     949          45 : xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj)
     950             : {
     951          45 :     MOZ_ASSERT(js::GetContextCompartment(cx) == js::GetObjectCompartment(obj));
     952             :     // Properties will be exposed to System automatically but not to Sandboxes
     953             :     // if |[Exposed=System]| is specified.
     954             :     // This function holds common properties not exposed automatically but able
     955             :     // to be requested either in |Cu.importGlobalProperties| or
     956             :     // |wantGlobalProperties| of a sandbox.
     957          45 :     if (CSS && !dom::CSSBinding::GetConstructorObject(cx))
     958           0 :         return false;
     959             : 
     960          49 :     if (XMLHttpRequest &&
     961           4 :         !dom::XMLHttpRequestBinding::GetConstructorObject(cx))
     962           0 :         return false;
     963             : 
     964          45 :     if (TextEncoder &&
     965           0 :         !dom::TextEncoderBinding::GetConstructorObject(cx))
     966           0 :         return false;
     967             : 
     968          45 :     if (TextDecoder &&
     969           0 :         !dom::TextDecoderBinding::GetConstructorObject(cx))
     970           0 :         return false;
     971             : 
     972          54 :     if (URL &&
     973           9 :         !dom::URLBinding::GetConstructorObject(cx))
     974           0 :         return false;
     975             : 
     976          47 :     if (URLSearchParams &&
     977           2 :         !dom::URLSearchParamsBinding::GetConstructorObject(cx))
     978           0 :         return false;
     979             : 
     980          45 :     if (atob &&
     981           0 :         !JS_DefineFunction(cx, obj, "atob", Atob, 1, 0))
     982           0 :         return false;
     983             : 
     984          46 :     if (btoa &&
     985           1 :         !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0))
     986           0 :         return false;
     987             : 
     988          45 :     if (Blob &&
     989           0 :         !dom::BlobBinding::GetConstructorObject(cx))
     990           0 :         return false;
     991             : 
     992          45 :     if (Directory &&
     993           0 :         !dom::DirectoryBinding::GetConstructorObject(cx))
     994           0 :         return false;
     995             : 
     996          46 :     if (File &&
     997           1 :         !dom::FileBinding::GetConstructorObject(cx))
     998           0 :         return false;
     999             : 
    1000          45 :     if (crypto && !SandboxCreateCrypto(cx, obj))
    1001           0 :         return false;
    1002             : 
    1003             : #ifdef MOZ_WEBRTC
    1004          45 :     if (rtcIdentityProvider && !SandboxCreateRTCIdentityProvider(cx, obj))
    1005           0 :         return false;
    1006             : #endif
    1007             : 
    1008          45 :     if (fetch && !SandboxCreateFetch(cx, obj))
    1009           0 :         return false;
    1010             : 
    1011          45 :     if (caches && !dom::cache::CacheStorage::DefineCaches(cx, obj))
    1012           0 :         return false;
    1013             : 
    1014          45 :     if (fileReader && !dom::FileReaderBinding::GetConstructorObject(cx))
    1015           0 :         return false;
    1016             : 
    1017          45 :     if (messageChannel &&
    1018           0 :         (!dom::MessageChannelBinding::GetConstructorObject(cx) ||
    1019           0 :          !dom::MessagePortBinding::GetConstructorObject(cx)))
    1020           0 :         return false;
    1021             : 
    1022          45 :     return true;
    1023             : }
    1024             : 
    1025             : bool
    1026          20 : xpc::GlobalProperties::DefineInXPCComponents(JSContext* cx, JS::HandleObject obj)
    1027             : {
    1028          20 :     if (indexedDB &&
    1029           0 :         !IndexedDatabaseManager::DefineIndexedDB(cx, obj))
    1030           0 :         return false;
    1031             : 
    1032          20 :     return Define(cx, obj);
    1033             : }
    1034             : 
    1035             : bool
    1036          25 : xpc::GlobalProperties::DefineInSandbox(JSContext* cx, JS::HandleObject obj)
    1037             : {
    1038          25 :     MOZ_ASSERT(IsSandbox(obj));
    1039          25 :     MOZ_ASSERT(js::GetContextCompartment(cx) == js::GetObjectCompartment(obj));
    1040             : 
    1041          39 :     if (indexedDB &&
    1042          28 :         !(IndexedDatabaseManager::ResolveSandboxBinding(cx) &&
    1043          14 :           IndexedDatabaseManager::DefineIndexedDB(cx, obj)))
    1044           0 :         return false;
    1045             : 
    1046          25 :     return Define(cx, obj);
    1047             : }
    1048             : 
    1049             : nsresult
    1050          25 : xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prinOrSop,
    1051             :                          SandboxOptions& options)
    1052             : {
    1053             :     // Create the sandbox global object
    1054          50 :     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
    1055          25 :     if (!principal) {
    1056          12 :         nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(prinOrSop);
    1057           6 :         if (sop) {
    1058           0 :             principal = sop->GetPrincipal();
    1059             :         } else {
    1060          12 :             RefPtr<NullPrincipal> nullPrin = NullPrincipal::Create();
    1061           6 :             principal = nullPrin;
    1062             :         }
    1063             :     }
    1064          25 :     MOZ_ASSERT(principal);
    1065             : 
    1066          25 :     JS::CompartmentOptions compartmentOptions;
    1067             : 
    1068          25 :     auto& creationOptions = compartmentOptions.creationOptions();
    1069             : 
    1070             :     // XXXjwatt: Consider whether/when sandboxes should be able to see
    1071             :     // [SecureContext] API (bug 1273687).  In that case we'd call
    1072             :     // creationOptions.setSecureContext(true).
    1073             : 
    1074          25 :     if (xpc::SharedMemoryEnabled())
    1075          25 :         creationOptions.setSharedMemoryAndAtomicsEnabled(true);
    1076             : 
    1077          25 :     if (options.sameZoneAs)
    1078           0 :         creationOptions.setExistingZone(js::UncheckedUnwrap(options.sameZoneAs));
    1079          25 :     else if (options.freshZone)
    1080           0 :         creationOptions.setNewZoneInSystemZoneGroup();
    1081             :     else
    1082          25 :         creationOptions.setSystemZone();
    1083             : 
    1084          25 :     creationOptions.setInvisibleToDebugger(options.invisibleToDebugger)
    1085          25 :                    .setTrace(TraceXPCGlobal);
    1086             : 
    1087             :     // Try to figure out any addon this sandbox should be associated with.
    1088             :     // The addon could have been passed in directly, as part of the metadata,
    1089             :     // or by being constructed from an addon's code.
    1090          25 :     JSAddonId* addonId = nullptr;
    1091          25 :     if (options.addonId) {
    1092          14 :         addonId = JS::NewAddonId(cx, options.addonId);
    1093          14 :         NS_ENSURE_TRUE(addonId, NS_ERROR_FAILURE);
    1094          11 :     } else if (JSObject* obj = JS::CurrentGlobalOrNull(cx)) {
    1095           2 :         if (JSAddonId* id = JS::AddonIdOfObject(obj))
    1096           0 :             addonId = id;
    1097             :     }
    1098             : 
    1099          25 :     creationOptions.setAddonId(addonId);
    1100             : 
    1101          25 :     compartmentOptions.behaviors().setDiscardSource(options.discardSource);
    1102             : 
    1103          25 :     const js::Class* clasp = options.writeToGlobalPrototype
    1104          25 :                              ? &SandboxWriteToProtoClass
    1105          25 :                              : &SandboxClass;
    1106             : 
    1107          50 :     RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp),
    1108          50 :                                                      principal, compartmentOptions));
    1109          25 :     if (!sandbox)
    1110           0 :         return NS_ERROR_FAILURE;
    1111             : 
    1112          25 :     CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox);
    1113          25 :     priv->allowWaivers = options.allowWaivers;
    1114          25 :     priv->writeToGlobalPrototype = options.writeToGlobalPrototype;
    1115          25 :     priv->isWebExtensionContentScript = options.isWebExtensionContentScript;
    1116          25 :     priv->waiveInterposition = options.waiveInterposition;
    1117             : 
    1118             :     // Set up the wantXrays flag, which indicates whether xrays are desired even
    1119             :     // for same-origin access.
    1120             :     //
    1121             :     // This flag has historically been ignored for chrome sandboxes due to
    1122             :     // quirks in the wrapping implementation that have now been removed. Indeed,
    1123             :     // same-origin Xrays for chrome->chrome access seems a bit superfluous.
    1124             :     // Arguably we should just flip the default for chrome and still honor the
    1125             :     // flag, but such a change would break code in subtle ways for minimal
    1126             :     // benefit. So we just switch it off here.
    1127          25 :     priv->wantXrays =
    1128          25 :       AccessCheck::isChrome(sandbox) ? false : options.wantXrays;
    1129             : 
    1130             :     {
    1131          50 :         JSAutoCompartment ac(cx, sandbox);
    1132             : 
    1133             :         nsCOMPtr<nsIScriptObjectPrincipal> sbp =
    1134          75 :             new SandboxPrivate(principal, sandbox);
    1135             : 
    1136             :         // Pass on ownership of sbp to |sandbox|.
    1137          25 :         JS_SetPrivate(sandbox, sbp.forget().take());
    1138             : 
    1139             :         {
    1140             :             // Don't try to mirror standard class properties, if we're using a
    1141             :             // mirroring sandbox.  (This is meaningless for non-mirroring
    1142             :             // sandboxes.)
    1143          50 :             AutoSkipPropertyMirroring askip(CompartmentPrivate::Get(sandbox));
    1144             : 
    1145             :             // Ensure |Object.prototype| is instantiated before prototype-
    1146             :             // splicing below.  For write-to-global-prototype behavior, extend
    1147             :             // this to all builtin properties.
    1148          25 :             if (options.writeToGlobalPrototype) {
    1149           0 :                 if (!JS_EnumerateStandardClasses(cx, sandbox))
    1150           0 :                     return NS_ERROR_XPC_UNEXPECTED;
    1151             :             } else {
    1152          25 :                 if (!JS_GetObjectPrototype(cx, sandbox))
    1153           0 :                     return NS_ERROR_XPC_UNEXPECTED;
    1154             :             }
    1155             :         }
    1156             : 
    1157          25 :         if (options.proto) {
    1158           1 :             bool ok = JS_WrapObject(cx, &options.proto);
    1159           1 :             if (!ok)
    1160           0 :                 return NS_ERROR_XPC_UNEXPECTED;
    1161             : 
    1162             :             // Now check what sort of thing we've got in |proto|, and figure out
    1163             :             // if we need a SandboxProxyHandler.
    1164             :             //
    1165             :             // Note that, in the case of a window, we can't require that the
    1166             :             // Sandbox subsumes the prototype, because we have to hold our
    1167             :             // reference to it via an outer window, and the window may navigate
    1168             :             // at any time. So we have to handle that case separately.
    1169           1 :             bool useSandboxProxy = !!WindowOrNull(js::UncheckedUnwrap(options.proto, false));
    1170           1 :             if (!useSandboxProxy) {
    1171           1 :                 JSObject* unwrappedProto = js::CheckedUnwrap(options.proto, false);
    1172           1 :                 if (!unwrappedProto) {
    1173           0 :                     JS_ReportErrorASCII(cx, "Sandbox must subsume sandboxPrototype");
    1174           0 :                     return NS_ERROR_INVALID_ARG;
    1175             :                 }
    1176           1 :                 const js::Class* unwrappedClass = js::GetObjectClass(unwrappedProto);
    1177           2 :                 useSandboxProxy = IS_WN_CLASS(unwrappedClass) ||
    1178           1 :                                   mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass));
    1179             :             }
    1180             : 
    1181           1 :             if (useSandboxProxy) {
    1182             :                 // Wrap it up in a proxy that will do the right thing in terms
    1183             :                 // of this-binding for methods.
    1184           0 :                 RootedValue priv(cx, ObjectValue(*options.proto));
    1185           0 :                 options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler,
    1186           0 :                                                    priv, nullptr);
    1187           0 :                 if (!options.proto)
    1188           0 :                     return NS_ERROR_OUT_OF_MEMORY;
    1189             :             }
    1190             : 
    1191           1 :             ok = JS_SplicePrototype(cx, sandbox, options.proto);
    1192           1 :             if (!ok)
    1193           0 :                 return NS_ERROR_XPC_UNEXPECTED;
    1194             :         }
    1195             : 
    1196             :         // Don't try to mirror the properties that are set below.
    1197          50 :         AutoSkipPropertyMirroring askip(CompartmentPrivate::Get(sandbox));
    1198             : 
    1199          31 :         bool allowComponents = principal == nsXPConnect::SystemPrincipal() ||
    1200          31 :                                nsContentUtils::IsExpandedPrincipal(principal);
    1201          40 :         if (options.wantComponents && allowComponents &&
    1202          15 :             !ObjectScope(sandbox)->AttachComponentsObject(cx))
    1203           0 :             return NS_ERROR_XPC_UNEXPECTED;
    1204             : 
    1205          25 :         if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox))
    1206           0 :             return NS_ERROR_XPC_UNEXPECTED;
    1207             : 
    1208          25 :         if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions))
    1209           0 :             return NS_ERROR_XPC_UNEXPECTED;
    1210             : 
    1211          75 :         if (options.wantExportHelpers &&
    1212          25 :             (!JS_DefineFunction(cx, sandbox, "exportFunction", SandboxExportFunction, 3, 0) ||
    1213          25 :              !JS_DefineFunction(cx, sandbox, "createObjectIn", SandboxCreateObjectIn, 2, 0) ||
    1214          25 :              !JS_DefineFunction(cx, sandbox, "cloneInto", SandboxCloneInto, 3, 0) ||
    1215          25 :              !JS_DefineFunction(cx, sandbox, "isProxy", SandboxIsProxy, 1, 0)))
    1216           0 :             return NS_ERROR_XPC_UNEXPECTED;
    1217             : 
    1218          25 :         if (!options.globalProperties.DefineInSandbox(cx, sandbox))
    1219           0 :             return NS_ERROR_XPC_UNEXPECTED;
    1220             :     }
    1221             : 
    1222             :     // We handle the case where the context isn't in a compartment for the
    1223             :     // benefit of InitSingletonScopes.
    1224          25 :     vp.setObject(*sandbox);
    1225          25 :     if (js::GetContextCompartment(cx) && !JS_WrapValue(cx, vp))
    1226           0 :         return NS_ERROR_UNEXPECTED;
    1227             : 
    1228             :     // Set the location information for the new global, so that tools like
    1229             :     // about:memory may use that information
    1230          25 :     xpc::SetLocationForGlobal(sandbox, options.sandboxName);
    1231             : 
    1232          25 :     xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
    1233             : 
    1234          50 :     JSAutoCompartment ac(cx, sandbox);
    1235          25 :     JS_FireOnNewGlobalObject(cx, sandbox);
    1236             : 
    1237          25 :     return NS_OK;
    1238             : }
    1239             : 
    1240             : NS_IMETHODIMP
    1241           2 : nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
    1242             :                                     JSObject* objArg, const CallArgs& args, bool* _retval)
    1243             : {
    1244           4 :     RootedObject obj(cx, objArg);
    1245           4 :     return CallOrConstruct(wrapper, cx, obj, args, _retval);
    1246             : }
    1247             : 
    1248             : NS_IMETHODIMP
    1249          14 : nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
    1250             :                                          JSObject* objArg, const CallArgs& args, bool* _retval)
    1251             : {
    1252          28 :     RootedObject obj(cx, objArg);
    1253          28 :     return CallOrConstruct(wrapper, cx, obj, args, _retval);
    1254             : }
    1255             : 
    1256             : /*
    1257             :  * For sandbox constructor the first argument can be a URI string in which case
    1258             :  * we use the related Codebase Principal for the sandbox.
    1259             :  */
    1260             : bool
    1261           0 : ParsePrincipal(JSContext* cx, HandleString codebase, const OriginAttributes& aAttrs,
    1262             :                nsIPrincipal** principal)
    1263             : {
    1264           0 :     MOZ_ASSERT(principal);
    1265           0 :     MOZ_ASSERT(codebase);
    1266           0 :     nsCOMPtr<nsIURI> uri;
    1267           0 :     nsAutoJSString codebaseStr;
    1268           0 :     NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), false);
    1269           0 :     nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr);
    1270           0 :     if (NS_FAILED(rv)) {
    1271           0 :         JS_ReportErrorASCII(cx, "Creating URI from string failed");
    1272           0 :         return false;
    1273             :     }
    1274             : 
    1275             :     // We could allow passing in the app-id and browser-element info to the
    1276             :     // sandbox constructor. But creating a sandbox based on a string is a
    1277             :     // deprecated API so no need to add features to it.
    1278             :     nsCOMPtr<nsIPrincipal> prin =
    1279           0 :         BasePrincipal::CreateCodebasePrincipal(uri, aAttrs);
    1280           0 :     prin.forget(principal);
    1281             : 
    1282           0 :     if (!*principal) {
    1283           0 :         JS_ReportErrorASCII(cx, "Creating Principal from URI failed");
    1284           0 :         return false;
    1285             :     }
    1286           0 :     return true;
    1287             : }
    1288             : 
    1289             : /*
    1290             :  * For sandbox constructor the first argument can be a principal object or
    1291             :  * a script object principal (Document, Window).
    1292             :  */
    1293             : static bool
    1294          16 : GetPrincipalOrSOP(JSContext* cx, HandleObject from, nsISupports** out)
    1295             : {
    1296          16 :     MOZ_ASSERT(out);
    1297          16 :     *out = nullptr;
    1298             : 
    1299          32 :     nsCOMPtr<nsISupports> native = xpc::UnwrapReflectorToISupports(from);
    1300             : 
    1301          32 :     if (nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native)) {
    1302           0 :         sop.forget(out);
    1303           0 :         return true;
    1304             :     }
    1305             : 
    1306          32 :     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(native);
    1307          16 :     principal.forget(out);
    1308          16 :     NS_ENSURE_TRUE(*out, false);
    1309             : 
    1310          16 :     return true;
    1311             : }
    1312             : 
    1313             : /*
    1314             :  * The first parameter of the sandbox constructor might be an array of principals, either in string
    1315             :  * format or actual objects (see GetPrincipalOrSOP)
    1316             :  */
    1317             : static bool
    1318           0 : GetExpandedPrincipal(JSContext* cx, HandleObject arrayObj,
    1319             :                      const SandboxOptions& options, nsIExpandedPrincipal** out)
    1320             : {
    1321           0 :     MOZ_ASSERT(out);
    1322             :     uint32_t length;
    1323             : 
    1324           0 :     if (!JS_GetArrayLength(cx, arrayObj, &length))
    1325           0 :         return false;
    1326           0 :     if (!length) {
    1327             :         // We need a whitelist of principals or uri strings to create an
    1328             :         // expanded principal, if we got an empty array or something else
    1329             :         // report error.
    1330           0 :         JS_ReportErrorASCII(cx, "Expected an array of URI strings");
    1331           0 :         return false;
    1332             :     }
    1333             : 
    1334           0 :     nsTArray< nsCOMPtr<nsIPrincipal> > allowedDomains(length);
    1335           0 :     allowedDomains.SetLength(length);
    1336             : 
    1337             :     // If an originAttributes option has been specified, we will use that as the
    1338             :     // OriginAttribute of all of the string arguments passed to this function.
    1339             :     // Otherwise, we will use the OriginAttributes of a principal or SOP object
    1340             :     // in the array, if any.  If no such object is present, and all we have are
    1341             :     // strings, then we will use a default OriginAttribute.
    1342             :     // Otherwise, we will use the origin attributes of the passed object(s). If
    1343             :     // more than one object is specified, we ensure that the OAs match.
    1344           0 :     Maybe<OriginAttributes> attrs;
    1345           0 :     if (options.originAttributes) {
    1346           0 :         attrs.emplace();
    1347           0 :         JS::RootedValue val(cx, JS::ObjectValue(*options.originAttributes));
    1348           0 :         if (!attrs->Init(cx, val)) {
    1349             :             // The originAttributes option, if specified, must be valid!
    1350           0 :             JS_ReportErrorASCII(cx, "Expected a valid OriginAttributes object");
    1351           0 :             return false;
    1352             :         }
    1353             :     }
    1354             : 
    1355             :     // Now we go over the array in two passes.  In the first pass, we ignore
    1356             :     // strings, and only process objects.  Assuming that no originAttributes
    1357             :     // option has been passed, if we encounter a principal or SOP object, we
    1358             :     // grab its OA and save it if it's the first OA encountered, otherwise
    1359             :     // check to make sure that it is the same as the OA found before.
    1360             :     // In the second pass, we ignore objects, and use the OA found in pass 0
    1361             :     // (or the previously computed OA if we have obtained it from the options)
    1362             :     // to construct codebase principals.
    1363             :     //
    1364             :     // The effective OA selected above will also be set as the OA of the
    1365             :     // expanded principal object.
    1366             : 
    1367             :     // First pass:
    1368           0 :     for (uint32_t i = 0; i < length; ++i) {
    1369           0 :         RootedValue allowed(cx);
    1370           0 :         if (!JS_GetElement(cx, arrayObj, i, &allowed))
    1371           0 :             return false;
    1372             : 
    1373             :         nsresult rv;
    1374           0 :         nsCOMPtr<nsIPrincipal> principal;
    1375           0 :         if (allowed.isObject()) {
    1376             :             // In case of object let's see if it's a Principal or a ScriptObjectPrincipal.
    1377           0 :             nsCOMPtr<nsISupports> prinOrSop;
    1378           0 :             RootedObject obj(cx, &allowed.toObject());
    1379           0 :             if (!GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)))
    1380           0 :                 return false;
    1381             : 
    1382           0 :             nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(prinOrSop));
    1383           0 :             principal = do_QueryInterface(prinOrSop);
    1384           0 :             if (sop)
    1385           0 :                 principal = sop->GetPrincipal();
    1386           0 :             NS_ENSURE_TRUE(principal, false);
    1387             : 
    1388           0 :             if (!options.originAttributes) {
    1389             :                 const OriginAttributes prinAttrs =
    1390           0 :                     principal->OriginAttributesRef();
    1391           0 :                 if (attrs.isNothing()) {
    1392           0 :                     attrs.emplace(prinAttrs);
    1393           0 :                 } else if (prinAttrs != attrs.ref()) {
    1394             :                     // If attrs is from a previously encountered principal in the
    1395             :                     // array, we need to ensure that it matches the OA of the
    1396             :                     // principal we have here.
    1397             :                     // If attrs comes from OriginAttributes, we don't need
    1398             :                     // this check.
    1399           0 :                     return false;
    1400             :                 }
    1401             :             }
    1402             : 
    1403             :             // We do not allow ExpandedPrincipals to contain any system principals.
    1404             :             bool isSystem;
    1405           0 :             rv = nsXPConnect::SecurityManager()->IsSystemPrincipal(principal, &isSystem);
    1406           0 :             NS_ENSURE_SUCCESS(rv, false);
    1407           0 :             if (isSystem) {
    1408           0 :                 JS_ReportErrorASCII(cx, "System principal is not allowed in an expanded principal");
    1409           0 :                 return false;
    1410             :             }
    1411           0 :             allowedDomains[i] = principal;
    1412           0 :         } else if (allowed.isString()) {
    1413             :             // Skip any string arguments - we handle them in the next pass.
    1414             :         } else {
    1415             :             // Don't know what this is.
    1416           0 :             return false;
    1417             :         }
    1418             :     }
    1419             : 
    1420           0 :     if (attrs.isNothing()) {
    1421             :         // If no OriginAttributes was found in the first pass, fall back to a default one.
    1422           0 :         attrs.emplace();
    1423             :     }
    1424             : 
    1425             :     // Second pass:
    1426           0 :     for (uint32_t i = 0; i < length; ++i) {
    1427           0 :         RootedValue allowed(cx);
    1428           0 :         if (!JS_GetElement(cx, arrayObj, i, &allowed))
    1429           0 :             return false;
    1430             : 
    1431           0 :         nsCOMPtr<nsIPrincipal> principal;
    1432           0 :         if (allowed.isString()) {
    1433             :             // In case of string let's try to fetch a codebase principal from it.
    1434           0 :             RootedString str(cx, allowed.toString());
    1435             : 
    1436             :             // attrs here is either a default OriginAttributes in case the
    1437             :             // originAttributes option isn't specified, and no object in the array
    1438             :             // provides a principal.  Otherwise it's either the forced principal, or
    1439             :             // the principal found before, so we can use it here.
    1440           0 :             if (!ParsePrincipal(cx, str, attrs.ref(), getter_AddRefs(principal)))
    1441           0 :                 return false;
    1442           0 :             NS_ENSURE_TRUE(principal, false);
    1443           0 :             allowedDomains[i] = principal;
    1444             :         } else {
    1445           0 :             MOZ_ASSERT(allowed.isObject());
    1446             :         }
    1447             :     }
    1448             : 
    1449             :     RefPtr<ExpandedPrincipal> result =
    1450           0 :         ExpandedPrincipal::Create(allowedDomains, attrs.ref());
    1451           0 :     result.forget(out);
    1452           0 :     return true;
    1453             : }
    1454             : 
    1455             : /*
    1456             :  * Helper that tries to get a property from the options object.
    1457             :  */
    1458             : bool
    1459         288 : OptionsBase::ParseValue(const char* name, MutableHandleValue prop, bool* aFound)
    1460             : {
    1461             :     bool found;
    1462         288 :     bool ok = JS_HasProperty(mCx, mObject, name, &found);
    1463         288 :     NS_ENSURE_TRUE(ok, false);
    1464             : 
    1465         288 :     if (aFound)
    1466         272 :         *aFound = found;
    1467             : 
    1468         288 :     if (!found)
    1469         224 :         return true;
    1470             : 
    1471          64 :     return JS_GetProperty(mCx, mObject, name, prop);
    1472             : }
    1473             : 
    1474             : /*
    1475             :  * Helper that tries to get a boolean property from the options object.
    1476             :  */
    1477             : bool
    1478         160 : OptionsBase::ParseBoolean(const char* name, bool* prop)
    1479             : {
    1480         160 :     MOZ_ASSERT(prop);
    1481         320 :     RootedValue value(mCx);
    1482             :     bool found;
    1483         160 :     bool ok = ParseValue(name, &value, &found);
    1484         160 :     NS_ENSURE_TRUE(ok, false);
    1485             : 
    1486         160 :     if (!found)
    1487         157 :         return true;
    1488             : 
    1489           3 :     if (!value.isBoolean()) {
    1490           0 :         JS_ReportErrorASCII(mCx, "Expected a boolean value for property %s", name);
    1491           0 :         return false;
    1492             :     }
    1493             : 
    1494           3 :     *prop = value.toBoolean();
    1495           3 :     return true;
    1496             : }
    1497             : 
    1498             : /*
    1499             :  * Helper that tries to get an object property from the options object.
    1500             :  */
    1501             : bool
    1502          48 : OptionsBase::ParseObject(const char* name, MutableHandleObject prop)
    1503             : {
    1504          96 :     RootedValue value(mCx);
    1505             :     bool found;
    1506          48 :     bool ok = ParseValue(name, &value, &found);
    1507          48 :     NS_ENSURE_TRUE(ok, false);
    1508             : 
    1509          48 :     if (!found)
    1510          47 :         return true;
    1511             : 
    1512           1 :     if (!value.isObject()) {
    1513           0 :         JS_ReportErrorASCII(mCx, "Expected an object value for property %s", name);
    1514           0 :         return false;
    1515             :     }
    1516           1 :     prop.set(&value.toObject());
    1517           1 :     return true;
    1518             : }
    1519             : 
    1520             : /*
    1521             :  * Helper that tries to get an object property from the options object.
    1522             :  */
    1523             : bool
    1524          16 : OptionsBase::ParseJSString(const char* name, MutableHandleString prop)
    1525             : {
    1526          32 :     RootedValue value(mCx);
    1527             :     bool found;
    1528          16 :     bool ok = ParseValue(name, &value, &found);
    1529          16 :     NS_ENSURE_TRUE(ok, false);
    1530             : 
    1531          16 :     if (!found)
    1532           2 :         return true;
    1533             : 
    1534          14 :     if (!value.isString()) {
    1535           0 :         JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
    1536           0 :         return false;
    1537             :     }
    1538          14 :     prop.set(value.toString());
    1539          14 :     return true;
    1540             : }
    1541             : 
    1542             : /*
    1543             :  * Helper that tries to get a string property from the options object.
    1544             :  */
    1545             : bool
    1546          16 : OptionsBase::ParseString(const char* name, nsCString& prop)
    1547             : {
    1548          32 :     RootedValue value(mCx);
    1549             :     bool found;
    1550          16 :     bool ok = ParseValue(name, &value, &found);
    1551          16 :     NS_ENSURE_TRUE(ok, false);
    1552             : 
    1553          16 :     if (!found)
    1554           1 :         return true;
    1555             : 
    1556          15 :     if (!value.isString()) {
    1557           0 :         JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
    1558           0 :         return false;
    1559             :     }
    1560             : 
    1561          15 :     char* tmp = JS_EncodeString(mCx, value.toString());
    1562          15 :     NS_ENSURE_TRUE(tmp, false);
    1563          15 :     prop.Assign(tmp, strlen(tmp));
    1564          15 :     js_free(tmp);
    1565          15 :     return true;
    1566             : }
    1567             : 
    1568             : /*
    1569             :  * Helper that tries to get a string property from the options object.
    1570             :  */
    1571             : bool
    1572           0 : OptionsBase::ParseString(const char* name, nsString& prop)
    1573             : {
    1574           0 :     RootedValue value(mCx);
    1575             :     bool found;
    1576           0 :     bool ok = ParseValue(name, &value, &found);
    1577           0 :     NS_ENSURE_TRUE(ok, false);
    1578             : 
    1579           0 :     if (!found)
    1580           0 :         return true;
    1581             : 
    1582           0 :     if (!value.isString()) {
    1583           0 :         JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
    1584           0 :         return false;
    1585             :     }
    1586             : 
    1587           0 :     nsAutoJSString strVal;
    1588           0 :     if (!strVal.init(mCx, value.toString()))
    1589           0 :         return false;
    1590             : 
    1591           0 :     prop = strVal;
    1592           0 :     return true;
    1593             : }
    1594             : 
    1595             : /*
    1596             :  * Helper that tries to get jsid property from the options object.
    1597             :  */
    1598             : bool
    1599           0 : OptionsBase::ParseId(const char* name, MutableHandleId prop)
    1600             : {
    1601           0 :     RootedValue value(mCx);
    1602             :     bool found;
    1603           0 :     bool ok = ParseValue(name, &value, &found);
    1604           0 :     NS_ENSURE_TRUE(ok, false);
    1605             : 
    1606           0 :     if (!found)
    1607           0 :         return true;
    1608             : 
    1609           0 :     return JS_ValueToId(mCx, value, prop);
    1610             : }
    1611             : 
    1612             : /*
    1613             :  * Helper that tries to get a uint32_t property from the options object.
    1614             :  */
    1615             : bool
    1616          16 : OptionsBase::ParseUInt32(const char* name, uint32_t* prop)
    1617             : {
    1618          16 :     MOZ_ASSERT(prop);
    1619          32 :     RootedValue value(mCx);
    1620             :     bool found;
    1621          16 :     bool ok = ParseValue(name, &value, &found);
    1622          16 :     NS_ENSURE_TRUE(ok, false);
    1623             : 
    1624          16 :     if (!found)
    1625          16 :         return true;
    1626             : 
    1627           0 :     if(!JS::ToUint32(mCx, value, prop)) {
    1628           0 :         JS_ReportErrorASCII(mCx, "Expected a uint32_t value for property %s", name);
    1629           0 :         return false;
    1630             :     }
    1631             : 
    1632           0 :     return true;
    1633             : }
    1634             : 
    1635             : /*
    1636             :  * Helper that tries to get a list of DOM constructors and other helpers from the options object.
    1637             :  */
    1638             : bool
    1639          16 : SandboxOptions::ParseGlobalProperties()
    1640             : {
    1641          32 :     RootedValue value(mCx);
    1642             :     bool found;
    1643          16 :     bool ok = ParseValue("wantGlobalProperties", &value, &found);
    1644          16 :     NS_ENSURE_TRUE(ok, false);
    1645          16 :     if (!found)
    1646           0 :         return true;
    1647             : 
    1648          16 :     if (!value.isObject()) {
    1649           0 :         JS_ReportErrorASCII(mCx, "Expected an array value for wantGlobalProperties");
    1650           0 :         return false;
    1651             :     }
    1652             : 
    1653          32 :     RootedObject ctors(mCx, &value.toObject());
    1654             :     bool isArray;
    1655          16 :     if (!JS_IsArrayObject(mCx, ctors, &isArray))
    1656           0 :         return false;
    1657          16 :     if (!isArray) {
    1658           0 :         JS_ReportErrorASCII(mCx, "Expected an array value for wantGlobalProperties");
    1659           0 :         return false;
    1660             :     }
    1661             : 
    1662          16 :     return globalProperties.Parse(mCx, ctors);
    1663             : }
    1664             : 
    1665             : /*
    1666             :  * Helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options).
    1667             :  */
    1668             : bool
    1669          16 : SandboxOptions::Parse()
    1670             : {
    1671             :     /* All option names must be ASCII-only. */
    1672          80 :     bool ok = ParseObject("sandboxPrototype", &proto) &&
    1673          32 :               ParseBoolean("wantXrays", &wantXrays) &&
    1674          32 :               ParseBoolean("allowWaivers", &allowWaivers) &&
    1675          32 :               ParseBoolean("wantComponents", &wantComponents) &&
    1676          32 :               ParseBoolean("wantExportHelpers", &wantExportHelpers) &&
    1677          32 :               ParseBoolean("isWebExtensionContentScript", &isWebExtensionContentScript) &&
    1678          32 :               ParseBoolean("waiveInterposition", &waiveInterposition) &&
    1679          48 :               ParseString("sandboxName", sandboxName) &&
    1680          64 :               ParseObject("sameZoneAs", &sameZoneAs) &&
    1681          32 :               ParseBoolean("freshZone", &freshZone) &&
    1682          32 :               ParseBoolean("invisibleToDebugger", &invisibleToDebugger) &&
    1683          48 :               ParseBoolean("discardSource", &discardSource) &&
    1684          64 :               ParseJSString("addonId", &addonId) &&
    1685          32 :               ParseBoolean("writeToGlobalPrototype", &writeToGlobalPrototype) &&
    1686          48 :               ParseGlobalProperties() &&
    1687          64 :               ParseValue("metadata", &metadata) &&
    1688          96 :               ParseUInt32("userContextId", &userContextId) &&
    1689          48 :               ParseObject("originAttributes", &originAttributes);
    1690          16 :     if (!ok)
    1691           0 :         return false;
    1692             : 
    1693          16 :     if (freshZone && sameZoneAs) {
    1694           0 :         JS_ReportErrorASCII(mCx, "Cannot use both sameZoneAs and freshZone");
    1695           0 :         return false;
    1696             :     }
    1697             : 
    1698          16 :     return true;
    1699             : }
    1700             : 
    1701             : static nsresult
    1702          16 : AssembleSandboxMemoryReporterName(JSContext* cx, nsCString& sandboxName)
    1703             : {
    1704             :     // Use a default name when the caller did not provide a sandboxName.
    1705          16 :     if (sandboxName.IsEmpty())
    1706           1 :         sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]");
    1707             : 
    1708          16 :     nsXPConnect* xpc = nsXPConnect::XPConnect();
    1709             :     // Get the xpconnect native call context.
    1710          16 :     nsAXPCNativeCallContext* cc = nullptr;
    1711          16 :     xpc->GetCurrentNativeCallContext(&cc);
    1712          16 :     NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG);
    1713             : 
    1714             :     // Get the current source info from xpc.
    1715          32 :     nsCOMPtr<nsIStackFrame> frame;
    1716          16 :     xpc->GetCurrentJSStack(getter_AddRefs(frame));
    1717             : 
    1718             :     // Append the caller's location information.
    1719          16 :     if (frame) {
    1720          32 :         nsString location;
    1721          16 :         int32_t lineNumber = 0;
    1722          16 :         frame->GetFilename(cx, location);
    1723          16 :         frame->GetLineNumber(cx, &lineNumber);
    1724             : 
    1725          16 :         sandboxName.AppendLiteral(" (from: ");
    1726          16 :         sandboxName.Append(NS_ConvertUTF16toUTF8(location));
    1727          16 :         sandboxName.Append(':');
    1728          16 :         sandboxName.AppendInt(lineNumber);
    1729          16 :         sandboxName.Append(')');
    1730             :     }
    1731             : 
    1732          16 :     return NS_OK;
    1733             : }
    1734             : 
    1735             : // static
    1736             : nsresult
    1737          16 : nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
    1738             :                                                JSContext* cx, HandleObject obj,
    1739             :                                                const CallArgs& args, bool* _retval)
    1740             : {
    1741          16 :     if (args.length() < 1)
    1742           0 :         return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval);
    1743             : 
    1744             :     nsresult rv;
    1745          16 :     bool ok = false;
    1746          16 :     bool calledWithOptions = args.length() > 1;
    1747          16 :     if (calledWithOptions && !args[1].isObject())
    1748           0 :         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
    1749             : 
    1750          48 :     RootedObject optionsObject(cx, calledWithOptions ? &args[1].toObject()
    1751          32 :                                                      : nullptr);
    1752             : 
    1753          32 :     SandboxOptions options(cx, optionsObject);
    1754          16 :     if (calledWithOptions && !options.Parse())
    1755           0 :         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
    1756             : 
    1757             :     // Make sure to set up principals on the sandbox before initing classes.
    1758          32 :     nsCOMPtr<nsIPrincipal> principal;
    1759          32 :     nsCOMPtr<nsIExpandedPrincipal> expanded;
    1760          32 :     nsCOMPtr<nsISupports> prinOrSop;
    1761             : 
    1762          16 :     if (args[0].isString()) {
    1763           0 :         RootedString str(cx, args[0].toString());
    1764           0 :         OriginAttributes attrs;
    1765           0 :         if (options.originAttributes) {
    1766           0 :             JS::RootedValue val(cx, JS::ObjectValue(*options.originAttributes));
    1767           0 :             if (!attrs.Init(cx, val)) {
    1768             :                 // The originAttributes option, if specified, must be valid!
    1769           0 :                 JS_ReportErrorASCII(cx, "Expected a valid OriginAttributes object");
    1770           0 :                 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
    1771             :             }
    1772             :         }
    1773           0 :         attrs.mUserContextId = options.userContextId;
    1774           0 :         ok = ParsePrincipal(cx, str, attrs, getter_AddRefs(principal));
    1775           0 :         prinOrSop = principal;
    1776          16 :     } else if (args[0].isObject()) {
    1777          32 :         RootedObject obj(cx, &args[0].toObject());
    1778             :         bool isArray;
    1779          16 :         if (!JS_IsArrayObject(cx, obj, &isArray)) {
    1780           0 :             ok = false;
    1781          16 :         } else if (isArray) {
    1782           0 :             if (options.userContextId != 0) {
    1783             :                 // We don't support passing a userContextId with an array.
    1784           0 :                 ok = false;
    1785             :             } else {
    1786           0 :                 ok = GetExpandedPrincipal(cx, obj, options, getter_AddRefs(expanded));
    1787           0 :                 prinOrSop = expanded;
    1788             :             }
    1789             :         } else {
    1790          16 :             ok = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
    1791             :         }
    1792           0 :     } else if (args[0].isNull()) {
    1793             :         // Null means that we just pass prinOrSop = nullptr, and get an
    1794             :         // NullPrincipal.
    1795           0 :         ok = true;
    1796             :     }
    1797             : 
    1798          16 :     if (!ok)
    1799           0 :         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
    1800             : 
    1801             : 
    1802          16 :     if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName)))
    1803           0 :         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
    1804             : 
    1805          16 :     if (options.metadata.isNullOrUndefined()) {
    1806             :         // If the caller is running in a sandbox, inherit.
    1807           2 :         RootedObject callerGlobal(cx, CurrentGlobalOrNull(cx));
    1808           1 :         if (IsSandbox(callerGlobal)) {
    1809           1 :             rv = GetSandboxMetadata(cx, callerGlobal, &options.metadata);
    1810           1 :             if (NS_WARN_IF(NS_FAILED(rv)))
    1811           0 :                 return rv;
    1812             :         }
    1813             :     }
    1814             : 
    1815          16 :     rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options);
    1816             : 
    1817          16 :     if (NS_FAILED(rv))
    1818           0 :         return ThrowAndFail(rv, cx, _retval);
    1819             : 
    1820             :     // We have this crazy behavior where wantXrays=false also implies that the
    1821             :     // returned sandbox is implicitly waived. We've stopped advertising it, but
    1822             :     // keep supporting it for now.
    1823          16 :     if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, args.rval()))
    1824           0 :         return NS_ERROR_UNEXPECTED;
    1825             : 
    1826          16 :     *_retval = true;
    1827          16 :     return NS_OK;
    1828             : }
    1829             : 
    1830             : nsresult
    1831           0 : xpc::EvalInSandbox(JSContext* cx, HandleObject sandboxArg, const nsAString& source,
    1832             :                    const nsACString& filename, int32_t lineNo,
    1833             :                    JSVersion jsVersion, MutableHandleValue rval)
    1834             : {
    1835           0 :     JS_AbortIfWrongThread(cx);
    1836           0 :     rval.set(UndefinedValue());
    1837             : 
    1838           0 :     bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg);
    1839           0 :     RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg));
    1840           0 :     if (!sandbox || !IsSandbox(sandbox)) {
    1841           0 :         return NS_ERROR_INVALID_ARG;
    1842             :     }
    1843             : 
    1844             :     nsIScriptObjectPrincipal* sop =
    1845           0 :         static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(sandbox));
    1846           0 :     MOZ_ASSERT(sop, "Invalid sandbox passed");
    1847           0 :     SandboxPrivate* priv = static_cast<SandboxPrivate*>(sop);
    1848           0 :     nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal();
    1849           0 :     NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
    1850             : 
    1851           0 :     nsAutoCString filenameBuf;
    1852           0 :     if (!filename.IsVoid() && filename.Length() != 0) {
    1853           0 :         filenameBuf.Assign(filename);
    1854             :     } else {
    1855             :         // Default to the spec of the principal.
    1856           0 :         nsresult rv = nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf);
    1857           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1858           0 :         lineNo = 1;
    1859             :     }
    1860             : 
    1861             :     // We create a separate cx to do the sandbox evaluation. Scope it.
    1862           0 :     RootedValue v(cx, UndefinedValue());
    1863           0 :     RootedValue exn(cx, UndefinedValue());
    1864           0 :     bool ok = true;
    1865             :     {
    1866             :         // We're about to evaluate script, so make an AutoEntryScript.
    1867             :         // This is clearly Gecko-specific and not in any spec.
    1868           0 :         mozilla::dom::AutoEntryScript aes(priv, "XPConnect sandbox evaluation");
    1869           0 :         JSContext* sandcx = aes.cx();
    1870           0 :         JSAutoCompartment ac(sandcx, sandbox);
    1871             : 
    1872           0 :         JS::CompileOptions options(sandcx);
    1873           0 :         options.setFileAndLine(filenameBuf.get(), lineNo)
    1874           0 :                .setVersion(jsVersion);
    1875           0 :         MOZ_ASSERT(JS_IsGlobalObject(sandbox));
    1876           0 :         ok = JS::Evaluate(sandcx, options,
    1877           0 :                           PromiseFlatString(source).get(), source.Length(), &v);
    1878             : 
    1879             :         // If the sandbox threw an exception, grab it off the context.
    1880           0 :         if (aes.HasException()) {
    1881           0 :             if (!aes.StealException(&exn)) {
    1882           0 :                 return NS_ERROR_OUT_OF_MEMORY;
    1883             :             }
    1884             :         }
    1885             :     }
    1886             : 
    1887             :     //
    1888             :     // Alright, we're back on the caller's cx. If an error occured, try to
    1889             :     // wrap and set the exception. Otherwise, wrap the return value.
    1890             :     //
    1891             : 
    1892           0 :     if (!ok) {
    1893             :         // If we end up without an exception, it was probably due to OOM along
    1894             :         // the way, in which case we thow. Otherwise, wrap it.
    1895           0 :         if (exn.isUndefined() || !JS_WrapValue(cx, &exn))
    1896           0 :             return NS_ERROR_OUT_OF_MEMORY;
    1897             : 
    1898             :         // Set the exception on our caller's cx.
    1899           0 :         JS_SetPendingException(cx, exn);
    1900           0 :         return NS_ERROR_FAILURE;
    1901             :     }
    1902             : 
    1903             :     // Transitively apply Xray waivers if |sb| was waived.
    1904           0 :     if (waiveXray) {
    1905           0 :         ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
    1906             :     } else {
    1907           0 :         ok = JS_WrapValue(cx, &v);
    1908             :     }
    1909           0 :     NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
    1910             : 
    1911             :     // Whew!
    1912           0 :     rval.set(v);
    1913           0 :     return NS_OK;
    1914             : }
    1915             : 
    1916             : nsresult
    1917           0 : xpc::GetSandboxAddonId(JSContext* cx, HandleObject sandbox, MutableHandleValue rval)
    1918             : {
    1919           0 :     MOZ_ASSERT(NS_IsMainThread());
    1920           0 :     MOZ_ASSERT(IsSandbox(sandbox));
    1921             : 
    1922           0 :     JSAddonId* id = JS::AddonIdOfObject(sandbox);
    1923           0 :     if (!id) {
    1924           0 :         rval.setNull();
    1925           0 :         return NS_OK;
    1926             :     }
    1927             : 
    1928           0 :     JS::RootedValue idStr(cx, StringValue(JS::StringOfAddonId(id)));
    1929           0 :     if (!JS_WrapValue(cx, &idStr))
    1930           0 :         return NS_ERROR_UNEXPECTED;
    1931             : 
    1932           0 :     rval.set(idStr);
    1933           0 :     return NS_OK;
    1934             : }
    1935             : 
    1936             : nsresult
    1937           1 : xpc::GetSandboxMetadata(JSContext* cx, HandleObject sandbox, MutableHandleValue rval)
    1938             : {
    1939           1 :     MOZ_ASSERT(NS_IsMainThread());
    1940           1 :     MOZ_ASSERT(IsSandbox(sandbox));
    1941             : 
    1942           2 :     RootedValue metadata(cx);
    1943             :     {
    1944           2 :       JSAutoCompartment ac(cx, sandbox);
    1945           1 :       metadata = JS_GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT);
    1946             :     }
    1947             : 
    1948           1 :     if (!JS_WrapValue(cx, &metadata))
    1949           0 :         return NS_ERROR_UNEXPECTED;
    1950             : 
    1951           1 :     rval.set(metadata);
    1952           1 :     return NS_OK;
    1953             : }
    1954             : 
    1955             : nsresult
    1956          25 : xpc::SetSandboxMetadata(JSContext* cx, HandleObject sandbox, HandleValue metadataArg)
    1957             : {
    1958          25 :     MOZ_ASSERT(NS_IsMainThread());
    1959          25 :     MOZ_ASSERT(IsSandbox(sandbox));
    1960             : 
    1961          50 :     RootedValue metadata(cx);
    1962             : 
    1963          50 :     JSAutoCompartment ac(cx, sandbox);
    1964          25 :     if (!JS_StructuredClone(cx, metadataArg, &metadata, nullptr, nullptr))
    1965           0 :         return NS_ERROR_UNEXPECTED;
    1966             : 
    1967          25 :     JS_SetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT, metadata);
    1968             : 
    1969          25 :     return NS_OK;
    1970             : }

Generated by: LCOV version 1.13