Bug 1524688: Part 1a - Support static registration JS components. r=mccr8
☠☠ backed out by 3b1b94e39795 ☠ ☠
authorKris Maglione <maglione.k@gmail.com>
Tue, 29 Jan 2019 17:46:27 -0800
changeset 458973 68eb174a337b
parent 458972 625f71135038
child 458974 d5dca413e2da
push id111909
push usermaglione.k@gmail.com
push dateThu, 14 Feb 2019 02:30:40 +0000
treeherdermozilla-inbound@81dc12cc9257 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1524688
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1524688: Part 1a - Support static registration JS components. r=mccr8
build/docs/defining-xpcom-components.rst
xpcom/components/StaticComponents.cpp.in
xpcom/components/gen_static_components.py
xpcom/components/nsComponentManager.cpp
xpcom/components/nsComponentManager.h
--- a/build/docs/defining-xpcom-components.rst
+++ b/build/docs/defining-xpcom-components.rst
@@ -97,16 +97,22 @@ Class definitions may have the following
   The fully-qualified name of a constructor function to call in order to
   create instances of this class. This function must be declared in one of the
   headers listed in the ``headers`` property, must take no arguments, and must
   return ``already_AddRefed<iface>`` where ``iface`` is the interface provided
   in the ``type`` property.
   
   This property is incompatible with ``legacy_constructor``.
 
+``jsm`` (optional)
+  If provided, must be the URL of a JavaScript module which contains a
+  JavaScript implementation of the component. The ``constructor`` property
+  must contain the name of an exported function which can be constructed to
+  create a new instance of the component.
+
 ``legacy_constructor`` (optional)
   This property is deprecated, and should not be used in new code.
   
   The fully-qualified name of a constructor function to call in order to
   create instances of this class. This function must be declared in one of the
   headers listed in the ``headers`` property, and must have the signature
   ``nsresult(nsISupports* aOuter, const nsID& aIID, void** aResult)``, and
   behave equivalently to ``nsIFactory::CreateInstance``.
--- a/xpcom/components/StaticComponents.cpp.in
+++ b/xpcom/components/StaticComponents.cpp.in
@@ -4,34 +4,40 @@
  * 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 "StaticComponents.h"
 
 #include "mozilla/PerfectHash.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozJSComponentLoader.h"
 #include "nsCOMPtr.h"
 #include "nsComponentManager.h"
