summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/src/JSServices.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/src/JSServices.cpp')
-rw-r--r--js/xpconnect/src/JSServices.cpp172
1 files changed, 172 insertions, 0 deletions
diff --git a/js/xpconnect/src/JSServices.cpp b/js/xpconnect/src/JSServices.cpp
new file mode 100644
index 0000000000..abdf39800f
--- /dev/null
+++ b/js/xpconnect/src/JSServices.cpp
@@ -0,0 +1,172 @@
+/* -*- 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 "xpcprivate.h"
+#include "GeckoProfiler.h"
+#include "StaticComponents.h"
+#include "mozilla/ErrorResult.h"
+#include "js/String.h" // JS::LinearStringHasLatin1Chars
+#include "nsJSUtils.h"
+
+using namespace mozilla;
+using namespace JS;
+
+namespace xpc {
+
+static bool Services_NewEnumerate(JSContext* cx, HandleObject obj,
+ MutableHandleIdVector properties,
+ bool enumerableOnly);
+static bool Services_Resolve(JSContext* cx, HandleObject obj, HandleId id,
+ bool* resolvedp);
+static bool Services_MayResolve(const JSAtomState& names, jsid id,
+ JSObject* maybeObj);
+
+static const JSClassOps sServices_ClassOps = {
+ nullptr, // addProperty
+ nullptr, // delProperty
+ nullptr, // enumerate
+ Services_NewEnumerate, // newEnumerate
+ Services_Resolve, // resolve
+ Services_MayResolve, // mayResolve
+ nullptr, // finalize
+ nullptr, // call
+ nullptr, // hasInstance
+ nullptr, // construct
+ nullptr, // trace
+};
+
+static const JSClass sServices_Class = {"JSServices", 0, &sServices_ClassOps};
+
+JSObject* NewJSServices(JSContext* cx) {
+ return JS_NewObject(cx, &sServices_Class);
+}
+
+static bool Services_NewEnumerate(JSContext* cx, HandleObject obj,
+ MutableHandleIdVector properties,
+ bool enumerableOnly) {
+ auto services = xpcom::StaticComponents::GetJSServices();
+
+ if (!properties.reserve(services.Length())) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+
+ RootedId id(cx);
+ RootedString name(cx);
+ for (const auto& service : services) {
+ name = JS_AtomizeString(cx, service.Name().get());
+ if (!name || !JS_StringToId(cx, name, &id)) {
+ return false;
+ }
+ properties.infallibleAppend(id);
+ }
+
+ return true;
+}
+
+static JSLinearString* GetNameIfLatin1(jsid id) {
+ if (JSID_IS_STRING(id)) {
+ JSLinearString* name = JSID_TO_LINEAR_STRING(id);
+ if (JS::LinearStringHasLatin1Chars(name)) {
+ return name;
+ }
+ }
+ return nullptr;
+}
+
+static bool GetServiceImpl(JSContext* cx, const xpcom::JSServiceEntry& service,
+ JS::MutableHandleObject aObj, ErrorResult& aRv) {
+ nsresult rv;
+ nsCOMPtr<nsISupports> inst = service.Module().GetService(&rv);
+ if (!inst) {
+ aRv.Throw(rv);
+ return false;
+ }
+
+ auto ifaces = service.Interfaces();
+
+ if (ifaces.Length() == 0) {
+ // If we weren't given any interfaces, we're expecting either a WebIDL
+ // object or a wrapped JS object. In the former case, the object will handle
+ // its own wrapping, and there's nothing to do. In the latter case, we want
+ // to unwrap the underlying JS object.
+ if (nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(inst)) {
+ aObj.set(wrappedJS->GetJSObject());
+ return !!aObj;
+ }
+ }
+
+ JS::RootedValue val(cx);
+
+ const nsIID* iid = ifaces.Length() ? ifaces[0] : nullptr;
+ xpcObjectHelper helper(inst);
+ if (!XPCConvert::NativeInterface2JSObject(cx, &val, helper, iid,
+ /* allowNativeWrapper */ true,
+ &rv)) {
+ aRv.Throw(rv);
+ return false;
+ }
+
+ if (ifaces.Length() > 1) {
+ auto* wn = XPCWrappedNative::Get(&val.toObject());
+ for (const nsIID* iid : Span(ifaces).From(1)) {
+ // Ignore any supplemental interfaces that aren't implemented. Tests do
+ // weird things with some services, and JS can generally handle the
+ // interfaces being absent.
+ Unused << wn->FindTearOff(cx, *iid);
+ }
+ }
+
+ aObj.set(&val.toObject());
+ return true;
+}
+
+static JSObject* GetService(JSContext* cx, const xpcom::JSServiceEntry& service,
+ ErrorResult& aRv) {
+ JS::RootedObject obj(cx);
+ if (!GetServiceImpl(cx, service, &obj, aRv)) {
+ return nullptr;
+ }
+ return obj;
+}
+
+static bool Services_Resolve(JSContext* cx, HandleObject obj, HandleId id,
+ bool* resolvedp) {
+ *resolvedp = false;
+ JSLinearString* name = GetNameIfLatin1(id);
+ if (!name) {
+ return true;
+ }
+
+ nsAutoJSLinearCString nameStr(name);
+ if (const auto* service = xpcom::JSServiceEntry::Lookup(nameStr)) {
+ AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE("Services_Resolve",
+ OTHER, service->Name());
+ *resolvedp = true;
+
+ ErrorResult rv;
+ JS::RootedValue val(cx);
+
+ val.setObjectOrNull(GetService(cx, *service, rv));
+ if (rv.MaybeSetPendingException(cx)) {
+ return false;
+ }
+
+ return JS_DefinePropertyById(cx, obj, id, val, JSPROP_ENUMERATE);
+ }
+ return true;
+}
+
+static bool Services_MayResolve(const JSAtomState& names, jsid id,
+ JSObject* maybeObj) {
+ if (JSLinearString* name = GetNameIfLatin1(id)) {
+ nsAutoJSLinearCString nameStr(name);
+ return xpcom::JSServiceEntry::Lookup(nameStr);
+ }
+ return false;
+}
+
+} // namespace xpc