Back to index

lightning-sunbird  0.9+nobinonly
components.py
Go to the documentation of this file.
00001 # ***** BEGIN LICENSE BLOCK *****
00002 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003 #
00004 # The contents of this file are subject to the Mozilla Public License Version
00005 # 1.1 (the "License"); you may not use this file except in compliance with
00006 # the License. You may obtain a copy of the License at
00007 # http://www.mozilla.org/MPL/
00008 #
00009 # Software distributed under the License is distributed on an "AS IS" basis,
00010 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011 # for the specific language governing rights and limitations under the
00012 # License.
00013 #
00014 # The Original Code is the Python XPCOM language bindings.
00015 #
00016 # The Initial Developer of the Original Code is
00017 # ActiveState Tool Corp.
00018 # Portions created by the Initial Developer are Copyright (C) 2000, 2001
00019 # the Initial Developer. All Rights Reserved.
00020 #
00021 # Contributor(s):
00022 #   Mark Hammond <MarkH@ActiveState.com> (original author)
00023 #
00024 # Alternatively, the contents of this file may be used under the terms of
00025 # either the GNU General Public License Version 2 or later (the "GPL"), or
00026 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027 # in which case the provisions of the GPL or the LGPL are applicable instead
00028 # of those above. If you wish to allow use of your version of this file only
00029 # under the terms of either the GPL or the LGPL, and not to allow others to
00030 # use your version of this file under the terms of the MPL, indicate your
00031 # decision by deleting the provisions above and replace them with the notice
00032 # and other provisions required by the GPL or the LGPL. If you do not delete
00033 # the provisions above, a recipient may use your version of this file under
00034 # the terms of any one of the MPL, the GPL or the LGPL.
00035 #
00036 # ***** END LICENSE BLOCK *****
00037 
00038 # This module provides the JavaScript "components" interface
00039 import xpt
00040 import xpcom, _xpcom
00041 import xpcom.client
00042 import xpcom.server
00043 import types
00044 
00045 StringTypes = [types.StringType, types.UnicodeType]
00046 
00047 def _get_good_iid(iid):
00048     if iid is None:
00049         iid = _xpcom.IID_nsISupports
00050     elif type(iid) in StringTypes and len(iid)>0 and iid[0] != "{":
00051         iid = getattr(interfaces, iid)
00052     return iid
00053 
00054 # The "manager" object.
00055 manager = xpcom.client.Component(_xpcom.GetComponentManager(), _xpcom.IID_nsIComponentManager)
00056 
00057 # The component registrar
00058 registrar = xpcom.client.Component(_xpcom.GetComponentManager(), _xpcom.IID_nsIComponentRegistrar)
00059 
00060 # The "interfaceInfoManager" object - JS doesnt have this.
00061 interfaceInfoManager = _xpcom.XPTI_GetInterfaceInfoManager()
00062 
00063 # The serviceManager - JS doesnt have this either!
00064 serviceManager = _xpcom.GetServiceManager()
00065 
00066 # The "Exception" object
00067 Exception = xpcom.COMException
00068 
00069 # Base class for our collections.
00070 # It appears that all objects supports "." and "[]" notation.
00071 # eg, "interface.nsISupports" or interfaces["nsISupports"]
00072 class _ComponentCollection:
00073     # Bases are to over-ride 2 methods.
00074     # _get_one(self, name) - to return one object by name
00075     # _build_dict - to return a dictionary which provide access into
00076     def __init__(self):
00077         self._dict_data = None
00078     def keys(self):
00079         if self._dict_data is None:
00080             self._dict_data = self._build_dict()
00081         return self._dict_data.keys()
00082     def items(self):
00083         if self._dict_data is None:
00084             self._dict_data = self._build_dict()
00085         return self._dict_data.items()
00086     def values(self):
00087         if self._dict_data is None:
00088             self._dict_data = self._build_dict()
00089         return self._dict_data.values()
00090     def has_key(self, key):
00091         if self._dict_data is None:
00092             self._dict_data = self._build_dict()
00093         return self._dict_data.has_key(key)
00094 
00095     def __len__(self):
00096         if self._dict_data is None:
00097             self._dict_data = self._build_dict()
00098         return len(self._dict_data)
00099 
00100     def __getattr__(self, attr):
00101         if self._dict_data is not None and self._dict_data.has_key(attr):
00102             return self._dict_data[attr]
00103         return self._get_one(attr)
00104     def __getitem__(self, item):
00105         if self._dict_data is not None and self._dict_data.has_key(item):
00106             return self._dict_data[item]
00107         return self._get_one(item)
00108 
00109 _constants_by_iid_map = {}
00110 
00111 class _Interface:
00112     # An interface object.
00113     def __init__(self, name, iid):
00114         # Bypass self.__setattr__ when initializing attributes.
00115         d = self.__dict__
00116         d['_iidobj_'] = iid # Allows the C++ framework to treat this as a native IID.
00117         d['name'] = name
00118     def __cmp__(self, other):
00119         this_iid = self._iidobj_
00120         other_iid = getattr(other, "_iidobj_", other)
00121         return cmp(this_iid, other_iid)
00122     def __hash__(self):
00123         return hash(self._iidobj_)
00124     def __str__(self):
00125         return str(self._iidobj_)
00126     def __getitem__(self, item):
00127         raise TypeError, "components.interface objects are not subscriptable"
00128     def __setitem__(self, item, value):
00129         raise TypeError, "components.interface objects are not subscriptable"
00130     def __setattr__(self, attr, value):
00131         raise AttributeError, "Can not set attributes on components.Interface objects"
00132     def __getattr__(self, attr):
00133         # Support constants as attributes.
00134         c = _constants_by_iid_map.get(self._iidobj_)
00135         if c is None:
00136             c = {}
00137             i = xpt.Interface(self._iidobj_)
00138             for c_ob in i.constants:
00139                 c[c_ob.name] = c_ob.value
00140             _constants_by_iid_map[self._iidobj_] = c
00141         if c.has_key(attr):
00142             return c[attr]
00143         raise AttributeError, "'%s' interfaces do not define a constant '%s'" % (self.name, attr)
00144 
00145 
00146 class _Interfaces(_ComponentCollection):
00147     def _get_one(self, name):
00148         try:
00149             item = interfaceInfoManager.GetInfoForName(name)
00150         except xpcom.COMException, why:
00151             # Present a better exception message, and give a more useful error code.
00152             import nsError
00153             raise xpcom.COMException(nsError.NS_ERROR_NO_INTERFACE, "The interface '%s' does not exist" % (name,))
00154         return _Interface(item.GetName(), item.GetIID())
00155 
00156     def _build_dict(self):
00157         ret = {}
00158         enum = interfaceInfoManager.EnumerateInterfaces()
00159         while not enum.IsDone():
00160             # Call the Python-specific FetchBlock, to keep the loop in C.
00161             items = enum.FetchBlock(500, _xpcom.IID_nsIInterfaceInfo)
00162             # This shouldnt be necessary, but appears to be so!
00163             for item in items:
00164                 ret[item.GetName()] = _Interface(item.GetName(), item.GetIID())
00165         return ret
00166 
00167 # And the actual object people use.
00168 interfaces = _Interfaces()
00169 
00170 del _Interfaces # Keep our namespace clean.
00171 
00172 #################################################
00173 class _Class:
00174     def __init__(self, contractid):
00175         self.contractid = contractid
00176     def __getattr__(self, attr):
00177         if attr == "clsid":
00178             rc = registrar.contractIDToCID(self.contractid)
00179             # stash it away - it can never change!
00180             self.clsid = rc
00181             return rc
00182         raise AttributeError, "%s class has no attribute '%s'" % (self.contractid, attr)
00183     def createInstance(self, iid = None):
00184         import xpcom.client
00185         try:
00186             return xpcom.client.Component(self.contractid, _get_good_iid(iid))
00187         except xpcom.COMException, details:
00188             import nsError
00189             # Handle "no such component" in a cleaner way for the user.
00190             if details.errno == nsError.NS_ERROR_FACTORY_NOT_REGISTERED:
00191                 raise xpcom.COMException(details.errno, "No such component '%s'" % (self.contractid,))
00192             raise # Any other exception reraise.
00193     def getService(self, iid = None):
00194         return serviceManager.getServiceByContractID(self.contractid, _get_good_iid(iid))
00195 
00196 class _Classes(_ComponentCollection):
00197     def __init__(self):
00198         _ComponentCollection.__init__(self)
00199     def _get_one(self, name):
00200         # XXX - Need to check the contractid is valid!
00201         return _Class(name)
00202 
00203     def _build_dict(self):
00204         ret = {}
00205         enum = registrar.enumerateContractIDs()
00206         while enum.hasMoreElements():
00207             # Call the Python-specific FetchBlock, to keep the loop in C.
00208             items = enum.fetchBlock(2000, _xpcom.IID_nsISupportsCString)
00209             for item in items:
00210                 name = str(item.data)
00211                 ret[name] = _Class(name)
00212         return ret
00213 
00214 classes = _Classes()
00215 
00216 del _Classes
00217 
00218 del _ComponentCollection
00219 
00220 # The ID function
00221 ID = _xpcom.IID
00222 
00223 # A helper to cleanup our namespace as xpcom shuts down.
00224 class _ShutdownObserver:
00225     _com_interfaces_ = interfaces.nsIObserver
00226     def observe(self, service, topic, extra):
00227         global manager, registrar, classes, interfaces, interfaceInfoManager, _shutdownObserver, serviceManager, _constants_by_iid_map
00228         manager = registrar = classes = interfaces = interfaceInfoManager = _shutdownObserver = serviceManager = _constants_by_iid_map = None
00229         xpcom.client._shutdown()
00230         xpcom.server._shutdown()
00231 
00232 svc = _xpcom.GetServiceManager().getServiceByContractID("@mozilla.org/observer-service;1", interfaces.nsIObserverService)
00233 # Observers will be QI'd for a weak-reference, so we must keep the
00234 # observer alive ourself, and must keep the COM object alive,
00235 # _not_ just the Python instance!!!
00236 _shutdownObserver = xpcom.server.WrapObject(_ShutdownObserver(), interfaces.nsIObserver)
00237 # Say we want a weak ref due to an assertion failing.  If this is fixed, we can pass 0,
00238 # and remove the lifetime hacks above!  See http://bugzilla.mozilla.org/show_bug.cgi?id=99163
00239 svc.addObserver(_shutdownObserver, "xpcom-shutdown", 1)
00240 del svc, _ShutdownObserver