JSServices.cpp (5683B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "xpcprivate.h" 8 #include "StaticComponents.h" 9 #include "mozilla/ErrorResult.h" 10 #include "mozilla/ProfilerLabels.h" 11 #include "js/Debug.h" // JS::dbg::ShouldAvoidSideEffects 12 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById 13 #include "js/String.h" // JS::LinearStringHasLatin1Chars 14 #include "nsJSUtils.h" 15 16 using namespace mozilla; 17 using namespace JS; 18 19 namespace xpc { 20 21 static bool Services_NewEnumerate(JSContext* cx, HandleObject obj, 22 MutableHandleIdVector properties, 23 bool enumerableOnly); 24 static bool Services_Resolve(JSContext* cx, HandleObject obj, HandleId id, 25 bool* resolvedp); 26 static bool Services_MayResolve(const JSAtomState& names, jsid id, 27 JSObject* maybeObj); 28 29 static const JSClassOps sServices_ClassOps = { 30 nullptr, // addProperty 31 nullptr, // delProperty 32 nullptr, // enumerate 33 Services_NewEnumerate, // newEnumerate 34 Services_Resolve, // resolve 35 Services_MayResolve, // mayResolve 36 nullptr, // finalize 37 nullptr, // call 38 nullptr, // construct 39 nullptr, // trace 40 }; 41 42 static const JSClass sServices_Class = {"JSServices", 0, &sServices_ClassOps}; 43 44 JSObject* NewJSServices(JSContext* cx) { 45 return JS_NewObject(cx, &sServices_Class); 46 } 47 48 static bool Services_NewEnumerate(JSContext* cx, HandleObject obj, 49 MutableHandleIdVector properties, 50 bool enumerableOnly) { 51 auto services = xpcom::StaticComponents::GetJSServices(); 52 53 if (!properties.reserve(services.Length())) { 54 JS_ReportOutOfMemory(cx); 55 return false; 56 } 57 58 RootedId id(cx); 59 RootedString name(cx); 60 for (const auto& service : services) { 61 name = JS_AtomizeString(cx, service.Name().get()); 62 if (!name || !JS_StringToId(cx, name, &id)) { 63 return false; 64 } 65 properties.infallibleAppend(id); 66 } 67 68 return true; 69 } 70 71 static JSLinearString* GetNameIfLatin1(jsid id) { 72 if (id.isString()) { 73 JSLinearString* name = id.toLinearString(); 74 if (JS::LinearStringHasLatin1Chars(name)) { 75 return name; 76 } 77 } 78 return nullptr; 79 } 80 81 static bool GetServiceImpl(JSContext* cx, const xpcom::JSServiceEntry& service, 82 JS::MutableHandleObject aObj, ErrorResult& aRv) { 83 nsresult rv; 84 nsCOMPtr<nsISupports> inst = service.Module().GetService(&rv); 85 if (!inst) { 86 aRv.Throw(rv); 87 return false; 88 } 89 90 auto ifaces = service.Interfaces(); 91 92 if (ifaces.Length() == 0) { 93 // If we weren't given any interfaces, we're expecting either a WebIDL 94 // object or a wrapped JS object. In the former case, the object will handle 95 // its own wrapping, and there's nothing to do. In the latter case, we want 96 // to unwrap the underlying JS object. 97 if (nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(inst)) { 98 aObj.set(wrappedJS->GetJSObject()); 99 return !!aObj; 100 } 101 } 102 103 JS::RootedValue val(cx); 104 105 const nsIID* iid = ifaces.Length() ? ifaces[0] : nullptr; 106 xpcObjectHelper helper(inst); 107 if (!XPCConvert::NativeInterface2JSObject(cx, &val, helper, iid, 108 /* allowNativeWrapper */ true, 109 &rv)) { 110 aRv.Throw(rv); 111 return false; 112 } 113 114 if (ifaces.Length() > 1) { 115 auto* wn = XPCWrappedNative::Get(&val.toObject()); 116 for (const nsIID* iid : Span(ifaces).From(1)) { 117 // Ignore any supplemental interfaces that aren't implemented. Tests do 118 // weird things with some services, and JS can generally handle the 119 // interfaces being absent. 120 (void)wn->FindTearOff(cx, *iid); 121 } 122 } 123 124 aObj.set(&val.toObject()); 125 return true; 126 } 127 128 static JSObject* GetService(JSContext* cx, const xpcom::JSServiceEntry& service, 129 ErrorResult& aRv) { 130 JS::RootedObject obj(cx); 131 if (!GetServiceImpl(cx, service, &obj, aRv)) { 132 return nullptr; 133 } 134 return obj; 135 } 136 137 static bool Services_Resolve(JSContext* cx, HandleObject obj, HandleId id, 138 bool* resolvedp) { 139 *resolvedp = false; 140 if (JS::dbg::ShouldAvoidSideEffects(cx)) { 141 JS::ReportUncatchableException(cx); 142 return false; 143 } 144 145 JSLinearString* name = GetNameIfLatin1(id); 146 if (!name) { 147 return true; 148 } 149 150 nsAutoJSLinearCString nameStr(name); 151 if (const auto* service = xpcom::JSServiceEntry::Lookup(nameStr)) { 152 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE("Services_Resolve", 153 OTHER, service->Name()); 154 *resolvedp = true; 155 156 ErrorResult rv; 157 JS::RootedValue val(cx); 158 159 val.setObjectOrNull(GetService(cx, *service, rv)); 160 if (rv.MaybeSetPendingException(cx)) { 161 return false; 162 } 163 164 return JS_DefinePropertyById(cx, obj, id, val, JSPROP_ENUMERATE); 165 } 166 return true; 167 } 168 169 static bool Services_MayResolve(const JSAtomState& names, jsid id, 170 JSObject* maybeObj) { 171 if (JSLinearString* name = GetNameIfLatin1(id)) { 172 nsAutoJSLinearCString nameStr(name); 173 return xpcom::JSServiceEntry::Lookup(nameStr); 174 } 175 return false; 176 } 177 178 } // namespace xpc