mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 03:47:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
458
js/xpconnect/wrappers/AccessCheck.cpp
Normal file
458
js/xpconnect/wrappers/AccessCheck.cpp
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AccessCheck.h"
|
||||
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
#include "XPCWrapper.h"
|
||||
#include "XrayWrapper.h"
|
||||
#include "FilteringWrapper.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/LocationBinding.h"
|
||||
#include "mozilla/dom/WindowBinding.h"
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
#include "nsIDOMWindowCollection.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "xpcprivate.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace JS;
|
||||
using namespace js;
|
||||
|
||||
namespace xpc {
|
||||
|
||||
nsIPrincipal*
|
||||
GetCompartmentPrincipal(JSCompartment* compartment)
|
||||
{
|
||||
return nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment));
|
||||
}
|
||||
|
||||
nsIPrincipal*
|
||||
GetObjectPrincipal(JSObject* obj)
|
||||
{
|
||||
return GetCompartmentPrincipal(js::GetObjectCompartment(obj));
|
||||
}
|
||||
|
||||
// Does the principal of compartment a subsume the principal of compartment b?
|
||||
bool
|
||||
AccessCheck::subsumes(JSCompartment* a, JSCompartment* b)
|
||||
{
|
||||
nsIPrincipal* aprin = GetCompartmentPrincipal(a);
|
||||
nsIPrincipal* bprin = GetCompartmentPrincipal(b);
|
||||
return aprin->Subsumes(bprin);
|
||||
}
|
||||
|
||||
bool
|
||||
AccessCheck::subsumes(JSObject* a, JSObject* b)
|
||||
{
|
||||
return subsumes(js::GetObjectCompartment(a), js::GetObjectCompartment(b));
|
||||
}
|
||||
|
||||
// Same as above, but considering document.domain.
|
||||
bool
|
||||
AccessCheck::subsumesConsideringDomain(JSCompartment* a, JSCompartment* b)
|
||||
{
|
||||
nsIPrincipal* aprin = GetCompartmentPrincipal(a);
|
||||
nsIPrincipal* bprin = GetCompartmentPrincipal(b);
|
||||
return aprin->SubsumesConsideringDomain(bprin);
|
||||
}
|
||||
|
||||
// Does the compartment of the wrapper subsumes the compartment of the wrappee?
|
||||
bool
|
||||
AccessCheck::wrapperSubsumes(JSObject* wrapper)
|
||||
{
|
||||
MOZ_ASSERT(js::IsWrapper(wrapper));
|
||||
JSObject* wrapped = js::UncheckedUnwrap(wrapper);
|
||||
return AccessCheck::subsumes(js::GetObjectCompartment(wrapper),
|
||||
js::GetObjectCompartment(wrapped));
|
||||
}
|
||||
|
||||
bool
|
||||
AccessCheck::isChrome(JSCompartment* compartment)
|
||||
{
|
||||
bool privileged;
|
||||
nsIPrincipal* principal = GetCompartmentPrincipal(compartment);
|
||||
return NS_SUCCEEDED(nsXPConnect::SecurityManager()->IsSystemPrincipal(principal, &privileged)) && privileged;
|
||||
}
|
||||
|
||||
bool
|
||||
AccessCheck::isChrome(JSObject* obj)
|
||||
{
|
||||
return isChrome(js::GetObjectCompartment(obj));
|
||||
}
|
||||
|
||||
nsIPrincipal*
|
||||
AccessCheck::getPrincipal(JSCompartment* compartment)
|
||||
{
|
||||
return GetCompartmentPrincipal(compartment);
|
||||
}
|
||||
|
||||
// Hardcoded policy for cross origin property access. See the HTML5 Spec.
|
||||
static bool
|
||||
IsPermitted(CrossOriginObjectType type, JSFlatString* prop, bool set)
|
||||
{
|
||||
size_t propLength = JS_GetStringLength(JS_FORGET_STRING_FLATNESS(prop));
|
||||
if (!propLength)
|
||||
return false;
|
||||
|
||||
char16_t propChar0 = JS_GetFlatStringCharAt(prop, 0);
|
||||
if (type == CrossOriginLocation)
|
||||
return dom::LocationBinding::IsPermitted(prop, propChar0, set);
|
||||
if (type == CrossOriginWindow)
|
||||
return dom::WindowBinding::IsPermitted(prop, propChar0, set);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsFrameId(JSContext* cx, JSObject* obj, jsid idArg)
|
||||
{
|
||||
MOZ_ASSERT(!js::IsWrapper(obj));
|
||||
RootedId id(cx, idArg);
|
||||
|
||||
nsGlobalWindow* win = WindowOrNull(obj);
|
||||
if (!win) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMWindowCollection> col = win->GetFrames();
|
||||
if (!col) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIDOMWindowProxy> domwin;
|
||||
if (JSID_IS_INT(id)) {
|
||||
col->Item(JSID_TO_INT(id), getter_AddRefs(domwin));
|
||||
} else if (JSID_IS_STRING(id)) {
|
||||
nsAutoJSString idAsString;
|
||||
if (!idAsString.init(cx, JSID_TO_STRING(id))) {
|
||||
return false;
|
||||
}
|
||||
col->NamedItem(idAsString, getter_AddRefs(domwin));
|
||||
}
|
||||
|
||||
return domwin != nullptr;
|
||||
}
|
||||
|
||||
CrossOriginObjectType
|
||||
IdentifyCrossOriginObject(JSObject* obj)
|
||||
{
|
||||
obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
|
||||
const js::Class* clasp = js::GetObjectClass(obj);
|
||||
MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(Jsvalify(clasp)), "shouldn't have a holder here");
|
||||
|
||||
if (clasp->name[0] == 'L' && !strcmp(clasp->name, "Location"))
|
||||
return CrossOriginLocation;
|
||||
if (clasp->name[0] == 'W' && !strcmp(clasp->name, "Window"))
|
||||
return CrossOriginWindow;
|
||||
|
||||
return CrossOriginOpaque;
|
||||
}
|
||||
|
||||
bool
|
||||
AccessCheck::isCrossOriginAccessPermitted(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
Wrapper::Action act)
|
||||
{
|
||||
if (act == Wrapper::CALL)
|
||||
return false;
|
||||
|
||||
if (act == Wrapper::ENUMERATE)
|
||||
return true;
|
||||
|
||||
// For the case of getting a property descriptor, we allow if either GET or SET
|
||||
// is allowed, and rely on FilteringWrapper to filter out any disallowed accessors.
|
||||
if (act == Wrapper::GET_PROPERTY_DESCRIPTOR) {
|
||||
return isCrossOriginAccessPermitted(cx, wrapper, id, Wrapper::GET) ||
|
||||
isCrossOriginAccessPermitted(cx, wrapper, id, Wrapper::SET);
|
||||
}
|
||||
|
||||
RootedObject obj(cx, js::UncheckedUnwrap(wrapper, /* stopAtWindowProxy = */ false));
|
||||
CrossOriginObjectType type = IdentifyCrossOriginObject(obj);
|
||||
if (JSID_IS_STRING(id)) {
|
||||
if (IsPermitted(type, JSID_TO_FLAT_STRING(id), act == Wrapper::SET))
|
||||
return true;
|
||||
} else if (type != CrossOriginOpaque &&
|
||||
IsCrossOriginWhitelistedSymbol(cx, id)) {
|
||||
// We always allow access to @@toStringTag, @@hasInstance, and
|
||||
// @@isConcatSpreadable. But then we nerf them to be a value descriptor
|
||||
// with value undefined in CrossOriginXrayWrapper.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (act != Wrapper::GET)
|
||||
return false;
|
||||
|
||||
// Check for frame IDs. If we're resolving named frames, make sure to only
|
||||
// resolve ones that don't shadow native properties. See bug 860494.
|
||||
if (type == CrossOriginWindow) {
|
||||
if (JSID_IS_STRING(id)) {
|
||||
bool wouldShadow = false;
|
||||
if (!XrayUtils::HasNativeProperty(cx, wrapper, id, &wouldShadow) ||
|
||||
wouldShadow)
|
||||
{
|
||||
// If the named subframe matches the name of a DOM constructor,
|
||||
// the global resolve triggered by the HasNativeProperty call
|
||||
// above will try to perform a CheckedUnwrap on |wrapper|, and
|
||||
// throw a security error if it fails. That exception isn't
|
||||
// really useful for our callers, so we silence it and just
|
||||
// deny access to the property (since it matched a builtin).
|
||||
//
|
||||
// Note that this would be a problem if the resolve code ever
|
||||
// tried to CheckedUnwrap the wrapper _before_ concluding that
|
||||
// the name corresponds to a builtin global property, since it
|
||||
// would mean that we'd never permit cross-origin named subframe
|
||||
// access (something we regrettably need to support).
|
||||
JS_ClearPendingException(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return IsFrameId(cx, obj, id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
AccessCheck::checkPassToPrivilegedCode(JSContext* cx, HandleObject wrapper, HandleValue v)
|
||||
{
|
||||
// Primitives are fine.
|
||||
if (!v.isObject())
|
||||
return true;
|
||||
RootedObject obj(cx, &v.toObject());
|
||||
|
||||
// Non-wrappers are fine.
|
||||
if (!js::IsWrapper(obj))
|
||||
return true;
|
||||
|
||||
// CPOWs use COWs (in the unprivileged junk scope) for all child->parent
|
||||
// references. Without this test, the child process wouldn't be able to
|
||||
// pass any objects at all to CPOWs.
|
||||
if (mozilla::jsipc::IsWrappedCPOW(obj) &&
|
||||
js::GetObjectCompartment(wrapper) == js::GetObjectCompartment(xpc::UnprivilegedJunkScope()) &&
|
||||
XRE_IsParentProcess())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// COWs are fine to pass to chrome if and only if they have __exposedProps__,
|
||||
// since presumably content should never have a reason to pass an opaque
|
||||
// object back to chrome.
|
||||
if (AccessCheck::isChrome(js::UncheckedUnwrap(wrapper)) && WrapperFactory::IsCOW(obj)) {
|
||||
RootedObject target(cx, js::UncheckedUnwrap(obj));
|
||||
JSAutoCompartment ac(cx, target);
|
||||
RootedId id(cx, GetJSIDByIndex(cx, XPCJSContext::IDX_EXPOSEDPROPS));
|
||||
bool found = false;
|
||||
if (!JS_HasPropertyById(cx, target, id, &found))
|
||||
return false;
|
||||
if (found)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Same-origin wrappers are fine.
|
||||
if (AccessCheck::wrapperSubsumes(obj))
|
||||
return true;
|
||||
|
||||
// Badness.
|
||||
JS_ReportErrorASCII(cx, "Permission denied to pass object to privileged code");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
AccessCheck::checkPassToPrivilegedCode(JSContext* cx, HandleObject wrapper, const CallArgs& args)
|
||||
{
|
||||
if (!checkPassToPrivilegedCode(cx, wrapper, args.thisv()))
|
||||
return false;
|
||||
for (size_t i = 0; i < args.length(); ++i) {
|
||||
if (!checkPassToPrivilegedCode(cx, wrapper, args[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 };
|
||||
|
||||
static void
|
||||
EnterAndThrowASCII(JSContext* cx, JSObject* wrapper, const char* msg)
|
||||
{
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
JS_ReportErrorASCII(cx, "%s", msg);
|
||||
}
|
||||
|
||||
bool
|
||||
ExposedPropertiesOnly::check(JSContext* cx, HandleObject wrapper, HandleId id, Wrapper::Action act)
|
||||
{
|
||||
RootedObject wrappedObject(cx, Wrapper::wrappedObject(wrapper));
|
||||
|
||||
if (act == Wrapper::CALL)
|
||||
return false;
|
||||
|
||||
// For the case of getting a property descriptor, we allow if either GET or SET
|
||||
// is allowed, and rely on FilteringWrapper to filter out any disallowed accessors.
|
||||
if (act == Wrapper::GET_PROPERTY_DESCRIPTOR) {
|
||||
return check(cx, wrapper, id, Wrapper::GET) ||
|
||||
check(cx, wrapper, id, Wrapper::SET);
|
||||
}
|
||||
|
||||
RootedId exposedPropsId(cx, GetJSIDByIndex(cx, XPCJSContext::IDX_EXPOSEDPROPS));
|
||||
|
||||
// We need to enter the wrappee's compartment to look at __exposedProps__,
|
||||
// but we want to be in the wrapper's compartment if we call Deny().
|
||||
//
|
||||
// Unfortunately, |cx| can be in either compartment when we call ::check. :-(
|
||||
JSAutoCompartment ac(cx, wrappedObject);
|
||||
|
||||
bool found = false;
|
||||
if (!JS_HasPropertyById(cx, wrappedObject, exposedPropsId, &found))
|
||||
return false;
|
||||
|
||||
// If no __exposedProps__ existed, deny access.
|
||||
if (!found) {
|
||||
// Previously we automatically granted access to indexed properties and
|
||||
// .length for Array COWs. We're not doing that anymore, so make sure to
|
||||
// let people know what's going on.
|
||||
bool isArray;
|
||||
if (!JS_IsArrayObject(cx, wrappedObject, &isArray))
|
||||
return false;
|
||||
if (!isArray)
|
||||
isArray = JS_IsTypedArrayObject(wrappedObject);
|
||||
bool isIndexedAccessOnArray = isArray && JSID_IS_INT(id) && JSID_TO_INT(id) >= 0;
|
||||
bool isLengthAccessOnArray = isArray && JSID_IS_STRING(id) &&
|
||||
JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "length");
|
||||
if (isIndexedAccessOnArray || isLengthAccessOnArray) {
|
||||
JSAutoCompartment ac2(cx, wrapper);
|
||||
ReportWrapperDenial(cx, id, WrapperDenialForCOW,
|
||||
"Access to elements and length of privileged Array not permitted");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (id == JSID_VOID)
|
||||
return true;
|
||||
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!JS_GetPropertyDescriptorById(cx, wrappedObject, exposedPropsId, &desc))
|
||||
return false;
|
||||
|
||||
if (!desc.object())
|
||||
return false;
|
||||
|
||||
if (desc.hasGetterOrSetter()) {
|
||||
EnterAndThrowASCII(cx, wrapper, "__exposedProps__ must be a value property");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue exposedProps(cx, desc.value());
|
||||
if (exposedProps.isNullOrUndefined())
|
||||
return false;
|
||||
|
||||
if (!exposedProps.isObject()) {
|
||||
EnterAndThrowASCII(cx, wrapper, "__exposedProps__ must be undefined, null, or an Object");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject hallpass(cx, &exposedProps.toObject());
|
||||
|
||||
if (!AccessCheck::subsumes(js::UncheckedUnwrap(hallpass), wrappedObject)) {
|
||||
EnterAndThrowASCII(cx, wrapper, "Invalid __exposedProps__");
|
||||
return false;
|
||||
}
|
||||
|
||||
Access access = NO_ACCESS;
|
||||
|
||||
if (!JS_GetPropertyDescriptorById(cx, hallpass, id, &desc)) {
|
||||
return false; // Error
|
||||
}
|
||||
if (!desc.object() || !desc.enumerable())
|
||||
return false;
|
||||
|
||||
if (!desc.value().isString()) {
|
||||
EnterAndThrowASCII(cx, wrapper, "property must be a string");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSFlatString* flat = JS_FlattenString(cx, desc.value().toString());
|
||||
if (!flat)
|
||||
return false;
|
||||
|
||||
size_t length = JS_GetStringLength(JS_FORGET_STRING_FLATNESS(flat));
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
char16_t ch = JS_GetFlatStringCharAt(flat, i);
|
||||
switch (ch) {
|
||||
case 'r':
|
||||
if (access & READ) {
|
||||
EnterAndThrowASCII(cx, wrapper, "duplicate 'readable' property flag");
|
||||
return false;
|
||||
}
|
||||
access = Access(access | READ);
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
if (access & WRITE) {
|
||||
EnterAndThrowASCII(cx, wrapper, "duplicate 'writable' property flag");
|
||||
return false;
|
||||
}
|
||||
access = Access(access | WRITE);
|
||||
break;
|
||||
|
||||
default:
|
||||
EnterAndThrowASCII(cx, wrapper, "properties can only be readable or read and writable");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (access == NO_ACCESS) {
|
||||
EnterAndThrowASCII(cx, wrapper, "specified properties must have a permission bit set");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((act == Wrapper::SET && !(access & WRITE)) ||
|
||||
(act != Wrapper::SET && !(access & READ))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Inspect the property on the underlying object to check for red flags.
|
||||
if (!JS_GetPropertyDescriptorById(cx, wrappedObject, id, &desc))
|
||||
return false;
|
||||
|
||||
// Reject accessor properties.
|
||||
if (desc.hasGetterOrSetter()) {
|
||||
EnterAndThrowASCII(cx, wrapper, "Exposing privileged accessor properties is prohibited");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reject privileged or cross-origin callables.
|
||||
if (desc.value().isObject()) {
|
||||
RootedObject maybeCallable(cx, js::UncheckedUnwrap(&desc.value().toObject()));
|
||||
if (JS::IsCallable(maybeCallable) && !AccessCheck::subsumes(wrapper, maybeCallable)) {
|
||||
EnterAndThrowASCII(cx, wrapper, "Exposing privileged or cross-origin callable is prohibited");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ExposedPropertiesOnly::deny(js::Wrapper::Action act, HandleId id)
|
||||
{
|
||||
// Fail silently for GET, ENUMERATE, and GET_PROPERTY_DESCRIPTOR.
|
||||
if (act == js::Wrapper::GET || act == js::Wrapper::ENUMERATE ||
|
||||
act == js::Wrapper::GET_PROPERTY_DESCRIPTOR)
|
||||
{
|
||||
AutoJSContext cx;
|
||||
return ReportWrapperDenial(cx, id, WrapperDenialForCOW,
|
||||
"Access to privileged JS object not permitted");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace xpc
|
||||
106
js/xpconnect/wrappers/AccessCheck.h
Normal file
106
js/xpconnect/wrappers/AccessCheck.h
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __AccessCheck_h__
|
||||
#define __AccessCheck_h__
|
||||
|
||||
#include "jswrapper.h"
|
||||
#include "js/Id.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace xpc {
|
||||
|
||||
class AccessCheck {
|
||||
public:
|
||||
static bool subsumes(JSCompartment* a, JSCompartment* b);
|
||||
static bool subsumes(JSObject* a, JSObject* b);
|
||||
static bool wrapperSubsumes(JSObject* wrapper);
|
||||
static bool subsumesConsideringDomain(JSCompartment* a, JSCompartment* b);
|
||||
static bool isChrome(JSCompartment* compartment);
|
||||
static bool isChrome(JSObject* obj);
|
||||
static nsIPrincipal* getPrincipal(JSCompartment* compartment);
|
||||
static bool isCrossOriginAccessPermitted(JSContext* cx, JS::HandleObject obj,
|
||||
JS::HandleId id, js::Wrapper::Action act);
|
||||
static bool checkPassToPrivilegedCode(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleValue value);
|
||||
static bool checkPassToPrivilegedCode(JSContext* cx, JS::HandleObject wrapper,
|
||||
const JS::CallArgs& args);
|
||||
};
|
||||
|
||||
enum CrossOriginObjectType {
|
||||
CrossOriginWindow,
|
||||
CrossOriginLocation,
|
||||
CrossOriginOpaque
|
||||
};
|
||||
CrossOriginObjectType IdentifyCrossOriginObject(JSObject* obj);
|
||||
|
||||
struct Policy {
|
||||
static bool checkCall(JSContext* cx, JS::HandleObject wrapper, const JS::CallArgs& args) {
|
||||
MOZ_CRASH("As a rule, filtering wrappers are non-callable");
|
||||
}
|
||||
};
|
||||
|
||||
// This policy allows no interaction with the underlying callable. Everything throws.
|
||||
struct Opaque : public Policy {
|
||||
static bool check(JSContext* cx, JSObject* wrapper, jsid id, js::Wrapper::Action act) {
|
||||
return false;
|
||||
}
|
||||
static bool deny(js::Wrapper::Action act, JS::HandleId id) {
|
||||
return false;
|
||||
}
|
||||
static bool allowNativeCall(JSContext* cx, JS::IsAcceptableThis test, JS::NativeImpl impl) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Like the above, but allows CALL.
|
||||
struct OpaqueWithCall : public Policy {
|
||||
static bool check(JSContext* cx, JSObject* wrapper, jsid id, js::Wrapper::Action act) {
|
||||
return act == js::Wrapper::CALL;
|
||||
}
|
||||
static bool deny(js::Wrapper::Action act, JS::HandleId id) {
|
||||
return false;
|
||||
}
|
||||
static bool allowNativeCall(JSContext* cx, JS::IsAcceptableThis test, JS::NativeImpl impl) {
|
||||
return false;
|
||||
}
|
||||
static bool checkCall(JSContext* cx, JS::HandleObject wrapper, const JS::CallArgs& args) {
|
||||
return AccessCheck::checkPassToPrivilegedCode(cx, wrapper, args);
|
||||
}
|
||||
};
|
||||
|
||||
// This policy only permits access to properties that are safe to be used
|
||||
// across origins.
|
||||
struct CrossOriginAccessiblePropertiesOnly : public Policy {
|
||||
static bool check(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, js::Wrapper::Action act) {
|
||||
return AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act);
|
||||
}
|
||||
static bool deny(js::Wrapper::Action act, JS::HandleId id) {
|
||||
// Silently fail for enumerate-like operations.
|
||||
if (act == js::Wrapper::ENUMERATE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
static bool allowNativeCall(JSContext* cx, JS::IsAcceptableThis test, JS::NativeImpl impl) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// This policy only permits access to properties if they appear in the
|
||||
// objects exposed properties list.
|
||||
struct ExposedPropertiesOnly : public Policy {
|
||||
static bool check(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, js::Wrapper::Action act);
|
||||
|
||||
static bool deny(js::Wrapper::Action act, JS::HandleId id);
|
||||
static bool allowNativeCall(JSContext* cx, JS::IsAcceptableThis test, JS::NativeImpl impl) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
#endif /* __AccessCheck_h__ */
|
||||
270
js/xpconnect/wrappers/AddonWrapper.cpp
Normal file
270
js/xpconnect/wrappers/AddonWrapper.cpp
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AddonWrapper.h"
|
||||
#include "WrapperFactory.h"
|
||||
#include "XrayWrapper.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "nsIAddonInterposition.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
|
||||
#include "nsID.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace JS;
|
||||
|
||||
namespace xpc {
|
||||
|
||||
bool
|
||||
InterposeProperty(JSContext* cx, HandleObject target, const nsIID* iid, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> descriptor)
|
||||
{
|
||||
// We only want to do interpostion on DOM instances and
|
||||
// wrapped natives.
|
||||
RootedObject unwrapped(cx, UncheckedUnwrap(target));
|
||||
const js::Class* clasp = js::GetObjectClass(unwrapped);
|
||||
bool isCPOW = jsipc::IsWrappedCPOW(unwrapped);
|
||||
if (!mozilla::dom::IsDOMClass(clasp) &&
|
||||
!IS_WN_CLASS(clasp) &&
|
||||
!IS_PROTO_CLASS(clasp) &&
|
||||
clasp != &OuterWindowProxyClass &&
|
||||
!isCPOW) {
|
||||
return true;
|
||||
}
|
||||
|
||||
XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(cx));
|
||||
MOZ_ASSERT(scope->HasInterposition());
|
||||
|
||||
nsCOMPtr<nsIAddonInterposition> interp = scope->GetInterposition();
|
||||
InterpositionWhitelist* wl = XPCWrappedNativeScope::GetInterpositionWhitelist(interp);
|
||||
// We do InterposeProperty only if the id is on the whitelist of the interpostion
|
||||
// or if the target is a CPOW.
|
||||
if ((!wl || !wl->has(JSID_BITS(id.get()))) && !isCPOW)
|
||||
return true;
|
||||
|
||||
JSAddonId* addonId = AddonIdOfObject(target);
|
||||
RootedValue addonIdValue(cx, StringValue(StringOfAddonId(addonId)));
|
||||
RootedValue prop(cx, IdToValue(id));
|
||||
RootedValue targetValue(cx, ObjectValue(*target));
|
||||
RootedValue descriptorVal(cx);
|
||||
nsresult rv = interp->InterposeProperty(addonIdValue, targetValue,
|
||||
iid, prop, &descriptorVal);
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc::Throw(cx, rv);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!descriptorVal.isObject())
|
||||
return true;
|
||||
|
||||
// We need to be careful parsing descriptorVal. |cx| is in the compartment
|
||||
// of the add-on and the descriptor is in the compartment of the
|
||||
// interposition. We could wrap the descriptor in the add-on's compartment
|
||||
// and then parse it. However, parsing the descriptor fetches properties
|
||||
// from it, and we would try to interpose on those property accesses. So
|
||||
// instead we parse in the interposition's compartment and then wrap the
|
||||
// descriptor.
|
||||
|
||||
{
|
||||
JSAutoCompartment ac(cx, &descriptorVal.toObject());
|
||||
if (!JS::ObjectToCompletePropertyDescriptor(cx, target, descriptorVal, descriptor))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Always make the property non-configurable regardless of what the
|
||||
// interposition wants.
|
||||
descriptor.setAttributes(descriptor.attributes() | JSPROP_PERMANENT);
|
||||
|
||||
if (!JS_WrapPropertyDescriptor(cx, descriptor))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterposeCall(JSContext* cx, JS::HandleObject target, const JS::CallArgs& args, bool* done)
|
||||
{
|
||||
*done = false;
|
||||
XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(cx));
|
||||
MOZ_ASSERT(scope->HasInterposition());
|
||||
|
||||
nsCOMPtr<nsIAddonInterposition> interp = scope->GetInterposition();
|
||||
|
||||
RootedObject unwrappedTarget(cx, UncheckedUnwrap(target));
|
||||
XPCWrappedNativeScope* targetScope = ObjectScope(unwrappedTarget);
|
||||
bool hasInterpostion = targetScope->HasCallInterposition();
|
||||
|
||||
if (!hasInterpostion)
|
||||
return true;
|
||||
|
||||
// If there is a call interpostion, we don't want to propogate the
|
||||
// call to Base:
|
||||
*done = true;
|
||||
|
||||
JSAddonId* addonId = AddonIdOfObject(target);
|
||||
RootedValue addonIdValue(cx, StringValue(StringOfAddonId(addonId)));
|
||||
RootedValue targetValue(cx, ObjectValue(*target));
|
||||
RootedValue thisValue(cx, args.thisv());
|
||||
RootedObject argsArray(cx, ConvertArgsToArray(cx, args));
|
||||
if (!argsArray)
|
||||
return false;
|
||||
|
||||
RootedValue argsVal(cx, ObjectValue(*argsArray));
|
||||
RootedValue returnVal(cx);
|
||||
|
||||
nsresult rv = interp->InterposeCall(addonIdValue, targetValue,
|
||||
thisValue, argsVal, args.rval());
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc::Throw(cx, rv);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
bool AddonWrapper<Base>::call(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
const JS::CallArgs& args) const
|
||||
{
|
||||
bool done = false;
|
||||
if (!InterposeCall(cx, wrapper, args, &done))
|
||||
return false;
|
||||
|
||||
return done || Base::call(cx, wrapper, args);
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
bool
|
||||
AddonWrapper<Base>::getPropertyDescriptor(JSContext* cx, HandleObject wrapper,
|
||||
HandleId id, MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
if (!InterposeProperty(cx, wrapper, nullptr, id, desc))
|
||||
return false;
|
||||
|
||||
if (desc.object())
|
||||
return true;
|
||||
|
||||
return Base::getPropertyDescriptor(cx, wrapper, id, desc);
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
bool
|
||||
AddonWrapper<Base>::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper,
|
||||
HandleId id, MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
if (!InterposeProperty(cx, wrapper, nullptr, id, desc))
|
||||
return false;
|
||||
|
||||
if (desc.object())
|
||||
return true;
|
||||
|
||||
return Base::getOwnPropertyDescriptor(cx, wrapper, id, desc);
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
bool
|
||||
AddonWrapper<Base>::get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<Value> receiver,
|
||||
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const
|
||||
{
|
||||
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
|
||||
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!InterposeProperty(cx, wrapper, nullptr, id, &desc))
|
||||
return false;
|
||||
|
||||
if (!desc.object())
|
||||
return Base::get(cx, wrapper, receiver, id, vp);
|
||||
|
||||
if (desc.getter()) {
|
||||
return Call(cx, receiver, desc.getterObject(), HandleValueArray::empty(), vp);
|
||||
} else {
|
||||
vp.set(desc.value());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
bool
|
||||
AddonWrapper<Base>::set(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::HandleValue v,
|
||||
JS::HandleValue receiver, JS::ObjectOpResult& result) const
|
||||
{
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!InterposeProperty(cx, wrapper, nullptr, id, &desc))
|
||||
return false;
|
||||
|
||||
if (!desc.object())
|
||||
return Base::set(cx, wrapper, id, v, receiver, result);
|
||||
|
||||
if (desc.setter()) {
|
||||
MOZ_ASSERT(desc.hasSetterObject());
|
||||
JS::AutoValueVector args(cx);
|
||||
if (!args.append(v))
|
||||
return false;
|
||||
RootedValue fval(cx, ObjectValue(*desc.setterObject()));
|
||||
RootedValue ignored(cx);
|
||||
if (!JS::Call(cx, receiver, fval, args, &ignored))
|
||||
return false;
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
return result.failCantSetInterposed();
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
bool
|
||||
AddonWrapper<Base>::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result) const
|
||||
{
|
||||
Rooted<PropertyDescriptor> interpDesc(cx);
|
||||
if (!InterposeProperty(cx, wrapper, nullptr, id, &interpDesc))
|
||||
return false;
|
||||
|
||||
if (!interpDesc.object())
|
||||
return Base::defineProperty(cx, wrapper, id, desc, result);
|
||||
|
||||
js::ReportASCIIErrorWithId(cx, "unable to modify interposed property %s", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Base>
|
||||
bool
|
||||
AddonWrapper<Base>::delete_(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
ObjectOpResult& result) const
|
||||
{
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
if (!InterposeProperty(cx, wrapper, nullptr, id, &desc))
|
||||
return false;
|
||||
|
||||
if (!desc.object())
|
||||
return Base::delete_(cx, wrapper, id, result);
|
||||
|
||||
js::ReportASCIIErrorWithId(cx, "unable to delete interposed property %s", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
#define AddonWrapperCC AddonWrapper<CrossCompartmentWrapper>
|
||||
#define AddonWrapperXrayXPCWN AddonWrapper<PermissiveXrayXPCWN>
|
||||
#define AddonWrapperXrayDOM AddonWrapper<PermissiveXrayDOM>
|
||||
|
||||
template<> const AddonWrapperCC AddonWrapperCC::singleton(0);
|
||||
template<> const AddonWrapperXrayXPCWN AddonWrapperXrayXPCWN::singleton(0);
|
||||
template<> const AddonWrapperXrayDOM AddonWrapperXrayDOM::singleton(0);
|
||||
|
||||
template class AddonWrapperCC;
|
||||
template class AddonWrapperXrayXPCWN;
|
||||
template class AddonWrapperXrayDOM;
|
||||
|
||||
#undef AddonWrapperCC
|
||||
#undef AddonWrapperXrayXPCWN
|
||||
#undef AddonWrapperXrayDOM
|
||||
|
||||
} // namespace xpc
|
||||
55
js/xpconnect/wrappers/AddonWrapper.h
Normal file
55
js/xpconnect/wrappers/AddonWrapper.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef AddonWrapper_h
|
||||
#define AddonWrapper_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "nsID.h"
|
||||
|
||||
#include "jswrapper.h"
|
||||
|
||||
namespace xpc {
|
||||
|
||||
bool
|
||||
InterposeProperty(JSContext* cx, JS::HandleObject target, const nsIID* iid, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> descriptor);
|
||||
|
||||
bool
|
||||
InterposeCall(JSContext* cx, JS::HandleObject wrapper,
|
||||
const JS::CallArgs& args, bool& done);
|
||||
|
||||
template<typename Base>
|
||||
class AddonWrapper : public Base {
|
||||
public:
|
||||
explicit constexpr AddonWrapper(unsigned flags) : Base(flags) { }
|
||||
|
||||
virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::ObjectOpResult& result) const override;
|
||||
virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
|
||||
JS::ObjectOpResult& result) const override;
|
||||
virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JS::Value> receiver,
|
||||
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const override;
|
||||
virtual bool set(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::HandleValue v,
|
||||
JS::HandleValue receiver, JS::ObjectOpResult& result) const override;
|
||||
virtual bool call(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
const JS::CallArgs& args) const override;
|
||||
|
||||
virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
|
||||
static const AddonWrapper singleton;
|
||||
};
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
#endif // AddonWrapper_h
|
||||
41
js/xpconnect/wrappers/ChromeObjectWrapper.cpp
Normal file
41
js/xpconnect/wrappers/ChromeObjectWrapper.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ChromeObjectWrapper.h"
|
||||
#include "WrapperFactory.h"
|
||||
#include "AccessCheck.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "jsapi.h"
|
||||
#include "jswrapper.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
using namespace JS;
|
||||
|
||||
namespace xpc {
|
||||
|
||||
const ChromeObjectWrapper ChromeObjectWrapper::singleton;
|
||||
|
||||
bool
|
||||
ChromeObjectWrapper::defineProperty(JSContext* cx, HandleObject wrapper,
|
||||
HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result) const
|
||||
{
|
||||
if (!AccessCheck::checkPassToPrivilegedCode(cx, wrapper, desc.value()))
|
||||
return false;
|
||||
return ChromeObjectWrapperBase::defineProperty(cx, wrapper, id, desc, result);
|
||||
}
|
||||
|
||||
bool
|
||||
ChromeObjectWrapper::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
|
||||
HandleValue receiver, ObjectOpResult& result) const
|
||||
{
|
||||
if (!AccessCheck::checkPassToPrivilegedCode(cx, wrapper, v))
|
||||
return false;
|
||||
return ChromeObjectWrapperBase::set(cx, wrapper, id, v, receiver, result);
|
||||
}
|
||||
|
||||
} // namespace xpc
|
||||
43
js/xpconnect/wrappers/ChromeObjectWrapper.h
Normal file
43
js/xpconnect/wrappers/ChromeObjectWrapper.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __ChromeObjectWrapper_h__
|
||||
#define __ChromeObjectWrapper_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "FilteringWrapper.h"
|
||||
|
||||
namespace xpc {
|
||||
|
||||
struct ExposedPropertiesOnly;
|
||||
|
||||
// When a vanilla chrome JS object is exposed to content, we use a wrapper that
|
||||
// supports __exposedProps__ for legacy reasons. For extra security, we override
|
||||
// the traps that allow content to pass an object to chrome, and perform extra
|
||||
// security checks on them.
|
||||
#define ChromeObjectWrapperBase \
|
||||
FilteringWrapper<js::CrossCompartmentSecurityWrapper, ExposedPropertiesOnly>
|
||||
|
||||
class ChromeObjectWrapper : public ChromeObjectWrapperBase
|
||||
{
|
||||
public:
|
||||
constexpr ChromeObjectWrapper() : ChromeObjectWrapperBase(0) {}
|
||||
|
||||
virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::ObjectOpResult& result) const override;
|
||||
virtual bool set(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
|
||||
JS::HandleValue v, JS::HandleValue receiver,
|
||||
JS::ObjectOpResult& result) const override;
|
||||
|
||||
static const ChromeObjectWrapper singleton;
|
||||
};
|
||||
|
||||
} /* namespace xpc */
|
||||
|
||||
#endif /* __ChromeObjectWrapper_h__ */
|
||||
312
js/xpconnect/wrappers/FilteringWrapper.cpp
Normal file
312
js/xpconnect/wrappers/FilteringWrapper.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "FilteringWrapper.h"
|
||||
#include "AccessCheck.h"
|
||||
#include "ChromeObjectWrapper.h"
|
||||
#include "XrayWrapper.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
using namespace JS;
|
||||
using namespace js;
|
||||
|
||||
namespace xpc {
|
||||
|
||||
static JS::SymbolCode sCrossOriginWhitelistedSymbolCodes[] = {
|
||||
JS::SymbolCode::toStringTag,
|
||||
JS::SymbolCode::hasInstance,
|
||||
JS::SymbolCode::isConcatSpreadable
|
||||
};
|
||||
|
||||
bool
|
||||
IsCrossOriginWhitelistedSymbol(JSContext* cx, JS::HandleId id)
|
||||
{
|
||||
if (!JSID_IS_SYMBOL(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Symbol* symbol = JSID_TO_SYMBOL(id);
|
||||
for (auto code : sCrossOriginWhitelistedSymbolCodes) {
|
||||
if (symbol == JS::GetWellKnownSymbol(cx, code)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Policy>
|
||||
static bool
|
||||
Filter(JSContext* cx, HandleObject wrapper, AutoIdVector& props)
|
||||
{
|
||||
size_t w = 0;
|
||||
RootedId id(cx);
|
||||
for (size_t n = 0; n < props.length(); ++n) {
|
||||
id = props[n];
|
||||
if (Policy::check(cx, wrapper, id, Wrapper::GET) || Policy::check(cx, wrapper, id, Wrapper::SET))
|
||||
props[w++].set(id);
|
||||
else if (JS_IsExceptionPending(cx))
|
||||
return false;
|
||||
}
|
||||
if (!props.resize(w))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Policy>
|
||||
static bool
|
||||
FilterPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, MutableHandle<PropertyDescriptor> desc)
|
||||
{
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
bool getAllowed = Policy::check(cx, wrapper, id, Wrapper::GET);
|
||||
if (JS_IsExceptionPending(cx))
|
||||
return false;
|
||||
bool setAllowed = Policy::check(cx, wrapper, id, Wrapper::SET);
|
||||
if (JS_IsExceptionPending(cx))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(getAllowed || setAllowed,
|
||||
"Filtering policy should not allow GET_PROPERTY_DESCRIPTOR in this case");
|
||||
|
||||
if (!desc.hasGetterOrSetter()) {
|
||||
// Handle value properties.
|
||||
if (!getAllowed)
|
||||
desc.value().setUndefined();
|
||||
} else {
|
||||
// Handle accessor properties.
|
||||
MOZ_ASSERT(desc.value().isUndefined());
|
||||
if (!getAllowed)
|
||||
desc.setGetter(nullptr);
|
||||
if (!setAllowed)
|
||||
desc.setSetter(nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
FilteringWrapper<Base, Policy>::getPropertyDescriptor(JSContext* cx, HandleObject wrapper,
|
||||
HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET |
|
||||
BaseProxyHandler::GET_PROPERTY_DESCRIPTOR);
|
||||
if (!Base::getPropertyDescriptor(cx, wrapper, id, desc))
|
||||
return false;
|
||||
return FilterPropertyDescriptor<Policy>(cx, wrapper, id, desc);
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
FilteringWrapper<Base, Policy>::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper,
|
||||
HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET |
|
||||
BaseProxyHandler::GET_PROPERTY_DESCRIPTOR);
|
||||
if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc))
|
||||
return false;
|
||||
return FilterPropertyDescriptor<Policy>(cx, wrapper, id, desc);
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
FilteringWrapper<Base, Policy>::ownPropertyKeys(JSContext* cx, HandleObject wrapper,
|
||||
AutoIdVector& props) const
|
||||
{
|
||||
assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
|
||||
return Base::ownPropertyKeys(cx, wrapper, props) &&
|
||||
Filter<Policy>(cx, wrapper, props);
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
FilteringWrapper<Base, Policy>::getOwnEnumerablePropertyKeys(JSContext* cx,
|
||||
HandleObject wrapper,
|
||||
AutoIdVector& props) const
|
||||
{
|
||||
assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
|
||||
return Base::getOwnEnumerablePropertyKeys(cx, wrapper, props) &&
|
||||
Filter<Policy>(cx, wrapper, props);
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
FilteringWrapper<Base, Policy>::enumerate(JSContext* cx, HandleObject wrapper,
|
||||
MutableHandleObject objp) const
|
||||
{
|
||||
assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
|
||||
// We refuse to trigger the enumerate hook across chrome wrappers because
|
||||
// we don't know how to censor custom iterator objects. Instead we trigger
|
||||
// the default proxy enumerate trap, which will use js::GetPropertyKeys
|
||||
// for the list of (censored) ids.
|
||||
return js::BaseProxyHandler::enumerate(cx, wrapper, objp);
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
FilteringWrapper<Base, Policy>::call(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
const JS::CallArgs& args) const
|
||||
{
|
||||
if (!Policy::checkCall(cx, wrapper, args))
|
||||
return false;
|
||||
return Base::call(cx, wrapper, args);
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
FilteringWrapper<Base, Policy>::construct(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
const JS::CallArgs& args) const
|
||||
{
|
||||
if (!Policy::checkCall(cx, wrapper, args))
|
||||
return false;
|
||||
return Base::construct(cx, wrapper, args);
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
FilteringWrapper<Base, Policy>::nativeCall(JSContext* cx, JS::IsAcceptableThis test,
|
||||
JS::NativeImpl impl, const JS::CallArgs& args) const
|
||||
{
|
||||
if (Policy::allowNativeCall(cx, test, impl))
|
||||
return Base::Permissive::nativeCall(cx, test, impl, args);
|
||||
return Base::Restrictive::nativeCall(cx, test, impl, args);
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
FilteringWrapper<Base, Policy>::getPrototype(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::MutableHandleObject protop) const
|
||||
{
|
||||
// Filtering wrappers do not allow access to the prototype.
|
||||
protop.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
FilteringWrapper<Base, Policy>::enter(JSContext* cx, HandleObject wrapper,
|
||||
HandleId id, Wrapper::Action act, bool* bp) const
|
||||
{
|
||||
if (!Policy::check(cx, wrapper, id, act)) {
|
||||
*bp = JS_IsExceptionPending(cx) ? false : Policy::deny(act, id);
|
||||
return false;
|
||||
}
|
||||
*bp = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CrossOriginXrayWrapper::getPropertyDescriptor(JSContext* cx,
|
||||
JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
if (!SecurityXrayDOM::getPropertyDescriptor(cx, wrapper, id, desc))
|
||||
return false;
|
||||
if (desc.object()) {
|
||||
// Cross-origin DOM objects do not have symbol-named properties apart
|
||||
// from the ones we add ourselves here.
|
||||
MOZ_ASSERT(!JSID_IS_SYMBOL(id),
|
||||
"What's this symbol-named property that appeared on a "
|
||||
"Window or Location instance?");
|
||||
|
||||
// All properties on cross-origin DOM objects are |own|.
|
||||
desc.object().set(wrapper);
|
||||
|
||||
// All properties on cross-origin DOM objects are non-enumerable and
|
||||
// "configurable". Any value attributes are read-only.
|
||||
desc.attributesRef() &= ~JSPROP_ENUMERATE;
|
||||
desc.attributesRef() &= ~JSPROP_PERMANENT;
|
||||
if (!desc.getter() && !desc.setter())
|
||||
desc.attributesRef() |= JSPROP_READONLY;
|
||||
} else if (IsCrossOriginWhitelistedSymbol(cx, id)) {
|
||||
// Spec says to return PropertyDescriptor {
|
||||
// [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false,
|
||||
// [[Configurable]]: true
|
||||
// }.
|
||||
//
|
||||
desc.setDataDescriptor(JS::UndefinedHandleValue, JSPROP_READONLY);
|
||||
desc.object().set(wrapper);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CrossOriginXrayWrapper::getOwnPropertyDescriptor(JSContext* cx,
|
||||
JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
// All properties on cross-origin DOM objects are |own|.
|
||||
return getPropertyDescriptor(cx, wrapper, id, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
CrossOriginXrayWrapper::ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::AutoIdVector& props) const
|
||||
{
|
||||
// All properties on cross-origin objects are supposed |own|, despite what
|
||||
// the underlying native object may report. Override the inherited trap to
|
||||
// avoid passing JSITER_OWNONLY as a flag.
|
||||
if (!SecurityXrayDOM::getPropertyKeys(cx, wrapper, JSITER_HIDDEN, props)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now add the three symbol-named props cross-origin objects have.
|
||||
#ifdef DEBUG
|
||||
for (size_t n = 0; n < props.length(); ++n) {
|
||||
MOZ_ASSERT(!JSID_IS_SYMBOL(props[n]),
|
||||
"Unexpected existing symbol-name prop");
|
||||
}
|
||||
#endif
|
||||
if (!props.reserve(props.length() +
|
||||
ArrayLength(sCrossOriginWhitelistedSymbolCodes))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto code : sCrossOriginWhitelistedSymbolCodes) {
|
||||
props.infallibleAppend(SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, code)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CrossOriginXrayWrapper::defineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::Handle<PropertyDescriptor> desc,
|
||||
JS::ObjectOpResult& result) const
|
||||
{
|
||||
JS_ReportErrorASCII(cx, "Permission denied to define property on cross-origin object");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CrossOriginXrayWrapper::delete_(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id, JS::ObjectOpResult& result) const
|
||||
{
|
||||
JS_ReportErrorASCII(cx, "Permission denied to delete property on cross-origin object");
|
||||
return false;
|
||||
}
|
||||
|
||||
#define XOW FilteringWrapper<CrossOriginXrayWrapper, CrossOriginAccessiblePropertiesOnly>
|
||||
#define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>
|
||||
#define NNXOWC FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>
|
||||
|
||||
template<> const XOW XOW::singleton(0);
|
||||
template<> const NNXOW NNXOW::singleton(0);
|
||||
template<> const NNXOWC NNXOWC::singleton(0);
|
||||
|
||||
template class XOW;
|
||||
template class NNXOW;
|
||||
template class NNXOWC;
|
||||
template class ChromeObjectWrapperBase;
|
||||
} // namespace xpc
|
||||
91
js/xpconnect/wrappers/FilteringWrapper.h
Normal file
91
js/xpconnect/wrappers/FilteringWrapper.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __FilteringWrapper_h__
|
||||
#define __FilteringWrapper_h__
|
||||
|
||||
#include "XrayWrapper.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "jswrapper.h"
|
||||
#include "js/CallNonGenericMethod.h"
|
||||
|
||||
namespace JS {
|
||||
class AutoIdVector;
|
||||
} // namespace JS
|
||||
|
||||
namespace xpc {
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
class FilteringWrapper : public Base {
|
||||
public:
|
||||
constexpr explicit FilteringWrapper(unsigned flags) : Base(flags) {}
|
||||
|
||||
virtual bool enter(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
|
||||
js::Wrapper::Action act, bool* bp) const override;
|
||||
|
||||
virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
virtual bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::AutoIdVector& props) const override;
|
||||
|
||||
virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::AutoIdVector& props) const override;
|
||||
virtual bool enumerate(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::MutableHandle<JSObject*> objp) const override;
|
||||
|
||||
virtual bool call(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
const JS::CallArgs& args) const override;
|
||||
virtual bool construct(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
const JS::CallArgs& args) const override;
|
||||
|
||||
virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test, JS::NativeImpl impl,
|
||||
const JS::CallArgs& args) const override;
|
||||
|
||||
virtual bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::MutableHandleObject protop) const override;
|
||||
|
||||
static const FilteringWrapper singleton;
|
||||
};
|
||||
|
||||
/*
|
||||
* The HTML5 spec mandates very particular object behavior for cross-origin DOM
|
||||
* objects (Window and Location), some of which runs contrary to the way that
|
||||
* other XrayWrappers behave. We use this class to implement those semantics.
|
||||
*/
|
||||
class CrossOriginXrayWrapper : public SecurityXrayDOM {
|
||||
public:
|
||||
constexpr explicit CrossOriginXrayWrapper(unsigned flags) :
|
||||
SecurityXrayDOM(flags) {}
|
||||
|
||||
|
||||
virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::ObjectOpResult& result) const override;
|
||||
virtual bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::AutoIdVector& props) const override;
|
||||
virtual bool delete_(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id, JS::ObjectOpResult& result) const override;
|
||||
|
||||
virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
};
|
||||
|
||||
// Check whether the given jsid is a symbol whose value can be gotten
|
||||
// cross-origin. Cross-origin gets always return undefined as the value.
|
||||
bool IsCrossOriginWhitelistedSymbol(JSContext* cx, JS::HandleId id);
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
#endif /* __FilteringWrapper_h__ */
|
||||
105
js/xpconnect/wrappers/WaiveXrayWrapper.cpp
Normal file
105
js/xpconnect/wrappers/WaiveXrayWrapper.cpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WaiveXrayWrapper.h"
|
||||
#include "WrapperFactory.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
using namespace JS;
|
||||
|
||||
namespace xpc {
|
||||
|
||||
static bool
|
||||
WaiveAccessors(JSContext* cx, MutableHandle<PropertyDescriptor> desc)
|
||||
{
|
||||
if (desc.hasGetterObject() && desc.getterObject()) {
|
||||
RootedValue v(cx, JS::ObjectValue(*desc.getterObject()));
|
||||
if (!WrapperFactory::WaiveXrayAndWrap(cx, &v))
|
||||
return false;
|
||||
desc.setGetterObject(&v.toObject());
|
||||
}
|
||||
|
||||
if (desc.hasSetterObject() && desc.setterObject()) {
|
||||
RootedValue v(cx, JS::ObjectValue(*desc.setterObject()));
|
||||
if (!WrapperFactory::WaiveXrayAndWrap(cx, &v))
|
||||
return false;
|
||||
desc.setSetterObject(&v.toObject());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WaiveXrayWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
return CrossCompartmentWrapper::getPropertyDescriptor(cx, wrapper, id, desc) &&
|
||||
WrapperFactory::WaiveXrayAndWrap(cx, desc.value()) && WaiveAccessors(cx, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
WaiveXrayWrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
|
||||
MutableHandle<PropertyDescriptor> desc) const
|
||||
{
|
||||
return CrossCompartmentWrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc) &&
|
||||
WrapperFactory::WaiveXrayAndWrap(cx, desc.value()) && WaiveAccessors(cx, desc);
|
||||
}
|
||||
|
||||
bool
|
||||
WaiveXrayWrapper::get(JSContext* cx, HandleObject wrapper, HandleValue receiver, HandleId id,
|
||||
MutableHandleValue vp) const
|
||||
{
|
||||
return CrossCompartmentWrapper::get(cx, wrapper, receiver, id, vp) &&
|
||||
WrapperFactory::WaiveXrayAndWrap(cx, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
WaiveXrayWrapper::enumerate(JSContext* cx, HandleObject proxy,
|
||||
MutableHandleObject objp) const
|
||||
{
|
||||
return CrossCompartmentWrapper::enumerate(cx, proxy, objp) &&
|
||||
WrapperFactory::WaiveXrayAndWrap(cx, objp);
|
||||
}
|
||||
|
||||
bool
|
||||
WaiveXrayWrapper::call(JSContext* cx, HandleObject wrapper, const JS::CallArgs& args) const
|
||||
{
|
||||
return CrossCompartmentWrapper::call(cx, wrapper, args) &&
|
||||
WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
|
||||
}
|
||||
|
||||
bool
|
||||
WaiveXrayWrapper::construct(JSContext* cx, HandleObject wrapper, const JS::CallArgs& args) const
|
||||
{
|
||||
return CrossCompartmentWrapper::construct(cx, wrapper, args) &&
|
||||
WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
|
||||
}
|
||||
|
||||
// NB: This is important as the other side of a handshake with FieldGetter. See
|
||||
// nsXBLProtoImplField.cpp.
|
||||
bool
|
||||
WaiveXrayWrapper::nativeCall(JSContext* cx, JS::IsAcceptableThis test,
|
||||
JS::NativeImpl impl, const JS::CallArgs& args) const
|
||||
{
|
||||
return CrossCompartmentWrapper::nativeCall(cx, test, impl, args) &&
|
||||
WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
|
||||
}
|
||||
|
||||
bool
|
||||
WaiveXrayWrapper::getPrototype(JSContext* cx, HandleObject wrapper, MutableHandleObject protop) const
|
||||
{
|
||||
return CrossCompartmentWrapper::getPrototype(cx, wrapper, protop) &&
|
||||
(!protop || WrapperFactory::WaiveXrayAndWrap(cx, protop));
|
||||
}
|
||||
|
||||
bool
|
||||
WaiveXrayWrapper::getPrototypeIfOrdinary(JSContext* cx, HandleObject wrapper, bool* isOrdinary,
|
||||
MutableHandleObject protop) const
|
||||
{
|
||||
return CrossCompartmentWrapper::getPrototypeIfOrdinary(cx, wrapper, isOrdinary, protop) &&
|
||||
(!protop || WrapperFactory::WaiveXrayAndWrap(cx, protop));
|
||||
}
|
||||
|
||||
} // namespace xpc
|
||||
48
js/xpconnect/wrappers/WaiveXrayWrapper.h
Normal file
48
js/xpconnect/wrappers/WaiveXrayWrapper.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __CrossOriginWrapper_h__
|
||||
#define __CrossOriginWrapper_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "jswrapper.h"
|
||||
|
||||
namespace xpc {
|
||||
|
||||
class WaiveXrayWrapper : public js::CrossCompartmentWrapper {
|
||||
public:
|
||||
explicit constexpr WaiveXrayWrapper(unsigned flags) : js::CrossCompartmentWrapper(flags) { }
|
||||
|
||||
virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
virtual bool getPrototype(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::MutableHandle<JSObject*> protop) const override;
|
||||
virtual bool getPrototypeIfOrdinary(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
bool* isOrdinary,
|
||||
JS::MutableHandle<JSObject*> protop) const override;
|
||||
virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JS::Value> receiver,
|
||||
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const override;
|
||||
virtual bool call(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
const JS::CallArgs& args) const override;
|
||||
virtual bool construct(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
const JS::CallArgs& args) const override;
|
||||
|
||||
virtual bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::MutableHandle<JSObject*> objp) const override;
|
||||
virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test,
|
||||
JS::NativeImpl impl, const JS::CallArgs& args) const override;
|
||||
virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
|
||||
static const WaiveXrayWrapper singleton;
|
||||
};
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
#endif
|
||||
671
js/xpconnect/wrappers/WrapperFactory.cpp
Normal file
671
js/xpconnect/wrappers/WrapperFactory.cpp
Normal file
|
|
@ -0,0 +1,671 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WaiveXrayWrapper.h"
|
||||
#include "FilteringWrapper.h"
|
||||
#include "AddonWrapper.h"
|
||||
#include "XrayWrapper.h"
|
||||
#include "AccessCheck.h"
|
||||
#include "XPCWrapper.h"
|
||||
#include "ChromeObjectWrapper.h"
|
||||
#include "WrapperFactory.h"
|
||||
|
||||
#include "xpcprivate.h"
|
||||
#include "XPCMaps.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
using namespace JS;
|
||||
using namespace js;
|
||||
using namespace mozilla;
|
||||
|
||||
namespace xpc {
|
||||
|
||||
// When chrome pulls a naked property across the membrane using
|
||||
// .wrappedJSObject, we want it to cross the membrane into the
|
||||
// chrome compartment without automatically being wrapped into an
|
||||
// X-ray wrapper. We achieve this by wrapping it into a special
|
||||
// transparent wrapper in the origin (non-chrome) compartment. When
|
||||
// an object with that special wrapper applied crosses into chrome,
|
||||
// we know to not apply an X-ray wrapper.
|
||||
const Wrapper XrayWaiver(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG);
|
||||
|
||||
// When objects for which we waived the X-ray wrapper cross into
|
||||
// chrome, we wrap them into a special cross-compartment wrapper
|
||||
// that transitively extends the waiver to all properties we get
|
||||
// off it.
|
||||
const WaiveXrayWrapper WaiveXrayWrapper::singleton(0);
|
||||
|
||||
bool
|
||||
WrapperFactory::IsCOW(JSObject* obj)
|
||||
{
|
||||
return IsWrapper(obj) &&
|
||||
Wrapper::wrapperHandler(obj) == &ChromeObjectWrapper::singleton;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
WrapperFactory::GetXrayWaiver(HandleObject obj)
|
||||
{
|
||||
// Object should come fully unwrapped but outerized.
|
||||
MOZ_ASSERT(obj == UncheckedUnwrap(obj));
|
||||
MOZ_ASSERT(!js::IsWindow(obj));
|
||||
XPCWrappedNativeScope* scope = ObjectScope(obj);
|
||||
MOZ_ASSERT(scope);
|
||||
|
||||
if (!scope->mWaiverWrapperMap)
|
||||
return nullptr;
|
||||
|
||||
return scope->mWaiverWrapperMap->Find(obj);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
WrapperFactory::CreateXrayWaiver(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
// The caller is required to have already done a lookup.
|
||||
// NB: This implictly performs the assertions of GetXrayWaiver.
|
||||
MOZ_ASSERT(!GetXrayWaiver(obj));
|
||||
XPCWrappedNativeScope* scope = ObjectScope(obj);
|
||||
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
JSObject* waiver = Wrapper::New(cx, obj, &XrayWaiver);
|
||||
if (!waiver)
|
||||
return nullptr;
|
||||
|
||||
// Add the new waiver to the map. It's important that we only ever have
|
||||
// one waiver for the lifetime of the target object.
|
||||
if (!scope->mWaiverWrapperMap) {
|
||||
scope->mWaiverWrapperMap =
|
||||
JSObject2JSObjectMap::newMap(XPC_WRAPPER_MAP_LENGTH);
|
||||
}
|
||||
if (!scope->mWaiverWrapperMap->Add(cx, obj, waiver))
|
||||
return nullptr;
|
||||
return waiver;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
WrapperFactory::WaiveXray(JSContext* cx, JSObject* objArg)
|
||||
{
|
||||
RootedObject obj(cx, objArg);
|
||||
obj = UncheckedUnwrap(obj);
|
||||
MOZ_ASSERT(!js::IsWindow(obj));
|
||||
|
||||
JSObject* waiver = GetXrayWaiver(obj);
|
||||
if (!waiver) {
|
||||
waiver = CreateXrayWaiver(cx, obj);
|
||||
}
|
||||
MOZ_ASSERT(!ObjectIsMarkedGray(waiver));
|
||||
return waiver;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
WrapperFactory::AllowWaiver(JSCompartment* target, JSCompartment* origin)
|
||||
{
|
||||
return CompartmentPrivate::Get(target)->allowWaivers &&
|
||||
AccessCheck::subsumes(target, origin);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
WrapperFactory::AllowWaiver(JSObject* wrapper) {
|
||||
MOZ_ASSERT(js::IsCrossCompartmentWrapper(wrapper));
|
||||
return AllowWaiver(js::GetObjectCompartment(wrapper),
|
||||
js::GetObjectCompartment(js::UncheckedUnwrap(wrapper)));
|
||||
}
|
||||
|
||||
inline bool
|
||||
ShouldWaiveXray(JSContext* cx, JSObject* originalObj)
|
||||
{
|
||||
unsigned flags;
|
||||
(void) js::UncheckedUnwrap(originalObj, /* stopAtWindowProxy = */ true, &flags);
|
||||
|
||||
// If the original object did not point through an Xray waiver, we're done.
|
||||
if (!(flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG))
|
||||
return false;
|
||||
|
||||
// If the original object was not a cross-compartment wrapper, that means
|
||||
// that the caller explicitly created a waiver. Preserve it so that things
|
||||
// like WaiveXrayAndWrap work.
|
||||
if (!(flags & Wrapper::CROSS_COMPARTMENT))
|
||||
return true;
|
||||
|
||||
// Otherwise, this is a case of explicitly passing a wrapper across a
|
||||
// compartment boundary. In that case, we only want to preserve waivers
|
||||
// in transactions between same-origin compartments.
|
||||
JSCompartment* oldCompartment = js::GetObjectCompartment(originalObj);
|
||||
JSCompartment* newCompartment = js::GetContextCompartment(cx);
|
||||
bool sameOrigin =
|
||||
AccessCheck::subsumesConsideringDomain(oldCompartment, newCompartment) &&
|
||||
AccessCheck::subsumesConsideringDomain(newCompartment, oldCompartment);
|
||||
return sameOrigin;
|
||||
}
|
||||
|
||||
void
|
||||
WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope,
|
||||
HandleObject objArg, HandleObject objectPassedToWrap,
|
||||
MutableHandleObject retObj)
|
||||
{
|
||||
bool waive = ShouldWaiveXray(cx, objectPassedToWrap);
|
||||
RootedObject obj(cx, objArg);
|
||||
retObj.set(nullptr);
|
||||
// Outerize any raw inner objects at the entry point here, so that we don't
|
||||
// have to worry about them for the rest of the wrapping code.
|
||||
if (js::IsWindow(obj)) {
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
obj = js::ToWindowProxyIfWindow(obj);
|
||||
MOZ_ASSERT(obj);
|
||||
// ToWindowProxyIfWindow can return a CCW if |obj| was a
|
||||
// navigated-away-from Window. Strip any CCWs.
|
||||
obj = js::UncheckedUnwrap(obj);
|
||||
if (JS_IsDeadWrapper(obj)) {
|
||||
JS_ReportErrorASCII(cx, "Can't wrap dead object");
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(js::IsWindowProxy(obj));
|
||||
// We crossed a compartment boundary there, so may now have a gray
|
||||
// object. This function is not allowed to return gray objects, so
|
||||
// don't do that.
|
||||
ExposeObjectToActiveJS(obj);
|
||||
}
|
||||
|
||||
// If we've got a WindowProxy, there's nothing special that needs to be
|
||||
// done here, and we can move on to the next phase of wrapping. We handle
|
||||
// this case first to allow us to assert against wrappers below.
|
||||
if (js::IsWindowProxy(obj)) {
|
||||
retObj.set(waive ? WaiveXray(cx, obj) : obj);
|
||||
return;
|
||||
}
|
||||
|
||||
// Here are the rules for wrapping:
|
||||
// We should never get a proxy here (the JS engine unwraps those for us).
|
||||
MOZ_ASSERT(!IsWrapper(obj));
|
||||
|
||||
// Now, our object is ready to be wrapped, but several objects (notably
|
||||
// nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
|
||||
// those objects in a security wrapper, then we need to hand back the
|
||||
// wrapper for the new scope instead. Also, global objects don't move
|
||||
// between scopes so for those we also want to return the wrapper. So...
|
||||
if (!IS_WN_REFLECTOR(obj) || JS_IsGlobalObject(obj)) {
|
||||
retObj.set(waive ? WaiveXray(cx, obj) : obj);
|
||||
return;
|
||||
}
|
||||
|
||||
XPCWrappedNative* wn = XPCWrappedNative::Get(obj);
|
||||
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
XPCCallContext ccx(cx, obj);
|
||||
RootedObject wrapScope(cx, scope);
|
||||
|
||||
{
|
||||
if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) {
|
||||
// We have a precreate hook. This object might enforce that we only
|
||||
// ever create JS object for it.
|
||||
|
||||
// Note: this penalizes objects that only have one wrapper, but are
|
||||
// being accessed across compartments. We would really prefer to
|
||||
// replace the above code with a test that says "do you only have one
|
||||
// wrapper?"
|
||||
nsresult rv = wn->GetScriptableInfo()->GetCallback()->
|
||||
PreCreate(wn->Native(), cx, scope, wrapScope.address());
|
||||
if (NS_FAILED(rv)) {
|
||||
retObj.set(waive ? WaiveXray(cx, obj) : obj);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the handed back scope differs from the passed-in scope and is in
|
||||
// a separate compartment, then this object is explicitly requesting
|
||||
// that we don't create a second JS object for it: create a security
|
||||
// wrapper.
|
||||
if (js::GetObjectCompartment(scope) != js::GetObjectCompartment(wrapScope)) {
|
||||
retObj.set(waive ? WaiveXray(cx, obj) : obj);
|
||||
return;
|
||||
}
|
||||
|
||||
RootedObject currentScope(cx, JS_GetGlobalForObject(cx, obj));
|
||||
if (MOZ_UNLIKELY(wrapScope != currentScope)) {
|
||||
// The wrapper claims it wants to be in the new scope, but
|
||||
// currently has a reflection that lives in the old scope. This
|
||||
// can mean one of two things, both of which are rare:
|
||||
//
|
||||
// 1 - The object has a PreCreate hook (we checked for it above),
|
||||
// but is deciding to request one-wrapper-per-scope (rather than
|
||||
// one-wrapper-per-native) for some reason. Usually, a PreCreate
|
||||
// hook indicates one-wrapper-per-native. In this case we want to
|
||||
// make a new wrapper in the new scope.
|
||||
//
|
||||
// 2 - We're midway through wrapper reparenting. The document has
|
||||
// moved to a new scope, but |wn| hasn't been moved yet, and
|
||||
// we ended up calling JS_WrapObject() on its JS object. In this
|
||||
// case, we want to return the existing wrapper.
|
||||
//
|
||||
// So we do a trick: call PreCreate _again_, but say that we're
|
||||
// wrapping for the old scope, rather than the new one. If (1) is
|
||||
// the case, then PreCreate will return the scope we pass to it
|
||||
// (the old scope). If (2) is the case, PreCreate will return the
|
||||
// scope of the document (the new scope).
|
||||
RootedObject probe(cx);
|
||||
rv = wn->GetScriptableInfo()->GetCallback()->
|
||||
PreCreate(wn->Native(), cx, currentScope, probe.address());
|
||||
|
||||
// Check for case (2).
|
||||
if (probe != currentScope) {
|
||||
MOZ_ASSERT(probe == wrapScope);
|
||||
retObj.set(waive ? WaiveXray(cx, obj) : obj);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ok, must be case (1). Fall through and create a new wrapper.
|
||||
}
|
||||
|
||||
// Nasty hack for late-breaking bug 781476. This will confuse identity checks,
|
||||
// but it's probably better than any of our alternatives.
|
||||
//
|
||||
// Note: We have to ignore domain here. The JS engine assumes that, given a
|
||||
// compartment c, if c->wrap(x) returns a cross-compartment wrapper at time t0,
|
||||
// it will also return a cross-compartment wrapper for any time t1 > t0 unless
|
||||
// an explicit transplant is performed. In particular, wrapper recomputation
|
||||
// assumes that recomputing a wrapper will always result in a wrapper.
|
||||
//
|
||||
// This doesn't actually pose a security issue, because we'll still compute
|
||||
// the correct (opaque) wrapper for the object below given the security
|
||||
// characteristics of the two compartments.
|
||||
if (!AccessCheck::isChrome(js::GetObjectCompartment(wrapScope)) &&
|
||||
AccessCheck::subsumes(js::GetObjectCompartment(wrapScope),
|
||||
js::GetObjectCompartment(obj)))
|
||||
{
|
||||
retObj.set(waive ? WaiveXray(cx, obj) : obj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This public WrapNativeToJSVal API enters the compartment of 'wrapScope'
|
||||
// so we don't have to.
|
||||
RootedValue v(cx);
|
||||
nsresult rv =
|
||||
nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, wrapScope, wn->Native(), nullptr,
|
||||
&NS_GET_IID(nsISupports), false, &v);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
obj.set(&v.toObject());
|
||||
MOZ_ASSERT(IS_WN_REFLECTOR(obj), "bad object");
|
||||
MOZ_ASSERT(!ObjectIsMarkedGray(obj), "Should never return gray reflectors");
|
||||
|
||||
// Because the underlying native didn't have a PreCreate hook, we had
|
||||
// to a new (or possibly pre-existing) XPCWN in our compartment.
|
||||
// This could be a problem for chrome code that passes XPCOM objects
|
||||
// across compartments, because the effects of QI would disappear across
|
||||
// compartments.
|
||||
//
|
||||
// So whenever we pull an XPCWN across compartments in this manner, we
|
||||
// give the destination object the union of the two native sets. We try
|
||||
// to do this cleverly in the common case to avoid too much overhead.
|
||||
XPCWrappedNative* newwn = XPCWrappedNative::Get(obj);
|
||||
RefPtr<XPCNativeSet> unionSet = XPCNativeSet::GetNewOrUsed(newwn->GetSet(),
|
||||
wn->GetSet(), false);
|
||||
if (!unionSet) {
|
||||
return;
|
||||
}
|
||||
newwn->SetSet(unionSet.forget());
|
||||
|
||||
retObj.set(waive ? WaiveXray(cx, obj) : obj);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
DEBUG_CheckUnwrapSafety(HandleObject obj, const js::Wrapper* handler,
|
||||
JSCompartment* origin, JSCompartment* target)
|
||||
{
|
||||
if (AccessCheck::isChrome(target) || xpc::IsUniversalXPConnectEnabled(target)) {
|
||||
// If the caller is chrome (or effectively so), unwrap should always be allowed.
|
||||
MOZ_ASSERT(!handler->hasSecurityPolicy());
|
||||
} else if (CompartmentPrivate::Get(origin)->forcePermissiveCOWs) {
|
||||
// Similarly, if this is a privileged scope that has opted to make itself
|
||||
// accessible to the world (allowed only during automation), unwrap should
|
||||
// be allowed.
|
||||
MOZ_ASSERT(!handler->hasSecurityPolicy());
|
||||
} else {
|
||||
// Otherwise, it should depend on whether the target subsumes the origin.
|
||||
MOZ_ASSERT(handler->hasSecurityPolicy() == !AccessCheck::subsumesConsideringDomain(target, origin));
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define DEBUG_CheckUnwrapSafety(obj, handler, origin, target) {}
|
||||
#endif
|
||||
|
||||
static const Wrapper*
|
||||
SelectWrapper(bool securityWrapper, bool wantXrays, XrayType xrayType,
|
||||
bool waiveXrays, bool originIsXBLScope, JSObject* obj)
|
||||
{
|
||||
// Waived Xray uses a modified CCW that has transparent behavior but
|
||||
// transitively waives Xrays on arguments.
|
||||
if (waiveXrays) {
|
||||
MOZ_ASSERT(!securityWrapper);
|
||||
return &WaiveXrayWrapper::singleton;
|
||||
}
|
||||
|
||||
// If we don't want or can't use Xrays, select a wrapper that's either
|
||||
// entirely transparent or entirely opaque.
|
||||
if (!wantXrays || xrayType == NotXray) {
|
||||
if (!securityWrapper)
|
||||
return &CrossCompartmentWrapper::singleton;
|
||||
return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
|
||||
}
|
||||
|
||||
// Ok, we're using Xray. If this isn't a security wrapper, use the permissive
|
||||
// version and skip the filter.
|
||||
if (!securityWrapper) {
|
||||
if (xrayType == XrayForWrappedNative)
|
||||
return &PermissiveXrayXPCWN::singleton;
|
||||
else if (xrayType == XrayForDOMObject)
|
||||
return &PermissiveXrayDOM::singleton;
|
||||
else if (xrayType == XrayForJSObject)
|
||||
return &PermissiveXrayJS::singleton;
|
||||
MOZ_ASSERT(xrayType == XrayForOpaqueObject);
|
||||
return &PermissiveXrayOpaque::singleton;
|
||||
}
|
||||
|
||||
// This is a security wrapper. Use the security versions and filter.
|
||||
if (xrayType == XrayForDOMObject && IdentifyCrossOriginObject(obj) != CrossOriginOpaque)
|
||||
return &FilteringWrapper<CrossOriginXrayWrapper,
|
||||
CrossOriginAccessiblePropertiesOnly>::singleton;
|
||||
|
||||
// There's never any reason to expose other objects to non-subsuming actors.
|
||||
// Just use an opaque wrapper in these cases.
|
||||
//
|
||||
// In general, we don't want opaque function wrappers to be callable.
|
||||
// But in the case of XBL, we rely on content being able to invoke
|
||||
// functions exposed from the XBL scope. We could remove this exception,
|
||||
// if needed, by using ExportFunction to generate the content-side
|
||||
// representations of XBL methods.
|
||||
if (xrayType == XrayForJSObject && originIsXBLScope)
|
||||
return &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton;
|
||||
return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
|
||||
}
|
||||
|
||||
static const Wrapper*
|
||||
SelectAddonWrapper(JSContext* cx, HandleObject obj, const Wrapper* wrapper)
|
||||
{
|
||||
JSAddonId* originAddon = JS::AddonIdOfObject(obj);
|
||||
JSAddonId* targetAddon = JS::AddonIdOfObject(JS::CurrentGlobalOrNull(cx));
|
||||
|
||||
MOZ_ASSERT(AccessCheck::isChrome(JS::CurrentGlobalOrNull(cx)));
|
||||
MOZ_ASSERT(targetAddon);
|
||||
|
||||
if (targetAddon == originAddon)
|
||||
return wrapper;
|
||||
|
||||
// Add-on interposition only supports certain wrapper types, so we check if
|
||||
// we would have used one of the supported ones.
|
||||
if (wrapper == &CrossCompartmentWrapper::singleton)
|
||||
return &AddonWrapper<CrossCompartmentWrapper>::singleton;
|
||||
else if (wrapper == &PermissiveXrayXPCWN::singleton)
|
||||
return &AddonWrapper<PermissiveXrayXPCWN>::singleton;
|
||||
else if (wrapper == &PermissiveXrayDOM::singleton)
|
||||
return &AddonWrapper<PermissiveXrayDOM>::singleton;
|
||||
|
||||
// |wrapper| is not supported for interposition, so we don't do it.
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj)
|
||||
{
|
||||
MOZ_ASSERT(!IsWrapper(obj) ||
|
||||
GetProxyHandler(obj) == &XrayWaiver ||
|
||||
js::IsWindowProxy(obj),
|
||||
"wrapped object passed to rewrap");
|
||||
MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder");
|
||||
MOZ_ASSERT(!js::IsWindow(obj));
|
||||
MOZ_ASSERT(dom::IsJSAPIActive());
|
||||
|
||||
// Compute the information we need to select the right wrapper.
|
||||
JSCompartment* origin = js::GetObjectCompartment(obj);
|
||||
JSCompartment* target = js::GetContextCompartment(cx);
|
||||
bool originIsChrome = AccessCheck::isChrome(origin);
|
||||
bool targetIsChrome = AccessCheck::isChrome(target);
|
||||
bool originSubsumesTarget = AccessCheck::subsumesConsideringDomain(origin, target);
|
||||
bool targetSubsumesOrigin = AccessCheck::subsumesConsideringDomain(target, origin);
|
||||
bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget;
|
||||
XrayType xrayType = GetXrayType(obj);
|
||||
|
||||
const Wrapper* wrapper;
|
||||
|
||||
//
|
||||
// First, handle the special cases.
|
||||
//
|
||||
|
||||
// If UniversalXPConnect is enabled, this is just some dumb mochitest. Use
|
||||
// a vanilla CCW.
|
||||
if (xpc::IsUniversalXPConnectEnabled(target)) {
|
||||
CrashIfNotInAutomation();
|
||||
wrapper = &CrossCompartmentWrapper::singleton;
|
||||
}
|
||||
|
||||
// Let the SpecialPowers scope make its stuff easily accessible to content.
|
||||
else if (CompartmentPrivate::Get(origin)->forcePermissiveCOWs) {
|
||||
CrashIfNotInAutomation();
|
||||
wrapper = &CrossCompartmentWrapper::singleton;
|
||||
}
|
||||
|
||||
// Special handling for chrome objects being exposed to content.
|
||||
else if (originIsChrome && !targetIsChrome) {
|
||||
// If this is a chrome function being exposed to content, we need to allow
|
||||
// call (but nothing else). We allow CPOWs that purport to be function's
|
||||
// here, but only in the content process.
|
||||
if ((IdentifyStandardInstance(obj) == JSProto_Function ||
|
||||
(jsipc::IsCPOW(obj) && JS::IsCallable(obj) &&
|
||||
XRE_IsContentProcess())))
|
||||
{
|
||||
wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton;
|
||||
}
|
||||
|
||||
// For Vanilla JSObjects exposed from chrome to content, we use a wrapper
|
||||
// that supports __exposedProps__. We'd like to get rid of these eventually,
|
||||
// but in their current form they don't cause much trouble.
|
||||
else if (IdentifyStandardInstance(obj) == JSProto_Object) {
|
||||
wrapper = &ChromeObjectWrapper::singleton;
|
||||
}
|
||||
|
||||
// Otherwise we get an opaque wrapper.
|
||||
else {
|
||||
wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Now, handle the regular cases.
|
||||
//
|
||||
// These are wrappers we can compute using a rule-based approach. In order
|
||||
// to do so, we need to compute some parameters.
|
||||
//
|
||||
else {
|
||||
|
||||
// The wrapper is a security wrapper (protecting the wrappee) if and
|
||||
// only if the target does not subsume the origin.
|
||||
bool securityWrapper = !targetSubsumesOrigin;
|
||||
|
||||
// Xrays are warranted if either the target or the origin don't trust
|
||||
// each other. This is generally the case, unless the two are same-origin
|
||||
// and the caller has not requested same-origin Xrays.
|
||||
//
|
||||
// Xrays are a bidirectional protection, since it affords clarity to the
|
||||
// caller and privacy to the callee.
|
||||
bool sameOriginXrays = CompartmentPrivate::Get(origin)->wantXrays ||
|
||||
CompartmentPrivate::Get(target)->wantXrays;
|
||||
bool wantXrays = !sameOrigin || sameOriginXrays;
|
||||
|
||||
// If Xrays are warranted, the caller may waive them for non-security
|
||||
// wrappers (unless explicitly forbidden from doing so).
|
||||
bool waiveXrays = wantXrays && !securityWrapper &&
|
||||
CompartmentPrivate::Get(target)->allowWaivers &&
|
||||
HasWaiveXrayFlag(obj);
|
||||
|
||||
// We have slightly different behavior for the case when the object
|
||||
// being wrapped is in an XBL scope.
|
||||
bool originIsContentXBLScope = IsContentXBLScope(origin);
|
||||
|
||||
wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays,
|
||||
originIsContentXBLScope, obj);
|
||||
|
||||
// If we want to apply add-on interposition in the target compartment,
|
||||
// then we try to "upgrade" the wrapper to an interposing one.
|
||||
if (CompartmentPrivate::Get(target)->scope->HasInterposition())
|
||||
wrapper = SelectAddonWrapper(cx, obj, wrapper);
|
||||
}
|
||||
|
||||
if (!targetSubsumesOrigin) {
|
||||
// Do a belt-and-suspenders check against exposing eval()/Function() to
|
||||
// non-subsuming content.
|
||||
if (JSFunction* fun = JS_GetObjectFunction(obj)) {
|
||||
if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) {
|
||||
NS_WARNING("Trying to expose eval or Function to non-subsuming content!");
|
||||
wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target);
|
||||
|
||||
if (existing)
|
||||
return Wrapper::Renew(cx, existing, obj, wrapper);
|
||||
|
||||
return Wrapper::New(cx, obj, wrapper);
|
||||
}
|
||||
|
||||
// Call WaiveXrayAndWrap when you have a JS object that you don't want to be
|
||||
// wrapped in an Xray wrapper. cx->compartment is the compartment that will be
|
||||
// using the returned object. If the object to be wrapped is already in the
|
||||
// correct compartment, then this returns the unwrapped object.
|
||||
bool
|
||||
WrapperFactory::WaiveXrayAndWrap(JSContext* cx, MutableHandleValue vp)
|
||||
{
|
||||
if (vp.isPrimitive())
|
||||
return JS_WrapValue(cx, vp);
|
||||
|
||||
RootedObject obj(cx, &vp.toObject());
|
||||
if (!WaiveXrayAndWrap(cx, &obj))
|
||||
return false;
|
||||
|
||||
vp.setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WrapperFactory::WaiveXrayAndWrap(JSContext* cx, MutableHandleObject argObj)
|
||||
{
|
||||
MOZ_ASSERT(argObj);
|
||||
RootedObject obj(cx, js::UncheckedUnwrap(argObj));
|
||||
MOZ_ASSERT(!js::IsWindow(obj));
|
||||
if (js::IsObjectInContextCompartment(obj, cx)) {
|
||||
argObj.set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Even though waivers have no effect on access by scopes that don't subsume
|
||||
// the underlying object, good defense-in-depth dictates that we should avoid
|
||||
// handing out waivers to callers that can't use them. The transitive waiving
|
||||
// machinery unconditionally calls WaiveXrayAndWrap on return values from
|
||||
// waived functions, even though the return value might be not be same-origin
|
||||
// with the function. So if we find ourselves trying to create a waiver for
|
||||
// |cx|, we should check whether the caller has any business with waivers
|
||||
// to things in |obj|'s compartment.
|
||||
JSCompartment* target = js::GetContextCompartment(cx);
|
||||
JSCompartment* origin = js::GetObjectCompartment(obj);
|
||||
obj = AllowWaiver(target, origin) ? WaiveXray(cx, obj) : obj;
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
if (!JS_WrapObject(cx, &obj))
|
||||
return false;
|
||||
argObj.set(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls to JS_TransplantObject* should go through these helpers here so that
|
||||
* waivers get fixed up properly.
|
||||
*/
|
||||
|
||||
static bool
|
||||
FixWaiverAfterTransplant(JSContext* cx, HandleObject oldWaiver, HandleObject newobj)
|
||||
{
|
||||
MOZ_ASSERT(Wrapper::wrapperHandler(oldWaiver) == &XrayWaiver);
|
||||
MOZ_ASSERT(!js::IsCrossCompartmentWrapper(newobj));
|
||||
|
||||
// Create a waiver in the new compartment. We know there's not one already
|
||||
// because we _just_ transplanted, which means that |newobj| was either
|
||||
// created from scratch, or was previously cross-compartment wrapper (which
|
||||
// should have no waiver). CreateXrayWaiver asserts this.
|
||||
JSObject* newWaiver = WrapperFactory::CreateXrayWaiver(cx, newobj);
|
||||
if (!newWaiver)
|
||||
return false;
|
||||
|
||||
// Update all the cross-compartment references to oldWaiver to point to
|
||||
// newWaiver.
|
||||
if (!js::RemapAllWrappersForObject(cx, oldWaiver, newWaiver))
|
||||
return false;
|
||||
|
||||
// There should be no same-compartment references to oldWaiver, and we
|
||||
// just remapped all cross-compartment references. It's dead, so we can
|
||||
// remove it from the map.
|
||||
XPCWrappedNativeScope* scope = ObjectScope(oldWaiver);
|
||||
JSObject* key = Wrapper::wrappedObject(oldWaiver);
|
||||
MOZ_ASSERT(scope->mWaiverWrapperMap->Find(key));
|
||||
scope->mWaiverWrapperMap->Remove(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TransplantObject(JSContext* cx, JS::HandleObject origobj, JS::HandleObject target)
|
||||
{
|
||||
RootedObject oldWaiver(cx, WrapperFactory::GetXrayWaiver(origobj));
|
||||
RootedObject newIdentity(cx, JS_TransplantObject(cx, origobj, target));
|
||||
if (!newIdentity || !oldWaiver)
|
||||
return newIdentity;
|
||||
|
||||
if (!FixWaiverAfterTransplant(cx, oldWaiver, newIdentity))
|
||||
return nullptr;
|
||||
return newIdentity;
|
||||
}
|
||||
|
||||
nsIGlobalObject*
|
||||
NativeGlobal(JSObject* obj)
|
||||
{
|
||||
obj = js::GetGlobalForObjectCrossCompartment(obj);
|
||||
|
||||
// Every global needs to hold a native as its private or be a
|
||||
// WebIDL object with an nsISupports DOM object.
|
||||
MOZ_ASSERT((GetObjectClass(obj)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
|
||||
JSCLASS_HAS_PRIVATE)) ||
|
||||
dom::UnwrapDOMObjectToISupports(obj));
|
||||
|
||||
nsISupports* native = dom::UnwrapDOMObjectToISupports(obj);
|
||||
if (!native) {
|
||||
native = static_cast<nsISupports*>(js::GetObjectPrivate(obj));
|
||||
MOZ_ASSERT(native);
|
||||
|
||||
// In some cases (like for windows) it is a wrapped native,
|
||||
// in other cases (sandboxes, backstage passes) it's just
|
||||
// a direct pointer to the native. If it's a wrapped native
|
||||
// let's unwrap it first.
|
||||
if (nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(native)) {
|
||||
native = wn->Native();
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(native);
|
||||
MOZ_ASSERT(global, "Native held by global needs to implement nsIGlobalObject!");
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
} // namespace xpc
|
||||
68
js/xpconnect/wrappers/WrapperFactory.h
Normal file
68
js/xpconnect/wrappers/WrapperFactory.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _xpc_WRAPPERFACTORY_H
|
||||
#define _xpc_WRAPPERFACTORY_H
|
||||
|
||||
#include "jswrapper.h"
|
||||
|
||||
namespace xpc {
|
||||
|
||||
class WrapperFactory {
|
||||
public:
|
||||
enum { WAIVE_XRAY_WRAPPER_FLAG = js::Wrapper::LAST_USED_FLAG << 1,
|
||||
IS_XRAY_WRAPPER_FLAG = WAIVE_XRAY_WRAPPER_FLAG << 1 };
|
||||
|
||||
// Return true if any of any of the nested wrappers have the flag set.
|
||||
static bool HasWrapperFlag(JSObject* wrapper, unsigned flag) {
|
||||
unsigned flags = 0;
|
||||
js::UncheckedUnwrap(wrapper, true, &flags);
|
||||
return !!(flags & flag);
|
||||
}
|
||||
|
||||
static bool IsXrayWrapper(JSObject* wrapper) {
|
||||
return HasWrapperFlag(wrapper, IS_XRAY_WRAPPER_FLAG);
|
||||
}
|
||||
|
||||
static bool HasWaiveXrayFlag(JSObject* wrapper) {
|
||||
return HasWrapperFlag(wrapper, WAIVE_XRAY_WRAPPER_FLAG);
|
||||
}
|
||||
|
||||
static bool IsCOW(JSObject* wrapper);
|
||||
|
||||
static JSObject* GetXrayWaiver(JS::HandleObject obj);
|
||||
static JSObject* CreateXrayWaiver(JSContext* cx, JS::HandleObject obj);
|
||||
static JSObject* WaiveXray(JSContext* cx, JSObject* obj);
|
||||
|
||||
// Computes whether we should allow the creation of an Xray waiver from
|
||||
// |target| to |origin|.
|
||||
static bool AllowWaiver(JSCompartment* target, JSCompartment* origin);
|
||||
|
||||
// Convenience method for the above, operating on a wrapper.
|
||||
static bool AllowWaiver(JSObject* wrapper);
|
||||
|
||||
// Prepare a given object for wrapping in a new compartment.
|
||||
static void PrepareForWrapping(JSContext* cx,
|
||||
JS::HandleObject scope,
|
||||
JS::HandleObject obj,
|
||||
JS::HandleObject objectPassedToWrap,
|
||||
JS::MutableHandleObject retObj);
|
||||
|
||||
// Rewrap an object that is about to cross compartment boundaries.
|
||||
static JSObject* Rewrap(JSContext* cx,
|
||||
JS::HandleObject existing,
|
||||
JS::HandleObject obj);
|
||||
|
||||
// Wrap wrapped object into a waiver wrapper and then re-wrap it.
|
||||
static bool WaiveXrayAndWrap(JSContext* cx, JS::MutableHandleValue vp);
|
||||
static bool WaiveXrayAndWrap(JSContext* cx, JS::MutableHandleObject object);
|
||||
};
|
||||
|
||||
extern const js::Wrapper XrayWaiver;
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
#endif /* _xpc_WRAPPERFACTORY_H */
|
||||
2466
js/xpconnect/wrappers/XrayWrapper.cpp
Normal file
2466
js/xpconnect/wrappers/XrayWrapper.cpp
Normal file
File diff suppressed because it is too large
Load diff
620
js/xpconnect/wrappers/XrayWrapper.h
Normal file
620
js/xpconnect/wrappers/XrayWrapper.h
Normal file
|
|
@ -0,0 +1,620 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef XrayWrapper_h
|
||||
#define XrayWrapper_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "WrapperFactory.h"
|
||||
|
||||
#include "jswrapper.h"
|
||||
#include "js/Proxy.h"
|
||||
|
||||
// Slot where Xray functions for Web IDL methods store a pointer to
|
||||
// the Xray wrapper they're associated with.
|
||||
#define XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT 0
|
||||
// Slot where in debug builds Xray functions for Web IDL methods store
|
||||
// a pointer to their themselves, just so we can assert that they're the
|
||||
// sort of functions we expect.
|
||||
#define XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF 1
|
||||
|
||||
// Xray wrappers re-resolve the original native properties on the native
|
||||
// object and always directly access to those properties.
|
||||
// Because they work so differently from the rest of the wrapper hierarchy,
|
||||
// we pull them out of the Wrapper inheritance hierarchy and create a
|
||||
// little world around them.
|
||||
|
||||
class nsIPrincipal;
|
||||
class XPCWrappedNative;
|
||||
|
||||
namespace xpc {
|
||||
|
||||
namespace XrayUtils {
|
||||
|
||||
bool IsXPCWNHolderClass(const JSClass* clasp);
|
||||
|
||||
bool CloneExpandoChain(JSContext* cx, JSObject* src, JSObject* dst);
|
||||
|
||||
bool
|
||||
IsTransparent(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id);
|
||||
|
||||
JSObject*
|
||||
GetNativePropertiesObject(JSContext* cx, JSObject* wrapper);
|
||||
|
||||
bool
|
||||
HasNativeProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
|
||||
bool* hasProp);
|
||||
} // namespace XrayUtils
|
||||
|
||||
enum XrayType {
|
||||
XrayForDOMObject,
|
||||
XrayForWrappedNative,
|
||||
XrayForJSObject,
|
||||
XrayForOpaqueObject,
|
||||
NotXray
|
||||
};
|
||||
|
||||
class XrayTraits
|
||||
{
|
||||
public:
|
||||
constexpr XrayTraits() {}
|
||||
|
||||
static JSObject* getTargetObject(JSObject* wrapper) {
|
||||
return js::UncheckedUnwrap(wrapper, /* stopAtWindowProxy = */ false);
|
||||
}
|
||||
|
||||
virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject holder, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) = 0;
|
||||
// NB: resolveOwnProperty may decide whether or not to cache what it finds
|
||||
// on the holder. If the result is not cached, the lookup will happen afresh
|
||||
// for each access, which is the right thing for things like dynamic NodeList
|
||||
// properties.
|
||||
virtual bool resolveOwnProperty(JSContext* cx, const js::Wrapper& jsWrapper,
|
||||
JS::HandleObject wrapper, JS::HandleObject holder,
|
||||
JS::HandleId id, JS::MutableHandle<JS::PropertyDescriptor> desc);
|
||||
|
||||
bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
|
||||
JS::ObjectOpResult& result) {
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
static bool getBuiltinClass(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance,
|
||||
js::ESClass* cls) {
|
||||
return baseInstance.getBuiltinClass(cx, wrapper, cls);
|
||||
}
|
||||
|
||||
static const char* className(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance) {
|
||||
return baseInstance.className(cx, wrapper);
|
||||
}
|
||||
|
||||
virtual void preserveWrapper(JSObject* target) = 0;
|
||||
|
||||
bool getExpandoObject(JSContext* cx, JS::HandleObject target,
|
||||
JS::HandleObject consumer, JS::MutableHandleObject expandObject);
|
||||
JSObject* ensureExpandoObject(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject target);
|
||||
|
||||
JSObject* getHolder(JSObject* wrapper);
|
||||
JSObject* ensureHolder(JSContext* cx, JS::HandleObject wrapper);
|
||||
virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) = 0;
|
||||
|
||||
JSObject* getExpandoChain(JS::HandleObject obj);
|
||||
bool setExpandoChain(JSContext* cx, JS::HandleObject obj, JS::HandleObject chain);
|
||||
bool cloneExpandoChain(JSContext* cx, JS::HandleObject dst, JS::HandleObject src);
|
||||
|
||||
protected:
|
||||
// Get the JSClass we should use for our expando object.
|
||||
virtual const JSClass* getExpandoClass(JSContext* cx,
|
||||
JS::HandleObject target) const;
|
||||
|
||||
private:
|
||||
bool expandoObjectMatchesConsumer(JSContext* cx, JS::HandleObject expandoObject,
|
||||
nsIPrincipal* consumerOrigin,
|
||||
JS::HandleObject exclusiveGlobal);
|
||||
bool getExpandoObjectInternal(JSContext* cx, JS::HandleObject target,
|
||||
nsIPrincipal* origin, JSObject* exclusiveGlobal,
|
||||
JS::MutableHandleObject expandoObject);
|
||||
JSObject* attachExpandoObject(JSContext* cx, JS::HandleObject target,
|
||||
nsIPrincipal* origin,
|
||||
JS::HandleObject exclusiveGlobal);
|
||||
|
||||
XrayTraits(XrayTraits&) = delete;
|
||||
const XrayTraits& operator=(XrayTraits&) = delete;
|
||||
};
|
||||
|
||||
class XPCWrappedNativeXrayTraits : public XrayTraits
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
HasPrototype = 0
|
||||
};
|
||||
|
||||
static const XrayType Type = XrayForWrappedNative;
|
||||
|
||||
virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject holder, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) override;
|
||||
virtual bool resolveOwnProperty(JSContext* cx, const js::Wrapper& jsWrapper, JS::HandleObject wrapper,
|
||||
JS::HandleObject holder, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) override;
|
||||
bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::Handle<JS::PropertyDescriptor> existingDesc,
|
||||
JS::ObjectOpResult& result, bool* defined)
|
||||
{
|
||||
*defined = false;
|
||||
return true;
|
||||
}
|
||||
virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper, unsigned flags,
|
||||
JS::AutoIdVector& props);
|
||||
static bool call(JSContext* cx, JS::HandleObject wrapper,
|
||||
const JS::CallArgs& args, const js::Wrapper& baseInstance);
|
||||
static bool construct(JSContext* cx, JS::HandleObject wrapper,
|
||||
const JS::CallArgs& args, const js::Wrapper& baseInstance);
|
||||
|
||||
static XPCWrappedNative* getWN(JSObject* wrapper);
|
||||
|
||||
virtual void preserveWrapper(JSObject* target) override;
|
||||
|
||||
virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override;
|
||||
|
||||
static const JSClass HolderClass;
|
||||
static XPCWrappedNativeXrayTraits singleton;
|
||||
};
|
||||
|
||||
class DOMXrayTraits : public XrayTraits
|
||||
{
|
||||
public:
|
||||
constexpr DOMXrayTraits() = default;
|
||||
|
||||
enum {
|
||||
HasPrototype = 1
|
||||
};
|
||||
|
||||
static const XrayType Type = XrayForDOMObject;
|
||||
|
||||
virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject holder, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) override
|
||||
{
|
||||
// Xrays for DOM binding objects have a prototype chain that consists of
|
||||
// Xrays for the prototypes of the DOM binding object (ignoring changes
|
||||
// in the prototype chain made by script, plugins or XBL). All properties for
|
||||
// these Xrays are really own properties, either of the instance object or
|
||||
// of the prototypes.
|
||||
// FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1072482
|
||||
// This should really be:
|
||||
// MOZ_CRASH("resolveNativeProperty hook should never be called with HasPrototype = 1");
|
||||
// but we can't do that yet because XrayUtils::HasNativeProperty calls this.
|
||||
return true;
|
||||
}
|
||||
virtual bool resolveOwnProperty(JSContext* cx, const js::Wrapper& jsWrapper, JS::HandleObject wrapper,
|
||||
JS::HandleObject holder, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) override;
|
||||
|
||||
bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::ObjectOpResult& result);
|
||||
|
||||
bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::Handle<JS::PropertyDescriptor> existingDesc,
|
||||
JS::ObjectOpResult& result, bool* defined);
|
||||
virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper, unsigned flags,
|
||||
JS::AutoIdVector& props);
|
||||
static bool call(JSContext* cx, JS::HandleObject wrapper,
|
||||
const JS::CallArgs& args, const js::Wrapper& baseInstance);
|
||||
static bool construct(JSContext* cx, JS::HandleObject wrapper,
|
||||
const JS::CallArgs& args, const js::Wrapper& baseInstance);
|
||||
|
||||
static bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject target,
|
||||
JS::MutableHandleObject protop);
|
||||
|
||||
virtual void preserveWrapper(JSObject* target) override;
|
||||
|
||||
virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override;
|
||||
|
||||
static DOMXrayTraits singleton;
|
||||
|
||||
protected:
|
||||
virtual const JSClass* getExpandoClass(JSContext* cx,
|
||||
JS::HandleObject target) const override;
|
||||
};
|
||||
|
||||
class JSXrayTraits : public XrayTraits
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
HasPrototype = 1
|
||||
};
|
||||
static const XrayType Type = XrayForJSObject;
|
||||
|
||||
virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject holder, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) override
|
||||
{
|
||||
MOZ_CRASH("resolveNativeProperty hook should never be called with HasPrototype = 1");
|
||||
}
|
||||
|
||||
virtual bool resolveOwnProperty(JSContext* cx, const js::Wrapper& jsWrapper, JS::HandleObject wrapper,
|
||||
JS::HandleObject holder, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) override;
|
||||
|
||||
bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::ObjectOpResult& result);
|
||||
|
||||
bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::Handle<JS::PropertyDescriptor> existingDesc,
|
||||
JS::ObjectOpResult& result, bool* defined);
|
||||
|
||||
virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper, unsigned flags,
|
||||
JS::AutoIdVector& props);
|
||||
|
||||
static bool call(JSContext* cx, JS::HandleObject wrapper,
|
||||
const JS::CallArgs& args, const js::Wrapper& baseInstance)
|
||||
{
|
||||
JSXrayTraits& self = JSXrayTraits::singleton;
|
||||
JS::RootedObject holder(cx, self.ensureHolder(cx, wrapper));
|
||||
if (self.getProtoKey(holder) == JSProto_Function)
|
||||
return baseInstance.call(cx, wrapper, args);
|
||||
|
||||
JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
|
||||
js::ReportIsNotFunction(cx, v);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool construct(JSContext* cx, JS::HandleObject wrapper,
|
||||
const JS::CallArgs& args, const js::Wrapper& baseInstance);
|
||||
|
||||
bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject target,
|
||||
JS::MutableHandleObject protop)
|
||||
{
|
||||
JS::RootedObject holder(cx, ensureHolder(cx, wrapper));
|
||||
JSProtoKey key = getProtoKey(holder);
|
||||
if (isPrototype(holder)) {
|
||||
JSProtoKey protoKey = js::InheritanceProtoKeyForStandardClass(key);
|
||||
if (protoKey == JSProto_Null) {
|
||||
protop.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
key = protoKey;
|
||||
}
|
||||
|
||||
{
|
||||
JSAutoCompartment ac(cx, target);
|
||||
if (!JS_GetClassPrototype(cx, key, protop))
|
||||
return false;
|
||||
}
|
||||
return JS_WrapObject(cx, protop);
|
||||
}
|
||||
|
||||
virtual void preserveWrapper(JSObject* target) override {
|
||||
// In the case of pure JS objects, there is no underlying object, and
|
||||
// the target is the canonical representation of state. If it gets
|
||||
// collected, then expandos and such should be collected too. So there's
|
||||
// nothing to do here.
|
||||
}
|
||||
|
||||
enum {
|
||||
SLOT_PROTOKEY = 0,
|
||||
SLOT_ISPROTOTYPE,
|
||||
SLOT_CONSTRUCTOR_FOR,
|
||||
SLOT_COUNT
|
||||
};
|
||||
virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override;
|
||||
|
||||
static JSProtoKey getProtoKey(JSObject* holder) {
|
||||
int32_t key = js::GetReservedSlot(holder, SLOT_PROTOKEY).toInt32();
|
||||
return static_cast<JSProtoKey>(key);
|
||||
}
|
||||
|
||||
static bool isPrototype(JSObject* holder) {
|
||||
return js::GetReservedSlot(holder, SLOT_ISPROTOTYPE).toBoolean();
|
||||
}
|
||||
|
||||
static JSProtoKey constructorFor(JSObject* holder) {
|
||||
int32_t key = js::GetReservedSlot(holder, SLOT_CONSTRUCTOR_FOR).toInt32();
|
||||
return static_cast<JSProtoKey>(key);
|
||||
}
|
||||
|
||||
// Operates in the wrapper compartment.
|
||||
static bool getOwnPropertyFromWrapperIfSafe(JSContext* cx,
|
||||
JS::HandleObject wrapper,
|
||||
JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc);
|
||||
|
||||
// Like the above, but operates in the target compartment.
|
||||
static bool getOwnPropertyFromTargetIfSafe(JSContext* cx,
|
||||
JS::HandleObject target,
|
||||
JS::HandleObject wrapper,
|
||||
JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc);
|
||||
|
||||
static const JSClass HolderClass;
|
||||
static JSXrayTraits singleton;
|
||||
};
|
||||
|
||||
// These traits are used when the target is not Xrayable and we therefore want
|
||||
// to make it opaque modulo the usual Xray machinery (like expandos and
|
||||
// .wrappedJSObject).
|
||||
class OpaqueXrayTraits : public XrayTraits
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
HasPrototype = 1
|
||||
};
|
||||
static const XrayType Type = XrayForOpaqueObject;
|
||||
|
||||
virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject holder, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) override
|
||||
{
|
||||
MOZ_CRASH("resolveNativeProperty hook should never be called with HasPrototype = 1");
|
||||
}
|
||||
|
||||
virtual bool resolveOwnProperty(JSContext* cx, const js::Wrapper& jsWrapper, JS::HandleObject wrapper,
|
||||
JS::HandleObject holder, JS::HandleId id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) override;
|
||||
|
||||
bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::Handle<JS::PropertyDescriptor> existingDesc,
|
||||
JS::ObjectOpResult& result, bool* defined)
|
||||
{
|
||||
*defined = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper, unsigned flags,
|
||||
JS::AutoIdVector& props)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool call(JSContext* cx, JS::HandleObject wrapper,
|
||||
const JS::CallArgs& args, const js::Wrapper& baseInstance)
|
||||
{
|
||||
JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
|
||||
js::ReportIsNotFunction(cx, v);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool construct(JSContext* cx, JS::HandleObject wrapper,
|
||||
const JS::CallArgs& args, const js::Wrapper& baseInstance)
|
||||
{
|
||||
JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
|
||||
js::ReportIsNotFunction(cx, v);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject target,
|
||||
JS::MutableHandleObject protop)
|
||||
{
|
||||
// Opaque wrappers just get targetGlobal.Object.prototype as their
|
||||
// prototype. This is preferable to using a null prototype because it
|
||||
// lets things like |toString| and |__proto__| work.
|
||||
{
|
||||
JSAutoCompartment ac(cx, target);
|
||||
if (!JS_GetClassPrototype(cx, JSProto_Object, protop))
|
||||
return false;
|
||||
}
|
||||
return JS_WrapObject(cx, protop);
|
||||
}
|
||||
|
||||
static bool getBuiltinClass(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance,
|
||||
js::ESClass* cls) {
|
||||
*cls = js::ESClass::Other;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* className(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance) {
|
||||
return "Opaque";
|
||||
}
|
||||
|
||||
virtual void preserveWrapper(JSObject* target) override { }
|
||||
|
||||
virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override
|
||||
{
|
||||
return JS_NewObjectWithGivenProto(cx, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static OpaqueXrayTraits singleton;
|
||||
};
|
||||
|
||||
XrayType GetXrayType(JSObject* obj);
|
||||
XrayTraits* GetXrayTraits(JSObject* obj);
|
||||
|
||||
// NB: Base *must* derive from JSProxyHandler
|
||||
template <typename Base, typename Traits = XPCWrappedNativeXrayTraits >
|
||||
class XrayWrapper : public Base {
|
||||
public:
|
||||
constexpr explicit XrayWrapper(unsigned flags)
|
||||
: Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG, Traits::HasPrototype)
|
||||
{ };
|
||||
|
||||
/* Standard internal methods. */
|
||||
virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
|
||||
JS::Handle<JS::PropertyDescriptor> desc,
|
||||
JS::ObjectOpResult& result) const override;
|
||||
virtual bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::AutoIdVector& props) const override;
|
||||
virtual bool delete_(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::Handle<jsid> id, JS::ObjectOpResult& result) const override;
|
||||
virtual bool enumerate(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::MutableHandle<JSObject*> objp) const override;
|
||||
virtual bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::MutableHandleObject protop) const override;
|
||||
virtual bool setPrototype(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject proto, JS::ObjectOpResult& result) const override;
|
||||
virtual bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject wrapper, bool* isOrdinary,
|
||||
JS::MutableHandleObject protop) const override;
|
||||
virtual bool setImmutablePrototype(JSContext* cx, JS::HandleObject wrapper,
|
||||
bool* succeeded) const override;
|
||||
virtual bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::ObjectOpResult& result) const override;
|
||||
virtual bool isExtensible(JSContext* cx, JS::Handle<JSObject*> wrapper, bool* extensible) const override;
|
||||
virtual bool has(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
|
||||
bool* bp) const override;
|
||||
virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::HandleValue receiver,
|
||||
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const override;
|
||||
virtual bool set(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
|
||||
JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
|
||||
JS::ObjectOpResult& result) const override;
|
||||
virtual bool call(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
const JS::CallArgs& args) const override;
|
||||
virtual bool construct(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
const JS::CallArgs& args) const override;
|
||||
|
||||
/* SpiderMonkey extensions. */
|
||||
virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
virtual bool hasOwn(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
|
||||
bool* bp) const override;
|
||||
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
|
||||
JS::AutoIdVector& props) const override;
|
||||
|
||||
virtual bool getBuiltinClass(JSContext* cx, JS::HandleObject wapper, js::ESClass* cls) const override;
|
||||
virtual const char* className(JSContext* cx, JS::HandleObject proxy) const override;
|
||||
|
||||
static const XrayWrapper singleton;
|
||||
|
||||
private:
|
||||
template <bool HasPrototype>
|
||||
typename mozilla::EnableIf<HasPrototype, bool>::Type
|
||||
getPrototypeHelper(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject target, JS::MutableHandleObject protop) const
|
||||
{
|
||||
return Traits::singleton.getPrototype(cx, wrapper, target, protop);
|
||||
}
|
||||
template <bool HasPrototype>
|
||||
typename mozilla::EnableIf<!HasPrototype, bool>::Type
|
||||
getPrototypeHelper(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject target, JS::MutableHandleObject protop) const
|
||||
{
|
||||
return Base::getPrototype(cx, wrapper, protop);
|
||||
}
|
||||
bool getPrototypeHelper(JSContext* cx, JS::HandleObject wrapper,
|
||||
JS::HandleObject target, JS::MutableHandleObject protop) const
|
||||
{
|
||||
return getPrototypeHelper<Traits::HasPrototype>(cx, wrapper, target,
|
||||
protop);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool getPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper, unsigned flags,
|
||||
JS::AutoIdVector& props) const;
|
||||
};
|
||||
|
||||
#define PermissiveXrayXPCWN xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::XPCWrappedNativeXrayTraits>
|
||||
#define SecurityXrayXPCWN xpc::XrayWrapper<js::CrossCompartmentSecurityWrapper, xpc::XPCWrappedNativeXrayTraits>
|
||||
#define PermissiveXrayDOM xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>
|
||||
#define SecurityXrayDOM xpc::XrayWrapper<js::CrossCompartmentSecurityWrapper, xpc::DOMXrayTraits>
|
||||
#define PermissiveXrayJS xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>
|
||||
#define PermissiveXrayOpaque xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>
|
||||
|
||||
extern template class PermissiveXrayXPCWN;
|
||||
extern template class SecurityXrayXPCWN;
|
||||
extern template class PermissiveXrayDOM;
|
||||
extern template class SecurityXrayDOM;
|
||||
extern template class PermissiveXrayJS;
|
||||
extern template class PermissiveXrayOpaque;
|
||||
extern template class PermissiveXrayXPCWN;
|
||||
|
||||
class SandboxProxyHandler : public js::Wrapper {
|
||||
public:
|
||||
constexpr SandboxProxyHandler() : js::Wrapper(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
|
||||
// We just forward the high-level methods to the BaseProxyHandler versions
|
||||
// which implement them in terms of lower-level methods.
|
||||
virtual bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
|
||||
bool* bp) const override;
|
||||
virtual bool get(JSContext* cx, JS::Handle<JSObject*> proxy, JS::HandleValue receiver,
|
||||
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const override;
|
||||
virtual bool set(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
|
||||
JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
|
||||
JS::ObjectOpResult& result) const override;
|
||||
|
||||
virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::Handle<jsid> id,
|
||||
JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
|
||||
virtual bool hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
|
||||
bool* bp) const override;
|
||||
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::AutoIdVector& props) const override;
|
||||
virtual bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
JS::MutableHandle<JSObject*> objp) const override;
|
||||
};
|
||||
|
||||
extern const SandboxProxyHandler sandboxProxyHandler;
|
||||
|
||||
// A proxy handler that lets us wrap callables and invoke them with
|
||||
// the correct this object, while forwarding all other operations down
|
||||
// to them directly.
|
||||
class SandboxCallableProxyHandler : public js::Wrapper {
|
||||
public:
|
||||
constexpr SandboxCallableProxyHandler() : js::Wrapper(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool call(JSContext* cx, JS::Handle<JSObject*> proxy,
|
||||
const JS::CallArgs& args) const override;
|
||||
|
||||
static const size_t SandboxProxySlot = 0;
|
||||
|
||||
static inline JSObject* getSandboxProxy(JS::Handle<JSObject*> proxy)
|
||||
{
|
||||
return &js::GetProxyExtra(proxy, SandboxProxySlot).toObject();
|
||||
}
|
||||
};
|
||||
|
||||
extern const SandboxCallableProxyHandler sandboxCallableProxyHandler;
|
||||
|
||||
class AutoSetWrapperNotShadowing;
|
||||
|
||||
/*
|
||||
* Slots for Xray expando objects. See comments in XrayWrapper.cpp for details
|
||||
* of how these get used; we mostly want the value of JSSLOT_EXPANDO_COUNT here.
|
||||
*/
|
||||
enum ExpandoSlots {
|
||||
JSSLOT_EXPANDO_NEXT = 0,
|
||||
JSSLOT_EXPANDO_ORIGIN,
|
||||
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
|
||||
JSSLOT_EXPANDO_PROTOTYPE,
|
||||
JSSLOT_EXPANDO_COUNT
|
||||
};
|
||||
|
||||
extern const JSClassOps XrayExpandoObjectClassOps;
|
||||
|
||||
/*
|
||||
* Clear the given slot on all Xray expandos for the given object.
|
||||
*
|
||||
* No-op when called on non-main threads (where Xrays don't exist).
|
||||
*/
|
||||
void
|
||||
ClearXrayExpandoSlots(JSObject* target, size_t slotIndex);
|
||||
|
||||
/*
|
||||
* Ensure the given wrapper has an expando object and return it. This can
|
||||
* return null on failure. Will only be called when "wrapper" is an Xray for a
|
||||
* DOM object.
|
||||
*/
|
||||
JSObject*
|
||||
EnsureXrayExpandoObject(JSContext* cx, JS::HandleObject wrapper);
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
#endif
|
||||
41
js/xpconnect/wrappers/moz.build
Normal file
41
js/xpconnect/wrappers/moz.build
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'WrapperFactory.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AccessCheck.cpp',
|
||||
'AddonWrapper.cpp',
|
||||
'ChromeObjectWrapper.cpp',
|
||||
'FilteringWrapper.cpp',
|
||||
'WaiveXrayWrapper.cpp',
|
||||
'WrapperFactory.cpp',
|
||||
]
|
||||
|
||||
# XrayWrapper needs to be built separately becaue of template instantiations.
|
||||
SOURCES += [
|
||||
'XrayWrapper.cpp',
|
||||
]
|
||||
|
||||
# warning C4661 for FilteringWrapper
|
||||
if CONFIG['_MSC_VER']:
|
||||
CXXFLAGS += [
|
||||
'-wd4661', # no suitable definition provided for explicit template instantiation request
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../../../dom/base',
|
||||
'../src',
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-shadow']
|
||||
Loading…
Add table
Add a link
Reference in a new issue