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 <[email protected]> (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 |
|
---|
38 | import os
|
---|
39 | from types import MethodType
|
---|
40 | import logging
|
---|
41 | from xpcom import xpt, COMException, nsError, logger
|
---|
42 |
|
---|
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
|
---|
48 |
|
---|
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__"]
|
---|
52 |
|
---|
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
|
---|
60 |
|
---|
61 | method_template = """
|
---|
62 | def %s(self, %s):
|
---|
63 | return XPTC_InvokeByIndex(self._comobj_, %d, (%s, (%s)))
|
---|
64 | """
|
---|
65 | def _MakeMethodCode(method):
|
---|
66 | # Build a declaration
|
---|
67 | param_no = 0
|
---|
68 | param_decls = []
|
---|
69 | param_flags = []
|
---|
70 | param_names = []
|
---|
71 | used_default = 0
|
---|
72 | for param in method.params:
|
---|
73 | param_no = param_no + 1
|
---|
74 | param_name = "Param%d" % (param_no,)
|
---|
75 | param_default = ""
|
---|
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)
|
---|
82 |
|
---|
83 | type_repr = xpt.MakeReprForInvoke(param)
|
---|
84 | param_flags.append( (param.param_flags,) + type_repr )
|
---|
85 | sep = ", "
|
---|
86 | param_decls = sep.join(param_decls)
|
---|
87 | if len(param_names)==1: # Damn tuple reprs.
|
---|
88 | param_names = param_names[0] + ","
|
---|
89 | else:
|
---|
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)
|
---|
93 |
|
---|
94 | # Keyed by IID, each item is a tuple of (methods, getters, setters)
|
---|
95 | interface_cache = {}
|
---|
96 | # Keyed by [iid][name], each item is an unbound method.
|
---|
97 | interface_method_cache = {}
|
---|
98 |
|
---|
99 | # Keyed by clsid from nsIClassInfo - everything ever queried for the CID.
|
---|
100 | contractid_info_cache = {}
|
---|
101 | have_shutdown = 0
|
---|
102 |
|
---|
103 | def _shutdown():
|
---|
104 | interface_cache.clear()
|
---|
105 | interface_method_cache.clear()
|
---|
106 | contractid_info_cache.clear()
|
---|
107 | global have_shutdown
|
---|
108 | have_shutdown = 1
|
---|
109 |
|
---|
110 | # Fully process the named method, generating method code etc.
|
---|
111 | def BuildMethod(method_info, iid):
|
---|
112 | name = method_info.name
|
---|
113 | try:
|
---|
114 | return interface_method_cache[iid][name]
|
---|
115 | except KeyError:
|
---|
116 | pass
|
---|
117 | # Generate it.
|
---|
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.
|
---|
122 |
|
---|
123 | ## print "Method Code for %s (%s):" % (name, iid)
|
---|
124 | ## print method_code
|
---|
125 | codeObject = compile(method_code, "<XPCOMObject method '%s'>" % (name,), "exec")
|
---|
126 | # Exec the code object
|
---|
127 | tempNameSpace = {}
|
---|
128 | exec(codeObject, globals(), tempNameSpace)
|
---|
129 | ret = tempNameSpace[name]
|
---|
130 | if iid not in interface_method_cache:
|
---|
131 | interface_method_cache[iid] = {}
|
---|
132 | interface_method_cache[iid][name] = ret
|
---|
133 | return ret
|
---|
134 |
|
---|
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
|
---|
137 |
|
---|
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)
|
---|
143 | if ret is None:
|
---|
144 | # Build the data for the cache.
|
---|
145 | method_code_blocks = []
|
---|
146 | getters = {}
|
---|
147 | setters = {}
|
---|
148 | method_infos = {}
|
---|
149 |
|
---|
150 | interface = xpt.Interface(iid)
|
---|
151 | for m in interface.methods:
|
---|
152 | flags = m.flags
|
---|
153 | if flags & FLAGS_TO_IGNORE == 0:
|
---|
154 | if flags & XPT_MD_SETTER:
|
---|
155 | param_flags = list([(x.param_flags,) + xpt.MakeReprForInvoke(x) for x in m.params])
|
---|
156 | setters[m.name] = m.method_index, param_flags
|
---|
157 | elif flags & XPT_MD_GETTER:
|
---|
158 | param_flags = list([(x.param_flags,) + xpt.MakeReprForInvoke(x) for x in m.params])
|
---|
159 | getters[m.name] = m.method_index, param_flags
|
---|
160 | else:
|
---|
161 | method_infos[m.name] = m
|
---|
162 |
|
---|
163 | # Build the constants.
|
---|
164 | 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
|
---|
169 | return ret
|
---|
170 |
|
---|
171 | class _XPCOMBase:
|
---|
172 | def __cmp__(self, other):
|
---|
173 | try:
|
---|
174 | other = other._comobj_
|
---|
175 | except AttributeError:
|
---|
176 | pass
|
---|
177 | return cmp(self._comobj_, other)
|
---|
178 |
|
---|
179 | def __hash__(self):
|
---|
180 | return hash(self._comobj_)
|
---|
181 |
|
---|
182 | # The basic rich compare ops for equality
|
---|
183 | def __eq__(self, other):
|
---|
184 | try:
|
---|
185 | other = other._comobj_
|
---|
186 | except AttributeError:
|
---|
187 | pass
|
---|
188 | return self._comobj_ == other
|
---|
189 |
|
---|
190 | def __neq__(self, other):
|
---|
191 | try:
|
---|
192 | other = other._comobj_
|
---|
193 | except AttributeError:
|
---|
194 | pass
|
---|
195 | return self._comobj_ != other
|
---|
196 |
|
---|
197 | # See if the object support strings.
|
---|
198 | def __str__(self):
|
---|
199 | try:
|
---|
200 | self._comobj_.QueryInterface(IID_nsISupportsCString, 0)
|
---|
201 | return str(self._comobj_)
|
---|
202 | except COMException:
|
---|
203 | return self.__repr__()
|
---|
204 |
|
---|
205 | def __unicode__(self):
|
---|
206 | try:
|
---|
207 | prin = self._comobj_.QueryInterface(IID_nsISupportsString)
|
---|
208 | except COMException:
|
---|
209 | return unicode(str(self))
|
---|
210 | return prin.data
|
---|
211 |
|
---|
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()
|
---|
217 | try:
|
---|
218 | prim = self._comobj_.QueryInterface(iid)
|
---|
219 | return cvt(prim.data)
|
---|
220 | except COMException:
|
---|
221 | pass
|
---|
222 | raise ValueError("This object does not support automatic numeric conversion to this type")
|
---|
223 |
|
---|
224 | def __int__(self):
|
---|
225 | return self._do_conversion(_int_interfaces, int)
|
---|
226 |
|
---|
227 | def __long__(self):
|
---|
228 | return self._do_conversion(_long_interfaces, long)
|
---|
229 |
|
---|
230 | def __float__(self):
|
---|
231 | return self._do_conversion(_float_interfaces, float)
|
---|
232 |
|
---|
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"
|
---|
236 | ob_name = None
|
---|
237 | if not hasattr(ob, "IID"):
|
---|
238 | ob_name = ob
|
---|
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
|
---|
250 |
|
---|
251 | if ob_name is None:
|
---|
252 | ob_name = "<unknown>"
|
---|
253 | self.__dict__['_object_name_'] = ob_name
|
---|
254 | self.QueryInterface(iid)
|
---|
255 |
|
---|
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.
|
---|
262 | try:
|
---|
263 | classinfo = self._comobj_.QueryInterface(IID_nsIClassInfo, 0)
|
---|
264 | except COMException:
|
---|
265 | classinfo = None
|
---|
266 | if classinfo is not None:
|
---|
267 | try:
|
---|
268 | real_cid = classinfo.contractID
|
---|
269 | except COMException:
|
---|
270 | real_cid = None
|
---|
271 | if real_cid:
|
---|
272 | self.__dict__['_object_name_'] = real_cid
|
---|
273 | contractid_info = contractid_info_cache.get(real_cid)
|
---|
274 | else:
|
---|
275 | contractid_info = None
|
---|
276 | if contractid_info is None:
|
---|
277 | try:
|
---|
278 | interface_infos = classinfo.getInterfaces()
|
---|
279 | except COMException:
|
---|
280 | interface_infos = []
|
---|
281 | for nominated_iid in interface_infos:
|
---|
282 | # Interface may appear twice in the class info list, so check this here.
|
---|
283 | if nominated_iid not in self.__dict__['_interface_infos_']:
|
---|
284 | # Just invoke our QI on the object
|
---|
285 | self.queryInterface(nominated_iid)
|
---|
286 | if real_cid is not None:
|
---|
287 | contractid_info = {}
|
---|
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
|
---|
291 | else:
|
---|
292 | for key, val in list(contractid_info.items()):
|
---|
293 | self.__dict__[key].update(val)
|
---|
294 |
|
---|
295 | self.__dict__['_com_classinfo_'] = classinfo
|
---|
296 |
|
---|
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 iid not in iis, "Already remembered this interface!"
|
---|
303 | try:
|
---|
304 | method_infos, getters, setters, constants = BuildInterfaceInfo(iid)
|
---|
305 | except COMException as 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.
|
---|
310 | iis[iid] = None
|
---|
311 | return
|
---|
312 |
|
---|
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 list(method_infos.keys()): names[name] = iid
|
---|
317 | for name in list(getters.keys()): names[name] = iid
|
---|
318 | for name in list(setters.keys()): names[name] = iid
|
---|
319 | for name in list(constants.keys()): names[name] = iid
|
---|
320 |
|
---|
321 | def QueryInterface(self, iid):
|
---|
322 | if iid in self._interfaces_:
|
---|
323 | assert iid_name in self._interface_names_, "_interfaces_ has the key, but _interface_names_ does not!"
|
---|
324 | return self
|
---|
325 | # Haven't seen this before - do a real QI.
|
---|
326 | if iid not in self._interface_infos_:
|
---|
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)
|
---|
335 |
|
---|
336 | raw_iface = self._comobj_.QueryInterface(iid, 0)
|
---|
337 |
|
---|
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
|
---|
345 | # available.
|
---|
346 | return self
|
---|
347 |
|
---|
348 | queryInterface = QueryInterface # Alternate name.
|
---|
349 |
|
---|
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:
|
---|
356 | return interface
|
---|
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
|
---|
364 | # available.
|
---|
365 | interface = self.__dict__['_interface_names_'].get(attr, None)
|
---|
366 | if interface is not None:
|
---|
367 | return interface
|
---|
368 |
|
---|
369 | if iid 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 list(self.__dict__['_interfaces_'].values()):
|
---|
378 | try:
|
---|
379 | ret = getattr(interface, attr)
|
---|
380 | self.__dict__['_name_to_interface_iid_'][attr] = interface._iid_
|
---|
381 | return ret
|
---|
382 | except AttributeError:
|
---|
383 | pass
|
---|
384 | raise AttributeError("XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr))
|
---|
385 |
|
---|
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)
|
---|
392 | if iid is not 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)
|
---|
398 | return
|
---|
399 | raise AttributeError("XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr))
|
---|
400 |
|
---|
401 | def _get_classinfo_repr_(self):
|
---|
402 | try:
|
---|
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!"
|
---|
406 | except COMException:
|
---|
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
|
---|
411 |
|
---|
412 | iface_names = list(self.__dict__['_interface_names_'].keys())
|
---|
413 | try:
|
---|
414 | iface_names.remove("nsISupports")
|
---|
415 | except ValueError:
|
---|
416 | pass
|
---|
417 | iface_names.sort()
|
---|
418 |
|
---|
419 | iface_desc = "implementing %s" % (",".join(iface_names),)
|
---|
420 | return iface_desc
|
---|
421 |
|
---|
422 | def __repr__(self):
|
---|
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)
|
---|
426 |
|
---|
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_'] = list(constants.keys())
|
---|
439 |
|
---|
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)
|
---|
444 |
|
---|
445 | ret = getattr(self.__dict__['_comobj_'], attr, None)
|
---|
446 | if ret is not None:
|
---|
447 | return ret
|
---|
448 | # Do the function thing first.
|
---|
449 | unbound_method = self.__dict__['_methods_'].get(attr, None)
|
---|
450 | if unbound_method is not None:
|
---|
451 | return MethodType(unbound_method, self)
|
---|
452 |
|
---|
453 | getters = self.__dict__['_property_getters_']
|
---|
454 | info = getters.get(attr)
|
---|
455 | if info is not None:
|
---|
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)
|
---|
461 |
|
---|
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_)
|
---|
467 | # Cache it locally
|
---|
468 | self.__dict__['_methods_'][attr] = unbound_method
|
---|
469 | return MethodType(unbound_method, self)
|
---|
470 |
|
---|
471 | raise AttributeError("XPCOM component '%s' has no attribute '%s'" % (self._object_name_, attr))
|
---|
472 |
|
---|
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 attr in self.__dict__ and attr not in self.__dict__['_constant_names_']:
|
---|
477 | self.__dict__[attr] = val
|
---|
478 | return
|
---|
479 | # Start sniffing for what sort of attribute this might be?
|
---|
480 | setters = self.__dict__['_property_setters_']
|
---|
481 | info = setters.get(attr)
|
---|
482 | if info is None:
|
---|
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)
|
---|
489 |
|
---|
490 | def __repr__(self):
|
---|
491 | return "<XPCOM interface '%s'>" % (self._object_name_,)
|
---|
492 |
|
---|
493 |
|
---|
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)
|
---|
498 |
|
---|
499 | class WeakReference:
|
---|
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.
|
---|
503 |
|
---|
504 | Once you have a weak-reference, you can "call" the object to get
|
---|
505 | back a strong reference. Eg:
|
---|
506 |
|
---|
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)
|
---|
512 |
|
---|
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.
|
---|
515 | """
|
---|
516 | def __init__(self, ob, iid = None):
|
---|
517 | swr = Component(ob._comobj_, IID_nsISupportsWeakReference)
|
---|
518 | self._comobj_ = Component(swr.GetWeakReference()._comobj_, IID_nsIWeakReference)
|
---|
519 | if iid is None:
|
---|
520 | try:
|
---|
521 | iid = ob.IID
|
---|
522 | except AttributeError:
|
---|
523 | iid = IID_nsISupports
|
---|
524 | self._iid_ = iid
|
---|
525 | def __call__(self, iid = None):
|
---|
526 | if iid is None: iid = self._iid_
|
---|
527 | try:
|
---|
528 | return Component(self._comobj_.QueryReferent(iid)._comobj_, iid)
|
---|
529 | except COMException as details:
|
---|
530 | if details.errno != nsError.NS_ERROR_NULL_POINTER:
|
---|
531 | raise
|
---|
532 | return None
|
---|