2014-04-03 04:58:00 -07:00
|
|
|
/* -*- 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
|
2012-05-21 04:12:37 -07:00
|
|
|
* 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/. */
|
2010-07-02 14:09:48 -07:00
|
|
|
|
|
|
|
#include "XrayWrapper.h"
|
|
|
|
#include "AccessCheck.h"
|
|
|
|
#include "WrapperFactory.h"
|
|
|
|
|
2014-01-16 15:45:40 -08:00
|
|
|
#include "nsIControllers.h"
|
2014-09-15 07:51:40 -07:00
|
|
|
#include "nsDependentString.h"
|
2014-08-06 20:32:23 -07:00
|
|
|
#include "nsIScriptError.h"
|
2014-06-04 11:11:05 -07:00
|
|
|
#include "mozilla/dom/Element.h"
|
2011-09-21 18:30:27 -07:00
|
|
|
|
2010-07-02 14:09:48 -07:00
|
|
|
#include "XPCWrapper.h"
|
|
|
|
#include "xpcprivate.h"
|
|
|
|
|
2012-01-15 00:13:11 -08:00
|
|
|
#include "jsapi.h"
|
2013-08-17 15:50:18 -07:00
|
|
|
#include "jsprf.h"
|
2012-08-27 06:15:20 -07:00
|
|
|
#include "nsJSUtils.h"
|
2014-06-05 22:32:38 -07:00
|
|
|
#include "nsPrintfCString.h"
|
2012-01-11 00:23:08 -08:00
|
|
|
|
2012-05-02 21:35:38 -07:00
|
|
|
#include "mozilla/dom/BindingUtils.h"
|
2014-01-29 22:34:25 -08:00
|
|
|
#include "mozilla/dom/WindowBinding.h"
|
2013-01-17 09:30:37 -08:00
|
|
|
#include "nsGlobalWindow.h"
|
2012-03-30 21:42:20 -07:00
|
|
|
|
2012-05-02 21:35:38 -07:00
|
|
|
using namespace mozilla::dom;
|
2013-03-27 04:33:30 -07:00
|
|
|
using namespace JS;
|
2013-04-23 09:50:17 -07:00
|
|
|
using namespace mozilla;
|
2012-03-30 21:42:20 -07:00
|
|
|
|
2013-03-27 04:33:30 -07:00
|
|
|
using js::Wrapper;
|
2014-02-13 10:54:07 -08:00
|
|
|
using js::BaseProxyHandler;
|
2013-05-17 10:44:25 -07:00
|
|
|
using js::IsCrossCompartmentWrapper;
|
|
|
|
using js::UncheckedUnwrap;
|
|
|
|
using js::CheckedUnwrap;
|
2010-07-02 14:09:48 -07:00
|
|
|
|
2013-03-27 04:33:30 -07:00
|
|
|
namespace xpc {
|
2010-07-02 14:09:48 -07:00
|
|
|
|
2012-10-05 09:59:23 -07:00
|
|
|
using namespace XrayUtils;
|
|
|
|
|
2014-07-11 09:37:00 -07:00
|
|
|
#define Between(x, a, b) (a <= x && x <= b)
|
2014-07-11 09:09:21 -07:00
|
|
|
|
|
|
|
static_assert(JSProto_URIError - JSProto_Error == 7, "New prototype added in error object range");
|
|
|
|
#define AssertErrorObjectKeyInBounds(key) \
|
|
|
|
static_assert(Between(key, JSProto_Error, JSProto_URIError), "We depend on jsprototypes.h ordering here");
|
|
|
|
MOZ_FOR_EACH(AssertErrorObjectKeyInBounds, (),
|
|
|
|
(JSProto_Error, JSProto_InternalError, JSProto_EvalError, JSProto_RangeError,
|
|
|
|
JSProto_ReferenceError, JSProto_SyntaxError, JSProto_TypeError, JSProto_URIError));
|
|
|
|
|
|
|
|
static_assert(JSProto_Uint8ClampedArray - JSProto_Int8Array == 8, "New prototype added in typed array range");
|
|
|
|
#define AssertTypedArrayKeyInBounds(key) \
|
|
|
|
static_assert(Between(key, JSProto_Int8Array, JSProto_Uint8ClampedArray), "We depend on jsprototypes.h ordering here");
|
|
|
|
MOZ_FOR_EACH(AssertTypedArrayKeyInBounds, (),
|
|
|
|
(JSProto_Int8Array, JSProto_Uint8Array, JSProto_Int16Array, JSProto_Uint16Array,
|
|
|
|
JSProto_Int32Array, JSProto_Uint32Array, JSProto_Float32Array, JSProto_Float64Array, JSProto_Uint8ClampedArray));
|
|
|
|
|
2014-07-11 09:37:00 -07:00
|
|
|
#undef Between
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
IsErrorObjectKey(JSProtoKey key)
|
|
|
|
{
|
|
|
|
return key >= JSProto_Error && key <= JSProto_URIError;
|
|
|
|
}
|
|
|
|
|
2014-06-17 10:16:08 -07:00
|
|
|
inline bool
|
|
|
|
IsTypedArrayKey(JSProtoKey key)
|
|
|
|
{
|
2014-07-11 09:09:21 -07:00
|
|
|
return key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray;
|
2014-06-17 10:16:08 -07:00
|
|
|
}
|
|
|
|
|
2014-03-23 07:02:12 -07:00
|
|
|
// Whitelist for the standard ES classes we can Xray to.
|
|
|
|
static bool
|
|
|
|
IsJSXraySupported(JSProtoKey key)
|
|
|
|
{
|
2014-06-17 10:16:08 -07:00
|
|
|
if (IsTypedArrayKey(key))
|
|
|
|
return true;
|
2014-07-11 09:09:22 -07:00
|
|
|
if (IsErrorObjectKey(key))
|
|
|
|
return true;
|
2014-03-23 07:02:14 -07:00
|
|
|
switch (key) {
|
|
|
|
case JSProto_Date:
|
2014-06-05 22:32:39 -07:00
|
|
|
case JSProto_Object:
|
2014-06-11 15:16:07 -07:00
|
|
|
case JSProto_Array:
|
2014-06-19 09:57:06 -07:00
|
|
|
case JSProto_Function:
|
2014-08-21 20:39:30 -07:00
|
|
|
case JSProto_TypedArray:
|
2015-02-13 09:21:50 -08:00
|
|
|
case JSProto_SavedFrame:
|
2015-03-18 02:22:05 -07:00
|
|
|
case JSProto_RegExp:
|
2014-03-23 07:02:14 -07:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
2014-03-23 07:02:12 -07:00
|
|
|
}
|
|
|
|
|
2012-10-05 09:59:23 -07:00
|
|
|
XrayType
|
2015-03-28 15:22:11 -07:00
|
|
|
GetXrayType(JSObject* obj)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
2013-04-11 11:50:18 -07:00
|
|
|
obj = js::UncheckedUnwrap(obj, /* stopAtOuter = */ false);
|
2012-10-09 11:50:27 -07:00
|
|
|
if (mozilla::dom::UseDOMXray(obj))
|
2012-10-05 09:59:23 -07:00
|
|
|
return XrayForDOMObject;
|
|
|
|
|
2013-09-11 05:49:05 -07:00
|
|
|
const js::Class* clasp = js::GetObjectClass(obj);
|
2013-04-19 12:58:30 -07:00
|
|
|
if (IS_WN_CLASS(clasp) || clasp->ext.innerObject)
|
2012-10-05 09:59:23 -07:00
|
|
|
return XrayForWrappedNative;
|
2013-04-19 12:57:18 -07:00
|
|
|
|
2014-03-23 07:02:12 -07:00
|
|
|
JSProtoKey standardProto = IdentifyStandardInstanceOrPrototype(obj);
|
|
|
|
if (IsJSXraySupported(standardProto))
|
|
|
|
return XrayForJSObject;
|
|
|
|
|
2014-07-14 10:09:07 -07:00
|
|
|
// Modulo a few exceptions, everything else counts as an XrayWrapper to an
|
|
|
|
// opaque object, which means that more-privileged code sees nothing from
|
|
|
|
// the underlying object. This is very important for security. In some cases
|
|
|
|
// though, we need to make an exception for compatibility.
|
|
|
|
if (IsSandbox(obj))
|
|
|
|
return NotXray;
|
|
|
|
|
|
|
|
return XrayForOpaqueObject;
|
2012-10-05 09:59:23 -07:00
|
|
|
}
|
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
JSObject*
|
|
|
|
XrayAwareCalleeGlobal(JSObject* fun)
|
2014-03-04 10:05:07 -08:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(js::IsFunctionObject(fun));
|
2015-03-06 13:33:31 -08:00
|
|
|
|
|
|
|
if (!js::FunctionHasNativeReserved(fun)) {
|
|
|
|
// Just a normal function, no Xrays involved.
|
|
|
|
return js::GetGlobalForObjectCrossCompartment(fun);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The functions we expect here have the Xray wrapper they're associated with
|
|
|
|
// in their XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT and, in a debug build,
|
|
|
|
// themselves in their XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF. Assert that
|
|
|
|
// last bit.
|
|
|
|
MOZ_ASSERT(&js::GetFunctionNativeReserved(fun, XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF).toObject() ==
|
|
|
|
fun);
|
|
|
|
|
|
|
|
Value v =
|
|
|
|
js::GetFunctionNativeReserved(fun, XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT);
|
|
|
|
MOZ_ASSERT(IsXrayWrapper(&v.toObject()));
|
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
JSObject* xrayTarget = js::UncheckedUnwrap(&v.toObject());
|
2015-03-06 13:33:31 -08:00
|
|
|
return js::GetGlobalForObjectCrossCompartment(xrayTarget);
|
2014-03-04 10:05:07 -08:00
|
|
|
}
|
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
JSObject*
|
2014-08-28 13:47:16 -07:00
|
|
|
XrayTraits::getExpandoChain(HandleObject obj)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
2014-08-28 13:47:16 -07:00
|
|
|
return ObjectScope(obj)->GetExpandoChain(obj);
|
|
|
|
}
|
2012-10-05 09:59:23 -07:00
|
|
|
|
2014-08-28 13:47:16 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayTraits::setExpandoChain(JSContext* cx, HandleObject obj, HandleObject chain)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
2014-08-28 13:47:16 -07:00
|
|
|
return ObjectScope(obj)->SetExpandoChain(cx, obj, chain);
|
|
|
|
}
|
2012-10-05 09:59:23 -07:00
|
|
|
|
2014-08-28 13:47:16 -07:00
|
|
|
// static
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNative*
|
|
|
|
XPCWrappedNativeXrayTraits::getWN(JSObject* wrapper)
|
2014-08-28 13:47:16 -07:00
|
|
|
{
|
|
|
|
return XPCWrappedNative::Get(getTargetObject(wrapper));
|
|
|
|
}
|
2012-10-05 09:59:23 -07:00
|
|
|
|
2014-02-21 15:55:31 -08:00
|
|
|
const JSClass XPCWrappedNativeXrayTraits::HolderClass = {
|
2014-11-22 10:23:39 -08:00
|
|
|
"NativePropertyHolder", JSCLASS_HAS_RESERVED_SLOTS(2)
|
2014-02-21 15:55:31 -08:00
|
|
|
};
|
|
|
|
|
2014-03-23 07:02:12 -07:00
|
|
|
|
2014-03-23 07:02:13 -07:00
|
|
|
const JSClass JSXrayTraits::HolderClass = {
|
2014-11-22 10:23:39 -08:00
|
|
|
"JSXrayHolder", JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT)
|
2014-03-23 07:02:13 -07:00
|
|
|
};
|
|
|
|
|
2014-08-28 13:47:16 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
OpaqueXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, HandleObject wrapper,
|
2014-08-28 13:47:16 -07:00
|
|
|
HandleObject holder, HandleId id,
|
|
|
|
MutableHandle<JSPropertyDescriptor> desc)
|
2014-07-10 09:31:37 -07:00
|
|
|
{
|
2014-08-28 13:47:16 -07:00
|
|
|
bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, id, desc);
|
|
|
|
if (!ok || desc.object())
|
|
|
|
return ok;
|
2014-07-10 09:31:37 -07:00
|
|
|
|
2014-10-03 01:05:51 -07:00
|
|
|
return ReportWrapperDenial(cx, id, WrapperDenialForXray, "object is not safely Xrayable");
|
2014-08-28 13:47:16 -07:00
|
|
|
}
|
2014-07-10 09:31:37 -07:00
|
|
|
|
2014-07-14 10:09:07 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
ReportWrapperDenial(JSContext* cx, HandleId id, WrapperDenialType type, const char* reason)
|
2014-06-05 22:32:38 -07:00
|
|
|
{
|
2015-03-28 15:22:11 -07:00
|
|
|
CompartmentPrivate* priv = CompartmentPrivate::Get(CurrentGlobalOrNull(cx));
|
2014-10-03 01:05:51 -07:00
|
|
|
bool alreadyWarnedOnce = priv->wrapperDenialWarnings[type];
|
|
|
|
priv->wrapperDenialWarnings[type] = true;
|
2014-08-06 20:32:23 -07:00
|
|
|
|
|
|
|
// The browser console warning is only emitted for the first violation,
|
|
|
|
// whereas the (debug-only) NS_WARNING is emitted for each violation.
|
|
|
|
#ifndef DEBUG
|
|
|
|
if (alreadyWarnedOnce)
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsAutoJSString propertyName;
|
2015-03-20 12:02:55 -07:00
|
|
|
RootedValue idval(cx);
|
|
|
|
if (!JS_IdToValue(cx, id, &idval))
|
|
|
|
return false;
|
|
|
|
JSString* str = JS_ValueToSource(cx, idval);
|
|
|
|
if (!str)
|
|
|
|
return false;
|
|
|
|
if (!propertyName.init(cx, str))
|
2014-06-10 20:15:56 -07:00
|
|
|
return false;
|
2014-06-05 22:32:38 -07:00
|
|
|
AutoFilename filename;
|
2015-08-31 21:28:50 -07:00
|
|
|
unsigned line = 0, column = 0;
|
|
|
|
DescribeScriptedCaller(cx, &filename, &line, &column);
|
2014-08-06 20:32:23 -07:00
|
|
|
|
|
|
|
// Warn to the terminal for the logs.
|
2015-03-20 12:02:55 -07:00
|
|
|
NS_WARNING(nsPrintfCString("Silently denied access to property %s: %s (@%s:%u:%u)",
|
2014-08-06 20:32:23 -07:00
|
|
|
NS_LossyConvertUTF16toASCII(propertyName).get(), reason,
|
2015-08-31 21:28:50 -07:00
|
|
|
filename.get(), line, column).get());
|
2014-08-06 20:32:23 -07:00
|
|
|
|
|
|
|
// If this isn't the first warning on this topic for this global, we've
|
|
|
|
// already bailed out in opt builds. Now that the NS_WARNING is done, bail
|
|
|
|
// out in debug builds as well.
|
|
|
|
if (alreadyWarnedOnce)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Log a message to the console service.
|
|
|
|
//
|
|
|
|
|
|
|
|
// Grab the pieces.
|
|
|
|
nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
|
|
|
NS_ENSURE_TRUE(consoleService, true);
|
|
|
|
nsCOMPtr<nsIScriptError> errorObject = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
|
|
|
|
NS_ENSURE_TRUE(errorObject, true);
|
|
|
|
|
|
|
|
// Compute the current window id if any.
|
|
|
|
uint64_t windowId = 0;
|
2015-03-28 15:22:11 -07:00
|
|
|
nsGlobalWindow* win = WindowGlobalOrNull(CurrentGlobalOrNull(cx));
|
2014-08-06 20:32:23 -07:00
|
|
|
if (win)
|
|
|
|
windowId = win->WindowID();
|
|
|
|
|
2014-10-03 01:05:51 -07:00
|
|
|
|
|
|
|
Maybe<nsPrintfCString> errorMessage;
|
|
|
|
if (type == WrapperDenialForXray) {
|
|
|
|
errorMessage.emplace("XrayWrapper denied access to property %s (reason: %s). "
|
|
|
|
"See https://developer.mozilla.org/en-US/docs/Xray_vision "
|
|
|
|
"for more information. Note that only the first denied "
|
|
|
|
"property access from a given global object will be reported.",
|
|
|
|
NS_LossyConvertUTF16toASCII(propertyName).get(),
|
|
|
|
reason);
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(type == WrapperDenialForCOW);
|
|
|
|
errorMessage.emplace("Security wrapper denied access to property %s on privileged "
|
|
|
|
"Javascript object. Support for exposing privileged objects "
|
|
|
|
"to untrusted content via __exposedProps__ is being gradually "
|
|
|
|
"removed - use WebIDL bindings or Components.utils.cloneInto "
|
|
|
|
"instead. Note that only the first denied property access from a "
|
|
|
|
"given global object will be reported.",
|
|
|
|
NS_LossyConvertUTF16toASCII(propertyName).get());
|
|
|
|
}
|
2014-08-06 20:32:23 -07:00
|
|
|
nsString filenameStr(NS_ConvertASCIItoUTF16(filename.get()));
|
2014-10-03 01:05:51 -07:00
|
|
|
nsresult rv = errorObject->InitWithWindowID(NS_ConvertASCIItoUTF16(errorMessage.ref()),
|
2014-08-06 20:32:23 -07:00
|
|
|
filenameStr,
|
|
|
|
EmptyString(),
|
2015-08-31 21:28:50 -07:00
|
|
|
line, column,
|
2014-08-06 20:32:23 -07:00
|
|
|
nsIScriptError::warningFlag,
|
|
|
|
"XPConnect",
|
|
|
|
windowId);
|
|
|
|
NS_ENSURE_SUCCESS(rv, true);
|
|
|
|
rv = consoleService->LogMessage(errorObject);
|
|
|
|
NS_ENSURE_SUCCESS(rv, true);
|
|
|
|
|
2014-06-05 22:32:38 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-15 18:35:12 -07:00
|
|
|
bool JSXrayTraits::getOwnPropertyFromWrapperIfSafe(JSContext* cx,
|
|
|
|
HandleObject wrapper,
|
|
|
|
HandleId id,
|
|
|
|
MutableHandle<JSPropertyDescriptor> outDesc)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
|
|
|
|
RootedObject target(cx, getTargetObject(wrapper));
|
|
|
|
{
|
|
|
|
JSAutoCompartment ac(cx, target);
|
|
|
|
if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, outDesc))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return JS_WrapPropertyDescriptor(cx, outDesc);
|
|
|
|
}
|
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
bool JSXrayTraits::getOwnPropertyFromTargetIfSafe(JSContext* cx,
|
2014-06-05 22:32:38 -07:00
|
|
|
HandleObject target,
|
|
|
|
HandleObject wrapper,
|
2014-06-23 08:57:02 -07:00
|
|
|
HandleId id,
|
2014-06-05 22:32:38 -07:00
|
|
|
MutableHandle<JSPropertyDescriptor> outDesc)
|
|
|
|
{
|
|
|
|
// Note - This function operates in the target compartment, because it
|
|
|
|
// avoids a bunch of back-and-forth wrapping in enumerateNames.
|
|
|
|
MOZ_ASSERT(getTargetObject(wrapper) == target);
|
|
|
|
MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
|
|
|
|
MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
|
|
|
|
MOZ_ASSERT(outDesc.object() == nullptr);
|
|
|
|
|
|
|
|
Rooted<JSPropertyDescriptor> desc(cx);
|
|
|
|
if (!JS_GetOwnPropertyDescriptorById(cx, target, id, &desc))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// If the property doesn't exist at all, we're done.
|
|
|
|
if (!desc.object())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Disallow accessor properties.
|
2014-08-06 20:32:22 -07:00
|
|
|
if (desc.hasGetterOrSetter()) {
|
|
|
|
JSAutoCompartment ac(cx, wrapper);
|
2014-10-03 01:05:51 -07:00
|
|
|
return ReportWrapperDenial(cx, id, WrapperDenialForXray, "property has accessor");
|
2014-08-06 20:32:22 -07:00
|
|
|
}
|
2014-06-05 22:32:38 -07:00
|
|
|
|
|
|
|
// Apply extra scrutiny to objects.
|
|
|
|
if (desc.value().isObject()) {
|
|
|
|
RootedObject propObj(cx, js::UncheckedUnwrap(&desc.value().toObject()));
|
|
|
|
JSAutoCompartment ac(cx, propObj);
|
|
|
|
|
|
|
|
// Disallow non-subsumed objects.
|
2014-08-06 20:32:22 -07:00
|
|
|
if (!AccessCheck::subsumes(target, propObj)) {
|
|
|
|
JSAutoCompartment ac(cx, wrapper);
|
2014-10-03 01:05:51 -07:00
|
|
|
return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value not same-origin with target");
|
2014-08-06 20:32:22 -07:00
|
|
|
}
|
2014-06-05 22:32:38 -07:00
|
|
|
|
2015-04-03 17:15:03 -07:00
|
|
|
// Disallow (most) non-Xrayable objects.
|
2014-07-10 09:31:37 -07:00
|
|
|
XrayType xrayType = GetXrayType(propObj);
|
2014-08-06 20:32:22 -07:00
|
|
|
if (xrayType == NotXray || xrayType == XrayForOpaqueObject) {
|
2015-04-03 17:15:03 -07:00
|
|
|
if (IdentifyStandardInstance(propObj) == JSProto_ArrayBuffer) {
|
|
|
|
// Given that non-Xrayable objects are now opaque by default,
|
|
|
|
// this restriction is somewhat more draconian than it needs to
|
|
|
|
// be. It's true that script can't do much with an opaque
|
|
|
|
// object, so in general it doesn't make much of a difference.
|
|
|
|
// But one place it _does_ make a difference is in the
|
|
|
|
// structured clone algorithm. When traversing an object to
|
|
|
|
// clone it, the algorithm dutifully traverses inspects the
|
|
|
|
// security wrapper without unwrapping it, so it won't see
|
|
|
|
// properties we restrict here. But there are some object types
|
|
|
|
// that the structured clone algorithm can handle safely even
|
|
|
|
// without Xrays (i.e. ArrayBuffer, where it just clones the
|
|
|
|
// underlying byte array).
|
|
|
|
//
|
|
|
|
// So we make some special cases here for such situations. Pass
|
|
|
|
// them through.
|
|
|
|
} else {
|
|
|
|
JSAutoCompartment ac(cx, wrapper);
|
|
|
|
return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value not Xrayable");
|
|
|
|
}
|
2014-08-06 20:32:22 -07:00
|
|
|
}
|
2014-06-05 22:32:38 -07:00
|
|
|
|
|
|
|
// Disallow callables.
|
2014-09-25 04:13:28 -07:00
|
|
|
if (JS::IsCallable(propObj)) {
|
2014-08-06 20:32:22 -07:00
|
|
|
JSAutoCompartment ac(cx, wrapper);
|
2014-10-03 01:05:51 -07:00
|
|
|
return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value is callable");
|
2014-08-06 20:32:22 -07:00
|
|
|
}
|
2014-06-05 22:32:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Disallow any property that shadows something on its (Xrayed)
|
|
|
|
// prototype chain.
|
|
|
|
JSAutoCompartment ac2(cx, wrapper);
|
|
|
|
RootedObject proto(cx);
|
|
|
|
bool foundOnProto = false;
|
|
|
|
if (!JS_GetPrototype(cx, wrapper, &proto) ||
|
|
|
|
(proto && !JS_HasPropertyById(cx, proto, id, &foundOnProto)))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (foundOnProto)
|
2014-10-03 01:05:51 -07:00
|
|
|
return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value shadows a property on the standard prototype");
|
2014-06-05 22:32:38 -07:00
|
|
|
|
|
|
|
// We made it! Assign over the descriptor, and don't forget to wrap.
|
|
|
|
outDesc.assign(desc.get());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-03-23 07:02:13 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
|
2014-03-23 07:02:13 -07:00
|
|
|
HandleObject wrapper, HandleObject holder,
|
|
|
|
HandleId id,
|
2014-04-25 14:11:02 -07:00
|
|
|
MutableHandle<JSPropertyDescriptor> desc)
|
2014-03-23 07:02:13 -07:00
|
|
|
{
|
|
|
|
// Call the common code.
|
|
|
|
bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder,
|
2014-04-25 14:11:02 -07:00
|
|
|
id, desc);
|
2014-03-23 07:02:13 -07:00
|
|
|
if (!ok || desc.object())
|
|
|
|
return ok;
|
|
|
|
|
2014-06-05 22:32:38 -07:00
|
|
|
RootedObject target(cx, getTargetObject(wrapper));
|
2014-07-11 09:09:22 -07:00
|
|
|
JSProtoKey key = getProtoKey(holder);
|
2014-06-05 22:32:38 -07:00
|
|
|
if (!isPrototype(holder)) {
|
2014-06-11 15:16:07 -07:00
|
|
|
// For Object and Array instances, we expose some properties from the
|
|
|
|
// underlying object, but only after filtering them carefully.
|
|
|
|
//
|
|
|
|
// Note that, as far as JS observables go, Arrays are just Objects with
|
|
|
|
// a different prototype and a magic (own, non-configurable) |.length| that
|
|
|
|
// serves as a non-tight upper bound on |own| indexed properties. So while
|
|
|
|
// it's tempting to try to impose some sort of structure on what Arrays
|
|
|
|
// "should" look like over Xrays, the underlying object is squishy enough
|
|
|
|
// that it makes sense to just treat them like Objects for Xray purposes.
|
2014-06-17 10:16:08 -07:00
|
|
|
if (key == JSProto_Object || key == JSProto_Array) {
|
2015-06-15 18:35:12 -07:00
|
|
|
return getOwnPropertyFromWrapperIfSafe(cx, wrapper, id, desc);
|
2014-06-17 10:16:08 -07:00
|
|
|
} else if (IsTypedArrayKey(key)) {
|
2015-10-14 12:53:16 -07:00
|
|
|
int32_t index = GetArrayIndexFromId(cx, id);
|
|
|
|
if (IsArrayIndex(index)) {
|
|
|
|
// WebExtensions can't use cloneInto(), so we just let them do
|
|
|
|
// the slow thing to maximize compatibility.
|
|
|
|
if (CompartmentPrivate::Get(CurrentGlobalOrNull(cx))->isWebExtensionContentScript) {
|
|
|
|
Rooted<JSPropertyDescriptor> innerDesc(cx);
|
|
|
|
{
|
|
|
|
JSAutoCompartment ac(cx, target);
|
|
|
|
if (!JS_GetOwnPropertyDescriptorById(cx, target, id, &innerDesc))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (innerDesc.isDataDescriptor() && innerDesc.value().isNumber()) {
|
|
|
|
desc.setValue(innerDesc.value());
|
|
|
|
desc.object().set(wrapper);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
JS_ReportError(cx, "Accessing TypedArray data over Xrays is slow, and forbidden "
|
|
|
|
"in order to encourage performant code. To copy TypedArrays "
|
|
|
|
"across origin boundaries, consider using Components.utils.cloneInto().");
|
|
|
|
return false;
|
|
|
|
}
|
2014-06-17 10:16:08 -07:00
|
|
|
}
|
2014-06-19 09:57:06 -07:00
|
|
|
} else if (key == JSProto_Function) {
|
2014-06-19 09:57:06 -07:00
|
|
|
if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_LENGTH)) {
|
|
|
|
FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
|
|
|
|
NumberValue(JS_GetFunctionArity(JS_GetObjectFunction(target))));
|
|
|
|
return true;
|
|
|
|
} else if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_NAME)) {
|
2014-07-02 11:02:11 -07:00
|
|
|
RootedString fname(cx, JS_GetFunctionId(JS_GetObjectFunction(target)));
|
2014-06-19 09:57:06 -07:00
|
|
|
FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
|
2014-07-02 11:02:11 -07:00
|
|
|
fname ? StringValue(fname) : JS_GetEmptyStringValue(cx));
|
2014-06-19 09:57:06 -07:00
|
|
|
} else if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE)) {
|
|
|
|
// Handle the 'prototype' property to make xrayedGlobal.StandardClass.prototype work.
|
2014-06-19 09:57:06 -07:00
|
|
|
JSProtoKey standardConstructor = constructorFor(holder);
|
|
|
|
if (standardConstructor != JSProto_Null) {
|
|
|
|
RootedObject standardProto(cx);
|
|
|
|
{
|
|
|
|
JSAutoCompartment ac(cx, target);
|
|
|
|
if (!JS_GetClassPrototype(cx, standardConstructor, &standardProto))
|
|
|
|
return false;
|
|
|
|
MOZ_ASSERT(standardProto);
|
|
|
|
}
|
|
|
|
if (!JS_WrapObject(cx, &standardProto))
|
|
|
|
return false;
|
|
|
|
FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
|
|
|
|
ObjectValue(*standardProto));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2014-07-11 09:09:21 -07:00
|
|
|
} else if (IsErrorObjectKey(key)) {
|
2015-03-27 13:08:46 -07:00
|
|
|
// The useful state of error objects (except for .stack) is
|
|
|
|
// (unfortunately) represented as own data properties per-spec. This
|
|
|
|
// means that we can't have a a clean representation of the data
|
|
|
|
// (free from tampering) without doubling the slots of Error
|
|
|
|
// objects, which isn't great. So we forward these properties to the
|
|
|
|
// underlying object and then just censor any values with the wrong
|
|
|
|
// type. This limits the ability of content to do anything all that
|
|
|
|
// confusing.
|
2014-07-11 09:09:21 -07:00
|
|
|
bool isErrorIntProperty =
|
|
|
|
id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_LINENUMBER) ||
|
|
|
|
id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_COLUMNNUMBER);
|
|
|
|
bool isErrorStringProperty =
|
|
|
|
id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_FILENAME) ||
|
|
|
|
id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_MESSAGE);
|
|
|
|
if (isErrorIntProperty || isErrorStringProperty) {
|
|
|
|
RootedObject waiver(cx, wrapper);
|
|
|
|
if (!WrapperFactory::WaiveXrayAndWrap(cx, &waiver))
|
|
|
|
return false;
|
|
|
|
if (!JS_GetOwnPropertyDescriptorById(cx, waiver, id, desc))
|
|
|
|
return false;
|
|
|
|
bool valueMatchesType = (isErrorIntProperty && desc.value().isInt32()) ||
|
|
|
|
(isErrorStringProperty && desc.value().isString());
|
|
|
|
if (desc.hasGetterOrSetter() || !valueMatchesType)
|
|
|
|
FillPropertyDescriptor(desc, nullptr, 0, UndefinedValue());
|
|
|
|
return true;
|
|
|
|
}
|
2015-03-18 02:22:05 -07:00
|
|
|
} else if (key == JSProto_RegExp) {
|
2015-06-15 18:35:12 -07:00
|
|
|
if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_LASTINDEX))
|
|
|
|
return getOwnPropertyFromWrapperIfSafe(cx, wrapper, id, desc);
|
2014-06-05 22:32:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// The rest of this function applies only to prototypes.
|
2014-03-23 07:02:13 -07:00
|
|
|
return true;
|
2014-06-05 22:32:38 -07:00
|
|
|
}
|
2014-03-23 07:02:13 -07:00
|
|
|
|
|
|
|
// The non-HasPrototypes semantics implemented by traditional Xrays are kind
|
|
|
|
// of broken with respect to |own|-ness and the holder. The common code
|
|
|
|
// muddles through by only checking the holder for non-|own| lookups, but
|
|
|
|
// that doesn't work for us. So we do an explicit holder check here, and hope
|
|
|
|
// that this mess gets fixed up soon.
|
2015-06-02 14:27:39 -07:00
|
|
|
if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
|
2014-03-23 07:02:13 -07:00
|
|
|
return false;
|
|
|
|
if (desc.object()) {
|
|
|
|
desc.object().set(wrapper);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle the 'constructor' property.
|
|
|
|
if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) {
|
|
|
|
RootedObject constructor(cx);
|
|
|
|
{
|
|
|
|
JSAutoCompartment ac(cx, target);
|
2014-09-18 20:06:32 -07:00
|
|
|
if (!JS_GetClassObject(cx, key, &constructor))
|
2014-03-23 07:02:13 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!JS_WrapObject(cx, &constructor))
|
|
|
|
return false;
|
|
|
|
desc.object().set(wrapper);
|
|
|
|
desc.setAttributes(0);
|
|
|
|
desc.setGetter(nullptr);
|
|
|
|
desc.setSetter(nullptr);
|
|
|
|
desc.value().setObject(*constructor);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-07-11 09:09:22 -07:00
|
|
|
// Handle the 'name' property for error prototypes.
|
|
|
|
if (IsErrorObjectKey(key) && id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_NAME)) {
|
|
|
|
RootedId className(cx);
|
|
|
|
ProtoKeyToId(cx, key, &className);
|
|
|
|
FillPropertyDescriptor(desc, wrapper, 0, UndefinedValue());
|
|
|
|
return JS_IdToValue(cx, className, desc.value());
|
|
|
|
}
|
|
|
|
|
2015-03-18 02:22:05 -07:00
|
|
|
// Handle the 'lastIndex' property for RegExp prototypes.
|
2015-06-15 18:35:12 -07:00
|
|
|
if (key == JSProto_RegExp && id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_LASTINDEX))
|
|
|
|
return getOwnPropertyFromWrapperIfSafe(cx, wrapper, id, desc);
|
2015-03-18 02:22:05 -07:00
|
|
|
|
2014-09-18 20:06:32 -07:00
|
|
|
// Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
|
2015-03-28 15:22:11 -07:00
|
|
|
const js::Class* clasp = js::GetObjectClass(target);
|
2014-09-18 20:06:32 -07:00
|
|
|
MOZ_ASSERT(clasp->spec.defined());
|
|
|
|
|
2014-09-18 10:30:38 -07:00
|
|
|
// Scan through the functions. Indexed array properties are handled above.
|
2015-03-28 15:22:11 -07:00
|
|
|
const JSFunctionSpec* fsMatch = nullptr;
|
2015-05-13 21:03:14 -07:00
|
|
|
for (const JSFunctionSpec* fs = clasp->spec.prototypeFunctions(); fs && fs->name; ++fs) {
|
2014-09-18 10:30:38 -07:00
|
|
|
if (PropertySpecNameEqualsId(fs->name, id)) {
|
2014-05-23 16:53:03 -07:00
|
|
|
fsMatch = fs;
|
2014-03-23 07:02:13 -07:00
|
|
|
break;
|
2014-05-23 16:53:03 -07:00
|
|
|
}
|
2014-03-23 07:02:13 -07:00
|
|
|
}
|
2014-05-23 16:53:03 -07:00
|
|
|
if (fsMatch) {
|
|
|
|
// Generate an Xrayed version of the method.
|
2015-07-07 17:22:20 -07:00
|
|
|
RootedFunction fun(cx, JS::NewFunctionFromSpec(cx, fsMatch, id));
|
2014-05-23 16:53:03 -07:00
|
|
|
if (!fun)
|
|
|
|
return false;
|
2014-03-23 07:02:13 -07:00
|
|
|
|
2015-06-02 14:27:39 -07:00
|
|
|
// The generic Xray machinery only defines non-own properties of the target on
|
|
|
|
// the holder. This is broken, and will be fixed at some point, but for now we
|
|
|
|
// need to cache the value explicitly. See the corresponding call to
|
|
|
|
// JS_GetOwnPropertyDescriptorById at the top of this function.
|
2014-05-23 16:53:03 -07:00
|
|
|
RootedObject funObj(cx, JS_GetFunctionObject(fun));
|
|
|
|
return JS_DefinePropertyById(cx, holder, id, funObj, 0) &&
|
2015-06-02 14:27:39 -07:00
|
|
|
JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
|
2014-05-23 16:53:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Scan through the properties.
|
2015-03-28 15:22:11 -07:00
|
|
|
const JSPropertySpec* psMatch = nullptr;
|
2015-05-13 21:03:14 -07:00
|
|
|
for (const JSPropertySpec* ps = clasp->spec.prototypeProperties(); ps && ps->name; ++ps) {
|
2014-09-18 10:30:38 -07:00
|
|
|
if (PropertySpecNameEqualsId(ps->name, id)) {
|
2014-05-23 16:53:03 -07:00
|
|
|
psMatch = ps;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (psMatch) {
|
2014-06-02 13:55:20 -07:00
|
|
|
desc.value().setUndefined();
|
|
|
|
RootedFunction getterObj(cx);
|
|
|
|
RootedFunction setterObj(cx);
|
|
|
|
unsigned flags = psMatch->flags;
|
2014-10-29 14:00:48 -07:00
|
|
|
if (psMatch->isSelfHosted()) {
|
2014-06-02 13:55:20 -07:00
|
|
|
getterObj = JS::GetSelfHostedFunction(cx, psMatch->getter.selfHosted.funname, id, 0);
|
|
|
|
if (!getterObj)
|
|
|
|
return false;
|
|
|
|
desc.setGetterObject(JS_GetFunctionObject(getterObj));
|
|
|
|
if (psMatch->setter.selfHosted.funname) {
|
|
|
|
MOZ_ASSERT(flags & JSPROP_SETTER);
|
|
|
|
setterObj = JS::GetSelfHostedFunction(cx, psMatch->setter.selfHosted.funname, id, 0);
|
|
|
|
if (!setterObj)
|
|
|
|
return false;
|
|
|
|
desc.setSetterObject(JS_GetFunctionObject(setterObj));
|
|
|
|
}
|
2014-10-29 14:00:48 -07:00
|
|
|
} else {
|
|
|
|
desc.setGetter(JS_CAST_NATIVE_TO(psMatch->getter.native.op,
|
2015-01-30 11:56:15 -08:00
|
|
|
JSGetterOp));
|
2014-10-29 14:00:48 -07:00
|
|
|
desc.setSetter(JS_CAST_NATIVE_TO(psMatch->setter.native.op,
|
2015-01-30 11:56:15 -08:00
|
|
|
JSSetterOp));
|
2014-06-02 13:55:20 -07:00
|
|
|
}
|
|
|
|
desc.setAttributes(flags);
|
|
|
|
|
2014-05-23 16:53:03 -07:00
|
|
|
// The generic Xray machinery only defines non-own properties on the holder.
|
|
|
|
// This is broken, and will be fixed at some point, but for now we need to
|
|
|
|
// cache the value explicitly. See the corresponding call to
|
|
|
|
// JS_GetPropertyById at the top of this function.
|
|
|
|
//
|
|
|
|
// Note also that the public-facing API here doesn't give us a way to
|
|
|
|
// pass along JITInfo. It's probably ok though, since Xrays are already
|
|
|
|
// pretty slow.
|
|
|
|
return JS_DefinePropertyById(cx, holder, id,
|
2014-10-29 12:06:31 -07:00
|
|
|
UndefinedHandleValue,
|
|
|
|
// This particular descriptor, unlike most,
|
|
|
|
// actually stores JSNatives directly,
|
|
|
|
// since we just set it up. Do NOT pass
|
|
|
|
// JSPROP_PROPOP_ACCESSORS here!
|
|
|
|
desc.attributes(),
|
|
|
|
JS_PROPERTYOP_GETTER(desc.getter()),
|
|
|
|
JS_PROPERTYOP_SETTER(desc.setter())) &&
|
2015-06-02 14:27:39 -07:00
|
|
|
JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
|
2014-05-23 16:53:03 -07:00
|
|
|
}
|
2014-03-23 07:02:13 -07:00
|
|
|
|
2014-05-23 16:53:03 -07:00
|
|
|
return true;
|
2014-03-23 07:02:13 -07:00
|
|
|
}
|
|
|
|
|
2014-06-05 22:32:39 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
JSXrayTraits::delete_(JSContext* cx, HandleObject wrapper, HandleId id, ObjectOpResult& result)
|
2014-06-05 22:32:39 -07:00
|
|
|
{
|
|
|
|
RootedObject holder(cx, ensureHolder(cx, wrapper));
|
|
|
|
|
|
|
|
// If we're using Object Xrays, we allow callers to attempt to delete any
|
|
|
|
// property from the underlying object that they are able to resolve. Note
|
|
|
|
// that this deleting may fail if the property is non-configurable.
|
2014-06-11 15:16:07 -07:00
|
|
|
JSProtoKey key = getProtoKey(holder);
|
|
|
|
bool isObjectOrArrayInstance = (key == JSProto_Object || key == JSProto_Array) &&
|
|
|
|
!isPrototype(holder);
|
|
|
|
if (isObjectOrArrayInstance) {
|
2014-06-05 22:32:39 -07:00
|
|
|
RootedObject target(cx, getTargetObject(wrapper));
|
|
|
|
JSAutoCompartment ac(cx, target);
|
|
|
|
Rooted<JSPropertyDescriptor> desc(cx);
|
|
|
|
if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, &desc))
|
|
|
|
return false;
|
|
|
|
if (desc.object())
|
2015-02-04 08:20:04 -08:00
|
|
|
return JS_DeletePropertyById(cx, target, id, result);
|
2014-06-05 22:32:39 -07:00
|
|
|
}
|
2015-02-04 08:20:04 -08:00
|
|
|
return result.succeed();
|
2014-06-05 22:32:39 -07:00
|
|
|
}
|
|
|
|
|
2014-06-05 22:32:39 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
JSXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
|
2015-02-17 09:58:43 -08:00
|
|
|
Handle<JSPropertyDescriptor> desc,
|
2015-01-30 09:37:07 -08:00
|
|
|
Handle<JSPropertyDescriptor> existingDesc,
|
2015-03-28 15:22:11 -07:00
|
|
|
ObjectOpResult& result,
|
|
|
|
bool* defined)
|
2014-06-05 22:32:39 -07:00
|
|
|
{
|
|
|
|
*defined = false;
|
|
|
|
RootedObject holder(cx, ensureHolder(cx, wrapper));
|
|
|
|
if (!holder)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
2014-06-11 15:16:07 -07:00
|
|
|
// Object and Array instances are special. For those cases, we forward property
|
2014-06-05 22:32:39 -07:00
|
|
|
// definitions to the underlying object if the following conditions are met:
|
|
|
|
// * The property being defined is a value-prop.
|
|
|
|
// * The property being defined is either a primitive or subsumed by the target.
|
|
|
|
// * As seen from the Xray, any existing property that we would overwrite is an
|
|
|
|
// |own| value-prop.
|
|
|
|
//
|
2014-06-11 15:16:07 -07:00
|
|
|
// To avoid confusion, we disallow expandos on Object and Array instances, and
|
2014-06-05 22:32:39 -07:00
|
|
|
// therefore raise an exception here if the above conditions aren't met.
|
2014-06-11 15:16:07 -07:00
|
|
|
JSProtoKey key = getProtoKey(holder);
|
2015-10-14 12:53:16 -07:00
|
|
|
bool isInstance = !isPrototype(holder);
|
|
|
|
bool isObjectOrArray = (key == JSProto_Object || key == JSProto_Array);
|
|
|
|
if (isObjectOrArray && isInstance) {
|
2014-06-05 22:32:39 -07:00
|
|
|
RootedObject target(cx, getTargetObject(wrapper));
|
|
|
|
if (desc.hasGetterOrSetter()) {
|
2014-06-11 15:16:07 -07:00
|
|
|
JS_ReportError(cx, "Not allowed to define accessor property on [Object] or [Array] XrayWrapper");
|
2014-06-05 22:32:39 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (desc.value().isObject() &&
|
|
|
|
!AccessCheck::subsumes(target, js::UncheckedUnwrap(&desc.value().toObject())))
|
|
|
|
{
|
2014-06-11 15:16:07 -07:00
|
|
|
JS_ReportError(cx, "Not allowed to define cross-origin object as property on [Object] or [Array] XrayWrapper");
|
2014-06-05 22:32:39 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (existingDesc.hasGetterOrSetter()) {
|
2014-06-11 15:16:07 -07:00
|
|
|
JS_ReportError(cx, "Not allowed to overwrite accessor property on [Object] or [Array] XrayWrapper");
|
2014-06-05 22:32:39 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (existingDesc.object() && existingDesc.object() != wrapper) {
|
2014-06-11 15:16:07 -07:00
|
|
|
JS_ReportError(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] or [Array] XrayWrapper");
|
2014-06-05 22:32:39 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-02-17 09:58:43 -08:00
|
|
|
Rooted<JSPropertyDescriptor> wrappedDesc(cx, desc);
|
2014-06-05 22:32:39 -07:00
|
|
|
JSAutoCompartment ac(cx, target);
|
2015-02-17 09:58:43 -08:00
|
|
|
if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc) ||
|
|
|
|
!JS_DefinePropertyById(cx, target, id, wrappedDesc, result))
|
2014-06-05 22:32:39 -07:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*defined = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-10-14 12:53:16 -07:00
|
|
|
// For WebExtensions content scripts, we forward the definition of indexed properties. By
|
|
|
|
// validating that the key and value are both numbers, we can avoid doing any wrapping.
|
|
|
|
if (isInstance && IsTypedArrayKey(key) &&
|
|
|
|
CompartmentPrivate::Get(JS::CurrentGlobalOrNull(cx))->isWebExtensionContentScript &&
|
|
|
|
desc.isDataDescriptor() && (desc.value().isNumber() || desc.value().isUndefined()) &&
|
|
|
|
IsArrayIndex(GetArrayIndexFromId(cx, id)))
|
|
|
|
{
|
|
|
|
RootedObject target(cx, getTargetObject(wrapper));
|
|
|
|
JSAutoCompartment ac(cx, target);
|
|
|
|
if (!JS_DefinePropertyById(cx, target, id, desc, result))
|
|
|
|
return false;
|
|
|
|
*defined = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-05 22:32:39 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-18 10:30:38 -07:00
|
|
|
static bool
|
2015-03-28 15:22:11 -07:00
|
|
|
MaybeAppend(jsid id, unsigned flags, AutoIdVector& props)
|
2014-09-18 10:30:38 -07:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(!(flags & JSITER_SYMBOLSONLY));
|
|
|
|
if (!(flags & JSITER_SYMBOLS) && JSID_IS_SYMBOL(id))
|
|
|
|
return true;
|
|
|
|
return props.append(id);
|
|
|
|
}
|
|
|
|
|
2014-03-23 07:02:13 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags,
|
|
|
|
AutoIdVector& props)
|
2014-03-23 07:02:13 -07:00
|
|
|
{
|
2014-06-05 22:32:38 -07:00
|
|
|
RootedObject target(cx, getTargetObject(wrapper));
|
2014-03-23 07:02:13 -07:00
|
|
|
RootedObject holder(cx, ensureHolder(cx, wrapper));
|
|
|
|
if (!holder)
|
|
|
|
return false;
|
|
|
|
|
2014-07-11 09:09:22 -07:00
|
|
|
JSProtoKey key = getProtoKey(holder);
|
2014-06-05 22:32:38 -07:00
|
|
|
if (!isPrototype(holder)) {
|
2014-06-11 15:16:07 -07:00
|
|
|
// For Object and Array instances, we expose some properties from the underlying
|
2014-06-05 22:32:38 -07:00
|
|
|
// object, but only after filtering them carefully.
|
2014-06-17 10:16:08 -07:00
|
|
|
if (key == JSProto_Object || key == JSProto_Array) {
|
2014-06-05 22:32:38 -07:00
|
|
|
MOZ_ASSERT(props.empty());
|
|
|
|
{
|
|
|
|
JSAutoCompartment ac(cx, target);
|
|
|
|
AutoIdVector targetProps(cx);
|
2014-09-26 17:39:35 -07:00
|
|
|
if (!js::GetPropertyKeys(cx, target, flags | JSITER_OWNONLY, &targetProps))
|
2014-06-05 22:32:38 -07:00
|
|
|
return false;
|
|
|
|
// Loop over the properties, and only pass along the ones that
|
|
|
|
// we determine to be safe.
|
2014-06-17 10:16:08 -07:00
|
|
|
if (!props.reserve(targetProps.length()))
|
|
|
|
return false;
|
2014-06-05 22:32:38 -07:00
|
|
|
for (size_t i = 0; i < targetProps.length(); ++i) {
|
|
|
|
Rooted<JSPropertyDescriptor> desc(cx);
|
|
|
|
RootedId id(cx, targetProps[i]);
|
|
|
|
if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, &desc))
|
|
|
|
return false;
|
|
|
|
if (desc.object())
|
2014-06-17 10:16:08 -07:00
|
|
|
props.infallibleAppend(id);
|
2014-06-05 22:32:38 -07:00
|
|
|
}
|
|
|
|
}
|
2014-06-23 08:57:02 -07:00
|
|
|
return true;
|
2014-06-17 10:16:08 -07:00
|
|
|
} else if (IsTypedArrayKey(key)) {
|
|
|
|
uint32_t length = JS_GetTypedArrayLength(target);
|
|
|
|
// TypedArrays enumerate every indexed property in range, but
|
|
|
|
// |length| is a getter that lives on the proto, like it should be.
|
|
|
|
if (!props.reserve(length))
|
|
|
|
return false;
|
|
|
|
for (int32_t i = 0; i <= int32_t(length - 1); ++i)
|
|
|
|
props.infallibleAppend(INT_TO_JSID(i));
|
2014-06-19 09:57:06 -07:00
|
|
|
} else if (key == JSProto_Function) {
|
2014-06-19 09:57:06 -07:00
|
|
|
if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_LENGTH)))
|
|
|
|
return false;
|
|
|
|
if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_NAME)))
|
|
|
|
return false;
|
2014-06-19 09:57:06 -07:00
|
|
|
// Handle the .prototype property on standard constructors.
|
|
|
|
if (constructorFor(holder) != JSProto_Null) {
|
|
|
|
if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE)))
|
|
|
|
return false;
|
|
|
|
}
|
2014-07-11 09:09:21 -07:00
|
|
|
} else if (IsErrorObjectKey(key)) {
|
|
|
|
if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_FILENAME)) ||
|
|
|
|
!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_LINENUMBER)) ||
|
|
|
|
!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_COLUMNNUMBER)) ||
|
|
|
|
!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_STACK)) ||
|
|
|
|
!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_MESSAGE)))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2015-03-18 02:22:05 -07:00
|
|
|
} else if (key == JSProto_RegExp) {
|
|
|
|
if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_LASTINDEX)))
|
|
|
|
return false;
|
2014-06-05 22:32:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// The rest of this function applies only to prototypes.
|
2014-03-23 07:02:13 -07:00
|
|
|
return true;
|
2014-06-05 22:32:38 -07:00
|
|
|
}
|
2014-03-23 07:02:13 -07:00
|
|
|
|
2014-07-04 12:41:28 -07:00
|
|
|
// Add the 'constructor' property.
|
|
|
|
if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)))
|
|
|
|
return false;
|
|
|
|
|
2014-07-11 09:09:22 -07:00
|
|
|
// For Error protoypes, add the 'name' property.
|
|
|
|
if (IsErrorObjectKey(key) && !props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_NAME)))
|
|
|
|
return false;
|
|
|
|
|
2015-03-18 02:22:05 -07:00
|
|
|
// For RegExp protoypes, add the 'lastIndex' property.
|
|
|
|
if (key == JSProto_RegExp && !props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_LASTINDEX)))
|
|
|
|
return false;
|
|
|
|
|
2014-09-18 20:06:32 -07:00
|
|
|
// Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
|
2015-03-28 15:22:11 -07:00
|
|
|
const js::Class* clasp = js::GetObjectClass(target);
|
2014-09-18 20:06:32 -07:00
|
|
|
MOZ_ASSERT(clasp->spec.defined());
|
2014-03-23 07:02:13 -07:00
|
|
|
|
2014-09-18 10:30:38 -07:00
|
|
|
// Convert the method and property names to jsids and pass them to the caller.
|
2015-05-13 21:03:14 -07:00
|
|
|
for (const JSFunctionSpec* fs = clasp->spec.prototypeFunctions(); fs && fs->name; ++fs) {
|
2014-09-18 10:30:38 -07:00
|
|
|
jsid id;
|
|
|
|
if (!PropertySpecNameToPermanentId(cx, fs->name, &id))
|
2014-03-23 07:02:13 -07:00
|
|
|
return false;
|
2014-09-18 10:30:38 -07:00
|
|
|
if (!MaybeAppend(id, flags, props))
|
2014-03-23 07:02:13 -07:00
|
|
|
return false;
|
2014-05-23 16:53:03 -07:00
|
|
|
}
|
2015-05-13 21:03:14 -07:00
|
|
|
for (const JSPropertySpec* ps = clasp->spec.prototypeProperties(); ps && ps->name; ++ps) {
|
2014-09-18 10:30:38 -07:00
|
|
|
jsid id;
|
|
|
|
if (!PropertySpecNameToPermanentId(cx, ps->name, &id))
|
2014-05-23 16:53:03 -07:00
|
|
|
return false;
|
2014-09-18 10:30:38 -07:00
|
|
|
if (!MaybeAppend(id, flags, props))
|
2014-05-23 16:53:03 -07:00
|
|
|
return false;
|
2014-03-23 07:02:13 -07:00
|
|
|
}
|
|
|
|
|
2014-07-04 12:41:28 -07:00
|
|
|
return true;
|
2014-03-23 07:02:13 -07:00
|
|
|
}
|
|
|
|
|
2014-03-23 07:02:13 -07:00
|
|
|
JSObject*
|
2015-03-28 15:22:11 -07:00
|
|
|
JSXrayTraits::createHolder(JSContext* cx, JSObject* wrapper)
|
2014-03-23 07:02:13 -07:00
|
|
|
{
|
|
|
|
RootedObject target(cx, getTargetObject(wrapper));
|
|
|
|
RootedObject holder(cx, JS_NewObjectWithGivenProto(cx, &HolderClass,
|
2015-05-13 14:07:34 -07:00
|
|
|
nullptr));
|
2014-03-23 07:02:13 -07:00
|
|
|
if (!holder)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
// Compute information about the target.
|
|
|
|
bool isPrototype = false;
|
|
|
|
JSProtoKey key = IdentifyStandardInstance(target);
|
|
|
|
if (key == JSProto_Null) {
|
|
|
|
isPrototype = true;
|
|
|
|
key = IdentifyStandardPrototype(target);
|
|
|
|
}
|
|
|
|
MOZ_ASSERT(key != JSProto_Null);
|
|
|
|
|
|
|
|
// Store it on the holder.
|
|
|
|
RootedValue v(cx);
|
|
|
|
v.setNumber(static_cast<uint32_t>(key));
|
|
|
|
js::SetReservedSlot(holder, SLOT_PROTOKEY, v);
|
|
|
|
v.setBoolean(isPrototype);
|
|
|
|
js::SetReservedSlot(holder, SLOT_ISPROTOTYPE, v);
|
|
|
|
|
2014-06-19 09:57:06 -07:00
|
|
|
// If this is a function, also compute whether it serves as a constructor
|
|
|
|
// for a standard class.
|
|
|
|
if (key == JSProto_Function) {
|
|
|
|
v.setNumber(static_cast<uint32_t>(IdentifyStandardConstructor(target)));
|
|
|
|
js::SetReservedSlot(holder, SLOT_CONSTRUCTOR_FOR, v);
|
|
|
|
}
|
|
|
|
|
2014-03-23 07:02:13 -07:00
|
|
|
return holder;
|
|
|
|
}
|
|
|
|
|
2012-10-05 09:59:23 -07:00
|
|
|
XPCWrappedNativeXrayTraits XPCWrappedNativeXrayTraits::singleton;
|
|
|
|
DOMXrayTraits DOMXrayTraits::singleton;
|
2014-03-23 07:02:12 -07:00
|
|
|
JSXrayTraits JSXrayTraits::singleton;
|
2014-07-10 09:31:37 -07:00
|
|
|
OpaqueXrayTraits OpaqueXrayTraits::singleton;
|
2010-07-02 14:09:48 -07:00
|
|
|
|
2012-10-05 09:59:23 -07:00
|
|
|
XrayTraits*
|
2015-03-28 15:22:11 -07:00
|
|
|
GetXrayTraits(JSObject* obj)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
|
|
|
switch (GetXrayType(obj)) {
|
|
|
|
case XrayForDOMObject:
|
|
|
|
return &DOMXrayTraits::singleton;
|
|
|
|
case XrayForWrappedNative:
|
|
|
|
return &XPCWrappedNativeXrayTraits::singleton;
|
2014-03-23 07:02:12 -07:00
|
|
|
case XrayForJSObject:
|
|
|
|
return &JSXrayTraits::singleton;
|
2014-07-10 09:31:37 -07:00
|
|
|
case XrayForOpaqueObject:
|
|
|
|
return &OpaqueXrayTraits::singleton;
|
2012-10-05 09:59:23 -07:00
|
|
|
default:
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-05 10:07:37 -07:00
|
|
|
/*
|
|
|
|
* Xray expando handling.
|
|
|
|
*
|
|
|
|
* We hang expandos for Xray wrappers off a reserved slot on the target object
|
|
|
|
* so that same-origin compartments can share expandos for a given object. We
|
|
|
|
* have a linked list of expando objects, one per origin. The properties on these
|
|
|
|
* objects are generally wrappers pointing back to the compartment that applied
|
|
|
|
* them.
|
|
|
|
*
|
|
|
|
* The expando objects should _never_ be exposed to script. The fact that they
|
|
|
|
* live in the target compartment is a detail of the implementation, and does
|
|
|
|
* not imply that code in the target compartment should be allowed to inspect
|
|
|
|
* them. They are private to the origin that placed them.
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum ExpandoSlots {
|
|
|
|
JSSLOT_EXPANDO_NEXT = 0,
|
|
|
|
JSSLOT_EXPANDO_ORIGIN,
|
|
|
|
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
|
2013-12-13 12:01:30 -08:00
|
|
|
JSSLOT_EXPANDO_PROTOTYPE,
|
2012-06-05 10:07:37 -07:00
|
|
|
JSSLOT_EXPANDO_COUNT
|
|
|
|
};
|
|
|
|
|
|
|
|
static nsIPrincipal*
|
2015-03-28 15:22:11 -07:00
|
|
|
ObjectPrincipal(JSObject* obj)
|
2012-06-05 10:07:37 -07:00
|
|
|
{
|
|
|
|
return GetCompartmentPrincipal(js::GetObjectCompartment(obj));
|
|
|
|
}
|
|
|
|
|
|
|
|
static nsIPrincipal*
|
2015-03-28 15:22:11 -07:00
|
|
|
GetExpandoObjectPrincipal(JSObject* expandoObject)
|
2012-06-05 10:07:37 -07:00
|
|
|
{
|
2013-03-27 04:33:30 -07:00
|
|
|
Value v = JS_GetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN);
|
2012-06-05 10:07:37 -07:00
|
|
|
return static_cast<nsIPrincipal*>(v.toPrivate());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-03-28 15:22:11 -07:00
|
|
|
ExpandoObjectFinalize(JSFreeOp* fop, JSObject* obj)
|
2012-06-05 10:07:37 -07:00
|
|
|
{
|
|
|
|
// Release the principal.
|
2015-03-28 15:22:11 -07:00
|
|
|
nsIPrincipal* principal = GetExpandoObjectPrincipal(obj);
|
2012-06-05 10:07:37 -07:00
|
|
|
NS_RELEASE(principal);
|
|
|
|
}
|
|
|
|
|
2013-09-11 05:49:05 -07:00
|
|
|
const JSClass ExpandoObjectClass = {
|
2012-06-05 10:07:37 -07:00
|
|
|
"XrayExpandoObject",
|
|
|
|
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_EXPANDO_COUNT),
|
2015-03-20 14:28:59 -07:00
|
|
|
nullptr, nullptr, nullptr, nullptr,
|
2014-12-02 14:56:35 -08:00
|
|
|
nullptr, nullptr, nullptr, ExpandoObjectFinalize
|
2012-06-05 10:07:37 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayTraits::expandoObjectMatchesConsumer(JSContext* cx,
|
2013-03-27 04:33:30 -07:00
|
|
|
HandleObject expandoObject,
|
2015-03-28 15:22:11 -07:00
|
|
|
nsIPrincipal* consumerOrigin,
|
2013-03-27 04:33:30 -07:00
|
|
|
HandleObject exclusiveGlobal)
|
2012-06-05 10:07:37 -07:00
|
|
|
{
|
2012-06-18 06:28:11 -07:00
|
|
|
MOZ_ASSERT(js::IsObjectInContextCompartment(expandoObject, cx));
|
|
|
|
|
2012-06-05 10:07:37 -07:00
|
|
|
// First, compare the principals.
|
2015-03-28 15:22:11 -07:00
|
|
|
nsIPrincipal* o = GetExpandoObjectPrincipal(expandoObject);
|
2012-06-05 10:07:37 -07:00
|
|
|
// Note that it's very important here to ignore document.domain. We
|
|
|
|
// pull the principal for the expando object off of the first consumer
|
|
|
|
// for a given origin, and freely share the expandos amongst multiple
|
|
|
|
// same-origin consumers afterwards. However, this means that we have
|
|
|
|
// no way to know whether _all_ consumers have opted in to collaboration
|
|
|
|
// by explicitly setting document.domain. So we just mandate that expando
|
|
|
|
// sharing is unaffected by it.
|
2014-02-13 18:57:36 -08:00
|
|
|
if (!consumerOrigin->Equals(o))
|
|
|
|
return false;
|
2012-06-05 10:07:37 -07:00
|
|
|
|
|
|
|
// Sandboxes want exclusive expando objects.
|
2015-03-28 15:22:11 -07:00
|
|
|
JSObject* owner = JS_GetReservedSlot(expandoObject,
|
2012-06-05 10:07:37 -07:00
|
|
|
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL)
|
|
|
|
.toObjectOrNull();
|
|
|
|
if (!owner && !exclusiveGlobal)
|
|
|
|
return true;
|
2012-06-18 06:28:11 -07:00
|
|
|
|
|
|
|
// The exclusive global should always be wrapped in the target's compartment.
|
|
|
|
MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx));
|
|
|
|
MOZ_ASSERT(!owner || js::IsObjectInContextCompartment(owner, cx));
|
2012-06-05 10:07:37 -07:00
|
|
|
return owner == exclusiveGlobal;
|
|
|
|
}
|
|
|
|
|
2014-10-21 02:59:30 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayTraits::getExpandoObjectInternal(JSContext* cx, HandleObject target,
|
|
|
|
nsIPrincipal* origin,
|
|
|
|
JSObject* exclusiveGlobalArg,
|
2014-10-21 02:59:30 -07:00
|
|
|
MutableHandleObject expandoObject)
|
2012-06-05 10:07:37 -07:00
|
|
|
{
|
2014-10-21 02:59:30 -07:00
|
|
|
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
|
|
|
expandoObject.set(nullptr);
|
|
|
|
|
2012-06-18 06:28:11 -07:00
|
|
|
// The expando object lives in the compartment of the target, so all our
|
|
|
|
// work needs to happen there.
|
2013-04-15 07:29:37 -07:00
|
|
|
RootedObject exclusiveGlobal(cx, exclusiveGlobalArg);
|
2012-08-21 18:42:53 -07:00
|
|
|
JSAutoCompartment ac(cx, target);
|
2013-10-15 17:02:23 -07:00
|
|
|
if (!JS_WrapObject(cx, &exclusiveGlobal))
|
2014-10-21 02:59:30 -07:00
|
|
|
return false;
|
2012-06-18 06:28:11 -07:00
|
|
|
|
2012-06-05 10:07:37 -07:00
|
|
|
// Iterate through the chain, looking for a same-origin object.
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject head(cx, getExpandoChain(target));
|
2012-06-05 10:07:37 -07:00
|
|
|
while (head) {
|
2014-10-21 02:59:30 -07:00
|
|
|
if (expandoObjectMatchesConsumer(cx, head, origin, exclusiveGlobal)) {
|
|
|
|
expandoObject.set(head);
|
|
|
|
return true;
|
|
|
|
}
|
2012-06-05 10:07:37 -07:00
|
|
|
head = JS_GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not found.
|
2014-10-21 02:59:30 -07:00
|
|
|
return true;
|
2012-06-05 10:07:37 -07:00
|
|
|
}
|
|
|
|
|
2014-10-21 02:59:30 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayTraits::getExpandoObject(JSContext* cx, HandleObject target, HandleObject consumer,
|
2014-10-21 02:59:30 -07:00
|
|
|
MutableHandleObject expandoObject)
|
2012-06-05 10:07:37 -07:00
|
|
|
{
|
2015-03-28 15:22:11 -07:00
|
|
|
JSObject* consumerGlobal = js::GetGlobalForObjectCrossCompartment(consumer);
|
2012-06-05 10:07:37 -07:00
|
|
|
bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox");
|
2012-10-05 09:59:23 -07:00
|
|
|
return getExpandoObjectInternal(cx, target, ObjectPrincipal(consumer),
|
2014-10-21 02:59:30 -07:00
|
|
|
isSandbox ? consumerGlobal : nullptr,
|
|
|
|
expandoObject);
|
2012-06-05 10:07:37 -07:00
|
|
|
}
|
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
JSObject*
|
|
|
|
XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target,
|
|
|
|
nsIPrincipal* origin, HandleObject exclusiveGlobal)
|
2012-06-05 10:07:37 -07:00
|
|
|
{
|
2012-06-18 06:28:11 -07:00
|
|
|
// Make sure the compartments are sane.
|
|
|
|
MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
|
|
|
|
MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx));
|
|
|
|
|
2012-06-05 10:07:37 -07:00
|
|
|
// No duplicates allowed.
|
2014-10-21 02:59:30 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
RootedObject existingExpandoObject(cx);
|
|
|
|
if (getExpandoObjectInternal(cx, target, origin, exclusiveGlobal, &existingExpandoObject))
|
|
|
|
MOZ_ASSERT(!existingExpandoObject);
|
|
|
|
else
|
|
|
|
JS_ClearPendingException(cx);
|
|
|
|
}
|
|
|
|
#endif
|
2012-06-05 10:07:37 -07:00
|
|
|
|
2015-02-26 06:40:08 -08:00
|
|
|
// Create the expando object.
|
2015-02-26 06:40:08 -08:00
|
|
|
RootedObject expandoObject(cx,
|
2015-05-13 14:07:34 -07:00
|
|
|
JS_NewObjectWithGivenProto(cx, &ExpandoObjectClass, nullptr));
|
2012-06-05 10:07:37 -07:00
|
|
|
if (!expandoObject)
|
2012-07-30 07:20:58 -07:00
|
|
|
return nullptr;
|
2012-06-05 10:07:37 -07:00
|
|
|
|
|
|
|
// AddRef and store the principal.
|
|
|
|
NS_ADDREF(origin);
|
2015-06-30 11:20:58 -07:00
|
|
|
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, JS::PrivateValue(origin));
|
2012-06-05 10:07:37 -07:00
|
|
|
|
|
|
|
// Note the exclusive global, if any.
|
|
|
|
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
|
2015-06-30 21:09:46 -07:00
|
|
|
ObjectOrNullValue(exclusiveGlobal));
|
2012-06-05 10:07:37 -07:00
|
|
|
|
|
|
|
// If this is our first expando object, take the opportunity to preserve
|
|
|
|
// the wrapper. This keeps our expandos alive even if the Xray wrapper gets
|
|
|
|
// collected.
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject chain(cx, getExpandoChain(target));
|
2012-10-05 09:59:23 -07:00
|
|
|
if (!chain)
|
|
|
|
preserveWrapper(target);
|
2012-06-05 10:07:37 -07:00
|
|
|
|
|
|
|
// Insert it at the front of the chain.
|
2015-06-30 21:09:46 -07:00
|
|
|
JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, ObjectOrNullValue(chain));
|
2014-02-21 11:35:48 -08:00
|
|
|
setExpandoChain(cx, target, expandoObject);
|
2012-06-05 10:07:37 -07:00
|
|
|
|
|
|
|
return expandoObject;
|
|
|
|
}
|
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
JSObject*
|
|
|
|
XrayTraits::ensureExpandoObject(JSContext* cx, HandleObject wrapper,
|
2013-03-27 04:33:30 -07:00
|
|
|
HandleObject target)
|
2012-06-05 10:07:37 -07:00
|
|
|
{
|
2012-06-18 06:28:11 -07:00
|
|
|
// Expando objects live in the target compartment.
|
2012-08-21 18:42:53 -07:00
|
|
|
JSAutoCompartment ac(cx, target);
|
2014-10-21 02:59:30 -07:00
|
|
|
RootedObject expandoObject(cx);
|
|
|
|
if (!getExpandoObject(cx, target, wrapper, &expandoObject))
|
|
|
|
return nullptr;
|
2012-06-05 10:07:37 -07:00
|
|
|
if (!expandoObject) {
|
|
|
|
// If the object is a sandbox, we don't want it to share expandos with
|
|
|
|
// anyone else, so we tag it with the sandbox global.
|
2012-06-18 06:28:11 -07:00
|
|
|
//
|
|
|
|
// NB: We first need to check the class, _then_ wrap for the target's
|
|
|
|
// compartment.
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject consumerGlobal(cx, js::GetGlobalForObjectCrossCompartment(wrapper));
|
2012-06-05 10:07:37 -07:00
|
|
|
bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox");
|
2013-10-15 17:02:23 -07:00
|
|
|
if (!JS_WrapObject(cx, &consumerGlobal))
|
2013-10-10 13:35:53 -07:00
|
|
|
return nullptr;
|
2012-10-05 09:59:23 -07:00
|
|
|
expandoObject = attachExpandoObject(cx, target, ObjectPrincipal(wrapper),
|
2015-05-13 14:07:34 -07:00
|
|
|
isSandbox ? (HandleObject)consumerGlobal : nullptr);
|
2012-06-05 10:07:37 -07:00
|
|
|
}
|
|
|
|
return expandoObject;
|
|
|
|
}
|
|
|
|
|
2012-06-05 10:07:37 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayTraits::cloneExpandoChain(JSContext* cx, HandleObject dst, HandleObject src)
|
2012-06-05 10:07:37 -07:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(js::IsObjectInContextCompartment(dst, cx));
|
2012-10-05 09:59:23 -07:00
|
|
|
MOZ_ASSERT(getExpandoChain(dst) == nullptr);
|
2012-06-05 10:07:37 -07:00
|
|
|
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject oldHead(cx, getExpandoChain(src));
|
2015-03-05 05:39:53 -08:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// When this is called from dom::ReparentWrapper() there will be no native
|
|
|
|
// set for |dst|. Eventually it will be set to that of |src|. This will
|
|
|
|
// prevent attachExpandoObject() from preserving the wrapper, but this is
|
|
|
|
// not a problem because in this case the wrapper will already have been
|
2015-03-06 02:19:07 -08:00
|
|
|
// preserved when expandos were originally added to |src|. Assert the
|
|
|
|
// wrapper for |src| has been preserved if it has expandos set.
|
2015-03-05 05:39:53 -08:00
|
|
|
if (oldHead) {
|
2015-03-28 15:22:11 -07:00
|
|
|
nsISupports* identity = mozilla::dom::UnwrapDOMObjectToISupports(src);
|
2015-03-06 02:19:07 -08:00
|
|
|
if (identity) {
|
|
|
|
nsWrapperCache* cache = nullptr;
|
|
|
|
CallQueryInterface(identity, &cache);
|
|
|
|
MOZ_ASSERT_IF(cache, cache->PreservingWrapper());
|
2015-03-05 05:39:53 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-06-05 10:07:37 -07:00
|
|
|
while (oldHead) {
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject exclusive(cx, JS_GetReservedSlot(oldHead,
|
|
|
|
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL)
|
|
|
|
.toObjectOrNull());
|
2013-10-15 17:02:23 -07:00
|
|
|
if (!JS_WrapObject(cx, &exclusive))
|
2012-06-18 06:28:11 -07:00
|
|
|
return false;
|
2014-03-17 09:17:58 -07:00
|
|
|
RootedObject newHead(cx, attachExpandoObject(cx, dst, GetExpandoObjectPrincipal(oldHead),
|
|
|
|
exclusive));
|
2012-06-05 10:07:37 -07:00
|
|
|
if (!JS_CopyPropertiesFrom(cx, newHead, oldHead))
|
|
|
|
return false;
|
|
|
|
oldHead = JS_GetReservedSlot(oldHead, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2012-10-05 09:59:23 -07:00
|
|
|
|
|
|
|
namespace XrayUtils {
|
2015-03-28 15:22:11 -07:00
|
|
|
bool CloneExpandoChain(JSContext* cx, JSObject* dstArg, JSObject* srcArg)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
2013-04-15 07:29:37 -07:00
|
|
|
RootedObject dst(cx, dstArg);
|
|
|
|
RootedObject src(cx, srcArg);
|
2012-10-05 09:59:23 -07:00
|
|
|
return GetXrayTraits(src)->cloneExpandoChain(cx, dst, src);
|
|
|
|
}
|
2015-07-13 08:25:42 -07:00
|
|
|
} // namespace XrayUtils
|
2012-10-05 09:59:22 -07:00
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
static JSObject*
|
|
|
|
GetHolder(JSObject* obj)
|
2010-09-23 15:56:28 -07:00
|
|
|
{
|
2011-05-23 08:39:25 -07:00
|
|
|
return &js::GetProxyExtra(obj, 0).toObject();
|
2010-09-23 15:56:28 -07:00
|
|
|
}
|
|
|
|
|
2012-10-05 09:59:23 -07:00
|
|
|
JSObject*
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayTraits::getHolder(JSObject* wrapper)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
|
|
|
|
js::Value v = js::GetProxyExtra(wrapper, 0);
|
|
|
|
return v.isObject() ? &v.toObject() : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject*
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayTraits::ensureHolder(JSContext* cx, HandleObject wrapper)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject holder(cx, getHolder(wrapper));
|
2012-10-05 09:59:23 -07:00
|
|
|
if (holder)
|
|
|
|
return holder;
|
|
|
|
holder = createHolder(cx, wrapper); // virtual trap.
|
|
|
|
if (holder)
|
|
|
|
js::SetProxyExtra(wrapper, 0, ObjectValue(*holder));
|
|
|
|
return holder;
|
|
|
|
}
|
|
|
|
|
2014-02-21 15:55:31 -08:00
|
|
|
namespace XrayUtils {
|
|
|
|
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
IsXPCWNHolderClass(const JSClass* clasp)
|
2014-02-21 15:55:31 -08:00
|
|
|
{
|
|
|
|
return clasp == &XPCWrappedNativeXrayTraits::HolderClass;
|
|
|
|
}
|
|
|
|
|
2015-07-13 08:25:42 -07:00
|
|
|
} // namespace XrayUtils
|
2014-02-21 15:55:31 -08:00
|
|
|
|
2014-01-29 22:34:25 -08:00
|
|
|
static nsGlobalWindow*
|
2015-03-28 15:22:11 -07:00
|
|
|
AsWindow(JSContext* cx, JSObject* wrapper)
|
2012-08-27 06:15:20 -07:00
|
|
|
{
|
2014-01-29 22:34:25 -08:00
|
|
|
nsGlobalWindow* win;
|
|
|
|
// We want to use our target object here, since we don't want to be
|
|
|
|
// doing a security check while unwrapping.
|
|
|
|
JSObject* target = XrayTraits::getTargetObject(wrapper);
|
|
|
|
nsresult rv = UNWRAP_OBJECT(Window, target, win);
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
return win;
|
|
|
|
|
|
|
|
nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(
|
|
|
|
nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, target));
|
|
|
|
return static_cast<nsGlobalWindow*>(piWin.get());
|
2013-01-17 09:30:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2015-03-28 15:22:11 -07:00
|
|
|
IsWindow(JSContext* cx, JSObject* wrapper)
|
2013-01-17 09:30:37 -08:00
|
|
|
{
|
2014-01-29 22:34:25 -08:00
|
|
|
return !!AsWindow(cx, wrapper);
|
2012-08-27 06:15:20 -07:00
|
|
|
}
|
|
|
|
|
2012-10-05 09:59:23 -07:00
|
|
|
void
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNativeXrayTraits::preserveWrapper(JSObject* target)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNative* wn = XPCWrappedNative::Get(target);
|
2012-10-05 09:59:23 -07:00
|
|
|
nsRefPtr<nsXPCClassInfo> ci;
|
|
|
|
CallQueryInterface(wn->Native(), getter_AddRefs(ci));
|
|
|
|
if (ci)
|
|
|
|
ci->PreserveWrapper(wn->Native());
|
|
|
|
}
|
|
|
|
|
2014-09-15 07:45:38 -07:00
|
|
|
static bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayToString(JSContext* cx, unsigned argc, JS::Value* vp);
|
2014-09-15 07:45:38 -07:00
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNativeXrayTraits::resolveNativeProperty(JSContext* cx, HandleObject wrapper,
|
2013-03-27 04:33:30 -07:00
|
|
|
HandleObject holder, HandleId id,
|
2014-04-25 14:11:02 -07:00
|
|
|
MutableHandle<JSPropertyDescriptor> desc)
|
2010-07-02 14:09:48 -07:00
|
|
|
{
|
2012-08-27 06:15:20 -07:00
|
|
|
MOZ_ASSERT(js::GetObjectJSClass(holder) == &HolderClass);
|
|
|
|
|
2013-10-10 13:35:53 -07:00
|
|
|
desc.object().set(nullptr);
|
2010-07-02 14:09:48 -07:00
|
|
|
|
|
|
|
// This will do verification and the method lookup for us.
|
2013-04-20 01:52:56 -07:00
|
|
|
RootedObject target(cx, getTargetObject(wrapper));
|
2015-05-13 14:07:34 -07:00
|
|
|
XPCCallContext ccx(JS_CALLER, cx, target, nullptr, id);
|
2010-07-02 14:09:48 -07:00
|
|
|
|
2014-08-02 04:54:58 -07:00
|
|
|
// There are no native numeric (or symbol-keyed) properties, so we can
|
2014-09-15 05:13:02 -07:00
|
|
|
// shortcut here. We will not find the property.
|
|
|
|
if (!JSID_IS_STRING(id))
|
|
|
|
return true;
|
2014-01-16 15:45:40 -08:00
|
|
|
|
|
|
|
// The |controllers| property is accessible as a [ChromeOnly] property on
|
|
|
|
// Window.WebIDL, and [noscript] in XPIDL. Chrome needs to see this over
|
|
|
|
// Xray, so we need to special-case it until we move |Window| to WebIDL.
|
2015-03-28 15:22:11 -07:00
|
|
|
nsGlobalWindow* win = nullptr;
|
2014-01-16 15:45:40 -08:00
|
|
|
if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONTROLLERS) &&
|
|
|
|
AccessCheck::isChrome(wrapper) &&
|
2014-01-29 22:34:25 -08:00
|
|
|
(win = AsWindow(cx, wrapper)))
|
2014-01-16 15:45:40 -08:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIControllers> c;
|
|
|
|
nsresult rv = win->GetControllers(getter_AddRefs(c));
|
|
|
|
if (NS_SUCCEEDED(rv) && c) {
|
|
|
|
rv = nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, CurrentGlobalOrNull(cx),
|
|
|
|
c, nullptr, nullptr, true,
|
2014-01-16 16:11:25 -08:00
|
|
|
desc.value());
|
2014-01-16 15:45:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (NS_FAILED(rv) || !c) {
|
|
|
|
JS_ReportError(cx, "Failed to invoke GetControllers via Xrays");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
desc.object().set(wrapper);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-04 11:11:05 -07:00
|
|
|
// The |realFrameElement| property is accessible as a [ChromeOnly] property
|
|
|
|
// on Window.WebIDL, and [noscript] in XPIDL. Chrome needs to see this over
|
|
|
|
// Xray, so we need to special-case it until we move |Window| to WebIDL.
|
|
|
|
if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_REALFRAMEELEMENT) &&
|
|
|
|
AccessCheck::isChrome(wrapper) &&
|
|
|
|
(win = AsWindow(cx, wrapper)))
|
|
|
|
{
|
|
|
|
ErrorResult rv;
|
|
|
|
Element* f = win->GetRealFrameElement(rv);
|
|
|
|
if (!f) {
|
|
|
|
desc.object().set(nullptr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-11-26 11:25:20 -08:00
|
|
|
if (!GetOrCreateDOMReflector(cx, f, desc.value())) {
|
2014-06-04 11:11:05 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
desc.object().set(wrapper);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCNativeInterface* iface;
|
|
|
|
XPCNativeMember* member;
|
|
|
|
XPCWrappedNative* wn = getWN(wrapper);
|
2013-04-07 01:18:58 -07:00
|
|
|
|
|
|
|
if (ccx.GetWrapper() != wn || !wn->IsValid()) {
|
|
|
|
return true;
|
2010-07-02 14:09:48 -07:00
|
|
|
}
|
|
|
|
|
2014-09-15 07:45:38 -07:00
|
|
|
if (!(iface = ccx.GetInterface()) || !(member = ccx.GetMember())) {
|
|
|
|
if (id != nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING))
|
|
|
|
return true;
|
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
JSFunction* toString = JS_NewFunction(cx, XrayToString, 0, 0, "toString");
|
2014-09-15 07:45:38 -07:00
|
|
|
if (!toString)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
FillPropertyDescriptor(desc, wrapper, 0,
|
|
|
|
ObjectValue(*JS_GetFunctionObject(toString)));
|
|
|
|
|
2015-01-30 09:37:07 -08:00
|
|
|
return JS_DefinePropertyById(cx, holder, id, desc) &&
|
2015-06-02 14:27:39 -07:00
|
|
|
JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
|
2014-09-15 07:45:38 -07:00
|
|
|
}
|
2014-09-15 05:13:02 -07:00
|
|
|
|
2013-04-30 10:29:40 -07:00
|
|
|
desc.object().set(holder);
|
|
|
|
desc.setAttributes(JSPROP_ENUMERATE);
|
2013-10-10 13:35:53 -07:00
|
|
|
desc.setGetter(nullptr);
|
|
|
|
desc.setSetter(nullptr);
|
2015-06-29 18:36:44 -07:00
|
|
|
desc.value().setUndefined();
|
2010-07-02 14:09:48 -07:00
|
|
|
|
2015-06-29 18:36:44 -07:00
|
|
|
RootedValue fval(cx, JS::UndefinedValue());
|
2010-07-02 14:09:48 -07:00
|
|
|
if (member->IsConstant()) {
|
2013-04-30 10:29:40 -07:00
|
|
|
if (!member->GetConstantValue(ccx, iface, desc.value().address())) {
|
2010-07-02 14:09:48 -07:00
|
|
|
JS_ReportError(cx, "Failed to convert constant native property to JS value");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (member->IsAttribute()) {
|
|
|
|
// This is a getter/setter. Clone a function for it.
|
2013-03-27 04:33:30 -07:00
|
|
|
if (!member->NewFunctionObject(ccx, iface, wrapper, fval.address())) {
|
2010-07-02 14:09:48 -07:00
|
|
|
JS_ReportError(cx, "Failed to clone function object for native getter/setter");
|
|
|
|
return false;
|
|
|
|
}
|
2010-09-20 14:48:01 -07:00
|
|
|
|
2013-04-30 10:29:40 -07:00
|
|
|
unsigned attrs = desc.attributes();
|
|
|
|
attrs |= JSPROP_GETTER;
|
2010-09-20 14:48:01 -07:00
|
|
|
if (member->IsWritableAttribute())
|
2013-04-30 10:29:40 -07:00
|
|
|
attrs |= JSPROP_SETTER;
|
2010-07-02 14:09:48 -07:00
|
|
|
|
|
|
|
// Make the property shared on the holder so no slot is allocated
|
|
|
|
// for it. This avoids keeping garbage alive through that slot.
|
2013-04-30 10:29:40 -07:00
|
|
|
attrs |= JSPROP_SHARED;
|
|
|
|
desc.setAttributes(attrs);
|
2010-07-02 14:09:48 -07:00
|
|
|
} else {
|
|
|
|
// This is a method. Clone a function for it.
|
2013-04-30 10:29:40 -07:00
|
|
|
if (!member->NewFunctionObject(ccx, iface, wrapper, desc.value().address())) {
|
2010-07-02 14:09:48 -07:00
|
|
|
JS_ReportError(cx, "Failed to clone function object for native function");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Without a wrapper the function would live on the prototype. Since we
|
|
|
|
// don't have one, we have to avoid calling the scriptable helper's
|
2014-11-22 10:23:39 -08:00
|
|
|
// GetProperty method for this property, so null out the getter and
|
2010-07-02 14:09:48 -07:00
|
|
|
// setter here explicitly.
|
2014-11-22 10:23:39 -08:00
|
|
|
desc.setGetter(nullptr);
|
|
|
|
desc.setSetter(nullptr);
|
2010-07-02 14:09:48 -07:00
|
|
|
}
|
|
|
|
|
2013-10-26 09:19:05 -07:00
|
|
|
if (!JS_WrapValue(cx, desc.value()) || !JS_WrapValue(cx, &fval))
|
2010-09-20 14:48:01 -07:00
|
|
|
return false;
|
|
|
|
|
2013-04-30 10:29:40 -07:00
|
|
|
if (desc.hasGetterObject())
|
|
|
|
desc.setGetterObject(&fval.toObject());
|
|
|
|
if (desc.hasSetterObject())
|
|
|
|
desc.setSetterObject(&fval.toObject());
|
2010-09-20 14:48:01 -07:00
|
|
|
|
2015-01-30 09:37:07 -08:00
|
|
|
return JS_DefinePropertyById(cx, holder, id, desc);
|
2010-07-02 14:09:48 -07:00
|
|
|
}
|
|
|
|
|
2013-08-08 15:53:04 -07:00
|
|
|
static bool
|
2015-03-28 15:22:11 -07:00
|
|
|
wrappedJSObject_getter(JSContext* cx, unsigned argc, Value* vp)
|
2010-07-02 14:09:48 -07:00
|
|
|
{
|
2014-10-23 12:21:46 -07:00
|
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
|
if (!args.thisv().isObject()) {
|
|
|
|
JS_ReportError(cx, "This value not an object");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
RootedObject wrapper(cx, &args.thisv().toObject());
|
|
|
|
if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper) ||
|
2015-07-14 18:54:44 -07:00
|
|
|
!WrapperFactory::AllowWaiver(wrapper)) {
|
2011-09-21 18:30:27 -07:00
|
|
|
JS_ReportError(cx, "Unexpected object");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-10-23 12:21:46 -07:00
|
|
|
args.rval().setObject(*wrapper);
|
2010-10-10 15:47:22 -07:00
|
|
|
|
2014-10-23 12:21:46 -07:00
|
|
|
return WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
|
2010-07-02 14:09:48 -07:00
|
|
|
}
|
|
|
|
|
2012-10-05 09:59:23 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
|
2013-03-27 04:33:30 -07:00
|
|
|
HandleObject wrapper, HandleObject holder, HandleId id,
|
2014-04-25 14:11:02 -07:00
|
|
|
MutableHandle<JSPropertyDescriptor> desc)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
2013-10-10 13:35:53 -07:00
|
|
|
desc.object().set(nullptr);
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject target(cx, getTargetObject(wrapper));
|
2014-10-21 02:59:30 -07:00
|
|
|
RootedObject expando(cx);
|
|
|
|
if (!getExpandoObject(cx, target, wrapper, &expando))
|
|
|
|
return false;
|
2012-10-05 09:59:23 -07:00
|
|
|
|
|
|
|
// Check for expando properties first. Note that the expando object lives
|
|
|
|
// in the target compartment.
|
2013-11-22 10:55:42 -08:00
|
|
|
bool found = false;
|
2012-10-05 09:59:23 -07:00
|
|
|
if (expando) {
|
|
|
|
JSAutoCompartment ac(cx, expando);
|
2015-06-02 14:27:39 -07:00
|
|
|
if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc))
|
2012-10-05 09:59:23 -07:00
|
|
|
return false;
|
2013-11-22 10:55:42 -08:00
|
|
|
found = !!desc.object();
|
2012-10-05 09:59:23 -07:00
|
|
|
}
|
2013-11-22 10:55:42 -08:00
|
|
|
|
2013-11-22 10:55:43 -08:00
|
|
|
// Next, check for ES builtins.
|
2013-11-22 10:55:42 -08:00
|
|
|
if (!found && JS_IsGlobalObject(target)) {
|
|
|
|
JSProtoKey key = JS_IdToProtoKey(cx, id);
|
|
|
|
JSAutoCompartment ac(cx, target);
|
|
|
|
if (key != JSProto_Null) {
|
|
|
|
MOZ_ASSERT(key < JSProto_LIMIT);
|
|
|
|
RootedObject constructor(cx);
|
2014-01-30 07:45:16 -08:00
|
|
|
if (!JS_GetClassObject(cx, key, &constructor))
|
2013-11-22 10:55:42 -08:00
|
|
|
return false;
|
|
|
|
MOZ_ASSERT(constructor);
|
|
|
|
desc.value().set(ObjectValue(*constructor));
|
|
|
|
found = true;
|
2013-11-22 10:55:43 -08:00
|
|
|
} else if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_EVAL)) {
|
|
|
|
RootedObject eval(cx);
|
|
|
|
if (!js::GetOriginalEval(cx, target, &eval))
|
|
|
|
return false;
|
|
|
|
desc.value().set(ObjectValue(*eval));
|
|
|
|
found = true;
|
2013-11-22 10:55:42 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found) {
|
2013-08-12 04:09:14 -07:00
|
|
|
if (!JS_WrapPropertyDescriptor(cx, desc))
|
2012-10-05 09:59:23 -07:00
|
|
|
return false;
|
|
|
|
// Pretend the property lives on the wrapper.
|
2013-04-30 10:29:40 -07:00
|
|
|
desc.object().set(wrapper);
|
2012-10-05 09:59:23 -07:00
|
|
|
return true;
|
|
|
|
}
|
2014-02-21 16:03:08 -08:00
|
|
|
|
|
|
|
// Handle .wrappedJSObject for subsuming callers. This should move once we
|
|
|
|
// sort out own-ness for the holder.
|
|
|
|
if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT) &&
|
2015-07-14 18:54:44 -07:00
|
|
|
WrapperFactory::AllowWaiver(wrapper))
|
2014-02-21 16:03:08 -08:00
|
|
|
{
|
|
|
|
if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found))
|
|
|
|
return false;
|
2014-04-30 02:10:33 -07:00
|
|
|
if (!found && !JS_DefinePropertyById(cx, holder, id, UndefinedHandleValue,
|
2014-10-29 12:06:31 -07:00
|
|
|
JSPROP_ENUMERATE | JSPROP_SHARED,
|
|
|
|
wrappedJSObject_getter)) {
|
2014-02-21 16:03:08 -08:00
|
|
|
return false;
|
|
|
|
}
|
2015-06-02 14:27:39 -07:00
|
|
|
if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
|
2014-02-21 16:03:08 -08:00
|
|
|
return false;
|
|
|
|
desc.object().set(wrapper);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-10-05 09:59:24 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
|
2013-03-27 04:33:30 -07:00
|
|
|
HandleObject wrapper, HandleObject holder,
|
2014-04-25 14:11:02 -07:00
|
|
|
HandleId id,
|
|
|
|
MutableHandle<JSPropertyDescriptor> desc)
|
2012-10-05 09:59:24 -07:00
|
|
|
{
|
|
|
|
// Call the common code.
|
|
|
|
bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder,
|
2014-04-25 14:11:02 -07:00
|
|
|
id, desc);
|
2013-04-30 10:29:40 -07:00
|
|
|
if (!ok || desc.object())
|
2012-10-05 09:59:24 -07:00
|
|
|
return ok;
|
2012-10-05 09:59:23 -07:00
|
|
|
|
2013-01-17 09:30:37 -08:00
|
|
|
// Check for indexed access on a window.
|
|
|
|
int32_t index = GetArrayIndexFromId(cx, id);
|
|
|
|
if (IsArrayIndex(index)) {
|
2014-01-29 22:34:25 -08:00
|
|
|
nsGlobalWindow* win = AsWindow(cx, wrapper);
|
2013-01-17 09:30:37 -08:00
|
|
|
// Note: As() unwraps outer windows to get to the inner window.
|
|
|
|
if (win) {
|
2015-07-16 22:10:35 -07:00
|
|
|
nsCOMPtr<nsIDOMWindow> subframe = win->IndexedGetter(index);
|
2013-01-17 09:30:37 -08:00
|
|
|
if (subframe) {
|
|
|
|
nsGlobalWindow* global = static_cast<nsGlobalWindow*>(subframe.get());
|
|
|
|
global->EnsureInnerWindow();
|
|
|
|
JSObject* obj = global->FastGetGlobalJSObject();
|
|
|
|
if (MOZ_UNLIKELY(!obj)) {
|
|
|
|
// It's gone?
|
|
|
|
return xpc::Throw(cx, NS_ERROR_FAILURE);
|
|
|
|
}
|
2013-04-30 10:29:40 -07:00
|
|
|
desc.value().setObject(*obj);
|
2014-01-29 22:34:25 -08:00
|
|
|
FillPropertyDescriptor(desc, wrapper, true);
|
2013-08-12 04:09:14 -07:00
|
|
|
return JS_WrapPropertyDescriptor(cx, desc);
|
2013-01-17 09:30:37 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-18 06:47:09 -07:00
|
|
|
// Xray wrappers don't use the regular wrapper hierarchy, so we should be
|
|
|
|
// in the wrapper's compartment here, not the wrappee.
|
|
|
|
MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
|
2010-09-20 14:48:01 -07:00
|
|
|
|
2015-06-02 14:27:39 -07:00
|
|
|
return JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
|
2010-10-10 15:49:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNativeXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
|
2015-02-17 09:58:43 -08:00
|
|
|
Handle<JSPropertyDescriptor> desc,
|
2015-01-30 09:37:07 -08:00
|
|
|
Handle<JSPropertyDescriptor> existingDesc,
|
2015-03-28 15:22:11 -07:00
|
|
|
JS::ObjectOpResult& result, bool* defined)
|
2010-10-10 15:49:13 -07:00
|
|
|
{
|
2012-10-05 09:59:24 -07:00
|
|
|
*defined = false;
|
2014-04-30 02:10:33 -07:00
|
|
|
RootedObject holder(cx, singleton.ensureHolder(cx, wrapper));
|
2013-01-17 09:30:37 -08:00
|
|
|
|
|
|
|
// Check for an indexed property on a Window. If that's happening, do
|
|
|
|
// nothing but claim we defined it so it won't get added as an expando.
|
|
|
|
int32_t index = GetArrayIndexFromId(cx, id);
|
2014-01-29 22:34:25 -08:00
|
|
|
if (IsArrayIndex(index) && IsWindow(cx, wrapper)) {
|
2013-01-17 09:30:37 -08:00
|
|
|
*defined = true;
|
2015-01-30 09:37:07 -08:00
|
|
|
return result.succeed();
|
2013-01-17 09:30:37 -08:00
|
|
|
}
|
|
|
|
|
2010-11-10 14:08:44 -08:00
|
|
|
return true;
|
2010-07-02 14:09:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNativeXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags,
|
|
|
|
AutoIdVector& props)
|
2010-07-02 14:09:48 -07:00
|
|
|
{
|
2010-11-10 14:08:44 -08:00
|
|
|
// Force all native properties to be materialized onto the wrapped native.
|
2013-03-27 04:33:30 -07:00
|
|
|
AutoIdVector wnProps(cx);
|
2010-11-10 14:08:44 -08:00
|
|
|
{
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject target(cx, singleton.getTargetObject(wrapper));
|
2012-10-05 09:59:23 -07:00
|
|
|
JSAutoCompartment ac(cx, target);
|
2014-09-26 17:39:35 -07:00
|
|
|
if (!js::GetPropertyKeys(cx, target, flags, &wnProps))
|
2010-11-10 14:08:44 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-07-30 12:23:02 -07:00
|
|
|
// Go through the properties we found on the underlying object and see if
|
|
|
|
// they appear on the XrayWrapper. If it throws (which may happen if the
|
|
|
|
// wrapper is a SecurityWrapper), just clear the exception and move on.
|
|
|
|
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
2014-06-17 10:16:08 -07:00
|
|
|
if (!props.reserve(wnProps.length()))
|
|
|
|
return false;
|
2010-11-10 14:08:44 -08:00
|
|
|
for (size_t n = 0; n < wnProps.length(); ++n) {
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedId id(cx, wnProps[n]);
|
2013-08-08 15:53:04 -07:00
|
|
|
bool hasProp;
|
2014-07-30 12:23:02 -07:00
|
|
|
if (JS_HasPropertyById(cx, wrapper, id, &hasProp) && hasProp)
|
2014-06-17 10:16:08 -07:00
|
|
|
props.infallibleAppend(id);
|
2014-07-30 12:23:02 -07:00
|
|
|
JS_ClearPendingException(cx);
|
2010-11-10 14:08:44 -08:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
JSObject*
|
|
|
|
XPCWrappedNativeXrayTraits::createHolder(JSContext* cx, JSObject* wrapper)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
2015-05-13 14:07:34 -07:00
|
|
|
return JS_NewObjectWithGivenProto(cx, &HolderClass, nullptr);
|
2012-10-05 09:59:23 -07:00
|
|
|
}
|
|
|
|
|
2012-10-05 09:59:22 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNativeXrayTraits::call(JSContext* cx, HandleObject wrapper,
|
|
|
|
const JS::CallArgs& args,
|
2014-06-27 04:44:06 -07:00
|
|
|
const js::Wrapper& baseInstance)
|
2012-10-05 09:59:22 -07:00
|
|
|
{
|
2014-09-15 05:13:02 -07:00
|
|
|
// Run the call hook of the wrapped native.
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNative* wn = getWN(wrapper);
|
2012-10-05 09:59:22 -07:00
|
|
|
if (NATIVE_HAS_FLAG(wn, WantCall)) {
|
2015-05-13 14:07:34 -07:00
|
|
|
XPCCallContext ccx(JS_CALLER, cx, wrapper, nullptr, JSID_VOIDHANDLE, args.length(),
|
2013-04-20 01:52:56 -07:00
|
|
|
args.array(), args.rval().address());
|
2012-10-05 09:59:22 -07:00
|
|
|
if (!ccx.IsValid())
|
|
|
|
return false;
|
|
|
|
bool ok = true;
|
2013-04-04 00:02:24 -07:00
|
|
|
nsresult rv = wn->GetScriptableInfo()->GetCallback()->Call(
|
2013-05-04 10:25:15 -07:00
|
|
|
wn, cx, wrapper, args, &ok);
|
2012-10-05 09:59:22 -07:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
if (ok)
|
|
|
|
XPCThrower::Throw(rv, cx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNativeXrayTraits::construct(JSContext* cx, HandleObject wrapper,
|
|
|
|
const JS::CallArgs& args,
|
2014-06-27 04:44:06 -07:00
|
|
|
const js::Wrapper& baseInstance)
|
2012-10-05 09:59:22 -07:00
|
|
|
{
|
2014-09-15 05:13:02 -07:00
|
|
|
// Run the construct hook of the wrapped native.
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNative* wn = getWN(wrapper);
|
2012-10-05 09:59:22 -07:00
|
|
|
if (NATIVE_HAS_FLAG(wn, WantConstruct)) {
|
2015-05-13 14:07:34 -07:00
|
|
|
XPCCallContext ccx(JS_CALLER, cx, wrapper, nullptr, JSID_VOIDHANDLE, args.length(),
|
2013-04-20 01:52:56 -07:00
|
|
|
args.array(), args.rval().address());
|
2012-10-05 09:59:22 -07:00
|
|
|
if (!ccx.IsValid())
|
|
|
|
return false;
|
|
|
|
bool ok = true;
|
2013-04-04 00:02:24 -07:00
|
|
|
nsresult rv = wn->GetScriptableInfo()->GetCallback()->Construct(
|
2013-05-04 10:25:15 -07:00
|
|
|
wn, cx, wrapper, args, &ok);
|
2012-10-05 09:59:22 -07:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
if (ok)
|
|
|
|
XPCThrower::Throw(rv, cx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-03-30 21:42:20 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
DOMXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, HandleObject wrapper,
|
2013-03-27 04:33:30 -07:00
|
|
|
HandleObject holder, HandleId id,
|
2014-04-25 14:11:02 -07:00
|
|
|
MutableHandle<JSPropertyDescriptor> desc)
|
2012-03-30 21:42:20 -07:00
|
|
|
{
|
2012-10-05 09:59:23 -07:00
|
|
|
// Call the common code.
|
2014-04-25 14:11:02 -07:00
|
|
|
bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, id, desc);
|
2013-04-30 10:29:40 -07:00
|
|
|
if (!ok || desc.object())
|
2012-10-05 09:59:23 -07:00
|
|
|
return ok;
|
|
|
|
|
2014-01-29 22:34:25 -08:00
|
|
|
// Check for indexed access on a window.
|
|
|
|
int32_t index = GetArrayIndexFromId(cx, id);
|
|
|
|
if (IsArrayIndex(index)) {
|
|
|
|
nsGlobalWindow* win = AsWindow(cx, wrapper);
|
|
|
|
// Note: As() unwraps outer windows to get to the inner window.
|
|
|
|
if (win) {
|
2015-07-16 22:10:35 -07:00
|
|
|
nsCOMPtr<nsIDOMWindow> subframe = win->IndexedGetter(index);
|
2014-01-29 22:34:25 -08:00
|
|
|
if (subframe) {
|
|
|
|
nsGlobalWindow* global = static_cast<nsGlobalWindow*>(subframe.get());
|
|
|
|
global->EnsureInnerWindow();
|
|
|
|
JSObject* obj = global->FastGetGlobalJSObject();
|
|
|
|
if (MOZ_UNLIKELY(!obj)) {
|
|
|
|
// It's gone?
|
|
|
|
return xpc::Throw(cx, NS_ERROR_FAILURE);
|
|
|
|
}
|
|
|
|
desc.value().setObject(*obj);
|
|
|
|
FillPropertyDescriptor(desc, wrapper, true);
|
|
|
|
return JS_WrapPropertyDescriptor(cx, desc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-02 14:27:39 -07:00
|
|
|
if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
|
2014-08-22 02:25:07 -07:00
|
|
|
return false;
|
|
|
|
if (desc.object()) {
|
|
|
|
desc.object().set(wrapper);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-04-24 06:22:21 -07:00
|
|
|
RootedObject obj(cx, getTargetObject(wrapper));
|
2014-08-22 02:25:07 -07:00
|
|
|
bool cacheOnHolder;
|
|
|
|
if (!XrayResolveOwnProperty(cx, wrapper, obj, id, desc, cacheOnHolder))
|
2012-10-09 11:50:27 -07:00
|
|
|
return false;
|
2012-06-06 12:52:26 -07:00
|
|
|
|
2013-08-14 00:00:34 -07:00
|
|
|
MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?");
|
2012-06-06 12:52:26 -07:00
|
|
|
|
2014-08-22 02:25:07 -07:00
|
|
|
if (!desc.object() || !cacheOnHolder)
|
|
|
|
return true;
|
|
|
|
|
2015-01-30 09:37:07 -08:00
|
|
|
return JS_DefinePropertyById(cx, holder, id, desc) &&
|
2015-06-02 14:27:39 -07:00
|
|
|
JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
|
2012-03-30 21:42:20 -07:00
|
|
|
}
|
|
|
|
|
2013-01-14 02:29:48 -08:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
DOMXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
|
2015-02-17 09:58:43 -08:00
|
|
|
Handle<JSPropertyDescriptor> desc,
|
2015-01-30 09:37:07 -08:00
|
|
|
Handle<JSPropertyDescriptor> existingDesc,
|
2015-03-28 15:22:11 -07:00
|
|
|
JS::ObjectOpResult& result, bool* defined)
|
2013-01-14 02:29:48 -08:00
|
|
|
{
|
2014-01-29 22:34:25 -08:00
|
|
|
// Check for an indexed property on a Window. If that's happening, do
|
|
|
|
// nothing but claim we defined it so it won't get added as an expando.
|
2014-02-15 13:12:35 -08:00
|
|
|
if (IsWindow(cx, wrapper)) {
|
|
|
|
int32_t index = GetArrayIndexFromId(cx, id);
|
|
|
|
if (IsArrayIndex(index)) {
|
|
|
|
*defined = true;
|
2015-01-30 09:37:07 -08:00
|
|
|
return result.succeed();
|
2014-02-15 13:12:35 -08:00
|
|
|
}
|
2014-01-29 22:34:25 -08:00
|
|
|
}
|
|
|
|
|
2013-05-29 02:38:57 -07:00
|
|
|
JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper));
|
2015-01-30 09:37:07 -08:00
|
|
|
return XrayDefineProperty(cx, wrapper, obj, id, desc, result, defined);
|
2013-01-14 02:29:48 -08:00
|
|
|
}
|
|
|
|
|
2012-03-30 21:42:20 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
DOMXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags,
|
|
|
|
AutoIdVector& props)
|
2012-03-30 21:42:20 -07:00
|
|
|
{
|
2013-05-03 16:29:08 -07:00
|
|
|
JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper));
|
2014-09-26 13:16:36 -07:00
|
|
|
return XrayOwnPropertyKeys(cx, wrapper, obj, flags, props);
|
2012-10-09 11:50:27 -07:00
|
|
|
}
|
2012-03-30 21:42:20 -07:00
|
|
|
|
2012-10-09 11:50:27 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
DOMXrayTraits::call(JSContext* cx, HandleObject wrapper,
|
|
|
|
const JS::CallArgs& args, const js::Wrapper& baseInstance)
|
2012-10-09 11:50:27 -07:00
|
|
|
{
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject obj(cx, getTargetObject(wrapper));
|
2013-09-11 05:49:05 -07:00
|
|
|
const js::Class* clasp = js::GetObjectClass(obj);
|
2013-04-25 16:03:05 -07:00
|
|
|
// What we have is either a WebIDL interface object, a WebIDL prototype
|
|
|
|
// object, or a WebIDL instance object. WebIDL prototype objects never have
|
|
|
|
// a clasp->call. WebIDL interface objects we want to invoke on the xray
|
|
|
|
// compartment. WebIDL instance objects either don't have a clasp->call or
|
|
|
|
// are using "legacycaller", which basically means plug-ins. We want to
|
|
|
|
// call those on the content compartment.
|
|
|
|
if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) {
|
|
|
|
if (!clasp->call) {
|
2014-01-14 18:46:44 -08:00
|
|
|
RootedValue v(cx, ObjectValue(*wrapper));
|
2015-02-27 07:08:15 -08:00
|
|
|
js::ReportIsNotFunction(cx, v);
|
2012-03-30 21:42:20 -07:00
|
|
|
return false;
|
|
|
|
}
|
2013-04-25 16:03:05 -07:00
|
|
|
// call it on the Xray compartment
|
|
|
|
if (!clasp->call(cx, args.length(), args.base()))
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
// This is only reached for WebIDL instance objects, and in practice
|
|
|
|
// only for plugins. Just call them on the content compartment.
|
|
|
|
if (!baseInstance.call(cx, wrapper, args))
|
2013-04-04 00:02:24 -07:00
|
|
|
return false;
|
2012-10-09 11:50:27 -07:00
|
|
|
}
|
2013-10-26 09:19:05 -07:00
|
|
|
return JS_WrapValue(cx, args.rval());
|
2012-10-09 11:50:27 -07:00
|
|
|
}
|
2012-03-30 21:42:20 -07:00
|
|
|
|
2012-10-09 11:50:27 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
DOMXrayTraits::construct(JSContext* cx, HandleObject wrapper,
|
|
|
|
const JS::CallArgs& args, const js::Wrapper& baseInstance)
|
2012-10-09 11:50:27 -07:00
|
|
|
{
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject obj(cx, getTargetObject(wrapper));
|
2012-10-09 11:50:27 -07:00
|
|
|
MOZ_ASSERT(mozilla::dom::HasConstructor(obj));
|
2013-09-11 05:49:05 -07:00
|
|
|
const js::Class* clasp = js::GetObjectClass(obj);
|
2013-04-25 16:03:05 -07:00
|
|
|
// See comments in DOMXrayTraits::call() explaining what's going on here.
|
|
|
|
if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) {
|
|
|
|
if (!clasp->construct) {
|
2014-01-14 18:46:44 -08:00
|
|
|
RootedValue v(cx, ObjectValue(*wrapper));
|
2015-02-27 07:08:15 -08:00
|
|
|
js::ReportIsNotFunction(cx, v);
|
2013-04-25 16:03:05 -07:00
|
|
|
return false;
|
2012-10-09 11:50:27 -07:00
|
|
|
}
|
2013-04-25 16:03:05 -07:00
|
|
|
if (!clasp->construct(cx, args.length(), args.base()))
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
if (!baseInstance.construct(cx, wrapper, args))
|
|
|
|
return false;
|
2012-10-09 11:50:27 -07:00
|
|
|
}
|
2013-10-26 09:19:05 -07:00
|
|
|
if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval()))
|
2012-10-09 11:50:27 -07:00
|
|
|
return false;
|
2012-03-30 21:42:20 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-15 07:51:40 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
DOMXrayTraits::getPrototype(JSContext* cx, JS::HandleObject wrapper,
|
2015-02-05 14:36:50 -08:00
|
|
|
JS::HandleObject target,
|
|
|
|
JS::MutableHandleObject protop)
|
2014-09-15 07:51:40 -07:00
|
|
|
{
|
|
|
|
return mozilla::dom::XrayGetNativeProto(cx, target, protop);
|
|
|
|
}
|
|
|
|
|
2012-10-05 09:59:23 -07:00
|
|
|
void
|
2015-03-28 15:22:11 -07:00
|
|
|
DOMXrayTraits::preserveWrapper(JSObject* target)
|
2012-10-05 09:59:23 -07:00
|
|
|
{
|
2015-03-28 15:22:11 -07:00
|
|
|
nsISupports* identity = mozilla::dom::UnwrapDOMObjectToISupports(target);
|
2013-05-31 23:56:02 -07:00
|
|
|
if (!identity)
|
2012-10-05 09:59:23 -07:00
|
|
|
return;
|
|
|
|
nsWrapperCache* cache = nullptr;
|
|
|
|
CallQueryInterface(identity, &cache);
|
|
|
|
if (cache)
|
2013-06-23 00:15:42 -07:00
|
|
|
cache->PreserveWrapper(identity);
|
2012-10-05 09:59:23 -07:00
|
|
|
}
|
|
|
|
|
2012-03-30 21:42:20 -07:00
|
|
|
JSObject*
|
2015-03-28 15:22:11 -07:00
|
|
|
DOMXrayTraits::createHolder(JSContext* cx, JSObject* wrapper)
|
2012-03-30 21:42:20 -07:00
|
|
|
{
|
2015-05-13 14:07:34 -07:00
|
|
|
return JS_NewObjectWithGivenProto(cx, nullptr, nullptr);
|
2012-03-30 21:42:20 -07:00
|
|
|
}
|
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
namespace XrayUtils {
|
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
JSObject*
|
|
|
|
GetNativePropertiesObject(JSContext* cx, JSObject* wrapper)
|
2011-01-25 15:06:45 -08:00
|
|
|
{
|
2013-08-14 00:00:34 -07:00
|
|
|
MOZ_ASSERT(js::IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper),
|
|
|
|
"bad object passed in");
|
2011-01-25 15:06:45 -08:00
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
JSObject* holder = GetHolder(wrapper);
|
2013-08-14 00:00:34 -07:00
|
|
|
MOZ_ASSERT(holder, "uninitialized wrapper being used?");
|
2012-03-27 16:31:37 -07:00
|
|
|
return holder;
|
|
|
|
}
|
2011-01-25 15:06:45 -08:00
|
|
|
|
2013-04-23 09:50:17 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
HasNativeProperty(JSContext* cx, HandleObject wrapper, HandleId id, bool* hasProp)
|
2013-04-23 09:50:17 -07:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayTraits* traits = GetXrayTraits(wrapper);
|
2013-04-23 09:50:17 -07:00
|
|
|
MOZ_ASSERT(traits);
|
|
|
|
RootedObject holder(cx, traits->ensureHolder(cx, wrapper));
|
|
|
|
NS_ENSURE_TRUE(holder, false);
|
|
|
|
*hasProp = false;
|
2013-07-25 22:52:59 -07:00
|
|
|
Rooted<JSPropertyDescriptor> desc(cx);
|
2015-03-28 15:22:11 -07:00
|
|
|
const Wrapper* handler = Wrapper::wrapperHandler(wrapper);
|
2013-04-23 09:50:17 -07:00
|
|
|
|
|
|
|
// Try resolveOwnProperty.
|
2014-04-25 14:11:02 -07:00
|
|
|
if (!traits->resolveOwnProperty(cx, *handler, wrapper, holder, id, &desc))
|
2013-04-23 09:50:17 -07:00
|
|
|
return false;
|
2013-04-26 10:50:18 -07:00
|
|
|
if (desc.object()) {
|
2013-04-23 09:50:17 -07:00
|
|
|
*hasProp = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try the holder.
|
2013-08-08 15:53:04 -07:00
|
|
|
bool found = false;
|
2013-04-23 09:50:17 -07:00
|
|
|
if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found))
|
|
|
|
return false;
|
|
|
|
if (found) {
|
|
|
|
*hasProp = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try resolveNativeProperty.
|
2014-04-25 14:11:02 -07:00
|
|
|
if (!traits->resolveNativeProperty(cx, wrapper, holder, id, &desc))
|
2013-04-23 09:50:17 -07:00
|
|
|
return false;
|
2013-04-26 10:50:18 -07:00
|
|
|
*hasProp = !!desc.object();
|
2013-04-23 09:50:17 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-03-22 19:43:03 -07:00
|
|
|
} // namespace XrayUtils
|
2011-01-25 15:06:45 -08:00
|
|
|
|
2013-08-02 00:41:57 -07:00
|
|
|
static bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayToString(JSContext* cx, unsigned argc, Value* vp)
|
2011-01-25 15:06:45 -08:00
|
|
|
{
|
2013-10-26 09:19:05 -07:00
|
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
|
|
|
|
|
if (!args.thisv().isObject()) {
|
|
|
|
JS_ReportError(cx, "XrayToString called on an incompatible object");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
RootedObject wrapper(cx, &args.thisv().toObject());
|
2012-04-27 03:47:00 -07:00
|
|
|
if (!wrapper)
|
|
|
|
return false;
|
2012-10-09 11:50:27 -07:00
|
|
|
if (IsWrapper(wrapper) &&
|
|
|
|
GetProxyHandler(wrapper) == &sandboxCallableProxyHandler) {
|
|
|
|
wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper);
|
|
|
|
}
|
2012-04-27 03:47:00 -07:00
|
|
|
if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
|
2012-03-27 16:31:37 -07:00
|
|
|
JS_ReportError(cx, "XrayToString called on an incompatible object");
|
|
|
|
return false;
|
|
|
|
}
|
2011-01-25 15:06:45 -08:00
|
|
|
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject obj(cx, XrayTraits::getTargetObject(wrapper));
|
2014-09-15 07:51:40 -07:00
|
|
|
if (GetXrayType(obj) != XrayForWrappedNative) {
|
2014-09-24 12:58:33 -07:00
|
|
|
JS_ReportError(cx, "XrayToString called on an incompatible object");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-07-07 13:11:24 -07:00
|
|
|
static const char start[] = "[object XrayWrapper ";
|
|
|
|
static const char end[] = "]";
|
2012-10-09 11:50:27 -07:00
|
|
|
nsAutoString result;
|
|
|
|
result.AppendASCII(start);
|
|
|
|
|
|
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
2015-03-28 15:22:11 -07:00
|
|
|
XPCWrappedNative* wn = XPCWrappedNativeXrayTraits::getWN(wrapper);
|
|
|
|
char* wrapperStr = wn->ToString();
|
2012-10-09 11:50:27 -07:00
|
|
|
if (!wrapperStr) {
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return false;
|
2011-01-25 15:06:45 -08:00
|
|
|
}
|
2012-10-09 11:50:27 -07:00
|
|
|
result.AppendASCII(wrapperStr);
|
|
|
|
JS_smprintf_free(wrapperStr);
|
2011-01-25 15:06:45 -08:00
|
|
|
|
2012-10-09 11:50:27 -07:00
|
|
|
result.AppendASCII(end);
|
2011-05-25 08:30:50 -07:00
|
|
|
|
2015-03-28 15:22:11 -07:00
|
|
|
JSString* str = JS_NewUCStringCopyN(cx, result.get(), result.Length());
|
2012-03-27 16:31:37 -07:00
|
|
|
if (!str)
|
|
|
|
return false;
|
2011-05-27 08:06:42 -07:00
|
|
|
|
2013-10-26 09:19:05 -07:00
|
|
|
args.rval().setString(str);
|
2012-03-27 16:31:37 -07:00
|
|
|
return true;
|
2011-05-27 08:06:42 -07:00
|
|
|
}
|
|
|
|
|
2013-03-22 19:43:03 -07:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::preventExtensions(JSContext* cx, HandleObject wrapper,
|
|
|
|
ObjectOpResult& result) const
|
2013-03-22 19:43:03 -07:00
|
|
|
{
|
|
|
|
// Xray wrappers are supposed to provide a clean view of the target
|
|
|
|
// reflector, hiding any modifications by script in the target scope. So
|
|
|
|
// even if that script freezes the reflector, we don't want to make that
|
|
|
|
// visible to the caller. DOM reflectors are always extensible by default,
|
2015-02-04 10:01:01 -08:00
|
|
|
// so we can just return failure here.
|
|
|
|
return result.failCantPreventExtensions();
|
2013-03-22 19:43:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::isExtensible(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
|
|
|
bool* extensible) const
|
2013-03-22 19:43:03 -07:00
|
|
|
{
|
|
|
|
// See above.
|
2014-10-21 11:40:04 -07:00
|
|
|
*extensible = true;
|
2014-10-19 01:18:43 -07:00
|
|
|
return true;
|
2013-03-22 19:43:03 -07:00
|
|
|
}
|
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
2011-05-25 08:30:50 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
|
2014-04-25 14:11:01 -07:00
|
|
|
JS::MutableHandle<JSPropertyDescriptor> desc)
|
2014-06-27 04:44:06 -07:00
|
|
|
const
|
2011-05-25 08:30:50 -07:00
|
|
|
{
|
2014-07-30 12:23:03 -07:00
|
|
|
assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET |
|
|
|
|
BaseProxyHandler::GET_PROPERTY_DESCRIPTOR);
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper));
|
2011-08-20 06:53:32 -07:00
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
if (!holder)
|
2011-05-27 08:06:42 -07:00
|
|
|
return false;
|
2012-03-27 16:31:37 -07:00
|
|
|
|
2013-04-23 09:50:17 -07:00
|
|
|
// Ordering is important here.
|
|
|
|
//
|
|
|
|
// We first need to call resolveOwnProperty, even before checking the holder,
|
|
|
|
// because there might be a new dynamic |own| property that appears and
|
|
|
|
// shadows a previously-resolved non-own property that we cached on the
|
|
|
|
// holder. This can happen with indexed properties on NodeLists, for example,
|
|
|
|
// which are |own| value props.
|
|
|
|
//
|
|
|
|
// resolveOwnProperty may or may not cache what it finds on the holder,
|
|
|
|
// depending on how ephemeral it decides the property is. XPCWN |own|
|
2014-11-07 16:07:12 -08:00
|
|
|
// properties generally end up on the holder via Resolve, whereas
|
2013-04-23 09:50:17 -07:00
|
|
|
// NodeList |own| properties don't get defined on the holder, since they're
|
|
|
|
// supposed to be dynamic. This means that we have to first check the result
|
|
|
|
// of resolveOwnProperty, and _then_, if that comes up blank, check the
|
|
|
|
// holder for any cached native properties.
|
|
|
|
//
|
|
|
|
// Finally, we call resolveNativeProperty, which checks non-own properties,
|
|
|
|
// and unconditionally caches what it finds on the holder.
|
|
|
|
|
|
|
|
// Check resolveOwnProperty.
|
2014-04-25 14:11:02 -07:00
|
|
|
if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc))
|
2012-03-27 16:31:37 -07:00
|
|
|
return false;
|
2013-04-23 09:50:17 -07:00
|
|
|
|
|
|
|
// Check the holder.
|
2015-06-02 14:27:39 -07:00
|
|
|
if (!desc.object() && !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
|
2013-04-23 09:50:17 -07:00
|
|
|
return false;
|
2013-04-30 10:29:40 -07:00
|
|
|
if (desc.object()) {
|
|
|
|
desc.object().set(wrapper);
|
2011-05-27 08:06:42 -07:00
|
|
|
return true;
|
2013-01-22 21:04:38 -08:00
|
|
|
}
|
2011-05-27 08:06:42 -07:00
|
|
|
|
2013-04-23 09:50:17 -07:00
|
|
|
// Nothing in the cache. Call through, and cache the result.
|
2014-04-25 14:11:02 -07:00
|
|
|
if (!Traits::singleton.resolveNativeProperty(cx, wrapper, holder, id, desc))
|
2013-04-23 09:50:17 -07:00
|
|
|
return false;
|
|
|
|
|
2013-03-14 22:38:26 -07:00
|
|
|
// We need to handle named access on the Window somewhere other than
|
|
|
|
// Traits::resolveOwnProperty, because per spec it happens on the Global
|
|
|
|
// Scope Polluter and thus the resulting properties are non-|own|. However,
|
2013-04-23 09:50:17 -07:00
|
|
|
// we're set up (above) to cache (on the holder) anything that comes out of
|
2013-03-14 22:38:26 -07:00
|
|
|
// resolveNativeProperty, which we don't want for something dynamic like
|
2013-04-23 09:50:17 -07:00
|
|
|
// named access. So we just handle it separately here.
|
2015-03-28 15:22:11 -07:00
|
|
|
nsGlobalWindow* win = nullptr;
|
2013-10-11 22:02:39 -07:00
|
|
|
if (!desc.object() &&
|
|
|
|
JSID_IS_STRING(id) &&
|
2014-01-29 22:34:25 -08:00
|
|
|
(win = AsWindow(cx, wrapper)))
|
2013-03-14 22:38:26 -07:00
|
|
|
{
|
2014-07-05 08:30:54 -07:00
|
|
|
nsAutoJSString name;
|
|
|
|
if (!name.init(cx, JSID_TO_STRING(id)))
|
|
|
|
return false;
|
2013-05-05 00:03:19 -07:00
|
|
|
nsCOMPtr<nsIDOMWindow> childDOMWin = win->GetChildWindow(name);
|
2013-03-14 22:38:26 -07:00
|
|
|
if (childDOMWin) {
|
2015-03-28 15:22:11 -07:00
|
|
|
nsGlobalWindow* cwin = static_cast<nsGlobalWindow*>(childDOMWin.get());
|
|
|
|
JSObject* childObj = cwin->FastGetGlobalJSObject();
|
2013-03-14 22:38:26 -07:00
|
|
|
if (MOZ_UNLIKELY(!childObj))
|
|
|
|
return xpc::Throw(cx, NS_ERROR_FAILURE);
|
2014-01-29 22:34:25 -08:00
|
|
|
FillPropertyDescriptor(desc, wrapper, ObjectValue(*childObj),
|
|
|
|
/* readOnly = */ true);
|
2013-08-12 04:09:14 -07:00
|
|
|
return JS_WrapPropertyDescriptor(cx, desc);
|
2013-03-14 22:38:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-08 06:24:21 -08:00
|
|
|
// If we still have nothing, we're done.
|
2013-04-30 10:29:40 -07:00
|
|
|
if (!desc.object())
|
2013-07-01 00:14:36 -07:00
|
|
|
return true;
|
2013-02-08 06:24:21 -08:00
|
|
|
|
2015-01-30 09:37:07 -08:00
|
|
|
if (!JS_DefinePropertyById(cx, holder, id, desc) ||
|
2015-06-02 14:27:39 -07:00
|
|
|
!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
|
2013-01-22 21:04:38 -08:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2013-04-30 10:29:40 -07:00
|
|
|
MOZ_ASSERT(desc.object());
|
|
|
|
desc.object().set(wrapper);
|
2013-01-22 21:04:38 -08:00
|
|
|
return true;
|
2011-05-25 08:30:50 -07:00
|
|
|
}
|
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
2011-05-25 08:30:50 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
|
2014-04-25 14:11:01 -07:00
|
|
|
JS::MutableHandle<JSPropertyDescriptor> desc)
|
2014-06-27 04:44:06 -07:00
|
|
|
const
|
2011-05-25 08:30:50 -07:00
|
|
|
{
|
2014-07-30 12:23:03 -07:00
|
|
|
assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET |
|
|
|
|
BaseProxyHandler::GET_PROPERTY_DESCRIPTOR);
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper));
|
2011-08-20 06:53:32 -07:00
|
|
|
|
2014-04-25 14:11:02 -07:00
|
|
|
if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc))
|
2011-08-20 06:53:32 -07:00
|
|
|
return false;
|
2013-04-30 10:29:40 -07:00
|
|
|
if (desc.object())
|
|
|
|
desc.object().set(wrapper);
|
2011-12-06 11:05:26 -08:00
|
|
|
return true;
|
2011-05-25 08:30:50 -07:00
|
|
|
}
|
|
|
|
|
2013-05-17 10:44:25 -07:00
|
|
|
// Consider what happens when chrome does |xray.expando = xray.wrappedJSObject|.
|
|
|
|
//
|
|
|
|
// Since the expando comes from the target compartment, wrapping it back into
|
|
|
|
// the target compartment to define it on the expando object ends up stripping
|
|
|
|
// off the Xray waiver that gives |xray| and |xray.wrappedJSObject| different
|
|
|
|
// identities. This is generally the right thing to do when wrapping across
|
|
|
|
// compartments, but is incorrect in the special case of the Xray expando
|
|
|
|
// object. Manually re-apply Xrays if necessary.
|
|
|
|
//
|
|
|
|
// NB: In order to satisfy the invariants of WaiveXray, we need to pass
|
|
|
|
// in an object sans security wrapper, which means we need to strip off any
|
|
|
|
// potential same-compartment security wrapper that may have been applied
|
|
|
|
// to the content object. This is ok, because the the expando object is only
|
|
|
|
// ever accessed by code across the compartment boundary.
|
|
|
|
static bool
|
2015-03-28 15:22:11 -07:00
|
|
|
RecreateLostWaivers(JSContext* cx, const JSPropertyDescriptor* orig,
|
2013-07-25 22:52:59 -07:00
|
|
|
MutableHandle<JSPropertyDescriptor> wrapped)
|
2013-05-17 10:44:25 -07:00
|
|
|
{
|
|
|
|
// Compute whether the original objects were waived, and implicitly, whether
|
|
|
|
// they were objects at all.
|
|
|
|
bool valueWasWaived =
|
|
|
|
orig->value.isObject() &&
|
|
|
|
WrapperFactory::HasWaiveXrayFlag(&orig->value.toObject());
|
|
|
|
bool getterWasWaived =
|
|
|
|
(orig->attrs & JSPROP_GETTER) &&
|
|
|
|
WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->getter));
|
|
|
|
bool setterWasWaived =
|
|
|
|
(orig->attrs & JSPROP_SETTER) &&
|
|
|
|
WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->setter));
|
|
|
|
|
|
|
|
// Recreate waivers. Note that for value, we need an extra UncheckedUnwrap
|
|
|
|
// to handle same-compartment security wrappers (see above). This should
|
|
|
|
// never happen for getters/setters.
|
|
|
|
|
|
|
|
RootedObject rewaived(cx);
|
|
|
|
if (valueWasWaived && !IsCrossCompartmentWrapper(&wrapped.value().toObject())) {
|
|
|
|
rewaived = &wrapped.value().toObject();
|
|
|
|
rewaived = WrapperFactory::WaiveXray(cx, UncheckedUnwrap(rewaived));
|
|
|
|
NS_ENSURE_TRUE(rewaived, false);
|
|
|
|
wrapped.value().set(ObjectValue(*rewaived));
|
|
|
|
}
|
|
|
|
if (getterWasWaived && !IsCrossCompartmentWrapper(wrapped.getterObject())) {
|
|
|
|
MOZ_ASSERT(CheckedUnwrap(wrapped.getterObject()));
|
|
|
|
rewaived = WrapperFactory::WaiveXray(cx, wrapped.getterObject());
|
|
|
|
NS_ENSURE_TRUE(rewaived, false);
|
|
|
|
wrapped.setGetterObject(rewaived);
|
|
|
|
}
|
|
|
|
if (setterWasWaived && !IsCrossCompartmentWrapper(wrapped.setterObject())) {
|
|
|
|
MOZ_ASSERT(CheckedUnwrap(wrapped.setterObject()));
|
|
|
|
rewaived = WrapperFactory::WaiveXray(cx, wrapped.setterObject());
|
|
|
|
NS_ENSURE_TRUE(rewaived, false);
|
|
|
|
wrapped.setSetterObject(rewaived);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
2011-05-25 08:30:50 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::defineProperty(JSContext* cx, HandleObject wrapper,
|
2015-02-17 09:58:43 -08:00
|
|
|
HandleId id, Handle<JSPropertyDescriptor> desc,
|
2015-03-28 15:22:11 -07:00
|
|
|
ObjectOpResult& result) const
|
2011-05-25 08:30:50 -07:00
|
|
|
{
|
2014-02-13 10:54:07 -08:00
|
|
|
assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET);
|
2011-08-20 06:53:32 -07:00
|
|
|
|
2013-07-25 22:52:59 -07:00
|
|
|
Rooted<JSPropertyDescriptor> existing_desc(cx);
|
2014-06-05 22:32:38 -07:00
|
|
|
if (!JS_GetPropertyDescriptorById(cx, wrapper, id, &existing_desc))
|
2011-08-20 06:53:32 -07:00
|
|
|
return false;
|
|
|
|
|
2014-06-05 22:32:38 -07:00
|
|
|
// Note that the check here is intended to differentiate between own and
|
|
|
|
// non-own properties, since the above lookup is not limited to own
|
|
|
|
// properties. At present, this may not always do the right thing because
|
|
|
|
// we often lie (sloppily) about where we found properties and set
|
|
|
|
// desc.object() to |wrapper|. Once we fully fix our Xray prototype semantics,
|
|
|
|
// this should work as intended.
|
2015-02-15 04:18:30 -08:00
|
|
|
if (existing_desc.object() == wrapper && !existing_desc.configurable()) {
|
2014-06-11 15:16:06 -07:00
|
|
|
// We have a non-configurable property. See if the caller is trying to
|
|
|
|
// re-configure it in any way other than making it non-writable.
|
2015-02-15 04:18:30 -08:00
|
|
|
if (existing_desc.isAccessorDescriptor() || desc.isAccessorDescriptor() ||
|
|
|
|
(desc.hasEnumerable() && existing_desc.enumerable() != desc.enumerable()) ||
|
|
|
|
(desc.hasWritable() && !existing_desc.writable() && desc.writable()))
|
2014-06-11 15:16:06 -07:00
|
|
|
{
|
|
|
|
// We should technically report non-configurability in strict mode, but
|
2015-01-30 09:37:07 -08:00
|
|
|
// doing that via JSAPI used to be a lot of trouble. See bug 1135997.
|
|
|
|
return result.succeed();
|
2014-06-11 15:16:06 -07:00
|
|
|
}
|
2015-02-15 04:18:30 -08:00
|
|
|
if (!existing_desc.writable()) {
|
2014-06-11 15:16:06 -07:00
|
|
|
// Same as the above for non-writability.
|
2015-01-30 09:37:07 -08:00
|
|
|
return result.succeed();
|
2014-06-11 15:16:06 -07:00
|
|
|
}
|
|
|
|
}
|
2011-08-20 06:53:32 -07:00
|
|
|
|
2012-10-05 09:59:24 -07:00
|
|
|
bool defined = false;
|
2015-01-30 09:37:07 -08:00
|
|
|
if (!Traits::singleton.defineProperty(cx, wrapper, id, desc, existing_desc, result, &defined))
|
2012-10-05 09:59:24 -07:00
|
|
|
return false;
|
|
|
|
if (defined)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// We're placing an expando. The expando objects live in the target
|
|
|
|
// compartment, so we need to enter it.
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject target(cx, Traits::singleton.getTargetObject(wrapper));
|
2012-10-05 09:59:24 -07:00
|
|
|
JSAutoCompartment ac(cx, target);
|
|
|
|
|
|
|
|
// Grab the relevant expando object.
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject expandoObject(cx, Traits::singleton.ensureExpandoObject(cx, wrapper,
|
|
|
|
target));
|
2012-10-05 09:59:24 -07:00
|
|
|
if (!expandoObject)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Wrap the property descriptor for the target compartment.
|
2013-07-25 22:52:59 -07:00
|
|
|
Rooted<JSPropertyDescriptor> wrappedDesc(cx, desc);
|
2013-08-12 04:09:14 -07:00
|
|
|
if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc))
|
2012-10-05 09:59:24 -07:00
|
|
|
return false;
|
|
|
|
|
2013-05-17 10:44:25 -07:00
|
|
|
// Fix up Xray waivers.
|
2013-04-30 10:29:40 -07:00
|
|
|
if (!RecreateLostWaivers(cx, desc.address(), &wrappedDesc))
|
2013-05-17 10:44:25 -07:00
|
|
|
return false;
|
|
|
|
|
2015-01-30 09:37:07 -08:00
|
|
|
return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc, result);
|
2011-08-20 06:53:32 -07:00
|
|
|
}
|
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::ownPropertyKeys(JSContext* cx, HandleObject wrapper,
|
|
|
|
AutoIdVector& props) const
|
2011-08-20 06:53:32 -07:00
|
|
|
{
|
2014-02-13 10:54:08 -08:00
|
|
|
assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
|
2014-11-14 07:21:12 -08:00
|
|
|
return getPropertyKeys(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
|
2012-03-27 16:31:37 -07:00
|
|
|
}
|
2011-08-20 06:53:32 -07:00
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::delete_(JSContext* cx, HandleObject wrapper,
|
|
|
|
HandleId id, ObjectOpResult& result) const
|
2012-03-27 16:31:37 -07:00
|
|
|
{
|
2014-02-13 10:54:07 -08:00
|
|
|
assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET);
|
2012-03-27 16:31:37 -07:00
|
|
|
|
2012-10-05 09:59:24 -07:00
|
|
|
// Check the expando object.
|
2013-03-27 04:33:30 -07:00
|
|
|
RootedObject target(cx, Traits::getTargetObject(wrapper));
|
2014-10-21 02:59:30 -07:00
|
|
|
RootedObject expando(cx);
|
|
|
|
if (!Traits::singleton.getExpandoObject(cx, target, wrapper, &expando))
|
|
|
|
return false;
|
|
|
|
|
2012-10-05 09:59:24 -07:00
|
|
|
if (expando) {
|
|
|
|
JSAutoCompartment ac(cx, expando);
|
2015-02-04 08:20:04 -08:00
|
|
|
return JS_DeletePropertyById(cx, expando, id, result);
|
2012-10-05 09:59:24 -07:00
|
|
|
}
|
2014-06-05 22:32:39 -07:00
|
|
|
|
2015-02-04 08:20:04 -08:00
|
|
|
return Traits::singleton.delete_(cx, wrapper, id, result);
|
2012-03-27 16:31:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Base, typename Traits>
|
2011-05-25 08:30:50 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::get(JSContext* cx, HandleObject wrapper,
|
2015-09-17 15:14:41 -07:00
|
|
|
HandleValue receiver, HandleId id,
|
2014-06-27 04:44:06 -07:00
|
|
|
MutableHandleValue vp) const
|
2011-05-25 08:30:50 -07:00
|
|
|
{
|
2012-03-27 16:31:37 -07:00
|
|
|
// Skip our Base if it isn't already ProxyHandler.
|
|
|
|
// NB: None of the functions we call are prepared for the receiver not
|
|
|
|
// being the wrapper, so ignore the receiver here.
|
2015-09-17 15:14:41 -07:00
|
|
|
RootedValue thisv(cx);
|
|
|
|
if (Traits::HasPrototype)
|
|
|
|
thisv = receiver;
|
|
|
|
else
|
|
|
|
thisv.setObject(*wrapper);
|
|
|
|
return js::BaseProxyHandler::get(cx, wrapper, thisv, id, vp);
|
2012-03-27 16:31:37 -07:00
|
|
|
}
|
2011-08-20 06:53:32 -07:00
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
|
|
|
|
HandleValue receiver, ObjectOpResult& result) const
|
2012-03-27 16:31:37 -07:00
|
|
|
{
|
2014-09-15 07:51:40 -07:00
|
|
|
MOZ_ASSERT(!Traits::HasPrototype);
|
2014-09-15 07:52:11 -07:00
|
|
|
// Skip our Base if it isn't already BaseProxyHandler.
|
2012-03-27 16:31:37 -07:00
|
|
|
// NB: None of the functions we call are prepared for the receiver not
|
|
|
|
// being the wrapper, so ignore the receiver here.
|
Bug 1142794 - Change 'receiver' argument to SetProperty functions and ProxyHandler::set methods to be a HandleValue. r=Waldo.
Also: Change signature of these functions and methods to all have the same arguments in the same order: (cx, obj, id, v, receiver). Also change v from MutableHandleValue to HandleValue.
There is no change in behavior.
In fact the new error message `JSMSG_SET_NON_OBJECT_RECEIVER` is
impossible to trigger from scripts for now, I think (after re-reading
the whole patch with this in mind). JS_ForwardSetPropertyTo is the only
way to get a non-object receiver into the engine, but no caller
currently does so.
We're installing new pipes here, and they should work, but for now it's
the same cold water flowing through as before. Actually hooking up the
hot water is left for another bug (one with tests, not to put too fine a
point on it).
Notes:
* InvokeGetterOrSetter had to be split into two functions:
InvokeGetter takes a MutableHandleValue out-param,
InvokeSetter a HandleValue in-param.
* Watchpoints can still tamper with values being assigned. So can
JSSetterOps. I'm pleased we can support this craziness in a way that
doesn't have to spread via the type system to encompass the entire
codebase.
* Change in GlobalObject::setIntrinsicValue is not really a change.
Yes, it asserted before, but an exception thrown during self-hosting
initialization is not going to go unnoticed either.
* Since the receiver argument to js::SetProperty() is at the end now, it
makes sense for it to be optional. Some callers look nicer.
2015-03-01 11:16:19 -08:00
|
|
|
RootedValue wrapperValue(cx, ObjectValue(*wrapper));
|
|
|
|
return js::BaseProxyHandler::set(cx, wrapper, id, v, wrapperValue, result);
|
2012-03-27 16:31:37 -07:00
|
|
|
}
|
2011-08-20 06:53:32 -07:00
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::has(JSContext* cx, HandleObject wrapper,
|
|
|
|
HandleId id, bool* bp) const
|
2012-03-27 16:31:37 -07:00
|
|
|
{
|
|
|
|
// Skip our Base if it isn't already ProxyHandler.
|
2013-03-27 04:33:30 -07:00
|
|
|
return js::BaseProxyHandler::has(cx, wrapper, id, bp);
|
2012-03-27 16:31:37 -07:00
|
|
|
}
|
2011-08-20 06:53:32 -07:00
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::hasOwn(JSContext* cx, HandleObject wrapper,
|
|
|
|
HandleId id, bool* bp) const
|
2012-03-27 16:31:37 -07:00
|
|
|
{
|
|
|
|
// Skip our Base if it isn't already ProxyHandler.
|
2013-03-27 04:33:30 -07:00
|
|
|
return js::BaseProxyHandler::hasOwn(cx, wrapper, id, bp);
|
2012-03-27 16:31:37 -07:00
|
|
|
}
|
2011-08-20 06:53:32 -07:00
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::getOwnEnumerablePropertyKeys(JSContext* cx,
|
2014-10-08 20:01:55 -07:00
|
|
|
HandleObject wrapper,
|
2015-03-28 15:22:11 -07:00
|
|
|
AutoIdVector& props) const
|
2012-03-27 16:31:37 -07:00
|
|
|
{
|
|
|
|
// Skip our Base if it isn't already ProxyHandler.
|
2014-10-08 20:01:55 -07:00
|
|
|
return js::BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, wrapper, props);
|
2012-03-27 16:31:37 -07:00
|
|
|
}
|
2011-08-20 06:53:32 -07:00
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::enumerate(JSContext* cx, HandleObject wrapper,
|
2014-12-16 15:28:38 -08:00
|
|
|
MutableHandleObject objp) const
|
2012-03-27 16:31:37 -07:00
|
|
|
{
|
|
|
|
// Skip our Base if it isn't already ProxyHandler.
|
2014-12-16 15:28:38 -08:00
|
|
|
return js::BaseProxyHandler::enumerate(cx, wrapper, objp);
|
2012-03-27 16:31:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::call(JSContext* cx, HandleObject wrapper, const JS::CallArgs& args) const
|
2012-03-27 16:31:37 -07:00
|
|
|
{
|
2014-02-13 10:54:07 -08:00
|
|
|
assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL);
|
2014-06-27 04:44:04 -07:00
|
|
|
// Hard cast the singleton since SecurityWrapper doesn't have one.
|
2014-06-27 04:44:06 -07:00
|
|
|
return Traits::call(cx, wrapper, args, Base::singleton);
|
2011-05-25 08:30:50 -07:00
|
|
|
}
|
|
|
|
|
2012-03-27 16:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
2011-05-25 08:30:50 -07:00
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::construct(JSContext* cx, HandleObject wrapper, const JS::CallArgs& args) const
|
2011-05-25 08:30:50 -07:00
|
|
|
{
|
2014-02-13 10:54:07 -08:00
|
|
|
assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL);
|
2014-06-27 04:44:04 -07:00
|
|
|
// Hard cast the singleton since SecurityWrapper doesn't have one.
|
2014-06-27 04:44:06 -07:00
|
|
|
return Traits::construct(cx, wrapper, args, Base::singleton);
|
2011-05-25 08:30:50 -07:00
|
|
|
}
|
|
|
|
|
2014-07-10 09:31:37 -07:00
|
|
|
template <typename Base, typename Traits>
|
2015-03-28 15:22:11 -07:00
|
|
|
const char*
|
|
|
|
XrayWrapper<Base, Traits>::className(JSContext* cx, HandleObject wrapper) const
|
2014-07-10 09:31:37 -07:00
|
|
|
{
|
|
|
|
return Traits::className(cx, wrapper, Base::singleton);
|
|
|
|
}
|
|
|
|
|
2013-12-13 12:01:30 -08:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::getPrototype(JSContext* cx, JS::HandleObject wrapper,
|
2015-02-05 14:36:50 -08:00
|
|
|
JS::MutableHandleObject protop) const
|
2013-12-13 12:01:30 -08:00
|
|
|
{
|
2014-01-29 13:07:13 -08:00
|
|
|
// We really only want this override for non-SecurityWrapper-inheriting
|
|
|
|
// |Base|. But doing that statically with templates requires partial method
|
|
|
|
// specializations (and therefore a helper class), which is all more trouble
|
|
|
|
// than it's worth. Do a dynamic check.
|
|
|
|
if (Base::hasSecurityPolicy())
|
2015-02-05 14:36:50 -08:00
|
|
|
return Base::getPrototype(cx, wrapper, protop);
|
2014-01-29 13:07:13 -08:00
|
|
|
|
2013-12-13 12:01:30 -08:00
|
|
|
RootedObject target(cx, Traits::getTargetObject(wrapper));
|
2014-10-21 02:59:30 -07:00
|
|
|
RootedObject expando(cx);
|
|
|
|
if (!Traits::singleton.getExpandoObject(cx, target, wrapper, &expando))
|
|
|
|
return false;
|
2013-12-13 12:01:30 -08:00
|
|
|
|
2014-02-21 15:55:30 -08:00
|
|
|
// We want to keep the Xray's prototype distinct from that of content, but
|
|
|
|
// only if there's been a set. If there's not an expando, or the expando
|
|
|
|
// slot is |undefined|, hand back the default proto, appropriately wrapped.
|
2013-12-13 12:01:30 -08:00
|
|
|
|
|
|
|
RootedValue v(cx);
|
2014-02-21 15:55:30 -08:00
|
|
|
if (expando) {
|
2013-12-13 12:01:30 -08:00
|
|
|
JSAutoCompartment ac(cx, expando);
|
|
|
|
v = JS_GetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE);
|
|
|
|
}
|
|
|
|
if (v.isUndefined())
|
2015-02-05 14:36:50 -08:00
|
|
|
return getPrototypeHelper(cx, wrapper, target, protop);
|
2013-12-13 12:01:30 -08:00
|
|
|
|
|
|
|
protop.set(v.toObjectOrNull());
|
|
|
|
return JS_WrapObject(cx, protop);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::setPrototype(JSContext* cx, JS::HandleObject wrapper,
|
|
|
|
JS::HandleObject proto, JS::ObjectOpResult& result) const
|
2013-12-13 12:01:30 -08:00
|
|
|
{
|
2014-01-29 13:07:13 -08:00
|
|
|
// Do this only for non-SecurityWrapper-inheriting |Base|. See the comment
|
2015-02-05 14:36:50 -08:00
|
|
|
// in getPrototype().
|
2014-01-29 13:07:13 -08:00
|
|
|
if (Base::hasSecurityPolicy())
|
2015-02-05 14:36:50 -08:00
|
|
|
return Base::setPrototype(cx, wrapper, proto, result);
|
2014-01-29 13:07:13 -08:00
|
|
|
|
2013-12-13 12:01:30 -08:00
|
|
|
RootedObject target(cx, Traits::getTargetObject(wrapper));
|
|
|
|
RootedObject expando(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, target));
|
2014-10-21 02:59:30 -07:00
|
|
|
if (!expando)
|
|
|
|
return false;
|
2013-12-13 12:01:30 -08:00
|
|
|
|
|
|
|
// The expando lives in the target's compartment, so do our installation there.
|
|
|
|
JSAutoCompartment ac(cx, target);
|
|
|
|
|
2014-07-02 21:06:28 -07:00
|
|
|
RootedValue v(cx, ObjectOrNullValue(proto));
|
2013-12-13 12:01:30 -08:00
|
|
|
if (!JS_WrapValue(cx, &v))
|
|
|
|
return false;
|
|
|
|
JS_SetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE, v);
|
2015-02-05 14:36:50 -08:00
|
|
|
return result.succeed();
|
2013-12-13 12:01:30 -08:00
|
|
|
}
|
|
|
|
|
2014-09-23 13:03:40 -07:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::setImmutablePrototype(JSContext* cx, JS::HandleObject wrapper,
|
|
|
|
bool* succeeded) const
|
2014-09-23 13:03:40 -07:00
|
|
|
{
|
|
|
|
// For now, lacking an obvious place to store a bit, prohibit making an
|
|
|
|
// Xray's [[Prototype]] immutable. We can revisit this (or maybe give all
|
|
|
|
// Xrays immutable [[Prototype]], because who does this, really?) later if
|
|
|
|
// necessary.
|
|
|
|
*succeeded = false;
|
|
|
|
return true;
|
|
|
|
}
|
2013-05-31 10:36:01 -07:00
|
|
|
|
2014-11-14 07:21:12 -08:00
|
|
|
template <typename Base, typename Traits>
|
|
|
|
bool
|
2015-03-28 15:22:11 -07:00
|
|
|
XrayWrapper<Base, Traits>::getPropertyKeys(JSContext* cx, HandleObject wrapper, unsigned flags,
|
|
|
|
AutoIdVector& props) const
|
2014-11-14 07:21:12 -08:00
|
|
|
{
|
|
|
|
assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
|
|
|
|
|
|
|
|
// Enumerate expando properties first. Note that the expando object lives
|
|
|
|
// in the target compartment.
|
|
|
|
RootedObject target(cx, Traits::singleton.getTargetObject(wrapper));
|
|
|
|
RootedObject expando(cx);
|
|
|
|
if (!Traits::singleton.getExpandoObject(cx, target, wrapper, &expando))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (expando) {
|
|
|
|
JSAutoCompartment ac(cx, expando);
|
|
|
|
if (!js::GetPropertyKeys(cx, expando, flags, &props))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Traits::singleton.enumerateNames(cx, wrapper, flags, props);
|
|
|
|
}
|
|
|
|
|
2012-11-14 09:56:25 -08:00
|
|
|
/*
|
|
|
|
* The Permissive / Security variants should be used depending on whether the
|
|
|
|
* compartment of the wrapper is guranteed to subsume the compartment of the
|
|
|
|
* wrapped object (i.e. - whether it is safe from a security perspective to
|
|
|
|
* unwrap the wrapper).
|
|
|
|
*/
|
|
|
|
|
|
|
|
template<>
|
2014-06-27 04:44:04 -07:00
|
|
|
const PermissiveXrayXPCWN PermissiveXrayXPCWN::singleton(0);
|
2012-11-14 09:56:25 -08:00
|
|
|
template class PermissiveXrayXPCWN;
|
|
|
|
|
|
|
|
template<>
|
2014-06-27 04:44:04 -07:00
|
|
|
const SecurityXrayXPCWN SecurityXrayXPCWN::singleton(0);
|
2012-11-14 09:56:25 -08:00
|
|
|
template class SecurityXrayXPCWN;
|
|
|
|
|
|
|
|
template<>
|
2014-06-27 04:44:04 -07:00
|
|
|
const PermissiveXrayDOM PermissiveXrayDOM::singleton(0);
|
2012-11-14 09:56:25 -08:00
|
|
|
template class PermissiveXrayDOM;
|
|
|
|
|
|
|
|
template<>
|
2014-06-27 04:44:04 -07:00
|
|
|
const SecurityXrayDOM SecurityXrayDOM::singleton(0);
|
2012-11-14 09:56:25 -08:00
|
|
|
template class SecurityXrayDOM;
|
|
|
|
|
2014-03-23 07:02:12 -07:00
|
|
|
template<>
|
2014-06-27 04:44:04 -07:00
|
|
|
const PermissiveXrayJS PermissiveXrayJS::singleton(0);
|
2014-03-23 07:02:12 -07:00
|
|
|
template class PermissiveXrayJS;
|
|
|
|
|
2014-07-10 09:31:37 -07:00
|
|
|
template<>
|
|
|
|
const PermissiveXrayOpaque PermissiveXrayOpaque::singleton(0);
|
|
|
|
template class PermissiveXrayOpaque;
|
|
|
|
|
2012-11-14 09:56:25 -08:00
|
|
|
template<>
|
2014-06-27 04:44:04 -07:00
|
|
|
const SCSecurityXrayXPCWN SCSecurityXrayXPCWN::singleton(0);
|
2012-11-14 09:56:25 -08:00
|
|
|
template class SCSecurityXrayXPCWN;
|
2011-05-25 08:30:50 -07:00
|
|
|
|
2015-07-13 08:25:42 -07:00
|
|
|
} // namespace xpc
|