js/xpconnect/src/Sandbox.cpp

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

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

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

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * The Components.Sandbox object.
     9  */
    11 #include "AccessCheck.h"
    12 #include "jsfriendapi.h"
    13 #include "jsproxy.h"
    14 #include "js/OldDebugAPI.h"
    15 #include "js/StructuredClone.h"
    16 #include "nsContentUtils.h"
    17 #include "nsCxPusher.h"
    18 #include "nsGlobalWindow.h"
    19 #include "nsIScriptContext.h"
    20 #include "nsIScriptObjectPrincipal.h"
    21 #include "nsIScriptSecurityManager.h"
    22 #include "nsIURI.h"
    23 #include "nsJSUtils.h"
    24 #include "nsNetUtil.h"
    25 #include "nsPrincipal.h"
    26 #include "nsXMLHttpRequest.h"
    27 #include "WrapperFactory.h"
    28 #include "xpcprivate.h"
    29 #include "XPCQuickStubs.h"
    30 #include "XPCWrapper.h"
    31 #include "XrayWrapper.h"
    32 #include "mozilla/dom/BindingUtils.h"
    33 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
    34 #include "mozilla/dom/PromiseBinding.h"
    35 #include "mozilla/dom/TextDecoderBinding.h"
    36 #include "mozilla/dom/TextEncoderBinding.h"
    37 #include "mozilla/dom/URLBinding.h"
    39 using namespace mozilla;
    40 using namespace JS;
    41 using namespace js;
    42 using namespace xpc;
    44 using mozilla::dom::DestroyProtoAndIfaceCache;
    45 using mozilla::dom::indexedDB::IndexedDatabaseManager;
    47 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(SandboxPrivate)
    48 NS_IMPL_CYCLE_COLLECTING_ADDREF(SandboxPrivate)
    49 NS_IMPL_CYCLE_COLLECTING_RELEASE(SandboxPrivate)
    50 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SandboxPrivate)
    51   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    52   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptObjectPrincipal)
    53   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
    54   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
    55   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    56 NS_INTERFACE_MAP_END
    58 const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID;
    60 class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox,
    61                                       public nsIXPCScriptable
    62 {
    63 public:
    64     // Aren't macros nice?
    65     NS_DECL_ISUPPORTS
    66     NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX
    67     NS_DECL_NSIXPCSCRIPTABLE
    69 public:
    70     nsXPCComponents_utils_Sandbox();
    71     virtual ~nsXPCComponents_utils_Sandbox();
    73 private:
    74     static nsresult CallOrConstruct(nsIXPConnectWrappedNative *wrapper,
    75                                     JSContext *cx, HandleObject obj,
    76                                     const CallArgs &args, bool *_retval);
    77 };
    79 already_AddRefed<nsIXPCComponents_utils_Sandbox>
    80 xpc::NewSandboxConstructor()
    81 {
    82     nsCOMPtr<nsIXPCComponents_utils_Sandbox> sbConstructor =
    83         new nsXPCComponents_utils_Sandbox();
    84     return sbConstructor.forget();
    85 }
    87 static bool
    88 SandboxDump(JSContext *cx, unsigned argc, jsval *vp)
    89 {
    90     CallArgs args = CallArgsFromVp(argc, vp);
    92     if (args.length() == 0)
    93         return true;
    95     RootedString str(cx, ToString(cx, args[0]));
    96     if (!str)
    97         return false;
    99     size_t length;
   100     const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length);
   101     if (!chars)
   102         return false;
   104     nsDependentString wstr(chars, length);
   105     char *cstr = ToNewUTF8String(wstr);
   106     if (!cstr)
   107         return false;
   109 #if defined(XP_MACOSX)
   110     // Be nice and convert all \r to \n.
   111     char *c = cstr, *cEnd = cstr + strlen(cstr);
   112     while (c < cEnd) {
   113         if (*c == '\r')
   114             *c = '\n';
   115         c++;
   116     }
   117 #endif
   118 #ifdef ANDROID
   119     __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
   120 #endif
   122     fputs(cstr, stdout);
   123     fflush(stdout);
   124     NS_Free(cstr);
   125     args.rval().setBoolean(true);
   126     return true;
   127 }
   129 static bool
   130 SandboxDebug(JSContext *cx, unsigned argc, jsval *vp)
   131 {
   132 #ifdef DEBUG
   133     return SandboxDump(cx, argc, vp);
   134 #else
   135     return true;
   136 #endif
   137 }
   139 static bool
   140 SandboxImport(JSContext *cx, unsigned argc, Value *vp)
   141 {
   142     CallArgs args = CallArgsFromVp(argc, vp);
   144     if (args.length() < 1 || args[0].isPrimitive()) {
   145         XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
   146         return false;
   147     }
   149     RootedString funname(cx);
   150     if (args.length() > 1) {
   151         // Use the second parameter as the function name.
   152         funname = ToString(cx, args[1]);
   153         if (!funname)
   154             return false;
   155     } else {
   156         // NB: funobj must only be used to get the JSFunction out.
   157         RootedObject funobj(cx, &args[0].toObject());
   158         if (js::IsProxy(funobj)) {
   159             funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj);
   160         }
   162         JSAutoCompartment ac(cx, funobj);
   164         RootedValue funval(cx, ObjectValue(*funobj));
   165         JSFunction *fun = JS_ValueToFunction(cx, funval);
   166         if (!fun) {
   167             XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
   168             return false;
   169         }
   171         // Use the actual function name as the name.
   172         funname = JS_GetFunctionId(fun);
   173         if (!funname) {
   174             XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
   175             return false;
   176         }
   177     }
   179     RootedId id(cx);
   180     if (!JS_StringToId(cx, funname, &id))
   181         return false;
   183     // We need to resolve the this object, because this function is used
   184     // unbound and should still work and act on the original sandbox.
   185     RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp));
   186     if (!thisObject) {
   187         XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
   188         return false;
   189     }
   190     if (!JS_SetPropertyById(cx, thisObject, id, args[0]))
   191         return false;
   193     args.rval().setUndefined();
   194     return true;
   195 }
   197 static bool
   198 CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp)
   199 {
   200     CallArgs args = CallArgsFromVp(argc, vp);
   202     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
   203     if (!ssm)
   204         return false;
   206     nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
   207     if (!subjectPrincipal)
   208         return false;
   210     RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
   211     MOZ_ASSERT(global);
   213     nsIScriptObjectPrincipal *sop =
   214         static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(global));
   215     nsCOMPtr<nsIGlobalObject> iglobal = do_QueryInterface(sop);
   217     nsCOMPtr<nsIXMLHttpRequest> xhr = new nsXMLHttpRequest();
   218     nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr);
   219     if (NS_FAILED(rv))
   220         return false;
   222     rv = nsContentUtils::WrapNative(cx, xhr, args.rval());
   223     if (NS_FAILED(rv))
   224         return false;
   226     return true;
   227 }
   229 static bool
   230 IsProxy(JSContext *cx, unsigned argc, jsval *vp)
   231 {
   232     CallArgs args = CallArgsFromVp(argc, vp);
   233     if (args.length() < 1) {
   234         JS_ReportError(cx, "Function requires at least 1 argument");
   235         return false;
   236     }
   237     if (!args[0].isObject()) {
   238         args.rval().setBoolean(false);
   239         return true;
   240     }
   242     RootedObject obj(cx, &args[0].toObject());
   243     obj = js::CheckedUnwrap(obj);
   244     NS_ENSURE_TRUE(obj, false);
   246     args.rval().setBoolean(js::IsScriptedProxy(obj));
   247     return true;
   248 }
   250 namespace xpc {
   252 bool
   253 ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleValue voptions,
   254                MutableHandleValue rval)
   255 {
   256     bool hasOptions = !voptions.isUndefined();
   257     if (!vscope.isObject() || !vfunction.isObject() || (hasOptions && !voptions.isObject())) {
   258         JS_ReportError(cx, "Invalid argument");
   259         return false;
   260     }
   262     RootedObject funObj(cx, &vfunction.toObject());
   263     RootedObject targetScope(cx, &vscope.toObject());
   264     ExportOptions options(cx, hasOptions ? &voptions.toObject() : nullptr);
   265     if (hasOptions && !options.Parse())
   266         return false;
   268     // We can only export functions to scopes those are transparent for us,
   269     // so if there is a security wrapper around targetScope we must throw.
   270     targetScope = CheckedUnwrap(targetScope);
   271     if (!targetScope) {
   272         JS_ReportError(cx, "Permission denied to export function into scope");
   273         return false;
   274     }
   276     if (js::IsScriptedProxy(targetScope)) {
   277         JS_ReportError(cx, "Defining property on proxy object is not allowed");
   278         return false;
   279     }
   281     {
   282         // We need to operate in the target scope from here on, let's enter
   283         // its compartment.
   284         JSAutoCompartment ac(cx, targetScope);
   286         // Unwrapping to see if we have a callable.
   287         funObj = UncheckedUnwrap(funObj);
   288         if (!JS_ObjectIsCallable(cx, funObj)) {
   289             JS_ReportError(cx, "First argument must be a function");
   290             return false;
   291         }
   293         RootedId id(cx, options.defineAs);
   294         if (JSID_IS_VOID(id)) {
   295             // If there wasn't any function name specified,
   296             // copy the name from the function being imported.
   297             JSFunction *fun = JS_GetObjectFunction(funObj);
   298             RootedString funName(cx, JS_GetFunctionId(fun));
   299             if (!funName)
   300                 funName = JS_InternString(cx, "");
   302             if (!JS_StringToId(cx, funName, &id))
   303                 return false;
   304         }
   305         MOZ_ASSERT(JSID_IS_STRING(id));
   307         // The function forwarder will live in the target compartment. Since
   308         // this function will be referenced from its private slot, to avoid a
   309         // GC hazard, we must wrap it to the same compartment.
   310         if (!JS_WrapObject(cx, &funObj))
   311             return false;
   313         // And now, let's create the forwarder function in the target compartment
   314         // for the function the be exported.
   315         if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, rval)) {
   316             JS_ReportError(cx, "Exporting function failed");
   317             return false;
   318         }
   320         // We have the forwarder function in the target compartment. If
   321         // defineAs was set, we also need to define it as a property on
   322         // the target.
   323         if (!JSID_IS_VOID(options.defineAs)) {
   324             if (!JS_DefinePropertyById(cx, targetScope, id, rval,
   325                                        JS_PropertyStub, JS_StrictPropertyStub,
   326                                        JSPROP_ENUMERATE)) {
   327                 return false;
   328             }
   329         }
   330     }
   332     // Finally we have to re-wrap the exported function back to the caller compartment.
   333     if (!JS_WrapValue(cx, rval))
   334         return false;
   336     return true;
   337 }
   339 /*
   340  * Expected type of the arguments and the return value:
   341  * function exportFunction(function funToExport,
   342  *                         object targetScope,
   343  *                         [optional] object options)
   344  */
   345 static bool
   346 ExportFunction(JSContext *cx, unsigned argc, jsval *vp)
   347 {
   348     CallArgs args = CallArgsFromVp(argc, vp);
   349     if (args.length() < 2) {
   350         JS_ReportError(cx, "Function requires at least 2 arguments");
   351         return false;
   352     }
   354     RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
   355     return ExportFunction(cx, args[0], args[1], options, args.rval());
   356 }
   357 } /* namespace xpc */
   359 static bool
   360 GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno)
   361 {
   362     JS::AutoFilename scriptFilename;
   363     if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineno)) {
   364         if (const char *cfilename = scriptFilename.get()) {
   365             filename.Assign(nsDependentCString(cfilename));
   366             return true;
   367         }
   368     }
   369     return false;
   370 }
   372 bool
   373 xpc::IsReflector(JSObject *obj)
   374 {
   375     return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj);
   376 }
   378 enum ForwarderCloneTags {
   379     SCTAG_BASE = JS_SCTAG_USER_MIN,
   380     SCTAG_REFLECTOR
   381 };
   383 static JSObject *
   384 CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag,
   385                        uint32_t data, void *closure)
   386 {
   387     MOZ_ASSERT(closure, "Null pointer!");
   388     AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure);
   389     if (tag == SCTAG_REFLECTOR) {
   390         MOZ_ASSERT(!data);
   392         size_t idx;
   393         if (JS_ReadBytes(reader, &idx, sizeof(size_t))) {
   394             RootedObject reflector(cx, reflectors->handleAt(idx));
   395             MOZ_ASSERT(reflector, "No object pointer?");
   396             MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!");
   398             if (!JS_WrapObject(cx, &reflector))
   399                 return nullptr;
   400             MOZ_ASSERT(WrapperFactory::IsXrayWrapper(reflector) ||
   401                        IsReflector(reflector));
   403             return reflector;
   404         }
   405     }
   407     JS_ReportError(cx, "CloneNonReflectorsRead error");
   408     return nullptr;
   409 }
   411 static bool
   412 CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer,
   413                         Handle<JSObject *> obj, void *closure)
   414 {
   415     MOZ_ASSERT(closure, "Null pointer!");
   417     // We need to maintain a list of reflectors to make sure all these objects
   418     // are properly rooter. Only their indices will be serialized.
   419     AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure);
   420     if (IsReflector(obj)) {
   421         if (!reflectors->append(obj))
   422             return false;
   424         size_t idx = reflectors->length()-1;
   425         if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) &&
   426             JS_WriteBytes(writer, &idx, sizeof(size_t))) {
   427             return true;
   428         }
   429     }
   431     JS_ReportError(cx, "CloneNonReflectorsWrite error");
   432     return false;
   433 }
   435 static const JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = {
   436     CloneNonReflectorsRead,
   437     CloneNonReflectorsWrite,
   438     nullptr,
   439     nullptr,
   440     nullptr,
   441     nullptr
   442 };
   444 /*
   445  * This is a special structured cloning, that clones only non-reflectors.
   446  * The function assumes the cx is already entered the compartment we want
   447  * to clone to, and that if val is an object is from the compartment we
   448  * clone from.
   449  */
   450 static bool
   451 CloneNonReflectors(JSContext *cx, MutableHandleValue val)
   452 {
   453     JSAutoStructuredCloneBuffer buffer;
   454     AutoObjectVector rootedReflectors(cx);
   455     {
   456         // For parsing val we have to enter its compartment.
   457         // (unless it's a primitive)
   458         Maybe<JSAutoCompartment> ac;
   459         if (val.isObject()) {
   460             ac.construct(cx, &val.toObject());
   461         } else if (val.isString() && !JS_WrapValue(cx, val)) {
   462             return false;
   463         }
   465         if (!buffer.write(cx, val,
   466             &gForwarderStructuredCloneCallbacks,
   467             &rootedReflectors))
   468         {
   469             return false;
   470         }
   471     }
   473     // Now recreate the clones in the target compartment.
   474     if (!buffer.read(cx, val,
   475         &gForwarderStructuredCloneCallbacks,
   476         &rootedReflectors))
   477     {
   478         return false;
   479     }
   481     return true;
   482 }
   484 namespace xpc {
   486 bool
   487 EvalInWindow(JSContext *cx, const nsAString &source, HandleObject scope, MutableHandleValue rval)
   488 {
   489     // If we cannot unwrap we must not eval in it.
   490     RootedObject targetScope(cx, CheckedUnwrap(scope));
   491     if (!targetScope) {
   492         JS_ReportError(cx, "Permission denied to eval in target scope");
   493         return false;
   494     }
   496     // Make sure that we have a window object.
   497     RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false));
   498     nsCOMPtr<nsIGlobalObject> global;
   499     nsCOMPtr<nsPIDOMWindow> window;
   500     if (!JS_IsGlobalObject(inner) ||
   501         !(global = GetNativeForGlobal(inner)) ||
   502         !(window = do_QueryInterface(global)))
   503     {
   504         JS_ReportError(cx, "Second argument must be a window");
   505         return false;
   506     }
   508     nsCOMPtr<nsIScriptContext> context =
   509         (static_cast<nsGlobalWindow*>(window.get()))->GetScriptContext();
   510     if (!context) {
   511         JS_ReportError(cx, "Script context needed");
   512         return false;
   513     }
   515     nsCString filename;
   516     unsigned lineNo;
   517     if (!GetFilenameAndLineNumber(cx, filename, lineNo)) {
   518         // Default values for non-scripted callers.
   519         filename.Assign("Unknown");
   520         lineNo = 0;
   521     }
   523     RootedObject cxGlobal(cx, JS::CurrentGlobalOrNull(cx));
   524     {
   525         // CompileOptions must be created from the context
   526         // we will execute this script in.
   527         JSContext *wndCx = context->GetNativeContext();
   528         AutoCxPusher pusher(wndCx);
   529         JS::CompileOptions compileOptions(wndCx);
   530         compileOptions.setFileAndLine(filename.get(), lineNo);
   532         // We don't want the JS engine to automatically report
   533         // uncaught exceptions.
   534         nsJSUtils::EvaluateOptions evaluateOptions;
   535         evaluateOptions.setReportUncaught(false);
   537         nsresult rv = nsJSUtils::EvaluateString(wndCx,
   538                                                 source,
   539                                                 targetScope,
   540                                                 compileOptions,
   541                                                 evaluateOptions,
   542                                                 rval);
   544         if (NS_FAILED(rv)) {
   545             // If there was an exception we get it as a return value, if
   546             // the evaluation failed for some other reason, then a default
   547             // exception is raised.
   548             MOZ_ASSERT(!JS_IsExceptionPending(wndCx),
   549                        "Exception should be delivered as return value.");
   550             if (rval.isUndefined()) {
   551                 MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY);
   552                 return false;
   553             }
   555             // If there was an exception thrown we should set it
   556             // on the calling context.
   557             RootedValue exn(wndCx, rval);
   558             // First we should reset the return value.
   559             rval.set(UndefinedValue());
   561             // Then clone the exception.
   562             JSAutoCompartment ac(wndCx, cxGlobal);
   563             if (CloneNonReflectors(wndCx, &exn))
   564                 js::SetPendingExceptionCrossContext(cx, exn);
   566             return false;
   567         }
   568     }
   570     // Let's clone the return value back to the callers compartment.
   571     if (!CloneNonReflectors(cx, rval)) {
   572         rval.set(UndefinedValue());
   573         return false;
   574     }
   576     return true;
   577 }
   579 /*
   580  * Expected type of the arguments:
   581  * value evalInWindow(string script,
   582  *                    object window)
   583  */
   584 static bool
   585 EvalInWindow(JSContext *cx, unsigned argc, jsval *vp)
   586 {
   587     CallArgs args = CallArgsFromVp(argc, vp);
   588     if (args.length() < 2) {
   589         JS_ReportError(cx, "Function requires two arguments");
   590         return false;
   591     }
   593     if (!args[0].isString() || !args[1].isObject()) {
   594         JS_ReportError(cx, "Invalid arguments");
   595         return false;
   596     }
   598     RootedString srcString(cx, args[0].toString());
   599     RootedObject targetScope(cx, &args[1].toObject());
   601     nsDependentJSString srcDepString;
   602     if (!srcDepString.init(cx, srcString)) {
   603         JS_ReportError(cx, "Source string is invalid");
   604         return false;
   605     }
   607     return EvalInWindow(cx, srcDepString, targetScope, args.rval());
   608 }
   610 static bool
   611 CreateObjectIn(JSContext *cx, unsigned argc, jsval *vp)
   612 {
   613     CallArgs args = CallArgsFromVp(argc, vp);
   614     if (args.length() < 1) {
   615         JS_ReportError(cx, "Function requires at least 1 argument");
   616         return false;
   617     }
   619     RootedObject optionsObj(cx);
   620     bool calledWithOptions = args.length() > 1;
   621     if (calledWithOptions) {
   622         if (!args[1].isObject()) {
   623             JS_ReportError(cx, "Expected the 2nd argument (options) to be an object");
   624             return false;
   625         }
   626         optionsObj = &args[1].toObject();
   627     }
   629     CreateObjectInOptions options(cx, optionsObj);
   630     if (calledWithOptions && !options.Parse())
   631         return false;
   633     return xpc::CreateObjectIn(cx, args[0], options, args.rval());
   634 }
   636 static bool
   637 CloneInto(JSContext *cx, unsigned argc, jsval *vp)
   638 {
   639     CallArgs args = CallArgsFromVp(argc, vp);
   640     if (args.length() < 2) {
   641         JS_ReportError(cx, "Function requires at least 2 arguments");
   642         return false;
   643     }
   645     RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
   646     return xpc::CloneInto(cx, args[0], args[1], options, args.rval());
   647 }
   649 } /* namespace xpc */
   651 static bool
   652 sandbox_enumerate(JSContext *cx, HandleObject obj)
   653 {
   654     return JS_EnumerateStandardClasses(cx, obj);
   655 }
   657 static bool
   658 sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id)
   659 {
   660     bool resolved;
   661     return JS_ResolveStandardClass(cx, obj, id, &resolved);
   662 }
   664 static void
   665 sandbox_finalize(JSFreeOp *fop, JSObject *obj)
   666 {
   667     nsIScriptObjectPrincipal *sop =
   668         static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
   669     MOZ_ASSERT(sop);
   670     static_cast<SandboxPrivate *>(sop)->ForgetGlobalObject();
   671     NS_IF_RELEASE(sop);
   672     DestroyProtoAndIfaceCache(obj);
   673 }
   675 static bool
   676 sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
   677 {
   678     if (type == JSTYPE_OBJECT) {
   679         vp.set(OBJECT_TO_JSVAL(obj));
   680         return true;
   681     }
   683     return JS_ConvertStub(cx, obj, type, vp);
   684 }
   686 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
   688 static const JSClass SandboxClass = {
   689     "Sandbox",
   690     XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
   691     JS_PropertyStub,   JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   692     sandbox_enumerate, sandbox_resolve, sandbox_convert,  sandbox_finalize,
   693     nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
   694 };
   696 static const JSFunctionSpec SandboxFunctions[] = {
   697     JS_FS("dump",    SandboxDump,    1,0),
   698     JS_FS("debug",   SandboxDebug,   1,0),
   699     JS_FS("importFunction", SandboxImport, 1,0),
   700     JS_FS_END
   701 };
   703 bool
   704 xpc::IsSandbox(JSObject *obj)
   705 {
   706     return GetObjectJSClass(obj) == &SandboxClass;
   707 }
   709 /***************************************************************************/
   710 nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox()
   711 {
   712 }
   714 nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox()
   715 {
   716 }
   718 NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox)
   719   NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox)
   720   NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
   721   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox)
   722 NS_INTERFACE_MAP_END
   724 NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox)
   725 NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox)
   727 // We use the nsIXPScriptable macros to generate lots of stuff for us.
   728 #define XPC_MAP_CLASSNAME           nsXPCComponents_utils_Sandbox
   729 #define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_utils_Sandbox"
   730 #define                             XPC_MAP_WANT_CALL
   731 #define                             XPC_MAP_WANT_CONSTRUCT
   732 #define XPC_MAP_FLAGS               0
   733 #include "xpc_map_end.h" /* This #undef's the above. */
   735 xpc::SandboxProxyHandler xpc::sandboxProxyHandler;
   737 bool
   738 xpc::IsSandboxPrototypeProxy(JSObject *obj)
   739 {
   740     return js::IsProxy(obj) &&
   741            js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler;
   742 }
   744 bool
   745 xpc::SandboxCallableProxyHandler::call(JSContext *cx, JS::Handle<JSObject*> proxy,
   746                                        const JS::CallArgs &args)
   747 {
   748     // We forward the call to our underlying callable.
   750     // The parent of our proxy is the SandboxProxyHandler proxy
   751     RootedObject sandboxProxy(cx, JS_GetParent(proxy));
   752     MOZ_ASSERT(js::IsProxy(sandboxProxy) &&
   753                js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler);
   755     // The parent of the sandboxProxy is the sandbox global, and the
   756     // target object is the original proto.
   757     RootedObject sandboxGlobal(cx, JS_GetParent(sandboxProxy));
   758     MOZ_ASSERT(js::GetObjectJSClass(sandboxGlobal) == &SandboxClass);
   760     // If our this object is the sandbox global, we call with this set to the
   761     // original proto instead.
   762     //
   763     // There are two different ways we can compute |this|. If we use
   764     // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the
   765     // caller, which may be undefined if a global function was invoked without
   766     // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this|
   767     // in |vp| will be coerced to the global, which is not the correct
   768     // behavior in ES5 strict mode. And we have no way to compute strictness
   769     // here.
   770     //
   771     // The naive approach is simply to use JS_THIS_VALUE here. If |this| was
   772     // explicit, we can remap it appropriately. If it was implicit, then we
   773     // leave it as undefined, and let the callee sort it out. Since the callee
   774     // is generally in the same compartment as its global (eg the Window's
   775     // compartment, not the Sandbox's), the callee will generally compute the
   776     // correct |this|.
   777     //
   778     // However, this breaks down in the Xray case. If the sandboxPrototype
   779     // is an Xray wrapper, then we'll end up reifying the native methods in
   780     // the Sandbox's scope, which means that they'll compute |this| to be the
   781     // Sandbox, breaking old-style XPC_WN_CallMethod methods.
   782     //
   783     // Luckily, the intent of Xrays is to provide a vanilla view of a foreign
   784     // DOM interface, which means that we don't care about script-enacted
   785     // strictness in the prototype's home compartment. Indeed, since DOM
   786     // methods are always non-strict, we can just assume non-strict semantics
   787     // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately
   788     // remap |this|.
   789     bool isXray = WrapperFactory::IsXrayWrapper(sandboxProxy);
   790     RootedValue thisVal(cx, isXray ? args.computeThis(cx) : args.thisv());
   791     if (thisVal == ObjectValue(*sandboxGlobal)) {
   792         thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy));
   793     }
   795     RootedValue func(cx, js::GetProxyPrivate(proxy));
   796     return JS::Call(cx, thisVal, func, args, args.rval());
   797 }
   799 xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler;
   801 /*
   802  * Wrap a callable such that if we're called with oldThisObj as the
   803  * "this" we will instead call it with newThisObj as the this.
   804  */
   805 static JSObject*
   806 WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy)
   807 {
   808     MOZ_ASSERT(JS_ObjectIsCallable(cx, callable));
   809     // Our proxy is wrapping the callable.  So we need to use the
   810     // callable as the private.  We use the given sandboxProtoProxy as
   811     // the parent, and our call() hook depends on that.
   812     MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) &&
   813                js::GetProxyHandler(sandboxProtoProxy) ==
   814                  &xpc::sandboxProxyHandler);
   816     RootedValue priv(cx, ObjectValue(*callable));
   817     js::ProxyOptions options;
   818     options.selectDefaultClass(true);
   819     return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler,
   820                               priv, nullptr,
   821                               sandboxProtoProxy, options);
   822 }
   824 template<typename Op>
   825 bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id,
   826                     unsigned attrFlag, HandleObject sandboxProtoProxy)
   827 {
   828     if (!op) {
   829         return true;
   830     }
   832     RootedObject func(cx);
   833     if (desc->attrs & attrFlag) {
   834         // Already an object
   835         func = JS_FUNC_TO_DATA_PTR(JSObject *, op);
   836     } else {
   837         // We have an actual property op.  For getters, we use 0
   838         // args, for setters we use 1 arg.
   839         uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1;
   840         RootedObject obj(cx, desc->obj);
   841         func = GeneratePropertyOp(cx, obj, id, args, op);
   842         if (!func)
   843             return false;
   844     }
   845     func = WrapCallable(cx, func, sandboxProtoProxy);
   846     if (!func)
   847         return false;
   848     op = JS_DATA_TO_FUNC_PTR(Op, func.get());
   849     desc->attrs |= attrFlag;
   850     return true;
   851 }
   853 extern bool
   854 XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp);
   855 extern bool
   856 XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp);
   858 bool
   859 xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx,
   860                                                 JS::Handle<JSObject*> proxy,
   861                                                 JS::Handle<jsid> id,
   862                                                 JS::MutableHandle<JSPropertyDescriptor> desc)
   863 {
   864     JS::RootedObject obj(cx, wrappedObject(proxy));
   866     MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy));
   867     if (!JS_GetPropertyDescriptorById(cx, obj, id, desc))
   868         return false;
   870     if (!desc.object())
   871         return true; // No property, nothing to do
   873     // Now fix up the getter/setter/value as needed to be bound to desc->obj
   874     // Don't mess with holder_get and holder_set, though, because those rely on
   875     // the "vp is prefilled with the value in the slot" behavior that property
   876     // ops can in theory rely on, but our property op forwarder doesn't know how
   877     // to make that happen.  Since we really only need to rebind the DOM methods
   878     // here, not rebindings holder_get and holder_set is OK.
   879     //
   880     // Similarly, don't mess with XPC_WN_Helper_GetProperty and
   881     // XPC_WN_Helper_SetProperty, for the same reasons: that could confuse our
   882     // access to expandos when we're not doing Xrays.
   883     if (desc.getter() != xpc::holder_get &&
   884         desc.getter() != XPC_WN_Helper_GetProperty &&
   885         !BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy))
   886         return false;
   887     if (desc.setter() != xpc::holder_set &&
   888         desc.setter() != XPC_WN_Helper_SetProperty &&
   889         !BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy))
   890         return false;
   891     if (desc.value().isObject()) {
   892         JSObject* val = &desc.value().toObject();
   893         if (JS_ObjectIsCallable(cx, val)) {
   894             val = WrapCallable(cx, val, proxy);
   895             if (!val)
   896                 return false;
   897             desc.value().setObject(*val);
   898         }
   899     }
   901     return true;
   902 }
   904 bool
   905 xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext *cx,
   906                                                    JS::Handle<JSObject*> proxy,
   907                                                    JS::Handle<jsid> id,
   908                                                    JS::MutableHandle<JSPropertyDescriptor> desc)
   909 {
   910     if (!getPropertyDescriptor(cx, proxy, id, desc))
   911         return false;
   913     if (desc.object() != wrappedObject(proxy))
   914         desc.object().set(nullptr);
   916     return true;
   917 }
   919 /*
   920  * Reuse the BaseProxyHandler versions of the derived traps that are implemented
   921  * in terms of the fundamental traps.
   922  */
   924 bool
   925 xpc::SandboxProxyHandler::has(JSContext *cx, JS::Handle<JSObject*> proxy,
   926                               JS::Handle<jsid> id, bool *bp)
   927 {
   928     return BaseProxyHandler::has(cx, proxy, id, bp);
   929 }
   930 bool
   931 xpc::SandboxProxyHandler::hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
   932                                  JS::Handle<jsid> id, bool *bp)
   933 {
   934     return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
   935 }
   937 bool
   938 xpc::SandboxProxyHandler::get(JSContext *cx, JS::Handle<JSObject*> proxy,
   939                               JS::Handle<JSObject*> receiver,
   940                               JS::Handle<jsid> id,
   941                               JS::MutableHandle<Value> vp)
   942 {
   943     return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
   944 }
   946 bool
   947 xpc::SandboxProxyHandler::set(JSContext *cx, JS::Handle<JSObject*> proxy,
   948                               JS::Handle<JSObject*> receiver,
   949                               JS::Handle<jsid> id,
   950                               bool strict,
   951                               JS::MutableHandle<Value> vp)
   952 {
   953     return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
   954 }
   956 bool
   957 xpc::SandboxProxyHandler::keys(JSContext *cx, JS::Handle<JSObject*> proxy,
   958                                AutoIdVector &props)
   959 {
   960     return BaseProxyHandler::keys(cx, proxy, props);
   961 }
   963 bool
   964 xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
   965                                   unsigned flags, JS::MutableHandle<Value> vp)
   966 {
   967     return BaseProxyHandler::iterate(cx, proxy, flags, vp);
   968 }
   970 bool
   971 xpc::GlobalProperties::Parse(JSContext *cx, JS::HandleObject obj)
   972 {
   973     MOZ_ASSERT(JS_IsArrayObject(cx, obj));
   975     uint32_t length;
   976     bool ok = JS_GetArrayLength(cx, obj, &length);
   977     NS_ENSURE_TRUE(ok, false);
   978     bool promise = Promise;
   979     for (uint32_t i = 0; i < length; i++) {
   980         RootedValue nameValue(cx);
   981         ok = JS_GetElement(cx, obj, i, &nameValue);
   982         NS_ENSURE_TRUE(ok, false);
   983         if (!nameValue.isString()) {
   984             JS_ReportError(cx, "Property names must be strings");
   985             return false;
   986         }
   987         JSAutoByteString name(cx, nameValue.toString());
   988         NS_ENSURE_TRUE(name, false);
   989         if (promise && !strcmp(name.ptr(), "-Promise")) {
   990             Promise = false;
   991         } else if (!strcmp(name.ptr(), "indexedDB")) {
   992             indexedDB = true;
   993         } else if (!strcmp(name.ptr(), "XMLHttpRequest")) {
   994             XMLHttpRequest = true;
   995         } else if (!strcmp(name.ptr(), "TextEncoder")) {
   996             TextEncoder = true;
   997         } else if (!strcmp(name.ptr(), "TextDecoder")) {
   998             TextDecoder = true;
   999         } else if (!strcmp(name.ptr(), "URL")) {
  1000             URL = true;
  1001         } else if (!strcmp(name.ptr(), "atob")) {
  1002             atob = true;
  1003         } else if (!strcmp(name.ptr(), "btoa")) {
  1004             btoa = true;
  1005         } else {
  1006             JS_ReportError(cx, "Unknown property name: %s", name.ptr());
  1007             return false;
  1010     return true;
  1013 bool
  1014 xpc::GlobalProperties::Define(JSContext *cx, JS::HandleObject obj)
  1016     if (Promise && !dom::PromiseBinding::GetConstructorObject(cx, obj))
  1017         return false;
  1019     if (indexedDB && AccessCheck::isChrome(obj) &&
  1020         !IndexedDatabaseManager::DefineIndexedDB(cx, obj))
  1021         return false;
  1023     if (XMLHttpRequest &&
  1024         !JS_DefineFunction(cx, obj, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR))
  1025         return false;
  1027     if (TextEncoder &&
  1028         !dom::TextEncoderBinding::GetConstructorObject(cx, obj))
  1029         return false;
  1031     if (TextDecoder &&
  1032         !dom::TextDecoderBinding::GetConstructorObject(cx, obj))
  1033         return false;
  1035     if (URL &&
  1036         !dom::URLBinding::GetConstructorObject(cx, obj))
  1037         return false;
  1039     if (atob &&
  1040         !JS_DefineFunction(cx, obj, "atob", Atob, 1, 0))
  1041         return false;
  1043     if (btoa &&
  1044         !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0))
  1045         return false;
  1047     return true;
  1050 nsresult
  1051 xpc::CreateSandboxObject(JSContext *cx, MutableHandleValue vp, nsISupports *prinOrSop,
  1052                          SandboxOptions& options)
  1054     // Create the sandbox global object
  1055     nsresult rv;
  1056     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
  1057     if (NS_FAILED(rv))
  1058         return NS_ERROR_XPC_UNEXPECTED;
  1060     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
  1061     if (!principal) {
  1062         nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(prinOrSop);
  1063         if (sop) {
  1064             principal = sop->GetPrincipal();
  1065         } else {
  1066             principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
  1067             MOZ_ASSERT(NS_FAILED(rv) || principal, "Bad return from do_CreateInstance");
  1069             if (!principal || NS_FAILED(rv)) {
  1070                 if (NS_SUCCEEDED(rv)) {
  1071                     rv = NS_ERROR_FAILURE;
  1074                 return rv;
  1077         MOZ_ASSERT(principal);
  1080     JS::CompartmentOptions compartmentOptions;
  1081     if (options.sameZoneAs)
  1082         compartmentOptions.setSameZoneAs(js::UncheckedUnwrap(options.sameZoneAs));
  1083     else
  1084         compartmentOptions.setZone(JS::SystemZone);
  1086     compartmentOptions.setInvisibleToDebugger(options.invisibleToDebugger)
  1087                       .setDiscardSource(options.discardSource)
  1088                       .setTrace(TraceXPCGlobal);
  1090     RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, &SandboxClass,
  1091                                                      principal, compartmentOptions));
  1092     if (!sandbox)
  1093         return NS_ERROR_FAILURE;
  1095     // Set up the wantXrays flag, which indicates whether xrays are desired even
  1096     // for same-origin access.
  1097     //
  1098     // This flag has historically been ignored for chrome sandboxes due to
  1099     // quirks in the wrapping implementation that have now been removed. Indeed,
  1100     // same-origin Xrays for chrome->chrome access seems a bit superfluous.
  1101     // Arguably we should just flip the default for chrome and still honor the
  1102     // flag, but such a change would break code in subtle ways for minimal
  1103     // benefit. So we just switch it off here.
  1104     xpc::GetCompartmentPrivate(sandbox)->wantXrays =
  1105       AccessCheck::isChrome(sandbox) ? false : options.wantXrays;
  1108         JSAutoCompartment ac(cx, sandbox);
  1110         if (options.proto) {
  1111             bool ok = JS_WrapObject(cx, &options.proto);
  1112             if (!ok)
  1113                 return NS_ERROR_XPC_UNEXPECTED;
  1115             if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) {
  1116                 RootedValue v(cx, ObjectValue(*options.proto));
  1117                 if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v))
  1118                     return NS_ERROR_FAILURE;
  1119                 options.proto = &v.toObject();
  1122             // Now check what sort of thing we've got in |proto|
  1123             JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false);
  1124             const js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto);
  1125             if (IS_WN_CLASS(unwrappedClass) ||
  1126                 mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) {
  1127                 // Wrap it up in a proxy that will do the right thing in terms
  1128                 // of this-binding for methods.
  1129                 RootedValue priv(cx, ObjectValue(*options.proto));
  1130                 options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler,
  1131                                                    priv, nullptr, sandbox);
  1132                 if (!options.proto)
  1133                     return NS_ERROR_OUT_OF_MEMORY;
  1136             ok = JS_SetPrototype(cx, sandbox, options.proto);
  1137             if (!ok)
  1138                 return NS_ERROR_XPC_UNEXPECTED;
  1141         nsCOMPtr<nsIScriptObjectPrincipal> sbp =
  1142             new SandboxPrivate(principal, sandbox);
  1144         // Pass on ownership of sbp to |sandbox|.
  1145         JS_SetPrivate(sandbox, sbp.forget().take());
  1147         bool allowComponents = nsContentUtils::IsSystemPrincipal(principal) ||
  1148                                nsContentUtils::IsExpandedPrincipal(principal);
  1149         if (options.wantComponents && allowComponents &&
  1150             !GetObjectScope(sandbox)->AttachComponentsObject(cx))
  1151             return NS_ERROR_XPC_UNEXPECTED;
  1153         if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox))
  1154             return NS_ERROR_XPC_UNEXPECTED;
  1156         if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions))
  1157             return NS_ERROR_XPC_UNEXPECTED;
  1159         if (options.wantExportHelpers &&
  1160             (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) ||
  1161              !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0) ||
  1162              !JS_DefineFunction(cx, sandbox, "createObjectIn", CreateObjectIn, 2, 0) ||
  1163              !JS_DefineFunction(cx, sandbox, "cloneInto", CloneInto, 3, 0) ||
  1164              !JS_DefineFunction(cx, sandbox, "isProxy", IsProxy, 1, 0)))
  1165             return NS_ERROR_XPC_UNEXPECTED;
  1167         if (!options.globalProperties.Define(cx, sandbox))
  1168             return NS_ERROR_XPC_UNEXPECTED;
  1172     // We have this crazy behavior where wantXrays=false also implies that the
  1173     // returned sandbox is implicitly waived. We've stopped advertising it, but
  1174     // keep supporting it for now.
  1175     vp.setObject(*sandbox);
  1176     if (options.wantXrays && !JS_WrapValue(cx, vp))
  1177         return NS_ERROR_UNEXPECTED;
  1178     if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp))
  1179         return NS_ERROR_UNEXPECTED;
  1181     // Set the location information for the new global, so that tools like
  1182     // about:memory may use that information
  1183     xpc::SetLocationForGlobal(sandbox, options.sandboxName);
  1185     xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
  1187     JS_FireOnNewGlobalObject(cx, sandbox);
  1189     return NS_OK;
  1192 /* bool call(in nsIXPConnectWrappedNative wrapper,
  1193  *           in JSContextPtr cx,
  1194  *           in JSObjectPtr obj,
  1195  *           in uint32_t argc,
  1196  *           in JSValPtr argv,
  1197  *           in JSValPtr vp);
  1198  */
  1199 NS_IMETHODIMP
  1200 nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
  1201                                     JSObject *objArg, const CallArgs &args, bool *_retval)
  1203     RootedObject obj(cx, objArg);
  1204     return CallOrConstruct(wrapper, cx, obj, args, _retval);
  1207 /* bool construct(in nsIXPConnectWrappedNative wrapper,
  1208  *                in JSContextPtr cx,
  1209  *                in JSObjectPtr obj,
  1210  *                in uint32_t argc,
  1211  *                in JSValPtr argv,
  1212  *                in JSValPtr vp);
  1213  */
  1214 NS_IMETHODIMP
  1215 nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
  1216                                          JSObject *objArg, const CallArgs &args, bool *_retval)
  1218     RootedObject obj(cx, objArg);
  1219     return CallOrConstruct(wrapper, cx, obj, args, _retval);
  1222 /*
  1223  * For sandbox constructor the first argument can be a URI string in which case
  1224  * we use the related Codebase Principal for the sandbox.
  1225  */
  1226 bool
  1227 ParsePrincipal(JSContext *cx, HandleString codebase, nsIPrincipal **principal)
  1229     MOZ_ASSERT(principal);
  1230     MOZ_ASSERT(codebase);
  1231     nsCOMPtr<nsIURI> uri;
  1232     nsDependentJSString codebaseStr;
  1233     NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), false);
  1234     nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr);
  1235     if (NS_FAILED(rv)) {
  1236         JS_ReportError(cx, "Creating URI from string failed");
  1237         return false;
  1240     nsCOMPtr<nsIScriptSecurityManager> secman =
  1241         do_GetService(kScriptSecurityManagerContractID);
  1242     NS_ENSURE_TRUE(secman, false);
  1244     // We could allow passing in the app-id and browser-element info to the
  1245     // sandbox constructor. But creating a sandbox based on a string is a
  1246     // deprecated API so no need to add features to it.
  1247     rv = secman->GetNoAppCodebasePrincipal(uri, principal);
  1248     if (NS_FAILED(rv) || !*principal) {
  1249         JS_ReportError(cx, "Creating Principal from URI failed");
  1250         return false;
  1252     return true;
  1255 /*
  1256  * For sandbox constructor the first argument can be a principal object or
  1257  * a script object principal (Document, Window).
  1258  */
  1259 static bool
  1260 GetPrincipalOrSOP(JSContext *cx, HandleObject from, nsISupports **out)
  1262     MOZ_ASSERT(out);
  1263     *out = nullptr;
  1265     nsXPConnect* xpc = nsXPConnect::XPConnect();
  1266     nsISupports* native = xpc->GetNativeOfWrapper(cx, from);
  1268     if (nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native)) {
  1269         sop.forget(out);
  1270         return true;
  1273     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(native);
  1274     principal.forget(out);
  1275     NS_ENSURE_TRUE(*out, false);
  1277     return true;
  1280 /*
  1281  * The first parameter of the sandbox constructor might be an array of principals, either in string
  1282  * format or actual objects (see GetPrincipalOrSOP)
  1283  */
  1284 static bool
  1285 GetExpandedPrincipal(JSContext *cx, HandleObject arrayObj, nsIExpandedPrincipal **out)
  1287     MOZ_ASSERT(out);
  1288     uint32_t length;
  1290     if (!JS_IsArrayObject(cx, arrayObj) ||
  1291         !JS_GetArrayLength(cx, arrayObj, &length) ||
  1292         !length)
  1294         // We need a whitelist of principals or uri strings to create an
  1295         // expanded principal, if we got an empty array or something else
  1296         // report error.
  1297         JS_ReportError(cx, "Expected an array of URI strings");
  1298         return false;
  1301     nsTArray< nsCOMPtr<nsIPrincipal> > allowedDomains(length);
  1302     allowedDomains.SetLength(length);
  1303     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
  1304     NS_ENSURE_TRUE(ssm, false);
  1306     for (uint32_t i = 0; i < length; ++i) {
  1307         RootedValue allowed(cx);
  1308         if (!JS_GetElement(cx, arrayObj, i, &allowed))
  1309             return false;
  1311         nsresult rv;
  1312         nsCOMPtr<nsIPrincipal> principal;
  1313         if (allowed.isString()) {
  1314             // In case of string let's try to fetch a codebase principal from it.
  1315             RootedString str(cx, allowed.toString());
  1316             if (!ParsePrincipal(cx, str, getter_AddRefs(principal)))
  1317                 return false;
  1319         } else if (allowed.isObject()) {
  1320             // In case of object let's see if it's a Principal or a ScriptObjectPrincipal.
  1321             nsCOMPtr<nsISupports> prinOrSop;
  1322             RootedObject obj(cx, &allowed.toObject());
  1323             if (!GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)))
  1324                 return false;
  1326             nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(prinOrSop));
  1327             principal = do_QueryInterface(prinOrSop);
  1328             if (sop)
  1329                 principal = sop->GetPrincipal();
  1331         NS_ENSURE_TRUE(principal, false);
  1333         // We do not allow ExpandedPrincipals to contain any system principals.
  1334         bool isSystem;
  1335         rv = ssm->IsSystemPrincipal(principal, &isSystem);
  1336         NS_ENSURE_SUCCESS(rv, false);
  1337         if (isSystem) {
  1338             JS_ReportError(cx, "System principal is not allowed in an expanded principal");
  1339             return false;
  1341         allowedDomains[i] = principal;
  1344   nsCOMPtr<nsIExpandedPrincipal> result = new nsExpandedPrincipal(allowedDomains);
  1345   result.forget(out);
  1346   return true;
  1349 /*
  1350  * Helper that tries to get a property from the options object.
  1351  */
  1352 bool
  1353 OptionsBase::ParseValue(const char *name, MutableHandleValue prop, bool *aFound)
  1355     bool found;
  1356     bool ok = JS_HasProperty(mCx, mObject, name, &found);
  1357     NS_ENSURE_TRUE(ok, false);
  1359     if (aFound)
  1360         *aFound = found;
  1362     if (!found)
  1363         return true;
  1365     return JS_GetProperty(mCx, mObject, name, prop);
  1368 /*
  1369  * Helper that tries to get a boolean property from the options object.
  1370  */
  1371 bool
  1372 OptionsBase::ParseBoolean(const char *name, bool *prop)
  1374     MOZ_ASSERT(prop);
  1375     RootedValue value(mCx);
  1376     bool found;
  1377     bool ok = ParseValue(name, &value, &found);
  1378     NS_ENSURE_TRUE(ok, false);
  1380     if (!found)
  1381         return true;
  1383     if (!value.isBoolean()) {
  1384         JS_ReportError(mCx, "Expected a boolean value for property %s", name);
  1385         return false;
  1388     *prop = value.toBoolean();
  1389     return true;
  1392 /*
  1393  * Helper that tries to get an object property from the options object.
  1394  */
  1395 bool
  1396 OptionsBase::ParseObject(const char *name, MutableHandleObject prop)
  1398     RootedValue value(mCx);
  1399     bool found;
  1400     bool ok = ParseValue(name, &value, &found);
  1401     NS_ENSURE_TRUE(ok, false);
  1403     if (!found)
  1404         return true;
  1406     if (!value.isObject()) {
  1407         JS_ReportError(mCx, "Expected an object value for property %s", name);
  1408         return false;
  1410     prop.set(&value.toObject());
  1411     return true;
  1414 /*
  1415  * Helper that tries to get a string property from the options object.
  1416  */
  1417 bool
  1418 OptionsBase::ParseString(const char *name, nsCString &prop)
  1420     RootedValue value(mCx);
  1421     bool found;
  1422     bool ok = ParseValue(name, &value, &found);
  1423     NS_ENSURE_TRUE(ok, false);
  1425     if (!found)
  1426         return true;
  1428     if (!value.isString()) {
  1429         JS_ReportError(mCx, "Expected a string value for property %s", name);
  1430         return false;
  1433     char *tmp = JS_EncodeString(mCx, value.toString());
  1434     NS_ENSURE_TRUE(tmp, false);
  1435     prop.Adopt(tmp, strlen(tmp));
  1436     return true;
  1439 /*
  1440  * Helper that tries to get a string property from the options object.
  1441  */
  1442 bool
  1443 OptionsBase::ParseString(const char *name, nsString &prop)
  1445     RootedValue value(mCx);
  1446     bool found;
  1447     bool ok = ParseValue(name, &value, &found);
  1448     NS_ENSURE_TRUE(ok, false);
  1450     if (!found)
  1451         return true;
  1453     if (!value.isString()) {
  1454         JS_ReportError(mCx, "Expected a string value for property %s", name);
  1455         return false;
  1458     nsDependentJSString strVal;
  1459     strVal.init(mCx, value.toString());
  1460     prop = strVal;
  1461     return true;
  1464 /*
  1465  * Helper that tries to get jsid property from the options object.
  1466  */
  1467 bool
  1468 OptionsBase::ParseId(const char *name, MutableHandleId prop)
  1470     RootedValue value(mCx);
  1471     bool found;
  1472     bool ok = ParseValue(name, &value, &found);
  1473     NS_ENSURE_TRUE(ok, false);
  1475     if (!found)
  1476         return true;
  1478     return JS_ValueToId(mCx, value, prop);
  1481 /*
  1482  * Helper that tries to get a list of DOM constructors and other helpers from the options object.
  1483  */
  1484 bool
  1485 SandboxOptions::ParseGlobalProperties()
  1487     RootedValue value(mCx);
  1488     bool found;
  1489     bool ok = ParseValue("wantGlobalProperties", &value, &found);
  1490     NS_ENSURE_TRUE(ok, false);
  1491     if (!found)
  1492         return true;
  1494     if (!value.isObject()) {
  1495         JS_ReportError(mCx, "Expected an array value for wantGlobalProperties");
  1496         return false;
  1499     RootedObject ctors(mCx, &value.toObject());
  1500     if (!JS_IsArrayObject(mCx, ctors)) {
  1501         JS_ReportError(mCx, "Expected an array value for wantGlobalProperties");
  1502         return false;
  1505     return globalProperties.Parse(mCx, ctors);
  1508 /*
  1509  * Helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options).
  1510  */
  1511 bool
  1512 SandboxOptions::Parse()
  1514     return ParseObject("sandboxPrototype", &proto) &&
  1515            ParseBoolean("wantXrays", &wantXrays) &&
  1516            ParseBoolean("wantComponents", &wantComponents) &&
  1517            ParseBoolean("wantExportHelpers", &wantExportHelpers) &&
  1518            ParseString("sandboxName", sandboxName) &&
  1519            ParseObject("sameZoneAs", &sameZoneAs) &&
  1520            ParseBoolean("invisibleToDebugger", &invisibleToDebugger) &&
  1521            ParseBoolean("discardSource", &discardSource) &&
  1522            ParseGlobalProperties() &&
  1523            ParseValue("metadata", &metadata);
  1526 static nsresult
  1527 AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName)
  1529     // Use a default name when the caller did not provide a sandboxName.
  1530     if (sandboxName.IsEmpty())
  1531         sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]");
  1533     nsXPConnect* xpc = nsXPConnect::XPConnect();
  1534     // Get the xpconnect native call context.
  1535     nsAXPCNativeCallContext *cc = nullptr;
  1536     xpc->GetCurrentNativeCallContext(&cc);
  1537     NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG);
  1539     // Get the current source info from xpc.
  1540     nsCOMPtr<nsIStackFrame> frame;
  1541     xpc->GetCurrentJSStack(getter_AddRefs(frame));
  1543     // Append the caller's location information.
  1544     if (frame) {
  1545         nsString location;
  1546         int32_t lineNumber = 0;
  1547         frame->GetFilename(location);
  1548         frame->GetLineNumber(&lineNumber);
  1550         sandboxName.AppendLiteral(" (from: ");
  1551         sandboxName.Append(NS_ConvertUTF16toUTF8(location));
  1552         sandboxName.AppendLiteral(":");
  1553         sandboxName.AppendInt(lineNumber);
  1554         sandboxName.AppendLiteral(")");
  1557     return NS_OK;
  1560 // static
  1561 nsresult
  1562 nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrapper,
  1563                                                JSContext *cx, HandleObject obj,
  1564                                                const CallArgs &args, bool *_retval)
  1566     if (args.length() < 1)
  1567         return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval);
  1569     nsresult rv;
  1570     bool ok = false;
  1572     // Make sure to set up principals on the sandbox before initing classes.
  1573     nsCOMPtr<nsIPrincipal> principal;
  1574     nsCOMPtr<nsIExpandedPrincipal> expanded;
  1575     nsCOMPtr<nsISupports> prinOrSop;
  1577     if (args[0].isString()) {
  1578         RootedString str(cx, args[0].toString());
  1579         ok = ParsePrincipal(cx, str, getter_AddRefs(principal));
  1580         prinOrSop = principal;
  1581     } else if (args[0].isObject()) {
  1582         RootedObject obj(cx, &args[0].toObject());
  1583         if (JS_IsArrayObject(cx, obj)) {
  1584             ok = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded));
  1585             prinOrSop = expanded;
  1586         } else {
  1587             ok = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
  1591     if (!ok)
  1592         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
  1594     bool calledWithOptions = args.length() > 1;
  1595     if (calledWithOptions && !args[1].isObject())
  1596         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
  1598     RootedObject optionsObject(cx, calledWithOptions ? &args[1].toObject()
  1599                                                      : nullptr);
  1601     SandboxOptions options(cx, optionsObject);
  1602     if (calledWithOptions && !options.Parse())
  1603         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
  1605     if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName)))
  1606         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
  1608     if (options.metadata.isNullOrUndefined()) {
  1609         // If the caller is running in a sandbox, inherit.
  1610         RootedObject callerGlobal(cx, CurrentGlobalOrNull(cx));
  1611         if (IsSandbox(callerGlobal)) {
  1612             rv = GetSandboxMetadata(cx, callerGlobal, &options.metadata);
  1613             if (NS_WARN_IF(NS_FAILED(rv)))
  1614                 return rv;
  1618     rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options);
  1620     if (NS_FAILED(rv))
  1621         return ThrowAndFail(rv, cx, _retval);
  1623     *_retval = true;
  1624     return NS_OK;
  1627 class ContextHolder : public nsIScriptObjectPrincipal
  1629 public:
  1630     ContextHolder(JSContext *aOuterCx, HandleObject aSandbox, nsIPrincipal *aPrincipal);
  1631     virtual ~ContextHolder();
  1633     JSContext * GetJSContext()
  1635         return mJSContext;
  1638     nsIPrincipal * GetPrincipal() { return mPrincipal; }
  1640     NS_DECL_ISUPPORTS
  1642 private:
  1643     JSContext* mJSContext;
  1644     nsCOMPtr<nsIPrincipal> mPrincipal;
  1645 };
  1647 NS_IMPL_ISUPPORTS(ContextHolder, nsIScriptObjectPrincipal)
  1649 ContextHolder::ContextHolder(JSContext *aOuterCx,
  1650                              HandleObject aSandbox,
  1651                              nsIPrincipal *aPrincipal)
  1652     : mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024)),
  1653       mPrincipal(aPrincipal)
  1655     if (mJSContext) {
  1656         bool isChrome;
  1657         DebugOnly<nsresult> rv = XPCWrapper::GetSecurityManager()->
  1658                                    IsSystemPrincipal(mPrincipal, &isChrome);
  1659         MOZ_ASSERT(NS_SUCCEEDED(rv));
  1661         JS::ContextOptionsRef(mJSContext).setDontReportUncaught(true)
  1662                                          .setPrivateIsNSISupports(true);
  1663         js::SetDefaultObjectForContext(mJSContext, aSandbox);
  1664         JS_SetContextPrivate(mJSContext, this);
  1668 ContextHolder::~ContextHolder()
  1670     if (mJSContext)
  1671         JS_DestroyContextNoGC(mJSContext);
  1674 nsresult
  1675 xpc::EvalInSandbox(JSContext *cx, HandleObject sandboxArg, const nsAString& source,
  1676                    const nsACString& filename, int32_t lineNo,
  1677                    JSVersion jsVersion, bool returnStringOnly, MutableHandleValue rval)
  1679     JS_AbortIfWrongThread(JS_GetRuntime(cx));
  1680     rval.set(UndefinedValue());
  1682     bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg);
  1683     RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg));
  1684     if (!sandbox || js::GetObjectJSClass(sandbox) != &SandboxClass) {
  1685         return NS_ERROR_INVALID_ARG;
  1688     nsIScriptObjectPrincipal *sop =
  1689         (nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox);
  1690     MOZ_ASSERT(sop, "Invalid sandbox passed");
  1691     nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal();
  1692     NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
  1694     nsAutoCString filenameBuf;
  1695     if (!filename.IsVoid()) {
  1696         filenameBuf.Assign(filename);
  1697     } else {
  1698         // Default to the spec of the principal.
  1699         nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf);
  1700         lineNo = 1;
  1703     // We create a separate cx to do the sandbox evaluation. Scope it.
  1704     RootedValue v(cx, UndefinedValue());
  1705     RootedValue exn(cx, UndefinedValue());
  1706     bool ok = true;
  1708         // Make a special cx for the sandbox and push it.
  1709         // NB: As soon as the RefPtr goes away, the cx goes away. So declare
  1710         // it first so that it disappears last.
  1711         nsRefPtr<ContextHolder> sandcxHolder = new ContextHolder(cx, sandbox, prin);
  1712         JSContext *sandcx = sandcxHolder->GetJSContext();
  1713         if (!sandcx) {
  1714             JS_ReportError(cx, "Can't prepare context for evalInSandbox");
  1715             return NS_ERROR_OUT_OF_MEMORY;
  1717         nsCxPusher pusher;
  1718         pusher.Push(sandcx);
  1719         JSAutoCompartment ac(sandcx, sandbox);
  1721         JS::CompileOptions options(sandcx);
  1722         options.setFileAndLine(filenameBuf.get(), lineNo);
  1723         if (jsVersion != JSVERSION_DEFAULT)
  1724                options.setVersion(jsVersion);
  1725         JS::RootedObject rootedSandbox(sandcx, sandbox);
  1726         ok = JS::Evaluate(sandcx, rootedSandbox, options,
  1727                           PromiseFlatString(source).get(), source.Length(), &v);
  1728         if (ok && returnStringOnly && !v.isUndefined()) {
  1729             JSString *str = ToString(sandcx, v);
  1730             ok = !!str;
  1731             v = ok ? JS::StringValue(str) : JS::UndefinedValue();
  1734         // If the sandbox threw an exception, grab it off the context.
  1735         if (JS_GetPendingException(sandcx, &exn)) {
  1736             MOZ_ASSERT(!ok);
  1737             JS_ClearPendingException(sandcx);
  1738             if (returnStringOnly) {
  1739                 // The caller asked for strings only, convert the
  1740                 // exception into a string.
  1741                 JSString *str = ToString(sandcx, exn);
  1742                 exn = str ? JS::StringValue(str) : JS::UndefinedValue();
  1747     //
  1748     // Alright, we're back on the caller's cx. If an error occured, try to
  1749     // wrap and set the exception. Otherwise, wrap the return value.
  1750     //
  1752     if (!ok) {
  1753         // If we end up without an exception, it was probably due to OOM along
  1754         // the way, in which case we thow. Otherwise, wrap it.
  1755         if (exn.isUndefined() || !JS_WrapValue(cx, &exn))
  1756             return NS_ERROR_OUT_OF_MEMORY;
  1758         // Set the exception on our caller's cx.
  1759         JS_SetPendingException(cx, exn);
  1760         return NS_ERROR_FAILURE;
  1763     // Transitively apply Xray waivers if |sb| was waived.
  1764     if (waiveXray) {
  1765         ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
  1766     } else {
  1767         ok = JS_WrapValue(cx, &v);
  1769     NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
  1771     // Whew!
  1772     rval.set(v);
  1773     return NS_OK;
  1776 static bool
  1777 NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
  1779     CallArgs args = CallArgsFromVp(argc, vp);
  1781     RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
  1782     MOZ_ASSERT(v.isObject(), "weird function");
  1784     RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
  1785     if (!obj) {
  1786         return false;
  1788     return JS_CallFunctionValue(cx, obj, v, args, args.rval());
  1791 /*
  1792  * Forwards the call to the exported function. Clones all the non reflectors, ignores
  1793  * the |this| argument.
  1794  */
  1795 static bool
  1796 CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
  1798     CallArgs args = CallArgsFromVp(argc, vp);
  1800     RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
  1801     NS_ASSERTION(v.isObject(), "weird function");
  1802     RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject()));
  1804         JSAutoCompartment ac(cx, origFunObj);
  1805         // Note: only the arguments are cloned not the |this| or the |callee|.
  1806         // Function forwarder does not use those.
  1807         for (unsigned i = 0; i < args.length(); i++) {
  1808             if (!CloneNonReflectors(cx, args[i])) {
  1809                 return false;
  1813         // JS API does not support any JSObject to JSFunction conversion,
  1814         // so let's use JS_CallFunctionValue instead.
  1815         RootedValue functionVal(cx, ObjectValue(*origFunObj));
  1817         if (!JS_CallFunctionValue(cx, JS::NullPtr(), functionVal, args, args.rval()))
  1818             return false;
  1821     // Return value must be wrapped.
  1822     return JS_WrapValue(cx, args.rval());
  1825 bool
  1826 xpc::NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone,
  1827                           MutableHandleValue vp)
  1829     JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder :
  1830                                                                     NonCloningFunctionForwarder,
  1831                                                                     0,0, JS::CurrentGlobalOrNull(cx), id);
  1833     if (!fun)
  1834         return false;
  1836     JSObject *funobj = JS_GetFunctionObject(fun);
  1837     js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable));
  1838     vp.setObject(*funobj);
  1839     return true;
  1842 bool
  1843 xpc::NewFunctionForwarder(JSContext *cx, HandleObject callable, bool doclone,
  1844                           MutableHandleValue vp)
  1846     RootedId emptyId(cx);
  1847     RootedValue emptyStringValue(cx, JS_GetEmptyStringValue(cx));
  1848     if (!JS_ValueToId(cx, emptyStringValue, &emptyId))
  1849         return false;
  1851     return NewFunctionForwarder(cx, emptyId, callable, doclone, vp);
  1855 nsresult
  1856 xpc::GetSandboxMetadata(JSContext *cx, HandleObject sandbox, MutableHandleValue rval)
  1858     MOZ_ASSERT(NS_IsMainThread());
  1859     MOZ_ASSERT(IsSandbox(sandbox));
  1861     RootedValue metadata(cx);
  1863       JSAutoCompartment ac(cx, sandbox);
  1864       metadata = JS_GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT);
  1867     if (!JS_WrapValue(cx, &metadata))
  1868         return NS_ERROR_UNEXPECTED;
  1870     rval.set(metadata);
  1871     return NS_OK;
  1874 nsresult
  1875 xpc::SetSandboxMetadata(JSContext *cx, HandleObject sandbox, HandleValue metadataArg)
  1877     MOZ_ASSERT(NS_IsMainThread());
  1878     MOZ_ASSERT(IsSandbox(sandbox));
  1880     RootedValue metadata(cx);
  1882     JSAutoCompartment ac(cx, sandbox);
  1883     if (!JS_StructuredClone(cx, metadataArg, &metadata, nullptr, nullptr))
  1884         return NS_ERROR_UNEXPECTED;
  1886     JS_SetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT, metadata);
  1888     return NS_OK;

mercurial