michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "FilteringWrapper.h" michael@0: #include "AccessCheck.h" michael@0: #include "ChromeObjectWrapper.h" michael@0: #include "XrayWrapper.h" michael@0: michael@0: #include "jsapi.h" michael@0: michael@0: using namespace JS; michael@0: using namespace js; michael@0: michael@0: namespace xpc { michael@0: michael@0: template michael@0: FilteringWrapper::FilteringWrapper(unsigned flags) : Base(flags) michael@0: { michael@0: } michael@0: michael@0: template michael@0: FilteringWrapper::~FilteringWrapper() michael@0: { michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: Filter(JSContext *cx, HandleObject wrapper, AutoIdVector &props) michael@0: { michael@0: size_t w = 0; michael@0: RootedId id(cx); michael@0: for (size_t n = 0; n < props.length(); ++n) { michael@0: id = props[n]; michael@0: if (Policy::check(cx, wrapper, id, Wrapper::GET)) michael@0: props[w++] = id; michael@0: else if (JS_IsExceptionPending(cx)) michael@0: return false; michael@0: } michael@0: props.resize(w); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: FilterSetter(JSContext *cx, JSObject *wrapper, jsid id, JS::MutableHandle desc) michael@0: { michael@0: bool setAllowed = Policy::check(cx, wrapper, id, Wrapper::SET); michael@0: if (!setAllowed) { michael@0: if (JS_IsExceptionPending(cx)) michael@0: return false; michael@0: desc.setSetter(nullptr); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: FilteringWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, michael@0: HandleId id, michael@0: JS::MutableHandle desc) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); michael@0: if (!Base::getPropertyDescriptor(cx, wrapper, id, desc)) michael@0: return false; michael@0: return FilterSetter(cx, wrapper, id, desc); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: FilteringWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, michael@0: HandleId id, michael@0: JS::MutableHandle desc) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); michael@0: if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc)) michael@0: return false; michael@0: return FilterSetter(cx, wrapper, id, desc); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: FilteringWrapper::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, michael@0: AutoIdVector &props) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); michael@0: return Base::getOwnPropertyNames(cx, wrapper, props) && michael@0: Filter(cx, wrapper, props); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: FilteringWrapper::enumerate(JSContext *cx, HandleObject wrapper, michael@0: AutoIdVector &props) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); michael@0: return Base::enumerate(cx, wrapper, props) && michael@0: Filter(cx, wrapper, props); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: FilteringWrapper::keys(JSContext *cx, HandleObject wrapper, michael@0: AutoIdVector &props) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); michael@0: return Base::keys(cx, wrapper, props) && michael@0: Filter(cx, wrapper, props); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: FilteringWrapper::iterate(JSContext *cx, HandleObject wrapper, michael@0: unsigned flags, MutableHandleValue vp) michael@0: { michael@0: assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); michael@0: // We refuse to trigger the iterator hook across chrome wrappers because michael@0: // we don't know how to censor custom iterator objects. Instead we trigger michael@0: // the default proxy iterate trap, which will ask enumerate() for the list michael@0: // of (censored) ids. michael@0: return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: FilteringWrapper::nativeCall(JSContext *cx, JS::IsAcceptableThis test, michael@0: JS::NativeImpl impl, JS::CallArgs args) michael@0: { michael@0: if (Policy::allowNativeCall(cx, test, impl)) michael@0: return Base::Permissive::nativeCall(cx, test, impl, args); michael@0: return Base::Restrictive::nativeCall(cx, test, impl, args); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: FilteringWrapper::defaultValue(JSContext *cx, HandleObject obj, michael@0: JSType hint, MutableHandleValue vp) michael@0: { michael@0: return Base::defaultValue(cx, obj, hint, vp); michael@0: } michael@0: michael@0: // With our entirely-opaque wrapper, the DefaultValue algorithm throws, michael@0: // causing spurious exceptions. Manually implement something benign. michael@0: template<> michael@0: bool michael@0: FilteringWrapper michael@0: ::defaultValue(JSContext *cx, HandleObject obj, michael@0: JSType hint, MutableHandleValue vp) michael@0: { michael@0: JSString *str = JS_NewStringCopyZ(cx, "[Opaque]"); michael@0: if (!str) michael@0: return false; michael@0: vp.set(JS::StringValue(str)); michael@0: return true; michael@0: } michael@0: michael@0: michael@0: template michael@0: bool michael@0: FilteringWrapper::enter(JSContext *cx, HandleObject wrapper, michael@0: HandleId id, Wrapper::Action act, bool *bp) michael@0: { michael@0: // This is a super ugly hacky to get around Xray Resolve wonkiness. michael@0: // michael@0: // Basically, XPCWN Xrays sometimes call into the Resolve hook of the michael@0: // scriptable helper, and pass the wrapper itself as the object upon which michael@0: // the resolve is happening. Then, special handling happens in michael@0: // XrayWrapper::defineProperty to detect the resolve and redefine the michael@0: // property on the holder. Really, we should just pass the holder itself to michael@0: // NewResolve, but there's too much code in nsDOMClassInfo that assumes this michael@0: // isn't the case (in particular, code expects to be able to look up michael@0: // properties on the object, which doesn't work for the holder). Given that michael@0: // these hooks are going away eventually with the new DOM bindings, let's michael@0: // just hack around this for now. michael@0: if (XrayUtils::IsXrayResolving(cx, wrapper, id)) { michael@0: *bp = true; michael@0: return true; michael@0: } michael@0: if (!Policy::check(cx, wrapper, id, act)) { michael@0: *bp = JS_IsExceptionPending(cx) ? false : Policy::deny(act, id); michael@0: return false; michael@0: } michael@0: *bp = true; michael@0: return true; michael@0: } michael@0: michael@0: #define XOW FilteringWrapper michael@0: #define DXOW FilteringWrapper michael@0: #define NNXOW FilteringWrapper michael@0: #define NNXOWC FilteringWrapper michael@0: #define GO FilteringWrapper michael@0: template<> XOW XOW::singleton(0); michael@0: template<> DXOW DXOW::singleton(0); michael@0: template<> NNXOW NNXOW::singleton(0); michael@0: template<> NNXOWC NNXOWC::singleton(0); michael@0: michael@0: template<> GO GO::singleton(0); michael@0: michael@0: template class XOW; michael@0: template class DXOW; michael@0: template class NNXOW; michael@0: template class NNXOWC; michael@0: template class ChromeObjectWrapperBase; michael@0: template class GO; michael@0: }