js/xpconnect/src/Sandbox.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/xpconnect/src/Sandbox.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1889 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * The Components.Sandbox object.
    1.12 + */
    1.13 +
    1.14 +#include "AccessCheck.h"
    1.15 +#include "jsfriendapi.h"
    1.16 +#include "jsproxy.h"
    1.17 +#include "js/OldDebugAPI.h"
    1.18 +#include "js/StructuredClone.h"
    1.19 +#include "nsContentUtils.h"
    1.20 +#include "nsCxPusher.h"
    1.21 +#include "nsGlobalWindow.h"
    1.22 +#include "nsIScriptContext.h"
    1.23 +#include "nsIScriptObjectPrincipal.h"
    1.24 +#include "nsIScriptSecurityManager.h"
    1.25 +#include "nsIURI.h"
    1.26 +#include "nsJSUtils.h"
    1.27 +#include "nsNetUtil.h"
    1.28 +#include "nsPrincipal.h"
    1.29 +#include "nsXMLHttpRequest.h"
    1.30 +#include "WrapperFactory.h"
    1.31 +#include "xpcprivate.h"
    1.32 +#include "XPCQuickStubs.h"
    1.33 +#include "XPCWrapper.h"
    1.34 +#include "XrayWrapper.h"
    1.35 +#include "mozilla/dom/BindingUtils.h"
    1.36 +#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
    1.37 +#include "mozilla/dom/PromiseBinding.h"
    1.38 +#include "mozilla/dom/TextDecoderBinding.h"
    1.39 +#include "mozilla/dom/TextEncoderBinding.h"
    1.40 +#include "mozilla/dom/URLBinding.h"
    1.41 +
    1.42 +using namespace mozilla;
    1.43 +using namespace JS;
    1.44 +using namespace js;
    1.45 +using namespace xpc;
    1.46 +
    1.47 +using mozilla::dom::DestroyProtoAndIfaceCache;
    1.48 +using mozilla::dom::indexedDB::IndexedDatabaseManager;
    1.49 +
    1.50 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(SandboxPrivate)
    1.51 +NS_IMPL_CYCLE_COLLECTING_ADDREF(SandboxPrivate)
    1.52 +NS_IMPL_CYCLE_COLLECTING_RELEASE(SandboxPrivate)
    1.53 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SandboxPrivate)
    1.54 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    1.55 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptObjectPrincipal)
    1.56 +  NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
    1.57 +  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
    1.58 +  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    1.59 +NS_INTERFACE_MAP_END
    1.60 +
    1.61 +const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID;
    1.62 +
    1.63 +class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox,
    1.64 +                                      public nsIXPCScriptable
    1.65 +{
    1.66 +public:
    1.67 +    // Aren't macros nice?
    1.68 +    NS_DECL_ISUPPORTS
    1.69 +    NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX
    1.70 +    NS_DECL_NSIXPCSCRIPTABLE
    1.71 +
    1.72 +public:
    1.73 +    nsXPCComponents_utils_Sandbox();
    1.74 +    virtual ~nsXPCComponents_utils_Sandbox();
    1.75 +
    1.76 +private:
    1.77 +    static nsresult CallOrConstruct(nsIXPConnectWrappedNative *wrapper,
    1.78 +                                    JSContext *cx, HandleObject obj,
    1.79 +                                    const CallArgs &args, bool *_retval);
    1.80 +};
    1.81 +
    1.82 +already_AddRefed<nsIXPCComponents_utils_Sandbox>
    1.83 +xpc::NewSandboxConstructor()
    1.84 +{
    1.85 +    nsCOMPtr<nsIXPCComponents_utils_Sandbox> sbConstructor =
    1.86 +        new nsXPCComponents_utils_Sandbox();
    1.87 +    return sbConstructor.forget();
    1.88 +}
    1.89 +
    1.90 +static bool
    1.91 +SandboxDump(JSContext *cx, unsigned argc, jsval *vp)
    1.92 +{
    1.93 +    CallArgs args = CallArgsFromVp(argc, vp);
    1.94 +
    1.95 +    if (args.length() == 0)
    1.96 +        return true;
    1.97 +
    1.98 +    RootedString str(cx, ToString(cx, args[0]));
    1.99 +    if (!str)
   1.100 +        return false;
   1.101 +
   1.102 +    size_t length;
   1.103 +    const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length);
   1.104 +    if (!chars)
   1.105 +        return false;
   1.106 +
   1.107 +    nsDependentString wstr(chars, length);
   1.108 +    char *cstr = ToNewUTF8String(wstr);
   1.109 +    if (!cstr)
   1.110 +        return false;
   1.111 +
   1.112 +#if defined(XP_MACOSX)
   1.113 +    // Be nice and convert all \r to \n.
   1.114 +    char *c = cstr, *cEnd = cstr + strlen(cstr);
   1.115 +    while (c < cEnd) {
   1.116 +        if (*c == '\r')
   1.117 +            *c = '\n';
   1.118 +        c++;
   1.119 +    }
   1.120 +#endif
   1.121 +#ifdef ANDROID
   1.122 +    __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
   1.123 +#endif
   1.124 +
   1.125 +    fputs(cstr, stdout);
   1.126 +    fflush(stdout);
   1.127 +    NS_Free(cstr);
   1.128 +    args.rval().setBoolean(true);
   1.129 +    return true;
   1.130 +}
   1.131 +
   1.132 +static bool
   1.133 +SandboxDebug(JSContext *cx, unsigned argc, jsval *vp)
   1.134 +{
   1.135 +#ifdef DEBUG
   1.136 +    return SandboxDump(cx, argc, vp);
   1.137 +#else
   1.138 +    return true;
   1.139 +#endif
   1.140 +}
   1.141 +
   1.142 +static bool
   1.143 +SandboxImport(JSContext *cx, unsigned argc, Value *vp)
   1.144 +{
   1.145 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.146 +
   1.147 +    if (args.length() < 1 || args[0].isPrimitive()) {
   1.148 +        XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
   1.149 +        return false;
   1.150 +    }
   1.151 +
   1.152 +    RootedString funname(cx);
   1.153 +    if (args.length() > 1) {
   1.154 +        // Use the second parameter as the function name.
   1.155 +        funname = ToString(cx, args[1]);
   1.156 +        if (!funname)
   1.157 +            return false;
   1.158 +    } else {
   1.159 +        // NB: funobj must only be used to get the JSFunction out.
   1.160 +        RootedObject funobj(cx, &args[0].toObject());
   1.161 +        if (js::IsProxy(funobj)) {
   1.162 +            funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj);
   1.163 +        }
   1.164 +
   1.165 +        JSAutoCompartment ac(cx, funobj);
   1.166 +
   1.167 +        RootedValue funval(cx, ObjectValue(*funobj));
   1.168 +        JSFunction *fun = JS_ValueToFunction(cx, funval);
   1.169 +        if (!fun) {
   1.170 +            XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
   1.171 +            return false;
   1.172 +        }
   1.173 +
   1.174 +        // Use the actual function name as the name.
   1.175 +        funname = JS_GetFunctionId(fun);
   1.176 +        if (!funname) {
   1.177 +            XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
   1.178 +            return false;
   1.179 +        }
   1.180 +    }
   1.181 +
   1.182 +    RootedId id(cx);
   1.183 +    if (!JS_StringToId(cx, funname, &id))
   1.184 +        return false;
   1.185 +
   1.186 +    // We need to resolve the this object, because this function is used
   1.187 +    // unbound and should still work and act on the original sandbox.
   1.188 +    RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp));
   1.189 +    if (!thisObject) {
   1.190 +        XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
   1.191 +        return false;
   1.192 +    }
   1.193 +    if (!JS_SetPropertyById(cx, thisObject, id, args[0]))
   1.194 +        return false;
   1.195 +
   1.196 +    args.rval().setUndefined();
   1.197 +    return true;
   1.198 +}
   1.199 +
   1.200 +static bool
   1.201 +CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp)
   1.202 +{
   1.203 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.204 +
   1.205 +    nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
   1.206 +    if (!ssm)
   1.207 +        return false;
   1.208 +
   1.209 +    nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
   1.210 +    if (!subjectPrincipal)
   1.211 +        return false;
   1.212 +
   1.213 +    RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
   1.214 +    MOZ_ASSERT(global);
   1.215 +
   1.216 +    nsIScriptObjectPrincipal *sop =
   1.217 +        static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(global));
   1.218 +    nsCOMPtr<nsIGlobalObject> iglobal = do_QueryInterface(sop);
   1.219 +
   1.220 +    nsCOMPtr<nsIXMLHttpRequest> xhr = new nsXMLHttpRequest();
   1.221 +    nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr);
   1.222 +    if (NS_FAILED(rv))
   1.223 +        return false;
   1.224 +
   1.225 +    rv = nsContentUtils::WrapNative(cx, xhr, args.rval());
   1.226 +    if (NS_FAILED(rv))
   1.227 +        return false;
   1.228 +
   1.229 +    return true;
   1.230 +}
   1.231 +
   1.232 +static bool
   1.233 +IsProxy(JSContext *cx, unsigned argc, jsval *vp)
   1.234 +{
   1.235 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.236 +    if (args.length() < 1) {
   1.237 +        JS_ReportError(cx, "Function requires at least 1 argument");
   1.238 +        return false;
   1.239 +    }
   1.240 +    if (!args[0].isObject()) {
   1.241 +        args.rval().setBoolean(false);
   1.242 +        return true;
   1.243 +    }
   1.244 +
   1.245 +    RootedObject obj(cx, &args[0].toObject());
   1.246 +    obj = js::CheckedUnwrap(obj);
   1.247 +    NS_ENSURE_TRUE(obj, false);
   1.248 +
   1.249 +    args.rval().setBoolean(js::IsScriptedProxy(obj));
   1.250 +    return true;
   1.251 +}
   1.252 +
   1.253 +namespace xpc {
   1.254 +
   1.255 +bool
   1.256 +ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleValue voptions,
   1.257 +               MutableHandleValue rval)
   1.258 +{
   1.259 +    bool hasOptions = !voptions.isUndefined();
   1.260 +    if (!vscope.isObject() || !vfunction.isObject() || (hasOptions && !voptions.isObject())) {
   1.261 +        JS_ReportError(cx, "Invalid argument");
   1.262 +        return false;
   1.263 +    }
   1.264 +
   1.265 +    RootedObject funObj(cx, &vfunction.toObject());
   1.266 +    RootedObject targetScope(cx, &vscope.toObject());
   1.267 +    ExportOptions options(cx, hasOptions ? &voptions.toObject() : nullptr);
   1.268 +    if (hasOptions && !options.Parse())
   1.269 +        return false;
   1.270 +
   1.271 +    // We can only export functions to scopes those are transparent for us,
   1.272 +    // so if there is a security wrapper around targetScope we must throw.
   1.273 +    targetScope = CheckedUnwrap(targetScope);
   1.274 +    if (!targetScope) {
   1.275 +        JS_ReportError(cx, "Permission denied to export function into scope");
   1.276 +        return false;
   1.277 +    }
   1.278 +
   1.279 +    if (js::IsScriptedProxy(targetScope)) {
   1.280 +        JS_ReportError(cx, "Defining property on proxy object is not allowed");
   1.281 +        return false;
   1.282 +    }
   1.283 +
   1.284 +    {
   1.285 +        // We need to operate in the target scope from here on, let's enter
   1.286 +        // its compartment.
   1.287 +        JSAutoCompartment ac(cx, targetScope);
   1.288 +
   1.289 +        // Unwrapping to see if we have a callable.
   1.290 +        funObj = UncheckedUnwrap(funObj);
   1.291 +        if (!JS_ObjectIsCallable(cx, funObj)) {
   1.292 +            JS_ReportError(cx, "First argument must be a function");
   1.293 +            return false;
   1.294 +        }
   1.295 +
   1.296 +        RootedId id(cx, options.defineAs);
   1.297 +        if (JSID_IS_VOID(id)) {
   1.298 +            // If there wasn't any function name specified,
   1.299 +            // copy the name from the function being imported.
   1.300 +            JSFunction *fun = JS_GetObjectFunction(funObj);
   1.301 +            RootedString funName(cx, JS_GetFunctionId(fun));
   1.302 +            if (!funName)
   1.303 +                funName = JS_InternString(cx, "");
   1.304 +
   1.305 +            if (!JS_StringToId(cx, funName, &id))
   1.306 +                return false;
   1.307 +        }
   1.308 +        MOZ_ASSERT(JSID_IS_STRING(id));
   1.309 +
   1.310 +        // The function forwarder will live in the target compartment. Since
   1.311 +        // this function will be referenced from its private slot, to avoid a
   1.312 +        // GC hazard, we must wrap it to the same compartment.
   1.313 +        if (!JS_WrapObject(cx, &funObj))
   1.314 +            return false;
   1.315 +
   1.316 +        // And now, let's create the forwarder function in the target compartment
   1.317 +        // for the function the be exported.
   1.318 +        if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, rval)) {
   1.319 +            JS_ReportError(cx, "Exporting function failed");
   1.320 +            return false;
   1.321 +        }
   1.322 +
   1.323 +        // We have the forwarder function in the target compartment. If
   1.324 +        // defineAs was set, we also need to define it as a property on
   1.325 +        // the target.
   1.326 +        if (!JSID_IS_VOID(options.defineAs)) {
   1.327 +            if (!JS_DefinePropertyById(cx, targetScope, id, rval,
   1.328 +                                       JS_PropertyStub, JS_StrictPropertyStub,
   1.329 +                                       JSPROP_ENUMERATE)) {
   1.330 +                return false;
   1.331 +            }
   1.332 +        }
   1.333 +    }
   1.334 +
   1.335 +    // Finally we have to re-wrap the exported function back to the caller compartment.
   1.336 +    if (!JS_WrapValue(cx, rval))
   1.337 +        return false;
   1.338 +
   1.339 +    return true;
   1.340 +}
   1.341 +
   1.342 +/*
   1.343 + * Expected type of the arguments and the return value:
   1.344 + * function exportFunction(function funToExport,
   1.345 + *                         object targetScope,
   1.346 + *                         [optional] object options)
   1.347 + */
   1.348 +static bool
   1.349 +ExportFunction(JSContext *cx, unsigned argc, jsval *vp)
   1.350 +{
   1.351 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.352 +    if (args.length() < 2) {
   1.353 +        JS_ReportError(cx, "Function requires at least 2 arguments");
   1.354 +        return false;
   1.355 +    }
   1.356 +
   1.357 +    RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
   1.358 +    return ExportFunction(cx, args[0], args[1], options, args.rval());
   1.359 +}
   1.360 +} /* namespace xpc */
   1.361 +
   1.362 +static bool
   1.363 +GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno)
   1.364 +{
   1.365 +    JS::AutoFilename scriptFilename;
   1.366 +    if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineno)) {
   1.367 +        if (const char *cfilename = scriptFilename.get()) {
   1.368 +            filename.Assign(nsDependentCString(cfilename));
   1.369 +            return true;
   1.370 +        }
   1.371 +    }
   1.372 +    return false;
   1.373 +}
   1.374 +
   1.375 +bool
   1.376 +xpc::IsReflector(JSObject *obj)
   1.377 +{
   1.378 +    return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj);
   1.379 +}
   1.380 +
   1.381 +enum ForwarderCloneTags {
   1.382 +    SCTAG_BASE = JS_SCTAG_USER_MIN,
   1.383 +    SCTAG_REFLECTOR
   1.384 +};
   1.385 +
   1.386 +static JSObject *
   1.387 +CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag,
   1.388 +                       uint32_t data, void *closure)
   1.389 +{
   1.390 +    MOZ_ASSERT(closure, "Null pointer!");
   1.391 +    AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure);
   1.392 +    if (tag == SCTAG_REFLECTOR) {
   1.393 +        MOZ_ASSERT(!data);
   1.394 +
   1.395 +        size_t idx;
   1.396 +        if (JS_ReadBytes(reader, &idx, sizeof(size_t))) {
   1.397 +            RootedObject reflector(cx, reflectors->handleAt(idx));
   1.398 +            MOZ_ASSERT(reflector, "No object pointer?");
   1.399 +            MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!");
   1.400 +
   1.401 +            if (!JS_WrapObject(cx, &reflector))
   1.402 +                return nullptr;
   1.403 +            MOZ_ASSERT(WrapperFactory::IsXrayWrapper(reflector) ||
   1.404 +                       IsReflector(reflector));
   1.405 +
   1.406 +            return reflector;
   1.407 +        }
   1.408 +    }
   1.409 +
   1.410 +    JS_ReportError(cx, "CloneNonReflectorsRead error");
   1.411 +    return nullptr;
   1.412 +}
   1.413 +
   1.414 +static bool
   1.415 +CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer,
   1.416 +                        Handle<JSObject *> obj, void *closure)
   1.417 +{
   1.418 +    MOZ_ASSERT(closure, "Null pointer!");
   1.419 +
   1.420 +    // We need to maintain a list of reflectors to make sure all these objects
   1.421 +    // are properly rooter. Only their indices will be serialized.
   1.422 +    AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure);
   1.423 +    if (IsReflector(obj)) {
   1.424 +        if (!reflectors->append(obj))
   1.425 +            return false;
   1.426 +
   1.427 +        size_t idx = reflectors->length()-1;
   1.428 +        if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) &&
   1.429 +            JS_WriteBytes(writer, &idx, sizeof(size_t))) {
   1.430 +            return true;
   1.431 +        }
   1.432 +    }
   1.433 +
   1.434 +    JS_ReportError(cx, "CloneNonReflectorsWrite error");
   1.435 +    return false;
   1.436 +}
   1.437 +
   1.438 +static const JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = {
   1.439 +    CloneNonReflectorsRead,
   1.440 +    CloneNonReflectorsWrite,
   1.441 +    nullptr,
   1.442 +    nullptr,
   1.443 +    nullptr,
   1.444 +    nullptr
   1.445 +};
   1.446 +
   1.447 +/*
   1.448 + * This is a special structured cloning, that clones only non-reflectors.
   1.449 + * The function assumes the cx is already entered the compartment we want
   1.450 + * to clone to, and that if val is an object is from the compartment we
   1.451 + * clone from.
   1.452 + */
   1.453 +static bool
   1.454 +CloneNonReflectors(JSContext *cx, MutableHandleValue val)
   1.455 +{
   1.456 +    JSAutoStructuredCloneBuffer buffer;
   1.457 +    AutoObjectVector rootedReflectors(cx);
   1.458 +    {
   1.459 +        // For parsing val we have to enter its compartment.
   1.460 +        // (unless it's a primitive)
   1.461 +        Maybe<JSAutoCompartment> ac;
   1.462 +        if (val.isObject()) {
   1.463 +            ac.construct(cx, &val.toObject());
   1.464 +        } else if (val.isString() && !JS_WrapValue(cx, val)) {
   1.465 +            return false;
   1.466 +        }
   1.467 +
   1.468 +        if (!buffer.write(cx, val,
   1.469 +            &gForwarderStructuredCloneCallbacks,
   1.470 +            &rootedReflectors))
   1.471 +        {
   1.472 +            return false;
   1.473 +        }
   1.474 +    }
   1.475 +
   1.476 +    // Now recreate the clones in the target compartment.
   1.477 +    if (!buffer.read(cx, val,
   1.478 +        &gForwarderStructuredCloneCallbacks,
   1.479 +        &rootedReflectors))
   1.480 +    {
   1.481 +        return false;
   1.482 +    }
   1.483 +
   1.484 +    return true;
   1.485 +}
   1.486 +
   1.487 +namespace xpc {
   1.488 +
   1.489 +bool
   1.490 +EvalInWindow(JSContext *cx, const nsAString &source, HandleObject scope, MutableHandleValue rval)
   1.491 +{
   1.492 +    // If we cannot unwrap we must not eval in it.
   1.493 +    RootedObject targetScope(cx, CheckedUnwrap(scope));
   1.494 +    if (!targetScope) {
   1.495 +        JS_ReportError(cx, "Permission denied to eval in target scope");
   1.496 +        return false;
   1.497 +    }
   1.498 +
   1.499 +    // Make sure that we have a window object.
   1.500 +    RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false));
   1.501 +    nsCOMPtr<nsIGlobalObject> global;
   1.502 +    nsCOMPtr<nsPIDOMWindow> window;
   1.503 +    if (!JS_IsGlobalObject(inner) ||
   1.504 +        !(global = GetNativeForGlobal(inner)) ||
   1.505 +        !(window = do_QueryInterface(global)))
   1.506 +    {
   1.507 +        JS_ReportError(cx, "Second argument must be a window");
   1.508 +        return false;
   1.509 +    }
   1.510 +
   1.511 +    nsCOMPtr<nsIScriptContext> context =
   1.512 +        (static_cast<nsGlobalWindow*>(window.get()))->GetScriptContext();
   1.513 +    if (!context) {
   1.514 +        JS_ReportError(cx, "Script context needed");
   1.515 +        return false;
   1.516 +    }
   1.517 +
   1.518 +    nsCString filename;
   1.519 +    unsigned lineNo;
   1.520 +    if (!GetFilenameAndLineNumber(cx, filename, lineNo)) {
   1.521 +        // Default values for non-scripted callers.
   1.522 +        filename.Assign("Unknown");
   1.523 +        lineNo = 0;
   1.524 +    }
   1.525 +
   1.526 +    RootedObject cxGlobal(cx, JS::CurrentGlobalOrNull(cx));
   1.527 +    {
   1.528 +        // CompileOptions must be created from the context
   1.529 +        // we will execute this script in.
   1.530 +        JSContext *wndCx = context->GetNativeContext();
   1.531 +        AutoCxPusher pusher(wndCx);
   1.532 +        JS::CompileOptions compileOptions(wndCx);
   1.533 +        compileOptions.setFileAndLine(filename.get(), lineNo);
   1.534 +
   1.535 +        // We don't want the JS engine to automatically report
   1.536 +        // uncaught exceptions.
   1.537 +        nsJSUtils::EvaluateOptions evaluateOptions;
   1.538 +        evaluateOptions.setReportUncaught(false);
   1.539 +
   1.540 +        nsresult rv = nsJSUtils::EvaluateString(wndCx,
   1.541 +                                                source,
   1.542 +                                                targetScope,
   1.543 +                                                compileOptions,
   1.544 +                                                evaluateOptions,
   1.545 +                                                rval);
   1.546 +
   1.547 +        if (NS_FAILED(rv)) {
   1.548 +            // If there was an exception we get it as a return value, if
   1.549 +            // the evaluation failed for some other reason, then a default
   1.550 +            // exception is raised.
   1.551 +            MOZ_ASSERT(!JS_IsExceptionPending(wndCx),
   1.552 +                       "Exception should be delivered as return value.");
   1.553 +            if (rval.isUndefined()) {
   1.554 +                MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY);
   1.555 +                return false;
   1.556 +            }
   1.557 +
   1.558 +            // If there was an exception thrown we should set it
   1.559 +            // on the calling context.
   1.560 +            RootedValue exn(wndCx, rval);
   1.561 +            // First we should reset the return value.
   1.562 +            rval.set(UndefinedValue());
   1.563 +
   1.564 +            // Then clone the exception.
   1.565 +            JSAutoCompartment ac(wndCx, cxGlobal);
   1.566 +            if (CloneNonReflectors(wndCx, &exn))
   1.567 +                js::SetPendingExceptionCrossContext(cx, exn);
   1.568 +
   1.569 +            return false;
   1.570 +        }
   1.571 +    }
   1.572 +
   1.573 +    // Let's clone the return value back to the callers compartment.
   1.574 +    if (!CloneNonReflectors(cx, rval)) {
   1.575 +        rval.set(UndefinedValue());
   1.576 +        return false;
   1.577 +    }
   1.578 +
   1.579 +    return true;
   1.580 +}
   1.581 +
   1.582 +/*
   1.583 + * Expected type of the arguments:
   1.584 + * value evalInWindow(string script,
   1.585 + *                    object window)
   1.586 + */
   1.587 +static bool
   1.588 +EvalInWindow(JSContext *cx, unsigned argc, jsval *vp)
   1.589 +{
   1.590 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.591 +    if (args.length() < 2) {
   1.592 +        JS_ReportError(cx, "Function requires two arguments");
   1.593 +        return false;
   1.594 +    }
   1.595 +
   1.596 +    if (!args[0].isString() || !args[1].isObject()) {
   1.597 +        JS_ReportError(cx, "Invalid arguments");
   1.598 +        return false;
   1.599 +    }
   1.600 +
   1.601 +    RootedString srcString(cx, args[0].toString());
   1.602 +    RootedObject targetScope(cx, &args[1].toObject());
   1.603 +
   1.604 +    nsDependentJSString srcDepString;
   1.605 +    if (!srcDepString.init(cx, srcString)) {
   1.606 +        JS_ReportError(cx, "Source string is invalid");
   1.607 +        return false;
   1.608 +    }
   1.609 +
   1.610 +    return EvalInWindow(cx, srcDepString, targetScope, args.rval());
   1.611 +}
   1.612 +
   1.613 +static bool
   1.614 +CreateObjectIn(JSContext *cx, unsigned argc, jsval *vp)
   1.615 +{
   1.616 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.617 +    if (args.length() < 1) {
   1.618 +        JS_ReportError(cx, "Function requires at least 1 argument");
   1.619 +        return false;
   1.620 +    }
   1.621 +
   1.622 +    RootedObject optionsObj(cx);
   1.623 +    bool calledWithOptions = args.length() > 1;
   1.624 +    if (calledWithOptions) {
   1.625 +        if (!args[1].isObject()) {
   1.626 +            JS_ReportError(cx, "Expected the 2nd argument (options) to be an object");
   1.627 +            return false;
   1.628 +        }
   1.629 +        optionsObj = &args[1].toObject();
   1.630 +    }
   1.631 +
   1.632 +    CreateObjectInOptions options(cx, optionsObj);
   1.633 +    if (calledWithOptions && !options.Parse())
   1.634 +        return false;
   1.635 +
   1.636 +    return xpc::CreateObjectIn(cx, args[0], options, args.rval());
   1.637 +}
   1.638 +
   1.639 +static bool
   1.640 +CloneInto(JSContext *cx, unsigned argc, jsval *vp)
   1.641 +{
   1.642 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.643 +    if (args.length() < 2) {
   1.644 +        JS_ReportError(cx, "Function requires at least 2 arguments");
   1.645 +        return false;
   1.646 +    }
   1.647 +
   1.648 +    RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
   1.649 +    return xpc::CloneInto(cx, args[0], args[1], options, args.rval());
   1.650 +}
   1.651 +
   1.652 +} /* namespace xpc */
   1.653 +
   1.654 +static bool
   1.655 +sandbox_enumerate(JSContext *cx, HandleObject obj)
   1.656 +{
   1.657 +    return JS_EnumerateStandardClasses(cx, obj);
   1.658 +}
   1.659 +
   1.660 +static bool
   1.661 +sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id)
   1.662 +{
   1.663 +    bool resolved;
   1.664 +    return JS_ResolveStandardClass(cx, obj, id, &resolved);
   1.665 +}
   1.666 +
   1.667 +static void
   1.668 +sandbox_finalize(JSFreeOp *fop, JSObject *obj)
   1.669 +{
   1.670 +    nsIScriptObjectPrincipal *sop =
   1.671 +        static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
   1.672 +    MOZ_ASSERT(sop);
   1.673 +    static_cast<SandboxPrivate *>(sop)->ForgetGlobalObject();
   1.674 +    NS_IF_RELEASE(sop);
   1.675 +    DestroyProtoAndIfaceCache(obj);
   1.676 +}
   1.677 +
   1.678 +static bool
   1.679 +sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
   1.680 +{
   1.681 +    if (type == JSTYPE_OBJECT) {
   1.682 +        vp.set(OBJECT_TO_JSVAL(obj));
   1.683 +        return true;
   1.684 +    }
   1.685 +
   1.686 +    return JS_ConvertStub(cx, obj, type, vp);
   1.687 +}
   1.688 +
   1.689 +#define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
   1.690 +
   1.691 +static const JSClass SandboxClass = {
   1.692 +    "Sandbox",
   1.693 +    XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
   1.694 +    JS_PropertyStub,   JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   1.695 +    sandbox_enumerate, sandbox_resolve, sandbox_convert,  sandbox_finalize,
   1.696 +    nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
   1.697 +};
   1.698 +
   1.699 +static const JSFunctionSpec SandboxFunctions[] = {
   1.700 +    JS_FS("dump",    SandboxDump,    1,0),
   1.701 +    JS_FS("debug",   SandboxDebug,   1,0),
   1.702 +    JS_FS("importFunction", SandboxImport, 1,0),
   1.703 +    JS_FS_END
   1.704 +};
   1.705 +
   1.706 +bool
   1.707 +xpc::IsSandbox(JSObject *obj)
   1.708 +{
   1.709 +    return GetObjectJSClass(obj) == &SandboxClass;
   1.710 +}
   1.711 +
   1.712 +/***************************************************************************/
   1.713 +nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox()
   1.714 +{
   1.715 +}
   1.716 +
   1.717 +nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox()
   1.718 +{
   1.719 +}
   1.720 +
   1.721 +NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox)
   1.722 +  NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox)
   1.723 +  NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
   1.724 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox)
   1.725 +NS_INTERFACE_MAP_END
   1.726 +
   1.727 +NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox)
   1.728 +NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox)
   1.729 +
   1.730 +// We use the nsIXPScriptable macros to generate lots of stuff for us.
   1.731 +#define XPC_MAP_CLASSNAME           nsXPCComponents_utils_Sandbox
   1.732 +#define XPC_MAP_QUOTED_CLASSNAME   "nsXPCComponents_utils_Sandbox"
   1.733 +#define                             XPC_MAP_WANT_CALL
   1.734 +#define                             XPC_MAP_WANT_CONSTRUCT
   1.735 +#define XPC_MAP_FLAGS               0
   1.736 +#include "xpc_map_end.h" /* This #undef's the above. */
   1.737 +
   1.738 +xpc::SandboxProxyHandler xpc::sandboxProxyHandler;
   1.739 +
   1.740 +bool
   1.741 +xpc::IsSandboxPrototypeProxy(JSObject *obj)
   1.742 +{
   1.743 +    return js::IsProxy(obj) &&
   1.744 +           js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler;
   1.745 +}
   1.746 +
   1.747 +bool
   1.748 +xpc::SandboxCallableProxyHandler::call(JSContext *cx, JS::Handle<JSObject*> proxy,
   1.749 +                                       const JS::CallArgs &args)
   1.750 +{
   1.751 +    // We forward the call to our underlying callable.
   1.752 +
   1.753 +    // The parent of our proxy is the SandboxProxyHandler proxy
   1.754 +    RootedObject sandboxProxy(cx, JS_GetParent(proxy));
   1.755 +    MOZ_ASSERT(js::IsProxy(sandboxProxy) &&
   1.756 +               js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler);
   1.757 +
   1.758 +    // The parent of the sandboxProxy is the sandbox global, and the
   1.759 +    // target object is the original proto.
   1.760 +    RootedObject sandboxGlobal(cx, JS_GetParent(sandboxProxy));
   1.761 +    MOZ_ASSERT(js::GetObjectJSClass(sandboxGlobal) == &SandboxClass);
   1.762 +
   1.763 +    // If our this object is the sandbox global, we call with this set to the
   1.764 +    // original proto instead.
   1.765 +    //
   1.766 +    // There are two different ways we can compute |this|. If we use
   1.767 +    // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the
   1.768 +    // caller, which may be undefined if a global function was invoked without
   1.769 +    // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this|
   1.770 +    // in |vp| will be coerced to the global, which is not the correct
   1.771 +    // behavior in ES5 strict mode. And we have no way to compute strictness
   1.772 +    // here.
   1.773 +    //
   1.774 +    // The naive approach is simply to use JS_THIS_VALUE here. If |this| was
   1.775 +    // explicit, we can remap it appropriately. If it was implicit, then we
   1.776 +    // leave it as undefined, and let the callee sort it out. Since the callee
   1.777 +    // is generally in the same compartment as its global (eg the Window's
   1.778 +    // compartment, not the Sandbox's), the callee will generally compute the
   1.779 +    // correct |this|.
   1.780 +    //
   1.781 +    // However, this breaks down in the Xray case. If the sandboxPrototype
   1.782 +    // is an Xray wrapper, then we'll end up reifying the native methods in
   1.783 +    // the Sandbox's scope, which means that they'll compute |this| to be the
   1.784 +    // Sandbox, breaking old-style XPC_WN_CallMethod methods.
   1.785 +    //
   1.786 +    // Luckily, the intent of Xrays is to provide a vanilla view of a foreign
   1.787 +    // DOM interface, which means that we don't care about script-enacted
   1.788 +    // strictness in the prototype's home compartment. Indeed, since DOM
   1.789 +    // methods are always non-strict, we can just assume non-strict semantics
   1.790 +    // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately
   1.791 +    // remap |this|.
   1.792 +    bool isXray = WrapperFactory::IsXrayWrapper(sandboxProxy);
   1.793 +    RootedValue thisVal(cx, isXray ? args.computeThis(cx) : args.thisv());
   1.794 +    if (thisVal == ObjectValue(*sandboxGlobal)) {
   1.795 +        thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy));
   1.796 +    }
   1.797 +
   1.798 +    RootedValue func(cx, js::GetProxyPrivate(proxy));
   1.799 +    return JS::Call(cx, thisVal, func, args, args.rval());
   1.800 +}
   1.801 +
   1.802 +xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler;
   1.803 +
   1.804 +/*
   1.805 + * Wrap a callable such that if we're called with oldThisObj as the
   1.806 + * "this" we will instead call it with newThisObj as the this.
   1.807 + */
   1.808 +static JSObject*
   1.809 +WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy)
   1.810 +{
   1.811 +    MOZ_ASSERT(JS_ObjectIsCallable(cx, callable));
   1.812 +    // Our proxy is wrapping the callable.  So we need to use the
   1.813 +    // callable as the private.  We use the given sandboxProtoProxy as
   1.814 +    // the parent, and our call() hook depends on that.
   1.815 +    MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) &&
   1.816 +               js::GetProxyHandler(sandboxProtoProxy) ==
   1.817 +                 &xpc::sandboxProxyHandler);
   1.818 +
   1.819 +    RootedValue priv(cx, ObjectValue(*callable));
   1.820 +    js::ProxyOptions options;
   1.821 +    options.selectDefaultClass(true);
   1.822 +    return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler,
   1.823 +                              priv, nullptr,
   1.824 +                              sandboxProtoProxy, options);
   1.825 +}
   1.826 +
   1.827 +template<typename Op>
   1.828 +bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id,
   1.829 +                    unsigned attrFlag, HandleObject sandboxProtoProxy)
   1.830 +{
   1.831 +    if (!op) {
   1.832 +        return true;
   1.833 +    }
   1.834 +
   1.835 +    RootedObject func(cx);
   1.836 +    if (desc->attrs & attrFlag) {
   1.837 +        // Already an object
   1.838 +        func = JS_FUNC_TO_DATA_PTR(JSObject *, op);
   1.839 +    } else {
   1.840 +        // We have an actual property op.  For getters, we use 0
   1.841 +        // args, for setters we use 1 arg.
   1.842 +        uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1;
   1.843 +        RootedObject obj(cx, desc->obj);
   1.844 +        func = GeneratePropertyOp(cx, obj, id, args, op);
   1.845 +        if (!func)
   1.846 +            return false;
   1.847 +    }
   1.848 +    func = WrapCallable(cx, func, sandboxProtoProxy);
   1.849 +    if (!func)
   1.850 +        return false;
   1.851 +    op = JS_DATA_TO_FUNC_PTR(Op, func.get());
   1.852 +    desc->attrs |= attrFlag;
   1.853 +    return true;
   1.854 +}
   1.855 +
   1.856 +extern bool
   1.857 +XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp);
   1.858 +extern bool
   1.859 +XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp);
   1.860 +
   1.861 +bool
   1.862 +xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx,
   1.863 +                                                JS::Handle<JSObject*> proxy,
   1.864 +                                                JS::Handle<jsid> id,
   1.865 +                                                JS::MutableHandle<JSPropertyDescriptor> desc)
   1.866 +{
   1.867 +    JS::RootedObject obj(cx, wrappedObject(proxy));
   1.868 +
   1.869 +    MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy));
   1.870 +    if (!JS_GetPropertyDescriptorById(cx, obj, id, desc))
   1.871 +        return false;
   1.872 +
   1.873 +    if (!desc.object())
   1.874 +        return true; // No property, nothing to do
   1.875 +
   1.876 +    // Now fix up the getter/setter/value as needed to be bound to desc->obj
   1.877 +    // Don't mess with holder_get and holder_set, though, because those rely on
   1.878 +    // the "vp is prefilled with the value in the slot" behavior that property
   1.879 +    // ops can in theory rely on, but our property op forwarder doesn't know how
   1.880 +    // to make that happen.  Since we really only need to rebind the DOM methods
   1.881 +    // here, not rebindings holder_get and holder_set is OK.
   1.882 +    //
   1.883 +    // Similarly, don't mess with XPC_WN_Helper_GetProperty and
   1.884 +    // XPC_WN_Helper_SetProperty, for the same reasons: that could confuse our
   1.885 +    // access to expandos when we're not doing Xrays.
   1.886 +    if (desc.getter() != xpc::holder_get &&
   1.887 +        desc.getter() != XPC_WN_Helper_GetProperty &&
   1.888 +        !BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy))
   1.889 +        return false;
   1.890 +    if (desc.setter() != xpc::holder_set &&
   1.891 +        desc.setter() != XPC_WN_Helper_SetProperty &&
   1.892 +        !BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy))
   1.893 +        return false;
   1.894 +    if (desc.value().isObject()) {
   1.895 +        JSObject* val = &desc.value().toObject();
   1.896 +        if (JS_ObjectIsCallable(cx, val)) {
   1.897 +            val = WrapCallable(cx, val, proxy);
   1.898 +            if (!val)
   1.899 +                return false;
   1.900 +            desc.value().setObject(*val);
   1.901 +        }
   1.902 +    }
   1.903 +
   1.904 +    return true;
   1.905 +}
   1.906 +
   1.907 +bool
   1.908 +xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext *cx,
   1.909 +                                                   JS::Handle<JSObject*> proxy,
   1.910 +                                                   JS::Handle<jsid> id,
   1.911 +                                                   JS::MutableHandle<JSPropertyDescriptor> desc)
   1.912 +{
   1.913 +    if (!getPropertyDescriptor(cx, proxy, id, desc))
   1.914 +        return false;
   1.915 +
   1.916 +    if (desc.object() != wrappedObject(proxy))
   1.917 +        desc.object().set(nullptr);
   1.918 +
   1.919 +    return true;
   1.920 +}
   1.921 +
   1.922 +/*
   1.923 + * Reuse the BaseProxyHandler versions of the derived traps that are implemented
   1.924 + * in terms of the fundamental traps.
   1.925 + */
   1.926 +
   1.927 +bool
   1.928 +xpc::SandboxProxyHandler::has(JSContext *cx, JS::Handle<JSObject*> proxy,
   1.929 +                              JS::Handle<jsid> id, bool *bp)
   1.930 +{
   1.931 +    return BaseProxyHandler::has(cx, proxy, id, bp);
   1.932 +}
   1.933 +bool
   1.934 +xpc::SandboxProxyHandler::hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
   1.935 +                                 JS::Handle<jsid> id, bool *bp)
   1.936 +{
   1.937 +    return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
   1.938 +}
   1.939 +
   1.940 +bool
   1.941 +xpc::SandboxProxyHandler::get(JSContext *cx, JS::Handle<JSObject*> proxy,
   1.942 +                              JS::Handle<JSObject*> receiver,
   1.943 +                              JS::Handle<jsid> id,
   1.944 +                              JS::MutableHandle<Value> vp)
   1.945 +{
   1.946 +    return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
   1.947 +}
   1.948 +
   1.949 +bool
   1.950 +xpc::SandboxProxyHandler::set(JSContext *cx, JS::Handle<JSObject*> proxy,
   1.951 +                              JS::Handle<JSObject*> receiver,
   1.952 +                              JS::Handle<jsid> id,
   1.953 +                              bool strict,
   1.954 +                              JS::MutableHandle<Value> vp)
   1.955 +{
   1.956 +    return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
   1.957 +}
   1.958 +
   1.959 +bool
   1.960 +xpc::SandboxProxyHandler::keys(JSContext *cx, JS::Handle<JSObject*> proxy,
   1.961 +                               AutoIdVector &props)
   1.962 +{
   1.963 +    return BaseProxyHandler::keys(cx, proxy, props);
   1.964 +}
   1.965 +
   1.966 +bool
   1.967 +xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
   1.968 +                                  unsigned flags, JS::MutableHandle<Value> vp)
   1.969 +{
   1.970 +    return BaseProxyHandler::iterate(cx, proxy, flags, vp);
   1.971 +}
   1.972 +
   1.973 +bool
   1.974 +xpc::GlobalProperties::Parse(JSContext *cx, JS::HandleObject obj)
   1.975 +{
   1.976 +    MOZ_ASSERT(JS_IsArrayObject(cx, obj));
   1.977 +
   1.978 +    uint32_t length;
   1.979 +    bool ok = JS_GetArrayLength(cx, obj, &length);
   1.980 +    NS_ENSURE_TRUE(ok, false);
   1.981 +    bool promise = Promise;
   1.982 +    for (uint32_t i = 0; i < length; i++) {
   1.983 +        RootedValue nameValue(cx);
   1.984 +        ok = JS_GetElement(cx, obj, i, &nameValue);
   1.985 +        NS_ENSURE_TRUE(ok, false);
   1.986 +        if (!nameValue.isString()) {
   1.987 +            JS_ReportError(cx, "Property names must be strings");
   1.988 +            return false;
   1.989 +        }
   1.990 +        JSAutoByteString name(cx, nameValue.toString());
   1.991 +        NS_ENSURE_TRUE(name, false);
   1.992 +        if (promise && !strcmp(name.ptr(), "-Promise")) {
   1.993 +            Promise = false;
   1.994 +        } else if (!strcmp(name.ptr(), "indexedDB")) {
   1.995 +            indexedDB = true;
   1.996 +        } else if (!strcmp(name.ptr(), "XMLHttpRequest")) {
   1.997 +            XMLHttpRequest = true;
   1.998 +        } else if (!strcmp(name.ptr(), "TextEncoder")) {
   1.999 +            TextEncoder = true;
  1.1000 +        } else if (!strcmp(name.ptr(), "TextDecoder")) {
  1.1001 +            TextDecoder = true;
  1.1002 +        } else if (!strcmp(name.ptr(), "URL")) {
  1.1003 +            URL = true;
  1.1004 +        } else if (!strcmp(name.ptr(), "atob")) {
  1.1005 +            atob = true;
  1.1006 +        } else if (!strcmp(name.ptr(), "btoa")) {
  1.1007 +            btoa = true;
  1.1008 +        } else {
  1.1009 +            JS_ReportError(cx, "Unknown property name: %s", name.ptr());
  1.1010 +            return false;
  1.1011 +        }
  1.1012 +    }
  1.1013 +    return true;
  1.1014 +}
  1.1015 +
  1.1016 +bool
  1.1017 +xpc::GlobalProperties::Define(JSContext *cx, JS::HandleObject obj)
  1.1018 +{
  1.1019 +    if (Promise && !dom::PromiseBinding::GetConstructorObject(cx, obj))
  1.1020 +        return false;
  1.1021 +
  1.1022 +    if (indexedDB && AccessCheck::isChrome(obj) &&
  1.1023 +        !IndexedDatabaseManager::DefineIndexedDB(cx, obj))
  1.1024 +        return false;
  1.1025 +
  1.1026 +    if (XMLHttpRequest &&
  1.1027 +        !JS_DefineFunction(cx, obj, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR))
  1.1028 +        return false;
  1.1029 +
  1.1030 +    if (TextEncoder &&
  1.1031 +        !dom::TextEncoderBinding::GetConstructorObject(cx, obj))
  1.1032 +        return false;
  1.1033 +
  1.1034 +    if (TextDecoder &&
  1.1035 +        !dom::TextDecoderBinding::GetConstructorObject(cx, obj))
  1.1036 +        return false;
  1.1037 +
  1.1038 +    if (URL &&
  1.1039 +        !dom::URLBinding::GetConstructorObject(cx, obj))
  1.1040 +        return false;
  1.1041 +
  1.1042 +    if (atob &&
  1.1043 +        !JS_DefineFunction(cx, obj, "atob", Atob, 1, 0))
  1.1044 +        return false;
  1.1045 +
  1.1046 +    if (btoa &&
  1.1047 +        !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0))
  1.1048 +        return false;
  1.1049 +
  1.1050 +    return true;
  1.1051 +}
  1.1052 +
  1.1053 +nsresult
  1.1054 +xpc::CreateSandboxObject(JSContext *cx, MutableHandleValue vp, nsISupports *prinOrSop,
  1.1055 +                         SandboxOptions& options)
  1.1056 +{
  1.1057 +    // Create the sandbox global object
  1.1058 +    nsresult rv;
  1.1059 +    nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
  1.1060 +    if (NS_FAILED(rv))
  1.1061 +        return NS_ERROR_XPC_UNEXPECTED;
  1.1062 +
  1.1063 +    nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
  1.1064 +    if (!principal) {
  1.1065 +        nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(prinOrSop);
  1.1066 +        if (sop) {
  1.1067 +            principal = sop->GetPrincipal();
  1.1068 +        } else {
  1.1069 +            principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
  1.1070 +            MOZ_ASSERT(NS_FAILED(rv) || principal, "Bad return from do_CreateInstance");
  1.1071 +
  1.1072 +            if (!principal || NS_FAILED(rv)) {
  1.1073 +                if (NS_SUCCEEDED(rv)) {
  1.1074 +                    rv = NS_ERROR_FAILURE;
  1.1075 +                }
  1.1076 +
  1.1077 +                return rv;
  1.1078 +            }
  1.1079 +        }
  1.1080 +        MOZ_ASSERT(principal);
  1.1081 +    }
  1.1082 +
  1.1083 +    JS::CompartmentOptions compartmentOptions;
  1.1084 +    if (options.sameZoneAs)
  1.1085 +        compartmentOptions.setSameZoneAs(js::UncheckedUnwrap(options.sameZoneAs));
  1.1086 +    else
  1.1087 +        compartmentOptions.setZone(JS::SystemZone);
  1.1088 +
  1.1089 +    compartmentOptions.setInvisibleToDebugger(options.invisibleToDebugger)
  1.1090 +                      .setDiscardSource(options.discardSource)
  1.1091 +                      .setTrace(TraceXPCGlobal);
  1.1092 +
  1.1093 +    RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, &SandboxClass,
  1.1094 +                                                     principal, compartmentOptions));
  1.1095 +    if (!sandbox)
  1.1096 +        return NS_ERROR_FAILURE;
  1.1097 +
  1.1098 +    // Set up the wantXrays flag, which indicates whether xrays are desired even
  1.1099 +    // for same-origin access.
  1.1100 +    //
  1.1101 +    // This flag has historically been ignored for chrome sandboxes due to
  1.1102 +    // quirks in the wrapping implementation that have now been removed. Indeed,
  1.1103 +    // same-origin Xrays for chrome->chrome access seems a bit superfluous.
  1.1104 +    // Arguably we should just flip the default for chrome and still honor the
  1.1105 +    // flag, but such a change would break code in subtle ways for minimal
  1.1106 +    // benefit. So we just switch it off here.
  1.1107 +    xpc::GetCompartmentPrivate(sandbox)->wantXrays =
  1.1108 +      AccessCheck::isChrome(sandbox) ? false : options.wantXrays;
  1.1109 +
  1.1110 +    {
  1.1111 +        JSAutoCompartment ac(cx, sandbox);
  1.1112 +
  1.1113 +        if (options.proto) {
  1.1114 +            bool ok = JS_WrapObject(cx, &options.proto);
  1.1115 +            if (!ok)
  1.1116 +                return NS_ERROR_XPC_UNEXPECTED;
  1.1117 +
  1.1118 +            if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) {
  1.1119 +                RootedValue v(cx, ObjectValue(*options.proto));
  1.1120 +                if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v))
  1.1121 +                    return NS_ERROR_FAILURE;
  1.1122 +                options.proto = &v.toObject();
  1.1123 +            }
  1.1124 +
  1.1125 +            // Now check what sort of thing we've got in |proto|
  1.1126 +            JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false);
  1.1127 +            const js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto);
  1.1128 +            if (IS_WN_CLASS(unwrappedClass) ||
  1.1129 +                mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) {
  1.1130 +                // Wrap it up in a proxy that will do the right thing in terms
  1.1131 +                // of this-binding for methods.
  1.1132 +                RootedValue priv(cx, ObjectValue(*options.proto));
  1.1133 +                options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler,
  1.1134 +                                                   priv, nullptr, sandbox);
  1.1135 +                if (!options.proto)
  1.1136 +                    return NS_ERROR_OUT_OF_MEMORY;
  1.1137 +            }
  1.1138 +
  1.1139 +            ok = JS_SetPrototype(cx, sandbox, options.proto);
  1.1140 +            if (!ok)
  1.1141 +                return NS_ERROR_XPC_UNEXPECTED;
  1.1142 +        }
  1.1143 +
  1.1144 +        nsCOMPtr<nsIScriptObjectPrincipal> sbp =
  1.1145 +            new SandboxPrivate(principal, sandbox);
  1.1146 +
  1.1147 +        // Pass on ownership of sbp to |sandbox|.
  1.1148 +        JS_SetPrivate(sandbox, sbp.forget().take());
  1.1149 +
  1.1150 +        bool allowComponents = nsContentUtils::IsSystemPrincipal(principal) ||
  1.1151 +                               nsContentUtils::IsExpandedPrincipal(principal);
  1.1152 +        if (options.wantComponents && allowComponents &&
  1.1153 +            !GetObjectScope(sandbox)->AttachComponentsObject(cx))
  1.1154 +            return NS_ERROR_XPC_UNEXPECTED;
  1.1155 +
  1.1156 +        if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox))
  1.1157 +            return NS_ERROR_XPC_UNEXPECTED;
  1.1158 +
  1.1159 +        if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions))
  1.1160 +            return NS_ERROR_XPC_UNEXPECTED;
  1.1161 +
  1.1162 +        if (options.wantExportHelpers &&
  1.1163 +            (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) ||
  1.1164 +             !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0) ||
  1.1165 +             !JS_DefineFunction(cx, sandbox, "createObjectIn", CreateObjectIn, 2, 0) ||
  1.1166 +             !JS_DefineFunction(cx, sandbox, "cloneInto", CloneInto, 3, 0) ||
  1.1167 +             !JS_DefineFunction(cx, sandbox, "isProxy", IsProxy, 1, 0)))
  1.1168 +            return NS_ERROR_XPC_UNEXPECTED;
  1.1169 +
  1.1170 +        if (!options.globalProperties.Define(cx, sandbox))
  1.1171 +            return NS_ERROR_XPC_UNEXPECTED;
  1.1172 +    }
  1.1173 +
  1.1174 +
  1.1175 +    // We have this crazy behavior where wantXrays=false also implies that the
  1.1176 +    // returned sandbox is implicitly waived. We've stopped advertising it, but
  1.1177 +    // keep supporting it for now.
  1.1178 +    vp.setObject(*sandbox);
  1.1179 +    if (options.wantXrays && !JS_WrapValue(cx, vp))
  1.1180 +        return NS_ERROR_UNEXPECTED;
  1.1181 +    if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp))
  1.1182 +        return NS_ERROR_UNEXPECTED;
  1.1183 +
  1.1184 +    // Set the location information for the new global, so that tools like
  1.1185 +    // about:memory may use that information
  1.1186 +    xpc::SetLocationForGlobal(sandbox, options.sandboxName);
  1.1187 +
  1.1188 +    xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
  1.1189 +
  1.1190 +    JS_FireOnNewGlobalObject(cx, sandbox);
  1.1191 +
  1.1192 +    return NS_OK;
  1.1193 +}
  1.1194 +
  1.1195 +/* bool call(in nsIXPConnectWrappedNative wrapper,
  1.1196 + *           in JSContextPtr cx,
  1.1197 + *           in JSObjectPtr obj,
  1.1198 + *           in uint32_t argc,
  1.1199 + *           in JSValPtr argv,
  1.1200 + *           in JSValPtr vp);
  1.1201 + */
  1.1202 +NS_IMETHODIMP
  1.1203 +nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
  1.1204 +                                    JSObject *objArg, const CallArgs &args, bool *_retval)
  1.1205 +{
  1.1206 +    RootedObject obj(cx, objArg);
  1.1207 +    return CallOrConstruct(wrapper, cx, obj, args, _retval);
  1.1208 +}
  1.1209 +
  1.1210 +/* bool construct(in nsIXPConnectWrappedNative wrapper,
  1.1211 + *                in JSContextPtr cx,
  1.1212 + *                in JSObjectPtr obj,
  1.1213 + *                in uint32_t argc,
  1.1214 + *                in JSValPtr argv,
  1.1215 + *                in JSValPtr vp);
  1.1216 + */
  1.1217 +NS_IMETHODIMP
  1.1218 +nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
  1.1219 +                                         JSObject *objArg, const CallArgs &args, bool *_retval)
  1.1220 +{
  1.1221 +    RootedObject obj(cx, objArg);
  1.1222 +    return CallOrConstruct(wrapper, cx, obj, args, _retval);
  1.1223 +}
  1.1224 +
  1.1225 +/*
  1.1226 + * For sandbox constructor the first argument can be a URI string in which case
  1.1227 + * we use the related Codebase Principal for the sandbox.
  1.1228 + */
  1.1229 +bool
  1.1230 +ParsePrincipal(JSContext *cx, HandleString codebase, nsIPrincipal **principal)
  1.1231 +{
  1.1232 +    MOZ_ASSERT(principal);
  1.1233 +    MOZ_ASSERT(codebase);
  1.1234 +    nsCOMPtr<nsIURI> uri;
  1.1235 +    nsDependentJSString codebaseStr;
  1.1236 +    NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), false);
  1.1237 +    nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr);
  1.1238 +    if (NS_FAILED(rv)) {
  1.1239 +        JS_ReportError(cx, "Creating URI from string failed");
  1.1240 +        return false;
  1.1241 +    }
  1.1242 +
  1.1243 +    nsCOMPtr<nsIScriptSecurityManager> secman =
  1.1244 +        do_GetService(kScriptSecurityManagerContractID);
  1.1245 +    NS_ENSURE_TRUE(secman, false);
  1.1246 +
  1.1247 +    // We could allow passing in the app-id and browser-element info to the
  1.1248 +    // sandbox constructor. But creating a sandbox based on a string is a
  1.1249 +    // deprecated API so no need to add features to it.
  1.1250 +    rv = secman->GetNoAppCodebasePrincipal(uri, principal);
  1.1251 +    if (NS_FAILED(rv) || !*principal) {
  1.1252 +        JS_ReportError(cx, "Creating Principal from URI failed");
  1.1253 +        return false;
  1.1254 +    }
  1.1255 +    return true;
  1.1256 +}
  1.1257 +
  1.1258 +/*
  1.1259 + * For sandbox constructor the first argument can be a principal object or
  1.1260 + * a script object principal (Document, Window).
  1.1261 + */
  1.1262 +static bool
  1.1263 +GetPrincipalOrSOP(JSContext *cx, HandleObject from, nsISupports **out)
  1.1264 +{
  1.1265 +    MOZ_ASSERT(out);
  1.1266 +    *out = nullptr;
  1.1267 +
  1.1268 +    nsXPConnect* xpc = nsXPConnect::XPConnect();
  1.1269 +    nsISupports* native = xpc->GetNativeOfWrapper(cx, from);
  1.1270 +
  1.1271 +    if (nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native)) {
  1.1272 +        sop.forget(out);
  1.1273 +        return true;
  1.1274 +    }
  1.1275 +
  1.1276 +    nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(native);
  1.1277 +    principal.forget(out);
  1.1278 +    NS_ENSURE_TRUE(*out, false);
  1.1279 +
  1.1280 +    return true;
  1.1281 +}
  1.1282 +
  1.1283 +/*
  1.1284 + * The first parameter of the sandbox constructor might be an array of principals, either in string
  1.1285 + * format or actual objects (see GetPrincipalOrSOP)
  1.1286 + */
  1.1287 +static bool
  1.1288 +GetExpandedPrincipal(JSContext *cx, HandleObject arrayObj, nsIExpandedPrincipal **out)
  1.1289 +{
  1.1290 +    MOZ_ASSERT(out);
  1.1291 +    uint32_t length;
  1.1292 +
  1.1293 +    if (!JS_IsArrayObject(cx, arrayObj) ||
  1.1294 +        !JS_GetArrayLength(cx, arrayObj, &length) ||
  1.1295 +        !length)
  1.1296 +    {
  1.1297 +        // We need a whitelist of principals or uri strings to create an
  1.1298 +        // expanded principal, if we got an empty array or something else
  1.1299 +        // report error.
  1.1300 +        JS_ReportError(cx, "Expected an array of URI strings");
  1.1301 +        return false;
  1.1302 +    }
  1.1303 +
  1.1304 +    nsTArray< nsCOMPtr<nsIPrincipal> > allowedDomains(length);
  1.1305 +    allowedDomains.SetLength(length);
  1.1306 +    nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
  1.1307 +    NS_ENSURE_TRUE(ssm, false);
  1.1308 +
  1.1309 +    for (uint32_t i = 0; i < length; ++i) {
  1.1310 +        RootedValue allowed(cx);
  1.1311 +        if (!JS_GetElement(cx, arrayObj, i, &allowed))
  1.1312 +            return false;
  1.1313 +
  1.1314 +        nsresult rv;
  1.1315 +        nsCOMPtr<nsIPrincipal> principal;
  1.1316 +        if (allowed.isString()) {
  1.1317 +            // In case of string let's try to fetch a codebase principal from it.
  1.1318 +            RootedString str(cx, allowed.toString());
  1.1319 +            if (!ParsePrincipal(cx, str, getter_AddRefs(principal)))
  1.1320 +                return false;
  1.1321 +
  1.1322 +        } else if (allowed.isObject()) {
  1.1323 +            // In case of object let's see if it's a Principal or a ScriptObjectPrincipal.
  1.1324 +            nsCOMPtr<nsISupports> prinOrSop;
  1.1325 +            RootedObject obj(cx, &allowed.toObject());
  1.1326 +            if (!GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)))
  1.1327 +                return false;
  1.1328 +
  1.1329 +            nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(prinOrSop));
  1.1330 +            principal = do_QueryInterface(prinOrSop);
  1.1331 +            if (sop)
  1.1332 +                principal = sop->GetPrincipal();
  1.1333 +        }
  1.1334 +        NS_ENSURE_TRUE(principal, false);
  1.1335 +
  1.1336 +        // We do not allow ExpandedPrincipals to contain any system principals.
  1.1337 +        bool isSystem;
  1.1338 +        rv = ssm->IsSystemPrincipal(principal, &isSystem);
  1.1339 +        NS_ENSURE_SUCCESS(rv, false);
  1.1340 +        if (isSystem) {
  1.1341 +            JS_ReportError(cx, "System principal is not allowed in an expanded principal");
  1.1342 +            return false;
  1.1343 +        }
  1.1344 +        allowedDomains[i] = principal;
  1.1345 +  }
  1.1346 +
  1.1347 +  nsCOMPtr<nsIExpandedPrincipal> result = new nsExpandedPrincipal(allowedDomains);
  1.1348 +  result.forget(out);
  1.1349 +  return true;
  1.1350 +}
  1.1351 +
  1.1352 +/*
  1.1353 + * Helper that tries to get a property from the options object.
  1.1354 + */
  1.1355 +bool
  1.1356 +OptionsBase::ParseValue(const char *name, MutableHandleValue prop, bool *aFound)
  1.1357 +{
  1.1358 +    bool found;
  1.1359 +    bool ok = JS_HasProperty(mCx, mObject, name, &found);
  1.1360 +    NS_ENSURE_TRUE(ok, false);
  1.1361 +
  1.1362 +    if (aFound)
  1.1363 +        *aFound = found;
  1.1364 +
  1.1365 +    if (!found)
  1.1366 +        return true;
  1.1367 +
  1.1368 +    return JS_GetProperty(mCx, mObject, name, prop);
  1.1369 +}
  1.1370 +
  1.1371 +/*
  1.1372 + * Helper that tries to get a boolean property from the options object.
  1.1373 + */
  1.1374 +bool
  1.1375 +OptionsBase::ParseBoolean(const char *name, bool *prop)
  1.1376 +{
  1.1377 +    MOZ_ASSERT(prop);
  1.1378 +    RootedValue value(mCx);
  1.1379 +    bool found;
  1.1380 +    bool ok = ParseValue(name, &value, &found);
  1.1381 +    NS_ENSURE_TRUE(ok, false);
  1.1382 +
  1.1383 +    if (!found)
  1.1384 +        return true;
  1.1385 +
  1.1386 +    if (!value.isBoolean()) {
  1.1387 +        JS_ReportError(mCx, "Expected a boolean value for property %s", name);
  1.1388 +        return false;
  1.1389 +    }
  1.1390 +
  1.1391 +    *prop = value.toBoolean();
  1.1392 +    return true;
  1.1393 +}
  1.1394 +
  1.1395 +/*
  1.1396 + * Helper that tries to get an object property from the options object.
  1.1397 + */
  1.1398 +bool
  1.1399 +OptionsBase::ParseObject(const char *name, MutableHandleObject prop)
  1.1400 +{
  1.1401 +    RootedValue value(mCx);
  1.1402 +    bool found;
  1.1403 +    bool ok = ParseValue(name, &value, &found);
  1.1404 +    NS_ENSURE_TRUE(ok, false);
  1.1405 +
  1.1406 +    if (!found)
  1.1407 +        return true;
  1.1408 +
  1.1409 +    if (!value.isObject()) {
  1.1410 +        JS_ReportError(mCx, "Expected an object value for property %s", name);
  1.1411 +        return false;
  1.1412 +    }
  1.1413 +    prop.set(&value.toObject());
  1.1414 +    return true;
  1.1415 +}
  1.1416 +
  1.1417 +/*
  1.1418 + * Helper that tries to get a string property from the options object.
  1.1419 + */
  1.1420 +bool
  1.1421 +OptionsBase::ParseString(const char *name, nsCString &prop)
  1.1422 +{
  1.1423 +    RootedValue value(mCx);
  1.1424 +    bool found;
  1.1425 +    bool ok = ParseValue(name, &value, &found);
  1.1426 +    NS_ENSURE_TRUE(ok, false);
  1.1427 +
  1.1428 +    if (!found)
  1.1429 +        return true;
  1.1430 +
  1.1431 +    if (!value.isString()) {
  1.1432 +        JS_ReportError(mCx, "Expected a string value for property %s", name);
  1.1433 +        return false;
  1.1434 +    }
  1.1435 +
  1.1436 +    char *tmp = JS_EncodeString(mCx, value.toString());
  1.1437 +    NS_ENSURE_TRUE(tmp, false);
  1.1438 +    prop.Adopt(tmp, strlen(tmp));
  1.1439 +    return true;
  1.1440 +}
  1.1441 +
  1.1442 +/*
  1.1443 + * Helper that tries to get a string property from the options object.
  1.1444 + */
  1.1445 +bool
  1.1446 +OptionsBase::ParseString(const char *name, nsString &prop)
  1.1447 +{
  1.1448 +    RootedValue value(mCx);
  1.1449 +    bool found;
  1.1450 +    bool ok = ParseValue(name, &value, &found);
  1.1451 +    NS_ENSURE_TRUE(ok, false);
  1.1452 +
  1.1453 +    if (!found)
  1.1454 +        return true;
  1.1455 +
  1.1456 +    if (!value.isString()) {
  1.1457 +        JS_ReportError(mCx, "Expected a string value for property %s", name);
  1.1458 +        return false;
  1.1459 +    }
  1.1460 +
  1.1461 +    nsDependentJSString strVal;
  1.1462 +    strVal.init(mCx, value.toString());
  1.1463 +    prop = strVal;
  1.1464 +    return true;
  1.1465 +}
  1.1466 +
  1.1467 +/*
  1.1468 + * Helper that tries to get jsid property from the options object.
  1.1469 + */
  1.1470 +bool
  1.1471 +OptionsBase::ParseId(const char *name, MutableHandleId prop)
  1.1472 +{
  1.1473 +    RootedValue value(mCx);
  1.1474 +    bool found;
  1.1475 +    bool ok = ParseValue(name, &value, &found);
  1.1476 +    NS_ENSURE_TRUE(ok, false);
  1.1477 +
  1.1478 +    if (!found)
  1.1479 +        return true;
  1.1480 +
  1.1481 +    return JS_ValueToId(mCx, value, prop);
  1.1482 +}
  1.1483 +
  1.1484 +/*
  1.1485 + * Helper that tries to get a list of DOM constructors and other helpers from the options object.
  1.1486 + */
  1.1487 +bool
  1.1488 +SandboxOptions::ParseGlobalProperties()
  1.1489 +{
  1.1490 +    RootedValue value(mCx);
  1.1491 +    bool found;
  1.1492 +    bool ok = ParseValue("wantGlobalProperties", &value, &found);
  1.1493 +    NS_ENSURE_TRUE(ok, false);
  1.1494 +    if (!found)
  1.1495 +        return true;
  1.1496 +
  1.1497 +    if (!value.isObject()) {
  1.1498 +        JS_ReportError(mCx, "Expected an array value for wantGlobalProperties");
  1.1499 +        return false;
  1.1500 +    }
  1.1501 +
  1.1502 +    RootedObject ctors(mCx, &value.toObject());
  1.1503 +    if (!JS_IsArrayObject(mCx, ctors)) {
  1.1504 +        JS_ReportError(mCx, "Expected an array value for wantGlobalProperties");
  1.1505 +        return false;
  1.1506 +    }
  1.1507 +
  1.1508 +    return globalProperties.Parse(mCx, ctors);
  1.1509 +}
  1.1510 +
  1.1511 +/*
  1.1512 + * Helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options).
  1.1513 + */
  1.1514 +bool
  1.1515 +SandboxOptions::Parse()
  1.1516 +{
  1.1517 +    return ParseObject("sandboxPrototype", &proto) &&
  1.1518 +           ParseBoolean("wantXrays", &wantXrays) &&
  1.1519 +           ParseBoolean("wantComponents", &wantComponents) &&
  1.1520 +           ParseBoolean("wantExportHelpers", &wantExportHelpers) &&
  1.1521 +           ParseString("sandboxName", sandboxName) &&
  1.1522 +           ParseObject("sameZoneAs", &sameZoneAs) &&
  1.1523 +           ParseBoolean("invisibleToDebugger", &invisibleToDebugger) &&
  1.1524 +           ParseBoolean("discardSource", &discardSource) &&
  1.1525 +           ParseGlobalProperties() &&
  1.1526 +           ParseValue("metadata", &metadata);
  1.1527 +}
  1.1528 +
  1.1529 +static nsresult
  1.1530 +AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName)
  1.1531 +{
  1.1532 +    // Use a default name when the caller did not provide a sandboxName.
  1.1533 +    if (sandboxName.IsEmpty())
  1.1534 +        sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]");
  1.1535 +
  1.1536 +    nsXPConnect* xpc = nsXPConnect::XPConnect();
  1.1537 +    // Get the xpconnect native call context.
  1.1538 +    nsAXPCNativeCallContext *cc = nullptr;
  1.1539 +    xpc->GetCurrentNativeCallContext(&cc);
  1.1540 +    NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG);
  1.1541 +
  1.1542 +    // Get the current source info from xpc.
  1.1543 +    nsCOMPtr<nsIStackFrame> frame;
  1.1544 +    xpc->GetCurrentJSStack(getter_AddRefs(frame));
  1.1545 +
  1.1546 +    // Append the caller's location information.
  1.1547 +    if (frame) {
  1.1548 +        nsString location;
  1.1549 +        int32_t lineNumber = 0;
  1.1550 +        frame->GetFilename(location);
  1.1551 +        frame->GetLineNumber(&lineNumber);
  1.1552 +
  1.1553 +        sandboxName.AppendLiteral(" (from: ");
  1.1554 +        sandboxName.Append(NS_ConvertUTF16toUTF8(location));
  1.1555 +        sandboxName.AppendLiteral(":");
  1.1556 +        sandboxName.AppendInt(lineNumber);
  1.1557 +        sandboxName.AppendLiteral(")");
  1.1558 +    }
  1.1559 +
  1.1560 +    return NS_OK;
  1.1561 +}
  1.1562 +
  1.1563 +// static
  1.1564 +nsresult
  1.1565 +nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrapper,
  1.1566 +                                               JSContext *cx, HandleObject obj,
  1.1567 +                                               const CallArgs &args, bool *_retval)
  1.1568 +{
  1.1569 +    if (args.length() < 1)
  1.1570 +        return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval);
  1.1571 +
  1.1572 +    nsresult rv;
  1.1573 +    bool ok = false;
  1.1574 +
  1.1575 +    // Make sure to set up principals on the sandbox before initing classes.
  1.1576 +    nsCOMPtr<nsIPrincipal> principal;
  1.1577 +    nsCOMPtr<nsIExpandedPrincipal> expanded;
  1.1578 +    nsCOMPtr<nsISupports> prinOrSop;
  1.1579 +
  1.1580 +    if (args[0].isString()) {
  1.1581 +        RootedString str(cx, args[0].toString());
  1.1582 +        ok = ParsePrincipal(cx, str, getter_AddRefs(principal));
  1.1583 +        prinOrSop = principal;
  1.1584 +    } else if (args[0].isObject()) {
  1.1585 +        RootedObject obj(cx, &args[0].toObject());
  1.1586 +        if (JS_IsArrayObject(cx, obj)) {
  1.1587 +            ok = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded));
  1.1588 +            prinOrSop = expanded;
  1.1589 +        } else {
  1.1590 +            ok = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
  1.1591 +        }
  1.1592 +    }
  1.1593 +
  1.1594 +    if (!ok)
  1.1595 +        return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
  1.1596 +
  1.1597 +    bool calledWithOptions = args.length() > 1;
  1.1598 +    if (calledWithOptions && !args[1].isObject())
  1.1599 +        return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
  1.1600 +
  1.1601 +    RootedObject optionsObject(cx, calledWithOptions ? &args[1].toObject()
  1.1602 +                                                     : nullptr);
  1.1603 +
  1.1604 +    SandboxOptions options(cx, optionsObject);
  1.1605 +    if (calledWithOptions && !options.Parse())
  1.1606 +        return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
  1.1607 +
  1.1608 +    if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName)))
  1.1609 +        return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
  1.1610 +
  1.1611 +    if (options.metadata.isNullOrUndefined()) {
  1.1612 +        // If the caller is running in a sandbox, inherit.
  1.1613 +        RootedObject callerGlobal(cx, CurrentGlobalOrNull(cx));
  1.1614 +        if (IsSandbox(callerGlobal)) {
  1.1615 +            rv = GetSandboxMetadata(cx, callerGlobal, &options.metadata);
  1.1616 +            if (NS_WARN_IF(NS_FAILED(rv)))
  1.1617 +                return rv;
  1.1618 +        }
  1.1619 +    }
  1.1620 +
  1.1621 +    rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options);
  1.1622 +
  1.1623 +    if (NS_FAILED(rv))
  1.1624 +        return ThrowAndFail(rv, cx, _retval);
  1.1625 +
  1.1626 +    *_retval = true;
  1.1627 +    return NS_OK;
  1.1628 +}
  1.1629 +
  1.1630 +class ContextHolder : public nsIScriptObjectPrincipal
  1.1631 +{
  1.1632 +public:
  1.1633 +    ContextHolder(JSContext *aOuterCx, HandleObject aSandbox, nsIPrincipal *aPrincipal);
  1.1634 +    virtual ~ContextHolder();
  1.1635 +
  1.1636 +    JSContext * GetJSContext()
  1.1637 +    {
  1.1638 +        return mJSContext;
  1.1639 +    }
  1.1640 +
  1.1641 +    nsIPrincipal * GetPrincipal() { return mPrincipal; }
  1.1642 +
  1.1643 +    NS_DECL_ISUPPORTS
  1.1644 +
  1.1645 +private:
  1.1646 +    JSContext* mJSContext;
  1.1647 +    nsCOMPtr<nsIPrincipal> mPrincipal;
  1.1648 +};
  1.1649 +
  1.1650 +NS_IMPL_ISUPPORTS(ContextHolder, nsIScriptObjectPrincipal)
  1.1651 +
  1.1652 +ContextHolder::ContextHolder(JSContext *aOuterCx,
  1.1653 +                             HandleObject aSandbox,
  1.1654 +                             nsIPrincipal *aPrincipal)
  1.1655 +    : mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024)),
  1.1656 +      mPrincipal(aPrincipal)
  1.1657 +{
  1.1658 +    if (mJSContext) {
  1.1659 +        bool isChrome;
  1.1660 +        DebugOnly<nsresult> rv = XPCWrapper::GetSecurityManager()->
  1.1661 +                                   IsSystemPrincipal(mPrincipal, &isChrome);
  1.1662 +        MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.1663 +
  1.1664 +        JS::ContextOptionsRef(mJSContext).setDontReportUncaught(true)
  1.1665 +                                         .setPrivateIsNSISupports(true);
  1.1666 +        js::SetDefaultObjectForContext(mJSContext, aSandbox);
  1.1667 +        JS_SetContextPrivate(mJSContext, this);
  1.1668 +    }
  1.1669 +}
  1.1670 +
  1.1671 +ContextHolder::~ContextHolder()
  1.1672 +{
  1.1673 +    if (mJSContext)
  1.1674 +        JS_DestroyContextNoGC(mJSContext);
  1.1675 +}
  1.1676 +
  1.1677 +nsresult
  1.1678 +xpc::EvalInSandbox(JSContext *cx, HandleObject sandboxArg, const nsAString& source,
  1.1679 +                   const nsACString& filename, int32_t lineNo,
  1.1680 +                   JSVersion jsVersion, bool returnStringOnly, MutableHandleValue rval)
  1.1681 +{
  1.1682 +    JS_AbortIfWrongThread(JS_GetRuntime(cx));
  1.1683 +    rval.set(UndefinedValue());
  1.1684 +
  1.1685 +    bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg);
  1.1686 +    RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg));
  1.1687 +    if (!sandbox || js::GetObjectJSClass(sandbox) != &SandboxClass) {
  1.1688 +        return NS_ERROR_INVALID_ARG;
  1.1689 +    }
  1.1690 +
  1.1691 +    nsIScriptObjectPrincipal *sop =
  1.1692 +        (nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox);
  1.1693 +    MOZ_ASSERT(sop, "Invalid sandbox passed");
  1.1694 +    nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal();
  1.1695 +    NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
  1.1696 +
  1.1697 +    nsAutoCString filenameBuf;
  1.1698 +    if (!filename.IsVoid()) {
  1.1699 +        filenameBuf.Assign(filename);
  1.1700 +    } else {
  1.1701 +        // Default to the spec of the principal.
  1.1702 +        nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf);
  1.1703 +        lineNo = 1;
  1.1704 +    }
  1.1705 +
  1.1706 +    // We create a separate cx to do the sandbox evaluation. Scope it.
  1.1707 +    RootedValue v(cx, UndefinedValue());
  1.1708 +    RootedValue exn(cx, UndefinedValue());
  1.1709 +    bool ok = true;
  1.1710 +    {
  1.1711 +        // Make a special cx for the sandbox and push it.
  1.1712 +        // NB: As soon as the RefPtr goes away, the cx goes away. So declare
  1.1713 +        // it first so that it disappears last.
  1.1714 +        nsRefPtr<ContextHolder> sandcxHolder = new ContextHolder(cx, sandbox, prin);
  1.1715 +        JSContext *sandcx = sandcxHolder->GetJSContext();
  1.1716 +        if (!sandcx) {
  1.1717 +            JS_ReportError(cx, "Can't prepare context for evalInSandbox");
  1.1718 +            return NS_ERROR_OUT_OF_MEMORY;
  1.1719 +        }
  1.1720 +        nsCxPusher pusher;
  1.1721 +        pusher.Push(sandcx);
  1.1722 +        JSAutoCompartment ac(sandcx, sandbox);
  1.1723 +
  1.1724 +        JS::CompileOptions options(sandcx);
  1.1725 +        options.setFileAndLine(filenameBuf.get(), lineNo);
  1.1726 +        if (jsVersion != JSVERSION_DEFAULT)
  1.1727 +               options.setVersion(jsVersion);
  1.1728 +        JS::RootedObject rootedSandbox(sandcx, sandbox);
  1.1729 +        ok = JS::Evaluate(sandcx, rootedSandbox, options,
  1.1730 +                          PromiseFlatString(source).get(), source.Length(), &v);
  1.1731 +        if (ok && returnStringOnly && !v.isUndefined()) {
  1.1732 +            JSString *str = ToString(sandcx, v);
  1.1733 +            ok = !!str;
  1.1734 +            v = ok ? JS::StringValue(str) : JS::UndefinedValue();
  1.1735 +        }
  1.1736 +
  1.1737 +        // If the sandbox threw an exception, grab it off the context.
  1.1738 +        if (JS_GetPendingException(sandcx, &exn)) {
  1.1739 +            MOZ_ASSERT(!ok);
  1.1740 +            JS_ClearPendingException(sandcx);
  1.1741 +            if (returnStringOnly) {
  1.1742 +                // The caller asked for strings only, convert the
  1.1743 +                // exception into a string.
  1.1744 +                JSString *str = ToString(sandcx, exn);
  1.1745 +                exn = str ? JS::StringValue(str) : JS::UndefinedValue();
  1.1746 +            }
  1.1747 +        }
  1.1748 +    }
  1.1749 +
  1.1750 +    //
  1.1751 +    // Alright, we're back on the caller's cx. If an error occured, try to
  1.1752 +    // wrap and set the exception. Otherwise, wrap the return value.
  1.1753 +    //
  1.1754 +
  1.1755 +    if (!ok) {
  1.1756 +        // If we end up without an exception, it was probably due to OOM along
  1.1757 +        // the way, in which case we thow. Otherwise, wrap it.
  1.1758 +        if (exn.isUndefined() || !JS_WrapValue(cx, &exn))
  1.1759 +            return NS_ERROR_OUT_OF_MEMORY;
  1.1760 +
  1.1761 +        // Set the exception on our caller's cx.
  1.1762 +        JS_SetPendingException(cx, exn);
  1.1763 +        return NS_ERROR_FAILURE;
  1.1764 +    }
  1.1765 +
  1.1766 +    // Transitively apply Xray waivers if |sb| was waived.
  1.1767 +    if (waiveXray) {
  1.1768 +        ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
  1.1769 +    } else {
  1.1770 +        ok = JS_WrapValue(cx, &v);
  1.1771 +    }
  1.1772 +    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
  1.1773 +
  1.1774 +    // Whew!
  1.1775 +    rval.set(v);
  1.1776 +    return NS_OK;
  1.1777 +}
  1.1778 +
  1.1779 +static bool
  1.1780 +NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
  1.1781 +{
  1.1782 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1783 +
  1.1784 +    RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
  1.1785 +    MOZ_ASSERT(v.isObject(), "weird function");
  1.1786 +
  1.1787 +    RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
  1.1788 +    if (!obj) {
  1.1789 +        return false;
  1.1790 +    }
  1.1791 +    return JS_CallFunctionValue(cx, obj, v, args, args.rval());
  1.1792 +}
  1.1793 +
  1.1794 +/*
  1.1795 + * Forwards the call to the exported function. Clones all the non reflectors, ignores
  1.1796 + * the |this| argument.
  1.1797 + */
  1.1798 +static bool
  1.1799 +CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
  1.1800 +{
  1.1801 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.1802 +
  1.1803 +    RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
  1.1804 +    NS_ASSERTION(v.isObject(), "weird function");
  1.1805 +    RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject()));
  1.1806 +    {
  1.1807 +        JSAutoCompartment ac(cx, origFunObj);
  1.1808 +        // Note: only the arguments are cloned not the |this| or the |callee|.
  1.1809 +        // Function forwarder does not use those.
  1.1810 +        for (unsigned i = 0; i < args.length(); i++) {
  1.1811 +            if (!CloneNonReflectors(cx, args[i])) {
  1.1812 +                return false;
  1.1813 +            }
  1.1814 +        }
  1.1815 +
  1.1816 +        // JS API does not support any JSObject to JSFunction conversion,
  1.1817 +        // so let's use JS_CallFunctionValue instead.
  1.1818 +        RootedValue functionVal(cx, ObjectValue(*origFunObj));
  1.1819 +
  1.1820 +        if (!JS_CallFunctionValue(cx, JS::NullPtr(), functionVal, args, args.rval()))
  1.1821 +            return false;
  1.1822 +    }
  1.1823 +
  1.1824 +    // Return value must be wrapped.
  1.1825 +    return JS_WrapValue(cx, args.rval());
  1.1826 +}
  1.1827 +
  1.1828 +bool
  1.1829 +xpc::NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone,
  1.1830 +                          MutableHandleValue vp)
  1.1831 +{
  1.1832 +    JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder :
  1.1833 +                                                                    NonCloningFunctionForwarder,
  1.1834 +                                                                    0,0, JS::CurrentGlobalOrNull(cx), id);
  1.1835 +
  1.1836 +    if (!fun)
  1.1837 +        return false;
  1.1838 +
  1.1839 +    JSObject *funobj = JS_GetFunctionObject(fun);
  1.1840 +    js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable));
  1.1841 +    vp.setObject(*funobj);
  1.1842 +    return true;
  1.1843 +}
  1.1844 +
  1.1845 +bool
  1.1846 +xpc::NewFunctionForwarder(JSContext *cx, HandleObject callable, bool doclone,
  1.1847 +                          MutableHandleValue vp)
  1.1848 +{
  1.1849 +    RootedId emptyId(cx);
  1.1850 +    RootedValue emptyStringValue(cx, JS_GetEmptyStringValue(cx));
  1.1851 +    if (!JS_ValueToId(cx, emptyStringValue, &emptyId))
  1.1852 +        return false;
  1.1853 +
  1.1854 +    return NewFunctionForwarder(cx, emptyId, callable, doclone, vp);
  1.1855 +}
  1.1856 +
  1.1857 +
  1.1858 +nsresult
  1.1859 +xpc::GetSandboxMetadata(JSContext *cx, HandleObject sandbox, MutableHandleValue rval)
  1.1860 +{
  1.1861 +    MOZ_ASSERT(NS_IsMainThread());
  1.1862 +    MOZ_ASSERT(IsSandbox(sandbox));
  1.1863 +
  1.1864 +    RootedValue metadata(cx);
  1.1865 +    {
  1.1866 +      JSAutoCompartment ac(cx, sandbox);
  1.1867 +      metadata = JS_GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT);
  1.1868 +    }
  1.1869 +
  1.1870 +    if (!JS_WrapValue(cx, &metadata))
  1.1871 +        return NS_ERROR_UNEXPECTED;
  1.1872 +
  1.1873 +    rval.set(metadata);
  1.1874 +    return NS_OK;
  1.1875 +}
  1.1876 +
  1.1877 +nsresult
  1.1878 +xpc::SetSandboxMetadata(JSContext *cx, HandleObject sandbox, HandleValue metadataArg)
  1.1879 +{
  1.1880 +    MOZ_ASSERT(NS_IsMainThread());
  1.1881 +    MOZ_ASSERT(IsSandbox(sandbox));
  1.1882 +
  1.1883 +    RootedValue metadata(cx);
  1.1884 +
  1.1885 +    JSAutoCompartment ac(cx, sandbox);
  1.1886 +    if (!JS_StructuredClone(cx, metadataArg, &metadata, nullptr, nullptr))
  1.1887 +        return NS_ERROR_UNEXPECTED;
  1.1888 +
  1.1889 +    JS_SetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT, metadata);
  1.1890 +
  1.1891 +    return NS_OK;
  1.1892 +}

mercurial