# The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with the # License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for # the specific language governing rights and limitations under the License. # # The Original Code is the Python XPCOM language bindings. # # The Initial Developer of the Original Code is ActiveState Tool Corp. # Portions created by ActiveState Tool Corp. are Copyright (C) 2000, 2001 # ActiveState Tool Corp. All Rights Reserved. # # Contributor(s): Mark Hammond (original author) # # This module provides the JavaScript "components" interface import xpt import xpcom, _xpcom import xpcom.client import xpcom.server import types StringTypes = [types.StringType, types.UnicodeType] def _get_good_iid(iid): if iid is None: iid = _xpcom.IID_nsISupports elif type(iid) in StringTypes and len(iid)>0 and iid[0] != "{": iid = getattr(interfaces, iid) return iid # The "manager" object. manager = xpcom.client.Component(_xpcom.NS_GetGlobalComponentManager(), _xpcom.IID_nsIComponentManager) # The "interfaceInfoManager" object - JS doesnt have this. interfaceInfoManager = _xpcom.XPTI_GetInterfaceInfoManager() # The serviceManager - JS doesnt have this either! serviceManager = _xpcom.GetGlobalServiceManager() # The "Exception" object Exception = xpcom.COMException # Base class for our collections. # It appears that all objects supports "." and "[]" notation. # eg, "interface.nsISupports" or interfaces["nsISupports"] class _ComponentCollection: # Bases are to over-ride 2 methods. # _get_one(self, name) - to return one object by name # _build_dict - to return a dictionary which provide access into def __init__(self): self._dict_data = None def keys(self): if self._dict_data is None: self._dict_data = self._build_dict() return self._dict_data.keys() def items(self): if self._dict_data is None: self._dict_data = self._build_dict() return self._dict_data.items() def values(self): if self._dict_data is None: self._dict_data = self._build_dict() return self._dict_data.values() def has_key(self, key): if self._dict_data is None: self._dict_data = self._build_dict() return self._dict_data.has_key(key) def __len__(self): if self._dict_data is None: self._dict_data = self._build_dict() return len(self._dict_data) def __getattr__(self, attr): if self._dict_data is not None and self._dict_data.has_key(attr): return self._dict_data[attr] return self._get_one(attr) def __getitem__(self, item): if self._dict_data is not None and self._dict_data.has_key(item): return self._dict_data[item] return self._get_one(item) _constants_by_iid_map = {} class _Interface: # An interface object. def __init__(self, name, iid): # Bypass self.__setattr__ when initializing attributes. d = self.__dict__ d['_iidobj_'] = iid # Allows the C++ framework to treat this as a native IID. d['name'] = name def __cmp__(self, other): this_iid = self._iidobj_ other_iid = getattr(other, "_iidobj_", other) return cmp(this_iid, other_iid) def __hash__(self): return hash(self._iidobj_) def __str__(self): return str(self._iidobj_) def __getitem__(self, item): raise TypeError, "components.interface objects are not subscriptable" def __setitem__(self, item, value): raise TypeError, "components.interface objects are not subscriptable" def __setattr__(self, attr, value): raise AttributeError, "Can not set attributes on components.Interface objects" def __getattr__(self, attr): # Support constants as attributes. c = _constants_by_iid_map.get(self._iidobj_) if c is None: c = {} i = xpt.Interface(self._iidobj_) for c_ob in i.constants: c[c_ob.name] = c_ob.value _constants_by_iid_map[self._iidobj_] = c if c.has_key(attr): return c[attr] raise AttributeError, "'%s' interfaces do not define a constant '%s'" % (self.name, attr) class _Interfaces(_ComponentCollection): def _get_one(self, name): try: item = interfaceInfoManager.GetInfoForName(name) except xpcom.COMException, why: # Present a better exception message, and give a more useful error code. import nsError raise xpcom.COMException(nsError.NS_ERROR_NO_INTERFACE, "The interface '%s' does not exist" % (name,)) return _Interface(item.GetName(), item.GetIID()) def _build_dict(self): ret = {} enum = interfaceInfoManager.EnumerateInterfaces() while not enum.IsDone(): # Call the Python-specific FetchBlock, to keep the loop in C. items = enum.FetchBlock(500, _xpcom.IID_nsIInterfaceInfo) # This shouldnt be necessary, but appears to be so! for item in items: ret[item.GetName()] = _Interface(item.GetName(), item.GetIID()) return ret # And the actual object people use. interfaces = _Interfaces() del _Interfaces # Keep our namespace clean. ################################################# class _Class: def __init__(self, contractid): self.contractid = contractid def __getattr__(self, attr): if attr == "clsid": rc = manager.contractIDToClassID(self.contractid) # stash it away - it can never change! self.clsid = rc return rc raise AttributeError, "%s class has no attribute '%s'" % (self.contractid, attr) def createInstance(self, iid = None): import xpcom.client try: return xpcom.client.Component(self.contractid, _get_good_iid(iid)) except xpcom.COMException, details: import nsError # Handle "no such component" in a cleaner way for the user. if details.errno == nsError.NS_ERROR_FACTORY_NOT_REGISTERED: raise xpcom.COMException(details.errno, "No such component '%s'" % (self.contractid,)) raise # Any other exception reraise. def getService(self, iid = None): return serviceManager.getServiceByContractID(self.contractid, _get_good_iid(iid)) class _Classes(_ComponentCollection): def __init__(self): _ComponentCollection.__init__(self) def _get_one(self, name): # XXX - Need to check the contractid is valid! return _Class(name) def _build_dict(self): ret = {} enum = manager.EnumerateContractIDs() while not enum.IsDone(): # Call the Python-specific FetchBlock, to keep the loop in C. items = enum.FetchBlock(500) for item in items: name = str(item) ret[name] = _Class(name) return ret classes = _Classes() del _Classes del _ComponentCollection # The ID function ID = _xpcom.IID # A helper to cleanup our namespace as xpcom shuts down. class _ShutdownObserver: _com_interfaces_ = interfaces.nsIObserver def observe(self, service, topic, extra): global manager, classes, interfaces, interfaceInfoManager, _shutdownObserver, serviceManager, _constants_by_iid_map manager = classes = interfaces = interfaceInfoManager = _shutdownObserver = serviceManager = _constants_by_iid_map = None xpcom.client._shutdown() xpcom.server._shutdown() svc = _xpcom.GetGlobalServiceManager().getServiceByContractID("@mozilla.org/observer-service;1", interfaces.nsIObserverService) # Observers will be QI'd for a weak-reference, so we must keep the # observer alive ourself, and must keep the COM object alive, # _not_ just the Python instance!!! _shutdownObserver = xpcom.server.WrapObject(_ShutdownObserver(), interfaces.nsIObserver) # Say we want a weak ref due to an assertion failing. If this is fixed, we can pass 0, # and remove the lifetime hacks above! See http://bugzilla.mozilla.org/show_bug.cgi?id=99163 svc.addObserver(_shutdownObserver, "xpcom-shutdown", 1) del svc, _ShutdownObserver