tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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