Back to index

lightning-sunbird  0.9+nobinonly
policy.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 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) 2001
00019 # the Initial Developer. All Rights Reserved.
00020 #
00021 # Contributor(s):
00022 #  Mark Hammond <mhammond@skippinet.com.au> (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 from xpcom import xpcom_consts, _xpcom, client, nsError, ServerException, COMException
00039 import xpcom
00040 import traceback
00041 import xpcom.server
00042 import operator
00043 import types
00044 
00045 IID_nsISupports = _xpcom.IID_nsISupports
00046 IID_nsIVariant = _xpcom.IID_nsIVariant
00047 XPT_MD_IS_GETTER = xpcom_consts.XPT_MD_IS_GETTER
00048 XPT_MD_IS_SETTER = xpcom_consts.XPT_MD_IS_SETTER
00049 
00050 VARIANT_INT_TYPES = xpcom_consts.VTYPE_INT8, xpcom_consts.VTYPE_INT16, xpcom_consts.VTYPE_INT32, \
00051                     xpcom_consts.VTYPE_UINT8, xpcom_consts.VTYPE_UINT16, xpcom_consts.VTYPE_INT32
00052 VARIANT_LONG_TYPES = xpcom_consts.VTYPE_INT64, xpcom_consts.VTYPE_UINT64
00053 VARIANT_FLOAT_TYPES = xpcom_consts.VTYPE_FLOAT, xpcom_consts.VTYPE_DOUBLE
00054 VARIANT_STRING_TYPES = xpcom_consts.VTYPE_CHAR, xpcom_consts.VTYPE_CHAR_STR, xpcom_consts.VTYPE_STRING_SIZE_IS, \
00055                        xpcom_consts.VTYPE_CSTRING
00056 VARIANT_UNICODE_TYPES = xpcom_consts.VTYPE_WCHAR, xpcom_consts.VTYPE_DOMSTRING, xpcom_consts.VTYPE_WSTRING_SIZE_IS, \
00057                         xpcom_consts.VTYPE_ASTRING 
00058 
00059 _supports_primitives_map_ = {} # Filled on first use.
00060 
00061 _interface_sequence_types_ = types.TupleType, types.ListType
00062 _string_types_ = types.StringType, types.UnicodeType
00063 XPTI_GetInterfaceInfoManager = _xpcom.XPTI_GetInterfaceInfoManager
00064 
00065 def _GetNominatedInterfaces(obj):
00066     ret = getattr(obj, "_com_interfaces_", None)
00067     if ret is None: return None
00068     # See if the user only gave one.
00069     if type(ret) not in _interface_sequence_types_:
00070         ret = [ret]
00071     real_ret = []
00072     # For each interface, walk to the root of the interface tree.
00073     iim = XPTI_GetInterfaceInfoManager()
00074     for interface in ret:
00075         # Allow interface name or IID.
00076         interface_info = None
00077         if type(interface) in _string_types_:
00078             try:
00079                 interface_info = iim.GetInfoForName(interface)
00080             except COMException:
00081                 pass
00082         if interface_info is None:
00083             # Allow a real IID
00084             interface_info = iim.GetInfoForIID(interface)
00085         real_ret.append(interface_info.GetIID())
00086         parent = interface_info.GetParent()
00087         while parent is not None:
00088             parent_iid = parent.GetIID()
00089             if parent_iid == IID_nsISupports:
00090                 break
00091             real_ret.append(parent_iid)
00092             parent = parent.GetParent()
00093     return real_ret
00094 
00095 ##
00096 ## ClassInfo support
00097 ##
00098 ## We cache class infos by class
00099 class_info_cache = {}
00100 
00101 def GetClassInfoForObject(ob):
00102     if xpcom.server.tracer_unwrap is not None:
00103         ob = xpcom.server.tracer_unwrap(ob)
00104     klass = ob.__class__
00105     ci = class_info_cache.get(klass)
00106     if ci is None:
00107         ci = DefaultClassInfo(klass)
00108         ci = xpcom.server.WrapObject(ci, _xpcom.IID_nsIClassInfo, bWrapClient = 0)
00109         class_info_cache[klass] = ci
00110     return ci
00111 
00112 class DefaultClassInfo:
00113     _com_interfaces_ = _xpcom.IID_nsIClassInfo
00114     def __init__(self, klass):
00115         self.klass = klass
00116         self.contractID = getattr(klass, "_reg_contractid_", None)
00117         self.classDescription = getattr(klass, "_reg_desc_", None)
00118         self.classID = getattr(klass, "_reg_clsid_", None)
00119         self.implementationLanguage = 3 # Python - avoid lookups just for this
00120         self.flags = 0 # what to do here??
00121         self.interfaces = None
00122 
00123     def get_classID(self):
00124         if self.classID is None:
00125             raise ServerException(nsError.NS_ERROR_NOT_IMPLEMENTED, "Class '%r' has no class ID" % (self.klass,))
00126         return self.classID
00127 
00128     def getInterfaces(self):
00129         if self.interfaces is None:
00130             self.interfaces = _GetNominatedInterfaces(self.klass)
00131         return self.interfaces
00132 
00133     def getHelperForLanguage(self, language):
00134         return None # Not sure what to do here.
00135 
00136 class DefaultPolicy:
00137     def __init__(self, instance, iid):
00138         self._obj_ = instance
00139         self._nominated_interfaces_ = ni = _GetNominatedInterfaces(instance)
00140         self._iid_ = iid
00141         if ni is None:
00142             raise ValueError, "The object '%r' can not be used as a COM object" % (instance,)
00143         # This is really only a check for the user
00144         if __debug__:
00145             if iid != IID_nsISupports and iid not in ni:
00146                 # The object may delegate QI.
00147                 delegate_qi = getattr(instance, "_query_interface_", None)
00148                 # Perform the actual QI and throw away the result - the _real_
00149                 # QI performed by the framework will set things right!
00150                 if delegate_qi is None or not delegate_qi(iid):
00151                     raise ServerException(nsError.NS_ERROR_NO_INTERFACE)
00152         # Stuff for the magic interface conversion.
00153         self._interface_info_ = None
00154         self._interface_iid_map_ = {} # Cache - Indexed by (method_index, param_index)
00155 
00156     def _QueryInterface_(self, com_object, iid):
00157         # Framework allows us to return a single boolean integer,
00158         # or a COM object.
00159         if iid in self._nominated_interfaces_:
00160             # We return the underlying object re-wrapped
00161             # in a new gateway - which is desirable, as one gateway should only support
00162             # one interface (this wont affect the users of this policy - we can have as many
00163             # gateways as we like pointing to the same Python objects - the users never
00164             # see what object the call came in from.
00165             # NOTE: We could have simply returned the instance and let the framework
00166             # do the auto-wrap for us - but this way we prevent a round-trip back into Python
00167             # code just for the autowrap.
00168             return xpcom.server.WrapObject(self._obj_, iid, bWrapClient = 0)
00169 
00170         # Always support nsIClassInfo 
00171         if iid == _xpcom.IID_nsIClassInfo:
00172             return GetClassInfoForObject(self._obj_)
00173 
00174         # See if the instance has a QI
00175         # use lower-case "_query_interface_" as win32com does, and it doesnt really matter.
00176         delegate = getattr(self._obj_, "_query_interface_", None)
00177         if delegate is not None:
00178             # The COM object itself doesnt get passed to the child
00179             # (again, as win32com doesnt).  It is rarely needed
00180             # (in win32com, we dont even pass it to the policy, although we have identified
00181             # one place where we should - for marshalling - so I figured I may as well pass it
00182             # to the policy layer here, but no all the way down to the object.
00183             return delegate(iid)
00184         # Finally see if we are being queried for one of the "nsISupports primitives"
00185         if not _supports_primitives_map_:
00186             iim = _xpcom.XPTI_GetInterfaceInfoManager()
00187             for (iid_name, attr, cvt) in _supports_primitives_data_:
00188                 special_iid = iim.GetInfoForName(iid_name).GetIID()
00189                 _supports_primitives_map_[special_iid] = (attr, cvt)
00190         attr, cvt = _supports_primitives_map_.get(iid, (None,None))
00191         if attr is not None and hasattr(self._obj_, attr):
00192             return xpcom.server.WrapObject(SupportsPrimitive(iid, self._obj_, attr, cvt), iid, bWrapClient = 0)
00193         # Out of clever things to try!
00194         return None # We dont support this IID.
00195 
00196     def _MakeInterfaceParam_(self, interface, iid, method_index, mi, param_index):
00197         # Wrap a "raw" interface object in a nice object.  The result of this
00198         # function will be passed to one of the gateway methods.
00199         if iid is None:
00200             # look up the interface info - this will be true for all xpcom called interfaces.
00201             if self._interface_info_ is None:
00202                 import xpcom.xpt
00203                 self._interface_info_ = xpcom.xpt.Interface( self._iid_ )
00204             iid = self._interface_iid_map_.get( (method_index, param_index))
00205             if iid is None:
00206                 iid = self._interface_info_.GetIIDForParam(method_index, param_index)
00207                 self._interface_iid_map_[(method_index, param_index)] = iid
00208         # handle nsIVariant
00209         if iid == IID_nsIVariant:
00210             interface = interface.QueryInterface(iid)
00211             dt = interface.dataType
00212             if dt in VARIANT_INT_TYPES:
00213                 return interface.getAsInt32()
00214             if dt in VARIANT_LONG_TYPES:
00215                 return interface.getAsInt64()
00216             if dt in VARIANT_FLOAT_TYPES:
00217                 return interface.getAsFloat()
00218             if dt in VARIANT_STRING_TYPES:
00219                 return interface.getAsStringWithSize()
00220             if dt in VARIANT_UNICODE_TYPES:
00221                 return interface.getAsWStringWithSize()
00222             if dt == xpcom_consts.VTYPE_BOOL:
00223                 return interface.getAsBool()
00224             if dt == xpcom_consts.VTYPE_INTERFACE:
00225                 return interface.getAsISupports()
00226             if dt == xpcom_consts.VTYPE_INTERFACE_IS:
00227                 return interface.getAsInterface()
00228             if dt == xpcom_consts.VTYPE_EMPTY or dt == xpcom_consts.VTYPE_VOID:
00229                 return None
00230             if dt == xpcom_consts.VTYPE_ARRAY:
00231                 return interface.getAsArray()
00232             if dt == xpcom_consts.VTYPE_EMPTY_ARRAY:
00233                 return []
00234             if dt == xpcom_consts.VTYPE_ID:
00235                 return interface.getAsID()
00236             # all else fails...
00237             print "Warning: nsIVariant type %d not supported - returning a string" % (dt,)
00238             try:
00239                 return interface.getAsString()
00240             except COMException:
00241                 print "Error: failed to get Variant as a string - returning variant object"
00242                 traceback.print_exc()
00243                 return interface
00244             
00245         return client.Component(interface, iid)
00246     
00247     def _CallMethod_(self, com_object, index, info, params):
00248         #print "_CallMethod_", index, info, params
00249         flags, name, param_descs, ret = info
00250         assert ret[1][0] == xpcom_consts.TD_UINT32, "Expected an nsresult (%s)" % (ret,)
00251         if XPT_MD_IS_GETTER(flags):
00252             # Look for a function of that name
00253             func = getattr(self._obj_, "get_" + name, None)
00254             if func is None:
00255                 assert len(param_descs)==1 and len(params)==0, "Can only handle a single [out] arg for a default getter"
00256                 ret = getattr(self._obj_, name) # Let attribute error go here!
00257             else:
00258                 ret = func(*params)
00259             return 0, ret
00260         elif XPT_MD_IS_SETTER(flags):
00261             # Look for a function of that name
00262             func = getattr(self._obj_, "set_" + name, None)
00263             if func is None:
00264                 assert len(param_descs)==1 and len(params)==1, "Can only handle a single [in] arg for a default setter"
00265                 setattr(self._obj_, name, params[0]) # Let attribute error go here!
00266             else:
00267                 func(*params)
00268             return 0
00269         else:
00270             # A regular method.
00271             func = getattr(self._obj_, name)
00272             return 0, func(*params)
00273 
00274     def _doHandleException(self, func_name, exc_info):
00275         exc_val = exc_info[1]
00276         is_server_exception = isinstance(exc_val, ServerException)
00277         if is_server_exception:
00278             if xpcom.verbose:
00279                 print "** Information:  '%s' raised COM Exception %s" % (func_name, exc_val)
00280                 traceback.print_exception(exc_info[0], exc_val, exc_info[2])
00281                 print "** Returning nsresult from existing exception", exc_val
00282             return exc_val.errno
00283         # Unhandled exception - always print a warning.
00284         print "** Unhandled exception calling '%s'" % (func_name,)
00285         traceback.print_exception(exc_info[0], exc_val, exc_info[2])
00286         print "** Returning nsresult of NS_ERROR_FAILURE"
00287         return nsError.NS_ERROR_FAILURE
00288 
00289 
00290     # Called whenever an unhandled Python exception is detected as a result
00291     # of _CallMethod_ - this exception may have been raised during the _CallMethod_
00292     # invocation, or after its return, but when unpacking the results
00293     # eg, type errors, such as a Python integer being used as a string "out" param.
00294     def _CallMethodException_(self, com_object, index, info, params, exc_info):
00295         # Later we may want to have some smart "am I debugging" flags?
00296         # Or maybe just delegate to the actual object - it's probably got the best
00297         # idea what to do with them!
00298         flags, name, param_descs, ret = info
00299         exc_typ, exc_val, exc_tb = exc_info
00300         # use the xpt module to get a better repr for the method.
00301         # But if we fail, ignore it!
00302         try:
00303             import xpcom.xpt
00304             m = xpcom.xpt.Method(info, index, None)
00305             func_repr = m.Describe().lstrip()
00306         except:
00307             func_repr = "%s(%r)" % (name, param_descs)
00308         return self._doHandleException(func_repr, exc_info)
00309 
00310     # Called whenever a gateway fails due to anything other than _CallMethod_.
00311     # Really only used for the component loader etc objects, so most
00312     # users should never see exceptions triggered here.
00313     def _GatewayException_(self, name, exc_info):
00314         return self._doHandleException(name, exc_info)
00315 
00316 _supports_primitives_data_ = [
00317     ("nsISupportsCString", "__str__", str),
00318     ("nsISupportsString", "__str__", str),
00319     ("nsISupportsPRUint64", "__long__", long),
00320     ("nsISupportsPRInt64", "__long__", long),
00321     ("nsISupportsPRUint32", "__int__", int),
00322     ("nsISupportsPRInt32", "__int__", int),
00323     ("nsISupportsPRUint16", "__int__", int),
00324     ("nsISupportsPRInt16", "__int__", int),
00325     ("nsISupportsPRUint8", "__int__", int),
00326     ("nsISupportsPRBool", "__nonzero__", operator.truth),
00327     ("nsISupportsDouble", "__float__", float),
00328     ("nsISupportsFloat", "__float__", float),
00329 ]
00330 
00331 # Support for the nsISupports primitives:
00332 class SupportsPrimitive:
00333     _com_interfaces_ = ["nsISupports"]
00334     def __init__(self, iid, base_ob, attr_name, converter):
00335         self.iid = iid
00336         self.base_ob = base_ob
00337         self.attr_name = attr_name
00338         self.converter = converter
00339     def _query_interface_(self, iid):
00340         if iid == self.iid:
00341             return 1
00342         return None
00343     def get_data(self):
00344         method = getattr(self.base_ob, self.attr_name)
00345         val = method()
00346         return self.converter(val)
00347     def set_data(self, val):
00348         raise ServerException(nsError.NS_ERROR_NOT_IMPLEMENTED)
00349     def toString(self):
00350         return str(self.get_data())
00351 
00352 def _shutdown():
00353     class_info_cache.clear()