diff -r 000000000000 -r 6474c204b198 dom/bindings/DOMJSProxyHandler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/bindings/DOMJSProxyHandler.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,371 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=2 sw=2 et tw=99 ft=cpp: */ +/* 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 "mozilla/dom/DOMJSProxyHandler.h" +#include "xpcpublic.h" +#include "xpcprivate.h" +#include "XPCQuickStubs.h" +#include "XPCWrapper.h" +#include "WrapperFactory.h" +#include "nsDOMClassInfo.h" +#include "nsWrapperCacheInlines.h" +#include "mozilla/dom/BindingUtils.h" + +#include "jsapi.h" + +using namespace JS; + +namespace mozilla { +namespace dom { + +jsid s_length_id = JSID_VOID; + +bool +DefineStaticJSVals(JSContext* cx) +{ + return InternJSString(cx, s_length_id, "length"); +} + + +const char HandlerFamily = 0; + +js::DOMProxyShadowsResult +DOMProxyShadows(JSContext* cx, JS::Handle proxy, JS::Handle id) +{ + JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO); + if (v.isObject()) { + bool hasOwn; + Rooted object(cx, &v.toObject()); + if (!JS_AlreadyHasOwnPropertyById(cx, object, id, &hasOwn)) + return js::ShadowCheckFailed; + + return hasOwn ? js::Shadows : js::DoesntShadow; + } + + if (v.isUndefined()) { + return js::DoesntShadow; + } + + bool hasOwn; + if (!GetProxyHandler(proxy)->hasOwn(cx, proxy, id, &hasOwn)) + return js::ShadowCheckFailed; + + return hasOwn ? js::Shadows : js::DoesntShadowUnique; +} + +// Store the information for the specialized ICs. +struct SetDOMProxyInformation +{ + SetDOMProxyInformation() { + js::SetDOMProxyInformation((const void*) &HandlerFamily, + js::PROXY_EXTRA_SLOT + JSPROXYSLOT_EXPANDO, DOMProxyShadows); + } +}; + +SetDOMProxyInformation gSetDOMProxyInformation; + +// static +JSObject* +DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj) +{ + MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object"); + JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO); + if (v.isUndefined()) { + return nullptr; + } + + if (v.isObject()) { + js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue()); + XPCWrappedNativeScope* scope = xpc::MaybeGetObjectScope(obj); + if (scope) { + scope->RemoveDOMExpandoObject(obj); + } + } else { + js::ExpandoAndGeneration* expandoAndGeneration = + static_cast(v.toPrivate()); + v = expandoAndGeneration->expando; + if (v.isUndefined()) { + return nullptr; + } + expandoAndGeneration->expando = UndefinedValue(); + } + + + return &v.toObject(); +} + +// static +JSObject* +DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle obj) +{ + NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object"); + JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO); + if (v.isObject()) { + return &v.toObject(); + } + + js::ExpandoAndGeneration* expandoAndGeneration; + if (!v.isUndefined()) { + expandoAndGeneration = static_cast(v.toPrivate()); + if (expandoAndGeneration->expando.isObject()) { + return &expandoAndGeneration->expando.toObject(); + } + } else { + expandoAndGeneration = nullptr; + } + + JS::Rooted parent(cx, js::GetObjectParent(obj)); + JS::Rooted expando(cx, + JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), parent)); + if (!expando) { + return nullptr; + } + + nsISupports* native = UnwrapDOMObject(obj); + nsWrapperCache* cache; + CallQueryInterface(native, &cache); + if (expandoAndGeneration) { + cache->PreserveWrapper(native); + expandoAndGeneration->expando.setObject(*expando); + + return expando; + } + + XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj); + if (!scope->RegisterDOMExpandoObject(obj)) { + return nullptr; + } + + cache->SetPreservingWrapper(true); + js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando)); + + return expando; +} + +bool +DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle proxy, bool *extensible) +{ + // always extensible per WebIDL + *extensible = true; + return true; +} + +bool +DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle proxy) +{ + // Throw a TypeError, per WebIDL. + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, + JSMSG_CANT_CHANGE_EXTENSIBILITY); + return false; +} + +bool +BaseDOMProxyHandler::getPropertyDescriptor(JSContext* cx, + JS::Handle proxy, + JS::Handle id, + MutableHandle desc) +{ + if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) { + return false; + } + if (desc.object()) { + return true; + } + + JS::Rooted proto(cx); + if (!js::GetObjectProto(cx, proxy, &proto)) { + return false; + } + if (!proto) { + desc.object().set(nullptr); + return true; + } + + return JS_GetPropertyDescriptorById(cx, proto, id, desc); +} + +bool +BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx, + JS::Handle proxy, + JS::Handle id, + MutableHandle desc) +{ + return getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ false, + desc); +} + +bool +DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle proxy, JS::Handle id, + MutableHandle desc, bool* defined) +{ + if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) { + return JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING | JSREPORT_STRICT | + JSREPORT_STRICT_MODE_ERROR, + js_GetErrorMessage, nullptr, + JSMSG_GETTER_ONLY); + } + + if (xpc::WrapperFactory::IsXrayWrapper(proxy)) { + return true; + } + + JSObject* expando = EnsureExpandoObject(cx, proxy); + if (!expando) { + return false; + } + + bool dummy; + return js_DefineOwnProperty(cx, expando, id, desc, &dummy); +} + +bool +DOMProxyHandler::set(JSContext *cx, Handle proxy, Handle receiver, + Handle id, bool strict, MutableHandle vp) +{ + MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), + "Should not have a XrayWrapper here"); + bool done; + if (!setCustom(cx, proxy, id, vp, &done)) { + return false; + } + if (done) { + return true; + } + + // Make sure to ignore our named properties when checking for own + // property descriptors for a set. + JS::Rooted desc(cx); + if (!getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ true, + &desc)) { + return false; + } + bool descIsOwn = desc.object() != nullptr; + if (!desc.object()) { + // Don't just use getPropertyDescriptor, unlike BaseProxyHandler::set, + // because that would call getOwnPropertyDescriptor on ourselves. Instead, + // directly delegate to the proto, if any. + JS::Rooted proto(cx); + if (!js::GetObjectProto(cx, proxy, &proto)) { + return false; + } + if (proto && !JS_GetPropertyDescriptorById(cx, proto, id, &desc)) { + return false; + } + } + + return js::SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, + &desc, descIsOwn, strict, vp); +} + +bool +DOMProxyHandler::delete_(JSContext* cx, JS::Handle proxy, + JS::Handle id, bool* bp) +{ + JS::Rooted expando(cx); + if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) { + return JS_DeletePropertyById2(cx, expando, id, bp); + } + + *bp = true; + return true; +} + +bool +BaseDOMProxyHandler::enumerate(JSContext* cx, JS::Handle proxy, + AutoIdVector& props) +{ + JS::Rooted proto(cx); + if (!JS_GetPrototype(cx, proxy, &proto)) { + return false; + } + return keys(cx, proxy, props) && + (!proto || js::GetPropertyNames(cx, proto, 0, &props)); +} + +bool +BaseDOMProxyHandler::watch(JSContext* cx, JS::Handle proxy, JS::Handle id, + JS::Handle callable) +{ + return js::WatchGuts(cx, proxy, id, callable); +} + +bool +BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle proxy, JS::Handle id) +{ + return js::UnwatchGuts(cx, proxy, id); +} + +bool +BaseDOMProxyHandler::getOwnPropertyNames(JSContext* cx, + JS::Handle proxy, + JS::AutoIdVector& props) +{ + return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN, props); +} + +bool +BaseDOMProxyHandler::keys(JSContext* cx, + JS::Handle proxy, + JS::AutoIdVector& props) +{ + return ownPropNames(cx, proxy, JSITER_OWNONLY, props); +} + +bool +DOMProxyHandler::has(JSContext* cx, JS::Handle proxy, JS::Handle id, bool* bp) +{ + if (!hasOwn(cx, proxy, id, bp)) { + return false; + } + + if (*bp) { + // We have the property ourselves; no need to worry about our prototype + // chain. + return true; + } + + // OK, now we have to look at the proto + JS::Rooted proto(cx); + if (!js::GetObjectProto(cx, proxy, &proto)) { + return false; + } + if (!proto) { + return true; + } + bool protoHasProp; + bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp); + if (ok) { + *bp = protoHasProp; + } + return ok; +} + +int32_t +IdToInt32(JSContext* cx, JS::Handle id) +{ + JS::Rooted idval(cx); + double array_index; + int32_t i; + if (!::JS_IdToValue(cx, id, &idval) || + !JS::ToNumber(cx, idval, &array_index) || + !::JS_DoubleIsInt32(array_index, &i)) { + return -1; + } + + return i; +} + +bool +DOMProxyHandler::setCustom(JSContext* cx, JS::Handle proxy, JS::Handle id, + JS::MutableHandle vp, bool *done) +{ + *done = false; + return true; +} + +} // namespace dom +} // namespace mozilla