XPCOM Dynamic Component Registration

Suresh Duddi <dp@netscape.com>

Dynamic object registration in XPCOM is achieved by interaction of the following components:

The registration mechanism for XPCOM components is similar in many ways to that of COM. The XPCOM component dlls will have the opportunity to register themselves with the registry. The exact time of installation would be either at install time or as a result of autodetection by the Repository Manager at runtime.

The Component Registry

There are three types of Component Registries:
  1. App-Component-Registry
    Each application has its own Component Registry that lives along with the app in its <exe-dir>/components directory. The Component Registry is created on installation or first run. It is be used read-only by XPCOM
  2. User-Component-Registry

  3. Each user can install additional components under their user home directory. These components will be shared across all XPCOM applications that the user runs.
  4. Meta-Component-Registry

  5. Sharing Components between application: This can happen in two ways. The installer of an application can find the other application's components by looking for the application specifically and registering them with this app's component registry. The second and more preferable approach is to keep a machine wide Meta-Components-Registry that would aggregate all the app component registries.
The difference component registries will be searched in the following order:
  1. User Components Registry
  2. App Component Registry
  3. Meta Component Registry
The user component registry is the only one that will be updated on the fly by XPCOM. JS will be given the option to update either the App-Component-Registry or the User-Component-Registry and this may succeed fail based on write permission, although the general guideline is to update the User-Component-Registry. JS will have to do special things to update the App-Component-Registry.

Profiles are a notion of the app (navigator) and xpcom has nothing to do with it. The app will store app specific data in a Data-Registry that will be stored under the user's home directory.

How does this Solve our problems

  1. Multiple installations of mozilla and xpcom based apps

  2. Since each installation is going run with their own App-Component-Registry, basically both apps will work. No inter process locking is essential. Since both processes will operate on the User-Component-Registry, inter process locking of the User-Component-Registry will be required.
  3. Third Party components installation

  4. Third parties can install components in their own directories and update the App-Component-Registry (preferable) or User-Component-Registry depending on if the sharing of component needs to be specific to the user or for all users. Facilities for updating the registry would be to use JS or write XPCOM code in their installer. The other option would be to add their components in their own directory, create a App-Component-Registry of their own in their directory and reference it in the Global Meta-Components-Registry. This will get their components used by all applications.
  5. Registry used to store app specific data

  6. This is a totally separate registry: the Data-Registry. The theory is that this will reside in the user's home directory. The registry hierarchy is app specific.
  7. User Specific components

  8. This is basically the User-Component-Registry. Inter process locking is required as all processes with XPCOM will access the same User-Component-Registry.
  9. Embedding

  10. This is requires more thought. The fact is when say Gecko is embedded into an application, Gecko is running most probably in the process space of that application and hence the XPCOM used will look for components in this embedding applications directory. The embedding procedure should create a App-Component-Reqistry for the embedding application that should contain all the components from different apps this app would like to use. This is however not required, if the Meta-Component-Registry exists.
  11. User not having permission to the place where the global registry lives, if there is one.

  12. First the App-Component-Registry is written to only when there is a new component or a component has gone away. New components come with installers or the user calls regFactory.exe with the dll as an argument or clicks on a button that says "refresh my user components" which will cause autoregistration of user components. For deleted app components, annotations will be made in the User-Component-Registry. Deleted user components is a non-issue.
  13. NFS mounted home directories and app directories

  14. NFS mounted home directories requires inter-machine locking of the User-Component-Registry.  NFS mounted app directories dont have a problem as the App-Component-Registry is  only used read-only by XPCOM.
So in summary, As a first cut, I am going to implement the App-Component-Registry for M8.

Hierarchy Used by Component Registry

XPCOM uses the nsRegistry to store mappings between CLSIDs and their implementations. The Registry provides persistent storage of hierarchical keys and name-value pairs associated with each key. Each key also keeps a default value.

XPCOM will use the following registry hierarchy:

ROOTKEY_COMMON
    Common
        Classes
            CLSID
             {108d75a0-bab5-11d2-96c4-0060b0fb9956}
                    InprocServer (S)  = /home/dp/dist/bin/components/libnfs-protocol.so
                    ProgID       (S)  = component://netscape/network-protocol&type=nfs
                    ClassName    (S)  = NFS Protocol Handler

           component://netscape/network-protocol&type=nfs
                CLSID        (S)  = {108d75a0-bab5-11d2-96c4-0060b0fb9956}

    Software
        Netscape
            XPCOM
                VersionString          (S)  = alpha0.20

             /home/dp/dist/bin/components/libnfs-protocol.so
                    ComponentsCount    (Int)  = 1
                    FileSize           (Int)  = 78965
                    LastModTimeStamp   (S)    = Wed Feb 24 11:24:06 PST 1999

             Events
                    Startup
                     {108d75a0-bab5-11d2-96c4-0060b0fb9956}
                        {17894983-ab78-8d75-a0bb-511d296c4006}

                    Shutdown
                     {748958ea-abab-511d-296c-40060b0fb995}
                        {45617894-983a-b788-d75a-0bab11d296c4}
 


The Repository: Object instance creation

