1
# ***** BEGIN LICENSE BLOCK *****
2
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
14
# The Original Code is the Python XPCOM language bindings.
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.
22
# Mark Hammond <MarkH@ActiveState.com> (original author)
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.
36
# ***** END LICENSE BLOCK *****
41
from xpcom import xpt, COMException, nsError, logger
43
# Suck in stuff from _xpcom we use regularly to prevent a module lookup
44
from xpcom._xpcom import IID_nsISupports, IID_nsIClassInfo, \
45
IID_nsISupportsCString, IID_nsISupportsString, \
46
IID_nsISupportsWeakReference, IID_nsIWeakReference, \
47
XPTI_GetInterfaceInfoManager, GetComponentManager, XPTC_InvokeByIndex
49
# Attribute names we may be __getattr__'d for, but know we don't want to delegate
50
# Could maybe just look for startswith("__") but this may screw things for some objects.
51
_special_getattr_names = ["__del__", "__len__", "__nonzero__", "__eq__", "__neq__"]
53
_just_int_interfaces = ["nsISupportsPRInt32", "nsISupportsPRInt16", "nsISupportsPRUint32", "nsISupportsPRUint16", "nsISupportsPRUint8", "nsISupportsPRBool"]
54
_just_long_interfaces = ["nsISupportsPRInt64", "nsISupportsPRUint64"]
55
_just_float_interfaces = ["nsISupportsDouble", "nsISupportsFloat"]
56
# When doing a specific conversion, the order we try the interfaces in.
57
_int_interfaces = _just_int_interfaces + _just_float_interfaces
58
_long_interfaces = _just_long_interfaces + _just_int_interfaces + _just_float_interfaces
59
_float_interfaces = _just_float_interfaces + _just_long_interfaces + _just_int_interfaces
63
return XPTC_InvokeByIndex(self._comobj_, %d, (%s, (%s)))
65
def _MakeMethodCode(method):
72
for param in method.params:
73
param_no = param_no + 1
74
param_name = "Param%d" % (param_no,)
76
if not param.hidden_indicator and param.IsIn() and not param.IsDipper():
77
if param.IsOut() or used_default: # If the param is "inout", provide a useful default for the "in" direction.
78
param_default = " = None"
79
used_default = 1 # Once we have used one once, we must for the rest!
80
param_decls.append(param_name + param_default)
81
param_names.append(param_name)
83
type_repr = xpt.MakeReprForInvoke(param)
84
param_flags.append( (param.param_flags,) + type_repr )
86
param_decls = sep.join(param_decls)
87
if len(param_names)==1: # Damn tuple reprs.
88
param_names = param_names[0] + ","
90
param_names = sep.join(param_names)
91
# A couple of extra newlines make them easier to read for debugging :-)
92
return method_template % (method.name, param_decls, method.method_index, tuple(param_flags), param_names)
94
# Keyed by IID, each item is a tuple of (methods, getters, setters)
96
# Keyed by [iid][name], each item is an unbound method.
97
interface_method_cache = {}
99
# Keyed by clsid from nsIClassInfo - everything ever queried for the CID.
100
contractid_info_cache = {}
104
interface_cache.clear()
105
interface_method_cache.clear()
106
contractid_info_cache.clear()
110
# Fully process the named method, generating method code etc.
111
def BuildMethod(method_info, iid):
112
name = method_info.name
114
return interface_method_cache[iid][name]
118
assert not (method_info.IsSetter() or method_info.IsGetter()), "getters and setters should have been weeded out by now"
119
method_code = _MakeMethodCode(method_info)
120
# Build the method - We only build a function object here
121
# - they are bound to each instance as needed.
123
## print "Method Code for %s (%s):" % (name, iid)
125
codeObject = compile(method_code, "<XPCOMObject method '%s'>" % (name,), "exec")
126
# Exec the code object
128
exec codeObject in globals(), tempNameSpace
129
ret = tempNameSpace[name]
130
if not interface_method_cache.has_key(iid):
131
interface_method_cache[iid] = {}
132
interface_method_cache[iid][name] = ret
135
from xpcom.xpcom_consts import XPT_MD_GETTER, XPT_MD_SETTER, XPT_MD_NOTXPCOM, XPT_MD_CTOR, XPT_MD_HIDDEN
136
FLAGS_TO_IGNORE = XPT_MD_NOTXPCOM | XPT_MD_CTOR | XPT_MD_HIDDEN
138
# Pre-process the interface - generate a list of methods, constants etc,
139
# but don't actually generate the method code.
140
def BuildInterfaceInfo(iid):
141
assert not have_shutdown, "Can't build interface info after a shutdown"
142
ret = interface_cache.get(iid, None)
144
# Build the data for the cache.
145
method_code_blocks = []
150
interface = xpt.Interface(iid)
151
for m in interface.methods:
153
if flags & FLAGS_TO_IGNORE == 0:
154
if flags & XPT_MD_SETTER:
155
param_flags = map(lambda x: (x.param_flags,) + xpt.MakeReprForInvoke(x), m.params)
156
setters[m.name] = m.method_index, param_flags
157
elif flags & XPT_MD_GETTER:
158
param_flags = map(lambda x: (x.param_flags,) + xpt.MakeReprForInvoke(x), m.params)
159
getters[m.name] = m.method_index, param_flags
161
method_infos[m.name] = m
163
# Build the constants.
165
for c in interface.constants:
166
constants[c.name] = c.value
167
ret = method_infos, getters, setters, constants
168
interface_cache[iid] = ret
172
def __cmp__(self, other):
174
other = other._comobj_
175
except AttributeError:
177
return cmp(self._comobj_, other)
180
return hash(self._comobj_)
182
# The basic rich compare ops for equality
183
def __eq__(self, other):
185
other = other._comobj_
186
except AttributeError:
188
return self._comobj_ == other
190
def __neq__(self, other):
192
other = other._comobj_
193
except AttributeError:
195
return self._comobj_ != other
197
# See if the object support strings.
200
self._comobj_.QueryInterface(IID_nsISupportsCString, 0)
201
return str(self._comobj_)
203
return self.__repr__()
205
def __unicode__(self):
207
prin = self._comobj_.QueryInterface(IID_nsISupportsString)
209
return unicode(str(self))
212
# Try the numeric support.
213
def _do_conversion(self, interface_names, cvt):
214
iim = XPTI_GetInterfaceInfoManager()
215
for interface_name in interface_names:
216
iid = iim.GetInfoForName(interface_name).GetIID()
218
prim = self._comobj_.QueryInterface(iid)
219
return cvt(prim.data)
222
raise ValueError, "This object does not support automatic numeric conversion to this type"
225
return self._do_conversion(_int_interfaces, int)
228
return self._do_conversion(_long_interfaces, long)
231
return self._do_conversion(_float_interfaces, float)
233
class Component(_XPCOMBase):
234
def __init__(self, ob, iid = IID_nsISupports):
235
assert not hasattr(ob, "_comobj_"), "Should be a raw nsIWhatever, not a wrapped one"
237
if not hasattr(ob, "IID"):
239
cm = GetComponentManager()
240
ob = cm.createInstanceByContractID(ob)
241
assert not hasattr(ob, "_comobj_"), "The created object should be a raw nsIWhatever, not a wrapped one"
242
# Keep a reference to the object in the component too
243
self.__dict__['_comobj_'] = ob
244
# hit __dict__ directly to avoid __setattr__()
245
self.__dict__['_interfaces_'] = {} # keyed by IID
246
self.__dict__['_interface_names_'] = {} # keyed by IID name
247
self.__dict__['_interface_infos_'] = {} # keyed by IID
248
self.__dict__['_name_to_interface_iid_'] = {}
249
self.__dict__['_tried_classinfo_'] = 0
252
ob_name = "<unknown>"
253
self.__dict__['_object_name_'] = ob_name
254
self.QueryInterface(iid)
256
def _build_all_supported_interfaces_(self):
257
# Use nsIClassInfo, but don't do it at object construction to keep perf up.
258
# Only pay the penalty when we really need it.
259
assert not self._tried_classinfo_, "already tried to get the class info."
260
self.__dict__['_tried_classinfo_'] = 1
261
# See if nsIClassInfo is supported.
263
classinfo = self._comobj_.QueryInterface(IID_nsIClassInfo, 0)
266
if classinfo is not None:
268
real_cid = classinfo.contractID
272
self.__dict__['_object_name_'] = real_cid
273
contractid_info = contractid_info_cache.get(real_cid)
275
contractid_info = None
276
if contractid_info is None:
278
interface_infos = classinfo.getInterfaces()
281
for nominated_iid in interface_infos:
282
# Interface may appear twice in the class info list, so check this here.
283
if not self.__dict__['_interface_infos_'].has_key(nominated_iid):
284
# Just invoke our QI on the object
285
self.queryInterface(nominated_iid)
286
if real_cid is not None:
288
contractid_info['_name_to_interface_iid_'] = self.__dict__['_name_to_interface_iid_']
289
contractid_info['_interface_infos_'] = self.__dict__['_interface_infos_']
290
contractid_info_cache[real_cid] = contractid_info
292
for key, val in contractid_info.items():
293
self.__dict__[key].update(val)
295
self.__dict__['_com_classinfo_'] = classinfo
297
def _remember_interface_info(self, iid):
298
# XXX - there is no good reason to cache this only in each instance
299
# It should be cached at the module level, so we don't need to
300
# rebuild the world for each new object.
301
iis = self.__dict__['_interface_infos_']
302
assert not iis.has_key(iid), "Already remembered this interface!"
304
method_infos, getters, setters, constants = BuildInterfaceInfo(iid)
305
except COMException, why:
306
# Failing to build an interface info generally isn't a real
307
# problem - its probably just that the interface is non-scriptable.
308
logger.info("Failed to build interface info for %s: %s", iid, why)
309
# Remember the fact we failed.
313
# Remember all the names so we can delegate
314
iis[iid] = method_infos, getters, setters, constants
315
names = self.__dict__['_name_to_interface_iid_']
316
for name in method_infos.keys(): names[name] = iid
317
for name in getters.keys(): names[name] = iid
318
for name in setters.keys(): names[name] = iid
319
for name in constants.keys(): names[name] = iid
321
def QueryInterface(self, iid):
322
if self._interfaces_.has_key(iid):
323
assert self._interface_names_.has_key(iid.name), "_interfaces_ has the key, but _interface_names_ does not!"
325
# Haven't seen this before - do a real QI.
326
if not self._interface_infos_.has_key(iid):
327
self._remember_interface_info(iid)
328
iface_info = self._interface_infos_[iid]
329
if iface_info is None:
330
# We have tried, but failed, to get this interface info. Its
331
# unlikely to work later either - its probably non-scriptable.
332
# That means our component wrappers are useless - so just return a
333
# raw nsISupports object with no wrapper.
334
return self._comobj_.QueryInterface(iid, 0)
336
raw_iface = self._comobj_.QueryInterface(iid, 0)
338
method_infos, getters, setters, constants = iface_info
339
new_interface = _Interface(raw_iface, iid, method_infos,
340
getters, setters, constants)
341
self._interfaces_[iid] = new_interface
342
self._interface_names_[iid.name] = new_interface
343
# As we 'flatten' objects when possible, a QI on an object just
344
# returns ourself - all the methods etc on this interface are
348
queryInterface = QueryInterface # Alternate name.
350
def __getattr__(self, attr):
351
if attr in _special_getattr_names:
352
raise AttributeError, attr
353
# First allow the interface name to return the "raw" interface
354
interface = self.__dict__['_interface_names_'].get(attr, None)
355
if interface is not None:
357
# See if we know the IID of an interface providing this attribute
358
iid = self.__dict__['_name_to_interface_iid_'].get(attr, None)
359
# This may be first time trying this interface - get the nsIClassInfo
360
if iid is None and not self._tried_classinfo_:
361
self._build_all_supported_interfaces_()
362
iid = self.__dict__['_name_to_interface_iid_'].get(attr, None)
363
# If the request is for an interface name, it may now be
365
interface = self.__dict__['_interface_names_'].get(attr, None)
366
if interface is not None:
370
interface = self.__dict__['_interfaces_'].get(iid, None)
371
if interface is None:
372
self.QueryInterface(iid)
373
interface = self.__dict__['_interfaces_'][iid]
374
return getattr(interface, attr)
375
# Some interfaces may provide this name via "native" support.
376
# Loop over all interfaces, and if found, cache it for next time.
377
for interface in self.__dict__['_interfaces_'].values():
379
ret = getattr(interface, attr)
380
self.__dict__['_name_to_interface_iid_'][attr] = interface._iid_
382
except AttributeError:
384
raise AttributeError, "XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr)
386
def __setattr__(self, attr, val):
387
iid = self._name_to_interface_iid_.get(attr, None)
388
# This may be first time trying this interface - get the nsIClassInfo
389
if iid is None and not self._tried_classinfo_:
390
self._build_all_supported_interfaces_()
391
iid = self.__dict__['_name_to_interface_iid_'].get(attr, None)
393
interface = self._interfaces_.get(iid, None)
394
if interface is None:
395
self.QueryInterface(iid)
396
interface = self.__dict__['_interfaces_'][iid]
397
setattr(interface, attr, val)
399
raise AttributeError, "XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr)
401
def _get_classinfo_repr_(self):
403
if not self._tried_classinfo_:
404
self._build_all_supported_interfaces_()
405
assert self._tried_classinfo_, "Should have tried the class info by now!"
407
# Error building the info - ignore the error, but ensure that
408
# we are flagged as *not* having built, so the error is seen
409
# by the first caller who actually *needs* this to work.
410
self.__dict__['_tried_classinfo_'] = 0
412
iface_names = self.__dict__['_interface_names_'].keys()
414
iface_names.remove("nsISupports")
419
iface_desc = "implementing %s" % (",".join(iface_names),)
423
# We can advantage from nsIClassInfo - use it.
424
iface_desc = self._get_classinfo_repr_()
425
return "<XPCOM component '%s' (%s)>" % (self._object_name_,iface_desc)
427
class _Interface(_XPCOMBase):
428
def __init__(self, comobj, iid, method_infos, getters, setters, constants):
429
self.__dict__['_comobj_'] = comobj
430
self.__dict__['_iid_'] = iid
431
self.__dict__['_property_getters_'] = getters
432
self.__dict__['_property_setters_'] = setters
433
self.__dict__['_method_infos_'] = method_infos # method infos waiting to be turned into real methods.
434
self.__dict__['_methods_'] = {} # unbound methods
435
self.__dict__['_object_name_'] = iid.name
436
self.__dict__.update(constants)
437
# We remember the constant names to prevent the user trying to assign to them!
438
self.__dict__['_constant_names_'] = constants.keys()
440
def __getattr__(self, attr):
441
# Allow the underlying interface to provide a better implementation if desired.
442
if attr in _special_getattr_names:
443
raise AttributeError, attr
445
ret = getattr(self.__dict__['_comobj_'], attr, None)
448
# Do the function thing first.
449
unbound_method = self.__dict__['_methods_'].get(attr, None)
450
if unbound_method is not None:
451
return new.instancemethod(unbound_method, self, self.__class__)
453
getters = self.__dict__['_property_getters_']
454
info = getters.get(attr)
456
method_index, param_infos = info
457
if len(param_infos)!=1: # Only expecting a retval
458
raise RuntimeError, "Can't get properties with this many args!"
459
args = ( param_infos, () )
460
return XPTC_InvokeByIndex(self._comobj_, method_index, args)
462
# See if we have a method info waiting to be turned into a method.
463
# Do this last as it is a one-off hit.
464
method_info = self.__dict__['_method_infos_'].get(attr, None)
465
if method_info is not None:
466
unbound_method = BuildMethod(method_info, self._iid_)
468
self.__dict__['_methods_'][attr] = unbound_method
469
return new.instancemethod(unbound_method, self, self.__class__)
471
raise AttributeError, "XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr)
473
def __setattr__(self, attr, val):
474
# If we already have a __dict__ item of that name, and its not one of
475
# our constants, we just directly set it, and leave.
476
if self.__dict__.has_key(attr) and attr not in self.__dict__['_constant_names_']:
477
self.__dict__[attr] = val
479
# Start sniffing for what sort of attribute this might be?
480
setters = self.__dict__['_property_setters_']
481
info = setters.get(attr)
483
raise AttributeError, "XPCOM component '%s' can not set attribute '%s'" % (self._object_name_, attr)
484
method_index, param_infos = info
485
if len(param_infos)!=1: # Only expecting a single input val
486
raise RuntimeError, "Can't set properties with this many args!"
487
real_param_infos = ( param_infos, (val,) )
488
return XPTC_InvokeByIndex(self._comobj_, method_index, real_param_infos)
491
return "<XPCOM interface '%s'>" % (self._object_name_,)
494
# Called by the _xpcom C++ framework to wrap interfaces up just
495
# before they are returned.
496
def MakeInterfaceResult(ob, iid):
497
return Component(ob, iid)
500
"""A weak-reference object. You construct a weak reference by passing
501
any COM object you like. If the object does not support weak
502
refs, you will get a standard NS_NOINTERFACE exception.
504
Once you have a weak-reference, you can "call" the object to get
505
back a strong reference. Eg:
507
>>> some_ob = components.classes['...']
508
>>> weak_ref = WeakReference(some_ob)
509
>>> new_ob = weak_ref() # new_ob is effectively "some_ob" at this point
510
>>> # EXCEPT: new_ob may be None if some_ob has already died - a
511
>>> # weak reference does not keep the object alive (that is the point)
513
You should never hold onto this resulting strong object for a long time,
514
or else you defeat the purpose of the weak-reference.
516
def __init__(self, ob, iid = None):
517
swr = Component(ob._comobj_, IID_nsISupportsWeakReference)
518
self._comobj_ = Component(swr.GetWeakReference()._comobj_, IID_nsIWeakReference)
522
except AttributeError:
523
iid = IID_nsISupports
525
def __call__(self, iid = None):
526
if iid is None: iid = self._iid_
528
return Component(self._comobj_.QueryReferent(iid)._comobj_, iid)
529
except COMException, details:
530
if details.errno != nsError.NS_ERROR_NULL_POINTER: