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 +}