Correct the semantics of Python global variables in event handlers.

This commit is contained in:
mhammond%skippinet.com.au 2006-10-26 05:23:08 +00:00
Родитель 85f4af9d5d
Коммит b2ade93836
2 изменённых файлов: 36 добавлений и 17 удалений

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

@ -66,7 +66,9 @@ def dump(fmt, *args):
# 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):
def __init__(self, context, evt_name, handler, globs):
# 'globs' are the globals the event handler will use, or None
# if the handler should use the globals it was compiled with.
self.context = context
if callable(handler):
self.func = handler
@ -90,7 +92,7 @@ class EventListener:
def handleEvent(self, event):
# Although handler is already a function object, we must re-bind to
# new globals
# new globals if necessary.
if self.globals is not None:
f = new.function(self.func.func_code, self.globals,
self.func.func_name, self.func.func_closure)
@ -147,7 +149,7 @@ class WrappedNative(Component):
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",
logger.debug("setting expando %s.%r=%r for object %r",
self, attr, value, self._comobj_)
# and register if an event.
if attr.startswith("on"):
@ -185,7 +187,7 @@ class WrappedNative(Component):
else:
logger.info("Not a DOM element - not hooking extra interfaces")
def addEventListener(self, event, handler, useCapture=False):
def addEventListener(self, event, handler, useCapture=False, globs=None):
# We need to transform string or function objects into
# nsIDOMEventListener interfaces.
logger.debug("addEventListener for %r, event=%r, handler=%r, cap=%s",
@ -194,11 +196,16 @@ class WrappedNative(Component):
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)
# Handle semantics for event handler globals:
# * If a function, then function globals are used, as per normal.
# * If a string object and no explicit globals param, use the
# caller's globals
if globs is None and not callable(handler):
globs = sys._getframe().f_back.f_globals
handler = EventListener(self._context_, event, handler, globs)
else:
assert not globs, "can't specify a handler instance and globals"
base = self.__getattr__('addEventListener')
base(event, handler, useCapture)
@ -439,27 +446,36 @@ class ScriptContext:
return domcompile.compile(text, url, lineno=lineno-1)
def CompileEventHandler(self, name, argNames, body, url, lineno):
if __debug__:
logger.debug("%s.CompileEventHandler %s %s:%s ('%s')",
self, name, url, lineno, body[:100])
co = domcompile.compile_function(body, url, name, argNames,
lineno=lineno-1)
g = {}
exec co in g
handler = g[name]
# Flag that this function started life as inline script code,
# so we later ensure the correct globals are used when the event fires
handler._nsdom_compiled = True
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
# Event handlers must fire in their 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)
# If this was an inline event handler, re-bind to the window's globals.
if hasattr(handler, '_nsdom_compiled'):
globs = scope
# re-bind to new globals
f = new.function(handler.func_code, globs, handler.func_name,
handler.func_defaults)
handler = f
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)
return handler(*args)
def BindCompiledEventHandler(self, target, scope, name, handler):
if __debug__:

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

@ -197,9 +197,12 @@ def do_onload(event):
# the test to run.
tests = find_tests()
func_name = None
if len(window.arguments)==1 and not window.arguments[0]:
# Only 'dialogs' have window arguments, not 'windows'
if not hasattr(window, 'arguments') or \
(len(window.arguments)==1 and not window.arguments[0]):
try:
# This is an embedding component - it may not always be there.
# ACK - and is no longer available with firefox...
contract_id = "@mozilla.org/app-startup/commandLineService;1"
cmdline_svc = components.classes[contract_id] \
.getService(components.interfaces.nsICmdLineService)