summaryrefslogtreecommitdiffstats
path: root/dom/bindings/DOMJSProxyHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/bindings/DOMJSProxyHandler.cpp')
-rw-r--r--dom/bindings/DOMJSProxyHandler.cpp362
1 files changed, 362 insertions, 0 deletions
diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp
new file mode 100644
index 000000000..65e540bc1
--- /dev/null
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "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 AtomizeAndPinJSString(cx, s_length_id, "length");
+}
+
+const char DOMProxyHandler::family = 0;
+
+js::DOMProxyShadowsResult
+DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
+{
+ JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
+ JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
+ bool isOverrideBuiltins = !v.isObject() && !v.isUndefined();
+ if (expando) {
+ bool hasOwn;
+ if (!JS_AlreadyHasOwnPropertyById(cx, expando, id, &hasOwn))
+ return js::ShadowCheckFailed;
+
+ if (hasOwn) {
+ return isOverrideBuiltins ?
+ js::ShadowsViaIndirectExpando : js::ShadowsViaDirectExpando;
+ }
+ }
+
+ if (!isOverrideBuiltins) {
+ // Our expando, if any, didn't shadow, so we're not shadowing at all.
+ 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*) &DOMProxyHandler::family,
+ JSPROXYSLOT_EXPANDO, DOMProxyShadows);
+ }
+};
+
+SetDOMProxyInformation gSetDOMProxyInformation;
+
+// static
+void
+DOMProxyHandler::ClearExternalRefsForWrapperRelease(JSObject* obj)
+{
+ MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+ if (v.isUndefined()) {
+ // No expando.
+ return;
+ }
+
+ // See EnsureExpandoObject for the work we're trying to undo here.
+
+ if (v.isObject()) {
+ // Drop us from the DOM expando hashtable. Don't worry about clearing our
+ // slot reference to the expando; we're about to die anyway.
+ xpc::ObjectScope(obj)->RemoveDOMExpandoObject(obj);
+ return;
+ }
+
+ // Prevent having a dangling pointer to our expando from the
+ // ExpandoAndGeneration.
+ js::ExpandoAndGeneration* expandoAndGeneration =
+ static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+ expandoAndGeneration->expando = UndefinedValue();
+}
+
+// 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());
+ xpc::ObjectScope(obj)->RemoveDOMExpandoObject(obj);
+ } else {
+ js::ExpandoAndGeneration* expandoAndGeneration =
+ static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+ v = expandoAndGeneration->expando;
+ if (v.isUndefined()) {
+ return nullptr;
+ }
+ // We have to expose v to active JS here. The reason for that is that we
+ // might be in the middle of a GC right now. If our proxy hasn't been
+ // traced yet, when it _does_ get traced it won't trace the expando, since
+ // we're breaking that link. But the Rooted we're presumably being placed
+ // into is also not going to trace us, because Rooted marking is done at
+ // the very beginning of the GC. In that situation, we need to manually
+ // mark the expando as live here. JS::ExposeValueToActiveJS will do just
+ // that for us.
+ //
+ // We don't need to do this in the non-expandoAndGeneration case, because
+ // in that case our value is stored in a slot and slots will already mark
+ // the old thing live when the value in the slot changes.
+ JS::ExposeValueToActiveJS(v);
+ expandoAndGeneration->expando = UndefinedValue();
+ }
+
+
+ return &v.toObject();
+}
+
+// static
+JSObject*
+DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> 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<js::ExpandoAndGeneration*>(v.toPrivate());
+ if (expandoAndGeneration->expando.isObject()) {
+ return &expandoAndGeneration->expando.toObject();
+ }
+ } else {
+ expandoAndGeneration = nullptr;
+ }
+
+ JS::Rooted<JSObject*> expando(cx,
+ JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
+ if (!expando) {
+ return nullptr;
+ }
+
+ nsISupports* native = UnwrapDOMObject<nsISupports>(obj);
+ nsWrapperCache* cache;
+ CallQueryInterface(native, &cache);
+ if (expandoAndGeneration) {
+ cache->PreserveWrapper(native);
+ expandoAndGeneration->expando.setObject(*expando);
+
+ return expando;
+ }
+
+ if (!xpc::ObjectScope(obj)->RegisterDOMExpandoObject(obj)) {
+ return nullptr;
+ }
+
+ cache->SetPreservingWrapper(true);
+ js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
+
+ return expando;
+}
+
+bool
+DOMProxyHandler::preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::ObjectOpResult& result) const
+{
+ // always extensible per WebIDL
+ return result.failCantPreventExtensions();
+}
+
+bool
+DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) const
+{
+ *extensible = true;
+ return true;
+}
+
+bool
+BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id,
+ MutableHandle<PropertyDescriptor> desc) const
+{
+ return getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ false,
+ desc);
+}
+
+bool
+DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ Handle<PropertyDescriptor> desc,
+ JS::ObjectOpResult &result, bool *defined) const
+{
+ if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) {
+ return result.failGetterOnly();
+ }
+
+ if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
+ return result.succeed();
+ }
+
+ JS::Rooted<JSObject*> expando(cx, EnsureExpandoObject(cx, proxy));
+ if (!expando) {
+ return false;
+ }
+
+ if (!JS_DefinePropertyById(cx, expando, id, desc, result)) {
+ return false;
+ }
+ *defined = true;
+ return true;
+}
+
+bool
+DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<jsid> id,
+ Handle<JS::Value> v, Handle<JS::Value> receiver,
+ ObjectOpResult &result) const
+{
+ MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ "Should not have a XrayWrapper here");
+ bool done;
+ if (!setCustom(cx, proxy, id, v, &done)) {
+ return false;
+ }
+ if (done) {
+ return result.succeed();
+ }
+
+ // Make sure to ignore our named properties when checking for own
+ // property descriptors for a set.
+ JS::Rooted<PropertyDescriptor> ownDesc(cx);
+ if (!getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ true,
+ &ownDesc)) {
+ return false;
+ }
+ return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, ownDesc, result);
+}
+
+bool
+DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id, JS::ObjectOpResult &result) const
+{
+ JS::Rooted<JSObject*> expando(cx);
+ if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+ return JS_DeletePropertyById(cx, expando, id, result);
+ }
+
+ return result.succeed();
+}
+
+bool
+BaseDOMProxyHandler::watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::Handle<JSObject*> callable) const
+{
+ return js::WatchGuts(cx, proxy, id, callable);
+}
+
+bool
+BaseDOMProxyHandler::unwatch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id) const
+{
+ return js::UnwatchGuts(cx, proxy, id);
+}
+
+bool
+BaseDOMProxyHandler::ownPropertyKeys(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ JS::AutoIdVector& props) const
+{
+ return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
+}
+
+bool
+BaseDOMProxyHandler::getPrototypeIfOrdinary(JSContext* cx, JS::Handle<JSObject*> proxy,
+ bool* isOrdinary,
+ JS::MutableHandle<JSObject*> proto) const
+{
+ *isOrdinary = true;
+ proto.set(GetStaticPrototype(proxy));
+ return true;
+}
+
+bool
+BaseDOMProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ JS::AutoIdVector& props) const
+{
+ return ownPropNames(cx, proxy, JSITER_OWNONLY, props);
+}
+
+bool
+DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+ JS::Handle<JS::Value> v, bool *done) const
+{
+ *done = false;
+ return true;
+}
+
+//static
+JSObject *
+DOMProxyHandler::GetExpandoObject(JSObject *obj)
+{
+ MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+ if (v.isObject()) {
+ return &v.toObject();
+ }
+
+ if (v.isUndefined()) {
+ return nullptr;
+ }
+
+ js::ExpandoAndGeneration* expandoAndGeneration =
+ static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+ v = expandoAndGeneration->expando;
+ return v.isUndefined() ? nullptr : &v.toObject();
+}
+
+void
+ShadowingDOMProxyHandler::trace(JSTracer* trc, JSObject* proxy) const
+{
+ DOMProxyHandler::trace(trc, proxy);
+
+ MOZ_ASSERT(IsDOMProxy(proxy), "expected a DOM proxy object");
+ JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
+ MOZ_ASSERT(!v.isObject(), "Should not have expando object directly!");
+
+ if (v.isUndefined()) {
+ // This can happen if we GC while creating our object, before we get a
+ // chance to set up its JSPROXYSLOT_EXPANDO slot.
+ return;
+ }
+
+ js::ExpandoAndGeneration* expandoAndGeneration =
+ static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+ JS::TraceEdge(trc, &expandoAndGeneration->expando,
+ "Shadowing DOM proxy expando");
+}
+
+} // namespace dom
+} // namespace mozilla