diff --git a/extensions/python/dom/Makefile.in b/extensions/python/dom/Makefile.in new file mode 100644 index 000000000000..d67be291e2f3 --- /dev/null +++ b/extensions/python/dom/Makefile.in @@ -0,0 +1,50 @@ +# ***** 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 mozilla.org code +# +# The Initial Developer of the Original Code is mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Hammond: 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 ***** + +DEPTH =../../.. + +DIRS = \ + src \ + test \ + nsdom \ + $(NULL) + +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(topsrcdir)/config/rules.mk + diff --git a/extensions/python/dom/README.txt b/extensions/python/dom/README.txt new file mode 100644 index 000000000000..5c2447ae1b40 --- /dev/null +++ b/extensions/python/dom/README.txt @@ -0,0 +1,19 @@ +This is the Python/DOM bindings for Mozilla. + +These bindings consist of an XPCOM component implementing +nsIScriptRuntime, nsIScriptContext and related interfaces. See +http://wiki.mozilla.org/Breaking_the_grip_JS_has_on_the_DOM for +a general description of what this means. + +The XPCOM component is implemented in C++ and can be found in +the 'src' directory. This component delegates to a Python implementation +inside a Python 'nsdom' package, which can be found in the nsdom directory. + +This directory is built by configuring the Mozilla build process with +the 'python' extenstion enabled - eg, '--enable-extensions=python,default' + +The 'test' directory contains all test related code - notably a 'pyxultest' +chrome application. If you build with --enable-tests, you can run this test +by opening the chrome URL 'chrome://pyxultest/content' (exactly how you do +that depends on what project you are building). See test/pyxultest/README.txt +for more. diff --git a/extensions/python/dom/nsdom/Makefile.in b/extensions/python/dom/nsdom/Makefile.in new file mode 100644 index 000000000000..787c8befc7c1 --- /dev/null +++ b/extensions/python/dom/nsdom/Makefile.in @@ -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 mozilla.org code +# +# The Initial Developer of the Original Code is mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Hammond: Initial 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 ***** + +# Makefile for the nsdom package + +DEPTH =../../../.. + +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +DIRS = \ + $(NULL) + +ifdef ENABLE_TESTS + DIRS += test $(NULL) +endif + +PYSRCS_NSDOM = \ + __init__.py \ + context.py \ + domcompile.py \ + $(NULL) + +PYSRCS_NSDOM := $(addprefix $(srcdir)/,$(PYSRCS_NSDOM)) + +include $(topsrcdir)/config/rules.mk + +libs:: + $(INSTALL) $(PYSRCS_NSDOM) $(DIST)/bin/python/nsdom + +clobber:: + rm -rf $(DIST)/bin/python/nsdom diff --git a/extensions/python/dom/nsdom/__init__.py b/extensions/python/dom/nsdom/__init__.py new file mode 100644 index 000000000000..1a515dc09e54 --- /dev/null +++ b/extensions/python/dom/nsdom/__init__.py @@ -0,0 +1,37 @@ +# ***** 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 mozilla.org code +# +# The Initial Developer of the Original Code is mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Hammond: initial author +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# This is a Python package used by the Python DOM integration. diff --git a/extensions/python/dom/nsdom/context.py b/extensions/python/dom/nsdom/context.py new file mode 100644 index 000000000000..b75b2de470d0 --- /dev/null +++ b/extensions/python/dom/nsdom/context.py @@ -0,0 +1,482 @@ +# This is vaguely related to an nsIScriptContext. The pydom C code +# just calls back into this class - but that C code still retains some of the +# functionality. + +import sys, types, new, logging + +import domcompile + +from xpcom.client import Component +from xpcom import components, primitives, COMException, nsError, _xpcom + +import _nsdom + +GlobalWindowIIDs = [_nsdom.IID_nsIScriptGlobalObject, + components.interfaces.nsIDOMWindow] + +IID_nsIDOMXULElement = components.interfaces.nsIDOMXULElement + +# Borrow the xpcom logger (but use a new sub-logger) +logger = logging.getLogger("xpcom.nsdom") +# Note that many calls to logger.debug are prefixed with if __debug__. +# This is to prevent *any* penalty in non-debug mode for logging in perf +# critical areas. + +# Also note that assert statements have no cost in non-debug builds. + +# XUL Elements don't expose the XBL implemented interfaces via classinfo. +# So we cheat :) +# Most support nsIAccessibleProvider - don't list that! +# Although this sucks for now, if you strike something that isn't listed, +# then simply QI the object you have for the interface you need to use. To +# avoid the explicit QI, you could also have your code do: +# import nsdom.context +# xei = nsdom.context.xul_element_interfaces +# # flag for manual inspection should a future version include it +# if 'somewidget' in xei: raise RuntimeError, "already here!?" +# xei['somewidget'] = [...] + +xul_element_interfaces = { + # tagName: [IID, ...] + 'textbox': [components.interfaces.nsIDOMXULTextBoxElement], + 'button': [components.interfaces.nsIDOMXULButtonElement], + 'checkbox': [components.interfaces.nsIDOMXULCheckboxElement], + 'image': [components.interfaces.nsIDOMXULImageElement], + 'tree': [components.interfaces.nsIDOMXULTreeElement, + components.interfaces.nsIDOMXULMultiSelectControlElement], + 'listbox': [components.interfaces.nsIDOMXULMultiSelectControlElement], + 'menu': [components.interfaces.nsIDOMXULSelectControlItemElement], + 'popup': [components.interfaces.nsIDOMXULPopupElement], + 'radiogroup': [components.interfaces.nsIDOMXULSelectControlElement], +} + +def dump(fmt, *args): + """A global 'dump' function, available in global namespaces. Similar to + The JS version, but this supports % message formatting. + + Main advantage over 'print' is that it catches the possible IO error + when there is no available console. + """ + try: + sys.stderr.write(fmt % args) + sys.stderr.write("\n") + except IOError: + pass + +# The event listener class we attach to an object for addEventListener +class EventListener: + _com_interfaces_ = components.interfaces.nsIDOMEventListener + def __init__(self, context, evt_name, handler, globs = None): + self.context = context + if callable(handler): + self.func = handler + else: + # event name for addEventListener has no 'on' prefix + func_name = "on" + evt_name + # What to do about arg names? It looks like onerror + # still only gets one arg here anyway - handleEvent only takes + # one! + arg_names = ('event',) + co = domcompile.compile_function(handler, + "inline event '%s'" % evt_name, + func_name, + arg_names) + # Could use globs here, but should not be necessary - all we + # are doing is getting the function object from it. + g = {} + exec co in g + self.func = g[func_name] + self.globals = globs + + def handleEvent(self, event): + # Although handler is already a function object, we must re-bind to + # new globals + if self.globals is not None: + f = new.function(self.func.func_code, self.globals, self.func.func_name) + else: + f = self.func + # Convert the raw pyxpcom object to a magic _nsdom one, that + # knows how to remember expandos etc based on context (ie, winds + # up calling back into MakeInterfaceResult(). + event = _nsdom.MakeDOMObject(self.context, event) + + args = (event,) + # We support having less args declared than supplied, a-la JS. + # (This can only happen when passed a function object - we always + # compile a string handler into a function with 1 arg) + args = args[:f.func_code.co_argcount] + return f(*args) + +class WrappedNative(Component): + """Implements the xpconnect concept of 'wrapped natives' and 'expandos'. + + DOM objects can have arbitrary values set on them. Once this is done for + the first time, it gets stored in a map in the context. This leads to + cycles, which must be cleaned up when the context is closed. + """ + def __init__(self, context, obj, iid): + # Store our context - but our context doesn't keep a reference + # to us until someone calls self._remember_object() on the context - + # which sets up all kinds of cycles! + self.__dict__['_context_'] = context + # We store expandos in a seperate dict rather than directly in our + # __dict__. No real need for this other than to prevent these + # attributes clobbering ones we need to work! + self.__dict__['_expandos_'] = {} + Component.__init__(self, obj, iid) + + def __repr__(self): + iface_desc = self._get_classinfo_repr_() + return "" % (self._object_name_,iface_desc) + + def __getattr__(self, attr): + # If it exists in expandos, always return it. + if attr.startswith("__"): + raise AttributeError, attr + # expandos come before interface attributes (which may be wrong. If + # we do this, why not just store it in __dict__?) + expandos = self._expandos_ + if expandos.has_key(attr): + return expandos[attr] + return Component.__getattr__(self, attr) + + def __setattr__(self, attr, value): + try: + Component.__setattr__(self, attr, value) + except AttributeError: + # Set it as an 'expando'. It looks like we should delegate *all* + # to the outside object. + logger.debug("%s set expando property %r=%r for object %r", + self, attr, value, self._comobj_) + # and register if an event. + if attr.startswith("on"): + # I'm quite confused by this :( + target = self._comobj_ + if _nsdom.IsOuterWindow(target): + target = _nsdom.GetCurrentInnerWindow(target) + go = self._context_.globalObject + scope = self._context_.GetNativeGlobal() + if callable(value): + # no idea if this is right - set the compiled object ourselves. + self._expandos_[attr] = value + _nsdom.RegisterScriptEventListener(go, scope, target, attr) + else: + _nsdom.AddScriptEventListener(target, attr, value, False, False) + _nsdom.CompileScriptEventListener(go, scope, target, attr) + else: + self._expandos_[attr] = value + self._context_._remember_object(self) + + def _build_all_supported_interfaces_(self): + # Generally called as pyxpcom is finding an attribute, overridden to + # work around lack of class info for xbl bindings. + Component._build_all_supported_interfaces_(self) + # Now hard-code certain element names we know about, as the XBL + # implemented interfaces are not exposed by this. + ii = self.__dict__['_interface_infos_'] + if ii.has_key(IID_nsIDOMXULElement): + # Is a DOM element - see if in our map. + tagName = self.tagName + interfaces = xul_element_interfaces.get(tagName, []) + for interface in interfaces: + if not ii.has_key(interface): + self._remember_interface_info(interface) + else: + logger.info("Not a DOM element - not hooking extra interfaces") + + def addEventListener(self, event, handler, useCapture=False): + # We need to transform string or function objects into + # nsIDOMEventListener interfaces. + logger.debug("addEventListener for %r, event=%r, handler=%r, cap=%s", + self, event, handler, useCapture) + + if not hasattr(handler, "handleEvent"): # may already be a handler instance. + # Wrap it in our instance, which knows how to convert strings and + # function objects. + # Unlike JS, we always want to execute in the context of our + # "window" (which the user sees very much like a "module") + ns = self._context_.globalNamespace + ns = ns.get("_inner_", ns) # If available, use inner! + handler = EventListener(self._context_, event, handler, ns) + + base = self.__getattr__('addEventListener') + base(event, handler, useCapture) + +class WrappedNativeGlobal(WrappedNative): + # Special support for our global object. Certain methods exposed by + # IDL are JS specific - generally ones that take a variable number of args, + # a concept that doesn't exist in xpcom. + def __repr__(self): + iface_desc = self._get_classinfo_repr_() + outer = _nsdom.IsOuterWindow(self._comobj_) + return "" % (outer, iface_desc) + + # Open window/dialog + def openDialog(self, url, name, features="", *args): + svc = components.classes['@mozilla.org/embedcomp/window-watcher;1'] \ + .getService(components.interfaces.nsIWindowWatcher) + # Wrap in an nsIArray with special support for Python being able to + # access the raw objects if the call-site is smart enough + args = _nsdom.MakeArray(args) + ret = svc.openWindow(self._comobj_, url, name, features, args) + # and re-wrap in one of our "dom" objects + return _nsdom.MakeDOMObject(self._context_, ret) + + # window.open and window.openDialog seem identical except for *args??? + open = openDialog + + # Timeout related functions. + def setTimeout(self, interval, handler, *args): + return _nsdom.SetTimeoutOrInterval(self._comobj_, interval, handler, + args, False) + + # setInterval appears to have reversed args??? + def setInterval(self, handler, interval, *args): + return _nsdom.SetTimeoutOrInterval(self._comobj_, interval, handler, + args, True) + + def clearTimeout(self, tid): + return _nsdom.ClearTimeoutOrInterval(self._comobj_, tid) + + def clearInterval(self, tid): + return _nsdom.ClearTimeoutOrInterval(self._comobj_, tid) + +# Our "script context" - morally an nsIScriptContext, although that lives +# in our C++ code and delegates to this. +class ScriptContext: + def __init__(self): + self.globalNamespace = {} # must not change identity! + self._remembered_objects_ = {} # could, but doesn't + self._reset() + + def _reset(self): + # Explicitly wipe all 'expandos' for our remembered objects. + # Its not clear this is necessary, but it is easy to envisage someone + # setting up a cycle via expandos. + for ro in self._remembered_objects_.itervalues(): + ro._expandos_.clear() + self._remembered_objects_.clear() + # self.globalObject is the "outer window" global, whereas the + # inner window global tends to be passed in to each function as the + # 'scope' object. + self.globalObject = None + self.globalNamespace.clear() + + def __repr__(self): + return "" % id(self) + + # Called by the _nsdom C++ support to wrap the DOM objects. + def MakeInterfaceResult(self, object, iid): + if 0 and __debug__: # this is a little noisy... + logger.debug("MakeInterfaceResult for %r (remembered=%s)", + object, + self._remembered_objects_.has_key(object)) + assert not hasattr(object, "_comobj_"), "should not be wrapped!" + # If it is remembered, just return that object. + try: + return self._remembered_objects_[object] + except KeyError: + # Make a new wrapper - but don't remember the wrapper until + # we need to, when a property is set on it. + + # We should probably QI for nsIClassInfo, and only do this special + # wrapping for objects with the DOM flag set. + + if iid in GlobalWindowIIDs: + klass = WrappedNativeGlobal + else: + klass = WrappedNative + return klass(self, object, iid) + + # Called whenever this object must be "permanently" attached to the + # context. Typically this means an attribute or event handler + # has been set on the object, which should "persist" while the context + # is alive (ie, future requests to getElementById(), for example, must + # return the same underlying object, with event handlers and properties + # still in-place. + def _remember_object(self, object): + # You must only try and remember a wrapped object. + # Once an object has been wrapped once, all further requests must + # be identical + assert self._remembered_objects_.get(object._comobj_, object)==object, \ + "Previously remembered object is not this object!" + self._remembered_objects_[object._comobj_] = object + if __debug__: + logger.debug("%s remembering object %r - now %d items", self, + object, len(self._remembered_objects_)) + + def _fixArg(self, arg): + if arg is None: + return [] + # Already a tuple? This means the original Python args have been + # found and passed directly. + if type(arg) == types.TupleType: + return arg + try: + argv = arg.QueryInterface(components.interfaces.nsIArray) + except COMException, why: + if why.errno != nsError.NS_NOINTERFACE: + raise + # This is not an array - see if it is a variant or primitive. + try: + var = arg.queryInterface(components.interfaces.nsIVariant) + parent = None + if self.globalObject is not None: + parent = self.globalObject._comobj_ + if parent is None: + logger.warning("_fixArg for context with no global??") + return _xpcom.GetVariantValue(var, parent) + except COMException, why: + if why.errno != nsError.NS_NOINTERFACE: + raise + try: + return primitives.GetPrimitive(arg) + except COMException, why: + if why.errno != nsError.NS_NOINTERFACE: + raise + return arg + # Its an array - do each item + ret = [] + for i in range(argv.length): + val = argv.queryElementAt(i, components.interfaces.nsISupports) + ret.append(self._fixArg(val)) + return ret + + def GetNativeGlobal(self): + return self.globalNamespace + + def CreateNativeGlobalForInner(self, globalObject, isChrome): + ret = dict() + if __debug__: + logger.debug("%r CreateNativeGlobalForInner returning %d", + self, id(ret)) + return ret + + def ConnectToInner(self, newInner, globalScope, innerScope): + if __debug__: + logger.debug("%r ConnectToInner(%r, %d, %d)", + self, newInner, id(globalScope), id(innerScope)) + globalScope['_inner_'] = innerScope # will do for now. + + self._prepareGlobalNamespace(newInner, innerScope) + + def WillInitializeContext(self): + if __debug__: + logger.debug("%r WillInitializeContent", self) + + def DidInitializeContext(self): + if __debug__: + logger.debug("%r DidInitializeContent", self) + + def DidSetDocument(self, doc, scope): + if __debug__: + logger.debug("%r DidSetDocument doc=%r scope=%d", + self, doc, id(scope)) + scope['document'] = doc + + def _prepareGlobalNamespace(self, globalObject, globalNamespace): + assert isinstance(globalObject, WrappedNativeGlobal), \ + "Our global should have been wrapped in WrappedNativeGlobal" + if __debug__: + logger.debug("%r initializing (outer=%s), ns=%d", self, + _nsdom.IsOuterWindow(globalObject), + id(globalNamespace)) + assert len(globalObject._expandos_)==0, \ + "already initialized this global?" + ns = globalObject.__dict__['_expandos_'] = globalNamespace + self._remember_object(globalObject) + ns['window'] = globalObject + # we can't fetch 'document' and stash it now - there may not be one + # at this instant (ie, it may be in the process of being attached) + # Poke some other utilities etc into the namespace. + ns['dump'] = dump + + def InitContext(self, globalObject): + self._reset() + self.globalObject = globalObject + if globalObject is None: + logger.debug("%r initializing with NULL global, ns=%d", self, + id(self.globalNamespace)) + else: + self._prepareGlobalNamespace(globalObject, self.globalNamespace) + + def FinalizeClasses(self, globalObject): + self._reset() + + def ClearScope(self, globalObject): + if __debug__: + logger.debug("%s.ClearScope (%d)", self, id(globalObject)) + globalObject.clear() + + def FinalizeContext(self): + if __debug__: + logger.debug("%s.FinalizeContext", self) + self._reset() + + def EvaluateString(self, script, glob, principal, url, lineno, ver): + # This appears misnamed - return value it not used. Therefore we can + # just do a simple 'exec'. If we needed a return value, we would have + # to treat this like a string event-handler, and compile as a temp + # function. + assert type(glob) == types.DictType + # compile then exec to make use of lineno + co = domcompile.compile(script, url, lineno=lineno-1) + exec co in glob + + def ExecuteScript(self, scriptObject, scopeObject): + if __debug__: + logger.debug("%s.ExecuteScript %r in scope %s", + self, scriptObject, id(scopeObject)) + if scopeObject is None: + scopeObject = self.GetNativeGlobal() + assert type(scriptObject) == types.CodeType, \ + "Script object should be a code object (got %r)" % (scriptObject,) + exec scriptObject in scopeObject + + def CompileScript(self, text, scopeObject, principal, url, lineno, version): + # The line number passed is the first; offset is -1 + return domcompile.compile(text, url, lineno=lineno-1) + + def CompileEventHandler(self, name, argNames, body, url, lineno): + co = domcompile.compile_function(body, url, name, argNames, + lineno=lineno-1) + g = {} + exec co in g + return g[name] + + def CallEventHandler(self, target, scope, handler, argv): + if __debug__: + logger.debug("CallEventHandler %r on target %s in scope %s", + handler, target, id(scope)) + # Event handlers must fire in our real globals (not a copy) so + # it can set global vars! + globs = scope + # Although handler is already a function object, we must re-bind to + # new globals + f = new.function(handler.func_code, globs, handler.func_name, + handler.func_defaults) + args = tuple(self._fixArg(argv)) + # We support having less args declared than supplied, a-la JS. + args = args[:handler.func_code.co_argcount] + return f(*args) + + def BindCompiledEventHandler(self, target, scope, name, handler): + if __debug__: + logger.debug("%s.BindCompiledEventHandler (%s=%r) on target %s in scope %s", + self, name, handler, target, id(scope)) + # Keeps a ref to both the target and handler. + self._remember_object(target) + ns = target._expandos_ + ns[name] = handler + + def GetBoundEventHandler(self, target, scope, name): + if __debug__: + logger.debug("%s.GetBoundEventHandler for '%s' on target %s in scope %s", + self, name, target, id(scope)) + ns = target._expandos_ + return ns[name] + + def SetProperty(self, target, name, value): + if __debug__: + logger.debug("%s.SetProperty for %s=%r", self, name, value) + target[name] = self._fixArg(value) diff --git a/extensions/python/dom/nsdom/domcompile.py b/extensions/python/dom/nsdom/domcompile.py new file mode 100644 index 000000000000..308759d8817f --- /dev/null +++ b/extensions/python/dom/nsdom/domcompile.py @@ -0,0 +1,122 @@ +# ***** 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 mozilla.org code +# +# The Initial Developer of the Original Code is mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Hammond: initial 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 ***** + +# A utility for compiling Python code, using features not available via +# the builtin compile. +# +# (a) It is not possible to compile the body of a Python function, without the +# function declaration. ie, 'return None' will always give a syntax error when +# passed to compile. +# (b) It is very tricky to compile code with the line-number starting at +# anything other than zero. +# +# Both of these are solved by this module, which uses the 'compiler' module +# XXX - sad side-effect is that Unicode is not correctly supported - +# PyCF_SOURCE_IS_UTF8 is not exposed via compiler (in 2.3 at least) +# On the upside here, all 'src' params are unicode today, so expansion here +# requires no interface changes. + +from compiler import parse, syntax, compile +from compiler.pycodegen import ModuleCodeGenerator +import compiler.ast +import new + +def _fix_src(src): + # windows first - \r\n -> \n, then for mac, remaining \r -> \n + # Trailing whitespace can cause problems - make sure a final '\n' exists. + return src.replace("\r\n", "\n").replace("\r", "\n") + "\n" + +# from compiler.misc.set_filename - but we also adjust lineno attributes. +def set_filename_and_offset(filename, offset, tree): + """Set the filename attribute to filename on every node in tree""" + worklist = [tree] + while worklist: + node = worklist.pop(0) + node.filename = filename + if node.lineno is not None: + node.lineno += offset + worklist.extend(node.getChildNodes()) + +def parse_function(src, func_name, arg_names, defaults=[]): + tree = parse(src, "exec") + defaults = [compiler.ast.Const(d) for d in defaults] + # Insert a Stmt with function object. + try: + decs = compiler.ast.Decorators([]) + except AttributeError: + # 2.3 has no such concept (and different args!) + func = compiler.ast.Function(func_name, arg_names, defaults, 0, None, + tree.node) + else: + # 2.4 and later + func = compiler.ast.Function(decs, func_name, arg_names, defaults, 0, None, + tree.node) + stmt = compiler.ast.Stmt((func,)) + tree.node = stmt + syntax.check(tree) + return tree + +def compile_function(src, filename, func_name, arg_names, defaults=[], + # more args to come... + lineno=0): + assert filename, "filename is required" + try: + tree = parse_function(_fix_src(src), func_name, arg_names, defaults) + except SyntaxError, err: + err.lineno += lineno + err.filename = filename + raise SyntaxError, err + + set_filename_and_offset(filename, lineno, tree) + + gen = ModuleCodeGenerator(tree) + return gen.getCode() + +# And a 'standard' compile, but with the filename offset feature. +def compile(src, filename, mode='exec', flags=None, dont_inherit=None, lineno=0): + if flags is not None or dont_inherit is not None or mode != 'exec': + raise RuntimeError, "not implemented yet" + try: + tree = parse(_fix_src(src), mode) + except SyntaxError, err: + err.lineno += lineno + err.filename = filename + raise SyntaxError, err + + set_filename_and_offset(filename, lineno, tree) + + gen = ModuleCodeGenerator(tree) + return gen.getCode() diff --git a/extensions/python/dom/nsdom/test/Makefile.in b/extensions/python/dom/nsdom/test/Makefile.in new file mode 100644 index 000000000000..0546d5444527 --- /dev/null +++ b/extensions/python/dom/nsdom/test/Makefile.in @@ -0,0 +1,49 @@ +# ***** 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 mozilla.org code +# +# The Initial Developer of the Original Code is mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Hammond: Initial 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 ***** + +DEPTH =../../../../.. + +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +include $(topsrcdir)/config/rules.mk + +check:: + @echo "Running Python DOM tests" + @$(MOZ_PYTHON)$(MOZ_PYTHON_DEBUG_SUFFIX) regrtest.py diff --git a/extensions/python/dom/nsdom/test/regrtest.py b/extensions/python/dom/nsdom/test/regrtest.py new file mode 100644 index 000000000000..ca9fcdfab5db --- /dev/null +++ b/extensions/python/dom/nsdom/test/regrtest.py @@ -0,0 +1,56 @@ +# ***** 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 mozilla.org code +# +# The Initial Developer of the Original Code is mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Hammond: Initial 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 ***** + +# The Regression Tests for the nsdom package. + +# XXX - what I want is something a little closer to the Zope test runner. +# "find all tests and run them, for god's sake!" +import os +import sys + +import unittest + +path = os.path.abspath(os.path.split(sys.argv[0])[0]) +for fname in os.listdir(path): + if os.path.splitext(fname)[1] != '.py': + continue + if fname == "regrtest.py": + continue + m = __import__(os.path.splitext(fname)[0]) + try: + unittest.main(m) + except SystemExit: + pass diff --git a/extensions/python/dom/nsdom/test/test_compile.py b/extensions/python/dom/nsdom/test/test_compile.py new file mode 100644 index 000000000000..f690a629ea81 --- /dev/null +++ b/extensions/python/dom/nsdom/test/test_compile.py @@ -0,0 +1,138 @@ +# ***** 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 mozilla.org code +# +# The Initial Developer of the Original Code is mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Hammond: Initial 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 ***** + +# The Regression Tests for the nsdom package. +import sys +import unittest +import new +from nsdom.domcompile import compile, compile_function + +class TestCompile(unittest.TestCase): + def testSyntaxError(self): + globs = {} + try: + co = compile(u"\ndef class()", "foo.py", lineno=100) + raise AssertionError, "not reached!" + except SyntaxError, why: + self.failUnlessEqual(why.lineno, 102) + self.failUnlessEqual(why.filename, "foo.py") + + def testFilenameAndOffset(self): + src = u"a=1\nraise RuntimeError" + globs = {} + co = compile_function(src, "foo.py", "func_name", ["x", "y", "z"], + lineno=100) + exec co in globs + f = globs['func_name'] + # re-create the function - so we can bind to different globals + # Note we need to manually setup __builtins__ for this new globs. + globs = {'g':'call_globs', '__debug__': __debug__, + "__builtins__": globs["__builtins__"]} + f = new.function(f.func_code, globs, f.func_name) + try: + f(1,2,3) + raise AssertionError, "not reached!" + except RuntimeError: + # The function line number is 1 frames back. It should point to + # line 102 + tb = sys.exc_info()[2].tb_next + self.failUnlessEqual(tb.tb_frame.f_lineno, 102) + self.failUnlessEqual(tb.tb_frame.f_code.co_filename, "foo.py") + + def testFunction(self): + # Compile a test function, then execute it. + src = u"assert g=='call_globs'\nreturn 'wow'" + + globs = {'g':'compile_globs'} + co = compile_function(src, "foo.py", "func_name", ["x", "y", "z"], + lineno=100) + exec co in globs + f = globs['func_name'] + # re-create the function - so we can bind to different globals + globs = {'g':'call_globs', '__debug__': __debug__} + f = new.function(f.func_code, globs, f.func_name) + self.failUnlessEqual(f(1,2,3), 'wow') + + def testFunctionArgs(self): + # Compile a test function, then execute it. + src = u"assert a==1\nassert b=='b'\nassert c is None\nreturn 'yes'" + + defs = (1, "b", None) + co = compile_function(src, "foo.py", "func_name", + ["a", "b", "c"], + defs, + lineno=100) + globs = {} + exec co in globs + f = globs['func_name'] + # re-create the function - so we can bind to different globals + globs = {'__debug__': __debug__} + f = new.function(f.func_code, globs, f.func_name, defs) + self.failUnlessEqual(f(), 'yes') + + def testSimple(self): + globs = {} + # And the simple compile. + co = compile(u"a=1", "foo.py") + exec co in globs + self.failUnlessEqual(globs['a'], 1) + + def testTrailingWhitespace(self): + globs = {} + # And the simple compile. + co = compile(u"a=1\n ", "foo.py") + exec co in globs + self.failUnlessEqual(globs['a'], 1) + + def _testNewlines(self, sep): + src = u"a=1" + sep + "b=2" + globs = {} + co = compile(src, "foo.py") + exec co in globs + self.failUnlessEqual(globs['a'], 1) + self.failUnlessEqual(globs['b'], 2) + + def testMacNewlines(self): + self._testNewlines("\r") + + def testWindowsNewlines(self): + self._testNewlines("\r\n") + + def testUnixNewlines(self): + self._testNewlines("\n") + +if __name__=='__main__': + unittest.main() diff --git a/extensions/python/dom/src/Makefile.in b/extensions/python/dom/src/Makefile.in new file mode 100644 index 000000000000..6f47a71625ff --- /dev/null +++ b/extensions/python/dom/src/Makefile.in @@ -0,0 +1,47 @@ +# todo - add license. + +# The Python DOM ScriptLanguage implementation. + +DEPTH=../../../.. + +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +LIBRARY_NAME = pydom +IS_COMPONENT = 1 +REQUIRES = pyxpcom xpcom string xpcom_obsolete dom \ + widget js gfx gklayout content layout necko xpconnect $(NULL) + +MOZILLA_INTERNAL_API = 1 +FORCE_SHARED_LIB = 1 +FORCE_USE_PIC = 1 + +LOCAL_INCLUDES = $(MOZ_PYTHON_INCLUDES) +EXTRA_LIBS += $(MOZ_PYTHON_LIBS) $(MOZ_JS_LIBS) + +ifeq ($(OS_ARCH), WINNT) +EXTRA_LIBS += $(DIST)/lib/pyxpcom.lib +else +EXTRA_LIBS += -lpyxpcom +endif + +CPPSRCS = \ + nsPyArgArray.cpp \ + nsPyContext.cpp \ + nsPyRuntime.cpp \ + nsPyDOMModule.cpp \ + nsPyDOMISupports.cpp \ + nsPyTimeout.cpp \ + $(NULL) + +include $(topsrcdir)/config/config.mk + +include $(topsrcdir)/config/rules.mk +CXXFLAGS += -DPYTHON_SO=\"libpython$(MOZ_PYTHON_VER_DOTTED).so\" +EXTRA_DSO_LDOPTS += $(MOZ_COMPONENT_LIBS) + +clobber:: + rm -f *.ilk diff --git a/extensions/python/dom/src/nsPyArgArray.cpp b/extensions/python/dom/src/nsPyArgArray.cpp new file mode 100644 index 000000000000..4815949842dd --- /dev/null +++ b/extensions/python/dom/src/nsPyArgArray.cpp @@ -0,0 +1,131 @@ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** + */ + +#include "nsPyDOM.h" +#include "nsIArray.h" + +class nsPyArgArray : public nsIPyArgArray, public nsIArray { +public: + nsPyArgArray(PyObject *ob); + ~nsPyArgArray(); + // nsISupports + NS_DECL_ISUPPORTS + + // nsIArray + NS_DECL_NSIARRAY + + // nsIPyArgArray + PyObject *GetArgs() {return mObject;} +protected: + PyObject *mObject; +}; + +nsPyArgArray::nsPyArgArray(PyObject *ob) : + mObject(ob) +{ + NS_ASSERTION(PySequence_Check(ob), "You won't get far without a sequence!"); + Py_INCREF(ob); +} + +nsPyArgArray::~nsPyArgArray() +{ + Py_DECREF(mObject); +} + +// QueryInterface implementation for nsPyArgArray +NS_INTERFACE_MAP_BEGIN(nsPyArgArray) + NS_INTERFACE_MAP_ENTRY(nsIArray) + NS_INTERFACE_MAP_ENTRY(nsIPyArgArray) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPyArgArray) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(nsPyArgArray) +NS_IMPL_RELEASE(nsPyArgArray) + +// nsIArray impl +NS_IMETHODIMP nsPyArgArray::GetLength(PRUint32 *aLength) +{ + CEnterLeavePython _celp; + int size = PySequence_Length(mObject); + if (size==-1) { + return PyXPCOM_SetCOMErrorFromPyException(); + } + *aLength = (PRUint32) size; + return NS_OK; +} + +/* void queryElementAt (in unsigned long index, in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */ +NS_IMETHODIMP nsPyArgArray::QueryElementAt(PRUint32 index, const nsIID & uuid, void * *result) +{ + *result = nsnull; + // for now let Python check index validity. Probably won't get the correct + // hresult, but its not even clear what that is :) + + if (uuid.Equals(NS_GET_IID(nsIVariant)) || uuid.Equals(NS_GET_IID(nsISupports))) { + CEnterLeavePython _celp; + PyObject *sub = PySequence_GetItem(mObject, index); + if (sub==NULL) { + return PyXPCOM_SetCOMErrorFromPyException(); + } + nsresult rv = PyObject_AsVariant(sub, (nsIVariant **)result); + Py_DECREF(sub); + return rv; + } + NS_WARNING("nsPyArgArray only handles nsIVariant"); + return NS_ERROR_NO_INTERFACE; +} + +/* unsigned long indexOf (in unsigned long startIndex, in nsISupports element); */ +NS_IMETHODIMP nsPyArgArray::IndexOf(PRUint32 startIndex, nsISupports *element, PRUint32 *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* nsISimpleEnumerator enumerate (); */ +NS_IMETHODIMP nsPyArgArray::Enumerate(nsISimpleEnumerator **_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +// The factory function +nsresult NS_CreatePyArgv(PyObject *ob, nsIArray **aArray) +{ + nsPyArgArray *ret = new nsPyArgArray(ob); + if (ret == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + return ret->QueryInterface(NS_GET_IID(nsIArray), (void **)aArray); +} diff --git a/extensions/python/dom/src/nsPyContext.cpp b/extensions/python/dom/src/nsPyContext.cpp new file mode 100644 index 000000000000..a8cf27302daf --- /dev/null +++ b/extensions/python/dom/src/nsPyContext.cpp @@ -0,0 +1,942 @@ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "nsIScriptContext.h" +#include "nsIScriptGlobalObject.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsITimelineService.h" +#include "nsITimer.h" +#include "nsIArray.h" +#include "nsIAtom.h" +#include "prtime.h" +#include "nsString.h" +#include "nsGUIEvent.h" +#include "nsDOMScriptObjectHolder.h" +#include "nsIDOMDocument.h" + +#include "nsPyDOM.h" +#include "nsPyContext.h" +#include "compile.h" +#include "eval.h" +#include "marshal.h" + +#ifdef NS_DEBUG +nsPyDOMObjectLeakStats gLeakStats; +#endif + +// Straight from nsJSEnvironment. +static inline const char * +AtomToEventHandlerName(nsIAtom *aName) +{ + const char *name; + + aName->GetUTF8String(&name); + +#ifdef DEBUG + const char *cp; + char c; + for (cp = name; *cp != '\0'; ++cp) + { + c = *cp; + NS_ASSERTION (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'), + "non-ASCII non-alphabetic event handler name"); + } +#endif + + return name; +} + +// A simple "object holder" - an XPCOM object that keeps a reference to +// a Python object. When the XPCOM object dies, the Python reference is +// dropped. +class nsPyObjectHolder : public nsISupports +{ +public: + NS_DECL_ISUPPORTS + nsPyObjectHolder(PyObject *ob) : m_ob(ob) {Py_XINCREF(m_ob);} + ~nsPyObjectHolder() { + CEnterLeavePython _celp; + Py_XDECREF(m_ob); + } +private: + PyObject *m_ob; +}; + +// QueryInterface implementation for nsPyObjectHolder +NS_INTERFACE_MAP_BEGIN(nsPyObjectHolder) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(nsPyObjectHolder) +NS_IMPL_RELEASE(nsPyObjectHolder) + + +nsPythonContext::nsPythonContext() : + mIsInitialized(PR_FALSE), + mOwner(nsnull), + mScriptGlobal(nsnull), + mScriptsEnabled(PR_TRUE), + mProcessingScriptTag(PR_FALSE), + mDelegate(NULL) +{ + PYLEAK_STAT_INCREMENT(ScriptContext); +} + +nsPythonContext::~nsPythonContext() +{ + PYLEAK_STAT_DECREMENT(ScriptContext); +} + +// QueryInterface implementation for nsPythonContext +NS_INTERFACE_MAP_BEGIN(nsPythonContext) + NS_INTERFACE_MAP_ENTRY(nsIScriptContext) + NS_INTERFACE_MAP_ENTRY(nsITimerCallback) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptContext) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(nsPythonContext) +NS_IMPL_RELEASE(nsPythonContext) + + +nsresult nsPythonContext::HandlePythonError() +{ + // It looks like we first need to call the DOM. Depending on the result + // of the DOM error handler, we may then just treat it as unhandled. + if (!PyErr_Occurred()) + return NS_OK; + + nsScriptErrorEvent errorevent(PR_TRUE, NS_SCRIPT_ERROR); + nsAutoString strFilename; + + PyObject *exc, *typ, *tb; + PyErr_Fetch(&exc, &typ, &tb); + PyErr_NormalizeException( &exc, &typ, &tb); + // We must have a traceback object to report the filename/lineno. + // *sob* - PyTracebackObject not in a header file in 2.3. Use getattr etc + if (tb) { + PyObject *frame = PyObject_GetAttrString(tb, "tb_frame"); + if (frame) { + PyObject *obLineNo = PyObject_GetAttrString(frame, "f_lineno"); + if (obLineNo) { + errorevent.lineNr = PyInt_AsLong(obLineNo); + Py_DECREF(obLineNo); + } else { + NS_ERROR("Traceback had no lineNo attribute?"); + PyErr_Clear(); + } + PyObject *code = PyObject_GetAttrString(frame, "f_code"); + if (code) { + PyObject *filename = PyObject_GetAttrString(code, "co_filename"); + if (filename && PyString_Check(filename)) { + CopyUTF8toUTF16(PyString_AsString(filename), strFilename); + errorevent.fileName = strFilename.get(); + } + Py_XDECREF(filename); + Py_DECREF(code); + } + Py_DECREF(frame); + } + } + PRBool outOfMem = PyErr_GivenExceptionMatches(exc, PyExc_MemoryError); + + nsCAutoString cerrMsg; + PyXPCOM_FormatGivenException(cerrMsg, exc, typ, tb); + nsAutoString errMsg; + CopyUTF8toUTF16(cerrMsg, errMsg); + NS_ASSERTION(!errMsg.IsEmpty(), "Failed to extract a Python error message"); + errorevent.errorMsg = errMsg.get(); + nsEventStatus status = nsEventStatus_eIgnore; + + // Handle the script error before we restore the exception. + if (mScriptGlobal != nsnull && !outOfMem) { + mScriptGlobal->HandleScriptError(&errorevent, &status); + } + + PyErr_Restore(exc, typ, tb); + + if (status != nsEventStatus_eConsumeNoDefault) { + // report it 'normally'. We probably should use nsIScriptError + // (OTOH, pyxpcom itself is probably what should) + nsCAutoString streamout("Python DOM script error"); + nsCAutoString level; + if (status != nsEventStatus_eIgnore) { + streamout += NS_LITERAL_CSTRING(" (suppressed by event handler)"); + level = NS_LITERAL_CSTRING("info"); + } else { + level = NS_LITERAL_CSTRING("warning"); + } + PyXPCOM_FormatCurrentException(streamout); + PyXPCOM_Log(level.get(), streamout); + } + // We always want the hresult and exception state cleared. + nsresult ret = PyXPCOM_SetCOMErrorFromPyException(); + PyErr_Clear(); + return ret; +} + +// WillInitializeContext() is called before InitContext(). Both +// may be called multiple times as a context is re-initialized for a new +// document. +void +nsPythonContext::WillInitializeContext() +{ + mIsInitialized = PR_FALSE; + // Don't create a new delegate if we are being re-initialized - it + // may have useful state (eg, the previous script global) + CEnterLeavePython _celp; + if (!mDelegate) { + // Load our delegate. + PyObject *mod = PyImport_ImportModule("nsdom.context"); + if (mod==NULL) { + HandlePythonError(); + return; + } + PyObject *klass = PyObject_GetAttrString(mod, "ScriptContext"); + Py_DECREF(mod); + if (klass == NULL) { + HandlePythonError(); + return; + } + mDelegate = PyObject_Call(klass, NULL, NULL); + Py_DECREF(klass); + } + PyObject *ret = PyObject_CallMethod(mDelegate, "WillInitializeContext", NULL); + if (ret == NULL) + HandlePythonError(); +} + +void +nsPythonContext::DidInitializeContext() +{ + NS_ASSERTION(mDelegate, "No delegate!"); + if (mDelegate) { + CEnterLeavePython _celp; + PyObject *ret = PyObject_CallMethod(mDelegate, "DidInitializeContext", NULL); + Py_XDECREF(ret); + HandlePythonError(); + } + mIsInitialized = PR_TRUE; +} + +PRBool +nsPythonContext::IsContextInitialized() +{ + return mIsInitialized; +} + +nsresult +nsPythonContext::InitContext(nsIScriptGlobalObject *aGlobalObject) +{ + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::InitContext"); + NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED); + NS_ASSERTION(mDelegate != NULL, "WillInitContext didn't create delegate"); + + if (mDelegate == NULL) + return NS_ERROR_UNEXPECTED; + + CEnterLeavePython _celp; + PyObject *obGlobal; + if (aGlobalObject) { + obGlobal = PyObject_FromNSDOMInterface(mDelegate, aGlobalObject, + NS_GET_IID(nsIScriptGlobalObject)); + if (!obGlobal) + return HandlePythonError(); + } else { + obGlobal = Py_None; + Py_INCREF(Py_None); + } + + PyObject *ret = PyObject_CallMethod(mDelegate, "InitContext", "N", obGlobal); + if (ret == NULL) + return HandlePythonError(); + // stash the global away so we can fetch it locally. + mScriptGlobal = aGlobalObject; + return NS_OK; +} + +void +nsPythonContext::DidSetDocument(nsIDOMDocument *aDoc, void *aGlobal) +{ + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::DidSetDocument"); + NS_ASSERTION(mDelegate != NULL, "No delegate"); + + if (mDelegate == NULL) + return; + + CEnterLeavePython _celp; + PyObject *obDoc; + if (aDoc) { + obDoc = PyObject_FromNSDOMInterface(mDelegate, aDoc, + NS_GET_IID(nsIDOMDocument)); + if (!obDoc) { + HandlePythonError(); + return; + } + } else { + obDoc = Py_None; + Py_INCREF(Py_None); + } + PyObject *ret = PyObject_CallMethod(mDelegate, "DidSetDocument", + "NO", obDoc, aGlobal); + Py_XDECREF(ret); + HandlePythonError(); +} + +nsresult +nsPythonContext::EvaluateStringWithValue(const nsAString& aScript, + void *aScopeObject, + nsIPrincipal *aPrincipal, + const char *aURL, + PRUint32 aLineNo, + PRUint32 aVersion, + void* aRetValue, + PRBool* aIsUndefined) +{ + NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); + + if (!mScriptsEnabled) { + if (aIsUndefined) { + *aIsUndefined = PR_TRUE; + } + + return NS_OK; + } + NS_ERROR("Not implemented"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +nsPythonContext::EvaluateString(const nsAString& aScript, + void *aScopeObject, + nsIPrincipal *aPrincipal, + const char *aURL, + PRUint32 aLineNo, + PRUint32 aVersion, + nsAString *aRetValue, + PRBool* aIsUndefined) +{ + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::EvaluateString"); + NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); + + *aIsUndefined = PR_TRUE; + if (!mScriptsEnabled) { + *aIsUndefined = PR_TRUE; + if (aRetValue) { + aRetValue->Truncate(); + } + return NS_OK; + } + + CEnterLeavePython _celp; + + // ignore principal param - no one uses it yet. + PyObject *obScript = PyObject_FromNSString(aScript); + + PyObject *ret = PyObject_CallMethod(mDelegate, "EvaluateString", "NOOsii", + obScript, + (PyObject *)aScopeObject, + Py_None, // principal place holder + aURL, + aLineNo, + aVersion); + + // We happen to know that no impls use the return value. + Py_XDECREF(ret); + return HandlePythonError(); +} + +nsresult +nsPythonContext::ExecuteScript(void* aScriptObject, + void *aScopeObject, + nsAString* aRetValue, + PRBool* aIsUndefined) +{ + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::ExecuteScript"); + NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); + + if (aIsUndefined) { + *aIsUndefined = PR_TRUE; + } + if (!mScriptsEnabled) { + if (aRetValue) { + aRetValue->Truncate(); + } + return NS_OK; + } + NS_ENSURE_TRUE(aScriptObject, NS_ERROR_NULL_POINTER); + NS_ASSERTION(mDelegate, "Script context has no delegate"); + NS_ENSURE_TRUE(mDelegate, NS_ERROR_UNEXPECTED); + CEnterLeavePython _celp; + + PyObject *ret = PyObject_CallMethod(mDelegate, "ExecuteScript", "OO", + aScriptObject, aScopeObject); + // We always want to return OK here - any errors are "just" script errors. + if (!ret) { + HandlePythonError(); + if (aRetValue) + aRetValue->Truncate(); + } else if (ret == Py_None) { + if (aRetValue) + aRetValue->Truncate(); + } else { + if (aRetValue) { + PyObject_AsNSString(ret, *aRetValue); + } + if (aIsUndefined) { + *aIsUndefined = PR_FALSE; + } + } + Py_XDECREF(ret); + return NS_OK; +} + +nsresult +nsPythonContext::CompileScript(const PRUnichar* aText, + PRInt32 aTextLength, + void *aScopeObject, + nsIPrincipal *aPrincipal, + const char *aURL, + PRUint32 aLineNo, + PRUint32 aVersion, + nsScriptObjectHolder &aScriptObject) +{ + NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::CompileScript"); + + NS_ASSERTION(mDelegate, "Script context has no delegate"); + NS_ENSURE_TRUE(mDelegate, NS_ERROR_UNEXPECTED); + + CEnterLeavePython _celp; + + PyObject *obCode = PyObject_FromNSString(aText, aTextLength); + if (!obCode) + return HandlePythonError(); + PyObject *obPrincipal = Py_None; // fix this when we can use it! + + PyObject *ret = PyObject_CallMethod(mDelegate, "CompileScript", + "NOOsii", + obCode, + aScopeObject ? aScopeObject : Py_None, + obPrincipal, + aURL, + aLineNo, + aVersion); + if (!ret) { + return HandlePythonError(); + } + NS_ASSERTION(aScriptObject.getScriptTypeID()==nsIProgrammingLanguage::PYTHON, + "Expecting Python script object holder"); + aScriptObject.set(ret); + Py_DECREF(ret); + return NS_OK; +} + +nsresult +nsPythonContext::CompileEventHandler(nsIAtom *aName, + PRUint32 aArgCount, + const char** aArgNames, + const nsAString& aBody, + const char *aURL, PRUint32 aLineNo, + nsScriptObjectHolder &aHandler) +{ + NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::CompileEventHandler"); + + NS_ASSERTION(mDelegate, "Script context has no delegate"); + NS_ENSURE_TRUE(mDelegate, NS_ERROR_UNEXPECTED); + + CEnterLeavePython _celp; + + aHandler.drop(); + + PyObject *argNames = PyList_New(aArgCount); + if (!argNames) + return HandlePythonError(); + for (PRUint32 i=0;i pyargv(do_QueryInterface(aargv)); + if (pyargv) { + obArgv = pyargv->GetArgs(); + Py_XINCREF(obArgv); // match the non-py case + } + if (!obArgv) { + obArgv = PyObject_FromNSDOMInterface(mDelegate, aargv); + if (!obArgv) { + Py_DECREF(obTarget); + return HandlePythonError(); + } + } + + PyObject *ret = PyObject_CallMethod(mDelegate, "CallEventHandler", + "NOON", + obTarget, aScope, aHandler, + obArgv); + if (ret) { + PyObject_AsVariant(ret, arv); + Py_DECREF(ret); + } + return HandlePythonError(); +} + +nsresult +nsPythonContext::GetBoundEventHandler(nsISupports* aTarget, void *aScope, + nsIAtom* aName, + nsScriptObjectHolder &aHandler) +{ + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::GetBoundEventHandler"); + NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); + + NS_ASSERTION(mDelegate, "Script context has no delegate"); + NS_ENSURE_TRUE(mDelegate, NS_ERROR_UNEXPECTED); + + CEnterLeavePython _celp; + + aHandler.drop(); + + PyObject *obTarget; + obTarget = PyObject_FromNSDOMInterface(mDelegate, aTarget); + if (!obTarget) + return HandlePythonError(); + + PyObject *ret = PyObject_CallMethod(mDelegate, "GetBoundEventHandler", + "NOs", + obTarget, aScope, + AtomToEventHandlerName(aName)); + if (!ret) + return HandlePythonError(); + if (ret == Py_None) + return NS_OK; + NS_ASSERTION(aHandler.getScriptTypeID()==nsIProgrammingLanguage::PYTHON, + "Expecting Python script object holder"); + aHandler.set(ret); + Py_DECREF(ret); + return NS_OK; +} + +nsresult +nsPythonContext::SetProperty(void *aTarget, const char *aPropName, nsISupports *aVal) +{ + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::SetProperty"); + NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED); + + NS_ASSERTION(mDelegate, "Script context has no delegate"); + NS_ENSURE_TRUE(mDelegate, NS_ERROR_UNEXPECTED); + + CEnterLeavePython _celp; + PyObject *obVal; + obVal = PyObject_FromNSDOMInterface(mDelegate, aVal); + if (!obVal) + return HandlePythonError(); + + PyObject *ret = PyObject_CallMethod(mDelegate, "SetProperty", + "OsN", + aTarget, aPropName, obVal); + Py_XDECREF(ret); + return HandlePythonError(); +} + +nsresult +nsPythonContext::InitClasses(void *aGlobalObj) +{ + return NS_OK; +} + +nsresult +nsPythonContext::Serialize(nsIObjectOutputStream* aStream, void *aScriptObject) +{ + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::Serialize"); + CEnterLeavePython _celp; + nsresult rv; + PyObject *pyScriptObject = (PyObject *)aScriptObject; + if (!PyCode_Check(pyScriptObject) && !PyFunction_Check(pyScriptObject)) { + NS_ERROR("aScriptObject is not a code or function object"); + return NS_ERROR_UNEXPECTED; + } + rv = aStream->Write32(PyImport_GetMagicNumber()); + if (NS_FAILED(rv)) return rv; + + PyObject *obMarshal = +#ifdef Py_MARSHAL_VERSION + PyMarshal_WriteObjectToString((PyObject *)aScriptObject, + Py_MARSHAL_VERSION); +#else + // 2.3 etc - only takes 1 arg. + PyMarshal_WriteObjectToString((PyObject *)aScriptObject); + +#endif + if (!obMarshal) + return HandlePythonError(); + NS_ASSERTION(PyString_Check(obMarshal), "marshal returned a non string?"); + rv |= aStream->Write32(PyString_GET_SIZE(obMarshal)); + rv |= aStream->WriteBytes(PyString_AS_STRING(obMarshal), PyString_GET_SIZE(obMarshal)); + Py_DECREF(obMarshal); + return rv; +} + +nsresult +nsPythonContext::Deserialize(nsIObjectInputStream* aStream, + nsScriptObjectHolder &aResult) +{ + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::Deserialize"); + nsresult rv; + PRUint32 magic; + aResult.drop(); + rv = aStream->Read32(&magic); + if (NS_FAILED(rv)) return rv; + + // Note that if we find a different marshalled version, we should + // still read the bytes from the stream, but throw them away and + // simply return nsnull in aResult. + // Failure to do this throws the stream out for future objects. + PRUint32 nBytes; + rv = aStream->Read32(&nBytes); + if (NS_FAILED(rv)) return rv; + + char* data = nsnull; + rv = aStream->ReadBytes(nBytes, &data); + if (NS_FAILED(rv)) return rv; + + if (magic != (PRUint32)PyImport_GetMagicNumber()) { + NS_WARNING("Python has different marshal version"); + if (data) + nsMemory::Free(data); + return NS_OK; + } + + CEnterLeavePython _celp; + PyObject *codeObject = PyMarshal_ReadObjectFromString(data, nBytes); + if (data) + nsMemory::Free(data); + if (codeObject == NULL) + return HandlePythonError(); + NS_ASSERTION(PyCode_Check(codeObject) || PyFunction_Check(codeObject), + "unmarshal returned non code/functions"); + NS_ASSERTION(aResult.getScriptTypeID()==nsIProgrammingLanguage::PYTHON, + "Expecting Python script object holder"); + aResult.set(codeObject); + Py_DECREF(codeObject); + return NS_OK; +} + +void +nsPythonContext::SetDefaultLanguageVersion(PRUint32 aVersion) +{ + return; +} + +nsIScriptGlobalObject * +nsPythonContext::GetGlobalObject() +{ + return mScriptGlobal; +} + +void * +nsPythonContext::GetNativeContext() +{ + return nsnull; +} + +void * +nsPythonContext::GetNativeGlobal() +{ + NS_ASSERTION(mDelegate, "Script context has no delegate"); + NS_ENSURE_TRUE(mDelegate, nsnull); + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::GetNativeGlobal"); + CEnterLeavePython _celp; + PyObject *ret = PyObject_CallMethod(mDelegate, "GetNativeGlobal", NULL); + if (!ret) { + HandlePythonError(); + return nsnull; + } + // This is assumed a borrowed reference. + NS_ASSERTION(ret->ob_refcnt > 1, "Can't have a new object here!?"); + Py_DECREF(ret); + return ret; +} + +nsresult +nsPythonContext::CreateNativeGlobalForInner( + nsIScriptGlobalObject *aGlobal, + PRBool aIsChrome, + void **aNativeGlobal, nsISupports **aHolder) +{ + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::CreateNativeGlobalForInner"); + CEnterLeavePython _celp; + PyObject *obGlob = PyObject_FromNSDOMInterface(mDelegate, aGlobal, + NS_GET_IID(nsIScriptGlobalObject)); + if (!obGlob) + return HandlePythonError(); + + PyObject *ret = PyObject_CallMethod(mDelegate, + "CreateNativeGlobalForInner", + "Ni", obGlob, aIsChrome); + if (!ret) { + HandlePythonError(); + return nsnull; + } + // Create a new holder to manage the lifetime of the returned object. + nsPyObjectHolder *holder = new nsPyObjectHolder(ret); + *aNativeGlobal = ret; + Py_DECREF(ret); + if (!holder) + return NS_ERROR_OUT_OF_MEMORY; + + return holder->QueryInterface(NS_GET_IID(nsISupports), (void **)aHolder); +} + +nsresult +nsPythonContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal) +{ + NS_ENSURE_ARG(aNewInner); + NS_TIMELINE_MARK_FUNCTION("nsPythonContext::ConnectToInner"); + CEnterLeavePython _celp; + PyObject *obNewInner = PyObject_FromNSDOMInterface(mDelegate, aNewInner, + NS_GET_IID(nsIScriptGlobalObject)); + if (!obNewInner) + return HandlePythonError(); + // Python code can't call this method - so we do it here and pass it in. + PyObject *obInnerScope = (PyObject *)aNewInner->GetScriptGlobal( + nsIProgrammingLanguage::PYTHON); + + PyObject *ret = PyObject_CallMethod(mDelegate, "ConnectToInner", "NOO", + obNewInner, aOuterGlobal, obInnerScope); + Py_XDECREF(ret); + return HandlePythonError(); +} + +PRBool +nsPythonContext::GetProcessingScriptTag() +{ + return mProcessingScriptTag; +} + +void +nsPythonContext::SetProcessingScriptTag(PRBool aResult) +{ + mProcessingScriptTag = aResult; +} + +PRBool +nsPythonContext::GetScriptsEnabled() +{ + return mScriptsEnabled; +} + +void +nsPythonContext::SetScriptsEnabled(PRBool aEnabled, PRBool aFireTimeouts) +{ + // eeek - this seems the wrong way around - the global should callback + // into the context. + mScriptsEnabled = aEnabled; + + nsIScriptGlobalObject *global = GetGlobalObject(); + + if (global) { + global->SetScriptsEnabled(aEnabled, aFireTimeouts); + } +} + +void +nsPythonContext::SetOwner(nsIScriptContextOwner* owner) +{ + // The owner should not be addrefed!! We'll be told + // when the owner goes away. + mOwner = owner; +} + +nsIScriptContextOwner * +nsPythonContext::GetOwner() +{ + return mOwner; +} + +nsresult +nsPythonContext::SetTerminationFunction(nsScriptTerminationFunc aFunc, + nsISupports* aRef) +{ + NS_ERROR("Term functions need thought"); + return NS_ERROR_UNEXPECTED; +} + +void +nsPythonContext::ScriptEvaluated(PRBool aTerminated) +{ + ; +} + +void +nsPythonContext::FinalizeContext() +{ + NS_ASSERTION(mDelegate, "No delegate?"); + NS_ASSERTION(!mScriptGlobal, "ClearScope not called to clean me up?"); + if (mDelegate) { + CEnterLeavePython _celp; + PyObject *ret = PyObject_CallMethod(mDelegate, "FinalizeContext", NULL); + HandlePythonError(); + Py_XDECREF(ret); + Py_DECREF(mDelegate); + mDelegate = NULL; + } +} + +void +nsPythonContext::GC() +{ + // sadly as at python 2.4, PyGC_Collect(); is not correctly exported. +} + +void +nsPythonContext::ClearScope(void *aGlobalObj, PRBool aWhatever) +{ + CEnterLeavePython _celp; + if (mDelegate) { + PyObject *ret = PyObject_CallMethod(mDelegate, "ClearScope", + "O", + aGlobalObj); + Py_XDECREF(ret); + } + mScriptGlobal = nsnull; + HandlePythonError(); +} + +NS_IMETHODIMP +nsPythonContext::Notify(nsITimer *timer) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +nsPythonContext::DropScriptObject(void *object) +{ + if (object) { + PYLEAK_STAT_DECREMENT(ScriptObject); + CEnterLeavePython _celp; + Py_DECREF((PyObject *)object); + } + return NS_OK; +} + +nsresult +nsPythonContext::HoldScriptObject(void *object) +{ + if (object) { + PYLEAK_STAT_INCREMENT(ScriptObject); + CEnterLeavePython _celp; + Py_INCREF((PyObject *)object); + } + return NS_OK; +} diff --git a/extensions/python/dom/src/nsPyContext.h b/extensions/python/dom/src/nsPyContext.h new file mode 100644 index 000000000000..e1950343f3f3 --- /dev/null +++ b/extensions/python/dom/src/nsPyContext.h @@ -0,0 +1,241 @@ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIScriptContext.h" +#include "nsITimer.h" + +#include "nsPyDOM.h" +class nsIScriptObjectOwner; +class nsIArray; + +#ifdef NS_DEBUG +class nsPyDOMObjectLeakStats + { + public: + nsPyDOMObjectLeakStats() + : mEventHandlerCount(0), mScriptObjectCount(0), mBindingCount(0), + mScriptContextCount(0) {} + + ~nsPyDOMObjectLeakStats() + { + printf("pydom leaked objects:\n"); + PRBool leaked = PR_FALSE; +#define CHECKLEAK(attr) \ + if (attr) { \ + printf(" => %-20s % 6d\n", #attr ":", attr); \ + leaked = PR_TRUE; \ + } + + CHECKLEAK(mScriptObjectCount); + CHECKLEAK(mBindingCount); + CHECKLEAK(mScriptContextCount); + extern PRInt32 cPyDOMISupportsObjects; + if (_PyXPCOM_GetInterfaceCount()) { + printf(" => %-20s % 6d (%d are nsdom objects)\n", + "pyxpcom interfaces:", + _PyXPCOM_GetInterfaceCount(), cPyDOMISupportsObjects + ); + leaked = PR_TRUE; + } + + if (_PyXPCOM_GetGatewayCount()) { + printf(" => %-20s % 6d\n", + "pyxpcom gateways:", _PyXPCOM_GetGatewayCount()); + leaked = PR_TRUE; + } + if (!leaked) + printf(" => no leaks.\n"); +#ifdef Py_DEBUG // _Py_RefTotal only available in debug builds. + printf(" %d Python references remain\n", _Py_RefTotal); +#endif + } + + PRInt32 mEventHandlerCount; + PRInt32 mScriptObjectCount; + PRInt32 mBindingCount; + PRInt32 mScriptContextCount; + }; +extern nsPyDOMObjectLeakStats gLeakStats; +#define PYLEAK_STAT_INCREMENT(_s) PR_AtomicIncrement(&gLeakStats.m ## _s ## Count) +#define PYLEAK_STAT_XINCREMENT(_what, _s) if (_what) PR_AtomicIncrement(&gLeakStats.m ## _s ## Count) +#define PYLEAK_STAT_DECREMENT(_s) PR_AtomicDecrement(&gLeakStats.m ## _s ## Count) +#define PYLEAK_STAT_XDECREMENT(_what, _s) if (_what) PR_AtomicDecrement(&gLeakStats.m ## _s ## Count) +#else +#define PYLEAK_STAT_INCREMENT(_s) +#define PYLEAK_STAT_XINCREMENT(_what, _s) +#define PYLEAK_STAT_DECREMENT(_s) +#define PYLEAK_STAT_XDECREMENT(_what, _s) +#endif + +class nsPythonContext : public nsIScriptContext, + public nsITimerCallback +{ +public: + nsPythonContext(); + virtual ~nsPythonContext(); + + NS_DECL_ISUPPORTS + + virtual PRUint32 GetScriptTypeID() + { return nsIProgrammingLanguage::PYTHON; } + + virtual nsresult EvaluateString(const nsAString& aScript, + void *aScopeObject, + nsIPrincipal *principal, + const char *aURL, + PRUint32 aLineNo, + PRUint32 aVersion, + nsAString *aRetValue, + PRBool* aIsUndefined); + virtual nsresult EvaluateStringWithValue(const nsAString& aScript, + void *aScopeObject, + nsIPrincipal *aPrincipal, + const char *aURL, + PRUint32 aLineNo, + PRUint32 aVersion, + void* aRetValue, + PRBool* aIsUndefined); + + virtual nsresult CompileScript(const PRUnichar* aText, + PRInt32 aTextLength, + void *aScopeObject, + nsIPrincipal *principal, + const char *aURL, + PRUint32 aLineNo, + PRUint32 aVersion, + nsScriptObjectHolder &aScriptObject); + virtual nsresult ExecuteScript(void* aScriptObject, + void *aScopeObject, + nsAString* aRetValue, + PRBool* aIsUndefined); + virtual nsresult CompileEventHandler(nsIAtom *aName, + PRUint32 aArgCount, + const char** aArgNames, + const nsAString& aBody, + const char *aURL, + PRUint32 aLineNo, + nsScriptObjectHolder &aHandler); + virtual nsresult CallEventHandler(nsISupports* aTarget, void *aScope, + void* aHandler, + nsIArray *argv, nsIVariant **rv); + virtual nsresult BindCompiledEventHandler(nsISupports*aTarget, void *aScope, + nsIAtom *aName, + void *aHandler); + virtual nsresult GetBoundEventHandler(nsISupports* aTarget, void *aScope, + nsIAtom* aName, + nsScriptObjectHolder &aHandler); + virtual nsresult CompileFunction(void* aTarget, + const nsACString& aName, + PRUint32 aArgCount, + const char** aArgArray, + const nsAString& aBody, + const char* aURL, + PRUint32 aLineNo, + PRBool aShared, + void** aFunctionObject); + + virtual void SetDefaultLanguageVersion(PRUint32 aVersion); + virtual nsIScriptGlobalObject *GetGlobalObject(); + virtual void *GetNativeContext(); + virtual void *GetNativeGlobal(); + virtual nsresult CreateNativeGlobalForInner( + nsIScriptGlobalObject *aGlobal, + PRBool aIsChrome, + void **aNativeGlobal, + nsISupports **aHolder); + virtual nsresult ConnectToInner(nsIScriptGlobalObject *aNewInner, + void *aOuterGlobal); + virtual nsresult InitContext(nsIScriptGlobalObject *aGlobalObject); + virtual PRBool IsContextInitialized(); + virtual void FinalizeContext(); + virtual void GC(); + + virtual void ScriptEvaluated(PRBool aTerminated); + virtual void SetOwner(nsIScriptContextOwner* owner); + virtual nsIScriptContextOwner *GetOwner(); + virtual nsresult SetTerminationFunction(nsScriptTerminationFunc aFunc, + nsISupports* aRef); + virtual PRBool GetScriptsEnabled(); + virtual void SetScriptsEnabled(PRBool aEnabled, PRBool aFireTimeouts); + + virtual nsresult SetProperty(void *aTarget, const char *aPropName, + nsISupports *aVal); + + virtual PRBool GetProcessingScriptTag(); + virtual void SetProcessingScriptTag(PRBool aResult); + + virtual void SetGCOnDestruction(PRBool aGCOnDestruction) {;} + + virtual nsresult InitClasses(void *aGlobalObj); + virtual void ClearScope(void* aGlobalObj, PRBool aWhatever); + + virtual void WillInitializeContext(); + virtual void DidInitializeContext(); + virtual void DidSetDocument(nsIDOMDocument *aDocdoc, void *aGlobal); + + virtual nsresult Serialize(nsIObjectOutputStream* aStream, void *aScriptObject); + virtual nsresult Deserialize(nsIObjectInputStream* aStream, + nsScriptObjectHolder &aResult); + + virtual nsresult HoldScriptObject(void *object); + virtual nsresult DropScriptObject(void *object); + + NS_DECL_NSITIMERCALLBACK + + PyObject *PyObject_FromInterface(nsISupports *target, + const nsIID &iid) + { + return ::PyObject_FromNSDOMInterface(mDelegate, target, iid); + } +protected: + + // covert utf-16 source code into utf8 with normalized line endings. + nsCAutoString FixSource(const nsAString &aSource); + PyObject *InternalCompile(const nsAString &source, const char *url, + PRUint32 lineNo); + + PRPackedBool mIsInitialized; + PRPackedBool mScriptsEnabled; + PRPackedBool mProcessingScriptTag; + + nsIScriptContextOwner* mOwner; /* NB: weak reference, not ADDREF'd */ + // ditto - not ADDREF'd - but Python itself takes one! + nsIScriptGlobalObject *mScriptGlobal; + + nsresult HandlePythonError(); + + PyObject *mDelegate; // The Python code we delegate lots of code to. +}; diff --git a/extensions/python/dom/src/nsPyDOM.h b/extensions/python/dom/src/nsPyDOM.h new file mode 100644 index 000000000000..08d3ec551641 --- /dev/null +++ b/extensions/python/dom/src/nsPyDOM.h @@ -0,0 +1,76 @@ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __NSPYDOM_H__ +#define __NSPYDOM_H__ + +#include "PyXPCOM.h" + +class nsIScriptTimeoutHandler; +class nsIArray; + +PyObject *PyObject_FromNSDOMInterface(PyObject *pycontext, nsISupports *pis, + const nsIID &iid = NS_GET_IID(nsISupports), + PRBool bMakeNicePyObject = PR_TRUE); + +nsresult CreatePyTimeoutHandler(const nsAString &aExpr, + PyObject *aFunObj, PyObject *obArgs, + nsIScriptTimeoutHandler **aRet); + +void PyInit_DOMnsISupports(); + +// A little interface that allows us to provide an object that supports +// nsIArray, but also allows Python to QI for this private interface and +// get the underlying PyObjects directly, without incurring the transfer +// to and from an nsIVariant. + +#define NS_IPYARGARRAY_IID \ + { /* {C169DFB6-BA7A-4337-AED6-EA791BB9C04E} */ \ + 0xc169dfb6, 0xba7a, 0x4337, \ + { 0xae, 0xd6, 0xea, 0x79, 0x1b, 0xb9, 0xc0, 0x4e } } + +class nsIPyArgArray: public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPYARGARRAY_IID) + virtual PyObject *GetArgs() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIPyArgArray, NS_IPYARGARRAY_IID) + +nsresult NS_CreatePyArgv(PyObject *ob, nsIArray **aArray); + +#endif // __NSPYDOM_H__ diff --git a/extensions/python/dom/src/nsPyDOMISupports.cpp b/extensions/python/dom/src/nsPyDOMISupports.cpp new file mode 100644 index 000000000000..22200675775c --- /dev/null +++ b/extensions/python/dom/src/nsPyDOMISupports.cpp @@ -0,0 +1,166 @@ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsPyDOM.h" + +PRInt32 cPyDOMISupportsObjects=0; + +struct PyMethodDef +Py_DOMnsISupports_Methods[] = +{ + {NULL} +}; + +class Py_DOMnsISupports : public Py_nsISupports +{ +public: + static PyXPCOM_TypeObject *type; + static void InitType() { + type = new PyXPCOM_TypeObject( + "DOMISupports", + Py_nsISupports::type, + sizeof(Py_DOMnsISupports), + Py_DOMnsISupports_Methods, + nsnull); + const nsIID &iid = NS_GET_IID(nsISupports); + // Don't RegisterInterface! + // RegisterInterface(iid, type); + } + //virtual PyObject *getattr(const char *name); + //virtual int setattr(const char *name, PyObject *val); + virtual PyObject *MakeInterfaceResult(nsISupports *ps, const nsIID &iid, + PRBool bMakeNicePyObject = PR_TRUE) + { + if (!iid.Equals(NS_GET_IID(nsIClassInfo))) + return PyObject_FromNSDOMInterface(m_pycontext, ps, + iid, bMakeNicePyObject); + return Py_nsISupports::MakeInterfaceResult(ps, iid, bMakeNicePyObject); + } + + Py_DOMnsISupports(PyObject *pycontext, nsISupports *p, const nsIID &iid) : + Py_nsISupports(p, iid, type), + m_pycontext(pycontext) { + /* The IID _must_ be the IID of the interface we are wrapping! */ + //NS_ABORT_IF_FALSE(iid.Equals(NS_GET_IID(InterfaceName)), "Bad IID"); + Py_INCREF(pycontext); + PR_AtomicIncrement(&cPyDOMISupportsObjects); + + } + virtual ~Py_DOMnsISupports() { + Py_DECREF(m_pycontext); + PR_AtomicDecrement(&cPyDOMISupportsObjects); + } + static PyObject *MakeDefaultWrapper(PyObject *pycontext, + PyObject *pyis, const nsIID &iid); +protected: + PyObject *m_pycontext; +}; + + +PyObject *PyObject_FromNSDOMInterface(PyObject *pycontext, nsISupports *pisOrig, + const nsIID &iid, /* = nsISupports */ + PRBool bMakeNicePyObject /* = PR_TRUE */) +{ + if (pisOrig==NULL) { + Py_INCREF(Py_None); + return Py_None; + } + + nsCOMPtr pis; + // The default here is nsISupports as many callers don't really know + // the real interface. Explicitly query for ISupports. + if (iid.Equals(NS_GET_IID(nsISupports))) { + pis = do_QueryInterface(pisOrig); + if (!pis) { + static const char *err = "Object failed QI for nsISupports!"; + NS_ERROR(err); + PyErr_SetString(PyExc_RuntimeError, err); + return NULL; + } + } else + pis = pisOrig; +#ifdef NS_DEBUG + nsISupports *queryResult = nsnull; + pis->QueryInterface(iid, (void **)&queryResult); + NS_ASSERTION(queryResult == pis, "QueryInterface needed"); + NS_IF_RELEASE(queryResult); +#endif + + Py_DOMnsISupports *ret = new Py_DOMnsISupports(pycontext, pis, iid); + if (ret && bMakeNicePyObject) + return Py_DOMnsISupports::MakeDefaultWrapper(pycontext, ret, iid); + return ret; +} + +// Call back into the Python context +PyObject * +Py_DOMnsISupports::MakeDefaultWrapper(PyObject *pycontext, + PyObject *pyis, + const nsIID &iid) +{ + NS_PRECONDITION(pyis, "NULL pyobject!"); + PyObject *obIID = NULL; + PyObject *ret = NULL; + + obIID = Py_nsIID::PyObjectFromIID(iid); + if (obIID==NULL) + goto done; + + ret = PyObject_CallMethod(pycontext, "MakeInterfaceResult", + "OO", pyis, obIID); + if (ret==NULL) goto done; +done: + if (PyErr_Occurred()) { + NS_ABORT_IF_FALSE(ret==NULL, "Have an error, but also a return val!"); + PyXPCOM_LogError("Creating an interface object to be used as a result failed\n"); + PyErr_Clear(); + } + Py_XDECREF(obIID); + if (ret==NULL) // eek - error - return the original with no refcount mod. + ret = pyis; + else + // no error - decref the old object + Py_DECREF(pyis); + // return our obISupports. If NULL, we are really hosed and nothing we can do. + return ret; +} + +void PyInit_DOMnsISupports() +{ + Py_DOMnsISupports::InitType(); +} + +PyXPCOM_TypeObject *Py_DOMnsISupports::type = NULL; diff --git a/extensions/python/dom/src/nsPyDOMModule.cpp b/extensions/python/dom/src/nsPyDOMModule.cpp new file mode 100644 index 000000000000..58f8889618a7 --- /dev/null +++ b/extensions/python/dom/src/nsPyDOMModule.cpp @@ -0,0 +1,515 @@ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond (initial development) + * + * 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 ***** */ + +// "Module" is such an overloaded term! This file serves as both the xpcom +// component module and the implicit Python "_nsdom" module. The _nsdom module +// is implicit in that it is automatically imported as this component +// initializes even though it does not exist as a regular python module. + +#include "nsIGenericFactory.h" +#include "nsPyRuntime.h" + +#include "nsPyContext.h" +#include "nsIServiceManager.h" +#include "nsIScriptGlobalObject.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsIAtomService.h" +#include "nsIEventListenerManager.h" +#include "nsIScriptTimeoutHandler.h" +#include "nsIDOMEventReceiver.h" +#include "nsIArray.h" +#include "jscntxt.h" +#include "nsPIDOMWindow.h" +#include "nsDOMScriptObjectHolder.h" + +// just for constants... +#include "nsIScriptContext.h" +#include "nsIScriptGlobalObject.h" + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsPythonRuntime) + +static const nsModuleComponentInfo components[] = { + {"Python Script Language", NS_SCRIPT_LANGUAGE_PYTHON_CID, + NS_SCRIPT_LANGUAGE_PYTHON_CONTRACTID_NAME, + nsPythonRuntimeConstructor,}, + // And again with the contract ID including the language ID + {"Python Script Language", NS_SCRIPT_LANGUAGE_PYTHON_CID, + NS_SCRIPT_LANGUAGE_PYTHON_CONTRACTID_ID, + nsPythonRuntimeConstructor,}, +}; + +// Implementation of the module object. +NS_IMPL_NSGETMODULE(nsPyDOMModule, components) + +// The Python '_nsdom' module +//////////////////////////////////////////////////////////////////////// +// +// +PyObject *PyJSExec(PyObject *self, PyObject *args) +{ + PyObject *obGlobal; + char *code; + char *url = ""; + PRUint32 lineNo = 0; + PRUint32 version = 0; + if (!PyArg_ParseTuple(args, "Os|sii", &obGlobal, &code, &url, &lineNo, &version)) + return NULL; + nsCOMPtr isup; + if (!Py_nsISupports::InterfaceFromPyObject( + obGlobal, + NS_GET_IID(nsISupports), + getter_AddRefs(isup), + PR_FALSE, PR_FALSE)) + return NULL; + nsCOMPtr scriptGlobal = do_QueryInterface(isup); + if (scriptGlobal == nsnull) + return PyErr_Format(PyExc_TypeError, "Object is not an nsIScriptGlobal"); + nsIScriptContext *scriptContext = + scriptGlobal->GetScriptContext(nsIProgrammingLanguage::JAVASCRIPT); + if (!scriptContext) + return PyErr_Format(PyExc_RuntimeError, "No javascript context available"); + + // get the principal + nsIPrincipal *principal = nsnull; + nsCOMPtr globalData = do_QueryInterface(scriptGlobal); + if (globalData) + principal = globalData->GetPrincipal(); + if (!principal) + return PyErr_Format(PyExc_RuntimeError, "No nsIPrincipal available"); + + nsresult rv; + void *scope = scriptGlobal->GetScriptGlobal(nsIProgrammingLanguage::JAVASCRIPT); + nsScriptObjectHolder scriptObject(scriptContext); + JSContext *ctx = (JSContext *)scriptContext->GetNativeContext(); + + nsAutoString str; + PRBool bIsUndefined; + Py_BEGIN_ALLOW_THREADS + rv = scriptContext->CompileScript( NS_ConvertASCIItoUTF16(code).get(), + strlen(code), + scope, + principal, // no principal?? + url, + lineNo, + version, + scriptObject); + if (NS_SUCCEEDED(rv) && scriptObject) + rv = scriptContext->ExecuteScript(scriptObject, scope, &str, + &bIsUndefined); + Py_END_ALLOW_THREADS + + if (NS_FAILED(rv)) + return PyXPCOM_BuildPyException(rv); + return Py_BuildValue("NN", PyObject_FromNSString(str), PyBool_FromLong(bIsUndefined)); + // XXX - cleanup scriptObject??? +} + +// Various helpers to avoid exposing non-scriptable interfaces when we only +// need one or 2 functions. +static PyObject *PyIsOuterWindow(PyObject *self, PyObject *args) +{ + PyObject *obTarget; + if (!PyArg_ParseTuple(args, "O", &obTarget)) + return NULL; + + // target + nsCOMPtr target; + if (!Py_nsISupports::InterfaceFromPyObject( + obTarget, + NS_GET_IID(nsPIDOMWindow), + getter_AddRefs(target), + PR_FALSE, PR_FALSE)) + return NULL; + + return PyBool_FromLong(target->IsOuterWindow()); +} + +static PyObject *PyGetCurrentInnerWindow(PyObject *self, PyObject *args) +{ + PyObject *obTarget; + if (!PyArg_ParseTuple(args, "O", &obTarget)) + return NULL; + + // target + nsCOMPtr target; + if (!Py_nsISupports::InterfaceFromPyObject( + obTarget, + NS_GET_IID(nsPIDOMWindow), + getter_AddRefs(target), + PR_FALSE, PR_FALSE)) + return NULL; + + // Use the script global object to fetch the context. + nsCOMPtr sgo(do_QueryInterface(target)); + if (!sgo) + return PyErr_Format(PyExc_ValueError, "Object does not supports nsIScriptGlobalObject"); + nsIScriptContext *ctx = sgo->GetScriptContext(nsIProgrammingLanguage::PYTHON); + if (!ctx) + return PyErr_Format(PyExc_ValueError, "Object not initialized for Python"); + + nsCOMPtr innerGlobal( + do_QueryInterface(target->GetCurrentInnerWindow())); + if (!innerGlobal) + return PyErr_Format(PyExc_ValueError, "Result object does not supports nsIScriptGlobalObject"); + nsPythonContext *pyctx = (nsPythonContext *)ctx; + return pyctx->PyObject_FromInterface(innerGlobal, + NS_GET_IID(nsIScriptGlobalObject)); +} + +static PyObject *PySetTimeoutOrInterval(PyObject *self, PyObject *args) +{ + PyObject *obTarget; + PyObject *obHandler; + PyObject *obArgs; + double interval; + int isInterval; + if (!PyArg_ParseTuple(args, "OdOO!i:SetTimeoutOrInterval", &obTarget, + &interval, &obHandler, + &PyTuple_Type, &obArgs, + &isInterval)) + return NULL; + + if (!PySequence_Check(obArgs)) + return PyErr_Format(PyExc_TypeError, "args param must be a sequence (got %s)", + obArgs->ob_type->tp_name); + + PyObject *funObject = NULL; + nsAutoString strExpr; + if (PyCallable_Check(obHandler)) { + funObject = obHandler; + } else { + if (!PyObject_AsNSString(obHandler, strExpr)) + return NULL; + } + + // target + nsCOMPtr target; + if (!Py_nsISupports::InterfaceFromPyObject( + obTarget, + NS_GET_IID(nsPIDOMWindow), + getter_AddRefs(target), + PR_FALSE, PR_FALSE)) + return NULL; + + nsCOMPtr handler; + nsresult rv; + rv = CreatePyTimeoutHandler(strExpr, funObject, obArgs, + getter_AddRefs(handler)); + if (NS_FAILED(rv)) + return PyXPCOM_BuildPyException(rv); + + PRInt32 ret; + Py_BEGIN_ALLOW_THREADS; + rv = target->SetTimeoutOrInterval(handler, interval, isInterval, &ret); + Py_END_ALLOW_THREADS; + if (NS_FAILED(rv)) + return PyXPCOM_BuildPyException(rv); + return PyInt_FromLong(ret); +} + +static PyObject *PyClearTimeoutOrInterval(PyObject *self, PyObject *args) +{ + int tid; + PyObject *obTarget; + if (!PyArg_ParseTuple(args, "Oi", &obTarget, &tid)) + return NULL; + + // target + nsCOMPtr target; + if (!Py_nsISupports::InterfaceFromPyObject( + obTarget, + NS_GET_IID(nsPIDOMWindow), + getter_AddRefs(target), + PR_FALSE, PR_FALSE)) + return NULL; + + nsresult rv; + Py_BEGIN_ALLOW_THREADS; + rv = target->ClearTimeoutOrInterval((PRInt32)tid); + Py_END_ALLOW_THREADS; + if (NS_FAILED(rv)) + return PyXPCOM_BuildPyException(rv); + Py_INCREF(Py_None); + return Py_None; +} + +// Avoid exposing the entire non-scriptable event listener interface +// We are exposing: +// +//nsEventListenerManager::AddScriptEventListener(nsISupports *aObject, +// nsIAtom *aName, +// const nsAString& aBody, +// PRUint32 aLanguage, +// PRBool aDeferCompilation, +// PRBool aPermitUntrustedEvents) +// +// Implementation of the above seems strange - if !aDeferCompilation then +// aBody is ignored (presumably to later be fetched via GetAttr + +static PyObject *PyAddScriptEventListener(PyObject *self, PyObject *args) +{ + PyObject *obTarget, *obBody; + char *name; + int lang = nsIProgrammingLanguage::PYTHON; + int defer, untrusted; + if (!PyArg_ParseTuple(args, "OsOii|i", &obTarget, &name, &obBody, + &defer, &untrusted, &lang)) + return NULL; + + // target + nsCOMPtr target; + if (!Py_nsISupports::InterfaceFromPyObject( + obTarget, + NS_GET_IID(nsISupports), + getter_AddRefs(target), + PR_FALSE, PR_FALSE)) + return NULL; + + nsAutoString body; + if (!PyObject_AsNSString(obBody, body)) + return NULL; + + // The receiver, to get the manager. + nsCOMPtr receiver(do_QueryInterface(target)); + if (!receiver) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED); + + nsCOMPtr manager; + receiver->GetListenerManager(PR_TRUE, getter_AddRefs(manager)); + if (!manager) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED); + + nsCOMPtr atom(do_GetAtom(nsDependentCString(name))); + if (!atom) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED); + + nsresult rv; + Py_BEGIN_ALLOW_THREADS + rv = manager->AddScriptEventListener(target, atom, body, lang, defer, + untrusted); + Py_END_ALLOW_THREADS + if (NS_FAILED(rv)) + return PyXPCOM_BuildPyException(rv); + Py_INCREF(Py_None); + return Py_None; +} + +// NS_IMETHOD RegisterScriptEventListener(nsIScriptContext *aContext, +// void *aScopeObject, +// nsISupports *aObject, +// nsIAtom* aName) = 0; +static PyObject *PyRegisterScriptEventListener(PyObject *self, PyObject *args) +{ + PyObject *obTarget, *obGlobal, *obScope; + char *name; + if (!PyArg_ParseTuple(args, "OOOs", &obGlobal, &obScope, &obTarget, &name)) + return NULL; + + nsCOMPtr isup; + if (!Py_nsISupports::InterfaceFromPyObject( + obGlobal, + NS_GET_IID(nsISupports), + getter_AddRefs(isup), + PR_FALSE, PR_FALSE)) + return NULL; + nsCOMPtr scriptGlobal = do_QueryInterface(isup); + if (scriptGlobal == nsnull) + return PyErr_Format(PyExc_TypeError, "Object is not an nsIScriptGlobal"); + nsIScriptContext *scriptContext = + scriptGlobal->GetScriptContext(nsIProgrammingLanguage::PYTHON); + if (!scriptContext) + return PyErr_Format(PyExc_RuntimeError, "Can't find my context??"); + + // target + nsCOMPtr target; + if (!Py_nsISupports::InterfaceFromPyObject( + obTarget, + NS_GET_IID(nsISupports), + getter_AddRefs(target), + PR_FALSE, PR_FALSE)) + return NULL; + + // sob - I don't quite understand this, but things get upset when + // an outer window gets the event. + nsCOMPtr win(do_QueryInterface(target)); + if (win != nsnull && win->IsOuterWindow()) { + target = win->GetCurrentInnerWindow(); + } + // The receiver, to get the manager. + nsCOMPtr receiver(do_QueryInterface(target)); + if (!receiver) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED); + + nsCOMPtr manager; + receiver->GetListenerManager(PR_TRUE, getter_AddRefs(manager)); + if (!manager) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED); + + nsCOMPtr atom(do_GetAtom(nsDependentCString(name))); + if (!atom) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED); + + nsresult rv; + Py_BEGIN_ALLOW_THREADS + rv = manager->RegisterScriptEventListener(scriptContext, obScope, + target, atom); + Py_END_ALLOW_THREADS + if (NS_FAILED(rv)) + return PyXPCOM_BuildPyException(rv); + Py_INCREF(Py_None); + return Py_None; +} + +// NS_IMETHOD CompileScriptEventListener(nsIScriptContext *aContext, +// void *aScopeObject, +// nsISupports *aObject, +// nsIAtom* aName, +// PRBool *aDidCompile) = 0; +static PyObject *PyCompileScriptEventListener(PyObject *self, PyObject *args) +{ + PyObject *obTarget, *obGlobal, *obScope; + char *name; + if (!PyArg_ParseTuple(args, "OOOs", &obGlobal, &obScope, &obTarget, &name)) + return NULL; + + nsCOMPtr isup; + if (!Py_nsISupports::InterfaceFromPyObject( + obGlobal, + NS_GET_IID(nsISupports), + getter_AddRefs(isup), + PR_FALSE, PR_FALSE)) + return NULL; + nsCOMPtr scriptGlobal = do_QueryInterface(isup); + if (scriptGlobal == nsnull) + return PyErr_Format(PyExc_TypeError, "Object is not an nsIScriptGlobal"); + nsIScriptContext *scriptContext = + scriptGlobal->GetScriptContext(nsIProgrammingLanguage::PYTHON); + if (!scriptContext) + return PyErr_Format(PyExc_RuntimeError, "Can't find my context??"); + + // target + nsCOMPtr target; + if (!Py_nsISupports::InterfaceFromPyObject( + obTarget, + NS_GET_IID(nsISupports), + getter_AddRefs(target), + PR_FALSE, PR_FALSE)) + return NULL; + + // sob - I don't quite understand this, but things get upset when + // an outer window gets the event. + nsCOMPtr win(do_QueryInterface(target)); + if (win != nsnull && win->IsOuterWindow()) { + target = win->GetCurrentInnerWindow(); + } + // The receiver, to get the manager. + nsCOMPtr receiver(do_QueryInterface(target)); + if (!receiver) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED); + + nsCOMPtr manager; + receiver->GetListenerManager(PR_TRUE, getter_AddRefs(manager)); + if (!manager) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED); + + nsCOMPtr atom(do_GetAtom(nsDependentCString(name))); + if (!atom) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED); + + nsresult rv; + PRBool didCompile; + Py_BEGIN_ALLOW_THREADS + rv = manager->CompileScriptEventListener(scriptContext, obScope, + target, atom, &didCompile); + Py_END_ALLOW_THREADS + if (NS_FAILED(rv)) + return PyXPCOM_BuildPyException(rv); + return PyBool_FromLong(didCompile); +} + +static PyObject *PyMakeArray(PyObject *self, PyObject *args) +{ + PyObject *ob; + if (!PyArg_ParseTuple(args, "O!:MakeArray", &PyTuple_Type, &ob)) + return NULL; + + nsCOMPtr array; + nsresult rv = NS_CreatePyArgv(ob, getter_AddRefs(array)); + if (NS_FAILED(rv) || array == nsnull) + return PyXPCOM_BuildPyException(rv); + return PyObject_FromNSInterface(array, NS_GET_IID(nsIArray)); +} + +static PyObject *PyMakeDOMObject(PyObject *self, PyObject *args) +{ + PyObject *ob, *obContext; + if (!PyArg_ParseTuple(args, "OO:MakeDOMObject", &obContext, &ob)) + return NULL; + + nsCOMPtr sup; + if (!Py_nsISupports::InterfaceFromPyObject(ob, NS_GET_IID(nsISupports), + getter_AddRefs(sup), + PR_FALSE, PR_FALSE)) + return NULL; + + return PyObject_FromNSDOMInterface(obContext, sup, NS_GET_IID(nsISupports)); +} + +static struct PyMethodDef methods[]= +{ + {"JSExec", PyJSExec, 1}, + {"AddScriptEventListener", PyAddScriptEventListener, 1}, + {"RegisterScriptEventListener", PyRegisterScriptEventListener, 1}, + {"CompileScriptEventListener", PyCompileScriptEventListener, 1}, + {"GetCurrentInnerWindow", PyGetCurrentInnerWindow, 1}, + {"IsOuterWindow", PyIsOuterWindow, 1}, + {"SetTimeoutOrInterval", PySetTimeoutOrInterval, 1}, + {"ClearTimeoutOrInterval", PyClearTimeoutOrInterval, 1}, + {"MakeArray", PyMakeArray, 1}, + {"MakeDOMObject", PyMakeDOMObject, 1}, + { NULL } +}; + +//////////////////////////////////////////////////////////// +// The module init code. +// +#define REGISTER_IID(t) { \ + PyObject *iid_ob = Py_nsIID::PyObjectFromIID(NS_GET_IID(t)); \ + PyDict_SetItemString(dict, "IID_"#t, iid_ob); \ + Py_DECREF(iid_ob); \ + } + +// *NOT* called by Python - this is not a regular Python module. +// Caller must ensure only called once! +void +init_nsdom() { + CEnterLeavePython _celp; + // Create the module and add the functions + PyObject *oModule = Py_InitModule("_nsdom", methods); + PyObject *dict = PyModule_GetDict(oModule); + REGISTER_IID(nsIScriptGlobalObject); +} diff --git a/extensions/python/dom/src/nsPyRuntime.cpp b/extensions/python/dom/src/nsPyRuntime.cpp new file mode 100644 index 000000000000..23eebe822ee5 --- /dev/null +++ b/extensions/python/dom/src/nsPyRuntime.cpp @@ -0,0 +1,106 @@ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsPyRuntime.h" +#include "nsPyContext.h" +#include "nsICategoryManager.h" +#include "nsIScriptContext.h" +#include "nsIDOMEventReceiver.h" + +extern void init_nsdom(); + +static PRBool initialized = PR_FALSE; + +// QueryInterface implementation for nsPythonRuntime +NS_INTERFACE_MAP_BEGIN(nsPythonRuntime) + NS_INTERFACE_MAP_ENTRY(nsIScriptRuntime) +NS_INTERFACE_MAP_END + + +NS_IMPL_ADDREF(nsPythonRuntime) +NS_IMPL_RELEASE(nsPythonRuntime) + +nsresult +nsPythonRuntime::CreateContext(nsIScriptContext **ret) +{ + PyXPCOM_EnsurePythonEnvironment(); + if (!initialized) { + PyInit_DOMnsISupports(); + init_nsdom(); + initialized = PR_TRUE; + } + *ret = new nsPythonContext(); + if (!ret) + return NS_ERROR_OUT_OF_MEMORY; + NS_IF_ADDREF(*ret); + return NS_OK; +} + +nsresult +nsPythonRuntime::ParseVersion(const nsString &aVersionStr, PRUint32 *aFlags) { + // We ignore the version, but it is safer to fail whenever a version is + // specified, thereby ensuring noone will ever specify a version with + // Python, allowing future semantics to be defined without concern for + // existing behaviour. + if (aVersionStr.IsEmpty()) { + *aFlags = 0; + return NS_OK; + } + NS_ERROR("Don't specify a version for Python"); + return NS_ERROR_UNEXPECTED; + } + +nsresult +nsPythonRuntime::DropScriptObject(void *object) +{ + if (object) { + PYLEAK_STAT_DECREMENT(ScriptObject); + CEnterLeavePython _celp; + Py_DECREF((PyObject *)object); + } + return NS_OK; +} + +nsresult +nsPythonRuntime::HoldScriptObject(void *object) +{ + if (object) { + PYLEAK_STAT_INCREMENT(ScriptObject); + CEnterLeavePython _celp; + Py_INCREF((PyObject *)object); + } + return NS_OK; +} diff --git a/extensions/python/dom/src/nsPyRuntime.h b/extensions/python/dom/src/nsPyRuntime.h new file mode 100644 index 000000000000..c9d1b10be3ed --- /dev/null +++ b/extensions/python/dom/src/nsPyRuntime.h @@ -0,0 +1,71 @@ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIScriptRuntime.h" +#include "nsIGenericFactory.h" + + +#define NS_SCRIPT_LANGUAGE_PYTHON_CID \ + { 0xcee4ee7d, 0xf230, 0x49da, { 0x94, 0xd8, 0x6a, 0x9a, 0x48, 0xe, 0x12, 0xb3 } } + +#define NS_SCRIPT_LANGUAGE_PYTHON_CONTRACTID_NAME \ + "@mozilla.org/script-language;1?script-type=application/x-python" + +// nsIProgrammingLanguage::PYTHON == 3 +#define NS_SCRIPT_LANGUAGE_PYTHON_CONTRACTID_ID \ + "@mozilla.org/script-language;1?id=3" + +class nsPythonRuntime : public nsIScriptRuntime +{ +public: + // nsISupports + NS_DECL_ISUPPORTS + + // nsIScriptLanguage + virtual PRUint32 GetScriptTypeID() { + return nsIProgrammingLanguage::PYTHON; + } + + virtual void ShutDown() {;} + + virtual nsresult CreateContext(nsIScriptContext **ret); + + virtual nsresult ParseVersion(const nsString &aVersionStr, PRUint32 *flags); + + virtual nsresult HoldScriptObject(void *object); + virtual nsresult DropScriptObject(void *object); + +}; diff --git a/extensions/python/dom/src/nsPyTimeout.cpp b/extensions/python/dom/src/nsPyTimeout.cpp new file mode 100644 index 000000000000..74d56a2894ee --- /dev/null +++ b/extensions/python/dom/src/nsPyTimeout.cpp @@ -0,0 +1,150 @@ +/* ***** 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 mozilla.org code. + * + * The Initial Developer of the Original Code is mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsPyDOM.h" +#include "nsIProgrammingLanguage.h" +#include "nsIScriptTimeoutHandler.h" +#include "nsIArray.h" + +class nsPyTimeoutHandler: public nsIScriptTimeoutHandler +{ +public: + // nsISupports + NS_DECL_ISUPPORTS + + nsPyTimeoutHandler(const nsAString &aExpr, PyObject *aFunObj, + PyObject *obArgv) { + mFunObj = aFunObj; + Py_XINCREF(mFunObj); + mExpr = aExpr; + NS_ASSERTION(PyTuple_Check(obArgv), "Should be a tuple!"); + // We don't build the argv yet, as we want that to wrap a tuple with + // the 'lateness' arg added, and tuples are immutable, so we can't change + // it when we get called multiple times and need to change that last arg. + mObArgv = obArgv; + Py_INCREF(mObArgv); + // mArrayArgv remains empty + mLateness = 0; + } + ~nsPyTimeoutHandler() { + Py_XDECREF(mFunObj); + Py_XDECREF(mObArgv); + } + + virtual const PRUnichar *GetHandlerText() { + return mExpr.get(); + } + virtual void *GetScriptObject() { + return mFunObj; + } + virtual void GetLocation(const char **aFileName, PRUint32 *aLineNo) { + *aFileName = mFileName.get(); + *aLineNo = mLineNo; + } + virtual nsIArray *GetArgv(); + + virtual PRUint32 GetScriptTypeID() { + return nsIProgrammingLanguage::PYTHON; + } + virtual PRUint32 GetScriptVersion() { + return 0; + } + + // Called by the timeout mechanism so the secret 'lateness' arg can be + // added. + virtual void SetLateness(PRIntervalTime aHowLate) { + mLateness = aHowLate; + } + + +private: + + // filename, line number of the script + nsCAutoString mFileName; + PRUint32 mLineNo; + PRIntervalTime mLateness; + + nsCOMPtr mArrayArgv; + PyObject *mObArgv; + // The Python expression or script object + nsAutoString mExpr; + PyObject *mFunObj; +}; + + +// QueryInterface implementation for nsPyTimeoutHandler +NS_INTERFACE_MAP_BEGIN(nsPyTimeoutHandler) + NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(nsPyTimeoutHandler) +NS_IMPL_RELEASE(nsPyTimeoutHandler) + + +nsIArray *nsPyTimeoutHandler::GetArgv() { + // Create a new tuple object with our original args, plus one for + // lateness. + // Using tuples means we can skip alot of error checking. + CEnterLeavePython _celp; + if (!PyTuple_Check(mObArgv)) { + NS_ERROR("Must be a tuple"); + return nsnull; + } + int argc_orig = PyTuple_Size(mObArgv); + PyObject *obNew = PyTuple_New(argc_orig+1); + for (int i=0;iQueryInterface(NS_GET_IID(nsIScriptTimeoutHandler), + NS_REINTERPRET_CAST(void **, aRet)); +} diff --git a/extensions/python/dom/test/Makefile.in b/extensions/python/dom/test/Makefile.in new file mode 100644 index 000000000000..5fbb78b792a6 --- /dev/null +++ b/extensions/python/dom/test/Makefile.in @@ -0,0 +1,47 @@ +# ***** 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 mozilla.org code +# +# The Initial Developer of the Original Code is mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Hammond: 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 ***** + +DEPTH =../../../.. + +DIRS = \ + pyxultest \ + $(NULL) + +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(topsrcdir)/config/rules.mk diff --git a/extensions/python/dom/test/pyxultest/Makefile.in b/extensions/python/dom/test/pyxultest/Makefile.in new file mode 100644 index 000000000000..50007f1eedc8 --- /dev/null +++ b/extensions/python/dom/test/pyxultest/Makefile.in @@ -0,0 +1,66 @@ +# ***** 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 mozilla.org code +# +# The Initial Developer of the Original Code is mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Hammond: 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 ***** + +# XULRunner stuff based on http://lxr.mozilla.org/seamonkey/source/xulrunner/examples/simple/Makefile.in +# However - much of this depends on being in the Mozilla source tree - see +# http://developer.mozilla.org/en/docs/pyxpcom for current details. + +DEPTH = ../../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = pyxultest + +ifdef MOZ_XUL_APP +XPI_NAME = pyxultest +# Do I really want these? +#USE_EXTENSION_MANIFEST = 1 +#NO_JAR_AUTO_REG = 1 +#INSTALL_EXTENSION_ID = pyxultest@mozilla.org +#XPI_PKGNAME = pyxultest-$(MOZ_APP_VERSION) +DIST_FILES = application.ini +endif + +PREF_JS_EXPORTS = $(srcdir)/pyxultest-prefs.js + +include $(topsrcdir)/config/rules.mk + +#xpi: +# zip -j $(DIST)/$(XPI_FILE) +# cd $(DIST); zip -r $(XPI_FILE) bin/chrome/pyxultest.jar diff --git a/extensions/python/dom/test/pyxultest/README.txt b/extensions/python/dom/test/pyxultest/README.txt new file mode 100644 index 000000000000..f9977157811b --- /dev/null +++ b/extensions/python/dom/test/pyxultest/README.txt @@ -0,0 +1,21 @@ +This is the test (and also sample) code for Python in XUL. + +* Configure and build Mozilla with --enable-extensions=python,... --enable-tests + + See http://developer.mozilla.org/en/docs/PyXPCOM for any issues regarding + the building of PyXPCOM itself. If PyXPCOM builds, this DOM extension + should too! + +* Start it! + + - From XULRunner, execute: + % dist/bin/xulrunner xpi-stage/pyxultest/application.ini + + - From Seamonkey (suite) execute: + % seamonkey -chrome chrome://pyxultest/content + + - From Firebox (browser) execute: + % firefox ???? (Try the same -chrome as for suite) + +Note that on Linux, in all cases you should prefix the command with +run-mozilla.sh, or any other magic normally necessary... diff --git a/extensions/python/dom/test/pyxultest/application.ini b/extensions/python/dom/test/pyxultest/application.ini new file mode 100644 index 000000000000..c25d7455a852 --- /dev/null +++ b/extensions/python/dom/test/pyxultest/application.ini @@ -0,0 +1,35 @@ +#filter substitution +; A XULRunner INI file. +; This based on http://lxr.mozilla.org/seamonkey/source/xulrunner/examples/simple/application.ini + +[App] +; This field specifies your organization's name. This field is recommended, +; but optional. +Vendor=mozilla.org + +; This field specifies your application's name. This field is required. +Name=pyxultest + +; This field specifies your application's version. This field is required. +Version=1.0 + +; This field specifies your application's build ID (timestamp). This field is +; required. +BuildID=@BUILD_ID@ + +; This field specifies a compact copyright notice for your application. This +; field is optional. +Copyright=Copyright (c) 2006 Mozilla.org + +; This ID is just an example. Every XUL app ought to have it's own unique ID. +ID={67da7954-ecd3-4153-be0f-1c02284296ce} + +[Gecko] +; This field is required. It specifies the minimum Gecko version that this +; application requires. +MinVersion=@MOZILLA_VERSION_U@ + +; This field is optional. It specifies the maximum Gecko version that this +; application requires. It should be specified if your application uses +; unfrozen interfaces. +; MaxVersion=@MOZILLA_VERSION_U@ diff --git a/extensions/python/dom/test/pyxultest/chrome/chrome.manifest b/extensions/python/dom/test/pyxultest/chrome/chrome.manifest new file mode 100644 index 000000000000..585b9de9a453 --- /dev/null +++ b/extensions/python/dom/test/pyxultest/chrome/chrome.manifest @@ -0,0 +1,3 @@ +# See http://developer.mozilla.org/en/docs/Chrome_Registration + +content pyxultest file:content/pyxultest/ diff --git a/extensions/python/dom/test/pyxultest/chrome/content/dialog.xul b/extensions/python/dom/test/pyxultest/chrome/content/dialog.xul new file mode 100644 index 000000000000..e902e8b0f819 --- /dev/null +++ b/extensions/python/dom/test/pyxultest/chrome/content/dialog.xul @@ -0,0 +1,25 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/extensions/python/dom/test/pyxultest/chrome/content/document_write.xul b/extensions/python/dom/test/pyxultest/chrome/content/document_write.xul new file mode 100644 index 000000000000..994d4b21c03b --- /dev/null +++ b/extensions/python/dom/test/pyxultest/chrome/content/document_write.xul @@ -0,0 +1,74 @@ + + + + + + + + + + + This test uses the open, write and close methods of a + document to construct a document. It tests the 'out-of-line' + capabilties of document.write i.e. the ability to use + document.write to create an entirely new document. + > + + + +