+#include "nsContentUtils.h"
 #include "nsIFactory.h"
 #include "nsISupports.h"
 #include "nsString.h"
 
 // Cleanup pollution from zipstruct.h
 #undef UNSUPPORTED
 
 // Public includes
 //# @includes@
 
 // Relative includes
 //# @relative_includes@
 
 //# @decls@
 
 namespace mozilla {
+
+using dom::AutoJSAPI;
+
 namespace xpcom {
 
 static constexpr uint32_t kNoContractID = 0xffffffff;
 
 namespace {
 // Template helpers for constructor function sanity checks.
 template <typename T>
 struct RemoveAlreadyAddRefed {
@@ -78,16 +84,47 @@ nsCString ContractEntry::ContractID() co
   return GetString(mContractID);
 }
 
 bool ContractEntry::Matches(const nsACString& aContractID) const {
   return aContractID == ContractID() && Module().Active();
 }
 
 
+static nsresult ConstructJSMComponent(const nsACString& aURI,
+                                      const char* aConstructor,
+                                      nsISupports** aResult) {
+  if (!nsComponentManagerImpl::JSLoaderReady()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  AutoJSAPI jsapi;
+  MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
+  JSContext* cx = jsapi.cx();
+
+  JS::RootedObject global(cx);
+  JS::RootedObject exports(cx);
+  MOZ_TRY(mozJSComponentLoader::Get()->Import(cx, aURI, &global, &exports));
+
+  JS::RootedValue ctor(cx);
+  if (!JS_GetProperty(cx, exports, aConstructor, &ctor) ||
+      !ctor.isObject()) {
+    return NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
+  }
+
+  JS::RootedObject inst(cx);
+  if (!JS::Construct(cx, ctor, JS::HandleValueArray::empty(), &inst)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return nsContentUtils::XPConnect()->WrapJS(cx, inst, NS_GET_IID(nsISupports),
+                                             (void**)aResult);
+}
+
+
 //# @module_cid_table@
 
 //# @module_contract_id_table@
 
 static inline bool CalledInit(size_t aIdx) {
   return GetBit(gInitCalled, aIdx);
 }
 
--- a/xpcom/components/gen_static_components.py
+++ b/xpcom/components/gen_static_components.py
@@ -213,16 +213,18 @@ class ModuleEntry(object):
         # `CallInitFunc(init_idx)` call will be genrated before calling this
         # module's constructor.
         self.init_idx = init_idx
 
         self.constructor = data.get('constructor', None)
         self.legacy_constructor = data.get('legacy_constructor', None)
         self.init_method = data.get('init_method', [])
 
+        self.jsm = data.get('jsm', None)
+
         self.external = data.get('external', not (self.headers or
                                                   self.legacy_constructor))
         self.singleton = data.get('singleton', False)
         self.overridable = data.get('overridable', False)
 
         if 'name' in data:
             self.anonymous = False
             self.name = data['name']
@@ -231,17 +233,25 @@ class ModuleEntry(object):
             self.name = 'Anonymous%03d' % ModuleEntry.next_anon_id
             ModuleEntry.next_anon_id += 1
 
         def error(str_):
             raise Exception("Error defining component %s (%s): %s" % (
                 str(self.cid), ', '.join(map(repr, self.contract_ids)),
                 str_))
 
-        if self.external:
+        if self.jsm:
+            if not self.constructor:
+                error("JavaScript components must specify a constructor")
+
+            for prop in ('init_method', 'legacy_constructor', 'headers'):
+                if getattr(self, prop):
+                    error("JavaScript components may not specify a '%s' "
+                          "property" % prop)
+        elif self.external:
             if self.constructor or self.legacy_constructor:
                 error("Externally-constructed components may not specify "
                       "'constructor' or 'legacy_constructor' properties")
             if self.init_method:
                 error("Externally-constructed components may not specify "
                       "'init_method' properties")
             if self.type == 'nsISupports':
                 error("Externally-constructed components must specify a type "
@@ -294,17 +304,24 @@ class ModuleEntry(object):
         if self.init_idx is not None:
             res += '      MOZ_TRY(CallInitFunc(%d));\n' % self.init_idx
 
         if self.legacy_constructor:
             res += ('      return /* legacy */ %s(nullptr, aIID, aResult);\n'
                     % self.legacy_constructor)
             return res
 
-        if self.external:
+        if self.jsm:
+            res += (
+                '      nsCOMPtr<nsISupports> inst;\n'
+                '      MOZ_TRY(ConstructJSMComponent(NS_LITERAL_CSTRING(%s),\n'
+                '                                    %s,\n'
+                '                                    getter_AddRefs(inst)));'
+                '\n' % (json.dumps(self.jsm), json.dumps(self.constructor)))
+        elif self.external:
             res += ('      nsCOMPtr<nsISupports> inst = '
                     'mozCreateComponent<%s>();\n' % self.type)
         else:
             res += '      RefPtr<%s> inst = ' % self.type
 
             if not self.constructor:
                 res += 'new %s();\n' % self.type
             else:
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -526,16 +526,18 @@ nsresult nsComponentManagerImpl::Init() 
       break;
   }
 
   if (loadChromeManifests) {
     // This needs to be called very early, before anything in nsLayoutModule is
     // used, and before any calls are made into the JS engine.
     nsLayoutModuleInitialize();
 
+    mJSLoaderReady = true;
+
     // The overall order in which chrome.manifests are expected to be treated
     // is the following:
     // - greDir
     // - greDir's omni.ja
     // - appDir
     // - appDir's omni.ja
 
     InitializeModuleLocations();
--- a/xpcom/components/nsComponentManager.h
+++ b/xpcom/components/nsComponentManager.h
@@ -161,16 +161,18 @@ class nsComponentManagerImpl final : pub
   mozilla::Maybe<EntryWrapper> LookupByContractID(
       const nsACString& aContractID);
   mozilla::Maybe<EntryWrapper> LookupByContractID(
       const MutexLock&, const nsACString& aContractID);
 
   nsresult GetService(mozilla::xpcom::ModuleID, const nsIID& aIID,
                       void** aResult);
 
+  static bool JSLoaderReady() { return gComponentManager->mJSLoaderReady; }
+
   static void InitializeStaticModules();
   static void InitializeModuleLocations();
 
   struct ComponentLocation {
     NSLocationType type;
     mozilla::FileLocation location;
   };
 
@@ -276,16 +278,18 @@ class nsComponentManagerImpl final : pub
 
   inline PendingServiceInfo* AddPendingService(const nsCID& aServiceCID,
                                                PRThread* aThread);
   inline void RemovePendingService(const nsCID& aServiceCID);
   inline PRThread* GetPendingServiceThread(const nsCID& aServiceCID) const;
 
   nsTArray<PendingServiceInfo> mPendingServices;
 
+  bool mJSLoaderReady = false;
+
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
  private:
   ~nsComponentManagerImpl();
 
   nsresult GetServiceLocked(MutexLock& aLock, EntryWrapper& aEntry,
                             const nsIID& aIID, void** aResult);
 };