author | Bobby Holley <bobbyholley@gmail.com> |
Mon, 23 Jun 2014 13:25:07 -0700 | |
changeset 190324 | b576ed8a17c29f35a7cb4f37296fd398b4644e39 |
parent 190323 | aa2f06e6141a0ee9d5fc50f0dc68a945ae5e60bf |
child 190325 | 4c8faa8089e98d3628a21c26dad47fea6941f11f |
push id | 27004 |
push user | emorley@mozilla.com |
push date | Tue, 24 Jun 2014 15:52:34 +0000 |
treeherder | mozilla-central@7b174d47f3cc [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | gabor |
bugs | 1027131 |
milestone | 33.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /dev/null +++ b/js/xpconnect/src/ExportHelpers.cpp @@ -0,0 +1,447 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "xpcprivate.h" +#include "WrapperFactory.h" +#include "jsfriendapi.h" +#include "jsproxy.h" +#include "jswrapper.h" +#include "js/StructuredClone.h" +#include "mozilla/dom/BindingUtils.h" +#include "nsGlobalWindow.h" +#include "nsJSUtils.h" + +using namespace mozilla; +using namespace JS; +using namespace js; + +namespace xpc { + +bool +IsReflector(JSObject *obj) +{ + return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj); +} + +enum ForwarderCloneTags { + SCTAG_BASE = JS_SCTAG_USER_MIN, + SCTAG_REFLECTOR +}; + +static JSObject * +CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag, + uint32_t data, void *closure) +{ + MOZ_ASSERT(closure, "Null pointer!"); + AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure); + if (tag == SCTAG_REFLECTOR) { + MOZ_ASSERT(!data); + + size_t idx; + if (JS_ReadBytes(reader, &idx, sizeof(size_t))) { + RootedObject reflector(cx, (*reflectors)[idx]); + MOZ_ASSERT(reflector, "No object pointer?"); + MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!"); + + if (!JS_WrapObject(cx, &reflector)) + return nullptr; + MOZ_ASSERT(WrapperFactory::IsXrayWrapper(reflector) || + IsReflector(reflector)); + + return reflector; + } + } + + JS_ReportError(cx, "CloneNonReflectorsRead error"); + return nullptr; +} + +static bool +CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer, + Handle<JSObject *> obj, void *closure) +{ + MOZ_ASSERT(closure, "Null pointer!"); + + // We need to maintain a list of reflectors to make sure all these objects + // are properly rooter. Only their indices will be serialized. + AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure); + if (IsReflector(obj)) { + if (!reflectors->append(obj)) + return false; + + size_t idx = reflectors->length()-1; + if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) && + JS_WriteBytes(writer, &idx, sizeof(size_t))) { + return true; + } + } + + JS_ReportError(cx, "CloneNonReflectorsWrite error"); + return false; +} + +static const JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = { + CloneNonReflectorsRead, + CloneNonReflectorsWrite, + nullptr, + nullptr, + nullptr, + nullptr +}; + +/* + * This is a special structured cloning, that clones only non-reflectors. + * The function assumes the cx is already entered the compartment we want + * to clone to, and that if val is an object is from the compartment we + * clone from. + */ +static bool +CloneNonReflectors(JSContext *cx, MutableHandleValue val) +{ + JSAutoStructuredCloneBuffer buffer; + AutoObjectVector rootedReflectors(cx); + { + // For parsing val we have to enter its compartment. + // (unless it's a primitive) + Maybe<JSAutoCompartment> ac; + if (val.isObject()) { + ac.construct(cx, &val.toObject()); + } else if (val.isString() && !JS_WrapValue(cx, val)) { + return false; + } + + if (!buffer.write(cx, val, + &gForwarderStructuredCloneCallbacks, + &rootedReflectors)) + { + return false; + } + } + + // Now recreate the clones in the target compartment. + if (!buffer.read(cx, val, + &gForwarderStructuredCloneCallbacks, + &rootedReflectors)) + { + return false; + } + + return true; +} + +/* + * Forwards the call to the exported function. Clones all the non reflectors, ignores + * the |this| argument. + */ +static bool +CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); + NS_ASSERTION(v.isObject(), "weird function"); + RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject())); + { + JSAutoCompartment ac(cx, origFunObj); + // Note: only the arguments are cloned not the |this| or the |callee|. + // Function forwarder does not use those. + for (unsigned i = 0; i < args.length(); i++) { + if (!CloneNonReflectors(cx, args[i])) { + return false; + } + } + + // JS API does not support any JSObject to JSFunction conversion, + // so let's use JS_CallFunctionValue instead. + RootedValue functionVal(cx, ObjectValue(*origFunObj)); + + if (!JS_CallFunctionValue(cx, JS::NullPtr(), functionVal, args, args.rval())) + return false; + } + + // Return value must be wrapped. + return JS_WrapValue(cx, args.rval()); +} + +static bool +NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); + MOZ_ASSERT(v.isObject(), "weird function"); + + RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); + if (!obj) { + return false; + } + return JS_CallFunctionValue(cx, obj, v, args, args.rval()); +} +bool +NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone, + MutableHandleValue vp) +{ + JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder : + NonCloningFunctionForwarder, + 0,0, JS::CurrentGlobalOrNull(cx), id); + + if (!fun) + return false; + + JSObject *funobj = JS_GetFunctionObject(fun); + js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable)); + vp.setObject(*funobj); + return true; +} + +bool +NewFunctionForwarder(JSContext *cx, HandleObject callable, bool doclone, + MutableHandleValue vp) +{ + RootedId emptyId(cx); + RootedValue emptyStringValue(cx, JS_GetEmptyStringValue(cx)); + if (!JS_ValueToId(cx, emptyStringValue, &emptyId)) + return false; + + return NewFunctionForwarder(cx, emptyId, callable, doclone, vp); +} + +bool +ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleValue voptions, + MutableHandleValue rval) +{ + bool hasOptions = !voptions.isUndefined(); + if (!vscope.isObject() || !vfunction.isObject() || (hasOptions && !voptions.isObject())) { + JS_ReportError(cx, "Invalid argument"); + return false; + } + + RootedObject funObj(cx, &vfunction.toObject()); + RootedObject targetScope(cx, &vscope.toObject()); + ExportOptions options(cx, hasOptions ? &voptions.toObject() : nullptr); + if (hasOptions && !options.Parse()) + return false; + + // We can only export functions to scopes those are transparent for us, + // so if there is a security wrapper around targetScope we must throw. + targetScope = CheckedUnwrap(targetScope); + if (!targetScope) { + JS_ReportError(cx, "Permission denied to export function into scope"); + return false; + } + + if (js::IsScriptedProxy(targetScope)) { + JS_ReportError(cx, "Defining property on proxy object is not allowed"); + return false; + } + + { + // We need to operate in the target scope from here on, let's enter + // its compartment. + JSAutoCompartment ac(cx, targetScope); + + // Unwrapping to see if we have a callable. + funObj = UncheckedUnwrap(funObj); + if (!JS_ObjectIsCallable(cx, funObj)) { + JS_ReportError(cx, "First argument must be a function"); + return false; + } + + RootedId id(cx, options.defineAs); + if (JSID_IS_VOID(id)) { + // If there wasn't any function name specified, + // copy the name from the function being imported. + JSFunction *fun = JS_GetObjectFunction(funObj); + RootedString funName(cx, JS_GetFunctionId(fun)); + if (!funName) + funName = JS_InternString(cx, ""); + + if (!JS_StringToId(cx, funName, &id)) + return false; + } + MOZ_ASSERT(JSID_IS_STRING(id)); + + // The function forwarder will live in the target compartment. Since + // this function will be referenced from its private slot, to avoid a + // GC hazard, we must wrap it to the same compartment. + if (!JS_WrapObject(cx, &funObj)) + return false; + + // And now, let's create the forwarder function in the target compartment + // for the function the be exported. + if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, rval)) { + JS_ReportError(cx, "Exporting function failed"); + return false; + } + + // We have the forwarder function in the target compartment. If + // defineAs was set, we also need to define it as a property on + // the target. + if (!JSID_IS_VOID(options.defineAs)) { + if (!JS_DefinePropertyById(cx, targetScope, id, rval, JSPROP_ENUMERATE, + JS_PropertyStub, JS_StrictPropertyStub)) { + return false; + } + } + } + + // Finally we have to re-wrap the exported function back to the caller compartment. + if (!JS_WrapValue(cx, rval)) + return false; + + return true; +} + +static bool +GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno) +{ + JS::AutoFilename scriptFilename; + if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineno)) { + if (const char *cfilename = scriptFilename.get()) { + filename.Assign(nsDependentCString(cfilename)); + return true; + } + } + return false; +} + +bool +EvalInWindow(JSContext *cx, const nsAString &source, HandleObject scope, MutableHandleValue rval) +{ + // If we cannot unwrap we must not eval in it. + RootedObject targetScope(cx, CheckedUnwrap(scope)); + if (!targetScope) { + JS_ReportError(cx, "Permission denied to eval in target scope"); + return false; + } + + // Make sure that we have a window object. + RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false)); + nsCOMPtr<nsIGlobalObject> global; + nsCOMPtr<nsPIDOMWindow> window; + if (!JS_IsGlobalObject(inner) || + !(global = GetNativeForGlobal(inner)) || + !(window = do_QueryInterface(global))) + { + JS_ReportError(cx, "Second argument must be a window"); + return false; + } + + nsCOMPtr<nsIScriptContext> context = + (static_cast<nsGlobalWindow*>(window.get()))->GetScriptContext(); + if (!context) { + JS_ReportError(cx, "Script context needed"); + return false; + } + + nsCString filename; + unsigned lineNo; + if (!GetFilenameAndLineNumber(cx, filename, lineNo)) { + // Default values for non-scripted callers. + filename.AssignLiteral("Unknown"); + lineNo = 0; + } + + RootedObject cxGlobal(cx, JS::CurrentGlobalOrNull(cx)); + { + // CompileOptions must be created from the context + // we will execute this script in. + JSContext *wndCx = context->GetNativeContext(); + AutoCxPusher pusher(wndCx); + JS::CompileOptions compileOptions(wndCx); + compileOptions.setFileAndLine(filename.get(), lineNo); + + // We don't want the JS engine to automatically report + // uncaught exceptions. + nsJSUtils::EvaluateOptions evaluateOptions; + evaluateOptions.setReportUncaught(false); + + nsresult rv = nsJSUtils::EvaluateString(wndCx, + source, + targetScope, + compileOptions, + evaluateOptions, + rval); + + if (NS_FAILED(rv)) { + // If there was an exception we get it as a return value, if + // the evaluation failed for some other reason, then a default + // exception is raised. + MOZ_ASSERT(!JS_IsExceptionPending(wndCx), + "Exception should be delivered as return value."); + if (rval.isUndefined()) { + MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY); + return false; + } + + // If there was an exception thrown we should set it + // on the calling context. + RootedValue exn(wndCx, rval); + // First we should reset the return value. + rval.set(UndefinedValue()); + + // Then clone the exception. + JSAutoCompartment ac(wndCx, cxGlobal); + if (CloneNonReflectors(wndCx, &exn)) + js::SetPendingExceptionCrossContext(cx, exn); + + return false; + } + } + + // Let's clone the return value back to the callers compartment. + if (!CloneNonReflectors(cx, rval)) { + rval.set(UndefinedValue()); + return false; + } + + return true; +} + +bool +CreateObjectIn(JSContext *cx, HandleValue vobj, CreateObjectInOptions &options, + MutableHandleValue rval) +{ + if (!vobj.isObject()) { + JS_ReportError(cx, "Expected an object as the target scope"); + return false; + } + + RootedObject scope(cx, js::CheckedUnwrap(&vobj.toObject())); + if (!scope) { + JS_ReportError(cx, "Permission denied to create object in the target scope"); + return false; + } + + bool define = !JSID_IS_VOID(options.defineAs); + + if (define && js::IsScriptedProxy(scope)) { + JS_ReportError(cx, "Defining property on proxy object is not allowed"); + return false; + } + + RootedObject obj(cx); + { + JSAutoCompartment ac(cx, scope); + obj = JS_NewObject(cx, nullptr, JS::NullPtr(), scope); + if (!obj) + return false; + + if (define) { + if (!JS_DefinePropertyById(cx, scope, options.defineAs, obj, JSPROP_ENUMERATE, + JS_PropertyStub, JS_StrictPropertyStub)) + return false; + } + } + + rval.setObject(*obj); + if (!WrapperFactory::WaiveXrayAndWrap(cx, rval)) + return false; + + return true; +} + +} /* namespace xpc */
--- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -192,17 +192,17 @@ SandboxImport(JSContext *cx, unsigned ar if (!JS_SetPropertyById(cx, thisObject, id, args[0])) return false; args.rval().setUndefined(); return true; } static bool -CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp) +SandboxCreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); MOZ_ASSERT(global); nsIScriptObjectPrincipal *sop = static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(global)); @@ -217,17 +217,17 @@ CreateXMLHttpRequest(JSContext *cx, unsi rv = nsContentUtils::WrapNative(cx, xhr, args.rval()); if (NS_FAILED(rv)) return false; return true; } static bool -IsProxy(JSContext *cx, unsigned argc, jsval *vp) +SandboxIsProxy(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (args.length() < 1) { JS_ReportError(cx, "Function requires at least 1 argument"); return false; } if (!args[0].isObject()) { args.rval().setBoolean(false); @@ -237,351 +237,42 @@ IsProxy(JSContext *cx, unsigned argc, js RootedObject obj(cx, &args[0].toObject()); obj = js::CheckedUnwrap(obj); NS_ENSURE_TRUE(obj, false); args.rval().setBoolean(js::IsScriptedProxy(obj)); return true; } -namespace xpc { - -bool -ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleValue voptions, - MutableHandleValue rval) -{ - bool hasOptions = !voptions.isUndefined(); - if (!vscope.isObject() || !vfunction.isObject() || (hasOptions && !voptions.isObject())) { - JS_ReportError(cx, "Invalid argument"); - return false; - } - - RootedObject funObj(cx, &vfunction.toObject()); - RootedObject targetScope(cx, &vscope.toObject()); - ExportOptions options(cx, hasOptions ? &voptions.toObject() : nullptr); - if (hasOptions && !options.Parse()) - return false; - - // We can only export functions to scopes those are transparent for us, - // so if there is a security wrapper around targetScope we must throw. - targetScope = CheckedUnwrap(targetScope); - if (!targetScope) { - JS_ReportError(cx, "Permission denied to export function into scope"); - return false; - } - - if (js::IsScriptedProxy(targetScope)) { - JS_ReportError(cx, "Defining property on proxy object is not allowed"); - return false; - } - - { - // We need to operate in the target scope from here on, let's enter - // its compartment. - JSAutoCompartment ac(cx, targetScope); - - // Unwrapping to see if we have a callable. - funObj = UncheckedUnwrap(funObj); - if (!JS_ObjectIsCallable(cx, funObj)) { - JS_ReportError(cx, "First argument must be a function"); - return false; - } - - RootedId id(cx, options.defineAs); - if (JSID_IS_VOID(id)) { - // If there wasn't any function name specified, - // copy the name from the function being imported. - JSFunction *fun = JS_GetObjectFunction(funObj); - RootedString funName(cx, JS_GetFunctionId(fun)); - if (!funName) - funName = JS_InternString(cx, ""); - - if (!JS_StringToId(cx, funName, &id)) - return false; - } - MOZ_ASSERT(JSID_IS_STRING(id)); - - // The function forwarder will live in the target compartment. Since - // this function will be referenced from its private slot, to avoid a - // GC hazard, we must wrap it to the same compartment. - if (!JS_WrapObject(cx, &funObj)) - return false; - - // And now, let's create the forwarder function in the target compartment - // for the function the be exported. - if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, rval)) { - JS_ReportError(cx, "Exporting function failed"); - return false; - } - - // We have the forwarder function in the target compartment. If - // defineAs was set, we also need to define it as a property on - // the target. - if (!JSID_IS_VOID(options.defineAs)) { - if (!JS_DefinePropertyById(cx, targetScope, id, rval, JSPROP_ENUMERATE, - JS_PropertyStub, JS_StrictPropertyStub)) { - return false; - } - } - } - - // Finally we have to re-wrap the exported function back to the caller compartment. - if (!JS_WrapValue(cx, rval)) - return false; - - return true; -} - /* * Expected type of the arguments and the return value: * function exportFunction(function funToExport, * object targetScope, * [optional] object options) */ static bool -ExportFunction(JSContext *cx, unsigned argc, jsval *vp) +SandboxExportFunction(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (args.length() < 2) { JS_ReportError(cx, "Function requires at least 2 arguments"); return false; } RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue()); return ExportFunction(cx, args[0], args[1], options, args.rval()); } -} /* namespace xpc */ - -static bool -GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno) -{ - JS::AutoFilename scriptFilename; - if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineno)) { - if (const char *cfilename = scriptFilename.get()) { - filename.Assign(nsDependentCString(cfilename)); - return true; - } - } - return false; -} - -bool -xpc::IsReflector(JSObject *obj) -{ - return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj); -} - -enum ForwarderCloneTags { - SCTAG_BASE = JS_SCTAG_USER_MIN, - SCTAG_REFLECTOR -}; - -static JSObject * -CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag, - uint32_t data, void *closure) -{ - MOZ_ASSERT(closure, "Null pointer!"); - AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure); - if (tag == SCTAG_REFLECTOR) { - MOZ_ASSERT(!data); - - size_t idx; - if (JS_ReadBytes(reader, &idx, sizeof(size_t))) { - RootedObject reflector(cx, (*reflectors)[idx]); - MOZ_ASSERT(reflector, "No object pointer?"); - MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!"); - - if (!JS_WrapObject(cx, &reflector)) - return nullptr; - MOZ_ASSERT(WrapperFactory::IsXrayWrapper(reflector) || - IsReflector(reflector)); - - return reflector; - } - } - - JS_ReportError(cx, "CloneNonReflectorsRead error"); - return nullptr; -} - -static bool -CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer, - Handle<JSObject *> obj, void *closure) -{ - MOZ_ASSERT(closure, "Null pointer!"); - - // We need to maintain a list of reflectors to make sure all these objects - // are properly rooter. Only their indices will be serialized. - AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure); - if (IsReflector(obj)) { - if (!reflectors->append(obj)) - return false; - - size_t idx = reflectors->length()-1; - if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) && - JS_WriteBytes(writer, &idx, sizeof(size_t))) { - return true; - } - } - - JS_ReportError(cx, "CloneNonReflectorsWrite error"); - return false; -} - -static const JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = { - CloneNonReflectorsRead, - CloneNonReflectorsWrite, - nullptr, - nullptr, - nullptr, - nullptr -}; - -/* - * This is a special structured cloning, that clones only non-reflectors. - * The function assumes the cx is already entered the compartment we want - * to clone to, and that if val is an object is from the compartment we - * clone from. - */ -static bool -CloneNonReflectors(JSContext *cx, MutableHandleValue val) -{ - JSAutoStructuredCloneBuffer buffer; - AutoObjectVector rootedReflectors(cx); - { - // For parsing val we have to enter its compartment. - // (unless it's a primitive) - Maybe<JSAutoCompartment> ac; - if (val.isObject()) { - ac.construct(cx, &val.toObject()); - } else if (val.isString() && !JS_WrapValue(cx, val)) { - return false; - } - - if (!buffer.write(cx, val, - &gForwarderStructuredCloneCallbacks, - &rootedReflectors)) - { - return false; - } - } - - // Now recreate the clones in the target compartment. - if (!buffer.read(cx, val, - &gForwarderStructuredCloneCallbacks, - &rootedReflectors)) - { - return false; - } - - return true; -} - -namespace xpc { - -bool -EvalInWindow(JSContext *cx, const nsAString &source, HandleObject scope, MutableHandleValue rval) -{ - // If we cannot unwrap we must not eval in it. - RootedObject targetScope(cx, CheckedUnwrap(scope)); - if (!targetScope) { - JS_ReportError(cx, "Permission denied to eval in target scope"); - return false; - } - - // Make sure that we have a window object. - RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false)); - nsCOMPtr<nsIGlobalObject> global; - nsCOMPtr<nsPIDOMWindow> window; - if (!JS_IsGlobalObject(inner) || - !(global = GetNativeForGlobal(inner)) || - !(window = do_QueryInterface(global))) - { - JS_ReportError(cx, "Second argument must be a window"); - return false; - } - - nsCOMPtr<nsIScriptContext> context = - (static_cast<nsGlobalWindow*>(window.get()))->GetScriptContext(); - if (!context) { - JS_ReportError(cx, "Script context needed"); - return false; - } - - nsCString filename; - unsigned lineNo; - if (!GetFilenameAndLineNumber(cx, filename, lineNo)) { - // Default values for non-scripted callers. - filename.AssignLiteral("Unknown"); - lineNo = 0; - } - - RootedObject cxGlobal(cx, JS::CurrentGlobalOrNull(cx)); - { - // CompileOptions must be created from the context - // we will execute this script in. - JSContext *wndCx = context->GetNativeContext(); - AutoCxPusher pusher(wndCx); - JS::CompileOptions compileOptions(wndCx); - compileOptions.setFileAndLine(filename.get(), lineNo); - - // We don't want the JS engine to automatically report - // uncaught exceptions. - nsJSUtils::EvaluateOptions evaluateOptions; - evaluateOptions.setReportUncaught(false); - - nsresult rv = nsJSUtils::EvaluateString(wndCx, - source, - targetScope, - compileOptions, - evaluateOptions, - rval); - - if (NS_FAILED(rv)) { - // If there was an exception we get it as a return value, if - // the evaluation failed for some other reason, then a default - // exception is raised. - MOZ_ASSERT(!JS_IsExceptionPending(wndCx), - "Exception should be delivered as return value."); - if (rval.isUndefined()) { - MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY); - return false; - } - - // If there was an exception thrown we should set it - // on the calling context. - RootedValue exn(wndCx, rval); - // First we should reset the return value. - rval.set(UndefinedValue()); - - // Then clone the exception. - JSAutoCompartment ac(wndCx, cxGlobal); - if (CloneNonReflectors(wndCx, &exn)) - js::SetPendingExceptionCrossContext(cx, exn); - - return false; - } - } - - // Let's clone the return value back to the callers compartment. - if (!CloneNonReflectors(cx, rval)) { - rval.set(UndefinedValue()); - return false; - } - - return true; -} /* * Expected type of the arguments: * value evalInWindow(string script, * object window) */ static bool -EvalInWindow(JSContext *cx, unsigned argc, jsval *vp) +SandboxEvalInWindow(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (args.length() < 2) { JS_ReportError(cx, "Function requires two arguments"); return false; } if (!args[0].isString() || !args[1].isObject()) { @@ -597,17 +288,17 @@ EvalInWindow(JSContext *cx, unsigned arg JS_ReportError(cx, "Source string is invalid"); return false; } return EvalInWindow(cx, srcDepString, targetScope, args.rval()); } static bool -CreateObjectIn(JSContext *cx, unsigned argc, jsval *vp) +SandboxCreateObjectIn(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (args.length() < 1) { JS_ReportError(cx, "Function requires at least 1 argument"); return false; } RootedObject optionsObj(cx); @@ -623,30 +314,28 @@ CreateObjectIn(JSContext *cx, unsigned a CreateObjectInOptions options(cx, optionsObj); if (calledWithOptions && !options.Parse()) return false; return xpc::CreateObjectIn(cx, args[0], options, args.rval()); } static bool -CloneInto(JSContext *cx, unsigned argc, jsval *vp) +SandboxCloneInto(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (args.length() < 2) { JS_ReportError(cx, "Function requires at least 2 arguments"); return false; } RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue()); return xpc::CloneInto(cx, args[0], args[1], options, args.rval()); } -} /* namespace xpc */ - static bool sandbox_enumerate(JSContext *cx, HandleObject obj) { return JS_EnumerateStandardClasses(cx, obj); } static bool sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id) @@ -1111,17 +800,17 @@ xpc::GlobalProperties::Define(JSContext if (Promise && !dom::PromiseBinding::GetConstructorObject(cx, obj)) return false; if (indexedDB && AccessCheck::isChrome(obj) && !IndexedDatabaseManager::DefineIndexedDB(cx, obj)) return false; if (XMLHttpRequest && - !JS_DefineFunction(cx, obj, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR)) + !JS_DefineFunction(cx, obj, "XMLHttpRequest", SandboxCreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR)) return false; if (TextEncoder && !dom::TextEncoderBinding::GetConstructorObject(cx, obj)) return false; if (TextDecoder && !dom::TextDecoderBinding::GetConstructorObject(cx, obj)) @@ -1267,21 +956,21 @@ xpc::CreateSandboxObject(JSContext *cx, if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox)) return NS_ERROR_XPC_UNEXPECTED; if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions)) return NS_ERROR_XPC_UNEXPECTED; if (options.wantExportHelpers && - (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) || - !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0) || - !JS_DefineFunction(cx, sandbox, "createObjectIn", CreateObjectIn, 2, 0) || - !JS_DefineFunction(cx, sandbox, "cloneInto", CloneInto, 3, 0) || - !JS_DefineFunction(cx, sandbox, "isProxy", IsProxy, 1, 0))) + (!JS_DefineFunction(cx, sandbox, "exportFunction", SandboxExportFunction, 3, 0) || + !JS_DefineFunction(cx, sandbox, "evalInWindow", SandboxEvalInWindow, 2, 0) || + !JS_DefineFunction(cx, sandbox, "createObjectIn", SandboxCreateObjectIn, 2, 0) || + !JS_DefineFunction(cx, sandbox, "cloneInto", SandboxCloneInto, 3, 0) || + !JS_DefineFunction(cx, sandbox, "isProxy", SandboxIsProxy, 1, 0))) return NS_ERROR_XPC_UNEXPECTED; if (!options.globalProperties.Define(cx, sandbox)) return NS_ERROR_XPC_UNEXPECTED; // Resolve standard classes eagerly to avoid triggering mirroring hooks for them. if (options.writeToGlobalPrototype && !JS_EnumerateStandardClasses(cx, sandbox)) return NS_ERROR_XPC_UNEXPECTED; @@ -1909,94 +1598,16 @@ xpc::EvalInSandbox(JSContext *cx, Handle } NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); // Whew! rval.set(v); return NS_OK; } -static bool -NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); - MOZ_ASSERT(v.isObject(), "weird function"); - - RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); - if (!obj) { - return false; - } - return JS_CallFunctionValue(cx, obj, v, args, args.rval()); -} - -/* - * Forwards the call to the exported function. Clones all the non reflectors, ignores - * the |this| argument. - */ -static bool -CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0)); - NS_ASSERTION(v.isObject(), "weird function"); - RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject())); - { - JSAutoCompartment ac(cx, origFunObj); - // Note: only the arguments are cloned not the |this| or the |callee|. - // Function forwarder does not use those. - for (unsigned i = 0; i < args.length(); i++) { - if (!CloneNonReflectors(cx, args[i])) { - return false; - } - } - - // JS API does not support any JSObject to JSFunction conversion, - // so let's use JS_CallFunctionValue instead. - RootedValue functionVal(cx, ObjectValue(*origFunObj)); - - if (!JS_CallFunctionValue(cx, JS::NullPtr(), functionVal, args, args.rval())) - return false; - } - - // Return value must be wrapped. - return JS_WrapValue(cx, args.rval()); -} - -bool -xpc::NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone, - MutableHandleValue vp) -{ - JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder : - NonCloningFunctionForwarder, - 0,0, JS::CurrentGlobalOrNull(cx), id); - - if (!fun) - return false; - - JSObject *funobj = JS_GetFunctionObject(fun); - js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable)); - vp.setObject(*funobj); - return true; -} - -bool -xpc::NewFunctionForwarder(JSContext *cx, HandleObject callable, bool doclone, - MutableHandleValue vp) -{ - RootedId emptyId(cx); - RootedValue emptyStringValue(cx, JS_GetEmptyStringValue(cx)); - if (!JS_ValueToId(cx, emptyStringValue, &emptyId)) - return false; - - return NewFunctionForwarder(cx, emptyId, callable, doclone, vp); -} - nsresult xpc::GetSandboxAddonId(JSContext *cx, HandleObject sandbox, MutableHandleValue rval) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(IsSandbox(sandbox)); JSAddonId *id = JS::AddonIdOfObject(sandbox); if (!id) {
--- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -2985,60 +2985,16 @@ nsXPCComponents_Utils::GetGlobalForObjec // Outerize if necessary. if (js::ObjectOp outerize = js::GetObjectClass(obj)->ext.outerObject) obj = outerize(cx, obj); retval.setObject(*obj); return NS_OK; } -/* jsval createObjectIn(in jsval vobj); */ -bool -xpc::CreateObjectIn(JSContext *cx, HandleValue vobj, CreateObjectInOptions &options, - MutableHandleValue rval) -{ - if (!vobj.isObject()) { - JS_ReportError(cx, "Expected an object as the target scope"); - return false; - } - - RootedObject scope(cx, js::CheckedUnwrap(&vobj.toObject())); - if (!scope) { - JS_ReportError(cx, "Permission denied to create object in the target scope"); - return false; - } - - bool define = !JSID_IS_VOID(options.defineAs); - - if (define && js::IsScriptedProxy(scope)) { - JS_ReportError(cx, "Defining property on proxy object is not allowed"); - return false; - } - - RootedObject obj(cx); - { - JSAutoCompartment ac(cx, scope); - obj = JS_NewObject(cx, nullptr, JS::NullPtr(), scope); - if (!obj) - return false; - - if (define) { - if (!JS_DefinePropertyById(cx, scope, options.defineAs, obj, JSPROP_ENUMERATE, - JS_PropertyStub, JS_StrictPropertyStub)) - return false; - } - } - - rval.setObject(*obj); - if (!WrapperFactory::WaiveXrayAndWrap(cx, rval)) - return false; - - return true; -} - /* boolean isProxy(in value vobj); */ NS_IMETHODIMP nsXPCComponents_Utils::IsProxy(HandleValue vobj, JSContext *cx, bool *rval) { if (!vobj.isObject()) { *rval = false; return NS_OK; }
--- a/js/xpconnect/src/moz.build +++ b/js/xpconnect/src/moz.build @@ -9,16 +9,17 @@ EXPORTS += [ 'nsCxPusher.h', 'qsObjectHelper.h', 'XPCJSMemoryReporter.h', 'xpcObjectHelper.h', 'xpcpublic.h', ] UNIFIED_SOURCES += [ + 'ExportHelpers.cpp', 'nsCxPusher.cpp', 'nsScriptError.cpp', 'nsXPConnect.cpp', 'Sandbox.cpp', 'XPCCallContext.cpp', 'XPCContext.cpp', 'XPCConvert.cpp', 'XPCDebug.cpp',