diff -r 000000000000 -r 6474c204b198 js/xpconnect/wrappers/FilteringWrapper.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,207 @@ +/* -*- 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 { + +template +FilteringWrapper::FilteringWrapper(unsigned flags) : Base(flags) +{ +} + +template +FilteringWrapper::~FilteringWrapper() +{ +} + +template +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)) + props[w++] = id; + else if (JS_IsExceptionPending(cx)) + return false; + } + props.resize(w); + return true; +} + +template +static bool +FilterSetter(JSContext *cx, JSObject *wrapper, jsid id, JS::MutableHandle desc) +{ + bool setAllowed = Policy::check(cx, wrapper, id, Wrapper::SET); + if (!setAllowed) { + if (JS_IsExceptionPending(cx)) + return false; + desc.setSetter(nullptr); + } + return true; +} + +template +bool +FilteringWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, + HandleId id, + JS::MutableHandle desc) +{ + assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); + if (!Base::getPropertyDescriptor(cx, wrapper, id, desc)) + return false; + return FilterSetter(cx, wrapper, id, desc); +} + +template +bool +FilteringWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, + HandleId id, + JS::MutableHandle desc) +{ + assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); + if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc)) + return false; + return FilterSetter(cx, wrapper, id, desc); +} + +template +bool +FilteringWrapper::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, + AutoIdVector &props) +{ + assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); + return Base::getOwnPropertyNames(cx, wrapper, props) && + Filter(cx, wrapper, props); +} + +template +bool +FilteringWrapper::enumerate(JSContext *cx, HandleObject wrapper, + AutoIdVector &props) +{ + assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); + return Base::enumerate(cx, wrapper, props) && + Filter(cx, wrapper, props); +} + +template +bool +FilteringWrapper::keys(JSContext *cx, HandleObject wrapper, + AutoIdVector &props) +{ + assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); + return Base::keys(cx, wrapper, props) && + Filter(cx, wrapper, props); +} + +template +bool +FilteringWrapper::iterate(JSContext *cx, HandleObject wrapper, + unsigned flags, MutableHandleValue vp) +{ + assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); + // We refuse to trigger the iterator hook across chrome wrappers because + // we don't know how to censor custom iterator objects. Instead we trigger + // the default proxy iterate trap, which will ask enumerate() for the list + // of (censored) ids. + return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp); +} + +template +bool +FilteringWrapper::nativeCall(JSContext *cx, JS::IsAcceptableThis test, + JS::NativeImpl impl, JS::CallArgs args) +{ + if (Policy::allowNativeCall(cx, test, impl)) + return Base::Permissive::nativeCall(cx, test, impl, args); + return Base::Restrictive::nativeCall(cx, test, impl, args); +} + +template +bool +FilteringWrapper::defaultValue(JSContext *cx, HandleObject obj, + JSType hint, MutableHandleValue vp) +{ + return Base::defaultValue(cx, obj, hint, vp); +} + +// With our entirely-opaque wrapper, the DefaultValue algorithm throws, +// causing spurious exceptions. Manually implement something benign. +template<> +bool +FilteringWrapper + ::defaultValue(JSContext *cx, HandleObject obj, + JSType hint, MutableHandleValue vp) +{ + JSString *str = JS_NewStringCopyZ(cx, "[Opaque]"); + if (!str) + return false; + vp.set(JS::StringValue(str)); + return true; +} + + +template +bool +FilteringWrapper::enter(JSContext *cx, HandleObject wrapper, + HandleId id, Wrapper::Action act, bool *bp) +{ + // This is a super ugly hacky to get around Xray Resolve wonkiness. + // + // Basically, XPCWN Xrays sometimes call into the Resolve hook of the + // scriptable helper, and pass the wrapper itself as the object upon which + // the resolve is happening. Then, special handling happens in + // XrayWrapper::defineProperty to detect the resolve and redefine the + // property on the holder. Really, we should just pass the holder itself to + // NewResolve, but there's too much code in nsDOMClassInfo that assumes this + // isn't the case (in particular, code expects to be able to look up + // properties on the object, which doesn't work for the holder). Given that + // these hooks are going away eventually with the new DOM bindings, let's + // just hack around this for now. + if (XrayUtils::IsXrayResolving(cx, wrapper, id)) { + *bp = true; + return true; + } + if (!Policy::check(cx, wrapper, id, act)) { + *bp = JS_IsExceptionPending(cx) ? false : Policy::deny(act, id); + return false; + } + *bp = true; + return true; +} + +#define XOW FilteringWrapper +#define DXOW FilteringWrapper +#define NNXOW FilteringWrapper +#define NNXOWC FilteringWrapper +#define GO FilteringWrapper +template<> XOW XOW::singleton(0); +template<> DXOW DXOW::singleton(0); +template<> NNXOW NNXOW::singleton(0); +template<> NNXOWC NNXOWC::singleton(0); + +template<> GO GO::singleton(0); + +template class XOW; +template class DXOW; +template class NNXOW; +template class NNXOWC; +template class ChromeObjectWrapperBase; +template class GO; +}