xpcom-review

# XPCOM Brown Bag I

Suresh Duddi <dp@netscape.com>
August 17, 1999

### Terminology

Interface | Contract betwen the implementation and usage. A pure virtual abstract base class with no data members. eg. nsIZip
IID | Interface ID {xxxxx-xxx-xxx-xxxxxx} a unique number that identifies the Interface being talked about
nsISupports | The mother of all interfaces. All interfaces ultimately inherit from this. Provides refcounting. The IID and definition has been carefully choosen so as to be binary compatible with COM.
Component | An implementation of a set of interfaces identified by a CLSID
Object | An instance of a Component
CLSID | Component ID {xxxxx-xxx-xxx-xxxxxx} a unique number that identifies a component
Module | A packaging of a set of Components (ie) a DLL
Factory | An object whose sole purpose is creation of objects of a particular component. There exists only one factory for every Component (CLSID). The IID and definition has been carefully choosen so as to be binary compatible with COM. nsIFactory
ClassObject | Same object as factory.
ContractID | A String name for a Component.
ComponentManager | Central Object of XPCOM that provides API for object creation and management.
ServiceManager | A singleton manager. Holds and manages singleton references to objects for application. Going to be merged with the componentmanager.
Service | A singleton object within an application.
Registry | A hierarchical attribute-value pair storage.
XPIDL | Cross Platform Interface Definition Language. Language for expressing interfaces.
Typelib | A storage for meta information about interfaces. Interfaces expressed in IDL have their meta information maintained in Typelib automatically.
XPConnect | Ability to call c++ implementations of interfaces expressed in IDL from javascript and viceversa.

### An Interface

- E.g Image Decoder nsIImgDecoder
- Dont put data members
- Return value of member function should always be `nsresult using the NS_IMETHOD macro.`
- Use XPIDL for interface header files
- Useful Macros if not using XPIDL

- NS_DEFINE_STATIC_IID_ACCESSOR - Defines static GetIID() method for an interface
- NS_IMETHOD - Prototypes return value of interface function declaration

### The Component

- E.g GIF Image Decoder nsGIFDecoder nsGIFDecoder.h, nsGIFDecoder.cpp
- Useful Macros

- NS_DECL_ISUPPORTS - Declares all methods of nsISupports. Used in implementation declaration header file.
- NS_INIT_ISUPPORTS - nitilize the base class ISupports. Used in constructor of implementation.
- NS_IMPL_ISUPPORTS - Implement the nsISupports functions. Used in implementation file.

- NS_IMPL_ADDREF - Implementation of nsISupports::Release()
- NS_IMPL_RELEASE - Implementation of nsISupports::AddRef()
- NS_IMPL_QUERY_INTERFACE - Implementation of nsISupports::QueryInterface() for one IID
- NS_IMPL_ISUPPORTS2 - Implements the nsISupports function with QueryInterface for two IIDs

- NS_DEFINE_IID - Define a variable of type nsIID initialized with a struct representatio of an IID
- NS_GET_IID - Use this instead of nsIFoo::GetIID() or NS_DEFINE_IID()...
- NS_DEFINE_CID - Define a variable of type nsCID intialized with a struct representation of the CLSID
- NS_IMETHODIMP - Prototypes return value for interface functions implementation. Used in implementation file.
- NS_ADDREF, NS_IF_ADDREF - Addref an object by client
- NS_ADDREF_THIS - Addref used in object implementation. Dont need this if using NS_IMPL_ADDREF
- NS_RELEASE, NS_IF_RELEASE - Release an object by client
- NS_RELEASE_THIS - Released used in object implementation. Dont need this if using NS_IMPL_RELEASE
- NS_RELEASE2 - Release and get the refcount. Useful to check if refcount hit Zero.

- Generic Factory - A generic factory implementation with callback ability for object creation. Simplies factory implementation.

### Object Creation

`rv = compMgr->CreateInstance(imgContractID, NULL, NS_GET_IID(nsIImgDecoder), (void **)&imgdec);`
- It is preferable to use the NS_GET_IID() version of getting iid instead of the old way

- NS_DEFINE_IID(kImgDecodeIID, NS_IIMGDECODER_IID);
and then using kImgDecoderIID for the IID
- DO NOT USE nsIImgDecoder::GetIID()

- The component manager does this on a CreateInstance()

- map ContractID to CLSID
- Get module object for dll which implements component CLSID
- nsISupports classObject = module->GetClassObject()
- nsIFactory factory = classObject->QueryInterface(NS_GET_IID(nsIFactory));
- return factory->CreateInstance(IID);

- [hint] Use nsCOMPtr

`nsCOMPtr <nsIImgDecoder> imgdec;`
`rv = compMgr->CreateInstance(imgContractID, NULL, NS_GET_IID(nsIImgDecoder), getter_AddRefs(imgdec));`

