First cut at the Python implementation of nsIScriptContext etc (see

http://wiki.mozilla.org/Breaking_the_grip_JS_has_on_the_DOM).

See also bug 255942 - "Support other scripting languages than JS".

Not part of the build - indeed this will not build at all until bug
255942 lands, or the latest patch there is applied.
This commit is contained in:
mhammond%skippinet.com.au 2006-04-29 01:56:45 +00:00
Родитель fc083d22f5
Коммит 7027fc93ed
37 изменённых файлов: 4450 добавлений и 0 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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.

Просмотреть файл

@ -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

Просмотреть файл

@ -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.

Просмотреть файл

@ -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 "<DOM object '%s' (%s)>" % (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 "<GlobalWindow outer=%s %s>" % (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 "<ScriptContext at %d>" % 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)

Просмотреть файл

@ -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()

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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()

Просмотреть файл

@ -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

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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<aArgCount;i++) {
PyList_SET_ITEM(argNames, i, PyString_FromString(aArgNames[i]));
}
PyObject *ret = PyObject_CallMethod(mDelegate, "CompileEventHandler",
"sNNsi",
AtomToEventHandlerName(aName),
argNames,
PyObject_FromNSString(aBody),
aURL, aLineNo);
if (!ret)
return HandlePythonError();
NS_ASSERTION(aHandler.getScriptTypeID()==nsIProgrammingLanguage::PYTHON,
"Expecting Python script object holder");
aHandler.set(ret);
Py_DECREF(ret);
return NS_OK;
}
nsresult
nsPythonContext::BindCompiledEventHandler(nsISupports *aTarget, void *aScope,
nsIAtom *aName, void *aHandler)
{
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 *obTarget;
obTarget = PyObject_FromNSDOMInterface(mDelegate, aTarget);
if (!obTarget)
return HandlePythonError();
PyObject *ret = PyObject_CallMethod(mDelegate, "BindCompiledEventHandler",
"NOsO",
obTarget, aScope,
AtomToEventHandlerName(aName),
aHandler);
Py_XDECREF(ret);
return HandlePythonError();
}
nsresult
nsPythonContext::CompileFunction(void* aTarget,
const nsACString& aName,
PRUint32 aArgCount,
const char** aArgArray,
const nsAString& aBody,
const char* aURL,
PRUint32 aLineNo,
PRBool aShared,
void** aFunctionObject)
{
NS_TIMELINE_MARK_FUNCTION("nsPythonContext::CompileFunction");
NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
NS_ERROR("CompileFunction not implemented");
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsPythonContext::CallEventHandler(nsISupports *aTarget, void *aScope, void* aHandler,
nsIArray *aargv, nsIVariant **arv)
{
NS_TIMELINE_MARK_FUNCTION("nsPythonContext::CallEventHandler");
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 *obTarget;
obTarget = PyObject_FromNSDOMInterface(mDelegate, aTarget);
if (!obTarget)
return HandlePythonError();
// See if our argv was originally supplied by Python, and if so, just pass
// the original args.
PyObject *obArgv = NULL;
nsCOMPtr<nsIPyArgArray> 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;
}

Просмотреть файл

@ -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.
};

Просмотреть файл

@ -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__

Просмотреть файл

@ -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<nsISupports> 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;

Просмотреть файл

@ -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 = "<python JSExec script>";
PRUint32 lineNo = 0;
PRUint32 version = 0;
if (!PyArg_ParseTuple(args, "Os|sii", &obGlobal, &code, &url, &lineNo, &version))
return NULL;
nsCOMPtr<nsISupports> isup;
if (!Py_nsISupports::InterfaceFromPyObject(
obGlobal,
NS_GET_IID(nsISupports),
getter_AddRefs(isup),
PR_FALSE, PR_FALSE))
return NULL;
nsCOMPtr<nsIScriptGlobalObject> 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<nsIScriptObjectPrincipal> 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<nsPIDOMWindow> 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<nsPIDOMWindow> 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<nsIScriptGlobalObject> 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<nsIScriptGlobalObject> 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<nsPIDOMWindow> target;
if (!Py_nsISupports::InterfaceFromPyObject(
obTarget,
NS_GET_IID(nsPIDOMWindow),
getter_AddRefs(target),
PR_FALSE, PR_FALSE))
return NULL;
nsCOMPtr<nsIScriptTimeoutHandler> 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<nsPIDOMWindow> 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<nsISupports> 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<nsIDOMEventReceiver> receiver(do_QueryInterface(target));
if (!receiver) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIEventListenerManager> manager;
receiver->GetListenerManager(PR_TRUE, getter_AddRefs(manager));
if (!manager) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIAtom> 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<nsISupports> isup;
if (!Py_nsISupports::InterfaceFromPyObject(
obGlobal,
NS_GET_IID(nsISupports),
getter_AddRefs(isup),
PR_FALSE, PR_FALSE))
return NULL;
nsCOMPtr<nsIScriptGlobalObject> 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<nsISupports> 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<nsPIDOMWindow> win(do_QueryInterface(target));
if (win != nsnull && win->IsOuterWindow()) {
target = win->GetCurrentInnerWindow();
}
// The receiver, to get the manager.
nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(target));
if (!receiver) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIEventListenerManager> manager;
receiver->GetListenerManager(PR_TRUE, getter_AddRefs(manager));
if (!manager) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIAtom> 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<nsISupports> isup;
if (!Py_nsISupports::InterfaceFromPyObject(
obGlobal,
NS_GET_IID(nsISupports),
getter_AddRefs(isup),
PR_FALSE, PR_FALSE))
return NULL;
nsCOMPtr<nsIScriptGlobalObject> 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<nsISupports> 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<nsPIDOMWindow> win(do_QueryInterface(target));
if (win != nsnull && win->IsOuterWindow()) {
target = win->GetCurrentInnerWindow();
}
// The receiver, to get the manager.
nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(target));
if (!receiver) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIEventListenerManager> manager;
receiver->GetListenerManager(PR_TRUE, getter_AddRefs(manager));
if (!manager) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIAtom> 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<nsIArray> 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<nsISupports> 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);
}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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);
};

Просмотреть файл

