2007-03-22 10:30:00 -07:00
|
|
|
# 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):
|
|
|
|
# '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
|
|
|
|
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 necessary.
|
|
|
|
if self.globals is not None:
|
|
|
|
f = new.function(self.func.func_code, self.globals,
|
|
|
|
self.func.func_name, self.func.func_closure)
|
|
|
|
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 separate 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("setting expando %s.%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, globs=None):
|
|
|
|
# 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.
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
2008-02-15 21:13:16 -08:00
|
|
|
def CompileEventHandler(self, name, argNames, body, url, lineno, version):
|
2007-03-22 10:30:00 -07:00
|
|
|
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 their real globals (not a copy) so
|
|
|
|
# it can set global vars!
|
|
|
|
# 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 handler(*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)
|