Bug 1027131 - Split out ExportHelpers.cpp. r=gabor

A lot of this stuff is usable from both Sandbox.cpp and XPCComponents.cpp, and
those files are both pretty big these days.
This commit is contained in:
Bobby Holley 2014-06-23 13:25:07 -07:00
parent 610dff8cc8
commit fc116f2c1c
4 changed files with 460 additions and 445 deletions

View File

@ -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 */

View File

@ -197,7 +197,7 @@ SandboxImport(JSContext *cx, unsigned argc, Value *vp)
}
static bool
CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp)
SandboxCreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
@ -222,7 +222,7 @@ CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp)
}
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) {
@ -242,94 +242,6 @@ IsProxy(JSContext *cx, unsigned argc, jsval *vp)
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,
@ -337,7 +249,7 @@ ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleV
* [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) {
@ -348,227 +260,6 @@ ExportFunction(JSContext *cx, unsigned argc, jsval *vp)
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:
@ -576,7 +267,7 @@ EvalInWindow(JSContext *cx, const nsAString &source, HandleObject scope, Mutable
* 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) {
@ -602,7 +293,7 @@ EvalInWindow(JSContext *cx, unsigned argc, jsval *vp)
}
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) {
@ -628,7 +319,7 @@ CreateObjectIn(JSContext *cx, unsigned argc, jsval *vp)
}
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) {
@ -640,8 +331,6 @@ CloneInto(JSContext *cx, unsigned argc, jsval *vp)
return xpc::CloneInto(cx, args[0], args[1], options, args.rval());
}
} /* namespace xpc */
static bool
sandbox_enumerate(JSContext *cx, HandleObject obj)
{
@ -1116,7 +805,7 @@ xpc::GlobalProperties::Define(JSContext *cx, JS::HandleObject 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 &&
@ -1272,11 +961,11 @@ xpc::CreateSandboxObject(JSContext *cx, MutableHandleValue vp, nsISupports *prin
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))
@ -1914,84 +1603,6 @@ xpc::EvalInSandbox(JSContext *cx, HandleObject sandboxArg, const nsAString& sour
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)
{

View File

@ -2990,50 +2990,6 @@ nsXPCComponents_Utils::GetGlobalForObject(HandleValue object,
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)

View File

@ -14,6 +14,7 @@ EXPORTS += [
]
UNIFIED_SOURCES += [
'ExportHelpers.cpp',
'nsCxPusher.cpp',
'nsScriptError.cpp',
'nsXPConnect.cpp',