зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
fc083d22f5
Коммит
7027fc93ed
|
@ -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");
|
Загрузка…
Ссылка в новой задаче