summaryrefslogtreecommitdiffstats
path: root/src/libs/xpcom18a4/python/server
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libs/xpcom18a4/python/server/.cvsignore2
-rwxr-xr-xsrc/libs/xpcom18a4/python/server/__init__.py88
-rwxr-xr-xsrc/libs/xpcom18a4/python/server/enumerator.py58
-rwxr-xr-xsrc/libs/xpcom18a4/python/server/factory.py68
-rwxr-xr-xsrc/libs/xpcom18a4/python/server/loader.py227
-rwxr-xr-xsrc/libs/xpcom18a4/python/server/module.py108
-rwxr-xr-xsrc/libs/xpcom18a4/python/server/policy.py398
7 files changed, 949 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/python/server/.cvsignore b/src/libs/xpcom18a4/python/server/.cvsignore
new file mode 100644
index 00000000..52e4e611
--- /dev/null
+++ b/src/libs/xpcom18a4/python/server/.cvsignore
@@ -0,0 +1,2 @@
+*.pyc
+*.pyo
diff --git a/src/libs/xpcom18a4/python/server/__init__.py b/src/libs/xpcom18a4/python/server/__init__.py
new file mode 100755
index 00000000..48b15106
--- /dev/null
+++ b/src/libs/xpcom18a4/python/server/__init__.py
@@ -0,0 +1,88 @@
+# ***** 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
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Mark Hammond <MarkH@ActiveState.com>
+#
+# 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 *****
+
+# The xpcom.server package.
+
+from xpcom.server.policy import DefaultPolicy
+from xpcom import _xpcom
+
+# We define the concept of a single "tracer" object - similar to the single
+# Python "trace hook" for debugging. Someone can set
+# xpcom.server.tracer to some class/function, and it will be used in place
+# of the real xpcom object. Presumably this "trace" object will delegate
+# to the real object, but presumably also taking some other action, such
+# as calling a profiler or debugger.
+# tracer_unwrap is a function used to "unwrap" the tracer object.
+# If is expected that tracer_unwrap will be called with an object
+# previously returned by "tracer()".
+tracer = tracer_unwrap = None
+
+# Wrap an instance in an interface (via a policy)
+def WrapObject(ob, iid, policy = None, bWrapClient = 1):
+ """Called by the framework to attempt to wrap
+ an object in a policy.
+ If iid is None, it will use the first interface the object indicates it supports.
+ """
+ if policy is None:
+ policy = DefaultPolicy
+ if tracer is not None:
+ ob = tracer(ob)
+ return _xpcom.WrapObject(policy( ob, iid ), iid, bWrapClient)
+
+# Unwrap a Python object back into the Python object
+def UnwrapObject(ob):
+ if ob is None:
+ return None
+ ret = _xpcom.UnwrapObject(ob)._obj_
+ if tracer_unwrap is not None:
+ ret = tracer_unwrap(ret)
+ return ret
+
+# Create the main module for the Python loader.
+# This is a once only init process, and the returned object
+# if used to load all other Python components.
+
+# This means that we keep all factories, modules etc implemented in
+# Python!
+def NS_GetModule( serviceManager, nsIFile ):
+ from . import loader
+ iid = _xpcom.IID_nsIModule
+ return WrapObject(loader.MakePythonComponentLoaderModule(serviceManager, nsIFile), iid, bWrapClient = 0)
+
+def _shutdown():
+ from xpcom.server.policy import _shutdown
+ _shutdown()
diff --git a/src/libs/xpcom18a4/python/server/enumerator.py b/src/libs/xpcom18a4/python/server/enumerator.py
new file mode 100755
index 00000000..d3fb89a9
--- /dev/null
+++ b/src/libs/xpcom18a4/python/server/enumerator.py
@@ -0,0 +1,58 @@
+# ***** 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 <MarkH@ActiveState.com> (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 *****
+
+from xpcom import components
+
+# This class is created by Python components when it
+# needs to return an enumerator.
+# For example, a component may implement a function:
+# nsISimpleEnumerator enumSomething();
+# This could could simply say:
+# return SimpleEnumerator([something1, something2, something3])
+class SimpleEnumerator:
+ _com_interfaces_ = [components.interfaces.nsISimpleEnumerator]
+
+ def __init__(self, data):
+ self._data = data
+ self._index = 0
+
+ def hasMoreElements(self):
+ return self._index < len(self._data)
+
+ def getNext(self):
+ self._index = self._index + 1
+ return self._data[self._index-1]
diff --git a/src/libs/xpcom18a4/python/server/factory.py b/src/libs/xpcom18a4/python/server/factory.py
new file mode 100755
index 00000000..b65483dd
--- /dev/null
+++ b/src/libs/xpcom18a4/python/server/factory.py
@@ -0,0 +1,68 @@
+# ***** 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 <MarkH@ActiveState.com> (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 *****
+
+# Class factory
+#
+# Hardly worth its own source file!
+import xpcom
+from xpcom import components, nsError, _xpcom, logger
+
+class Factory:
+ _com_interfaces_ = components.interfaces.nsIFactory
+ # This will only ever be constructed via other Python code,
+ # so we can have ctor args.
+ def __init__(self, klass):
+ self.klass = klass
+
+ def createInstance(self, outer, iid):
+ if outer is not None:
+ raise xpcom.ServerException(nsError.NS_ERROR_NO_AGGREGATION)
+
+ logger.debug("Python Factory creating %s", self.klass.__name__)
+ try:
+ return self.klass()
+ except:
+ # An exception here may not be obvious to the user - none
+ # of their code has been called yet. It can be handy on
+ # failure to tell the user what class failed!
+ logger.error("Creation of class '%r' failed!\nException details follow\n",
+ self.klass)
+ # The framework itself will also report the error.
+ raise
+
+ def lockServer(self, lock):
+ logger.debug("Python Factory LockServer called '%s'", lock)
diff --git a/src/libs/xpcom18a4/python/server/loader.py b/src/libs/xpcom18a4/python/server/loader.py
new file mode 100755
index 00000000..ebd42b61
--- /dev/null
+++ b/src/libs/xpcom18a4/python/server/loader.py
@@ -0,0 +1,227 @@
+# ***** 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
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Mark Hammond <MarkH@ActiveState.com>
+#
+# 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 *****
+
+import xpcom
+from xpcom import components, logger
+from . import module
+import glob
+import os
+from xpcom.client import Component
+
+# Until we get interface constants.
+When_Startup = 0
+When_Component = 1
+When_Timer = 2
+
+def _has_good_attr(object, attr):
+ # Actually allows "None" to be specified to disable inherited attributes.
+ return getattr(object, attr, None) is not None
+
+def FindCOMComponents(py_module):
+ # For now, just run over all classes looking for likely candidates.
+ comps = []
+ for name, object in list(py_module.__dict__.items()):
+ try:
+ if (type(object) == type or issubclass(object, object)) and \
+ _has_good_attr(object, "_com_interfaces_") and \
+ _has_good_attr(object, "_reg_clsid_") and \
+ _has_good_attr(object, "_reg_contractid_"):
+ comps.append(object)
+ except TypeError:
+ # The issubclass call raises TypeError when the obj is not a class.
+ pass;
+ return comps
+
+def register_self(klass, compMgr, location, registryLocation, componentType):
+ pcl = PythonComponentLoader
+ from xpcom import _xpcom
+ svc = _xpcom.GetServiceManager().getServiceByContractID("@mozilla.org/categorymanager;1", components.interfaces.nsICategoryManager)
+ svc.addCategoryEntry("component-loader", pcl._reg_component_type_, pcl._reg_contractid_, 1, 1)
+
+class PythonComponentLoader:
+ _com_interfaces_ = components.interfaces.nsIComponentLoader
+ _reg_clsid_ = "{63B68B1E-3E62-45f0-98E3-5E0B5797970C}" # Never copy these!
+ _reg_contractid_ = "moz.pyloader.1"
+ _reg_desc_ = "Python component loader"
+ # Optional function which performs additional special registration
+ # Appears that no special unregistration is needed for ComponentLoaders, hence no unregister function.
+ _reg_registrar_ = (register_self,None)
+ # Custom attributes for ComponentLoader registration.
+ _reg_component_type_ = "script/python"
+
+ def __init__(self):
+ self.com_modules = {} # Keyed by module's FQN as obtained from nsIFile.path
+ self.moduleFactory = module.Module
+ self.num_modules_this_register = 0
+
+ def _getCOMModuleForLocation(self, componentFile):
+ fqn = componentFile.path
+ mod = self.com_modules.get(fqn)
+ if mod is not None:
+ return mod
+ import ihooks, sys
+ base_name = os.path.splitext(os.path.basename(fqn))[0]
+ loader = ihooks.ModuleLoader()
+
+ module_name_in_sys = "component:%s" % (base_name,)
+ stuff = loader.find_module(base_name, [componentFile.parent.path])
+ assert stuff is not None, "Couldnt find the module '%s'" % (base_name,)
+ py_mod = loader.load_module( module_name_in_sys, stuff )
+
+ # Make and remember the COM module.
+ comps = FindCOMComponents(py_mod)
+ mod = self.moduleFactory(comps)
+
+ self.com_modules[fqn] = mod
+ return mod
+
+ def getFactory(self, clsid, location, type):
+ # return the factory
+ assert type == self._reg_component_type_, "Being asked to create an object not of my type:%s" % (type,)
+ # FIXME: how to do this without obsolete component manager?
+ cmo = components.manager.queryInterface(components.interfaces.nsIComponentManagerObsolete)
+ file_interface = cmo.specForRegistryLocation(location)
+ # delegate to the module.
+ m = self._getCOMModuleForLocation(file_interface)
+ return m.getClassObject(components.manager, clsid, components.interfaces.nsIFactory)
+
+ def init(self, comp_mgr, registry):
+ # void
+ self.comp_mgr = comp_mgr
+ logger.debug("Python component loader init() called")
+
+ # Called when a component of the appropriate type is registered,
+ # to give the component loader an opportunity to do things like
+ # annotate the registry and such.
+ def onRegister (self, clsid, type, className, proId, location, replace, persist):
+ logger.debug("Python component loader - onRegister() called")
+
+ def autoRegisterComponents (self, when, directory):
+ directory_path = directory.path
+ self.num_modules_this_register = 0
+ logger.debug("Auto-registering all Python components in '%s'", directory_path)
+
+ # ToDo - work out the right thing here
+ # eg - do we recurse?
+ # - do we support packages?
+ entries = directory.directoryEntries
+ while entries.HasMoreElements():
+ entry = entries.GetNext(components.interfaces.nsIFile)
+ if os.path.splitext(entry.path)[1]==".py":
+ try:
+ self.autoRegisterComponent(when, entry)
+ # Handle some common user errors
+ except xpcom.COMException as details:
+ from xpcom import nsError
+ # If the interface name does not exist, suppress the traceback
+ if details.errno==nsError.NS_ERROR_NO_INTERFACE:
+ logger.error("Registration of '%s' failed\n %s",
+ entry.leafName, details.message)
+ else:
+ logger.exception("Registration of '%s' failed!", entry.leafName)
+ except SyntaxError as details:
+ # Syntax error in source file - no useful traceback here either.
+ logger.error("Registration of '%s' failed\n %s",
+ entry.leafName, details)
+ except:
+ # All other exceptions get the full traceback.
+ logger.exception("Registration of '%s' failed.", entry.leafName)
+
+ def autoRegisterComponent (self, when, componentFile):
+ # bool return
+
+ # Check if we actually need to do anything
+ modtime = componentFile.lastModifiedTime
+ loader_mgr = components.manager.queryInterface(components.interfaces.nsIComponentLoaderManager)
+ if not loader_mgr.hasFileChanged(componentFile, None, modtime):
+ return 1
+
+ if self.num_modules_this_register == 0:
+ # New components may have just installed new Python
+ # modules into the main python directory (including new .pth files)
+ # So we ask Python to re-process our site directory.
+ # Note that the pyloader does the equivalent when loading.
+ try:
+ from xpcom import _xpcom
+ import site
+ NS_XPCOM_CURRENT_PROCESS_DIR="XCurProcD"
+ dirname = _xpcom.GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR)
+ dirname.append("python")
+ site.addsitedir(dirname.path)
+ except:
+ logger.exception("PyXPCOM loader failed to process site directory before component registration")
+
+ self.num_modules_this_register += 1
+
+ # auto-register via the module.
+ m = self._getCOMModuleForLocation(componentFile)
+ m.registerSelf(components.manager, componentFile, None, self._reg_component_type_)
+ loader_mgr = components.manager.queryInterface(components.interfaces.nsIComponentLoaderManager)
+ loader_mgr.saveFileInfo(componentFile, None, modtime)
+ return 1
+
+ def autoUnregisterComponent (self, when, componentFile):
+ # bool return
+ # auto-unregister via the module.
+ m = self._getCOMModuleForLocation(componentFile)
+ loader_mgr = components.manager.queryInterface(components.interfaces.nsIComponentLoaderManager)
+ try:
+ m.unregisterSelf(components.manager, componentFile)
+ finally:
+ loader_mgr.removeFileInfo(componentFile, None)
+ return 1
+
+ def registerDeferredComponents (self, when):
+ # bool return
+ logger.debug("Python component loader - registerDeferred() called")
+ return 0 # no more to register
+
+ def unloadAll (self, when):
+ # This is called at shutdown time - don't get too upset if an error
+ # results from logging due to the logfile being closed
+ try:
+ logger.debug("Python component loader being asked to unload all components!")
+ except:
+ # Evil blank except, but restricting to just catching IOError
+ # failure means custom logs could still screw us
+ pass
+ self.comp_mgr = None
+ self.com_modules = {}
+
+def MakePythonComponentLoaderModule(serviceManager, nsIFile):
+ from . import module
+ return module.Module( [PythonComponentLoader] )
diff --git a/src/libs/xpcom18a4/python/server/module.py b/src/libs/xpcom18a4/python/server/module.py
new file mode 100755
index 00000000..73024e95
--- /dev/null
+++ b/src/libs/xpcom18a4/python/server/module.py
@@ -0,0 +1,108 @@
+# ***** 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 ActiveState Tool Corp. are Copyright (C) 2000, 2001
+# ActiveState Tool Corp. All Rights Reserved.
+#
+# Contributor(s):
+# Mark Hammond <MarkH@ActiveState.com> (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 *****
+
+from xpcom import components
+from xpcom import ServerException, Exception
+from xpcom import nsError
+
+from . import factory
+
+import types
+import os
+
+class Module:
+ _com_interfaces_ = components.interfaces.nsIModule
+ def __init__(self, comps):
+ # Build a map of classes we can provide factories for.
+ c = self.components = {}
+ for klass in comps:
+ c[components.ID(klass._reg_clsid_)] = klass
+ self.klassFactory = factory.Factory
+
+ def getClassObject(self, compMgr, clsid, iid):
+ # Single retval result.
+ try:
+ klass = self.components[clsid]
+ except KeyError:
+ raise ServerException(nsError.NS_ERROR_FACTORY_NOT_REGISTERED)
+
+ # We can ignore the IID - the auto-wrap process will automatically QI us.
+ return self.klassFactory(klass)
+
+ def registerSelf(self, compMgr, location, loaderStr, componentType):
+ # void function.
+ fname = os.path.basename(location.path)
+ for klass in list(self.components.values()):
+ reg_contractid = klass._reg_contractid_
+ print("Registering '%s' (%s)" % (reg_contractid, fname))
+ reg_desc = getattr(klass, "_reg_desc_", reg_contractid)
+ compMgr = compMgr.queryInterface(components.interfaces.nsIComponentRegistrar)
+ compMgr.registerFactoryLocation(klass._reg_clsid_,
+ reg_desc,
+ reg_contractid,
+ location,
+ loaderStr,
+ componentType)
+
+ # See if this class nominates custom register_self
+ extra_func = getattr(klass, "_reg_registrar_", (None,None))[0]
+ if extra_func is not None:
+ extra_func(klass, compMgr, location, loaderStr, componentType)
+
+ def unregisterSelf(self, compMgr, location, loaderStr):
+ # void function.
+ for klass in list(self.components.values()):
+ ok = 1
+ try:
+ compMgr.unregisterComponentSpec(klass._reg_clsid_, location)
+ except Exception:
+ ok = 0
+ # Give the class a bash even if we failed!
+ extra_func = getattr(klass, "_reg_registrar_", (None,None))[1]
+ if extra_func is not None:
+ try:
+ extra_func(klass, compMgr, location, loaderStr)
+ except Exception:
+ ok = 0
+ if ok:
+ print("Successfully unregistered", klass.__name__)
+ else:
+ print("Unregistration of", klass.__name__, "failed. (probably just not already registered)")
+
+ def canUnload(self, compMgr):
+ # single bool result
+ return 0 # we can never unload!
+
diff --git a/src/libs/xpcom18a4/python/server/policy.py b/src/libs/xpcom18a4/python/server/policy.py
new file mode 100755
index 00000000..7f84167c
--- /dev/null
+++ b/src/libs/xpcom18a4/python/server/policy.py
@@ -0,0 +1,398 @@
+# ***** 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 Python XPCOM language bindings.
+#
+# The Initial Developer of the Original Code is
+# ActiveState Tool Corp.
+# Portions created by the Initial Developer are Copyright (C) 2001
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Mark Hammond <mhammond@skippinet.com.au> (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 *****
+
+from xpcom import xpcom_consts, _xpcom, client, nsError, logger
+from xpcom import ServerException, COMException
+import xpcom
+import xpcom.server
+import operator
+import types
+import logging
+import sys
+
+# Python 3 hacks:
+if sys.version_info[0] >= 3:
+ long = int # pylint: disable=W0622,C0103
+
+
+IID_nsISupports = _xpcom.IID_nsISupports
+IID_nsIVariant = _xpcom.IID_nsIVariant
+XPT_MD_IS_GETTER = xpcom_consts.XPT_MD_IS_GETTER
+XPT_MD_IS_SETTER = xpcom_consts.XPT_MD_IS_SETTER
+
+VARIANT_INT_TYPES = xpcom_consts.VTYPE_INT8, xpcom_consts.VTYPE_INT16, xpcom_consts.VTYPE_INT32, \
+ xpcom_consts.VTYPE_UINT8, xpcom_consts.VTYPE_UINT16, xpcom_consts.VTYPE_INT32
+VARIANT_LONG_TYPES = xpcom_consts.VTYPE_INT64, xpcom_consts.VTYPE_UINT64
+VARIANT_FLOAT_TYPES = xpcom_consts.VTYPE_FLOAT, xpcom_consts.VTYPE_DOUBLE
+VARIANT_STRING_TYPES = xpcom_consts.VTYPE_CHAR, xpcom_consts.VTYPE_CHAR_STR, xpcom_consts.VTYPE_STRING_SIZE_IS, \
+ xpcom_consts.VTYPE_CSTRING
+VARIANT_UNICODE_TYPES = xpcom_consts.VTYPE_WCHAR, xpcom_consts.VTYPE_DOMSTRING, xpcom_consts.VTYPE_WSTRING_SIZE_IS, \
+ xpcom_consts.VTYPE_ASTRING
+
+_supports_primitives_map_ = {} # Filled on first use.
+
+_interface_sequence_types_ = tuple, list
+if sys.version_info[0] <= 2:
+ _string_types_ = str, unicode
+else:
+ _string_types_ = bytes, str
+XPTI_GetInterfaceInfoManager = _xpcom.XPTI_GetInterfaceInfoManager
+
+def _GetNominatedInterfaces(obj):
+ ret = getattr(obj, "_com_interfaces_", None)
+ if ret is None: return None
+ # See if the user only gave one.
+ if type(ret) not in _interface_sequence_types_:
+ ret = [ret]
+ real_ret = []
+ # For each interface, walk to the root of the interface tree.
+ iim = XPTI_GetInterfaceInfoManager()
+ for interface in ret:
+ # Allow interface name or IID.
+ interface_info = None
+ if type(interface) in _string_types_:
+ try:
+ interface_info = iim.GetInfoForName(interface)
+ except COMException:
+ pass
+ if interface_info is None:
+ # Allow a real IID
+ interface_info = iim.GetInfoForIID(interface)
+ real_ret.append(interface_info.GetIID())
+ parent = interface_info.GetParent()
+ while parent is not None:
+ parent_iid = parent.GetIID()
+ if parent_iid == IID_nsISupports:
+ break
+ real_ret.append(parent_iid)
+ parent = parent.GetParent()
+ return real_ret
+
+##
+## ClassInfo support
+##
+## We cache class infos by class
+class_info_cache = {}
+
+def GetClassInfoForObject(ob):
+ if xpcom.server.tracer_unwrap is not None:
+ ob = xpcom.server.tracer_unwrap(ob)
+ klass = ob.__class__
+ ci = class_info_cache.get(klass)
+ if ci is None:
+ ci = DefaultClassInfo(klass)
+ ci = xpcom.server.WrapObject(ci, _xpcom.IID_nsIClassInfo, bWrapClient = 0)
+ class_info_cache[klass] = ci
+ return ci
+
+class DefaultClassInfo:
+ _com_interfaces_ = _xpcom.IID_nsIClassInfo
+ def __init__(self, klass):
+ self.klass = klass
+ self.contractID = getattr(klass, "_reg_contractid_", None)
+ self.classDescription = getattr(klass, "_reg_desc_", None)
+ self.classID = getattr(klass, "_reg_clsid_", None)
+ self.implementationLanguage = 3 # Python - avoid lookups just for this
+ self.flags = 0 # what to do here??
+ self.interfaces = None
+
+ def get_classID(self):
+ if self.classID is None:
+ raise ServerException(nsError.NS_ERROR_NOT_IMPLEMENTED, "Class '%r' has no class ID" % (self.klass,))
+ return self.classID
+
+ def getInterfaces(self):
+ if self.interfaces is None:
+ self.interfaces = _GetNominatedInterfaces(self.klass)
+ return self.interfaces
+
+ def getHelperForLanguage(self, language):
+ return None # Not sure what to do here.
+
+class DefaultPolicy:
+ def __init__(self, instance, iid):
+ self._obj_ = instance
+ self._nominated_interfaces_ = ni = _GetNominatedInterfaces(instance)
+ self._iid_ = iid
+ if ni is None:
+ raise ValueError("The object '%r' can not be used as a COM object" % (instance,))
+ # This is really only a check for the user
+ if __debug__:
+ if iid != IID_nsISupports and iid not in ni:
+ # The object may delegate QI.
+ delegate_qi = getattr(instance, "_query_interface_", None)
+ # Perform the actual QI and throw away the result - the _real_
+ # QI performed by the framework will set things right!
+ if delegate_qi is None or not delegate_qi(iid):
+ raise ServerException(nsError.NS_ERROR_NO_INTERFACE)
+ # Stuff for the magic interface conversion.
+ self._interface_info_ = None
+ self._interface_iid_map_ = {} # Cache - Indexed by (method_index, param_index)
+
+ def _QueryInterface_(self, com_object, iid):
+ # Framework allows us to return a single boolean integer,
+ # or a COM object.
+ if iid in self._nominated_interfaces_:
+ # We return the underlying object re-wrapped
+ # in a new gateway - which is desirable, as one gateway should only support
+ # one interface (this wont affect the users of this policy - we can have as many
+ # gateways as we like pointing to the same Python objects - the users never
+ # see what object the call came in from.
+ # NOTE: We could have simply returned the instance and let the framework
+ # do the auto-wrap for us - but this way we prevent a round-trip back into Python
+ # code just for the autowrap.
+ return xpcom.server.WrapObject(self._obj_, iid, bWrapClient = 0)
+
+ # Always support nsIClassInfo
+ if iid == _xpcom.IID_nsIClassInfo:
+ return GetClassInfoForObject(self._obj_)
+
+ # See if the instance has a QI
+ # use lower-case "_query_interface_" as win32com does, and it doesnt really matter.
+ delegate = getattr(self._obj_, "_query_interface_", None)
+ if delegate is not None:
+ # The COM object itself doesnt get passed to the child
+ # (again, as win32com doesnt). It is rarely needed
+ # (in win32com, we dont even pass it to the policy, although we have identified
+ # one place where we should - for marshalling - so I figured I may as well pass it
+ # to the policy layer here, but no all the way down to the object.
+ return delegate(iid)
+ # Finally see if we are being queried for one of the "nsISupports primitives"
+ if not _supports_primitives_map_:
+ iim = _xpcom.XPTI_GetInterfaceInfoManager()
+ for (iid_name, attr, cvt) in _supports_primitives_data_:
+ special_iid = iim.GetInfoForName(iid_name).GetIID()
+ _supports_primitives_map_[special_iid] = (attr, cvt)
+ attr, cvt = _supports_primitives_map_.get(iid, (None,None))
+ if attr is not None and hasattr(self._obj_, attr):
+ return xpcom.server.WrapObject(SupportsPrimitive(iid, self._obj_, attr, cvt), iid, bWrapClient = 0)
+ # Out of clever things to try!
+ return None # We dont support this IID.
+
+ def _MakeInterfaceParam_(self, interface, iid, method_index, mi, param_index):
+ # Wrap a "raw" interface object in a nice object. The result of this
+ # function will be passed to one of the gateway methods.
+ if iid is None:
+ # look up the interface info - this will be true for all xpcom called interfaces.
+ if self._interface_info_ is None:
+ import xpcom.xpt
+ self._interface_info_ = xpcom.xpt.Interface( self._iid_ )
+ iid = self._interface_iid_map_.get( (method_index, param_index))
+ if iid is None:
+ iid = self._interface_info_.GetIIDForParam(method_index, param_index)
+ self._interface_iid_map_[(method_index, param_index)] = iid
+ # handle nsIVariant
+ if iid == IID_nsIVariant:
+ interface = interface.QueryInterface(iid)
+ dt = interface.dataType
+ if dt in VARIANT_INT_TYPES:
+ return interface.getAsInt32()
+ if dt in VARIANT_LONG_TYPES:
+ return interface.getAsInt64()
+ if dt in VARIANT_FLOAT_TYPES:
+ return interface.getAsFloat()
+ if dt in VARIANT_STRING_TYPES:
+ return interface.getAsStringWithSize()
+ if dt in VARIANT_UNICODE_TYPES:
+ return interface.getAsWStringWithSize()
+ if dt == xpcom_consts.VTYPE_BOOL:
+ return interface.getAsBool()
+ if dt == xpcom_consts.VTYPE_INTERFACE:
+ return interface.getAsISupports()
+ if dt == xpcom_consts.VTYPE_INTERFACE_IS:
+ return interface.getAsInterface()
+ if dt == xpcom_consts.VTYPE_EMPTY or dt == xpcom_consts.VTYPE_VOID:
+ return None
+ if dt == xpcom_consts.VTYPE_ARRAY:
+ return interface.getAsArray()
+ if dt == xpcom_consts.VTYPE_EMPTY_ARRAY:
+ return []
+ if dt == xpcom_consts.VTYPE_ID:
+ return interface.getAsID()
+ # all else fails...
+ logger.warning("Warning: nsIVariant type %d not supported - returning a string", dt)
+ try:
+ return interface.getAsString()
+ except COMException:
+ logger.exception("Error: failed to get Variant as a string - returning variant object")
+ return interface
+
+ return client.Component(interface, iid)
+
+ def _CallMethod_(self, com_object, index, info, params):
+ #print "_CallMethod_", index, info, params
+ flags, name, param_descs, ret = info
+ assert ret[1][0] == xpcom_consts.TD_UINT32, "Expected an nsresult (%s)" % (ret,)
+ if XPT_MD_IS_GETTER(flags):
+ # Look for a function of that name
+ func = getattr(self._obj_, "get_" + name, None)
+ if func is None:
+ assert len(param_descs)==1 and len(params)==0, "Can only handle a single [out] arg for a default getter"
+ ret = getattr(self._obj_, name) # Let attribute error go here!
+ else:
+ ret = func(*params)
+ return 0, ret
+ elif XPT_MD_IS_SETTER(flags):
+ # Look for a function of that name
+ func = getattr(self._obj_, "set_" + name, None)
+ if func is None:
+ assert len(param_descs)==1 and len(params)==1, "Can only handle a single [in] arg for a default setter"
+ setattr(self._obj_, name, params[0]) # Let attribute error go here!
+ else:
+ func(*params)
+ return 0
+ else:
+ # A regular method.
+ func = getattr(self._obj_, name)
+ return 0, func(*params)
+
+ def _doHandleException(self, func_name, exc_info):
+ exc_val = exc_info[1]
+ is_server_exception = isinstance(exc_val, ServerException)
+ if is_server_exception:
+ # When a component raised an explicit COM exception, it is
+ # considered 'normal' - however, we still write a debug log
+ # record to help track these otherwise silent exceptions.
+
+ # Note that Python 2.3 does not allow an explicit exc_info tuple
+ # and passing 'True' will not work as there is no exception pending.
+ # Trick things!
+ if logger.isEnabledFor(logging.DEBUG):
+ try:
+ if sys.version_info[0] <= 2:
+ exec('raise exc_info[0], exc_info[1], exc_info[2]')
+ else:
+ raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
+ except:
+ logger.debug("'%s' raised COM Exception %s",
+ func_name, exc_val, exc_info = 1)
+ return exc_val.errno
+ # Unhandled exception - always print a warning and the traceback.
+ # As above, trick the logging module to handle Python 2.3
+ try:
+ if sys.version_info[0] <= 2:
+ exec('raise exc_info[0], exc_info[1], exc_info[2]')
+ else:
+ raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
+ except:
+ logger.exception("Unhandled exception calling '%s'", func_name)
+ return nsError.NS_ERROR_FAILURE
+
+ # Called whenever an unhandled Python exception is detected as a result
+ # of _CallMethod_ - this exception may have been raised during the _CallMethod_
+ # invocation, or after its return, but when unpacking the results
+ # eg, type errors, such as a Python integer being used as a string "out" param.
+ def _CallMethodException_(self, com_object, index, info, params, exc_info):
+ # Later we may want to have some smart "am I debugging" flags?
+ # Or maybe just delegate to the actual object - it's probably got the best
+ # idea what to do with them!
+ flags, name, param_descs, ret = info
+ exc_typ, exc_val, exc_tb = exc_info
+ # use the xpt module to get a better repr for the method.
+ # But if we fail, ignore it!
+ try:
+ import xpcom.xpt
+ m = xpcom.xpt.Method(info, index, None)
+ func_repr = m.Describe().lstrip()
+ except COMException:
+ func_repr = "%s(%r)" % (name, param_descs)
+ except:
+ # any other errors are evil!? Log it
+ self._doHandleException("<building method repr>", sys.exc_info())
+ # And fall through to logging the original error.
+ return self._doHandleException(func_repr, exc_info)
+
+ # Called whenever a gateway fails due to anything other than _CallMethod_.
+ # Really only used for the component loader etc objects, so most
+ # users should never see exceptions triggered here.
+ def _GatewayException_(self, name, exc_info):
+ return self._doHandleException(name, exc_info)
+
+if sys.version_info[0] <= 2:
+ _supports_primitives_data_ = [
+ ("nsISupportsCString", "__str__", str),
+ ("nsISupportsString", "__unicode__", unicode),
+ ("nsISupportsPRUint64", "__long__", long),
+ ("nsISupportsPRInt64", "__long__", long),
+ ("nsISupportsPRUint32", "__int__", int),
+ ("nsISupportsPRInt32", "__int__", int),
+ ("nsISupportsPRUint16", "__int__", int),
+ ("nsISupportsPRInt16", "__int__", int),
+ ("nsISupportsPRUint8", "__int__", int),
+ ("nsISupportsPRBool", "__nonzero__", operator.truth),
+ ("nsISupportsDouble", "__float__", float),
+ ("nsISupportsFloat", "__float__", float),
+ ]
+else:
+ _supports_primitives_data_ = [
+ ("nsISupportsCString", "__str__", str),
+ ("nsISupportsString", "__unicode__", str),
+ ("nsISupportsPRUint64", "__long__", int),
+ ("nsISupportsPRInt64", "__long__", int),
+ ("nsISupportsPRUint32", "__int__", int),
+ ("nsISupportsPRInt32", "__int__", int),
+ ("nsISupportsPRUint16", "__int__", int),
+ ("nsISupportsPRInt16", "__int__", int),
+ ("nsISupportsPRUint8", "__int__", int),
+ ("nsISupportsPRBool", "__nonzero__", operator.truth),
+ ("nsISupportsDouble", "__float__", float),
+ ("nsISupportsFloat", "__float__", float),
+ ]
+
+# Support for the nsISupports primitives:
+class SupportsPrimitive:
+ _com_interfaces_ = ["nsISupports"]
+ def __init__(self, iid, base_ob, attr_name, converter):
+ self.iid = iid
+ self.base_ob = base_ob
+ self.attr_name = attr_name
+ self.converter = converter
+ def _query_interface_(self, iid):
+ if iid == self.iid:
+ return 1
+ return None
+ def get_data(self):
+ method = getattr(self.base_ob, self.attr_name)
+ val = method()
+ return self.converter(val)
+ def set_data(self, val):
+ raise ServerException(nsError.NS_ERROR_NOT_IMPLEMENTED)
+ def toString(self):
+ return str(self.get_data())
+
+def _shutdown():
+ class_info_cache.clear()