### Object Release

> `NS_IF_RELEASE(obj);`

> It should be noted that release and addref are directly talking to the object with no intermediaries. The macro NS_*_RELEASE() set the object to NULL.

### Component Registration Process

- nsIModule.idl

At startup of apprunner, a call to `nsIComponentManager::AutoRegister(Startup, <exedir>/Components)` This will descend recursively looking for loadable files (files with a certain externsion and on Mac of certain type) If the file had changed since the last time the file was visited (ModifiedDate check) registration is initiated on the file.

Registration involves:

- Load the dll
- nsIModule module = "NSGetModule"() - Create the module object
- module->RegisterSelf(location)

The module registers with the componentmanager every {CLSID, ContractID} it implements with a call to `nsIComponentManager::RegisterComponentSpec()` These registrations are stored in the registry. Hence the next time around, the module is loaded not at startup but only when the first object that is implemented by a component in the module is created.

e.g Registration of GIF Decoder nsGIFModule.cpp

DO NOT CONVERT YOUR COMPONENT TO nsIModule yet. We are working on the sample code which will make the conversion process even simpler.

### Module Unloading

> Every module implements a CanUnload() function that returns a boolean of whether the module can be unloaded. This implies that the module needs to be counting all outstanding object and return PR_TRUE only if there are no outstanding objects served from within the module.

> Unloading happens either on a Timer or on an explicit call to nsComponentManager::FreeLibraries(). The general logic is:

> - PRBool unloadable = module->CanUnload()
> - if (unloadable && marked for unload)
> - Unload()
> - if (unloadable && not mark for unload)
> - mark for unload
> No assumptions can be made that the module will be unloaded if nsIModule::CanUnload() returns PR_TRUE. It is guaranteed that if nsIModule::CanUnload() returns PR_FALSE, the module will not be unloaded.

### The Component Registry

> Nothing but a persistent hiearchical store of attribute value pairs. It is the use of the registry by XPCOM that is very interesting. Physical location of the registry is in the <exedir>/component.reg

> The xpcom hierarchy of the registry is considered to be opaque to components. Use the nsIComponentManager Api to register as opposed to meddling with the xpcom hierarchy in the registry directly.

> mozregistry.dat is NOT the component registry. It is the application (apprunner) registry.

### Old Method for Module definition

> Previously module definition was scattered across many exported functions from a dll.
> NSGetFactory( ), NSCanUnload( ), NSRegisterSelf( ), NSUnregisterSelf( )
> Now these are combined into a nsIModule

> Please continue to use the old method until sample code using nsIModule is available.

### ComponentManager vs ServiceManager

> ServiceManager is a an object that holds instances to objects in a cache and manages them. It uses the ComponentManager to create an instance the first time an object is queried for. Lifetime of the instance is currently the lifetime of the app. We are working on what is required here and how to control the lifetime. Possbily merging ServiceManager with the ComponentManager.

> From the components perspective there is nothing it does special to behave as a service. The application controls the creation of services by its usage or call to `nsServiceManager::GetService()` .Some components that want to enforce being a service return cached instances on `nsComponentManager::CreateInstance()` . We are working on a more elegant syntax and semantics for this. Post suggestions to newsgroup netscape.public.mozilla.xpcom (or Email mozilla-xpcom@mozilla.org) with suggestions.

### ContractID vs CLSID

> CLSID is a number identifying a specific implementation.

> ContractID is a like a pointer to a CLSID. It is an opaque string to xpcom.It is used for two purposes.

> 1. Implementation independence for clients: `@mozilla.org/registry;1`
> The CLSID associating itself to a ContractID can be changed. Hence if a new implementation with a new CLSID superceeds an older implementation, the client wont be tied to the older implementation if it uses the ContractID.
> 2. CLSID switching: `@mozilla.org/image/decoder;1?type=image/gif`
> A client wants to create different objects based on a switch (the mimetype for the case of imagedecoders). A Contractid is formed with a concatenation of a BASE_CONTRACTID and the switch (mimetype) and an instance of that created. Components, when registering, associate their CLSID to specific contractids.
> NOTE: Only Clients that are adamant about the use of a specific implementation need to create instances using CLSID.

> We encourage use of ContractID. This works well in the scriptable world too.

> We are working on a ContractID syntax.

### Steps for XPCOM Review

1. Know your interfaces and objects.
List the objects your component is serving. List the interfaces that it implements.
2. List the objects that your component uses.
3. http://www.mozilla.org/scriptable/interface-rules-we-break.html
4. Do not switch over to using nsIModule yet. We will publish sample code in a week with guidelines.
5. Use the above stated macros.
6. http://www.mozilla.org/projects/xpcom/ - Watch for more documents here