summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/wrappers/AddonWrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/wrappers/AddonWrapper.cpp')
-rw-r--r--js/xpconnect/wrappers/AddonWrapper.cpp270
1 files changed, 270 insertions, 0 deletions
diff --git a/js/xpconnect/wrappers/AddonWrapper.cpp b/js/xpconnect/wrappers/AddonWrapper.cpp
new file mode 100644
index 000000000..eb1670b3a
--- /dev/null
+++ b/js/xpconnect/wrappers/AddonWrapper.cpp
@@ -0,0 +1,270 @@
+/* -*- 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 "AddonWrapper.h"
+#include "WrapperFactory.h"
+#include "XrayWrapper.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "nsIAddonInterposition.h"
+#include "xpcprivate.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "nsGlobalWindow.h"
+
+#include "GeckoProfiler.h"
+
+#include "nsID.h"
+
+using namespace js;
+using namespace JS;
+
+namespace xpc {
+
+bool
+InterposeProperty(JSContext* cx, HandleObject target, const nsIID* iid, HandleId id,
+ MutableHandle<PropertyDescriptor> descriptor)
+{
+ // We only want to do interpostion on DOM instances and
+ // wrapped natives.
+ RootedObject unwrapped(cx, UncheckedUnwrap(target));
+ const js::Class* clasp = js::GetObjectClass(unwrapped);
+ bool isCPOW = jsipc::IsWrappedCPOW(unwrapped);
+ if (!mozilla::dom::IsDOMClass(clasp) &&
+ !IS_WN_CLASS(clasp) &&
+ !IS_PROTO_CLASS(clasp) &&
+ clasp != &OuterWindowProxyClass &&
+ !isCPOW) {
+ return true;
+ }
+
+ XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(cx));
+ MOZ_ASSERT(scope->HasInterposition());
+
+ nsCOMPtr<nsIAddonInterposition> interp = scope->GetInterposition();
+ InterpositionWhitelist* wl = XPCWrappedNativeScope::GetInterpositionWhitelist(interp);
+ // We do InterposeProperty only if the id is on the whitelist of the interpostion
+ // or if the target is a CPOW.
+ if ((!wl || !wl->has(JSID_BITS(id.get()))) && !isCPOW)
+ return true;
+
+ JSAddonId* addonId = AddonIdOfObject(target);
+ RootedValue addonIdValue(cx, StringValue(StringOfAddonId(addonId)));
+ RootedValue prop(cx, IdToValue(id));
+ RootedValue targetValue(cx, ObjectValue(*target));
+ RootedValue descriptorVal(cx);
+ nsresult rv = interp->InterposeProperty(addonIdValue, targetValue,
+ iid, prop, &descriptorVal);
+ if (NS_FAILED(rv)) {
+ xpc::Throw(cx, rv);
+ return false;
+ }
+
+ if (!descriptorVal.isObject())
+ return true;
+
+ // We need to be careful parsing descriptorVal. |cx| is in the compartment
+ // of the add-on and the descriptor is in the compartment of the
+ // interposition. We could wrap the descriptor in the add-on's compartment
+ // and then parse it. However, parsing the descriptor fetches properties
+ // from it, and we would try to interpose on those property accesses. So
+ // instead we parse in the interposition's compartment and then wrap the
+ // descriptor.
+
+ {
+ JSAutoCompartment ac(cx, &descriptorVal.toObject());
+ if (!JS::ObjectToCompletePropertyDescriptor(cx, target, descriptorVal, descriptor))
+ return false;
+ }
+
+ // Always make the property non-configurable regardless of what the
+ // interposition wants.
+ descriptor.setAttributes(descriptor.attributes() | JSPROP_PERMANENT);
+
+ if (!JS_WrapPropertyDescriptor(cx, descriptor))
+ return false;
+
+ return true;
+}
+
+bool
+InterposeCall(JSContext* cx, JS::HandleObject target, const JS::CallArgs& args, bool* done)
+{
+ *done = false;
+ XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(cx));
+ MOZ_ASSERT(scope->HasInterposition());
+
+ nsCOMPtr<nsIAddonInterposition> interp = scope->GetInterposition();
+
+ RootedObject unwrappedTarget(cx, UncheckedUnwrap(target));
+ XPCWrappedNativeScope* targetScope = ObjectScope(unwrappedTarget);
+ bool hasInterpostion = targetScope->HasCallInterposition();
+
+ if (!hasInterpostion)
+ return true;
+
+ // If there is a call interpostion, we don't want to propogate the
+ // call to Base:
+ *done = true;
+
+ JSAddonId* addonId = AddonIdOfObject(target);
+ RootedValue addonIdValue(cx, StringValue(StringOfAddonId(addonId)));
+ RootedValue targetValue(cx, ObjectValue(*target));
+ RootedValue thisValue(cx, args.thisv());
+ RootedObject argsArray(cx, ConvertArgsToArray(cx, args));
+ if (!argsArray)
+ return false;
+
+ RootedValue argsVal(cx, ObjectValue(*argsArray));
+ RootedValue returnVal(cx);
+
+ nsresult rv = interp->InterposeCall(addonIdValue, targetValue,
+ thisValue, argsVal, args.rval());
+ if (NS_FAILED(rv)) {
+ xpc::Throw(cx, rv);
+ return false;
+ }
+
+ return true;
+}
+
+template<typename Base>
+bool AddonWrapper<Base>::call(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ const JS::CallArgs& args) const
+{
+ bool done = false;
+ if (!InterposeCall(cx, wrapper, args, &done))
+ return false;
+
+ return done || Base::call(cx, wrapper, args);
+}
+
+template<typename Base>
+bool
+AddonWrapper<Base>::getPropertyDescriptor(JSContext* cx, HandleObject wrapper,
+ HandleId id, MutableHandle<PropertyDescriptor> desc) const
+{
+ if (!InterposeProperty(cx, wrapper, nullptr, id, desc))
+ return false;
+
+ if (desc.object())
+ return true;
+
+ return Base::getPropertyDescriptor(cx, wrapper, id, desc);
+}
+
+template<typename Base>
+bool
+AddonWrapper<Base>::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper,
+ HandleId id, MutableHandle<PropertyDescriptor> desc) const
+{
+ if (!InterposeProperty(cx, wrapper, nullptr, id, desc))
+ return false;
+
+ if (desc.object())
+ return true;
+
+ return Base::getOwnPropertyDescriptor(cx, wrapper, id, desc);
+}
+
+template<typename Base>
+bool
+AddonWrapper<Base>::get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<Value> receiver,
+ JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ Rooted<PropertyDescriptor> desc(cx);
+ if (!InterposeProperty(cx, wrapper, nullptr, id, &desc))
+ return false;
+
+ if (!desc.object())
+ return Base::get(cx, wrapper, receiver, id, vp);
+
+ if (desc.getter()) {
+ return Call(cx, receiver, desc.getterObject(), HandleValueArray::empty(), vp);
+ } else {
+ vp.set(desc.value());
+ return true;
+ }
+}
+
+template<typename Base>
+bool
+AddonWrapper<Base>::set(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::HandleValue v,
+ JS::HandleValue receiver, JS::ObjectOpResult& result) const
+{
+ Rooted<PropertyDescriptor> desc(cx);
+ if (!InterposeProperty(cx, wrapper, nullptr, id, &desc))
+ return false;
+
+ if (!desc.object())
+ return Base::set(cx, wrapper, id, v, receiver, result);
+
+ if (desc.setter()) {
+ MOZ_ASSERT(desc.hasSetterObject());
+ JS::AutoValueVector args(cx);
+ if (!args.append(v))
+ return false;
+ RootedValue fval(cx, ObjectValue(*desc.setterObject()));
+ RootedValue ignored(cx);
+ if (!JS::Call(cx, receiver, fval, args, &ignored))
+ return false;
+ return result.succeed();
+ }
+
+ return result.failCantSetInterposed();
+}
+
+template<typename Base>
+bool
+AddonWrapper<Base>::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
+ Handle<PropertyDescriptor> desc,
+ ObjectOpResult& result) const
+{
+ Rooted<PropertyDescriptor> interpDesc(cx);
+ if (!InterposeProperty(cx, wrapper, nullptr, id, &interpDesc))
+ return false;
+
+ if (!interpDesc.object())
+ return Base::defineProperty(cx, wrapper, id, desc, result);
+
+ js::ReportASCIIErrorWithId(cx, "unable to modify interposed property %s", id);
+ return false;
+}
+
+template<typename Base>
+bool
+AddonWrapper<Base>::delete_(JSContext* cx, HandleObject wrapper, HandleId id,
+ ObjectOpResult& result) const
+{
+ Rooted<PropertyDescriptor> desc(cx);
+ if (!InterposeProperty(cx, wrapper, nullptr, id, &desc))
+ return false;
+
+ if (!desc.object())
+ return Base::delete_(cx, wrapper, id, result);
+
+ js::ReportASCIIErrorWithId(cx, "unable to delete interposed property %s", id);
+ return false;
+}
+
+#define AddonWrapperCC AddonWrapper<CrossCompartmentWrapper>
+#define AddonWrapperXrayXPCWN AddonWrapper<PermissiveXrayXPCWN>
+#define AddonWrapperXrayDOM AddonWrapper<PermissiveXrayDOM>
+
+template<> const AddonWrapperCC AddonWrapperCC::singleton(0);
+template<> const AddonWrapperXrayXPCWN AddonWrapperXrayXPCWN::singleton(0);
+template<> const AddonWrapperXrayDOM AddonWrapperXrayDOM::singleton(0);
+
+template class AddonWrapperCC;
+template class AddonWrapperXrayXPCWN;
+template class AddonWrapperXrayDOM;
+
+#undef AddonWrapperCC
+#undef AddonWrapperXrayXPCWN
+#undef AddonWrapperXrayDOM
+
+} // namespace xpc