Written on Jul 28, 2018
Services.jsm
exposes a bunch of components and not all of them are treated equally.
Some of them are exposed only if another class has been registered, or if the current
platform is android. Sometimes we want to expose additional interfaces that we know the component implements for sure and sometimes the component might not be implementing those interfaces. Trying to take care of all of those differences
and passing extra parameters to the Service
object (in python) will be a nightmare and not very elegant.
For example,
appinfo
component, it might be the case that it does not implement nsIXULAppInfo
so we will need custom interface exposure for this componentcrashmanager
component is implemented in JavaScript, so I will have to retrieve it in a different way@mozilla.org/childprocessmessagemanager;1
is defined via webidl, so I will have to retrieve it in a different way as wellandroidbridge
component is only exposed if the platform is androidpolicies
is only exposed if "@mozilla.org/browser/enterprisepolicies;1" in Cc
So, at least
So here is my plan
Service
class to take the following boolean flags
isCustomServiceGetter
to signify that component retrieval will be customisCustomInterfaceDefinition
to signify that querying for interfaces will be customisCustomExposure
to signify that the getter will be exposed conditionally
Services
objectI was also thinking about how I will eliminate all calls to Services.jsm
or at least make it so they are forwarded to Components.Services
. For testing, I could make it so that I comment out all of Services.jsm
and instead define a global like so
const Services = Components.Services
. This will allow me to quickly test if all of the Services are working as expected instead of replacing all of them at once and discovering some of them are not working.
Another thing I could do is to create a mochitest of some sort and get each service manually and ensure that I get no errors while retrieving them. This can be an additional level of testing, in case not all of the exposed services are used by JavaScript consumers.
I need to figure out what exactly the following code bit means
if ("@mozilla.org/browser/enterprisepolicies;1" in Cc) {
initTable.policies = ["@mozilla.org/browser/enterprisepolicies;1", "nsIEnterprisePolicies"];
}
From this mdn article
Note that
Components.classes
reflects only those component classes that have been previously installed and registered with the component manager usingContractID
s. If you want to use a class which was only registered with their CID, useComponents.classesByID
instead ofComponents.classes
to retrieve it.
How do we check if a certain component has registered with their contract id in C++?
Fast forward to 3 minutes later, somehow, I stumbled upon XPCOM Reference, and from there I found Core XPCOM functions and there I saw this:
The
NS_GetComponentRegistrar
function returns a reference to the XPCOM Component Registrar.
Registrar sounds like something that could be useful to me! Looking at the method overview of nsIComponentRegistrar we can see that one of the functions is boolean isContractIDRegistered(in string aContractID);
. From the documentation,
This method is used to test for the existence of a class implementing a specific
ContractID
.
I think this is what I was looking for! To double check, I will do a code search for the use of this function to see if it’s ever used in C++ code.
Indeed, it is used in several places including here:
nsCOMPtr<nsIComponentRegistrar> registrar;
nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_SUCCEEDED(rv) && registrar) {
bool result;
rv = registrar->IsContractIDRegistered(contractID.get(), &result);
MOZ_ASSERT(NS_SUCCEEDED(rv));
...
}