# ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # 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 the Initial Developer are Copyright (C) 2000, 2001 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Mark Hammond (original author) # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** # This module provides the JavaScript "components" interface from . import xpt import xpcom import xpcom._xpcom as _xpcom import xpcom.client import xpcom.server StringTypes = [bytes, str] 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.GetComponentManager(), _xpcom.IID_nsIComponentManager) # The component registrar registrar = xpcom.client.Component(_xpcom.GetComponentManager(), _xpcom.IID_nsIComponentRegistrar) # The "interfaceInfoManager" object - JS doesnt have this. interfaceInfoManager = _xpcom.XPTI_GetInterfaceInfoManager() # The serviceManager - JS doesnt have this either! serviceManager = _xpcom.GetServiceManager() # 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 list(self._dict_data.keys()) def items(self): if self._dict_data is None: self._dict_data = self._build_dict() return list(self._dict_data.items()) def values(self): if self._dict_data is None: self._dict_data = self._build_dict() return list(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 attr in self._dict_data: return self._dict_data[attr] return self._get_one(attr) def __getitem__(self, item): if self._dict_data is not None and item in self._dict_data: 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 __eq__(self, other): this_iid = self._iidobj_ other_iid = getattr(other, "_iidobj_", other) return 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 attr in c: 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 as why: # Present a better exception message, and give a more useful error code. from . 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 = registrar.contractIDToCID(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 as details: from . 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 = registrar.enumerateContractIDs() while enum.hasMoreElements(): # Call the Python-specific FetchBlock, to keep the loop in C. items = enum.fetchBlock(2000, _xpcom.IID_nsISupportsCString) for item in items: name = str(item.data) ret[name] = _Class(name) return ret classes = _Classes() del _Classes del _ComponentCollection # The ID function ID = _xpcom.ID # A helper to cleanup our namespace as xpcom shuts down. class _ShutdownObserver: _com_interfaces_ = interfaces.nsIObserver def observe(self, service, topic, extra): global manager, registrar, classes, interfaces, interfaceInfoManager, _shutdownObserver, serviceManager, _constants_by_iid_map manager = registrar = classes = interfaces = interfaceInfoManager = _shutdownObserver = serviceManager = _constants_by_iid_map = None xpcom.client._shutdown() xpcom.server._shutdown() svc = _xpcom.GetServiceManager().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