123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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 (!cache) {
- return expando;
- }
-
- 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::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
|