@ -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<nsIArray> 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;i<argc_orig;i++) {
PyTuple_SET_ITEM(obNew, i, PyTuple_GET_ITEM(mObArgv, i));
Py_INCREF(PyTuple_GET_ITEM(obNew, i));
}
PyTuple_SET_ITEM(obNew, argc_orig, PyInt_FromLong(mLateness));
nsresult nr = NS_CreatePyArgv(obNew, getter_AddRefs(mArrayArgv));
NS_ASSERTION(NS_SUCCEEDED(nr), "Failed to create argv!?");
Py_DECREF(obNew); // reference held by mArrayArgv
return mArrayArgv;
}
// And our factory.
nsresult CreatePyTimeoutHandler(const nsAString &aExpr,
PyObject *aFunObj, PyObject *obArgs,
nsIScriptTimeoutHandler **aRet) {
*aRet = new nsPyTimeoutHandler(aExpr, aFunObj, obArgs);
if (!aRet)
return NS_ERROR_OUT_OF_MEMORY;
return (*aRet)->QueryInterface(NS_GET_IID(nsIScriptTimeoutHandler),
NS_REINTERPRET_CAST(void **, aRet));
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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...

Просмотреть файл

@ -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@

Просмотреть файл

@ -0,0 +1,3 @@
# See http://developer.mozilla.org/en/docs/Chrome_Registration
content pyxultest file:content/pyxultest/

Просмотреть файл

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!-- note: nsXBLPrototypeHandler::ExecuteHandler is still JS specific,
so 'ondialogaccept' handler does not execute Python
-->
<dialog
script-type="application/x-python"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="myDialog" orient="vertical"
buttons="accept,cancel"
ondialogaccept="dump('hello')"
>
<script>
<![CDATA[
dump("Dialog location is %s\n", window.location.href)
]]>
</script>
<description value="Select a button"/>
</dialog>

Просмотреть файл

@ -0,0 +1,74 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
<!-- taken from dom/tests/js/write2.html -->
<window
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="documentwrite" orient="vertical"
script-type="application/x-python"
title="document.write test">
<script>
<![CDATA[
w = None
count = 0
isOpen = False
def newWin():
global w
if w is None or w.closed:
w = window.open("about:blank", "writetest")
print "doc is", w.document
def incrWrite():
global count, isOpen
if w is not None:
if not isOpen:
count = 0
isOpen = True
w.document.nsIDOMHTMLDocument.write("<p>Opening document and counting up....</p>")
w.document.nsIDOMHTMLDocument.write("<p>Counter at: " + count++ + "</p>")
def closeDoc():
global isOpen
if w is not None and isOpen:
w.document.write("<p>Closing document!</p>")
w.document.close()
isOpen = false;
]]>
</script>
<description>
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.
</description>>
<box>
<text class="label" value="
Use this button to create a new window. If one already
exists, we'll use it."/>
<button label="New Window" onclick="newWin(); return True"/>
</box>
<box>
<description>
Use this button to write the new value of a
counter into the document. If the document was previously closed, it will be
reopened (and the old contents will be destroyed.)
</description>
<button label="Write" onclick="incrWrite(); return True"/>
</box>
<box>
<description>
Use this button to close the document. Subsequent writes will
rewrite the document.
</description>
<button label="Close Document" onclick="closeDoc(); return True"/>
</box>
</window>

Просмотреть файл

@ -0,0 +1,15 @@
<html>
<head>
<meta http-equiv="Content-Script-Type" content="application/x-python"/>
<script>
print "Hello from Python inside the HTML, from document", document.location.href
</script>
</head>
<body>
<p>This is HTML content inside the XUL. Please
<a href="foo" onmouseover="print 'hello from Python'">
click on this link.
</a>
</p>
</body>
</html>

Просмотреть файл

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<overlay id="js_overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<hbox id="language_box">
<button id="but_js" label="click for js"
oncommand="write('hello from js\n');"/>
</hbox>
<!-- The intent here is to test a XUL element merged from multiple
overlays, each with a different default script language, and each
setting a simple event handler string.
The Python version uses 'oncommand', this JS version uses 'onclick'
-->
<button id="but_multi"
onclick="write('hello from the js click event\n');"/>
<commandset id="cmdset">
<command id="menu-file-test-js:command"
oncommand="write('You selected a command implemented in JS\n');"/>
</commandset>
</overlay>

Просмотреть файл

@ -0,0 +1,241 @@
"""
Runs 'automated' tests - generally anything which can be tested without
visual confirmation of the results.
This is generally executed by the main pyxultest chrome, passing the name
of a test to run. If the test succeeds, the window closes before it is
shown - hence the requirement that only things that don't need visual
confirmation.
For testing purposes, you can specify:
-chrome chrome://pyxultest/content/pytester.xul -test test_name
And have a specific test executed from the command-line.
Executing without a -test option will print the name of all tests, then
execute one at random.
"""
import sys
from xpcom import components
import xpcom
close_on_success = True
# Utility functions
# Write something to the textbox - eg, error or trace messages.
def write( msg, *args):
tb = document.getElementById("output-box")
tb.value = tb.value + (msg % args) + "\n"
def success():
# This gets called if the test works. If it is not called, it must not
# have worked :)
write("The test worked!")
if close_on_success:
dump("Closing the window - nothing interesting to see here!")
window.close()
def cause_error(expect_cancel = True):
write("Raising an error")
if expect_cancel:
raise RuntimeError, \
"This error was cancelled - you should not have seen it!"
raise RuntimeError, \
"Testing an unhandled exception - you should see this!"
def handle_error_return_cancel(errMsg, source, lineno):
success()
return True # cancel the error
# This is set as an event handler, but it has the wrong number of args. It
# should still work, a-la js.
def handle_error_wrong_args(event):
success()
return True # cancel this error
def handle_error_no_args():
success()
def test_error_explicit():
"Test an explicit assignment of a function object to onerror"
# Assign a function to an onerror attribute
write("Assigning a function to window.onerror")
window.onerror = handle_error_return_cancel
cause_error()
def test_error_explicit_string():
"Test an explicit assignment of a string to onerror"
# Assign a string to an onerror attribute
write("Assigning a string to window.onerror")
window.onerror = "return handle_error_return_cancel(event, source, lineno)"
cause_error()
def test_error_explicit_no_cancel():
"""Test an error we don't handle"""
# NOTE: No 'return' - so we return None - ie, not cancelled.
window.onerror = "handle_error_wrong_args(event)"
cause_error(False)
def test_wrong_event_args():
"Test an error handler with too few args"
# Assign a string to an onerror attribute
write("Assigning a function taking only 1 arg to window.onerror")
window.onerror = handle_error_wrong_args
cause_error()
# A test class for use with addEventListener.
class EventListener:
_com_interfaces_ = components.interfaces.nsIDOMEventListener
def handleEvent(self, event):
write("nsIDOMEventListener caught the event")
success()
# return value ignored for handleEvent - must call preventDefault.
event.preventDefault()
def test_error_eventlistener_object():
"""Test we can hook the error with our own nsIDOMEventListener"""
write("Calling addEventListener with an nsIDOMEventListener instance")
window.addEventListener("error", EventListener(), False)
cause_error()
def handle_error_explicit_cancel(event):
success()
event.preventDefault()
# addEventListener for a string and function - these use the EventListener
# impl in nsdom.context
def test_error_eventlistener_function():
"""Test we can hook the error by passing addEventListener a function"""
write("Calling addEventListener with a function object")
window.addEventListener("error", handle_error_explicit_cancel, False)
cause_error()
def test_error_eventlistener_function_noargs():
"""Test we can addEventListener a function taking no args"""
write("Calling addEventListener with a function object")
window.addEventListener("error", handle_error_no_args, False)
# handle_error_no_args can't cancel the event as it is not passed - the
# return value is ignored for addEventListener functions.
cause_error(False)
def test_error_eventlistener_string():
"""Test we can hook the error by passing addEventListener a string"""
write("Calling addEventListener with a string")
window.addEventListener("error", "event.preventDefault();success()", False)
cause_error()
def find_tests():
ret = {}
for name, val in globals().items():
if callable(val) and name.startswith("test") and val.__doc__:
ret[name] = val
return ret
#
# Timeout and Interval tests.
#
timer_id = None
_interval_counter = 0
def interval_func():
global _interval_counter
_interval_counter += 1
if _interval_counter > 1:
window.clearInterval(timer_id)
write("Interval timer fired twice - looks good!")
success()
def test_interval_func():
"Test setInterval function"
global timer_id
write("Assigning an interval using a function")
timer_id = window.setInterval(interval_func, 100)
def timer_func(arg1, arg2):
# Also checks we can pass arbitrary Python objects.
if arg1 == "hello" and arg2 is test_timeout_func:
success()
else:
write("Args seemed wrong - test failed!")
def timer_func_lateness(lateness):
# Also checks we can pass arbitrary Python objects.
write("lateness is %r", lateness)
# Check value reasonbleness - we should always be an int and never more
# than a second late.
if type(lateness) == type(0) and lateness < 1000:
success()
else:
write("Eeek - lateness is %r", lateness)
def test_timeout_func():
"Test setTimeout function and general arg handling"
write("Assigning an interval using a function")
timer_id = window.setTimeout(100, timer_func, "hello", test_timeout_func)
def test_timeout_string():
"Test setTimeout function with a string"
write("Assigning an interval using a function")
timer_id = window.setTimeout(100, "write('The timeout string fired'); success()")
def test_timeout_func_lateness():
"Test setTimeout function handling of 'lateness' arg"
write("Assigning an interval using a function")
timer_id = window.setTimeout(100, timer_func_lateness)
# The general event handlers and test runner code.
def do_onload(event):
global close_on_success
klasses = []
# window.arguments may specify the name of a test to run - this is the
# case when opened from our main XUL demo form.
# When this form is executed directly via '--chrome ...', for some reason
# the nsAppRunner always passes an empty string as an arg.
# So we detect this, and try to find a '-test' option specifying
# the test to run.
tests = find_tests()
func_name = None
if len(window.arguments)==1 and not window.arguments[0]:
try:
# This is an embedding component - it may not always be there.
contract_id = "@mozilla.org/app-startup/commandLineService;1"
cmdline_svc = components.classes[contract_id] \
.getService(components.interfaces.nsICmdLineService)
func_name = cmdline_svc.getCmdLineValue("-test")
if func_name is None:
write("You can specify '-test test_name' on the command-line" \
" to specify a test to run")
write("The following tests can be specified:")
names = tests.keys()
names.sort()
for name in names:
write(" " + name)
close_on_success = False
except xpcom.COMException:
write("No command-line service - can't check for cmdline test")
elif len(window.arguments) and window.arguments[0]:
func_name = window.arguments[0]
func = None
if func_name:
try:
func = tests[func_name]
except KeyError:
write("*** No such test '%s' - running default test" % func_name)
write("The following tests can be specified:")
for name in tests:
write(" " + name)
func = None
if func is None:
close_on_success = False
import random
func = random.choice(tests.values())
write("Running random test '%s'" % (func.func_name,))
if len(window.arguments)>1 and window.arguments[1]=="-k":
close_on_success = False
if func:
write("%s: %s" % (func.__name__, func.__doc__ or func.__name__))
func()

Просмотреть файл

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!-- A container for general test code. Relies on being passed an argument
for which test to run. Generally it runs the test and closes, only
staying open when something goes wrong.
-->
<window
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="main-window" orient="vertical"
screenX="100" screenY="100"
height="300" width="500"
script-type="application/x-python"
onload="do_onload(event)"
onunload="dump('The test runner form is unloading')"
title="Test Runner"
>
<script src="chrome://pyxultest/content/pytester.py"/>
<textbox id="output-box" style="width:100%" value = ""
rows="15" multiline="true" flex="1"
/>
</window>

Просмотреть файл

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<overlay id="python_overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
script-type="application/x-python"
>
<hbox id="language_box">
<button id="but_python" label="click for Python"
oncommand="write('hello from Python')"/>
</hbox>
<!-- The intent here is to test a XUL element merged from multiple
overlays, each with a different default script language, and each
setting a simple event handler string.
This Python version uses 'oncommand', the JS version uses 'onclick'
-->
<button id="but_multi"
oncommand="write('hello from Python oncommand')"/>
<commandset id="cmdset">
<command id="menu-file-test-python:command"
oncommand="write('You selected a command implemented in Python');"/>
</commandset>
</overlay>

Просмотреть файл

@ -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 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 ***** */
/* nothing to see here! */

Просмотреть файл

@ -0,0 +1,129 @@
# The main script for the top-level pyxultest chrome.
import sys
import xpcom
from xpcom import components
import nsdom
# Utility functions
# Write something to the textbox - eg, error or trace messages.
def write( msg, *args):
tb = document.getElementById("output_box")
tb.value = tb.value + (msg % args) + "\n"
# An event listener class to test explicit hooking of events via objects.
# Note Python can now just call addEventListener a-la JS
class EventListener:
_com_interfaces_ = components.interfaces.nsIDOMEventListener
def __init__(self, handler, globs = None):
try:
self.co = handler.func_code
except AttributeError:
self.co = compile(handler, "inline script", "exec")
self.globals = globs or globals()
def handleEvent(self, event):
exec self.co in self.globals
# A timer function
timer_id = None
timer_count = 0
def on_timer(max):
global timer_count, timer_id
assert timer_id is not None, "We must have a timer id!"
change_image() # pick a random image
timer_count += 1
if timer_count >= max:
write("Stopping the image timer (but clicking it will still change it)")
window.clearTimeout(timer_id)
time_id = None
# An event function to handle onload - but hooked up manually rather than via
# having the magic name 'onload'
def do_load():
input = document.getElementById("output_box")
# Clear the text in the XUL
input.value = ""
write("This is the Python on XUL demo using\nPython %s", sys.version)
# hook up a click event using addEventListener
button = document.getElementById("but_dialog")
# Note the handler code is executed in the global scope of the targer, *not* the window.
# Therefore objects in the global namespace must be prefixed with "window"
button.addEventListener('click', 'write("hello from the click event for the dialog button")', False)
# Test 'expandos' - set a custom attribute on a node, and check that
# when a click event fires on it, the value is still there.
button = document.getElementById("some-button")
button.custom_value = "Python"
# The event-handler.
def check_expando(event):
write("The custom value is %s", event.target.custom_value)
if event.target.custom_value != "Python":
write("but it is wrong!!!")
button.addEventListener('click', check_expando, False)
# And a 2 second 'interval' timer to change the image.
global timer_id
assert timer_id is None, "Already have a timer - event fired twice??"
timer_id = window.setInterval(on_timer, 2000, 10)
# Add an event listener as a function
window.addEventListener('load', do_load, False)
# Add another one just to test passing a string instead of a function.
window.addEventListener('load', "dump('hello from string event handler')", False)
# And yet another with an explicit EventListener instance.
window.addEventListener('load', EventListener('dump("hello from an object event handler")'), False)
# Some other little functions called by the chrome
def on_but_dialog_click():
write("Button clicked from %s", window.location.href)
w = window.open("chrome://pyxultest/content/dialog.xul", "myDialog", "chrome")
def do_textbox_keypress(event):
if event.keyCode==13:
val = event.target.value
if val:
write('You wrote: %s', val)
else:
write("You wrote nothing!")
event.target.value = ""
# An on-click handler for the image control
def change_image():
image = document.getElementById("image-python");
import random
num = random.randrange(64) + 1
url = "http://www.python.org/pics/PyBanner%03d.gif" % num
image.src = url
def run_tests():
# I wish I could reach into the window and get all tests!
tests = """
test_error_eventlistener_function
test_error_eventlistener_function_noargs
test_error_eventlistener_object
test_error_eventlistener_string
test_error_explicit
test_error_explicit_no_cancel
test_error_explicit_string
test_interval_func
test_timeout_func
test_timeout_func_lateness
test_timeout_string
test_wrong_event_args
""".split()
keep_open = document.getElementById("keep_tests_open").getAttribute("checked")
for test in tests:
write("Running test %s", test)
if keep_open:
args = (test, "-k")
else:
args = (test,)
window.openDialog("chrome://pyxultest/content/pytester.xul", "testDialog", "modal", *args)
if keep_open:
write("Ran all the tests - the windows told you if the tests worked")
else:
write("Ran all the tests - if no window stayed open, it worked!")

Просмотреть файл

@ -0,0 +1,164 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xul-overlay href="chrome://pyxultest/content/js_overlay.xul"?>
<?xul-overlay href="chrome://pyxultest/content/python_overlay.xul"?>
<window
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="mainWindow" orient="vertical"
screenX="100" screenY="100"
height="450" width="600"
script-type="application/x-python"
onload="write('hello from the inline window load event')"
title="Python in XUL demos and tests"
>
<script type="application/x-python" src="chrome://pyxultest/content/pyxultest.py"/>
<script>
<![CDATA[
dump("Scrollbar visible = %s", window.scrollbars.visible)
dump("Window location is %s", window.location.href)
#promptService = xpcom.components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(xpcom.components.interfaces.nsIPromptService)
#promptService.alert(window, "title", "Hello from Python");
]]>
</script>
<script type="application/javascript">
// a 'write' function that writes to our window - like the Python one
// although this version does *not* auto-append a '\n' char. This keeps
// 'write' like 'dump', but also serves as a useful test that the wrong
// language isn't accidently being used.
function write(msg) {
tb = document.getElementById("output_box");
tb.value = tb.value + msg;
}
</script>
<!-- This commandset drags in commands from our overlays, each
implemented in different languages
-->
<commandset id="cmdset">
<!-- and one define inline -->
<command id="menu-file-close:command"
oncommand="print 'Python closing the window';window.close()"/>
</commandset>
<!-- this keyset and menu reference commands implemented in multiple
languages
-->
<keyset id="mainkeys">
<key
id = "menu-file-close:key"
key = "q"
observes = "menu-file-close:command"
modifiers = "accel" />
<key
id = "menu-file-test-js:key"
key = "j"
observes = "menu-file-test-js:command"
modifiers = "accel" />
<key
id = "menu-file-test-python:key"
key = "p"
observes = "menu-file-test-python:command"
modifiers = "accel" />
</keyset>
<toolbox id="main-toolbox">
<menubar id="menu">
<menu id="menu-file" label="File" accesskey="f">
<menupopup id="menu-file-popup">
<menuitem
id = "menu-file-test-js"
key = "menu-file-test-js:key"
label = "Test JS"
command = "menu-file-test-js:command"
accesskey = "J"/>
<menuitem
id = "menu-file-test-python"
key = "menu-file-test-python:key"
label = "Test Python"
command = "menu-file-test-python:command"
accesskey = "P"/>
<menuitem
id = "menu-file-test-inline"
key = "menu-file-test-inline:key"
label = "Test Inline menu handler"
oncommand = "write('Hello from an inline menu handler')"
accesskey = "I"/>
<menuitem
id = "menu-file-close"
key = "menu-file-close:key"
label = "Close"
command = "menu-file-close:command"
accesskey = "C"/>
</menupopup>
</menu>
</menubar>
</toolbox>
<textbox id="output_box" style="width:100%"
value = "If you can see this, things aren't working well!"
rows="15" multiline="true" flex="1"
/>
<!--
<iframe src="chrome://pyxultest/content/html_content.html" height="30"/>
-->
<groupbox>
<caption label="Items from Overlays"/>
<!-- A box from our overlay - it will have one button using js events
and another using Python events -->
<hbox>
<hbox id="language_box"/>
<button id="but_multi" label="click for both"/>
</hbox>
</groupbox>
<hbox>
<groupbox>
<caption label="Random Buttons"/>
<hbox>
<!-- Here we have a script element defined at a parent node -->
<vbox oncommand="write('You clicked on a %s with id=%s and type=%s' % (event.target.tagName, event.target.id, event.target.type))">
<hbox>
<button id="some-button" label = "click here"/>
<button id="another-button" label = "or here"/>
<!-- a button that overrides script-type and oncommand in a different language -->
<button id="js-button" label = "JS"
script-type="application/javascript"
oncommand="write('This is a JS button with ID ' + event.target.id + '\n');"
/>
</hbox>
<button type="checkbox" autocheck="1" id="some-checkbox" label="Click on this checkbox too"/>
</vbox>
<button id="but_dialog" label="open dialog"
oncommand="on_but_dialog_click()"/>
</hbox>
</groupbox>
<groupbox flex="1">
<caption label="Key Event Tests"/>
<textbox id="input_test_intrinsic" value="Type something and press Enter"
onkeypress="return do_textbox_keypress(event)"/>
</groupbox>
</hbox>
<groupbox>
<caption label="Automated tests"/>
<hbox>
<button id="test_error" label="Run tests" oncommand="run_tests()"/>
<vbox flex="1">
<text class="label"
value="The test window is not shown (or closes quickly) if the tests work"/>
<checkbox id="keep_tests_open" label="Always keep test window open"/>
</vbox>
<image id="image-python" src="http://www.python.org/pics/PyBanner011.gif"
width="150" height="35" flex="0"
onclick="change_image()"/>
</hbox>
</groupbox>
</window>

Просмотреть файл

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<!-- This file remains only for the "suite" (seamonkey). According to
http://developer.mozilla.org/en/docs/Chrome_Registration, as at Moz 1.8,
this file is *not* necessary for Firefox or xulrunner.
todo: Remove this file as soon as seamonkey gets up to speed -
this sample is *not* about backwards compat etc - it should remain
as small as possible, focusing on only Python specific details
-->
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
<RDF:Seq about="urn:mozilla:package:root">
<RDF:li resource="urn:mozilla:package:pyxultest"/>
</RDF:Seq>
<RDF:Description about="urn:mozilla:package:pyxultest"
chrome:displayName="Python in XUL test"
chrome:author="Mark Hammond"
chrome:name="pyxultest">
</RDF:Description>
</RDF:RDF>

Просмотреть файл

@ -0,0 +1,27 @@
# JAR manifest for the pyxultest chrome
# http://developer.mozilla.org/en/docs/jar.mn tells me that this file is
# only used by the Mozilla build process (although I'm not sure exactly now!)
# As most apps will be outside the mozilla tree and presumably have their
# own build process, they probably don't need this file.
#
# Clarifications and simplifications welcome!
pyxultest.jar:
#ifdef MOZ_XUL_APP
## A xulrunner app
% content pyxultest %content/pyxultest/
#else
## Old style manifest.
content/pyxultest/contents.rdf (chrome/contents.rdf)
#endif
content/pyxultest/pyxultest.xul (chrome/content/pyxultest.xul)
content/pyxultest/pyxultest.py (chrome/content/pyxultest.py)
content/pyxultest/pyxultest.css (chrome/content/pyxultest.css)
content/pyxultest/dialog.xul (chrome/content/dialog.xul)
content/pyxultest/python_overlay.xul (chrome/content/python_overlay.xul)
content/pyxultest/js_overlay.xul (chrome/content/js_overlay.xul)
content/pyxultest/pytester.xul (chrome/content/pytester.xul)
content/pyxultest/pytester.py (chrome/content/pytester.py)
content/pyxultest/html_content.html (chrome/content/html_content.html)

Просмотреть файл

@ -0,0 +1 @@
pref("toolkit.defaultChromeURI", "chrome://pyxultest/content/pyxultest.xul");