All object creation happens via The Repository.  nsIRepository::CreateInstance() will be the primary way of creation of object instances. The steps in instantiation of an object that implements the IID interface and of class CLSID is as follows:
  1. The CLSID of the component that would need to create the object instance is identified.
    1. Callers use nsIRepository::ProgIDToCLSID() to convert the ProgID string to the CLSID.
  2. Load the dll associated with the CLSID after consulting the Registry
  3. Instantiate the class factory by calling a globally exported dll function NSGetFactory(). This returns an instance of the class factory that implements the nsIFactory interface.
  4. The actual object creation is delegated to this nsIFactory instance with a call to nsIFactory::CreateInstance().


The Service Manager

All globally created system services are available via the nsIServiceManager, including the nsIRepository and nsIRegistry. Although the nsIServiceManager uses the Registry and Repository in the creation and maintenance of other services, the circular dependency is broken by not letting the nsIServiceManager create the nsIRepository and nsIRegistry instance and registering them specially with the nsIServiceManager. The nsIServiceManager is passed into NSGetFactory() for assisting the DLL in the Factory creation process.


Component Registration

Either at installation time of the Component or at times when the XPCOM library autodetect new/changed dlls, component registration is activated. The autodetection happens at startup time of the navigator or via a javascript trigger navigator.repository.autodetect(). The steps in component registration would be:
  1. The dll is loaded
  2. The component is allowed to self register by a call to a globally exported dll function NSRegisterSelf(). The nsIServiceManager and the fullpath of the dll are passed in to assist in the registration process. The dll is expected to create/modify its entries in the Registry according to the guidelines of the XPCOM hierarchy in the registry. nsIRepository, which can be queried from the nsIServiceManager, has useful registration functions that would easen the process.
  3. The dll is unloaded


Autodetection of Components

Autodetection of changed dlls happened by storing the dll's last modified time and its size in the Registry automatically. If either the last modified time stamp or the filesize differs from that stored in the Registry for the dll, re-registration takes place. Before re-registration, the existing instances of the objects created by the classes in the dll are not freed because the nsIRepository has no list of them. The NSCanUnload() will be called with input parameter force set to true. The dll has to prepare for getting unloaded. After this call returns, the dll will be unloaded if the return value is true. If the dll detects that it cannot properly prepare for unloading, then it can return false. XPCOM will not let the re-registration of the modified dll proceed in this case. There is nothing much that XPCOM library can do to salvage this situation other than warning the user of possible instability and advice a restart upon which the re-registration will happen.


ProgID Spec

The general format of ProgIDs is component://netscape/compname?var=value;var=value;var=value...

Let us consider some more examples:

  1. A pluggable protocol that implementes the nfs protocol
  2. A converter that can handle application/x-zip
  3. A plugin that can handle image/gif
  4. A widget that can do a toolbar
  5. A datasource that can handle mail
  6. A helperapp that deals with application/postscript
All the above have what type they are and one or more arguments on what they particularly do.

The ProgID for these would look like

  1. component://netscape/network-protocol?type=nfs
  2. component://netscape/data-converter?type=application/x-zip
  3. component://netscape/plugin?type=image/gif;name=ImageMedia%20Gif%20Image%20Plugin;Description=Renders%20GIF%20Images....
  4. component://netscape/widget?type=toolbar
  5. component://netscape/rdf/datsource?type=mail
  6. component://netscape/helperapp?type=application/postscript
{Assume proper escaping of all above URI}

The above semantics would let ProgID be an extensible mechanism that could be searched on multiple ways. And
query on a progid should match only whatever was passed in. So a query for
component://netscape/plugin?type=image/gif should pass for the progid specified above. We could extend this
mechanism with wildcards, but I dont want to go there yet... :-)
 


Components created on events

NOTE: THIS IS NOT BEING DONE. We are going to expect the apps to this themselves by using the registry.

Some dlls have components that want to be created on certain events namely Startup, Shutdown (for now). Example is xpinstall.

RegisterComponentForEvent(..., RegisterationTime when, ...)
RegisterFactoryForEvent(..., RegistrationTime when,...)
exists for this purpose. When an application wants to Fire the particular event, it calls
nsComponentManager::FireEvent(RegistrationTime when)
ComponentManager will look for components that are registered to be created on these events and do the following for each of the components:
  1. CreateInstance(...,CID, knsIStartupComponentIID, &obj);

  2. For a shutdown event, knsIShutdownComponentIID would be used.
     
  3. obj->Release();

  4. The component needs to take adequate measures to keep itself alive and figure out how it would delete the object, since a Release() happens immediately after a CreateInstace()
Warning: Order of creation of multiple components registered on the same event is not defined. Component dependencies aren't thought of yet.
 


How will all this help me

For Component Developers: For Component Users:
  • No more hacking in calls to nsIRepository::RegisterFactory()
  • No need to know the CLSID of components that you want to instantiate. Component creation can happen like this

  • nsIRepository::CreateInstance("component://netscape/network-protocol&type=nfs", NULL, kProtocolIID, &result);
    instead of
    nsIRepository::CreateInstance(NFS_PROTOCOL_CID, NULL, domIID, &result);


    What has happened so far


    Changes to XPCOM happening


    What should I do


    Issues



    Last Modified: 28 Jan 1998
    Feedback to: netscape.public.mozilla.xpcom