tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Sandbox.cpp (78799B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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/Array.h"             // JS::GetArrayLength, JS::IsArrayObject
     14 #include "js/CallAndConstruct.h"  // JS::Call, JS::IsCallable
     15 #include "js/CharacterEncoding.h"
     16 #include "js/CompilationAndEvaluation.h"
     17 #include "js/Object.h"  // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot
     18 #include "js/PropertyAndElement.h"  // JS_DefineFunction, JS_DefineFunctions, JS_DefineProperty, JS_GetElement, JS_GetProperty, JS_HasProperty, JS_SetProperty, JS_SetPropertyById
     19 #include "js/PropertyDescriptor.h"  // JS::PropertyDescriptor, JS_GetOwnPropertyDescriptorById, JS_GetPropertyDescriptorById
     20 #include "js/PropertySpec.h"
     21 #include "js/Proxy.h"
     22 #include "js/SourceText.h"
     23 #include "js/StructuredClone.h"
     24 #include "nsContentUtils.h"
     25 #include "nsGlobalWindowInner.h"
     26 #include "nsIException.h"  // for nsIStackFrame
     27 #include "nsIScriptContext.h"
     28 #include "nsIScriptObjectPrincipal.h"
     29 #include "nsIURI.h"
     30 #include "nsJSUtils.h"
     31 #include "nsNetUtil.h"
     32 #include "ExpandedPrincipal.h"
     33 #include "WrapperFactory.h"
     34 #include "xpcprivate.h"
     35 #include "xpc_make_class.h"
     36 #include "XPCWrapper.h"
     37 #include "Crypto.h"
     38 #include "mozilla/Result.h"
     39 #include "mozilla/dom/AbortControllerBinding.h"
     40 #include "mozilla/dom/AutoEntryScript.h"
     41 #include "mozilla/dom/BindingCallContext.h"
     42 #include "mozilla/dom/BindingUtils.h"
     43 #include "mozilla/dom/BlobBinding.h"
     44 #include "mozilla/dom/cache/CacheStorage.h"
     45 #include "mozilla/dom/CSSBinding.h"
     46 #include "mozilla/dom/CSSPositionTryDescriptorsBinding.h"
     47 #include "mozilla/dom/CSSRuleBinding.h"
     48 #include "mozilla/dom/DirectoryBinding.h"
     49 #include "mozilla/dom/DocumentBinding.h"
     50 #include "mozilla/dom/DOMExceptionBinding.h"
     51 #include "mozilla/dom/DOMParserBinding.h"
     52 #include "mozilla/dom/DOMTokenListBinding.h"
     53 #include "mozilla/dom/ElementBinding.h"
     54 #include "mozilla/dom/ElementInternalsBinding.h"
     55 #include "mozilla/dom/EventBinding.h"
     56 #include "mozilla/dom/Exceptions.h"
     57 #include "mozilla/dom/IndexedDatabaseManager.h"
     58 #include "mozilla/dom/Fetch.h"
     59 #include "mozilla/dom/FileBinding.h"
     60 #include "mozilla/dom/HeadersBinding.h"
     61 #include "mozilla/dom/IOUtilsBinding.h"
     62 #include "mozilla/dom/InspectorUtilsBinding.h"
     63 #include "mozilla/dom/LockManager.h"
     64 #include "mozilla/dom/MessageChannelBinding.h"
     65 #include "mozilla/dom/MessagePortBinding.h"
     66 #include "mozilla/dom/MIDIInputMapBinding.h"
     67 #include "mozilla/dom/MIDIOutputMapBinding.h"
     68 #include "mozilla/dom/ModuleLoader.h"
     69 #include "mozilla/dom/NodeBinding.h"
     70 #include "mozilla/dom/NodeFilterBinding.h"
     71 #include "mozilla/dom/PathUtilsBinding.h"
     72 #include "mozilla/dom/PerformanceBinding.h"
     73 #include "mozilla/dom/PromiseBinding.h"
     74 #include "mozilla/dom/PromiseDebuggingBinding.h"
     75 #include "mozilla/dom/RangeBinding.h"
     76 #include "mozilla/dom/RequestBinding.h"
     77 #include "mozilla/dom/ReadableStreamBinding.h"
     78 #include "mozilla/dom/ResponseBinding.h"
     79 #ifdef MOZ_WEBRTC
     80 #  include "mozilla/dom/RTCIdentityProviderRegistrar.h"
     81 #endif
     82 #include "mozilla/dom/FileReaderBinding.h"
     83 #include "mozilla/dom/ScriptLoader.h"
     84 #include "mozilla/dom/ScriptSettings.h"
     85 #include "mozilla/dom/SelectionBinding.h"
     86 #include "mozilla/dom/StorageManager.h"
     87 #include "mozilla/dom/StorageManagerBinding.h"
     88 #include "mozilla/dom/TextDecoderBinding.h"
     89 #include "mozilla/dom/TextEncoderBinding.h"
     90 #include "mozilla/dom/TrustedHTML.h"
     91 #include "mozilla/dom/TrustedScript.h"
     92 #include "mozilla/dom/TrustedScriptURL.h"
     93 #include "mozilla/dom/URLBinding.h"
     94 #include "mozilla/dom/URLSearchParamsBinding.h"
     95 #include "mozilla/dom/XMLHttpRequest.h"
     96 #include "mozilla/dom/WebSocketBinding.h"
     97 #include "mozilla/dom/WindowBinding.h"
     98 #include "mozilla/dom/XMLSerializerBinding.h"
     99 #include "mozilla/dom/FormDataBinding.h"
    100 #include "mozilla/dom/nsCSPContext.h"
    101 #include "mozilla/dom/PolicyContainer.h"
    102 #include "mozilla/ipc/BackgroundUtils.h"
    103 #include "mozilla/ipc/PBackgroundSharedTypes.h"
    104 #include "mozilla/BasePrincipal.h"
    105 #include "mozilla/DeferredFinalize.h"
    106 #include "mozilla/ExtensionPolicyService.h"
    107 #include "mozilla/Maybe.h"
    108 #include "mozilla/NullPrincipal.h"
    109 #include "mozilla/ResultExtensions.h"
    110 #include "mozilla/StaticPrefs_extensions.h"
    111 
    112 using namespace mozilla;
    113 using namespace mozilla::dom;
    114 using namespace JS;
    115 using namespace JS::loader;
    116 using namespace xpc;
    117 
    118 using mozilla::dom::DestroyProtoAndIfaceCache;
    119 using mozilla::dom::IndexedDatabaseManager;
    120 
    121 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(SandboxPrivate)
    122 
    123 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SandboxPrivate)
    124  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    125  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
    126  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
    127  NS_IMPL_CYCLE_COLLECTION_UNLINK(mModuleLoader)
    128  tmp->UnlinkObjectsInGlobal();
    129 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    130 
    131 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SandboxPrivate)
    132  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mModuleLoader)
    133  tmp->TraverseObjectsInGlobal(cb);
    134 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    135 
    136 NS_IMPL_CYCLE_COLLECTING_ADDREF(SandboxPrivate)
    137 NS_IMPL_CYCLE_COLLECTING_RELEASE(SandboxPrivate)
    138 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SandboxPrivate)
    139  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    140  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptObjectPrincipal)
    141  NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
    142  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
    143  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    144 NS_INTERFACE_MAP_END
    145 
    146 class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox,
    147                                      public nsIXPCScriptable {
    148 public:
    149  // Aren't macros nice?
    150  NS_DECL_ISUPPORTS
    151  NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX
    152  NS_DECL_NSIXPCSCRIPTABLE
    153 
    154 public:
    155  nsXPCComponents_utils_Sandbox();
    156 
    157 private:
    158  virtual ~nsXPCComponents_utils_Sandbox();
    159 
    160  static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
    161                                  JSContext* cx, HandleObject obj,
    162                                  const CallArgs& args, bool* _retval);
    163 };
    164 
    165 already_AddRefed<nsIXPCComponents_utils_Sandbox> xpc::NewSandboxConstructor() {
    166  nsCOMPtr<nsIXPCComponents_utils_Sandbox> sbConstructor =
    167      new nsXPCComponents_utils_Sandbox();
    168  return sbConstructor.forget();
    169 }
    170 
    171 static bool SandboxDump(JSContext* cx, unsigned argc, Value* vp) {
    172  if (!nsJSUtils::DumpEnabled()) {
    173    return true;
    174  }
    175 
    176  CallArgs args = CallArgsFromVp(argc, vp);
    177 
    178  if (args.length() == 0) {
    179    return true;
    180  }
    181 
    182  RootedString str(cx, ToString(cx, args[0]));
    183  if (!str) {
    184    return false;
    185  }
    186 
    187  JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
    188  char* cstr = utf8str.get();
    189  if (!cstr) {
    190    return false;
    191  }
    192 
    193 #if defined(XP_MACOSX)
    194  // Be nice and convert all \r to \n.
    195  char* c = cstr;
    196  char* cEnd = cstr + strlen(cstr);
    197  while (c < cEnd) {
    198    if (*c == '\r') {
    199      *c = '\n';
    200    }
    201    c++;
    202  }
    203 #endif
    204  MOZ_LOG(nsContentUtils::DOMDumpLog(), mozilla::LogLevel::Debug,
    205          ("[Sandbox.Dump] %s", cstr));
    206 #ifdef ANDROID
    207  __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
    208 #endif
    209  fputs(cstr, stdout);
    210  fflush(stdout);
    211  args.rval().setBoolean(true);
    212  return true;
    213 }
    214 
    215 static bool SandboxDebug(JSContext* cx, unsigned argc, Value* vp) {
    216 #ifdef DEBUG
    217  return SandboxDump(cx, argc, vp);
    218 #else
    219  return true;
    220 #endif
    221 }
    222 
    223 static bool SandboxImport(JSContext* cx, unsigned argc, Value* vp) {
    224  CallArgs args = CallArgsFromVp(argc, vp);
    225 
    226  if (args.length() < 1 || args[0].isPrimitive()) {
    227    XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
    228    return false;
    229  }
    230 
    231  RootedString funname(cx);
    232  if (args.length() > 1) {
    233    // Use the second parameter as the function name.
    234    funname = ToString(cx, args[1]);
    235    if (!funname) {
    236      return false;
    237    }
    238  } else {
    239    // NB: funobj must only be used to get the JSFunction out.
    240    RootedObject funobj(cx, &args[0].toObject());
    241    if (js::IsProxy(funobj)) {
    242      funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj);
    243    }
    244 
    245    JSAutoRealm ar(cx, funobj);
    246 
    247    RootedValue funval(cx, ObjectValue(*funobj));
    248    JS::Rooted<JSFunction*> fun(cx, JS_ValueToFunction(cx, funval));
    249    if (!fun) {
    250      XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
    251      return false;
    252    }
    253 
    254    // Use the actual function name as the name.
    255    if (!JS_GetFunctionId(cx, fun, &funname)) {
    256      return false;
    257    }
    258    if (!funname) {
    259      XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
    260      return false;
    261    }
    262  }
    263  JS_MarkCrossZoneIdValue(cx, StringValue(funname));
    264 
    265  RootedId id(cx);
    266  if (!JS_StringToId(cx, funname, &id)) {
    267    return false;
    268  }
    269 
    270  // We need to resolve the this object, because this function is used
    271  // unbound and should still work and act on the original sandbox.
    272 
    273  RootedObject thisObject(cx);
    274  if (!args.computeThis(cx, &thisObject)) {
    275    return false;
    276  }
    277 
    278  if (!JS_SetPropertyById(cx, thisObject, id, args[0])) {
    279    return false;
    280  }
    281 
    282  args.rval().setUndefined();
    283  return true;
    284 }
    285 
    286 bool xpc::SandboxCreateCrypto(JSContext* cx, JS::Handle<JSObject*> obj) {
    287  MOZ_ASSERT(JS_IsGlobalObject(obj));
    288 
    289  nsIGlobalObject* native = xpc::NativeGlobal(obj);
    290  MOZ_ASSERT(native);
    291 
    292  dom::Crypto* crypto = new dom::Crypto(native);
    293  JS::RootedObject wrapped(cx, crypto->WrapObject(cx, nullptr));
    294  return JS_DefineProperty(cx, obj, "crypto", wrapped, JSPROP_ENUMERATE);
    295 }
    296 
    297 #ifdef MOZ_WEBRTC
    298 static bool SandboxCreateRTCIdentityProvider(JSContext* cx,
    299                                             JS::HandleObject obj) {
    300  MOZ_ASSERT(JS_IsGlobalObject(obj));
    301 
    302  nsCOMPtr<nsIGlobalObject> nativeGlobal = xpc::NativeGlobal(obj);
    303  MOZ_ASSERT(nativeGlobal);
    304 
    305  dom::RTCIdentityProviderRegistrar* registrar =
    306      new dom::RTCIdentityProviderRegistrar(nativeGlobal);
    307  JS::RootedObject wrapped(cx, registrar->WrapObject(cx, nullptr));
    308  return JS_DefineProperty(cx, obj, "rtcIdentityProvider", wrapped,
    309                           JSPROP_ENUMERATE);
    310 }
    311 #endif
    312 
    313 static bool SandboxFetch(JSContext* cx, JS::HandleObject scope,
    314                         const CallArgs& args) {
    315  if (args.length() < 1) {
    316    JS_ReportErrorASCII(cx, "fetch requires at least 1 argument");
    317    return false;
    318  }
    319 
    320  BindingCallContext callCx(cx, "fetch");
    321  RequestOrUTF8String request;
    322  if (!request.Init(callCx, args[0], "Argument 1")) {
    323    return false;
    324  }
    325  RootedDictionary<dom::RequestInit> options(cx);
    326  if (!options.Init(callCx, args.hasDefined(1) ? args[1] : JS::NullHandleValue,
    327                    "Argument 2", false)) {
    328    return false;
    329  }
    330  nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(scope);
    331  if (!global) {
    332    return false;
    333  }
    334  dom::CallerType callerType = nsContentUtils::IsSystemCaller(cx)
    335                                   ? dom::CallerType::System
    336                                   : dom::CallerType::NonSystem;
    337  ErrorResult rv;
    338  RefPtr<dom::Promise> response = FetchRequest(
    339      global, Constify(request), Constify(options), callerType, rv);
    340  if (rv.MaybeSetPendingException(cx)) {
    341    return false;
    342  }
    343 
    344  args.rval().setObject(*response->PromiseObj());
    345  return true;
    346 }
    347 
    348 static bool SandboxFetchPromise(JSContext* cx, unsigned argc, Value* vp) {
    349  CallArgs args = CallArgsFromVp(argc, vp);
    350  RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
    351  if (SandboxFetch(cx, scope, args)) {
    352    return true;
    353  }
    354  return ConvertExceptionToPromise(cx, args.rval());
    355 }
    356 
    357 bool xpc::SandboxCreateFetch(JSContext* cx, JS::Handle<JSObject*> obj) {
    358  MOZ_ASSERT(JS_IsGlobalObject(obj));
    359 
    360  return JS_DefineFunction(cx, obj, "fetch", SandboxFetchPromise, 2, 0) &&
    361         Request_Binding::CreateAndDefineOnGlobal(cx) &&
    362         Response_Binding::CreateAndDefineOnGlobal(cx) &&
    363         Headers_Binding::CreateAndDefineOnGlobal(cx);
    364 }
    365 
    366 static bool SandboxCreateStorage(JSContext* cx, JS::HandleObject obj) {
    367  MOZ_ASSERT(JS_IsGlobalObject(obj));
    368 
    369  nsIGlobalObject* native = xpc::NativeGlobal(obj);
    370  MOZ_ASSERT(native);
    371 
    372  if (!StorageManager_Binding::CreateAndDefineOnGlobal(cx)) {
    373    return false;
    374  }
    375 
    376  dom::StorageManager* storageManager = new dom::StorageManager(native);
    377  JS::RootedObject wrapped(cx, storageManager->WrapObject(cx, nullptr));
    378  return JS_DefineProperty(cx, obj, "storage", wrapped, JSPROP_ENUMERATE);
    379 }
    380 
    381 static bool SandboxStructuredClone(JSContext* cx, unsigned argc, Value* vp) {
    382  CallArgs args = CallArgsFromVp(argc, vp);
    383 
    384  if (!args.requireAtLeast(cx, "structuredClone", 1)) {
    385    return false;
    386  }
    387 
    388  RootedDictionary<dom::StructuredSerializeOptions> options(cx);
    389  BindingCallContext callCx(cx, "structuredClone");
    390  if (!options.Init(cx, args.hasDefined(1) ? args[1] : JS::NullHandleValue,
    391                    "Argument 2", false)) {
    392    return false;
    393  }
    394 
    395  nsIGlobalObject* global = CurrentNativeGlobal(cx);
    396  if (!global) {
    397    JS_ReportErrorASCII(cx, "structuredClone: Missing global");
    398    return false;
    399  }
    400 
    401  JS::Rooted<JS::Value> result(cx);
    402  ErrorResult rv;
    403  nsContentUtils::StructuredClone(cx, global, args[0], options, &result, rv);
    404  if (rv.MaybeSetPendingException(cx)) {
    405    return false;
    406  }
    407 
    408  MOZ_ASSERT_IF(result.isGCThing(),
    409                !JS::GCThingIsMarkedGray(result.toGCCellPtr()));
    410  args.rval().set(result);
    411  return true;
    412 }
    413 
    414 bool xpc::SandboxCreateStructuredClone(JSContext* cx, HandleObject obj) {
    415  MOZ_ASSERT(JS_IsGlobalObject(obj));
    416 
    417  return JS_DefineFunction(cx, obj, "structuredClone", SandboxStructuredClone,
    418                           1, 0);
    419 }
    420 
    421 bool xpc::SandboxCreateLocks(JSContext* cx, JS::Handle<JSObject*> obj) {
    422  MOZ_ASSERT(JS_IsGlobalObject(obj));
    423 
    424  nsIGlobalObject* native = xpc::NativeGlobal(obj);
    425  MOZ_ASSERT(native);
    426 
    427  RefPtr<dom::LockManager> lockManager = dom::LockManager::Create(*native);
    428  JS::RootedObject wrapped(cx, lockManager->WrapObject(cx, nullptr));
    429  return JS_DefineProperty(cx, obj, "locks", wrapped, JSPROP_ENUMERATE);
    430 }
    431 
    432 static bool SandboxIsProxy(JSContext* cx, unsigned argc, Value* vp) {
    433  CallArgs args = CallArgsFromVp(argc, vp);
    434  if (args.length() < 1) {
    435    JS_ReportErrorASCII(cx, "Function requires at least 1 argument");
    436    return false;
    437  }
    438  if (!args[0].isObject()) {
    439    args.rval().setBoolean(false);
    440    return true;
    441  }
    442 
    443  RootedObject obj(cx, &args[0].toObject());
    444  // CheckedUnwrapStatic is OK here, since we only care about whether
    445  // it's a scripted proxy and the things CheckedUnwrapStatic fails on
    446  // are not.
    447  obj = js::CheckedUnwrapStatic(obj);
    448  if (!obj) {
    449    args.rval().setBoolean(false);
    450    return true;
    451  }
    452 
    453  args.rval().setBoolean(js::IsScriptedProxy(obj));
    454  return true;
    455 }
    456 
    457 /*
    458 * Expected type of the arguments and the return value:
    459 * function exportFunction(function funToExport,
    460 *                         object targetScope,
    461 *                         [optional] object options)
    462 */
    463 static bool SandboxExportFunction(JSContext* cx, unsigned argc, Value* vp) {
    464  CallArgs args = CallArgsFromVp(argc, vp);
    465  if (args.length() < 2) {
    466    JS_ReportErrorASCII(cx, "Function requires at least 2 arguments");
    467    return false;
    468  }
    469 
    470  RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
    471  return ExportFunction(cx, args[0], args[1], options, args.rval());
    472 }
    473 
    474 static bool SandboxCreateObjectIn(JSContext* cx, unsigned argc, Value* vp) {
    475  CallArgs args = CallArgsFromVp(argc, vp);
    476  if (args.length() < 1) {
    477    JS_ReportErrorASCII(cx, "Function requires at least 1 argument");
    478    return false;
    479  }
    480 
    481  RootedObject optionsObj(cx);
    482  bool calledWithOptions = args.length() > 1;
    483  if (calledWithOptions) {
    484    if (!args[1].isObject()) {
    485      JS_ReportErrorASCII(
    486          cx, "Expected the 2nd argument (options) to be an object");
    487      return false;
    488    }
    489    optionsObj = &args[1].toObject();
    490  }
    491 
    492  CreateObjectInOptions options(cx, optionsObj);
    493  if (calledWithOptions && !options.Parse()) {
    494    return false;
    495  }
    496 
    497  return xpc::CreateObjectIn(cx, args[0], options, args.rval());
    498 }
    499 
    500 static bool SandboxCloneInto(JSContext* cx, unsigned argc, Value* vp) {
    501  CallArgs args = CallArgsFromVp(argc, vp);
    502  if (args.length() < 2) {
    503    JS_ReportErrorASCII(cx, "Function requires at least 2 arguments");
    504    return false;
    505  }
    506 
    507  RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
    508  return xpc::CloneInto(cx, args[0], args[1], options, args.rval());
    509 }
    510 
    511 static void sandbox_finalize(JS::GCContext* gcx, JSObject* obj) {
    512  SandboxPrivate* priv = SandboxPrivate::GetPrivate(obj);
    513  if (!priv) {
    514    // priv can be null if CreateSandboxObject fails in the middle.
    515    return;
    516  }
    517 
    518  priv->ForgetGlobalObject(obj);
    519  DestroyProtoAndIfaceCache(obj);
    520  DeferredFinalize(static_cast<nsIScriptObjectPrincipal*>(priv));
    521 }
    522 
    523 static size_t sandbox_moved(JSObject* obj, JSObject* old) {
    524  // Note that this hook can be called before the private pointer is set. In
    525  // this case the SandboxPrivate will not exist yet, so there is nothing to
    526  // do.
    527  SandboxPrivate* priv = SandboxPrivate::GetPrivate(obj);
    528  if (!priv) {
    529    return 0;
    530  }
    531 
    532  return priv->ObjectMoved(obj, old);
    533 }
    534 
    535 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT \
    536  (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
    537 
    538 static const JSClassOps SandboxClassOps = {
    539    nullptr,                         // addProperty
    540    nullptr,                         // delProperty
    541    nullptr,                         // enumerate
    542    JS_NewEnumerateStandardClasses,  // newEnumerate
    543    JS_ResolveStandardClass,         // resolve
    544    JS_MayResolveStandardClass,      // mayResolve
    545    sandbox_finalize,                // finalize
    546    nullptr,                         // call
    547    nullptr,                         // construct
    548    JS_GlobalObjectTraceHook,        // trace
    549 };
    550 
    551 static const js::ClassExtension SandboxClassExtension = {
    552    sandbox_moved,  // objectMovedOp
    553 };
    554 
    555 static const JSClass SandboxClass = {
    556    "Sandbox",
    557    XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE,
    558    &SandboxClassOps,
    559    JS_NULL_CLASS_SPEC,
    560    &SandboxClassExtension,
    561    JS_NULL_OBJECT_OPS};
    562 
    563 static const JSFunctionSpec SandboxFunctions[] = {
    564    JS_FN("dump", SandboxDump, 1, 0), JS_FN("debug", SandboxDebug, 1, 0),
    565    JS_FN("importFunction", SandboxImport, 1, 0), JS_FS_END};
    566 
    567 bool xpc::IsSandbox(JSObject* obj) {
    568  const JSClass* clasp = JS::GetClass(obj);
    569  return clasp == &SandboxClass;
    570 }
    571 
    572 /***************************************************************************/
    573 nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox() = default;
    574 
    575 nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox() = default;
    576 
    577 NS_IMPL_QUERY_INTERFACE(nsXPCComponents_utils_Sandbox,
    578                        nsIXPCComponents_utils_Sandbox, nsIXPCScriptable)
    579 
    580 NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox)
    581 NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox)
    582 
    583 // We use the nsIXPScriptable macros to generate lots of stuff for us.
    584 #define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox
    585 #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox"
    586 #define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CALL | XPC_SCRIPTABLE_WANT_CONSTRUCT)
    587 #include "xpc_map_end.h" /* This #undef's the above. */
    588 
    589 class SandboxProxyHandler : public js::Wrapper {
    590 public:
    591  constexpr SandboxProxyHandler() : js::Wrapper(0) {}
    592 
    593  virtual bool getOwnPropertyDescriptor(
    594      JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    595      JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const override;
    596 
    597  // We just forward the high-level methods to the BaseProxyHandler versions
    598  // which implement them in terms of lower-level methods.
    599  virtual bool has(JSContext* cx, JS::Handle<JSObject*> proxy,
    600                   JS::Handle<jsid> id, bool* bp) const override;
    601  virtual bool get(JSContext* cx, JS::Handle<JSObject*> proxy,
    602                   JS::HandleValue receiver, JS::Handle<jsid> id,
    603                   JS::MutableHandle<JS::Value> vp) const override;
    604  virtual bool set(JSContext* cx, JS::Handle<JSObject*> proxy,
    605                   JS::Handle<jsid> id, JS::Handle<JS::Value> v,
    606                   JS::Handle<JS::Value> receiver,
    607                   JS::ObjectOpResult& result) const override;
    608 
    609  virtual bool hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
    610                      JS::Handle<jsid> id, bool* bp) const override;
    611  virtual bool getOwnEnumerablePropertyKeys(
    612      JSContext* cx, JS::Handle<JSObject*> proxy,
    613      JS::MutableHandleIdVector props) const override;
    614  virtual bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
    615                         JS::MutableHandleIdVector props) const override;
    616 
    617 private:
    618  // Implements the custom getPropertyDescriptor behavior. If the getOwn
    619  // argument is true we only look for "own" properties.
    620  bool getPropertyDescriptorImpl(
    621      JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    622      bool getOwn, JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const;
    623 };
    624 
    625 static const SandboxProxyHandler sandboxProxyHandler;
    626 
    627 namespace xpc {
    628 
    629 bool IsSandboxPrototypeProxy(JSObject* obj) {
    630  return js::IsProxy(obj) && js::GetProxyHandler(obj) == &sandboxProxyHandler;
    631 }
    632 
    633 bool IsWebExtensionContentScriptSandbox(JSObject* obj) {
    634  return IsSandbox(obj) &&
    635         CompartmentPrivate::Get(obj)->isWebExtensionContentScript;
    636 }
    637 
    638 }  // namespace xpc
    639 
    640 // A proxy handler that lets us wrap callables and invoke them with
    641 // the correct this object, while forwarding all other operations down
    642 // to them directly.
    643 class SandboxCallableProxyHandler : public js::Wrapper {
    644 public:
    645  constexpr SandboxCallableProxyHandler() : js::Wrapper(0) {}
    646 
    647  virtual bool call(JSContext* cx, JS::Handle<JSObject*> proxy,
    648                    const JS::CallArgs& args) const override;
    649 
    650  static const size_t SandboxProxySlot = 0;
    651 
    652  static inline JSObject* getSandboxProxy(JS::Handle<JSObject*> proxy) {
    653    return &js::GetProxyReservedSlot(proxy, SandboxProxySlot).toObject();
    654  }
    655 };
    656 
    657 static const SandboxCallableProxyHandler sandboxCallableProxyHandler;
    658 
    659 bool SandboxCallableProxyHandler::call(JSContext* cx,
    660                                       JS::Handle<JSObject*> proxy,
    661                                       const JS::CallArgs& args) const {
    662  // We forward the call to our underlying callable.
    663 
    664  // Get our SandboxProxyHandler proxy.
    665  RootedObject sandboxProxy(cx, getSandboxProxy(proxy));
    666  MOZ_ASSERT(js::IsProxy(sandboxProxy) &&
    667             js::GetProxyHandler(sandboxProxy) == &sandboxProxyHandler);
    668 
    669  // The global of the sandboxProxy is the sandbox global, and the
    670  // target object is the original proto.
    671  RootedObject sandboxGlobal(cx, JS::GetNonCCWObjectGlobal(sandboxProxy));
    672  MOZ_ASSERT(IsSandbox(sandboxGlobal));
    673 
    674  // If our this object is the sandbox global, we call with this set to the
    675  // original proto instead.
    676  //
    677  // There are two different ways we can compute |this|. If we use
    678  // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the
    679  // caller, which may be undefined if a global function was invoked without
    680  // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this|
    681  // in |vp| will be coerced to the global, which is not the correct
    682  // behavior in ES5 strict mode. And we have no way to compute strictness
    683  // here.
    684  //
    685  // The naive approach is simply to use JS_THIS_VALUE here. If |this| was
    686  // explicit, we can remap it appropriately. If it was implicit, then we
    687  // leave it as undefined, and let the callee sort it out. Since the callee
    688  // is generally in the same compartment as its global (eg the Window's
    689  // compartment, not the Sandbox's), the callee will generally compute the
    690  // correct |this|.
    691  //
    692  // However, this breaks down in the Xray case. If the sandboxPrototype
    693  // is an Xray wrapper, then we'll end up reifying the native methods in
    694  // the Sandbox's scope, which means that they'll compute |this| to be the
    695  // Sandbox, breaking old-style XPC_WN_CallMethod methods.
    696  //
    697  // Luckily, the intent of Xrays is to provide a vanilla view of a foreign
    698  // DOM interface, which means that we don't care about script-enacted
    699  // strictness in the prototype's home compartment. Indeed, since DOM
    700  // methods are always non-strict, we can just assume non-strict semantics
    701  // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately
    702  // remap |this|.
    703  bool isXray = WrapperFactory::IsXrayWrapper(sandboxProxy);
    704  RootedValue thisVal(cx, args.thisv());
    705  if (isXray) {
    706    RootedObject thisObject(cx);
    707    if (!args.computeThis(cx, &thisObject)) {
    708      return false;
    709    }
    710    thisVal.setObject(*thisObject);
    711  }
    712 
    713  if (thisVal == ObjectValue(*sandboxGlobal)) {
    714    thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy));
    715  }
    716 
    717  RootedValue func(cx, js::GetProxyPrivate(proxy));
    718  return JS::Call(cx, thisVal, func, args, args.rval());
    719 }
    720 
    721 /*
    722 * Wrap a callable such that if we're called with oldThisObj as the
    723 * "this" we will instead call it with newThisObj as the this.
    724 */
    725 static JSObject* WrapCallable(JSContext* cx, HandleObject callable,
    726                              HandleObject sandboxProtoProxy) {
    727  MOZ_ASSERT(JS::IsCallable(callable));
    728  // Our proxy is wrapping the callable.  So we need to use the
    729  // callable as the private.  We put the given sandboxProtoProxy in
    730  // an extra slot, and our call() hook depends on that.
    731  MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) &&
    732             js::GetProxyHandler(sandboxProtoProxy) == &sandboxProxyHandler);
    733 
    734  RootedValue priv(cx, ObjectValue(*callable));
    735  // We want to claim to have the same proto as our wrapped callable, so set
    736  // ourselves up with a lazy proto.
    737  js::ProxyOptions options;
    738  options.setLazyProto(true);
    739  JSObject* obj = js::NewProxyObject(cx, &sandboxCallableProxyHandler, priv,
    740                                     nullptr, options);
    741  if (obj) {
    742    js::SetProxyReservedSlot(obj, SandboxCallableProxyHandler::SandboxProxySlot,
    743                             ObjectValue(*sandboxProtoProxy));
    744  }
    745 
    746  return obj;
    747 }
    748 
    749 bool WrapAccessorFunction(JSContext* cx, MutableHandleObject accessor,
    750                          HandleObject sandboxProtoProxy) {
    751  if (!accessor) {
    752    return true;
    753  }
    754 
    755  accessor.set(WrapCallable(cx, accessor, sandboxProtoProxy));
    756  return !!accessor;
    757 }
    758 
    759 static bool IsMaybeWrappedDOMConstructor(JSObject* obj) {
    760  // We really care about the underlying object here, which might be wrapped in
    761  // cross-compartment wrappers.  CheckedUnwrapStatic is fine, since we just
    762  // care whether it's a DOM constructor.
    763  obj = js::CheckedUnwrapStatic(obj);
    764  if (!obj) {
    765    return false;
    766  }
    767 
    768  return dom::IsDOMConstructor(obj);
    769 }
    770 
    771 bool SandboxProxyHandler::getPropertyDescriptorImpl(
    772    JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    773    bool getOwn, MutableHandle<Maybe<PropertyDescriptor>> desc_) const {
    774  JS::RootedObject obj(cx, wrappedObject(proxy));
    775 
    776  MOZ_ASSERT(JS::GetCompartment(obj) == JS::GetCompartment(proxy));
    777 
    778  if (getOwn) {
    779    if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, desc_)) {
    780      return false;
    781    }
    782  } else {
    783    Rooted<JSObject*> holder(cx);
    784    if (!JS_GetPropertyDescriptorById(cx, obj, id, desc_, &holder)) {
    785      return false;
    786    }
    787  }
    788 
    789  if (desc_.isNothing()) {
    790    return true;
    791  }
    792 
    793  Rooted<PropertyDescriptor> desc(cx, *desc_);
    794 
    795  // Now fix up the getter/setter/value as needed.
    796  if (desc.hasGetter() && !WrapAccessorFunction(cx, desc.getter(), proxy)) {
    797    return false;
    798  }
    799  if (desc.hasSetter() && !WrapAccessorFunction(cx, desc.setter(), proxy)) {
    800    return false;
    801  }
    802  if (desc.hasValue() && desc.value().isObject()) {
    803    RootedObject val(cx, &desc.value().toObject());
    804    if (JS::IsCallable(val) &&
    805        // Don't wrap DOM constructors: they don't care about the "this"
    806        // they're invoked with anyway, being constructors.  And if we wrap
    807        // them here we break invariants like Node == Node and whatnot.
    808        !IsMaybeWrappedDOMConstructor(val)) {
    809      val = WrapCallable(cx, val, proxy);
    810      if (!val) {
    811        return false;
    812      }
    813      desc.value().setObject(*val);
    814    }
    815  }
    816 
    817  desc_.set(Some(desc.get()));
    818  return true;
    819 }
    820 
    821 bool SandboxProxyHandler::getOwnPropertyDescriptor(
    822    JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
    823    MutableHandle<Maybe<PropertyDescriptor>> desc) const {
    824  return getPropertyDescriptorImpl(cx, proxy, id, /* getOwn = */ true, desc);
    825 }
    826 
    827 /*
    828 * Reuse the BaseProxyHandler versions of the derived traps that are implemented
    829 * in terms of the fundamental traps.
    830 */
    831 
    832 bool SandboxProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy,
    833                              JS::Handle<jsid> id, bool* bp) const {
    834  // This uses JS_GetPropertyDescriptorById for backward compatibility.
    835  Rooted<Maybe<PropertyDescriptor>> desc(cx);
    836  if (!getPropertyDescriptorImpl(cx, proxy, id, /* getOwn = */ false, &desc)) {
    837    return false;
    838  }
    839 
    840  *bp = desc.isSome();
    841  return true;
    842 }
    843 bool SandboxProxyHandler::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
    844                                 JS::Handle<jsid> id, bool* bp) const {
    845  return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
    846 }
    847 
    848 bool SandboxProxyHandler::get(JSContext* cx, JS::Handle<JSObject*> proxy,
    849                              JS::Handle<JS::Value> receiver,
    850                              JS::Handle<jsid> id,
    851                              JS::MutableHandle<Value> vp) const {
    852  // This uses JS_GetPropertyDescriptorById for backward compatibility.
    853  Rooted<Maybe<PropertyDescriptor>> desc(cx);
    854  if (!getPropertyDescriptorImpl(cx, proxy, id, /* getOwn = */ false, &desc)) {
    855    return false;
    856  }
    857 
    858  if (desc.isNothing()) {
    859    vp.setUndefined();
    860    return true;
    861  } else {
    862    desc->assertComplete();
    863  }
    864 
    865  // Everything after here follows [[Get]] for ordinary objects.
    866  if (desc->isDataDescriptor()) {
    867    vp.set(desc->value());
    868    return true;
    869  }
    870 
    871  MOZ_ASSERT(desc->isAccessorDescriptor());
    872  RootedObject getter(cx, desc->getter());
    873 
    874  if (!getter) {
    875    vp.setUndefined();
    876    return true;
    877  }
    878 
    879  return Call(cx, receiver, getter, HandleValueArray::empty(), vp);
    880 }
    881 
    882 bool SandboxProxyHandler::set(JSContext* cx, JS::Handle<JSObject*> proxy,
    883                              JS::Handle<jsid> id, JS::Handle<Value> v,
    884                              JS::Handle<Value> receiver,
    885                              JS::ObjectOpResult& result) const {
    886  return BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
    887 }
    888 
    889 bool SandboxProxyHandler::getOwnEnumerablePropertyKeys(
    890    JSContext* cx, JS::Handle<JSObject*> proxy,
    891    MutableHandleIdVector props) const {
    892  return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
    893 }
    894 
    895 bool SandboxProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
    896                                    JS::MutableHandleIdVector props) const {
    897  return BaseProxyHandler::enumerate(cx, proxy, props);
    898 }
    899 
    900 bool xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj) {
    901  uint32_t length;
    902  bool ok = JS::GetArrayLength(cx, obj, &length);
    903  NS_ENSURE_TRUE(ok, false);
    904  for (uint32_t i = 0; i < length; i++) {
    905    RootedValue nameValue(cx);
    906    ok = JS_GetElement(cx, obj, i, &nameValue);
    907    NS_ENSURE_TRUE(ok, false);
    908    if (!nameValue.isString()) {
    909      JS_ReportErrorASCII(cx, "Property names must be strings");
    910      return false;
    911    }
    912    JSLinearString* nameStr = JS_EnsureLinearString(cx, nameValue.toString());
    913    if (!nameStr) {
    914      return false;
    915    }
    916 
    917    if (JS_LinearStringEqualsLiteral(nameStr, "AbortController")) {
    918      AbortController = true;
    919    } else if (JS_LinearStringEqualsLiteral(nameStr, "Blob")) {
    920      Blob = true;
    921    } else if (JS_LinearStringEqualsLiteral(nameStr, "ChromeUtils")) {
    922      ChromeUtils = true;
    923    } else if (JS_LinearStringEqualsLiteral(nameStr, "CSS")) {
    924      CSS = true;
    925    } else if (JS_LinearStringEqualsLiteral(nameStr,
    926                                            "CSSPositionTryDescriptors")) {
    927      CSSPositionTryDescriptors = true;
    928    } else if (JS_LinearStringEqualsLiteral(nameStr, "CSSRule")) {
    929      CSSRule = true;
    930    } else if (JS_LinearStringEqualsLiteral(nameStr, "CustomStateSet")) {
    931      CustomStateSet = true;
    932    } else if (JS_LinearStringEqualsLiteral(nameStr, "Document")) {
    933      Document = true;
    934    } else if (JS_LinearStringEqualsLiteral(nameStr, "Directory")) {
    935      Directory = true;
    936    } else if (JS_LinearStringEqualsLiteral(nameStr, "DOMException")) {
    937      DOMException = true;
    938    } else if (JS_LinearStringEqualsLiteral(nameStr, "DOMParser")) {
    939      DOMParser = true;
    940    } else if (JS_LinearStringEqualsLiteral(nameStr, "DOMTokenList")) {
    941      DOMTokenList = true;
    942    } else if (JS_LinearStringEqualsLiteral(nameStr, "Element")) {
    943      Element = true;
    944    } else if (JS_LinearStringEqualsLiteral(nameStr, "Event")) {
    945      Event = true;
    946    } else if (JS_LinearStringEqualsLiteral(nameStr, "File")) {
    947      File = true;
    948    } else if (JS_LinearStringEqualsLiteral(nameStr, "FileReader")) {
    949      FileReader = true;
    950    } else if (JS_LinearStringEqualsLiteral(nameStr, "FormData")) {
    951      FormData = true;
    952    } else if (JS_LinearStringEqualsLiteral(nameStr, "Headers")) {
    953      Headers = true;
    954    } else if (JS_LinearStringEqualsLiteral(nameStr, "IOUtils")) {
    955      IOUtils = true;
    956    } else if (JS_LinearStringEqualsLiteral(nameStr, "InspectorCSSParser")) {
    957      InspectorCSSParser = true;
    958    } else if (JS_LinearStringEqualsLiteral(nameStr, "InspectorUtils")) {
    959      InspectorUtils = true;
    960    } else if (JS_LinearStringEqualsLiteral(nameStr, "MessageChannel")) {
    961      MessageChannel = true;
    962    } else if (JS_LinearStringEqualsLiteral(nameStr, "MIDIInputMap")) {
    963      MIDIInputMap = true;
    964    } else if (JS_LinearStringEqualsLiteral(nameStr, "MIDIOutputMap")) {
    965      MIDIOutputMap = true;
    966    } else if (JS_LinearStringEqualsLiteral(nameStr, "Node")) {
    967      Node = true;
    968    } else if (JS_LinearStringEqualsLiteral(nameStr, "NodeFilter")) {
    969      NodeFilter = true;
    970    } else if (JS_LinearStringEqualsLiteral(nameStr, "PathUtils")) {
    971      PathUtils = true;
    972    } else if (JS_LinearStringEqualsLiteral(nameStr, "Performance")) {
    973      Performance = true;
    974    } else if (JS_LinearStringEqualsLiteral(nameStr, "PromiseDebugging")) {
    975      PromiseDebugging = true;
    976    } else if (JS_LinearStringEqualsLiteral(nameStr, "Range")) {
    977      Range = true;
    978    } else if (JS_LinearStringEqualsLiteral(nameStr, "Selection")) {
    979      Selection = true;
    980    } else if (JS_LinearStringEqualsLiteral(nameStr, "TextDecoder")) {
    981      TextDecoder = true;
    982    } else if (JS_LinearStringEqualsLiteral(nameStr, "TextEncoder")) {
    983      TextEncoder = true;
    984    } else if (JS_LinearStringEqualsLiteral(nameStr, "TrustedHTML")) {
    985      TrustedHTML = true;
    986    } else if (JS_LinearStringEqualsLiteral(nameStr, "TrustedScript")) {
    987      TrustedScript = true;
    988    } else if (JS_LinearStringEqualsLiteral(nameStr, "TrustedScriptURL")) {
    989      TrustedScriptURL = true;
    990    } else if (JS_LinearStringEqualsLiteral(nameStr, "URL")) {
    991      URL = true;
    992    } else if (JS_LinearStringEqualsLiteral(nameStr, "URLSearchParams")) {
    993      URLSearchParams = true;
    994    } else if (JS_LinearStringEqualsLiteral(nameStr, "XMLHttpRequest")) {
    995      XMLHttpRequest = true;
    996    } else if (JS_LinearStringEqualsLiteral(nameStr, "WebSocket")) {
    997      WebSocket = true;
    998    } else if (JS_LinearStringEqualsLiteral(nameStr, "Window")) {
    999      Window = true;
   1000    } else if (JS_LinearStringEqualsLiteral(nameStr, "XMLSerializer")) {
   1001      XMLSerializer = true;
   1002    } else if (JS_LinearStringEqualsLiteral(nameStr, "ReadableStream")) {
   1003      ReadableStream = true;
   1004    } else if (JS_LinearStringEqualsLiteral(nameStr, "atob")) {
   1005      atob = true;
   1006    } else if (JS_LinearStringEqualsLiteral(nameStr, "btoa")) {
   1007      btoa = true;
   1008    } else if (JS_LinearStringEqualsLiteral(nameStr, "caches")) {
   1009      caches = true;
   1010    } else if (JS_LinearStringEqualsLiteral(nameStr, "crypto")) {
   1011      crypto = true;
   1012    } else if (JS_LinearStringEqualsLiteral(nameStr, "fetch")) {
   1013      fetch = true;
   1014    } else if (JS_LinearStringEqualsLiteral(nameStr, "storage")) {
   1015      storage = true;
   1016    } else if (JS_LinearStringEqualsLiteral(nameStr, "structuredClone")) {
   1017      structuredClone = true;
   1018    } else if (JS_LinearStringEqualsLiteral(nameStr, "locks")) {
   1019      locks = true;
   1020    } else if (JS_LinearStringEqualsLiteral(nameStr, "indexedDB")) {
   1021      indexedDB = true;
   1022    } else if (JS_LinearStringEqualsLiteral(nameStr, "isSecureContext")) {
   1023      isSecureContext = true;
   1024 #ifdef MOZ_WEBRTC
   1025    } else if (JS_LinearStringEqualsLiteral(nameStr, "rtcIdentityProvider")) {
   1026      rtcIdentityProvider = true;
   1027 #endif
   1028    } else {
   1029      RootedString nameStr(cx, nameValue.toString());
   1030      JS::UniqueChars name = JS_EncodeStringToUTF8(cx, nameStr);
   1031      if (!name) {
   1032        return false;
   1033      }
   1034 
   1035      JS_ReportErrorUTF8(cx, "Unknown property name: %s", name.get());
   1036      return false;
   1037    }
   1038  }
   1039  return true;
   1040 }
   1041 
   1042 bool xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj) {
   1043  MOZ_ASSERT(js::GetContextCompartment(cx) == JS::GetCompartment(obj));
   1044  // Properties will be exposed to System automatically but not to Sandboxes
   1045  // if |[Exposed=System]| is specified.
   1046  // This function holds common properties not exposed automatically but able
   1047  // to be requested either in |Cu.importGlobalProperties| or
   1048  // |wantGlobalProperties| of a sandbox.
   1049 
   1050 #define DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(_iface)                     \
   1051  if ((_iface) && !dom::_iface##_Binding::CreateAndDefineOnGlobal(cx)) { \
   1052    return false;                                                        \
   1053  }
   1054 
   1055  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(AbortController)
   1056  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(ChromeUtils)
   1057  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Blob)
   1058  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(CSS)
   1059  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(CSSPositionTryDescriptors)
   1060  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(CSSRule)
   1061  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(CustomStateSet)
   1062  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Directory)
   1063  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Document)
   1064  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(DOMException)
   1065  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(DOMParser)
   1066  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(DOMTokenList)
   1067  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Element)
   1068  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Event)
   1069  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(File)
   1070  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(FileReader)
   1071  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(FormData)
   1072  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Headers)
   1073  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(IOUtils)
   1074  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(InspectorCSSParser)
   1075  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(InspectorUtils)
   1076  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(MessageChannel)
   1077  if (MessageChannel && !MessagePort_Binding::CreateAndDefineOnGlobal(cx)) {
   1078    return false;
   1079  }
   1080  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(MIDIInputMap)
   1081  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(MIDIOutputMap)
   1082  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Node)
   1083  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(NodeFilter)
   1084  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(PathUtils)
   1085  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Performance)
   1086  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(PromiseDebugging)
   1087  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Range)
   1088  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(ReadableStream)
   1089  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Selection)
   1090  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(TextDecoder)
   1091  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(TextEncoder)
   1092  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(TrustedHTML)
   1093  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(TrustedScript)
   1094  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(TrustedScriptURL)
   1095  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(URL)
   1096  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(URLSearchParams)
   1097  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(WebSocket)
   1098  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Window)
   1099  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(XMLHttpRequest)
   1100  DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(XMLSerializer)
   1101 
   1102 #undef DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE
   1103 
   1104  if (atob && !JS_DefineFunction(cx, obj, "atob", Atob, 1, 0)) return false;
   1105 
   1106  if (btoa && !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0)) return false;
   1107 
   1108  if (caches && !dom::cache::CacheStorage::DefineCachesForSandbox(cx, obj)) {
   1109    return false;
   1110  }
   1111 
   1112  if (crypto && !SandboxCreateCrypto(cx, obj)) {
   1113    return false;
   1114  }
   1115 
   1116  if (fetch && !SandboxCreateFetch(cx, obj)) {
   1117    return false;
   1118  }
   1119 
   1120  if (storage && !SandboxCreateStorage(cx, obj)) {
   1121    return false;
   1122  }
   1123 
   1124  if (structuredClone && !SandboxCreateStructuredClone(cx, obj)) {
   1125    return false;
   1126  }
   1127 
   1128  if (locks && !SandboxCreateLocks(cx, obj)) {
   1129    return false;
   1130  }
   1131 
   1132  // Note that isSecureContext here doesn't mean the context is actually secure
   1133  // - just that the caller wants the property defined
   1134  if (isSecureContext) {
   1135    bool hasSecureContext = IsSecureContextOrObjectIsFromSecureContext(cx, obj);
   1136    JS::Rooted<JS::Value> secureJsValue(cx, JS::BooleanValue(hasSecureContext));
   1137    return JS_DefineProperty(cx, obj, "isSecureContext", secureJsValue,
   1138                             JSPROP_ENUMERATE);
   1139  }
   1140 
   1141 #ifdef MOZ_WEBRTC
   1142  if (rtcIdentityProvider && !SandboxCreateRTCIdentityProvider(cx, obj)) {
   1143    return false;
   1144  }
   1145 #endif
   1146 
   1147  return true;
   1148 }
   1149 
   1150 bool xpc::GlobalProperties::DefineInXPCComponents(JSContext* cx,
   1151                                                  JS::HandleObject obj) {
   1152  if (indexedDB && !IndexedDatabaseManager::DefineIndexedDB(cx, obj))
   1153    return false;
   1154 
   1155  return Define(cx, obj);
   1156 }
   1157 
   1158 bool xpc::GlobalProperties::DefineInSandbox(JSContext* cx,
   1159                                            JS::HandleObject obj) {
   1160  MOZ_ASSERT(IsSandbox(obj));
   1161  MOZ_ASSERT(js::GetContextCompartment(cx) == JS::GetCompartment(obj));
   1162 
   1163  if (indexedDB && !(IndexedDatabaseManager::ResolveSandboxBinding(cx) &&
   1164                     IndexedDatabaseManager::DefineIndexedDB(cx, obj)))
   1165    return false;
   1166 
   1167  return Define(cx, obj);
   1168 }
   1169 
   1170 nsresult SetSandboxCSP(nsISupports* prinOrSop, const nsAString& cspString) {
   1171  nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
   1172  if (!principal) {
   1173    return NS_ERROR_INVALID_ARG;
   1174  }
   1175  auto* basePrin = BasePrincipal::Cast(principal);
   1176  if (!basePrin->Is<ExpandedPrincipal>()) {
   1177    return NS_ERROR_INVALID_ARG;
   1178  }
   1179  auto* expanded = basePrin->As<ExpandedPrincipal>();
   1180 
   1181  nsCOMPtr<nsIContentSecurityPolicy> csp;
   1182 
   1183  // The choice of self-uri (self-origin) to use in a Sandbox depends on the
   1184  // use case. For now, we default to a non-existing URL because there is no
   1185  // use case that requires 'self' to have a particular value. Consumers can
   1186  // always specify the URL explicitly instead of 'self'. Besides, the CSP
   1187  // enforcement in a Sandbox is barely implemented, except for eval()-like
   1188  // execution.
   1189  //
   1190  // moz-extension:-resources are never blocked by CSP because the protocol is
   1191  // registered with URI_IS_LOCAL_RESOURCE and subjectToCSP in nsCSPService.cpp
   1192  // therefore allows the load. This matches the CSP spec, which explicitly
   1193  // states that CSP should not interfere with addons. Because of this, we do
   1194  // not need to set selfURI to the real moz-extension:-URL, even in cases
   1195  // where we want to restrict all scripts except for moz-extension:-URLs.
   1196  nsCOMPtr<nsIURI> selfURI;
   1197  MOZ_TRY(NS_NewURI(getter_AddRefs(selfURI), "moz-extension://dummy"_ns));
   1198 
   1199 #ifdef MOZ_DEBUG
   1200  // Bug 1548468: Move CSP off ExpandedPrincipal
   1201  expanded->GetCsp(getter_AddRefs(csp));
   1202  if (csp) {
   1203    uint32_t count = 0;
   1204    csp->GetPolicyCount(&count);
   1205    if (count > 0) {
   1206      // Ensure that the policy was not already added.
   1207      nsAutoString parsedPolicyStr;
   1208      for (uint32_t i = 0; i < count; i++) {
   1209        csp->GetPolicyString(i, parsedPolicyStr);
   1210        MOZ_ASSERT(!parsedPolicyStr.Equals(cspString));
   1211      }
   1212    }
   1213  }
   1214 #endif
   1215 
   1216  // Create a clone of the expanded principal to be used for the call to
   1217  // SetRequestContextWithPrincipal (to prevent the CSP and expanded
   1218  // principal instances to keep each other alive indefinitely, see
   1219  // Bug 1741600).
   1220  //
   1221  // This may not be necessary anymore once Bug 1548468 will move CSP
   1222  // off ExpandedPrincipal.
   1223  RefPtr<ExpandedPrincipal> clonedPrincipal = ExpandedPrincipal::Create(
   1224      expanded->AllowList(), expanded->OriginAttributesRef());
   1225  MOZ_ASSERT(clonedPrincipal);
   1226 
   1227  csp = new nsCSPContext();
   1228  MOZ_TRY(
   1229      csp->SetRequestContextWithPrincipal(clonedPrincipal, selfURI, ""_ns, 0));
   1230 
   1231  MOZ_TRY(csp->AppendPolicy(cspString, false, false));
   1232 
   1233  expanded->SetCsp(csp);
   1234  return NS_OK;
   1235 }
   1236 
   1237 nsresult xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp,
   1238                                  nsISupports* prinOrSop,
   1239                                  SandboxOptions& options) {
   1240  // Create the sandbox global object
   1241  nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
   1242  nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(prinOrSop);
   1243  if (obj) {
   1244    nsGlobalWindowInner* window =
   1245        WindowOrNull(js::UncheckedUnwrap(obj->GetGlobalJSObject(), false));
   1246    // If we have a secure context window inherit from it's parent
   1247    if (window && window->IsSecureContext()) {
   1248      options.forceSecureContext = true;
   1249    }
   1250  }
   1251  if (!principal) {
   1252    nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(prinOrSop);
   1253    if (sop) {
   1254      principal = sop->GetPrincipal();
   1255    } else {
   1256      RefPtr<NullPrincipal> nullPrin =
   1257          NullPrincipal::CreateWithoutOriginAttributes();
   1258      principal = nullPrin;
   1259    }
   1260  }
   1261  MOZ_ASSERT(principal);
   1262 
   1263  JS::RealmOptions realmOptions;
   1264 
   1265  auto& creationOptions = realmOptions.creationOptions();
   1266 
   1267  bool isSystemPrincipal = principal->IsSystemPrincipal();
   1268 
   1269  if (isSystemPrincipal) {
   1270    options.forceSecureContext = true;
   1271  }
   1272 
   1273  // If we are able to see [SecureContext] API code
   1274  if (options.forceSecureContext) {
   1275    creationOptions.setSecureContext(true);
   1276  }
   1277 
   1278  xpc::SetPrefableRealmOptions(realmOptions);
   1279  if (options.sameZoneAs) {
   1280    creationOptions.setNewCompartmentInExistingZone(
   1281        js::UncheckedUnwrap(options.sameZoneAs));
   1282  } else if (options.freshZone) {
   1283    creationOptions.setNewCompartmentAndZone();
   1284  } else if (isSystemPrincipal && !options.invisibleToDebugger &&
   1285             !options.freshCompartment) {
   1286    // Use a shared system compartment for system-principal sandboxes that don't
   1287    // require invisibleToDebugger (this is a compartment property, see bug
   1288    // 1482215).
   1289    creationOptions.setExistingCompartment(xpc::PrivilegedJunkScope());
   1290  } else {
   1291    creationOptions.setNewCompartmentInSystemZone();
   1292  }
   1293 
   1294  if (options.alwaysUseFdlibm) {
   1295    creationOptions.setAlwaysUseFdlibm(true);
   1296  }
   1297 
   1298  creationOptions.setInvisibleToDebugger(options.invisibleToDebugger)
   1299      .setTrace(TraceXPCGlobal);
   1300 
   1301  realmOptions.behaviors().setDiscardSource(options.discardSource);
   1302 
   1303  if (isSystemPrincipal) {
   1304    realmOptions.behaviors().setClampAndJitterTime(false);
   1305  }
   1306 
   1307  if (obj) {
   1308    nsGlobalWindowInner* window =
   1309        WindowOrNull(js::UncheckedUnwrap(obj->GetGlobalJSObject(), false));
   1310    if (window) {
   1311      const nsCString& localeOverride =
   1312          window->GetBrowsingContext()->Top()->GetLanguageOverride();
   1313      if (!localeOverride.IsEmpty()) {
   1314        realmOptions.behaviors().setLocaleOverride(
   1315            PromiseFlatCString(localeOverride).get());
   1316      }
   1317 
   1318      const nsAString& timezoneOverride =
   1319          window->GetBrowsingContext()->Top()->GetTimezoneOverride();
   1320      if (!timezoneOverride.IsEmpty()) {
   1321        realmOptions.behaviors().setTimeZoneOverride(
   1322            NS_ConvertUTF16toUTF8(timezoneOverride).get());
   1323      }
   1324    }
   1325  }
   1326 
   1327  const JSClass* clasp = &SandboxClass;
   1328 
   1329  RootedObject sandbox(
   1330      cx, xpc::CreateGlobalObject(cx, clasp, principal, realmOptions));
   1331  if (!sandbox) {
   1332    return NS_ERROR_FAILURE;
   1333  }
   1334 
   1335  // Use exclusive expandos for non-system-principal sandboxes.
   1336  bool hasExclusiveExpandos = !isSystemPrincipal;
   1337 
   1338  // Set up the wantXrays flag, which indicates whether xrays are desired even
   1339  // for same-origin access.
   1340  //
   1341  // This flag has historically been ignored for chrome sandboxes due to
   1342  // quirks in the wrapping implementation that have now been removed. Indeed,
   1343  // same-origin Xrays for chrome->chrome access seems a bit superfluous.
   1344  // Arguably we should just flip the default for chrome and still honor the
   1345  // flag, but such a change would break code in subtle ways for minimal
   1346  // benefit. So we just switch it off here.
   1347  bool wantXrays = AccessCheck::isChrome(sandbox) ? false : options.wantXrays;
   1348 
   1349  if (creationOptions.compartmentSpecifier() ==
   1350      JS::CompartmentSpecifier::ExistingCompartment) {
   1351    // Make sure the compartment we're reusing has flags that match what we
   1352    // would set on a new compartment.
   1353    CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox);
   1354    MOZ_RELEASE_ASSERT(priv->allowWaivers == options.allowWaivers);
   1355    MOZ_RELEASE_ASSERT(priv->isWebExtensionContentScript ==
   1356                       options.isWebExtensionContentScript);
   1357    MOZ_RELEASE_ASSERT(priv->isUAWidgetCompartment == options.isUAWidgetScope);
   1358    MOZ_RELEASE_ASSERT(priv->hasExclusiveExpandos == hasExclusiveExpandos);
   1359    MOZ_RELEASE_ASSERT(priv->wantXrays == wantXrays);
   1360  } else {
   1361    CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox);
   1362    priv->allowWaivers = options.allowWaivers;
   1363    priv->isWebExtensionContentScript = options.isWebExtensionContentScript;
   1364    priv->isUAWidgetCompartment = options.isUAWidgetScope;
   1365    priv->hasExclusiveExpandos = hasExclusiveExpandos;
   1366    priv->wantXrays = wantXrays;
   1367  }
   1368 
   1369  {
   1370    JSAutoRealm ar(cx, sandbox);
   1371 
   1372    // This creates a SandboxPrivate and passes ownership of it to |sandbox|.
   1373    SandboxPrivate::Create(principal, sandbox);
   1374 
   1375    // Ensure |Object.prototype| is instantiated before prototype-
   1376    // splicing below.
   1377    if (!JS::GetRealmObjectPrototype(cx)) {
   1378      return NS_ERROR_XPC_UNEXPECTED;
   1379    }
   1380 
   1381    if (options.proto) {
   1382      bool ok = JS_WrapObject(cx, &options.proto);
   1383      if (!ok) {
   1384        return NS_ERROR_XPC_UNEXPECTED;
   1385      }
   1386 
   1387      // Now check what sort of thing we've got in |proto|, and figure out
   1388      // if we need a SandboxProxyHandler.
   1389      //
   1390      // Note that, in the case of a window, we can't require that the
   1391      // Sandbox subsumes the prototype, because we have to hold our
   1392      // reference to it via an outer window, and the window may navigate
   1393      // at any time. So we have to handle that case separately.
   1394      bool useSandboxProxy =
   1395          !!WindowOrNull(js::UncheckedUnwrap(options.proto, false));
   1396      if (!useSandboxProxy) {
   1397        // We just wrapped options.proto into the compartment of whatever Realm
   1398        // is on the cx, so use that same realm for the CheckedUnwrapDynamic
   1399        // call.
   1400        JSObject* unwrappedProto =
   1401            js::CheckedUnwrapDynamic(options.proto, cx, false);
   1402        if (!unwrappedProto) {
   1403          JS_ReportErrorASCII(cx, "Sandbox must subsume sandboxPrototype");
   1404          return NS_ERROR_INVALID_ARG;
   1405        }
   1406        const JSClass* unwrappedClass = JS::GetClass(unwrappedProto);
   1407        useSandboxProxy = unwrappedClass->isWrappedNative() ||
   1408                          mozilla::dom::IsDOMClass(unwrappedClass);
   1409      }
   1410 
   1411      if (useSandboxProxy) {
   1412        // Wrap it up in a proxy that will do the right thing in terms
   1413        // of this-binding for methods.
   1414        RootedValue priv(cx, ObjectValue(*options.proto));
   1415        options.proto =
   1416            js::NewProxyObject(cx, &sandboxProxyHandler, priv, nullptr);
   1417        if (!options.proto) {
   1418          return NS_ERROR_OUT_OF_MEMORY;
   1419        }
   1420      }
   1421 
   1422      ok = JS_SetPrototype(cx, sandbox, options.proto);
   1423      if (!ok) {
   1424        return NS_ERROR_XPC_UNEXPECTED;
   1425      }
   1426    }
   1427 
   1428    bool allowComponents = principal->IsSystemPrincipal();
   1429    if (options.wantComponents && allowComponents) {
   1430      if (!ObjectScope(sandbox)->AttachComponentsObject(cx)) {
   1431        return NS_ERROR_XPC_UNEXPECTED;
   1432      }
   1433 
   1434      if (!ObjectScope(sandbox)->AttachJSServices(cx)) {
   1435        return NS_ERROR_XPC_UNEXPECTED;
   1436      }
   1437    }
   1438 
   1439    if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox)) {
   1440      return NS_ERROR_XPC_UNEXPECTED;
   1441    }
   1442 
   1443    if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions)) {
   1444      return NS_ERROR_XPC_UNEXPECTED;
   1445    }
   1446 
   1447    if (options.wantExportHelpers &&
   1448        (!JS_DefineFunction(cx, sandbox, "exportFunction",
   1449                            SandboxExportFunction, 3, 0) ||
   1450         !JS_DefineFunction(cx, sandbox, "createObjectIn",
   1451                            SandboxCreateObjectIn, 2, 0) ||
   1452         !JS_DefineFunction(cx, sandbox, "cloneInto", SandboxCloneInto, 3, 0) ||
   1453         !JS_DefineFunction(cx, sandbox, "isProxy", SandboxIsProxy, 1, 0)))
   1454      return NS_ERROR_XPC_UNEXPECTED;
   1455 
   1456    if (!options.globalProperties.DefineInSandbox(cx, sandbox)) {
   1457      return NS_ERROR_XPC_UNEXPECTED;
   1458    }
   1459  }
   1460 
   1461  // We handle the case where the context isn't in a compartment for the
   1462  // benefit of UnprivilegedJunkScope().
   1463  vp.setObject(*sandbox);
   1464  if (js::GetContextCompartment(cx) && !JS_WrapValue(cx, vp)) {
   1465    return NS_ERROR_UNEXPECTED;
   1466  }
   1467 
   1468  // Set the location information for the new global, so that tools like
   1469  // about:memory may use that information
   1470  xpc::SetLocationForGlobal(sandbox, options.sandboxName);
   1471 
   1472  nsresult rv = xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
   1473  if (NS_WARN_IF(NS_FAILED(rv))) {
   1474    return rv;
   1475  }
   1476 
   1477  JSAutoRealm ar(cx, sandbox);
   1478  JS_FireOnNewGlobalObject(cx, sandbox);
   1479 
   1480  return NS_OK;
   1481 }
   1482 
   1483 NS_IMETHODIMP
   1484 nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative* wrapper,
   1485                                    JSContext* cx, JSObject* objArg,
   1486                                    const CallArgs& args, bool* _retval) {
   1487  RootedObject obj(cx, objArg);
   1488  return CallOrConstruct(wrapper, cx, obj, args, _retval);
   1489 }
   1490 
   1491 NS_IMETHODIMP
   1492 nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative* wrapper,
   1493                                         JSContext* cx, JSObject* objArg,
   1494                                         const CallArgs& args, bool* _retval) {
   1495  RootedObject obj(cx, objArg);
   1496  return CallOrConstruct(wrapper, cx, obj, args, _retval);
   1497 }
   1498 
   1499 /*
   1500 * For sandbox constructor the first argument can be a URI string in which case
   1501 * we use the related Content Principal for the sandbox.
   1502 */
   1503 bool ParsePrincipal(JSContext* cx, HandleString contentUrl,
   1504                    const OriginAttributes& aAttrs, nsIPrincipal** principal) {
   1505  MOZ_ASSERT(principal);
   1506  MOZ_ASSERT(contentUrl);
   1507  nsCOMPtr<nsIURI> uri;
   1508  nsAutoJSString contentStr;
   1509  NS_ENSURE_TRUE(contentStr.init(cx, contentUrl), false);
   1510  nsresult rv = NS_NewURI(getter_AddRefs(uri), contentStr);
   1511  if (NS_FAILED(rv)) {
   1512    JS_ReportErrorASCII(cx, "Creating URI from string failed");
   1513    return false;
   1514  }
   1515 
   1516  // We could allow passing in the app-id and browser-element info to the
   1517  // sandbox constructor. But creating a sandbox based on a string is a
   1518  // deprecated API so no need to add features to it.
   1519  nsCOMPtr<nsIPrincipal> prin =
   1520      BasePrincipal::CreateContentPrincipal(uri, aAttrs);
   1521  prin.forget(principal);
   1522 
   1523  if (!*principal) {
   1524    JS_ReportErrorASCII(cx, "Creating Principal from URI failed");
   1525    return false;
   1526  }
   1527  return true;
   1528 }
   1529 
   1530 /*
   1531 * For sandbox constructor the first argument can be a principal object or
   1532 * a script object principal (Document, Window).
   1533 */
   1534 static bool GetPrincipalOrSOP(JSContext* cx, HandleObject from,
   1535                              nsISupports** out) {
   1536  MOZ_ASSERT(out);
   1537  *out = nullptr;
   1538 
   1539  // We might have a Window here, so need ReflectorToISupportsDynamic
   1540  nsCOMPtr<nsISupports> native = ReflectorToISupportsDynamic(from, cx);
   1541 
   1542  if (nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native)) {
   1543    sop.forget(out);
   1544    return true;
   1545  }
   1546 
   1547  nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(native);
   1548  principal.forget(out);
   1549  NS_ENSURE_TRUE(*out, false);
   1550 
   1551  return true;
   1552 }
   1553 
   1554 /*
   1555 * The first parameter of the sandbox constructor might be an array of
   1556 * principals, either in string format or actual objects (see GetPrincipalOrSOP)
   1557 */
   1558 static bool GetExpandedPrincipal(JSContext* cx, HandleObject arrayObj,
   1559                                 const SandboxOptions& options,
   1560                                 nsIExpandedPrincipal** out) {
   1561  MOZ_ASSERT(out);
   1562  uint32_t length;
   1563 
   1564  if (!JS::GetArrayLength(cx, arrayObj, &length)) {
   1565    return false;
   1566  }
   1567  if (!length) {
   1568    // We need a whitelist of principals or uri strings to create an
   1569    // expanded principal, if we got an empty array or something else
   1570    // report error.
   1571    JS_ReportErrorASCII(cx, "Expected an array of URI strings");
   1572    return false;
   1573  }
   1574 
   1575  nsTArray<nsCOMPtr<nsIPrincipal>> allowedDomains(length);
   1576  allowedDomains.SetLength(length);
   1577 
   1578  // If an originAttributes option has been specified, we will use that as the
   1579  // OriginAttribute of all of the string arguments passed to this function.
   1580  // Otherwise, we will use the OriginAttributes of a principal or SOP object
   1581  // in the array, if any.  If no such object is present, and all we have are
   1582  // strings, then we will use a default OriginAttribute.
   1583  // Otherwise, we will use the origin attributes of the passed object(s). If
   1584  // more than one object is specified, we ensure that the OAs match.
   1585  Maybe<OriginAttributes> attrs;
   1586  if (options.originAttributes) {
   1587    attrs.emplace();
   1588    JS::RootedValue val(cx, JS::ObjectValue(*options.originAttributes));
   1589    if (!attrs->Init(cx, val)) {
   1590      // The originAttributes option, if specified, must be valid!
   1591      JS_ReportErrorASCII(cx, "Expected a valid OriginAttributes object");
   1592      return false;
   1593    }
   1594  }
   1595 
   1596  // Now we go over the array in two passes.  In the first pass, we ignore
   1597  // strings, and only process objects.  Assuming that no originAttributes
   1598  // option has been passed, if we encounter a principal or SOP object, we
   1599  // grab its OA and save it if it's the first OA encountered, otherwise
   1600  // check to make sure that it is the same as the OA found before.
   1601  // In the second pass, we ignore objects, and use the OA found in pass 0
   1602  // (or the previously computed OA if we have obtained it from the options)
   1603  // to construct content principals.
   1604  //
   1605  // The effective OA selected above will also be set as the OA of the
   1606  // expanded principal object.
   1607 
   1608  // First pass:
   1609  for (uint32_t i = 0; i < length; ++i) {
   1610    RootedValue allowed(cx);
   1611    if (!JS_GetElement(cx, arrayObj, i, &allowed)) {
   1612      return false;
   1613    }
   1614 
   1615    nsCOMPtr<nsIPrincipal> principal;
   1616    if (allowed.isObject()) {
   1617      // In case of object let's see if it's a Principal or a
   1618      // ScriptObjectPrincipal.
   1619      nsCOMPtr<nsISupports> prinOrSop;
   1620      RootedObject obj(cx, &allowed.toObject());
   1621      if (!GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop))) {
   1622        return false;
   1623      }
   1624 
   1625      nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(prinOrSop));
   1626      principal = do_QueryInterface(prinOrSop);
   1627      if (sop) {
   1628        principal = sop->GetPrincipal();
   1629      }
   1630      NS_ENSURE_TRUE(principal, false);
   1631 
   1632      if (!options.originAttributes) {
   1633        const OriginAttributes prinAttrs = principal->OriginAttributesRef();
   1634        if (attrs.isNothing()) {
   1635          attrs.emplace(prinAttrs);
   1636        } else if (prinAttrs != attrs.ref()) {
   1637          // If attrs is from a previously encountered principal in the
   1638          // array, we need to ensure that it matches the OA of the
   1639          // principal we have here.
   1640          // If attrs comes from OriginAttributes, we don't need
   1641          // this check.
   1642          return false;
   1643        }
   1644      }
   1645 
   1646      // We do not allow ExpandedPrincipals to contain any system principals.
   1647      bool isSystem = principal->IsSystemPrincipal();
   1648      if (isSystem) {
   1649        JS_ReportErrorASCII(
   1650            cx, "System principal is not allowed in an expanded principal");
   1651        return false;
   1652      }
   1653      allowedDomains[i] = principal;
   1654    } else if (allowed.isString()) {
   1655      // Skip any string arguments - we handle them in the next pass.
   1656    } else {
   1657      // Don't know what this is.
   1658      return false;
   1659    }
   1660  }
   1661 
   1662  if (attrs.isNothing()) {
   1663    // If no OriginAttributes was found in the first pass, fall back to a
   1664    // default one.
   1665    attrs.emplace();
   1666  }
   1667 
   1668  // Second pass:
   1669  for (uint32_t i = 0; i < length; ++i) {
   1670    RootedValue allowed(cx);
   1671    if (!JS_GetElement(cx, arrayObj, i, &allowed)) {
   1672      return false;
   1673    }
   1674 
   1675    nsCOMPtr<nsIPrincipal> principal;
   1676    if (allowed.isString()) {
   1677      // In case of string let's try to fetch a content principal from it.
   1678      RootedString str(cx, allowed.toString());
   1679 
   1680      // attrs here is either a default OriginAttributes in case the
   1681      // originAttributes option isn't specified, and no object in the array
   1682      // provides a principal.  Otherwise it's either the forced principal, or
   1683      // the principal found before, so we can use it here.
   1684      if (!ParsePrincipal(cx, str, attrs.ref(), getter_AddRefs(principal))) {
   1685        return false;
   1686      }
   1687      NS_ENSURE_TRUE(principal, false);
   1688      allowedDomains[i] = principal;
   1689    } else {
   1690      MOZ_ASSERT(allowed.isObject());
   1691    }
   1692  }
   1693 
   1694  RefPtr<ExpandedPrincipal> result =
   1695      ExpandedPrincipal::Create(allowedDomains, attrs.ref());
   1696  result.forget(out);
   1697  return true;
   1698 }
   1699 
   1700 /*
   1701 * Helper that tries to get a property from the options object.
   1702 */
   1703 bool OptionsBase::ParseValue(const char* name, MutableHandleValue prop,
   1704                             bool* aFound) {
   1705  bool found;
   1706  bool ok = JS_HasProperty(mCx, mObject, name, &found);
   1707  NS_ENSURE_TRUE(ok, false);
   1708 
   1709  if (aFound) {
   1710    *aFound = found;
   1711  }
   1712 
   1713  if (!found) {
   1714    return true;
   1715  }
   1716 
   1717  return JS_GetProperty(mCx, mObject, name, prop);
   1718 }
   1719 
   1720 /*
   1721 * Helper that tries to get a boolean property from the options object.
   1722 */
   1723 bool OptionsBase::ParseBoolean(const char* name, bool* prop) {
   1724  MOZ_ASSERT(prop);
   1725  RootedValue value(mCx);
   1726  bool found;
   1727  bool ok = ParseValue(name, &value, &found);
   1728  NS_ENSURE_TRUE(ok, false);
   1729 
   1730  if (!found) {
   1731    return true;
   1732  }
   1733 
   1734  if (!value.isBoolean()) {
   1735    JS_ReportErrorASCII(mCx, "Expected a boolean value for property %s", name);
   1736    return false;
   1737  }
   1738 
   1739  *prop = value.toBoolean();
   1740  return true;
   1741 }
   1742 
   1743 /*
   1744 * Helper that tries to get an object property from the options object.
   1745 */
   1746 bool OptionsBase::ParseObject(const char* name, MutableHandleObject prop) {
   1747  RootedValue value(mCx);
   1748  bool found;
   1749  bool ok = ParseValue(name, &value, &found);
   1750  NS_ENSURE_TRUE(ok, false);
   1751 
   1752  if (!found) {
   1753    return true;
   1754  }
   1755 
   1756  if (!value.isObject()) {
   1757    JS_ReportErrorASCII(mCx, "Expected an object value for property %s", name);
   1758    return false;
   1759  }
   1760  prop.set(&value.toObject());
   1761  return true;
   1762 }
   1763 
   1764 /*
   1765 * Helper that tries to get an object property from the options object.
   1766 */
   1767 bool OptionsBase::ParseJSString(const char* name, MutableHandleString prop) {
   1768  RootedValue value(mCx);
   1769  bool found;
   1770  bool ok = ParseValue(name, &value, &found);
   1771  NS_ENSURE_TRUE(ok, false);
   1772 
   1773  if (!found) {
   1774    return true;
   1775  }
   1776 
   1777  if (!value.isString()) {
   1778    JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
   1779    return false;
   1780  }
   1781  prop.set(value.toString());
   1782  return true;
   1783 }
   1784 
   1785 /*
   1786 * Helper that tries to get a string property from the options object.
   1787 */
   1788 bool OptionsBase::ParseString(const char* name, nsCString& prop) {
   1789  RootedValue value(mCx);
   1790  bool found;
   1791  bool ok = ParseValue(name, &value, &found);
   1792  NS_ENSURE_TRUE(ok, false);
   1793 
   1794  if (!found) {
   1795    return true;
   1796  }
   1797 
   1798  if (!value.isString()) {
   1799    JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
   1800    return false;
   1801  }
   1802 
   1803  JS::UniqueChars tmp = JS_EncodeStringToLatin1(mCx, value.toString());
   1804  NS_ENSURE_TRUE(tmp, false);
   1805  prop.Assign(tmp.get(), strlen(tmp.get()));
   1806  return true;
   1807 }
   1808 
   1809 /*
   1810 * Helper that tries to get a string property from the options object.
   1811 */
   1812 bool OptionsBase::ParseString(const char* name, nsString& prop) {
   1813  RootedValue value(mCx);
   1814  bool found;
   1815  bool ok = ParseValue(name, &value, &found);
   1816  NS_ENSURE_TRUE(ok, false);
   1817 
   1818  if (!found) {
   1819    return true;
   1820  }
   1821 
   1822  if (!value.isString()) {
   1823    JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
   1824    return false;
   1825  }
   1826 
   1827  nsAutoJSString strVal;
   1828  if (!strVal.init(mCx, value.toString())) {
   1829    return false;
   1830  }
   1831 
   1832  prop = strVal;
   1833  return true;
   1834 }
   1835 
   1836 /*
   1837 * Helper that tries to get a string property from the options object.
   1838 */
   1839 bool OptionsBase::ParseOptionalString(const char* name, Maybe<nsString>& prop) {
   1840  RootedValue value(mCx);
   1841  bool found;
   1842  bool ok = ParseValue(name, &value, &found);
   1843  NS_ENSURE_TRUE(ok, false);
   1844 
   1845  if (!found || value.isUndefined()) {
   1846    return true;
   1847  }
   1848 
   1849  if (!value.isString()) {
   1850    JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
   1851    return false;
   1852  }
   1853 
   1854  nsAutoJSString strVal;
   1855  if (!strVal.init(mCx, value.toString())) {
   1856    return false;
   1857  }
   1858 
   1859  prop = Some(strVal);
   1860  return true;
   1861 }
   1862 
   1863 /*
   1864 * Helper that tries to get jsid property from the options object.
   1865 */
   1866 bool OptionsBase::ParseId(const char* name, MutableHandleId prop) {
   1867  RootedValue value(mCx);
   1868  bool found;
   1869  bool ok = ParseValue(name, &value, &found);
   1870  NS_ENSURE_TRUE(ok, false);
   1871 
   1872  if (!found) {
   1873    return true;
   1874  }
   1875 
   1876  return JS_ValueToId(mCx, value, prop);
   1877 }
   1878 
   1879 /*
   1880 * Helper that tries to get a uint32_t property from the options object.
   1881 */
   1882 bool OptionsBase::ParseUInt32(const char* name, uint32_t* prop) {
   1883  MOZ_ASSERT(prop);
   1884  RootedValue value(mCx);
   1885  bool found;
   1886  bool ok = ParseValue(name, &value, &found);
   1887  NS_ENSURE_TRUE(ok, false);
   1888 
   1889  if (!found) {
   1890    return true;
   1891  }
   1892 
   1893  if (!JS::ToUint32(mCx, value, prop)) {
   1894    JS_ReportErrorASCII(mCx, "Expected a uint32_t value for property %s", name);
   1895    return false;
   1896  }
   1897 
   1898  return true;
   1899 }
   1900 
   1901 /*
   1902 * Helper that tries to get a list of DOM constructors and other helpers from
   1903 * the options object.
   1904 */
   1905 bool SandboxOptions::ParseGlobalProperties() {
   1906  RootedValue value(mCx);
   1907  bool found;
   1908  bool ok = ParseValue("wantGlobalProperties", &value, &found);
   1909  NS_ENSURE_TRUE(ok, false);
   1910  if (!found) {
   1911    return true;
   1912  }
   1913 
   1914  if (!value.isObject()) {
   1915    JS_ReportErrorASCII(mCx,
   1916                        "Expected an array value for wantGlobalProperties");
   1917    return false;
   1918  }
   1919 
   1920  RootedObject ctors(mCx, &value.toObject());
   1921  bool isArray;
   1922  if (!JS::IsArrayObject(mCx, ctors, &isArray)) {
   1923    return false;
   1924  }
   1925  if (!isArray) {
   1926    JS_ReportErrorASCII(mCx,
   1927                        "Expected an array value for wantGlobalProperties");
   1928    return false;
   1929  }
   1930 
   1931  return globalProperties.Parse(mCx, ctors);
   1932 }
   1933 
   1934 /*
   1935 * Helper that parsing the sandbox options object (from) and sets the fields of
   1936 * the incoming options struct (options).
   1937 */
   1938 bool SandboxOptions::Parse() {
   1939  /* All option names must be ASCII-only. */
   1940  bool ok = ParseObject("sandboxPrototype", &proto) &&
   1941            ParseBoolean("wantXrays", &wantXrays) &&
   1942            ParseBoolean("allowWaivers", &allowWaivers) &&
   1943            ParseBoolean("wantComponents", &wantComponents) &&
   1944            ParseBoolean("wantExportHelpers", &wantExportHelpers) &&
   1945            ParseBoolean("isWebExtensionContentScript",
   1946                         &isWebExtensionContentScript) &&
   1947            ParseBoolean("forceSecureContext", &forceSecureContext) &&
   1948            ParseOptionalString("sandboxContentSecurityPolicy",
   1949                                sandboxContentSecurityPolicy) &&
   1950            ParseString("sandboxName", sandboxName) &&
   1951            ParseObject("sameZoneAs", &sameZoneAs) &&
   1952            ParseBoolean("freshCompartment", &freshCompartment) &&
   1953            ParseBoolean("freshZone", &freshZone) &&
   1954            ParseBoolean("invisibleToDebugger", &invisibleToDebugger) &&
   1955            ParseBoolean("discardSource", &discardSource) &&
   1956            ParseGlobalProperties() && ParseValue("metadata", &metadata) &&
   1957            ParseUInt32("userContextId", &userContextId) &&
   1958            ParseObject("originAttributes", &originAttributes) &&
   1959            ParseBoolean("alwaysUseFdlibm", &alwaysUseFdlibm);
   1960  if (!ok) {
   1961    return false;
   1962  }
   1963 
   1964  if (freshZone && sameZoneAs) {
   1965    JS_ReportErrorASCII(mCx, "Cannot use both sameZoneAs and freshZone");
   1966    return false;
   1967  }
   1968 
   1969  return true;
   1970 }
   1971 
   1972 static nsresult AssembleSandboxMemoryReporterName(JSContext* cx,
   1973                                                  nsCString& sandboxName) {
   1974  // Use a default name when the caller did not provide a sandboxName.
   1975  if (sandboxName.IsEmpty()) {
   1976    sandboxName = "[anonymous sandbox]"_ns;
   1977  } else {
   1978 #ifndef DEBUG
   1979    // Adding the caller location is fairly expensive, so in non-debug
   1980    // builds, only add it if we don't have an explicit sandbox name.
   1981    return NS_OK;
   1982 #endif
   1983  }
   1984 
   1985  // Get the xpconnect native call context.
   1986  XPCCallContext* cc = XPCJSContext::Get()->GetCallContext();
   1987  NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG);
   1988 
   1989  // Get the current source info from xpc.
   1990  nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack();
   1991 
   1992  // Append the caller's location information.
   1993  if (frame) {
   1994    nsAutoCString location;
   1995    frame->GetFilename(cx, location);
   1996    int32_t lineNumber = frame->GetLineNumber(cx);
   1997 
   1998    sandboxName.AppendLiteral(" (from: ");
   1999    sandboxName.Append(location);
   2000    sandboxName.Append(':');
   2001    sandboxName.AppendInt(lineNumber);
   2002    sandboxName.Append(')');
   2003  }
   2004 
   2005  return NS_OK;
   2006 }
   2007 
   2008 // static
   2009 nsresult nsXPCComponents_utils_Sandbox::CallOrConstruct(
   2010    nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj,
   2011    const CallArgs& args, bool* _retval) {
   2012  if (args.length() < 1) {
   2013    return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval);
   2014  }
   2015 
   2016  nsresult rv;
   2017  bool ok = false;
   2018  bool calledWithOptions = args.length() > 1;
   2019  if (calledWithOptions && !args[1].isObject()) {
   2020    return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
   2021  }
   2022 
   2023  RootedObject optionsObject(cx,
   2024                             calledWithOptions ? &args[1].toObject() : nullptr);
   2025 
   2026  SandboxOptions options(cx, optionsObject);
   2027  if (calledWithOptions && !options.Parse()) {
   2028    return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
   2029  }
   2030 
   2031  // Make sure to set up principals on the sandbox before initing classes.
   2032  nsCOMPtr<nsIPrincipal> principal;
   2033  nsCOMPtr<nsIExpandedPrincipal> expanded;
   2034  nsCOMPtr<nsISupports> prinOrSop;
   2035 
   2036  if (args[0].isString()) {
   2037    RootedString str(cx, args[0].toString());
   2038    OriginAttributes attrs;
   2039    if (options.originAttributes) {
   2040      JS::RootedValue val(cx, JS::ObjectValue(*options.originAttributes));
   2041      if (!attrs.Init(cx, val)) {
   2042        // The originAttributes option, if specified, must be valid!
   2043        JS_ReportErrorASCII(cx, "Expected a valid OriginAttributes object");
   2044        return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
   2045      }
   2046    }
   2047    attrs.mUserContextId = options.userContextId;
   2048    ok = ParsePrincipal(cx, str, attrs, getter_AddRefs(principal));
   2049    prinOrSop = principal;
   2050  } else if (args[0].isObject()) {
   2051    RootedObject obj(cx, &args[0].toObject());
   2052    bool isArray;
   2053    if (!JS::IsArrayObject(cx, obj, &isArray)) {
   2054      ok = false;
   2055    } else if (isArray) {
   2056      if (options.userContextId != 0) {
   2057        // We don't support passing a userContextId with an array.
   2058        ok = false;
   2059      } else {
   2060        ok = GetExpandedPrincipal(cx, obj, options, getter_AddRefs(expanded));
   2061        prinOrSop = expanded;
   2062      }
   2063    } else {
   2064      ok = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
   2065    }
   2066  } else if (args[0].isNull()) {
   2067    // Null means that we just pass prinOrSop = nullptr, and get an
   2068    // NullPrincipal.
   2069    ok = true;
   2070  }
   2071 
   2072  if (!ok) {
   2073    return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
   2074  }
   2075 
   2076  if (options.sandboxContentSecurityPolicy.isSome()) {
   2077    if (!expanded) {
   2078      // CSP is currently stored on ExpandedPrincipal. If CSP moves off
   2079      // ExpandedPrincipal (bug 1548468), then we can drop/relax this check.
   2080      JS_ReportErrorASCII(cx,
   2081                          "sandboxContentSecurityPolicy is currently only "
   2082                          "supported with ExpandedPrincipals");
   2083      return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
   2084    }
   2085    rv = SetSandboxCSP(prinOrSop, options.sandboxContentSecurityPolicy.value());
   2086    if (NS_WARN_IF(NS_FAILED(rv))) {
   2087      return rv;
   2088    }
   2089  }
   2090 
   2091  if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName))) {
   2092    return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
   2093  }
   2094 
   2095  if (options.metadata.isNullOrUndefined()) {
   2096    // If the caller is running in a sandbox, inherit.
   2097    RootedObject callerGlobal(cx, JS::GetScriptedCallerGlobal(cx));
   2098    if (IsSandbox(callerGlobal)) {
   2099      rv = GetSandboxMetadata(cx, callerGlobal, &options.metadata);
   2100      if (NS_WARN_IF(NS_FAILED(rv))) {
   2101        return rv;
   2102      }
   2103    }
   2104  }
   2105 
   2106  rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options);
   2107 
   2108  if (NS_FAILED(rv)) {
   2109    return ThrowAndFail(rv, cx, _retval);
   2110  }
   2111 
   2112  *_retval = true;
   2113  return NS_OK;
   2114 }
   2115 
   2116 nsresult xpc::EvalInSandbox(JSContext* cx, HandleObject sandboxArg,
   2117                            const nsAString& source, const nsACString& filename,
   2118                            int32_t lineNo, bool enforceFilenameRestrictions,
   2119                            MutableHandleValue rval) {
   2120  JS_AbortIfWrongThread(cx);
   2121  rval.set(UndefinedValue());
   2122 
   2123  bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg);
   2124  // CheckedUnwrapStatic is fine here, since we're checking for "is it a
   2125  // sandbox".
   2126  RootedObject sandbox(cx, js::CheckedUnwrapStatic(sandboxArg));
   2127  if (!sandbox || !IsSandbox(sandbox)) {
   2128    return NS_ERROR_INVALID_ARG;
   2129  }
   2130 
   2131  SandboxPrivate* priv = SandboxPrivate::GetPrivate(sandbox);
   2132  nsIScriptObjectPrincipal* sop = priv;
   2133  MOZ_ASSERT(sop, "Invalid sandbox passed");
   2134  nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal();
   2135  NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
   2136 
   2137  nsAutoCString filenameBuf;
   2138  if (!filename.IsVoid() && filename.Length() != 0) {
   2139    filenameBuf.Assign(filename);
   2140  } else {
   2141    // Default to the spec of the principal.
   2142    nsresult rv = nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf);
   2143    NS_ENSURE_SUCCESS(rv, rv);
   2144    lineNo = 1;
   2145  }
   2146 
   2147  // We create a separate cx to do the sandbox evaluation. Scope it.
   2148  RootedValue v(cx, UndefinedValue());
   2149  RootedValue exn(cx, UndefinedValue());
   2150  bool ok = true;
   2151  {
   2152    // We're about to evaluate script, so make an AutoEntryScript.
   2153    // This is clearly Gecko-specific and not in any spec.
   2154    mozilla::dom::AutoEntryScript aes(priv, "XPConnect sandbox evaluation");
   2155    JSContext* sandcx = aes.cx();
   2156    JSAutoRealm ar(sandcx, sandbox);
   2157 
   2158    JS::CompileOptions options(sandcx);
   2159    options.setFileAndLine(filenameBuf.get(), lineNo);
   2160    options.setSkipFilenameValidation(!enforceFilenameRestrictions);
   2161    MOZ_ASSERT(JS_IsGlobalObject(sandbox));
   2162 
   2163    const nsPromiseFlatString& flat = PromiseFlatString(source);
   2164 
   2165    JS::SourceText<char16_t> buffer;
   2166    ok = buffer.init(sandcx, flat.get(), flat.Length(),
   2167                     JS::SourceOwnership::Borrowed) &&
   2168         JS::Evaluate(sandcx, options, buffer, &v);
   2169 
   2170    // If the sandbox threw an exception, grab it off the context.
   2171    if (aes.HasException()) {
   2172      if (!aes.StealException(&exn)) {
   2173        return NS_ERROR_OUT_OF_MEMORY;
   2174      }
   2175    }
   2176  }
   2177 
   2178  //
   2179  // Alright, we're back on the caller's cx. If an error occured, try to
   2180  // wrap and set the exception. Otherwise, wrap the return value.
   2181  //
   2182 
   2183  if (!ok) {
   2184    // If we end up without an exception, it was probably due to OOM along
   2185    // the way, in which case we thow. Otherwise, wrap it.
   2186    if (exn.isUndefined() || !JS_WrapValue(cx, &exn)) {
   2187      return NS_ERROR_OUT_OF_MEMORY;
   2188    }
   2189 
   2190    // Set the exception on our caller's cx.
   2191    JS_SetPendingException(cx, exn);
   2192    return NS_ERROR_FAILURE;
   2193  }
   2194 
   2195  // Transitively apply Xray waivers if |sb| was waived.
   2196  if (waiveXray) {
   2197    ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
   2198  } else {
   2199    ok = JS_WrapValue(cx, &v);
   2200  }
   2201  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
   2202 
   2203  // Whew!
   2204  rval.set(v);
   2205  return NS_OK;
   2206 }
   2207 
   2208 nsresult xpc::GetSandboxMetadata(JSContext* cx, HandleObject sandbox,
   2209                                 MutableHandleValue rval) {
   2210  MOZ_ASSERT(NS_IsMainThread());
   2211  MOZ_ASSERT(IsSandbox(sandbox));
   2212 
   2213  RootedValue metadata(cx);
   2214  {
   2215    JSAutoRealm ar(cx, sandbox);
   2216    metadata =
   2217        JS::GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT);
   2218  }
   2219 
   2220  if (!JS_WrapValue(cx, &metadata)) {
   2221    return NS_ERROR_UNEXPECTED;
   2222  }
   2223 
   2224  rval.set(metadata);
   2225  return NS_OK;
   2226 }
   2227 
   2228 nsresult xpc::SetSandboxLocaleOverride(JSContext* cx, HandleObject sandbox,
   2229                                       const char* locale) {
   2230  MOZ_ASSERT(NS_IsMainThread());
   2231  MOZ_ASSERT(IsSandbox(sandbox));
   2232 
   2233  JS::SetRealmLocaleOverride(JS::GetObjectRealmOrNull(sandbox), locale);
   2234 
   2235  return NS_OK;
   2236 }
   2237 
   2238 nsresult xpc::SetSandboxTimezoneOverride(JSContext* cx, HandleObject sandbox,
   2239                                         const char* timezone) {
   2240  MOZ_ASSERT(NS_IsMainThread());
   2241  MOZ_ASSERT(IsSandbox(sandbox));
   2242 
   2243  JS::SetRealmTimezoneOverride(JS::GetObjectRealmOrNull(sandbox), timezone);
   2244 
   2245  return NS_OK;
   2246 }
   2247 
   2248 nsresult xpc::SetSandboxMetadata(JSContext* cx, HandleObject sandbox,
   2249                                 HandleValue metadataArg) {
   2250  MOZ_ASSERT(NS_IsMainThread());
   2251  MOZ_ASSERT(IsSandbox(sandbox));
   2252 
   2253  RootedValue metadata(cx);
   2254 
   2255  JSAutoRealm ar(cx, sandbox);
   2256  if (!JS_StructuredClone(cx, metadataArg, &metadata, nullptr, nullptr)) {
   2257    return NS_ERROR_UNEXPECTED;
   2258  }
   2259 
   2260  JS_SetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT, metadata);
   2261 
   2262  return NS_OK;
   2263 }
   2264 
   2265 ModuleLoaderBase* SandboxPrivate::GetModuleLoader(JSContext* aCx) {
   2266  if (mModuleLoader) {
   2267    return mModuleLoader;
   2268  }
   2269 
   2270  JSObject* object = GetGlobalJSObject();
   2271  nsGlobalWindowInner* sandboxWindow = xpc::SandboxWindowOrNull(object, aCx);
   2272  if (!sandboxWindow) {
   2273    return nullptr;
   2274  }
   2275 
   2276  ModuleLoader* mainModuleLoader =
   2277      static_cast<ModuleLoader*>(sandboxWindow->GetModuleLoader(aCx));
   2278 
   2279  ScriptLoader* scriptLoader = mainModuleLoader->GetScriptLoader();
   2280 
   2281  ModuleLoader* moduleLoader =
   2282      new ModuleLoader(scriptLoader, this, ModuleLoader::WebExtension);
   2283  scriptLoader->RegisterContentScriptModuleLoader(moduleLoader);
   2284  mModuleLoader = moduleLoader;
   2285 
   2286  return moduleLoader;
   2287 }
   2288 
   2289 mozilla::Result<mozilla::ipc::PrincipalInfo, nsresult>
   2290 SandboxPrivate::GetStorageKey() {
   2291  MOZ_ASSERT(NS_IsMainThread());
   2292 
   2293  mozilla::ipc::PrincipalInfo principalInfo;
   2294  nsresult rv = PrincipalToPrincipalInfo(mPrincipal, &principalInfo);
   2295  if (NS_WARN_IF(NS_FAILED(rv))) {
   2296    return mozilla::Err(rv);
   2297  }
   2298 
   2299  // Block expanded and null principals, let content and system through.
   2300  if (principalInfo.type() !=
   2301          mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
   2302      principalInfo.type() !=
   2303          mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
   2304    return Err(NS_ERROR_DOM_SECURITY_ERR);
   2305  }
   2306 
   2307  return std::move(principalInfo);
   2308 }