зеркало из https://github.com/mozilla/gecko-dev.git
545 строки
22 KiB
HTML
545 строки
22 KiB
HTML
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
|
<meta name="Author" content="Suresh Duddi">
|
|
<meta name="GENERATOR" content="Mozilla/4.51 [en] (X11; U; Linux 2.0.36 i686) [Netscape]">
|
|
<title>XPCOM Dynamic Component Registration</title>
|
|
</head>
|
|
<body>
|
|
|
|
<center>
|
|
<h2>
|
|
XPCOM Dynamic Component Registration</h2></center>
|
|
|
|
<center>Suresh Duddi <<a href="mailto:dp@netscape.com">dp@netscape.com</a>>
|
|
<hr WIDTH="100%"></center>
|
|
|
|
<p>Dynamic object registration in XPCOM is achieved by interaction of the
|
|
following components:
|
|
<ul>
|
|
<li>
|
|
The Component Registry</li>
|
|
|
|
<li>
|
|
The Repository</li>
|
|
|
|
<li>
|
|
The Service Manager</li>
|
|
|
|
<li>
|
|
Component dll implementing <tt>NSRegisterSelf()</tt></li>
|
|
</ul>
|
|
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 <b>autodetection</b> by the Repository
|
|
Manager at runtime.
|
|
<br>
|
|
<hr WIDTH="100%">
|
|
<h3>
|
|
<a NAME="The Registry: XPCOM Hierarchy"></a>The Component Registry</h3>
|
|
There are three types of Component Registries:
|
|
<ol>
|
|
<li>
|
|
<b>App-Component-Registry<br>
|
|
</b>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</li>
|
|
|
|
<li>
|
|
<b>User-Component-Registry</b></li>
|
|
|
|
<br>Each user can install additional components under their user home directory.
|
|
These components will be shared across all XPCOM applications that the
|
|
user runs.
|
|
<li>
|
|
<b>Meta-Component-Registry</b></li>
|
|
|
|
<br>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.</ol>
|
|
The difference component registries will be searched in the following order:
|
|
<ol>
|
|
<li>
|
|
User Components Registry</li>
|
|
|
|
<li>
|
|
App Component Registry</li>
|
|
|
|
<li>
|
|
Meta Component Registry</li>
|
|
</ol>
|
|
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.
|
|
<p>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.
|
|
<h4>
|
|
How does this Solve our problems</h4>
|
|
|
|
<ol>
|
|
<li>
|
|
Multiple installations of mozilla and xpcom based apps</li>
|
|
|
|
<br>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.
|
|
<li>
|
|
Third Party components installation</li>
|
|
|
|
<br>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.
|
|
<li>
|
|
Registry used to store app specific data</li>
|
|
|
|
<br>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.
|
|
<li>
|
|
User Specific components</li>
|
|
|
|
<br>This is basically the User-Component-Registry. Inter process locking
|
|
is required as all processes with XPCOM will access the same User-Component-Registry.
|
|
<li>
|
|
Embedding</li>
|
|
|
|
<br>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.
|
|
<li>
|
|
User not having permission to the place where the global registry lives,
|
|
if there is one.</li>
|
|
|
|
<br>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.
|
|
<li>
|
|
NFS mounted home directories and app directories</li>
|
|
|
|
<br>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.</ol>
|
|
So in summary,
|
|
<ul>
|
|
<li>
|
|
App-Component-Registry pretty much solves the top problem of Multiple applications.
|
|
With some help from the installer, Third party components will also be
|
|
solved.</li>
|
|
|
|
<li>
|
|
User-Component-Registry solves the User Specific Components problem.</li>
|
|
|
|
<li>
|
|
Meta-Component-Registry enables the dynamic sharing of components between
|
|
apps which eases embedding.</li>
|
|
</ul>
|
|
As a first cut, I am going to implement the App-Component-Registry for
|
|
M8.
|
|
<h4>
|
|
Hierarchy Used by Component Registry</h4>
|
|
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.
|
|
<p>XPCOM will use the following registry hierarchy:
|
|
<blockquote><tt>ROOTKEY_COMMON</tt>
|
|
<br><tt> Common</tt>
|
|
<br><tt> Classes</tt>
|
|
<br><tt>
|
|
CLSID</tt>
|
|
<br><tt>
|
|
<b><font color="#CC0000">{108d75a0-bab5-11d2-96c4-0060b0fb9956}</font></b></tt>
|
|
<br><tt><nobr>
|
|
InprocServer (S) = <font color="#CC0000">/home/dp/dist/bin/components/libnfs-protocol.so</font></nobr></tt>
|
|
<br><tt><nobr>
|
|
ProgID (S) = <b><font color="#CC0000">component://netscape/network-protocol&type=nfs</font></b></nobr></tt>
|
|
<br><tt><nobr>
|
|
ClassName (S) = <b><font color="#CC0000">NFS Protocol
|
|
Handler</font></b></nobr></tt>
|
|
<p><tt> <font color="#CC0000">component://netscape/network-protocol&type=nfs</font></tt>
|
|
<br><tt><nobr>
|
|
CLSID (S) = <font color="#CC0000">{108d75a0-bab5-11d2-96c4-0060b0fb9956}</font></nobr></tt>
|
|
<p><tt><i> </i>Software</tt>
|
|
<br><tt> Netscape</tt>
|
|
<br><tt>
|
|
XPCOM</tt>
|
|
<br><tt>
|
|
VersionString (S)
|
|
= <font color="#CC0000">alpha0.20</font></tt>
|
|
<p><tt>
|
|
<font color="#CC0000">/home/dp/dist/bin/components/libnfs-protocol.so</font></tt>
|
|
<br><tt>
|
|
ComponentsCount (Int) = <font color="#CC0000">1</font></tt>
|
|
<br><tt>
|
|
FileSize (Int)
|
|
= <font color="#CC0000">78965</font></tt>
|
|
<br><tt>
|
|
LastModTimeStamp (S) = <font color="#CC0000">Wed
|
|
Feb 24 11:24:06 PST 1999</font></tt>
|
|
<p><tt><font color="#CC0000">
|
|
</font><font color="#000000">Events</font></tt>
|
|
<br><tt><font color="#000000">
|
|
Startup</font></tt>
|
|
<br><tt><font color="#000000">
|
|
</font><font color="#CC0000">{108d75a0-bab5-11d2-96c4-0060b0fb9956}</font></tt>
|
|
<br><tt><font color="#CC0000">
|
|
{17894983-ab78-8d75-a0bb-511d296c4006}</font></tt>
|
|
<p><tt><font color="#000000">
|
|
Shutdown</font></tt>
|
|
<br><tt><font color="#000000">
|
|
</font><font color="#CC0000">{748958ea-abab-511d-296c-40060b0fb995}</font></tt>
|
|
<br><tt><font color="#CC0000">
|
|
{45617894-983a-b788-d75a-0bab11d296c4}</font></tt>
|
|
<br> </blockquote>
|
|
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="The Repository: Object instance creation"></a>The
|
|
Repository: Object instance creation</h3>
|
|
All object creation happens via The Repository. <tt>nsIRepository::CreateInstance()</tt>
|
|
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:
|
|
<ol>
|
|
<li>
|
|
The CLSID of the component that would need to create the object instance
|
|
is identified.</li>
|
|
|
|
<ol>Callers use <tt>nsIRepository::ProgIDToCLSID()</tt> to convert the
|
|
ProgID string to the CLSID.</ol>
|
|
|
|
<li>
|
|
Load the dll associated with the CLSID after consulting the Registry</li>
|
|
|
|
<li>
|
|
Instantiate the class factory by calling a globally exported dll function
|
|
<tt>NSGetFactory()</tt>.
|
|
This returns an instance of the class factory that implements the <tt>nsIFactory</tt>
|
|
interface.</li>
|
|
|
|
<li>
|
|
The actual object creation is delegated to this <tt>nsIFactory</tt> instance
|
|
with a call to <tt>nsIFactory::CreateInstance()</tt>.</li>
|
|
</ol>
|
|
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="The Service Manager"></a>The Service Manager</h3>
|
|
All globally created system services are available via the <tt>nsIServiceManager</tt>,
|
|
including the <tt>nsIRepository</tt> and <tt>nsIRegistry</tt>. Although
|
|
the <tt>nsIServiceManager</tt> uses the Registry and Repository in the
|
|
creation and maintenance of other services, the circular dependency is
|
|
broken by not letting the <tt>nsIServiceManager</tt> create the <tt>nsIRepository</tt>
|
|
and <tt>nsIRegistry</tt> instance and registering them specially with the
|
|
<tt>nsIServiceManager</tt>.
|
|
The nsIServiceManager is passed into NSGetFactory() for assisting the DLL
|
|
in the Factory creation process.
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="Component Registration"></a>Component Registration</h3>
|
|
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 <tt>navigator.repository.autodetect()</tt>. The steps in component
|
|
registration would be:
|
|
<ol>
|
|
<li>
|
|
The dll is loaded</li>
|
|
|
|
<li>
|
|
The component is allowed to self register by a call to a globally exported
|
|
dll function <tt>NSRegisterSelf()</tt>. The <tt>nsIServiceManager</tt>
|
|
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 <a href="#The Registry: XPCOM Hierarchy">XPCOM
|
|
hierarchy</a> in the registry. <tt>nsIRepository</tt>, which can be queried
|
|
from the <tt>nsIServiceManager</tt>, has useful registration functions
|
|
that would easen the process.</li>
|
|
|
|
<li>
|
|
The dll is unloaded</li>
|
|
</ol>
|
|
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="Autodetection of Components"></a>Autodetection
|
|
of Components</h3>
|
|
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 <tt>nsIRepository</tt> has no list of them. The <tt>NSCanUnload()</tt>
|
|
will be called with input parameter <i>force</i> set to <tt>true</tt>.
|
|
The dll has to prepare for getting unloaded. After this call returns, the
|
|
dll <b>will</b> be unloaded if the return value is <tt>true</tt>. If the
|
|
dll detects that it cannot properly prepare for unloading, then it can
|
|
return <tt>false</tt>. 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.
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="ProgID Spec"></a>ProgID Spec</h3>
|
|
The general format of ProgIDs is <i><b><font color="#990000">component://</font></b>netscape<b><font color="#990000">/</font></b>compname<b><font color="#990000">?</font></b>var<b><font color="#990000">=</font></b>value<b><font color="#990000">;</font></b>var<b><font color="#990000">=</font></b>value<b><font color="#990000">;</font></b>var<b><font color="#990000">=</font></b>value...</i>
|
|
<p>Let us consider some more examples:
|
|
<ol>
|
|
<li>
|
|
A pluggable protocol that implementes the nfs protocol</li>
|
|
|
|
<li>
|
|
A converter that can handle application/x-zip</li>
|
|
|
|
<li>
|
|
A plugin that can handle image/gif</li>
|
|
|
|
<li>
|
|
A widget that can do a toolbar</li>
|
|
|
|
<li>
|
|
A datasource that can handle mail</li>
|
|
|
|
<li>
|
|
A helperapp that deals with application/postscript</li>
|
|
</ol>
|
|
All the above have what type they are and one or more arguments on what
|
|
they particularly do.
|
|
<p>The ProgID for these would look like
|
|
<ol>
|
|
<li>
|
|
<tt>component://netscape/network-protocol?type=nfs</tt></li>
|
|
|
|
<li>
|
|
<tt>component://netscape/data-converter?type=application/x-zip</tt></li>
|
|
|
|
<li>
|
|
<tt>component://netscape/plugin?type=image/gif;name=ImageMedia%20Gif%20Image%20Plugin;Description=Renders%20GIF%20Images....</tt></li>
|
|
|
|
<li>
|
|
<tt>component://netscape/widget?type=toolbar</tt></li>
|
|
|
|
<li>
|
|
<tt>component://netscape/rdf/datsource?type=mail</tt></li>
|
|
|
|
<li>
|
|
<tt>component://netscape/helperapp?type=application/postscript</tt></li>
|
|
</ol>
|
|
{Assume proper escaping of all above URI}
|
|
<p>The above semantics would let ProgID be an extensible mechanism that
|
|
could be searched on multiple ways. And
|
|
<br>query on a progid should match only whatever was passed in. So a query
|
|
for
|
|
<br>component://netscape/plugin?type=image/gif should pass for the progid
|
|
specified above. We could extend this
|
|
<br>mechanism with wildcards, but I dont want to go there yet... :-)
|
|
<br>
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="Startup Components"></a>Components created on
|
|
events</h3>
|
|
<b><font color="#990000">NOTE: THIS IS NOT BEING DONE. We are going to
|
|
expect the apps to this themselves by using the registry.</font></b>
|
|
<p>Some dlls have components that want to be created on certain events
|
|
namely Startup, Shutdown (for now). Example is xpinstall.
|
|
<blockquote>RegisterComponentForEvent(..., RegisterationTime when, ...)
|
|
<br>RegisterFactoryForEvent(..., RegistrationTime when,...)</blockquote>
|
|
exists for this purpose. When an application wants to Fire the particular
|
|
event, it calls
|
|
<blockquote>nsComponentManager::FireEvent(RegistrationTime when)</blockquote>
|
|
ComponentManager will look for components that are registered to be created
|
|
on these events and do the following for each of the components:
|
|
<ol>
|
|
<li>
|
|
CreateInstance(...,CID, knsIStartupComponentIID, &obj);</li>
|
|
|
|
<br>For a shutdown event, knsIShutdownComponentIID would be used.
|
|
<br>
|
|
<li>
|
|
obj->Release();</li>
|
|
|
|
<br>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()</ol>
|
|
<b><i>Warning: </i></b>Order of creation of multiple components registered
|
|
on the same event is not defined. Component dependencies aren't thought
|
|
of yet.
|
|
<br>
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="How will all this help me"></a>How will all this
|
|
help me</h3>
|
|
For Component Developers:
|
|
<ul>
|
|
<li>
|
|
Component dlls developed could be dropped into a directory, a JS function
|
|
called after which your component is in business without even a restart
|
|
of the browser. Of course, there needs to be someone accessing it.</li>
|
|
|
|
<li>
|
|
No need to export you CLSID or run around finding where to advertise your
|
|
CLSID</li>
|
|
</ul>
|
|
For Component Users:
|
|
<blockquote>
|
|
<li>
|
|
No more hacking in calls to <tt>nsIRepository::RegisterFactory()</tt></li>
|
|
|
|
<li>
|
|
No need to know the CLSID of components that you want to instantiate. Component
|
|
creation can happen like this</li>
|
|
|
|
<br><tt>nsIRepository::CreateInstance(<b>"<font color="#CC0000">component://netscape/network-protocol&type=nfs</font>"</b>,
|
|
NULL, kProtocolIID, &result);</tt>
|
|
<br>instead of
|
|
<br><strike>nsIRepository::CreateInstance(NFS_PROTOCOL_CID, NULL, domIID,
|
|
&result);</strike></blockquote>
|
|
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="What has happened so far"></a>What has happened
|
|
so far</h3>
|
|
|
|
<ul>
|
|
<li>
|
|
Autoregistration implemented to grovel for components from the current
|
|
directory</li>
|
|
|
|
<li>
|
|
nsRepository is a global object. Not nsIRepository exists.</li>
|
|
|
|
<li>
|
|
nsRepository::ProgIDToCLSID() implemented</li>
|
|
</ul>
|
|
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="Changes to XPCOM happening"></a>Changes to XPCOM
|
|
happening</h3>
|
|
|
|
<ul>
|
|
<li>
|
|
API changes to get ProgID in entire registration process for factories
|
|
and components</li>
|
|
|
|
<li>
|
|
ProgID cache to improve performance of ProgID queries</li>
|
|
|
|
<li>
|
|
Notion of Component dlls living in a <b>Components directory </b>and autoregistration
|
|
will load/manage only these components. Other components that are linked
|
|
in to the app need to call <tt>nsRepository::RegisterFactory()</tt> from
|
|
the app. <b>To make this happen we need a list of these.</b></li>
|
|
|
|
<li>
|
|
<b>nsRegistry:</b> Enabling the new nsRegistry developed by Bill Law <law@netscape.com></li>
|
|
|
|
<li>
|
|
Service Manager and Repository: Merge them</li>
|
|
|
|
<li>
|
|
Convenience function to createIntance(progid)</li>
|
|
|
|
<li>
|
|
Base ProgID in the interface ID header file</li>
|
|
</ul>
|
|
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="What should I do"></a>What should I do</h3>
|
|
|
|
<ul><b><font color="#CC0000">Component Developers</font></b>
|
|
<ul>
|
|
<li>
|
|
<font color="#000000">Implement <tt>NSRegisterSelf()</tt> for your component
|
|
dlls. Sample implementation that perf</font>
|
|
<a href="http://lxr.mozilla.org/mozilla/source/modules/libpref/src/nsPref.cpp#608">http://lxr.mozilla.org/mozilla/source/modules/libpref/src/nsPref.cpp#608</a></li>
|
|
|
|
<li>
|
|
Use <tt>nsRepository::RegisterComponent()</tt> instead of <tt>nsRepository::RegisterFactory()</tt>
|
|
if your component lives in a DLL</li>
|
|
|
|
<li>
|
|
<b>Dont use static constructors in loadable components</b></li>
|
|
</ul>
|
|
|
|
<p><br><b><font color="#CC0000">Component Users</font></b>
|
|
<ul>
|
|
<li>
|
|
Create all your components using ProgID rather than CID</li>
|
|
|
|
<li>
|
|
If you were thinking of managing creation of components yourself, think
|
|
again. The repository may be already doing that.</li>
|
|
</ul>
|
|
</ul>
|
|
|
|
<h3>
|
|
|
|
<hr WIDTH="100%"><a NAME="Issues"></a>Issues</h3>
|
|
|
|
<ul>
|
|
<li>
|
|
Need support for questions like:</li>
|
|
|
|
<ol>
|
|
<li>
|
|
Enumerate all CLSIDs that implement a particular interface.</li>
|
|
|
|
<li>
|
|
Let a particular CLSID be the preferable implementation for an interface.</li>
|
|
|
|
<br>I dont know how this a XPCOM component user could use it unless there
|
|
could be a call like:
|
|
<br><tt>nsIRepository::CreateInstance(<b>NULL</b>, NULL, nsWidgetIID, &result);</tt>
|
|
<li>
|
|
Enumerate all interfaces supported by a CLSID</li>
|
|
</ol>
|
|
|
|
<li>
|
|
Resolving naming conflicts between ProgID</li>
|
|
|
|
<li>
|
|
Store component specific name-values under <tt>ROOTKEY_COMMON\\<i>component.class.version\\</i></tt></li>
|
|
|
|
<li>
|
|
Add a registry entry under <tt>ROOTKEY_COMMON\\<i>component.class.version\\</i></tt>to
|
|
indicate the willingness for a CLSID to behave as a Service.</li>
|
|
|
|
<li>
|
|
Add quick registration support functions in <tt>nsIRepository</tt> for
|
|
components to use.</li>
|
|
|
|
<li>
|
|
Is the hierarchy <tt>ROOTKEY_COMMON\\<b>Classes</b>\\CLSID </tt>acceptable.</li>
|
|
|
|
<li>
|
|
Should we have a nsIRepository. JS needs it so that they can reflect it
|
|
in javascript. Remove &'s in the API.</li>
|
|
</ul>
|
|
|
|
<hr WIDTH="100%">
|
|
<br><i><font size=-1>Last Modified: 28 Jan 1998</font></i>
|
|
<br><font size=-1><i>Feedback to: </i><a href="news:netscape.public.mozilla.xpcom">netscape.public.mozilla.xpcom</a></font>
|
|
</body>
|
|
</html>
|