components.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. # ***** BEGIN LICENSE BLOCK *****
  2. # Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. #
  4. # The contents of this file are subject to the Mozilla Public License Version
  5. # 1.1 (the "License"); you may not use this file except in compliance with
  6. # the License. You may obtain a copy of the License at
  7. # http://www.mozilla.org/MPL/
  8. #
  9. # Software distributed under the License is distributed on an "AS IS" basis,
  10. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. # for the specific language governing rights and limitations under the
  12. # License.
  13. #
  14. # The Original Code is the Python XPCOM language bindings.
  15. #
  16. # The Initial Developer of the Original Code is
  17. # ActiveState Tool Corp.
  18. # Portions created by the Initial Developer are Copyright (C) 2000, 2001
  19. # the Initial Developer. All Rights Reserved.
  20. #
  21. # Contributor(s):
  22. # Mark Hammond <MarkH@ActiveState.com> (original author)
  23. #
  24. # Alternatively, the contents of this file may be used under the terms of
  25. # either the GNU General Public License Version 2 or later (the "GPL"), or
  26. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27. # in which case the provisions of the GPL or the LGPL are applicable instead
  28. # of those above. If you wish to allow use of your version of this file only
  29. # under the terms of either the GPL or the LGPL, and not to allow others to
  30. # use your version of this file under the terms of the MPL, indicate your
  31. # decision by deleting the provisions above and replace them with the notice
  32. # and other provisions required by the GPL or the LGPL. If you do not delete
  33. # the provisions above, a recipient may use your version of this file under
  34. # the terms of any one of the MPL, the GPL or the LGPL.
  35. #
  36. # ***** END LICENSE BLOCK *****
  37. # This module provides the JavaScript "components" interface
  38. import xpt
  39. import xpcom, _xpcom
  40. import xpcom.client
  41. import xpcom.server
  42. import types
  43. StringTypes = [types.StringType, types.UnicodeType]
  44. def _get_good_iid(iid):
  45. if iid is None:
  46. iid = _xpcom.IID_nsISupports
  47. elif type(iid) in StringTypes and len(iid)>0 and iid[0] != "{":
  48. iid = getattr(interfaces, iid)
  49. return iid
  50. # The "manager" object.
  51. manager = xpcom.client.Component(_xpcom.GetComponentManager(), _xpcom.IID_nsIComponentManager)
  52. # The component registrar
  53. registrar = xpcom.client.Component(_xpcom.GetComponentManager(), _xpcom.IID_nsIComponentRegistrar)
  54. # The "interfaceInfoManager" object - JS doesnt have this.
  55. interfaceInfoManager = _xpcom.XPTI_GetInterfaceInfoManager()
  56. # The serviceManager - JS doesnt have this either!
  57. serviceManager = _xpcom.GetServiceManager()
  58. # The "Exception" object
  59. Exception = xpcom.COMException
  60. # Base class for our collections.
  61. # It appears that all objects supports "." and "[]" notation.
  62. # eg, "interface.nsISupports" or interfaces["nsISupports"]
  63. class _ComponentCollection:
  64. # Bases are to over-ride 2 methods.
  65. # _get_one(self, name) - to return one object by name
  66. # _build_dict - to return a dictionary which provide access into
  67. def __init__(self):
  68. self._dict_data = None
  69. def keys(self):
  70. if self._dict_data is None:
  71. self._dict_data = self._build_dict()
  72. return self._dict_data.keys()
  73. def items(self):
  74. if self._dict_data is None:
  75. self._dict_data = self._build_dict()
  76. return self._dict_data.items()
  77. def values(self):
  78. if self._dict_data is None:
  79. self._dict_data = self._build_dict()
  80. return self._dict_data.values()
  81. def has_key(self, key):
  82. if self._dict_data is None:
  83. self._dict_data = self._build_dict()
  84. return self._dict_data.has_key(key)
  85. def __len__(self):
  86. if self._dict_data is None:
  87. self._dict_data = self._build_dict()
  88. return len(self._dict_data)
  89. def __getattr__(self, attr):
  90. if self._dict_data is not None and self._dict_data.has_key(attr):
  91. return self._dict_data[attr]
  92. return self._get_one(attr)
  93. def __getitem__(self, item):
  94. if self._dict_data is not None and self._dict_data.has_key(item):
  95. return self._dict_data[item]
  96. return self._get_one(item)
  97. _constants_by_iid_map = {}
  98. class _Interface:
  99. # An interface object.
  100. def __init__(self, name, iid):
  101. # Bypass self.__setattr__ when initializing attributes.
  102. d = self.__dict__
  103. d['_iidobj_'] = iid # Allows the C++ framework to treat this as a native IID.
  104. d['name'] = name
  105. def __cmp__(self, other):
  106. this_iid = self._iidobj_
  107. other_iid = getattr(other, "_iidobj_", other)
  108. return cmp(this_iid, other_iid)
  109. def __hash__(self):
  110. return hash(self._iidobj_)
  111. def __str__(self):
  112. return str(self._iidobj_)
  113. def __getitem__(self, item):
  114. raise TypeError, "components.interface objects are not subscriptable"
  115. def __setitem__(self, item, value):
  116. raise TypeError, "components.interface objects are not subscriptable"
  117. def __setattr__(self, attr, value):
  118. raise AttributeError, "Can not set attributes on components.Interface objects"
  119. def __get_constants(self):
  120. try:
  121. return _constants_by_iid_map[self._iidobj_]
  122. except KeyError:
  123. c = {}
  124. i = xpt.Interface(self._iidobj_)
  125. for c_ob in i.constants:
  126. c[c_ob.name] = c_ob.value
  127. _constants_by_iid_map[self._iidobj_] = c
  128. return c
  129. def __getattr__(self, attr):
  130. # Support constants as attributes.
  131. try:
  132. return self.__get_constants()[attr]
  133. except KeyError:
  134. raise AttributeError, "'%s' interfaces do not define a constant '%s'" % (self.name, attr)
  135. def __dir__(self):
  136. return sorted(self.__get_constants().keys())
  137. class _Interfaces(_ComponentCollection):
  138. def _get_one(self, name):
  139. try:
  140. item = interfaceInfoManager.GetInfoForName(name)
  141. except xpcom.COMException, why:
  142. # Present a better exception message, and give a more useful error code.
  143. import nsError
  144. raise xpcom.COMException(nsError.NS_ERROR_NO_INTERFACE, "The interface '%s' does not exist" % (name,))
  145. return _Interface(item.GetName(), item.GetIID())
  146. def _build_dict(self):
  147. ret = {}
  148. name_to_iid_dict = interfaceInfoManager.GetScriptableInterfaces()
  149. for name, iid in name_to_iid_dict.items():
  150. ret[name] = _Interface(name, iid)
  151. return ret
  152. # And the actual object people use.
  153. interfaces = _Interfaces()
  154. del _Interfaces # Keep our namespace clean.
  155. #################################################
  156. class _Class:
  157. def __init__(self, contractid):
  158. self.contractid = contractid
  159. def __getattr__(self, attr):
  160. if attr == "clsid":
  161. rc = registrar.contractIDToCID(self.contractid)
  162. # stash it away - it can never change!
  163. self.clsid = rc
  164. return rc
  165. raise AttributeError, "%s class has no attribute '%s'" % (self.contractid, attr)
  166. def createInstance(self, iid = None):
  167. import xpcom.client
  168. try:
  169. return xpcom.client.Component(self.contractid, _get_good_iid(iid))
  170. except xpcom.COMException, details:
  171. import nsError
  172. # Handle "no such component" in a cleaner way for the user.
  173. if details.errno == nsError.NS_ERROR_FACTORY_NOT_REGISTERED:
  174. raise xpcom.COMException(details.errno, "No such component '%s'" % (self.contractid,))
  175. raise # Any other exception reraise.
  176. def getService(self, iid = None):
  177. return serviceManager.getServiceByContractID(self.contractid, _get_good_iid(iid))
  178. class _Classes(_ComponentCollection):
  179. def __init__(self):
  180. _ComponentCollection.__init__(self)
  181. def _get_one(self, name):
  182. # XXX - Need to check the contractid is valid!
  183. return _Class(name)
  184. def _build_dict(self):
  185. ret = {}
  186. enum = registrar.enumerateContractIDs()
  187. while enum.hasMoreElements():
  188. # Call the Python-specific FetchBlock, to keep the loop in C.
  189. items = enum.fetchBlock(2000, _xpcom.IID_nsISupportsCString)
  190. for item in items:
  191. name = str(item.data)
  192. ret[name] = _Class(name)
  193. return ret
  194. classes = _Classes()
  195. del _Classes
  196. del _ComponentCollection
  197. # The ID function
  198. ID = _xpcom.IID
  199. def _on_shutdown():
  200. global manager, registrar, classes, interfaces, interfaceInfoManager, serviceManager, _constants_by_iid_map
  201. manager = registrar = classes = interfaces = interfaceInfoManager = serviceManager = _constants_by_iid_map = None
  202. # for historical reasons we call these manually.
  203. xpcom.client._shutdown()
  204. xpcom.server._shutdown()
  205. # import xpcom.shutdown late as it depends on us!
  206. import shutdown
  207. shutdown.register(_on_shutdown)
  208. del _on_shutdown, shutdown