auto merge of #2204 : jdm/servo/inlineev, r=Ms2ger

Fixes #1238.
This commit is contained in:
bors-servo 2014-05-27 14:49:21 -04:00
commit 0886a36b5d
25 changed files with 728 additions and 180 deletions

View file

@ -2,13 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::trace::trace_object; use dom::bindings::js::JSRef;
use dom::bindings::utils::Reflectable; use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, global_object_for_js_object};
use js::jsapi::{JSContext, JSObject, JS_WrapObject, JS_ObjectIsCallable}; use js::jsapi::{JSContext, JSObject, JS_WrapObject, JS_ObjectIsCallable};
use js::jsapi::{JS_GetProperty, JSTracer}; use js::jsapi::JS_GetProperty;
use js::jsval::{JSVal, UndefinedValue}; use js::jsval::{JSVal, UndefinedValue};
use std::cast;
use std::ptr; use std::ptr;
use serialize::{Encodable, Encoder}; use serialize::{Encodable, Encoder};
@ -24,42 +24,61 @@ pub enum ExceptionHandling {
RethrowExceptions RethrowExceptions
} }
#[deriving(Clone,Eq)] #[deriving(Clone,Eq,Encodable)]
pub struct CallbackInterface { pub struct CallbackFunction {
pub callback: *mut JSObject object: CallbackObject
} }
impl<S: Encoder<E>, E> Encodable<S, E> for CallbackInterface { impl CallbackFunction {
fn encode(&self, s: &mut S) -> Result<(), E> { pub fn new(callback: *mut JSObject) -> CallbackFunction {
unsafe { CallbackFunction {
let tracer: *mut JSTracer = cast::transmute(s); object: CallbackObject {
trace_object(tracer, "callback", self.callback); callback: Traceable::new(callback)
} }
Ok(())
} }
}
}
#[deriving(Clone,Eq,Encodable)]
pub struct CallbackInterface {
object: CallbackObject
}
#[deriving(Clone,Eq,Encodable)]
struct CallbackObject {
callback: Traceable<*mut JSObject>,
} }
pub trait CallbackContainer { pub trait CallbackContainer {
fn new(callback: *mut JSObject) -> Self;
fn callback(&self) -> *mut JSObject; fn callback(&self) -> *mut JSObject;
} }
impl CallbackContainer for CallbackInterface { impl CallbackInterface {
fn callback(&self) -> *mut JSObject { pub fn callback(&self) -> *mut JSObject {
self.callback *self.object.callback
}
}
impl CallbackFunction {
pub fn callback(&self) -> *mut JSObject {
*self.object.callback
} }
} }
impl CallbackInterface { impl CallbackInterface {
pub fn new(callback: *mut JSObject) -> CallbackInterface { pub fn new(callback: *mut JSObject) -> CallbackInterface {
CallbackInterface { CallbackInterface {
callback: callback object: CallbackObject {
callback: Traceable::new(callback)
}
} }
} }
pub fn GetCallableProperty(&self, cx: *mut JSContext, name: &str) -> Result<JSVal, ()> { pub fn GetCallableProperty(&self, cx: *mut JSContext, name: &str) -> Result<JSVal, ()> {
let mut callable = UndefinedValue(); let mut callable = UndefinedValue();
unsafe { unsafe {
if name.to_c_str().with_ref(|name| JS_GetProperty(cx, self.callback, name, &mut callable)) == 0 { if name.to_c_str().with_ref(|name| JS_GetProperty(cx, self.callback(), name, &mut callable)) == 0 {
return Err(()); return Err(());
} }
@ -73,14 +92,9 @@ impl CallbackInterface {
} }
} }
pub fn GetJSObjectFromCallback<T: CallbackContainer>(callback: &T) -> *mut JSObject { pub fn WrapCallThisObject<T: Reflectable>(cx: *mut JSContext,
callback.callback() p: &JSRef<T>) -> *mut JSObject {
} let mut obj = p.reflector().get_jsobject();
pub fn WrapCallThisObject<T: 'static + CallbackContainer + Reflectable>(cx: *mut JSContext,
_scope: *mut JSObject,
p: Box<T>) -> *mut JSObject {
let mut obj = GetJSObjectFromCallback(p);
assert!(obj.is_not_null()); assert!(obj.is_not_null());
unsafe { unsafe {
@ -98,7 +112,9 @@ pub struct CallSetup {
} }
impl CallSetup { impl CallSetup {
pub fn new(cx: *mut JSContext, handling: ExceptionHandling) -> CallSetup { pub fn new<T: CallbackContainer>(callback: &T, handling: ExceptionHandling) -> CallSetup {
let win = global_object_for_js_object(callback.callback()).root();
let cx = win.deref().get_cx();
CallSetup { CallSetup {
cx: cx, cx: cx,
handling: handling handling: handling

View file

@ -34,6 +34,7 @@ DOMInterfaces = {
'DOMParser': {}, 'DOMParser': {},
'Element': {}, 'Element': {},
'Event': {}, 'Event': {},
'EventHandler': {},
'EventListener': { 'EventListener': {
'nativeType': 'EventListenerBinding::EventListener', 'nativeType': 'EventListenerBinding::EventListener',
}, },
@ -133,4 +134,3 @@ DOMInterfaces = {
'TestBinding': {}, 'TestBinding': {},
} }

View file

@ -7,9 +7,10 @@
import os import os
import string import string
import operator import operator
import itertools
from WebIDL import * from WebIDL import *
from Configuration import Descriptor from Configuration import getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor
AUTOGENERATED_WARNING_COMMENT = \ AUTOGENERATED_WARNING_COMMENT = \
"/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n" "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
@ -406,6 +407,7 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
isEnforceRange=False, isEnforceRange=False,
isClamp=False, isClamp=False,
exceptionCode=None, exceptionCode=None,
allowTreatNonObjectAsNull=False,
isCallbackReturnValue=False, isCallbackReturnValue=False,
sourceDescription="value"): sourceDescription="value"):
""" """
@ -439,6 +441,9 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
If isClamp is true, we're converting an integer and clamping if the If isClamp is true, we're converting an integer and clamping if the
value is out of range. value is out of range.
If allowTreatNonObjectAsNull is true, then [TreatNonObjectAsNull]
extended attributes on nullable callback functions will be honored.
The return value from this function is a tuple consisting of four things: The return value from this function is a tuple consisting of four things:
1) A string representing the conversion code. This will have template 1) A string representing the conversion code. This will have template
@ -500,6 +505,14 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
'%s' % (firstCap(sourceDescription), typeName, '%s' % (firstCap(sourceDescription), typeName,
exceptionCode))), exceptionCode))),
post="\n") post="\n")
def onFailureNotCallable(failureCode):
return CGWrapper(
CGGeneric(
failureCode or
('//XXXjdm ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "%s");\n'
'%s' % (firstCap(sourceDescription), exceptionCode))),
post="\n")
# A helper function for handling null default values. Checks that the # A helper function for handling null default values. Checks that the
# default value, if it exists, is null. # default value, if it exists, is null.
@ -699,23 +712,39 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
if type.isCallback(): if type.isCallback():
assert not isEnforceRange and not isClamp assert not isEnforceRange and not isClamp
assert not type.treatNonCallableAsNull()
assert not type.treatNonObjectAsNull() or type.nullable()
assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
if isMember: declType = CGGeneric('%s::%s' % (type.unroll().module(), type.unroll().identifier.name))
raise TypeError("Can't handle member callbacks; need to sort out "
"rooting issues") conversion = CGCallbackTempRoot(declType.define())
# XXXbz we're going to assume that callback types are always
# nullable and always have [TreatNonCallableAsNull] for now. if type.nullable():
haveCallable = "${val}.isObject() && JS_ObjectIsCallable(cx, &${val}.toObject())" declType = CGTemplatedType("Option", declType)
conversion = CGWrapper(conversion, pre="Some(", post=")")
if allowTreatNonObjectAsNull and type.treatNonObjectAsNull():
if not isDefinitelyObject:
haveObject = "${val}.is_object()"
if defaultValue is not None: if defaultValue is not None:
assert(isinstance(defaultValue, IDLNullValue)) assert isinstance(defaultValue, IDLNullValue)
haveCallable = "${haveValue} && " + haveCallable haveObject = "${haveValue} && " + haveObject
return ( template = CGIfElseWrapper(haveObject,
"if (%s) {\n" conversion,
" ${declName} = &${val}.toObject();\n" CGGeneric("None")).define()
"} else {\n" else:
" ${declName} = NULL;\n" template = conversion
"}" % haveCallable, else:
CGGeneric("JSObject*"), None, needsRooting) template = CGIfElseWrapper("JS_ObjectIsCallable(cx, ${val}.to_object()) != 0",
conversion,
onFailureNotCallable(failureCode)).define()
template = wrapObjectTemplate(
template,
isDefinitelyObject,
type,
failureCode)
return (template, declType, needsRooting)
if type.isAny(): if type.isAny():
assert not isEnforceRange and not isClamp assert not isEnforceRange and not isClamp
@ -874,7 +903,8 @@ class CGArgumentConverter(CGThing):
defaultValue=argument.defaultValue, defaultValue=argument.defaultValue,
treatNullAs=argument.treatNullAs, treatNullAs=argument.treatNullAs,
isEnforceRange=argument.enforceRange, isEnforceRange=argument.enforceRange,
isClamp=argument.clamp) isClamp=argument.clamp,
allowTreatNonObjectAsNull=argument.allowTreatNonCallableAsNull())
if argument.optional and not argument.defaultValue: if argument.optional and not argument.defaultValue:
declType = CGWrapper(declType, pre="Option<", post=">") declType = CGWrapper(declType, pre="Option<", post=">")
@ -913,7 +943,7 @@ def typeNeedsCx(type, retVal=False):
return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes) return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
if retVal and type.isSpiderMonkeyInterface(): if retVal and type.isSpiderMonkeyInterface():
return True return True
return type.isCallback() or type.isAny() or type.isObject() return type.isAny() or type.isObject()
def typeRetValNeedsRooting(type): def typeRetValNeedsRooting(type):
if type is None: if type is None:
@ -958,9 +988,11 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
result = CGWrapper(result, pre="Option<", post=">") result = CGWrapper(result, pre="Option<", post=">")
return result return result
if returnType.isCallback(): if returnType.isCallback():
# XXXbz we're going to assume that callback types are always result = CGGeneric('%s::%s' % (returnType.unroll().module(),
# nullable for now. returnType.unroll().identifier.name))
return CGGeneric("*mut JSObject") if returnType.nullable():
result = CGWrapper(result, pre="Option<", post=">")
return result
if returnType.isAny(): if returnType.isAny():
return CGGeneric("JSVal") return CGGeneric("JSVal")
if returnType.isObject() or returnType.isSpiderMonkeyInterface(): if returnType.isObject() or returnType.isSpiderMonkeyInterface():
@ -1346,6 +1378,10 @@ class CGIfWrapper(CGWrapper):
CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(), CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(),
post="\n}") post="\n}")
class CGTemplatedType(CGWrapper):
def __init__(self, templateName, child):
CGWrapper.__init__(self, child, pre=templateName + "<", post=">")
class CGNamespace(CGWrapper): class CGNamespace(CGWrapper):
def __init__(self, namespace, child, public=False): def __init__(self, namespace, child, public=False):
pre = "%smod %s {\n" % ("pub " if public else "", namespace) pre = "%smod %s {\n" % ("pub " if public else "", namespace)
@ -1565,23 +1601,32 @@ class CGGeneric(CGThing):
def define(self): def define(self):
return self.text return self.text
def getTypes(descriptor): class CGCallbackTempRoot(CGGeneric):
""" def __init__(self, name):
Get all argument and return types for all members of the descriptor val = "%s::new(tempRoot)" % name
""" define = """{
members = [m for m in descriptor.interface.members] let tempRoot = ${val}.to_object();
if descriptor.interface.ctor(): %s
members.append(descriptor.interface.ctor()) }""" % val
signatures = [s for m in members if m.isMethod() for s in m.signatures()] CGGeneric.__init__(self, define)
types = []
for s in signatures:
assert len(s) == 2
(returnType, arguments) = s
types.append(returnType)
types.extend([a.type for a in arguments])
types.extend(a.type for a in members if a.isAttr())
return types def getAllTypes(descriptors, dictionaries, callbacks):
"""
Generate all the types we're dealing with. For each type, a tuple
containing type, descriptor, dictionary is yielded. The
descriptor and dictionary can be None if the type does not come
from a descriptor or dictionary; they will never both be non-None.
"""
for d in descriptors:
for t in getTypesFromDescriptor(d):
yield (t, d, None)
for dictionary in dictionaries:
for t in getTypesFromDictionary(dictionary):
yield (t, None, dictionary)
for callback in callbacks:
for t in getTypesFromCallback(callback):
yield (t, None, None)
def SortedTuples(l): def SortedTuples(l):
""" """
@ -1598,24 +1643,23 @@ def SortedDictValues(d):
# We're only interested in the values. # We're only interested in the values.
return (i[1] for i in d) return (i[1] for i in d)
def UnionTypes(descriptors): def UnionTypes(descriptors, dictionaries, callbacks, config):
""" """
Returns a tuple containing a set of header filenames to include, a set of Returns a CGList containing CGUnionStructs for every union.
tuples containing a type declaration and a boolean if the type is a struct
for member types of the unions and a CGList containing CGUnionStructs for
every union.
""" """
# Now find all the things we'll need as arguments and return values because # Now find all the things we'll need as arguments and return values because
# we need to wrap or unwrap them. # we need to wrap or unwrap them.
unionStructs = dict() unionStructs = dict()
for d in descriptors: for (t, descriptor, dictionary) in getAllTypes(descriptors, dictionaries, callbacks):
for t in getTypes(d): assert not descriptor or not dictionary
t = t.unroll() t = t.unroll()
if t.isUnion(): if not t.isUnion():
continue
name = str(t) name = str(t)
if not name in unionStructs: if not name in unionStructs:
unionStructs[name] = CGList([CGUnionStruct(t, d), CGUnionConversionStruct(t, d)]) provider = descriptor or config.getDescriptorProvider()
unionStructs[name] = CGList([CGUnionStruct(t, provider), CGUnionConversionStruct(t, provider)])
return CGList(SortedDictValues(unionStructs), "\n\n") return CGList(SortedDictValues(unionStructs), "\n\n")
@ -2301,15 +2345,19 @@ class FakeArgument():
A class that quacks like an IDLArgument. This is used to make A class that quacks like an IDLArgument. This is used to make
setters look like method calls or for special operations. setters look like method calls or for special operations.
""" """
def __init__(self, type, interfaceMember): def __init__(self, type, interfaceMember, allowTreatNonObjectAsNull=False):
self.type = type self.type = type
self.optional = False self.optional = False
self.variadic = False self.variadic = False
self.defaultValue = None self.defaultValue = None
self._allowTreatNonObjectAsNull = allowTreatNonObjectAsNull
self.treatNullAs = interfaceMember.treatNullAs self.treatNullAs = interfaceMember.treatNullAs
self.enforceRange = False self.enforceRange = False
self.clamp = False self.clamp = False
def allowTreatNonCallableAsNull(self):
return self._allowTreatNonObjectAsNull
class CGSetterCall(CGPerSignatureCall): class CGSetterCall(CGPerSignatureCall):
""" """
A class to generate a native object setter call for a particular IDL A class to generate a native object setter call for a particular IDL
@ -2317,7 +2365,7 @@ class CGSetterCall(CGPerSignatureCall):
""" """
def __init__(self, argsPre, argType, nativeMethodName, descriptor, attr): def __init__(self, argsPre, argType, nativeMethodName, descriptor, attr):
CGPerSignatureCall.__init__(self, None, argsPre, CGPerSignatureCall.__init__(self, None, argsPre,
[FakeArgument(argType, attr)], [FakeArgument(argType, attr, allowTreatNonObjectAsNull=True)],
nativeMethodName, False, descriptor, attr, nativeMethodName, False, descriptor, attr,
setter=True) setter=True)
def wrap_return_value(self): def wrap_return_value(self):
@ -2702,9 +2750,22 @@ class CGUnionStruct(CGThing):
enumValues = [ enumValues = [
" e%s(%s)," % (v["name"], v["typeName"]) for v in templateVars " e%s(%s)," % (v["name"], v["typeName"]) for v in templateVars
] ]
return ("pub enum %s {\n" enumConversions = [
"%s\n" " e%s(ref inner) => inner.to_jsval(cx)," % v["name"] for v in templateVars
"}\n") % (self.type, "\n".join(enumValues)) ]
return ("""pub enum %s {
%s
}
impl ToJSValConvertible for %s {
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
match *self {
%s
}
}
}
""") % (self.type, "\n".join(enumValues),
self.type, "\n".join(enumConversions))
class CGUnionConversionStruct(CGThing): class CGUnionConversionStruct(CGThing):
@ -3128,7 +3189,7 @@ class ClassMember(ClassItem):
ClassItem.__init__(self, name, visibility) ClassItem.__init__(self, name, visibility)
def declare(self, cgClass): def declare(self, cgClass):
return '%s: %s,\n' % (self.name, self.type) return '%s %s: %s,\n' % (self.visibility, self.name, self.type)
def define(self, cgClass): def define(self, cgClass):
if not self.static: if not self.static:
@ -4162,14 +4223,17 @@ class CGBindingRoot(CGThing):
for d in dictionaries]) for d in dictionaries])
# Do codegen for all the callbacks. # Do codegen for all the callbacks.
cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider()) cgthings.extend(CGList([CGCallbackFunction(c, config.getDescriptorProvider()),
CGCallbackFunctionImpl(c)], "\n")
for c in mainCallbacks) for c in mainCallbacks)
# Do codegen for all the descriptors # Do codegen for all the descriptors
cgthings.extend([CGDescriptor(x) for x in descriptors]) cgthings.extend([CGDescriptor(x) for x in descriptors])
# Do codegen for all the callback interfaces. # Do codegen for all the callback interfaces.
cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors]) cgthings.extend(CGList([CGCallbackInterface(x),
CGCallbackFunctionImpl(x)], "\n")
for x in callbackDescriptors)
# And make sure we have the right number of newlines at the end # And make sure we have the right number of newlines at the end
curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
@ -4228,7 +4292,7 @@ class CGBindingRoot(CGThing):
'dom::bindings::utils::VoidVal', 'dom::bindings::utils::VoidVal',
'dom::bindings::utils::get_dictionary_property', 'dom::bindings::utils::get_dictionary_property',
'dom::bindings::trace::JSTraceable', 'dom::bindings::trace::JSTraceable',
'dom::bindings::callback::{CallbackContainer,CallbackInterface}', 'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
'dom::bindings::callback::{CallSetup,ExceptionHandling}', 'dom::bindings::callback::{CallSetup,ExceptionHandling}',
'dom::bindings::callback::{WrapCallThisObject}', 'dom::bindings::callback::{WrapCallThisObject}',
'dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible}', 'dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible}',
@ -4314,8 +4378,9 @@ class CGNativeMember(ClassMethod):
elif type.isPrimitive() and type.tag() in builtinNames: elif type.isPrimitive() and type.tag() in builtinNames:
result = CGGeneric(builtinNames[type.tag()]) result = CGGeneric(builtinNames[type.tag()])
if type.nullable(): if type.nullable():
result = CGTemplatedType("Nullable", result) raise TypeError("Nullable primitives are not supported here.")
typeDecl, template = result.define(), "return ${declName};"
typeDecl, template = result.define(), "return Ok(${declName});"
elif type.isDOMString(): elif type.isDOMString():
if isMember: if isMember:
# No need for a third element in the isMember case # No need for a third element in the isMember case
@ -4355,7 +4420,7 @@ class CGNativeMember(ClassMethod):
("already_AddRefed<%s>" % type.unroll().identifier.name, ("already_AddRefed<%s>" % type.unroll().identifier.name,
"return ${declName}.forget();") "return ${declName}.forget();")
elif type.isAny(): elif type.isAny():
typeDecl, template = "JS::Value", "return ${declName};" typeDecl, template = "JSVal", "return Ok(${declName});"
elif type.isObject(): elif type.isObject():
typeDecl, template = "JSObject*", "return ${declName};" typeDecl, template = "JSObject*", "return ${declName};"
elif type.isSpiderMonkeyInterface(): elif type.isSpiderMonkeyInterface():
@ -4454,7 +4519,7 @@ class CGNativeMember(ClassMethod):
if type.isUnion(): if type.isUnion():
if type.nullable(): if type.nullable():
type = type.inner type = type.inner
return str(type), True, True return str(type), False, True
if type.isGeckoInterface() and not type.isCallbackInterface(): if type.isGeckoInterface() and not type.isCallbackInterface():
iface = type.unroll().inner iface = type.unroll().inner
@ -4484,10 +4549,7 @@ class CGNativeMember(ClassMethod):
return type.name, True, True return type.name, True, True
if type.isDOMString(): if type.isDOMString():
if isMember: declType = "DOMString"
declType = "nsString"
else:
declType = "nsAString"
return declType, True, False return declType, True, False
if type.isByteString(): if type.isByteString():
@ -4520,7 +4582,7 @@ class CGNativeMember(ClassMethod):
if isMember: if isMember:
declType = "JS::Value" declType = "JS::Value"
else: else:
declType = "JS::Handle<JS::Value>" declType = "JSVal"
return declType, False, False return declType, False, False
if type.isObject(): if type.isObject():
@ -4561,8 +4623,8 @@ class CGNativeMember(ClassMethod):
elif optional: elif optional:
# Note: All variadic args claim to be optional, but we can just use # Note: All variadic args claim to be optional, but we can just use
# empty arrays to represent them not being present. # empty arrays to represent them not being present.
decl = CGTemplatedType("Optional", decl) decl = CGTemplatedType("Option", decl)
ref = True ref = False
return (decl, ref) return (decl, ref)
def getArg(self, arg): def getArg(self, arg):
@ -4573,7 +4635,7 @@ class CGNativeMember(ClassMethod):
arg.optional and not arg.defaultValue, arg.optional and not arg.defaultValue,
"Variadic" if arg.variadic else False) "Variadic" if arg.variadic else False)
if ref: if ref:
decl = CGWrapper(decl, pre="const ", post="&") decl = CGWrapper(decl, pre="&")
return Argument(decl.define(), arg.identifier.name) return Argument(decl.define(), arg.identifier.name)
@ -4641,39 +4703,37 @@ class CGCallback(CGClass):
# And now insert our template argument. # And now insert our template argument.
argsWithoutThis = list(args) argsWithoutThis = list(args)
args.insert(0, Argument("Box<T>", "thisObj")) args.insert(0, Argument("&JSRef<T>", "thisObj"))
# And the self argument # And the self argument
method.args.insert(0, Argument(None, "&self")) method.args.insert(0, Argument(None, "&self"))
args.insert(0, Argument(None, "&self")) args.insert(0, Argument(None, "&self"))
argsWithoutThis.insert(0, Argument(None, "&self")) argsWithoutThis.insert(0, Argument(None, "&self"))
setupCall = ("let s = CallSetup::new(cx_for_dom_object(${cxProvider}), aExceptionHandling);\n" setupCall = ("let s = CallSetup::new(self, aExceptionHandling);\n"
"if s.GetContext().is_null() {\n" "if s.GetContext().is_null() {\n"
" return Err(FailureUnknown);\n" " return Err(FailureUnknown);\n"
"}\n") "}\n")
bodyWithThis = string.Template( bodyWithThis = string.Template(
setupCall+ setupCall+
"let thisObjJS = WrapCallThisObject(s.GetContext(), ptr::mut_null() /*XXXjdm proper scope*/, thisObj);\n" "let thisObjJS = WrapCallThisObject(s.GetContext(), thisObj);\n"
"if thisObjJS.is_null() {\n" "if thisObjJS.is_null() {\n"
" return Err(FailureUnknown);\n" " return Err(FailureUnknown);\n"
"}\n" "}\n"
"return ${methodName}(${callArgs});").substitute({ "return ${methodName}(${callArgs});").substitute({
"callArgs" : ", ".join(argnamesWithThis), "callArgs" : ", ".join(argnamesWithThis),
"methodName": 'self.' + method.name, "methodName": 'self.' + method.name,
"cxProvider": 'thisObj'
}) })
bodyWithoutThis = string.Template( bodyWithoutThis = string.Template(
setupCall + setupCall +
"return ${methodName}(${callArgs});").substitute({ "return ${methodName}(${callArgs});").substitute({
"callArgs" : ", ".join(argnamesWithoutThis), "callArgs" : ", ".join(argnamesWithoutThis),
"methodName": 'self.' + method.name, "methodName": 'self.' + method.name,
"cxProvider": args[2].name #XXXjdm There's no guarantee that this is a DOM object
}) })
return [ClassMethod(method.name+'_', method.returnType, args, return [ClassMethod(method.name+'_', method.returnType, args,
bodyInHeader=True, bodyInHeader=True,
templateArgs=["T: 'static+CallbackContainer+Reflectable"], templateArgs=["T: Reflectable"],
body=bodyWithThis, body=bodyWithThis,
visibility='pub'), visibility='pub'),
ClassMethod(method.name+'__', method.returnType, argsWithoutThis, ClassMethod(method.name+'__', method.returnType, argsWithoutThis,
@ -4699,15 +4759,27 @@ class CGCallbackFunction(CGCallback):
methods=[CallCallback(callback, descriptorProvider)]) methods=[CallCallback(callback, descriptorProvider)])
def getConstructors(self): def getConstructors(self):
return CGCallback.getConstructors(self) + [ return CGCallback.getConstructors(self)
ClassConstructor(
[Argument("CallbackFunction*", "aOther")], class CGCallbackFunctionImpl(CGGeneric):
bodyInHeader=True, def __init__(self, callback):
visibility="pub", impl = string.Template("""impl CallbackContainer for ${type} {
explicit=True, fn new(callback: *mut JSObject) -> ${type} {
baseConstructors=[ ${type}::new(callback)
"CallbackFunction(aOther)" }
])]
fn callback(&self) -> *mut JSObject {
self.parent.callback()
}
}
impl ToJSValConvertible for ${type} {
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
self.callback().to_jsval(cx)
}
}
""").substitute({"type": callback.name})
CGGeneric.__init__(self, impl)
class CGCallbackInterface(CGCallback): class CGCallbackInterface(CGCallback):
def __init__(self, descriptor): def __init__(self, descriptor):
@ -4809,13 +4881,14 @@ class CallbackMember(CGNativeMember):
return CGList([ return CGList([
CGGeneric(pre), CGGeneric(pre),
CGWrapper(CGIndenter(CGGeneric(body)), CGWrapper(CGIndenter(CGGeneric(body)),
pre="with_compartment(cx, self.parent.callback, || {\n", pre="with_compartment(cx, self.parent.callback(), || {\n",
post="})") post="})")
], "\n").define() ], "\n").define()
def getResultConversion(self): def getResultConversion(self):
replacements = { replacements = {
"val": "rval", "val": "rval",
"declName": "rvalDecl",
} }
if isJSImplementedDescriptor(self.descriptorProvider): if isJSImplementedDescriptor(self.descriptorProvider):
@ -4867,19 +4940,10 @@ class CallbackMember(CGNativeMember):
else: else:
jsvalIndex = "%d" % i jsvalIndex = "%d" % i
if arg.optional and not arg.defaultValue: if arg.optional and not arg.defaultValue:
argval += ".Value()" argval += ".clone().unwrap()"
if arg.type.isDOMString():
# XPConnect string-to-JS conversion wants to mutate the string. So
# let's give it a string it can mutate
# XXXbz if we try to do a sequence of strings, this will kinda fail.
result = "mutableStr"
prepend = "nsString mutableStr(%s);\n" % argval
else:
result = argval
prepend = ""
conversion = prepend + wrapForType("*argv.get_mut(%s)" % jsvalIndex, conversion = wrapForType("*argv.get_mut(%s)" % jsvalIndex,
result=result, result=argval,
successCode="continue;" if arg.variadic else "break;") successCode="continue;" if arg.variadic else "break;")
if arg.variadic: if arg.variadic:
conversion = string.Template( conversion = string.Template(
@ -4890,12 +4954,12 @@ class CallbackMember(CGNativeMember):
elif arg.optional and not arg.defaultValue: elif arg.optional and not arg.defaultValue:
conversion = ( conversion = (
CGIfWrapper(CGGeneric(conversion), CGIfWrapper(CGGeneric(conversion),
"%s.WasPassed()" % arg.identifier.name).define() + "%s.is_some()" % arg.identifier.name).define() +
" else if (argc == %d) {\n" " else if (argc == %d) {\n"
" // This is our current trailing argument; reduce argc\n" " // This is our current trailing argument; reduce argc\n"
" --argc;\n" " argc -= 1;\n"
"} else {\n" "} else {\n"
" argv[%d] = JS::UndefinedValue();\n" " *argv.get_mut(%d) = UndefinedValue();\n"
"}" % (i+1, i)) "}" % (i+1, i))
return conversion return conversion
@ -4937,7 +5001,7 @@ class CallbackMember(CGNativeMember):
}) })
def getArgcDecl(self): def getArgcDecl(self):
return CGGeneric("let argc = %su32;" % self.argCountStr); return CGGeneric("let mut argc = %su32;" % self.argCountStr);
@staticmethod @staticmethod
def ensureASCIIName(idlObject): def ensureASCIIName(idlObject):
@ -4990,7 +5054,7 @@ class CallCallback(CallbackMethod):
return "aThisObj" return "aThisObj"
def getCallableDecl(self): def getCallableDecl(self):
return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n" return "let callable = ObjectValue(unsafe {&*self.parent.callback()});\n";
class CallbackOperationBase(CallbackMethod): class CallbackOperationBase(CallbackMethod):
""" """
@ -5003,11 +5067,11 @@ class CallbackOperationBase(CallbackMethod):
def getThisObj(self): def getThisObj(self):
if not self.singleOperation: if not self.singleOperation:
return "self.parent.callback" return "self.parent.callback()"
# This relies on getCallableDecl declaring a boolean # This relies on getCallableDecl declaring a boolean
# isCallable in the case when we're a single-operation # isCallable in the case when we're a single-operation
# interface. # interface.
return "if isCallable { aThisObj } else { self.parent.callback }" return "if isCallable { aThisObj } else { self.parent.callback() }"
def getCallableDecl(self): def getCallableDecl(self):
replacements = { replacements = {
@ -5021,11 +5085,11 @@ class CallbackOperationBase(CallbackMethod):
if not self.singleOperation: if not self.singleOperation:
return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
return ( return (
'let isCallable = unsafe { JS_ObjectIsCallable(cx, self.parent.callback) != 0 };\n' 'let isCallable = unsafe { JS_ObjectIsCallable(cx, self.parent.callback()) != 0 };\n'
'let callable =\n' + 'let callable =\n' +
CGIndenter( CGIndenter(
CGIfElseWrapper('isCallable', CGIfElseWrapper('isCallable',
CGGeneric('unsafe { ObjectValue(&*self.parent.callback) }'), CGGeneric('unsafe { ObjectValue(&*self.parent.callback()) }'),
CGGeneric(getCallableFromProp))).define() + ';\n') CGGeneric(getCallableFromProp))).define() + ';\n')
class CallbackOperation(CallbackOperationBase): class CallbackOperation(CallbackOperationBase):
@ -5233,12 +5297,16 @@ class GlobalGenRoots():
@staticmethod @staticmethod
def UnionTypes(config): def UnionTypes(config):
curr = UnionTypes(config.getDescriptors()) curr = UnionTypes(config.getDescriptors(),
config.getDictionaries(),
config.getCallbacks(),
config)
curr = CGImports(curr, [], [ curr = CGImports(curr, [], [
'dom::bindings::utils::unwrap_jsmanaged', 'dom::bindings::utils::unwrap_jsmanaged',
'dom::bindings::codegen::PrototypeList', 'dom::bindings::codegen::PrototypeList',
'dom::bindings::conversions::FromJSValConvertible', 'dom::bindings::conversions::FromJSValConvertible',
'dom::bindings::conversions::ToJSValConvertible',
'dom::bindings::conversions::Default', 'dom::bindings::conversions::Default',
'dom::bindings::error::throw_not_in_union', 'dom::bindings::error::throw_not_in_union',
'dom::bindings::js::JS', 'dom::bindings::js::JS',

View file

@ -76,7 +76,11 @@ class Configuration:
@staticmethod @staticmethod
def _filterForFile(items, webIDLFile=""): def _filterForFile(items, webIDLFile=""):
"""Gets the items that match the given filters.""" """Gets the items that match the given filters."""
if not webIDLFile:
return items
return filter(lambda x: x.filename() == webIDLFile, items) return filter(lambda x: x.filename() == webIDLFile, items)
def getDictionaries(self, webIDLFile=""): def getDictionaries(self, webIDLFile=""):
return self._filterForFile(self.dictionaries, webIDLFile=webIDLFile) return self._filterForFile(self.dictionaries, webIDLFile=webIDLFile)
def getCallbacks(self, webIDLFile=""): def getCallbacks(self, webIDLFile=""):
@ -269,3 +273,54 @@ class Descriptor(DescriptorProvider):
throws = member.getExtendedAttribute(throwsAttr) throws = member.getExtendedAttribute(throwsAttr)
maybeAppendInfallibleToAttrs(attrs, throws) maybeAppendInfallibleToAttrs(attrs, throws)
return attrs return attrs
# Some utility methods
def getTypesFromDescriptor(descriptor):
"""
Get all argument and return types for all members of the descriptor
"""
members = [m for m in descriptor.interface.members]
if descriptor.interface.ctor():
members.append(descriptor.interface.ctor())
members.extend(descriptor.interface.namedConstructors)
signatures = [s for m in members if m.isMethod() for s in m.signatures()]
types = []
for s in signatures:
assert len(s) == 2
(returnType, arguments) = s
types.append(returnType)
types.extend(a.type for a in arguments)
types.extend(a.type for a in members if a.isAttr())
return types
def getFlatTypes(types):
retval = set()
for type in types:
type = type.unroll()
if type.isUnion():
retval |= set(type.flatMemberTypes)
else:
retval.add(type)
return retval
def getTypesFromDictionary(dictionary):
"""
Get all member types for this dictionary
"""
types = []
curDict = dictionary
while curDict:
types.extend([m.type for m in curDict.members])
curDict = curDict.parent
return types
def getTypesFromCallback(callback):
"""
Get the types this callback depends on: its return type and the
types of its arguments.
"""
sig = callback.signatures()[0]
types = [sig[0]] # Return type
types.extend(arg.type for arg in sig[1]) # Arguments
return types

View file

@ -464,6 +464,10 @@ class IDLInterface(IDLObjectWithScope):
self._callback = False self._callback = False
self._finished = False self._finished = False
self.members = [] self.members = []
# namedConstructors needs deterministic ordering because bindings code
# outputs the constructs in the order that namedConstructors enumerates
# them.
self.namedConstructors = list()
self.implementedInterfaces = set() self.implementedInterfaces = set()
self._consequential = False self._consequential = False
self._isPartial = True self._isPartial = True
@ -696,6 +700,9 @@ class IDLInterface(IDLObjectWithScope):
if identifier == "TreatNonCallableAsNull": if identifier == "TreatNonCallableAsNull":
raise WebIDLError("TreatNonCallableAsNull cannot be specified on interfaces", raise WebIDLError("TreatNonCallableAsNull cannot be specified on interfaces",
[attr.location, self.location]) [attr.location, self.location])
if identifier == "TreatNonObjectAsNull":
raise WebIDLError("TreatNonObjectAsNull cannot be specified on interfaces",
[attr.location, self.location])
elif identifier == "NoInterfaceObject": elif identifier == "NoInterfaceObject":
if not attr.noArguments(): if not attr.noArguments():
raise WebIDLError("[NoInterfaceObject] must take no arguments", raise WebIDLError("[NoInterfaceObject] must take no arguments",
@ -1049,11 +1056,12 @@ class IDLType(IDLObject):
assert False # Override me! assert False # Override me!
def treatNonCallableAsNull(self): def treatNonCallableAsNull(self):
if not (self.nullable() and self.tag() == IDLType.Tags.callback): assert self.tag() == IDLType.Tags.callback
raise WebIDLError("Type %s cannot be TreatNonCallableAsNull" % self, return self.nullable() and self.inner._treatNonCallableAsNull
[self.location])
return hasattr(self, "_treatNonCallableAsNull") def treatNonObjectAsNull(self):
assert self.tag() == IDLType.Tags.callback
return self.nullable() and self.inner._treatNonObjectAsNull
def markTreatNonCallableAsNull(self): def markTreatNonCallableAsNull(self):
assert not self.treatNonCallableAsNull() assert not self.treatNonCallableAsNull()
@ -2189,6 +2197,7 @@ class IDLArgument(IDLObjectWithIdentifier):
self._isComplete = False self._isComplete = False
self.enforceRange = False self.enforceRange = False
self.clamp = False self.clamp = False
self._allowTreatNonCallableAsNull = False
assert not variadic or optional assert not variadic or optional
@ -2215,6 +2224,8 @@ class IDLArgument(IDLObjectWithIdentifier):
raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
[self.location]); [self.location]);
self.enforceRange = True self.enforceRange = True
elif identifier == "TreatNonCallableAsNull":
self._allowTreatNonCallableAsNull = True
else: else:
raise WebIDLError("Unhandled extended attribute on an argument", raise WebIDLError("Unhandled extended attribute on an argument",
[attribute.location]) [attribute.location])
@ -2247,6 +2258,9 @@ class IDLArgument(IDLObjectWithIdentifier):
self.location) self.location)
assert self.defaultValue assert self.defaultValue
def allowTreatNonCallableAsNull(self):
return self._allowTreatNonCallableAsNull
def _getDependentObjects(self): def _getDependentObjects(self):
deps = set([self.type]) deps = set([self.type])
if self.defaultValue: if self.defaultValue:
@ -2269,6 +2283,12 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
for argument in arguments: for argument in arguments:
argument.resolve(self) argument.resolve(self)
self._treatNonCallableAsNull = False
self._treatNonObjectAsNull = False
def module(self):
return self.location.filename().split('/')[-1].split('.webidl')[0] + 'Binding'
def isCallback(self): def isCallback(self):
return True return True
@ -2308,6 +2328,21 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
return (other.isPrimitive() or other.isString() or other.isEnum() or return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isNonCallbackInterface() or other.isDate()) other.isNonCallbackInterface() or other.isDate())
def addExtendedAttributes(self, attrs):
unhandledAttrs = []
for attr in attrs:
if attr.identifier() == "TreatNonCallableAsNull":
self._treatNonCallableAsNull = True
elif attr.identifier() == "TreatNonObjectAsNull":
self._treatNonObjectAsNull = True
else:
unhandledAttrs.append(attr)
if self._treatNonCallableAsNull and self._treatNonObjectAsNull:
raise WebIDLError("Cannot specify both [TreatNonCallableAsNull] "
"and [TreatNonObjectAsNull]", [self.location])
if len(unhandledAttrs) != 0:
IDLType.addExtendedAttributes(self, unhandledAttrs)
def _getDependentObjects(self): def _getDependentObjects(self):
return set([self._returnType] + self._arguments) return set([self._returnType] + self._arguments)

View file

@ -9,7 +9,7 @@ use dom::bindings::utils::jsstring_to_str;
use dom::bindings::utils::unwrap_jsmanaged; use dom::bindings::utils::unwrap_jsmanaged;
use servo_util::str::DOMString; use servo_util::str::DOMString;
use js::jsapi::{JSBool, JSContext}; use js::jsapi::{JSBool, JSContext, JSObject};
use js::jsapi::{JS_ValueToUint64, JS_ValueToInt64}; use js::jsapi::{JS_ValueToUint64, JS_ValueToInt64};
use js::jsapi::{JS_ValueToECMAUint32, JS_ValueToECMAInt32}; use js::jsapi::{JS_ValueToECMAUint32, JS_ValueToECMAInt32};
use js::jsapi::{JS_ValueToUint16, JS_ValueToNumber, JS_ValueToBoolean}; use js::jsapi::{JS_ValueToUint16, JS_ValueToNumber, JS_ValueToBoolean};
@ -18,7 +18,7 @@ use js::jsapi::{JS_NewUCStringCopyN, JS_NewStringCopyN};
use js::jsapi::{JS_WrapValue}; use js::jsapi::{JS_WrapValue};
use js::jsval::JSVal; use js::jsval::JSVal;
use js::jsval::{UndefinedValue, NullValue, BooleanValue, Int32Value, UInt32Value}; use js::jsval::{UndefinedValue, NullValue, BooleanValue, Int32Value, UInt32Value};
use js::jsval::{StringValue, ObjectValue}; use js::jsval::{StringValue, ObjectValue, ObjectOrNullValue};
use js::glue::RUST_JS_NumberValue; use js::glue::RUST_JS_NumberValue;
use libc; use libc;
use std::default::Default; use std::default::Default;
@ -328,6 +328,12 @@ impl<'a, T: Reflectable> ToJSValConvertible for JSRef<'a, T> {
} }
} }
impl<'a, T: Reflectable> ToJSValConvertible for JS<T> {
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
self.reflector().to_jsval(cx)
}
}
impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> { impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> {
fn to_jsval(&self, cx: *mut JSContext) -> JSVal { fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
match self { match self {
@ -348,3 +354,13 @@ impl<X: Default, T: FromJSValConvertible<X>> FromJSValConvertible<()> for Option
} }
} }
} }
impl ToJSValConvertible for *mut JSObject {
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
let mut wrapped = ObjectOrNullValue(*self);
unsafe {
assert!(JS_WrapValue(cx, &mut wrapped) != 0);
}
wrapped
}
}

View file

@ -110,6 +110,7 @@ impl<T> DerefMut<T> for Untraceable<T> {
/// Encapsulates a type that can be traced but is boxed in a type we don't control /// Encapsulates a type that can be traced but is boxed in a type we don't control
/// (such as RefCell). Wrap a field in Traceable and implement the Encodable trait /// (such as RefCell). Wrap a field in Traceable and implement the Encodable trait
/// for that new concrete type to achieve magic compiler-derived trace hooks. /// for that new concrete type to achieve magic compiler-derived trace hooks.
#[deriving(Eq, Clone)]
pub struct Traceable<T> { pub struct Traceable<T> {
inner: T inner: T
} }

View file

@ -2,9 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::{DocumentDerived, EventCast, HTMLElementCast}; use dom::bindings::codegen::InheritTypes::{DocumentDerived, EventCast, HTMLElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLHeadElementCast, TextCast, ElementCast}; use dom::bindings::codegen::InheritTypes::{HTMLHeadElementCast, TextCast, ElementCast};
use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, HTMLHtmlElementCast, NodeCast}; use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, HTMLHtmlElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::codegen::BindingDeclarations::DocumentBinding; use dom::bindings::codegen::BindingDeclarations::DocumentBinding;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable, TemporaryPushable}; use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable, TemporaryPushable};
use dom::bindings::js::OptionalRootable; use dom::bindings::js::OptionalRootable;
@ -21,7 +23,7 @@ use dom::element::{Element, AttributeHandlers, get_attribute_parts};
use dom::element::{HTMLHtmlElementTypeId, HTMLHeadElementTypeId, HTMLTitleElementTypeId}; use dom::element::{HTMLHtmlElementTypeId, HTMLHeadElementTypeId, HTMLTitleElementTypeId};
use dom::element::{HTMLBodyElementTypeId, HTMLFrameSetElementTypeId}; use dom::element::{HTMLBodyElementTypeId, HTMLFrameSetElementTypeId};
use dom::event::Event; use dom::event::Event;
use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers};
use dom::htmlcollection::{HTMLCollection, CollectionFilter}; use dom::htmlcollection::{HTMLCollection, CollectionFilter};
use dom::htmlelement::HTMLElement; use dom::htmlelement::HTMLElement;
use dom::htmlheadelement::HTMLHeadElement; use dom::htmlheadelement::HTMLHeadElement;
@ -325,6 +327,8 @@ pub trait DocumentMethods {
fn Applets(&self) -> Temporary<HTMLCollection>; fn Applets(&self) -> Temporary<HTMLCollection>;
fn Location(&mut self) -> Temporary<Location>; fn Location(&mut self) -> Temporary<Location>;
fn Children(&self) -> Temporary<HTMLCollection>; fn Children(&self) -> Temporary<HTMLCollection>;
fn GetOnload(&self) -> Option<EventHandlerNonNull>;
fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>);
} }
impl<'a> DocumentMethods for JSRef<'a, Document> { impl<'a> DocumentMethods for JSRef<'a, Document> {
@ -804,4 +808,14 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
let window = self.window.root(); let window = self.window.root();
HTMLCollection::children(&*window, NodeCast::from_ref(self)) HTMLCollection::children(&*window, NodeCast::from_ref(self))
} }
fn GetOnload(&self) -> Option<EventHandlerNonNull> {
let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
eventtarget.get_event_handler_common("load")
}
fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>) {
let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self);
eventtarget.set_event_handler_common("load", listener)
}
} }

View file

@ -50,7 +50,7 @@ pub fn dispatch_event<'a, 'b>(target: &JSRef<'a, EventTarget>,
for listener in listeners.iter() { for listener in listeners.iter() {
//FIXME: this should have proper error handling, or explicitly //FIXME: this should have proper error handling, or explicitly
// drop the exception on the floor // drop the exception on the floor
assert!(listener.HandleEvent__(event, ReportExceptions).is_ok()); assert!(listener.HandleEvent_(&**cur_target, event, ReportExceptions).is_ok());
if event.deref().stop_immediate { if event.deref().stop_immediate {
break; break;
@ -80,7 +80,7 @@ pub fn dispatch_event<'a, 'b>(target: &JSRef<'a, EventTarget>,
for listener in listeners.iter() { for listener in listeners.iter() {
//FIXME: this should have proper error handling, or explicitly drop the //FIXME: this should have proper error handling, or explicitly drop the
// exception on the floor. // exception on the floor.
assert!(listener.HandleEvent__(event, ReportExceptions).is_ok()); assert!(listener.HandleEvent_(target, event, ReportExceptions).is_ok());
if event.deref().stop_immediate { if event.deref().stop_immediate {
break; break;
} }
@ -99,7 +99,7 @@ pub fn dispatch_event<'a, 'b>(target: &JSRef<'a, EventTarget>,
for listener in listeners.iter() { for listener in listeners.iter() {
//FIXME: this should have proper error handling or explicitly //FIXME: this should have proper error handling or explicitly
// drop exceptions on the floor. // drop exceptions on the floor.
assert!(listener.HandleEvent__(event, ReportExceptions).is_ok()); assert!(listener.HandleEvent_(&**cur_target, event, ReportExceptions).is_ok());
if event.deref().stop_immediate { if event.deref().stop_immediate {
break; break;

View file

@ -2,16 +2,23 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::callback::CallbackContainer;
use dom::bindings::codegen::BindingDeclarations::EventListenerBinding::EventListener;
use dom::bindings::codegen::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::error::{Fallible, InvalidState};
use dom::bindings::js::JSRef; use dom::bindings::js::JSRef;
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::bindings::error::{Fallible, InvalidState};
use dom::bindings::codegen::BindingDeclarations::EventListenerBinding::EventListener;
use dom::event::Event; use dom::event::Event;
use dom::eventdispatcher::dispatch_event; use dom::eventdispatcher::dispatch_event;
use dom::node::NodeTypeId; use dom::node::NodeTypeId;
use dom::xmlhttprequest::XMLHttpRequestId; use dom::xmlhttprequest::XMLHttpRequestId;
use dom::virtualmethods::VirtualMethods; use dom::virtualmethods::VirtualMethods;
use js::jsapi::{JS_CompileUCFunction, JS_GetFunctionObject, JS_CloneFunctionObject};
use js::jsapi::{JSContext, JSObject};
use servo_util::str::DOMString; use servo_util::str::DOMString;
use libc::{c_char, size_t};
use std::ptr;
use url::Url;
use collections::hashmap::HashMap; use collections::hashmap::HashMap;
@ -28,10 +35,24 @@ pub enum EventTargetTypeId {
XMLHttpRequestTargetTypeId(XMLHttpRequestId) XMLHttpRequestTargetTypeId(XMLHttpRequestId)
} }
#[deriving(Eq, Encodable)]
pub enum EventListenerType {
Additive(EventListener),
Inline(EventListener),
}
impl EventListenerType {
fn get_listener(&self) -> EventListener {
match *self {
Additive(listener) | Inline(listener) => listener
}
}
}
#[deriving(Eq,Encodable)] #[deriving(Eq,Encodable)]
pub struct EventListenerEntry { pub struct EventListenerEntry {
pub phase: ListenerPhase, pub phase: ListenerPhase,
pub listener: EventListener pub listener: EventListenerType
} }
#[deriving(Encodable)] #[deriving(Encodable)]
@ -52,7 +73,7 @@ impl EventTarget {
pub fn get_listeners(&self, type_: &str) -> Option<Vec<EventListener>> { pub fn get_listeners(&self, type_: &str) -> Option<Vec<EventListener>> {
self.handlers.find_equiv(&type_).map(|listeners| { self.handlers.find_equiv(&type_).map(|listeners| {
listeners.iter().map(|entry| entry.listener).collect() listeners.iter().map(|entry| entry.listener.get_listener()).collect()
}) })
} }
@ -60,7 +81,7 @@ impl EventTarget {
-> Option<Vec<EventListener>> { -> Option<Vec<EventListener>> {
self.handlers.find_equiv(&type_).map(|listeners| { self.handlers.find_equiv(&type_).map(|listeners| {
let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase); let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
filtered.map(|entry| entry.listener).collect() filtered.map(|entry| entry.listener.get_listener()).collect()
}) })
} }
} }
@ -69,6 +90,19 @@ pub trait EventTargetHelpers {
fn dispatch_event_with_target<'a>(&self, fn dispatch_event_with_target<'a>(&self,
target: Option<JSRef<'a, EventTarget>>, target: Option<JSRef<'a, EventTarget>>,
event: &mut JSRef<Event>) -> Fallible<bool>; event: &mut JSRef<Event>) -> Fallible<bool>;
fn set_inline_event_listener(&mut self,
ty: DOMString,
listener: Option<EventListener>);
fn get_inline_event_listener(&self, ty: DOMString) -> Option<EventListener>;
fn set_event_handler_uncompiled(&mut self,
cx: *mut JSContext,
url: Url,
scope: *mut JSObject,
ty: &str,
source: DOMString);
fn set_event_handler_common<T: CallbackContainer>(&mut self, ty: &str,
listener: Option<T>);
fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<T>;
} }
impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> { impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> {
@ -80,6 +114,91 @@ impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> {
} }
Ok(dispatch_event(self, target, event)) Ok(dispatch_event(self, target, event))
} }
fn set_inline_event_listener(&mut self,
ty: DOMString,
listener: Option<EventListener>) {
let entries = self.handlers.find_or_insert_with(ty, |_| vec!());
let idx = entries.iter().position(|&entry| {
match entry.listener {
Inline(_) => true,
_ => false,
}
});
match idx {
Some(idx) => {
match listener {
Some(listener) => entries.get_mut(idx).listener = Inline(listener),
None => {
entries.remove(idx);
}
}
}
None => {
if listener.is_some() {
entries.push(EventListenerEntry {
phase: Bubbling,
listener: Inline(listener.unwrap()),
});
}
}
}
}
fn get_inline_event_listener(&self, ty: DOMString) -> Option<EventListener> {
let entries = self.handlers.find(&ty);
entries.and_then(|entries| entries.iter().find(|entry| {
match entry.listener {
Inline(_) => true,
_ => false,
}
}).map(|entry| entry.listener.get_listener()))
}
fn set_event_handler_uncompiled(&mut self,
cx: *mut JSContext,
url: Url,
scope: *mut JSObject,
ty: &str,
source: DOMString) {
let url = url.to_str().to_c_str();
let name = ty.to_c_str();
let lineno = 0; //XXXjdm need to get a real number here
let nargs = 1; //XXXjdm not true for onerror
static arg_name: [c_char, ..6] =
['e' as c_char, 'v' as c_char, 'e' as c_char, 'n' as c_char, 't' as c_char, 0];
static arg_names: [*c_char, ..1] = [&arg_name as *c_char];
let source = source.to_utf16();
let handler =
name.with_ref(|name| {
url.with_ref(|url| { unsafe {
let fun = JS_CompileUCFunction(cx, ptr::mut_null(), name,
nargs, &arg_names as **i8 as *mut *i8, source.as_ptr(),
source.len() as size_t,
url, lineno);
assert!(fun.is_not_null());
JS_GetFunctionObject(fun)
}})});
let funobj = unsafe { JS_CloneFunctionObject(cx, handler, scope) };
assert!(funobj.is_not_null());
self.set_event_handler_common(ty, Some(EventHandlerNonNull::new(funobj)))
}
fn set_event_handler_common<T: CallbackContainer>(
&mut self, ty: &str, listener: Option<T>)
{
let event_listener = listener.map(|listener|
EventListener::new(listener.callback()));
self.set_inline_event_listener(ty.to_owned(), event_listener);
}
fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<T> {
let listener = self.get_inline_event_listener(ty.to_owned());
listener.map(|listener| CallbackContainer::new(listener.parent.callback()))
}
} }
pub trait EventTargetMethods { pub trait EventTargetMethods {
@ -104,7 +223,7 @@ impl<'a> EventTargetMethods for JSRef<'a, EventTarget> {
let phase = if capture { Capturing } else { Bubbling }; let phase = if capture { Capturing } else { Bubbling };
let new_entry = EventListenerEntry { let new_entry = EventListenerEntry {
phase: phase, phase: phase,
listener: listener listener: Additive(listener)
}; };
if entry.as_slice().position_elem(&new_entry).is_none() { if entry.as_slice().position_elem(&new_entry).is_none() {
entry.push(new_entry); entry.push(new_entry);
@ -122,7 +241,7 @@ impl<'a> EventTargetMethods for JSRef<'a, EventTarget> {
let phase = if capture { Capturing } else { Bubbling }; let phase = if capture { Capturing } else { Bubbling };
let old_entry = EventListenerEntry { let old_entry = EventListenerEntry {
phase: phase, phase: phase,
listener: listener listener: Additive(listener)
}; };
let position = entry.as_slice().position_elem(&old_entry); let position = entry.as_slice().position_elem(&old_entry);
for &position in position.iter() { for &position in position.iter() {

View file

@ -3,14 +3,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::BindingDeclarations::HTMLBodyElementBinding; use dom::bindings::codegen::BindingDeclarations::HTMLBodyElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLBodyElementDerived; use dom::bindings::codegen::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLElementCast};
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::error::ErrorResult; use dom::bindings::error::ErrorResult;
use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::Reflectable;
use dom::document::Document; use dom::document::Document;
use dom::element::HTMLBodyElementTypeId; use dom::element::HTMLBodyElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers};
use dom::htmlelement::HTMLElement; use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId}; use dom::node::{Node, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::window::WindowMethods;
use servo_util::str::DOMString; use servo_util::str::DOMString;
#[deriving(Encodable)] #[deriving(Encodable)]
@ -50,6 +55,8 @@ pub trait HTMLBodyElementMethods {
fn SetBgColor(&self, _bg_color: DOMString) -> ErrorResult; fn SetBgColor(&self, _bg_color: DOMString) -> ErrorResult;
fn Background(&self) -> DOMString; fn Background(&self) -> DOMString;
fn SetBackground(&self, _background: DOMString) -> ErrorResult; fn SetBackground(&self, _background: DOMString) -> ErrorResult;
fn GetOnunload(&self) -> Option<EventHandlerNonNull>;
fn SetOnunload(&mut self, listener: Option<EventHandlerNonNull>);
} }
impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> { impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> {
@ -100,4 +107,49 @@ impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> {
fn SetBackground(&self, _background: DOMString) -> ErrorResult { fn SetBackground(&self, _background: DOMString) -> ErrorResult {
Ok(()) Ok(())
} }
fn GetOnunload(&self) -> Option<EventHandlerNonNull> {
let win = window_from_node(self).root();
win.deref().GetOnunload()
}
fn SetOnunload(&mut self, listener: Option<EventHandlerNonNull>) {
let mut win = window_from_node(self).root();
win.SetOnunload(listener)
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLBodyElement> {
fn super_type<'a>(&'a mut self) -> Option<&'a mut VirtualMethods:> {
let element: &mut JSRef<HTMLElement> = HTMLElementCast::from_mut_ref(self);
Some(element as &mut VirtualMethods:)
}
fn after_set_attr(&mut self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref mut s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
if name.starts_with("on") {
static forwarded_events: &'static [&'static str] =
&["onfocus", "onload", "onscroll", "onafterprint", "onbeforeprint",
"onbeforeunload", "onhashchange", "onlanguagechange", "onmessage",
"onoffline", "ononline", "onpagehide", "onpageshow", "onpopstate",
"onstorage", "onresize", "onunload", "onerror"];
let mut window = window_from_node(self).root();
let (cx, url, reflector) = (window.get_cx(),
window.get_url(),
window.reflector().get_jsobject());
let evtarget: &mut JSRef<EventTarget> =
if forwarded_events.iter().any(|&event| name.as_slice() == event) {
EventTargetCast::from_mut_ref(&mut *window)
} else {
EventTargetCast::from_mut_ref(self)
};
evtarget.set_event_handler_uncompiled(cx, url, reflector,
name.slice_from(2).to_owned(),
value);
}
}
} }

View file

@ -3,15 +3,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::BindingDeclarations::HTMLElementBinding; use dom::bindings::codegen::BindingDeclarations::HTMLElementBinding;
use dom::bindings::codegen::InheritTypes::ElementCast; use dom::bindings::codegen::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::HTMLElementDerived; use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFrameSetElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLElementDerived, HTMLBodyElementDerived};
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::{ErrorResult, Fallible};
use dom::document::Document; use dom::document::Document;
use dom::element::{Element, ElementTypeId, HTMLElementTypeId}; use dom::element::{Element, ElementTypeId, HTMLElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::node::{Node, ElementNodeTypeId}; use dom::node::{Node, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods; use dom::virtualmethods::VirtualMethods;
use dom::window::WindowMethods;
use js::jsapi::JSContext; use js::jsapi::JSContext;
use js::jsval::{JSVal, NullValue}; use js::jsval::{JSVal, NullValue};
use servo_util::namespace; use servo_util::namespace;
@ -45,6 +48,17 @@ impl HTMLElement {
} }
} }
trait PrivateHTMLElementHelpers {
fn is_body_or_frameset(&self) -> bool;
}
impl<'a> PrivateHTMLElementHelpers for JSRef<'a, HTMLElement> {
fn is_body_or_frameset(&self) -> bool {
let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
eventtarget.is_htmlbodyelement() || eventtarget.is_htmlframesetelement()
}
}
pub trait HTMLElementMethods { pub trait HTMLElementMethods {
fn Title(&self) -> DOMString; fn Title(&self) -> DOMString;
fn SetTitle(&mut self, _title: DOMString); fn SetTitle(&mut self, _title: DOMString);
@ -76,6 +90,8 @@ pub trait HTMLElementMethods {
fn OffsetLeft(&self) -> i32; fn OffsetLeft(&self) -> i32;
fn OffsetWidth(&self) -> i32; fn OffsetWidth(&self) -> i32;
fn OffsetHeight(&self) -> i32; fn OffsetHeight(&self) -> i32;
fn GetOnload(&self) -> Option<EventHandlerNonNull>;
fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>);
} }
impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> { impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> {
@ -195,6 +211,22 @@ impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> {
fn OffsetHeight(&self) -> i32 { fn OffsetHeight(&self) -> i32 {
0 0
} }
fn GetOnload(&self) -> Option<EventHandlerNonNull> {
if self.is_body_or_frameset() {
let win = window_from_node(self).root();
win.deref().GetOnload()
} else {
None
}
}
fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>) {
if self.is_body_or_frameset() {
let mut win = window_from_node(self).root();
win.SetOnload(listener)
}
}
} }
impl<'a> VirtualMethods for JSRef<'a, HTMLElement> { impl<'a> VirtualMethods for JSRef<'a, HTMLElement> {

View file

@ -5,7 +5,7 @@
use dom::bindings::js::{JS, JSRef, Temporary}; use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::codegen::BindingDeclarations::TestBindingBinding::TestEnum; use dom::bindings::codegen::BindingDeclarations::TestBindingBinding::TestEnum;
use dom::bindings::codegen::BindingDeclarations::TestBindingBinding::TestEnumValues::_empty; use dom::bindings::codegen::BindingDeclarations::TestBindingBinding::TestEnumValues::_empty;
use dom::bindings::codegen::UnionTypes::{HTMLElementOrLong, StringOrFormData}; use dom::bindings::codegen::UnionTypes::{HTMLElementOrLong, EventOrString};
use dom::bindings::str::ByteString; use dom::bindings::str::ByteString;
use dom::bindings::utils::{Reflector, Reflectable}; use dom::bindings::utils::{Reflector, Reflectable};
use dom::blob::Blob; use dom::blob::Blob;
@ -136,7 +136,7 @@ pub trait TestBindingMethods {
fn PassEnum(&self, _: TestEnum) {} fn PassEnum(&self, _: TestEnum) {}
fn PassInterface(&self, _: &JSRef<Blob>) {} fn PassInterface(&self, _: &JSRef<Blob>) {}
fn PassUnion(&self, _: HTMLElementOrLong) {} fn PassUnion(&self, _: HTMLElementOrLong) {}
fn PassUnion2(&self, _: StringOrFormData) {} fn PassUnion2(&self, _: EventOrString) {}
fn PassAny(&self, _: *mut JSContext, _: JSVal) {} fn PassAny(&self, _: *mut JSContext, _: JSVal) {}
fn PassNullableBoolean(&self, _: Option<bool>) {} fn PassNullableBoolean(&self, _: Option<bool>) {}
@ -155,7 +155,7 @@ pub trait TestBindingMethods {
// fn PassNullableEnum(&self, _: Option<TestEnum>) {} // fn PassNullableEnum(&self, _: Option<TestEnum>) {}
fn PassNullableInterface(&self, _: Option<JSRef<Blob>>) {} fn PassNullableInterface(&self, _: Option<JSRef<Blob>>) {}
fn PassNullableUnion(&self, _: Option<HTMLElementOrLong>) {} fn PassNullableUnion(&self, _: Option<HTMLElementOrLong>) {}
fn PassNullableUnion2(&self, _: Option<StringOrFormData>) {} fn PassNullableUnion2(&self, _: Option<EventOrString>) {}
fn PassNullableAny(&self, _: *mut JSContext, _: Option<JSVal>) {} fn PassNullableAny(&self, _: *mut JSContext, _: Option<JSVal>) {}
fn PassOptionalBoolean(&self, _: Option<bool>) {} fn PassOptionalBoolean(&self, _: Option<bool>) {}
@ -174,7 +174,7 @@ pub trait TestBindingMethods {
fn PassOptionalEnum(&self, _: Option<TestEnum>) {} fn PassOptionalEnum(&self, _: Option<TestEnum>) {}
fn PassOptionalInterface(&self, _: Option<JSRef<Blob>>) {} fn PassOptionalInterface(&self, _: Option<JSRef<Blob>>) {}
fn PassOptionalUnion(&self, _: Option<HTMLElementOrLong>) {} fn PassOptionalUnion(&self, _: Option<HTMLElementOrLong>) {}
fn PassOptionalUnion2(&self, _: Option<StringOrFormData>) {} fn PassOptionalUnion2(&self, _: Option<EventOrString>) {}
fn PassOptionalAny(&self, _: *mut JSContext, _: Option<JSVal>) {} fn PassOptionalAny(&self, _: *mut JSContext, _: Option<JSVal>) {}
fn PassOptionalNullableBoolean(&self, _: Option<Option<bool>>) {} fn PassOptionalNullableBoolean(&self, _: Option<Option<bool>>) {}
@ -193,7 +193,7 @@ pub trait TestBindingMethods {
// fn PassOptionalNullableEnum(&self, _: Option<Option<TestEnum>>) {} // fn PassOptionalNullableEnum(&self, _: Option<Option<TestEnum>>) {}
fn PassOptionalNullableInterface(&self, _: Option<Option<JSRef<Blob>>>) {} fn PassOptionalNullableInterface(&self, _: Option<Option<JSRef<Blob>>>) {}
fn PassOptionalNullableUnion(&self, _: Option<Option<HTMLElementOrLong>>) {} fn PassOptionalNullableUnion(&self, _: Option<Option<HTMLElementOrLong>>) {}
fn PassOptionalNullableUnion2(&self, _: Option<Option<StringOrFormData>>) {} fn PassOptionalNullableUnion2(&self, _: Option<Option<EventOrString>>) {}
fn PassOptionalBooleanWithDefault(&self, _: bool) {} fn PassOptionalBooleanWithDefault(&self, _: bool) {}
fn PassOptionalByteWithDefault(&self, _: i8) {} fn PassOptionalByteWithDefault(&self, _: i8) {}
@ -223,7 +223,7 @@ pub trait TestBindingMethods {
// fn PassOptionalNullableEnumWithDefault(&self, _: Option<TestEnum>) {} // fn PassOptionalNullableEnumWithDefault(&self, _: Option<TestEnum>) {}
fn PassOptionalNullableInterfaceWithDefault(&self, _: Option<JSRef<Blob>>) {} fn PassOptionalNullableInterfaceWithDefault(&self, _: Option<JSRef<Blob>>) {}
fn PassOptionalNullableUnionWithDefault(&self, _: Option<HTMLElementOrLong>) {} fn PassOptionalNullableUnionWithDefault(&self, _: Option<HTMLElementOrLong>) {}
fn PassOptionalNullableUnion2WithDefault(&self, _: Option<StringOrFormData>) {} fn PassOptionalNullableUnion2WithDefault(&self, _: Option<EventOrString>) {}
fn PassOptionalAnyWithDefault(&self, _: *mut JSContext, _: JSVal) {} fn PassOptionalAnyWithDefault(&self, _: *mut JSContext, _: JSVal) {}
fn PassOptionalNullableBooleanWithNonNullDefault(&self, _: Option<bool>) {} fn PassOptionalNullableBooleanWithNonNullDefault(&self, _: Option<bool>) {}

View file

@ -4,6 +4,7 @@
use dom::bindings::codegen::InheritTypes::ElementCast; use dom::bindings::codegen::InheritTypes::ElementCast;
use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast; use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast;
use dom::bindings::codegen::InheritTypes::HTMLBodyElementCast;
use dom::bindings::codegen::InheritTypes::HTMLElementCast; use dom::bindings::codegen::InheritTypes::HTMLElementCast;
use dom::bindings::codegen::InheritTypes::HTMLIFrameElementCast; use dom::bindings::codegen::InheritTypes::HTMLIFrameElementCast;
use dom::bindings::codegen::InheritTypes::HTMLImageElementCast; use dom::bindings::codegen::InheritTypes::HTMLImageElementCast;
@ -11,10 +12,11 @@ use dom::bindings::codegen::InheritTypes::HTMLObjectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast; use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast;
use dom::bindings::js::JSRef; use dom::bindings::js::JSRef;
use dom::element::Element; use dom::element::Element;
use dom::element::{ElementTypeId, HTMLAnchorElementTypeId, HTMLImageElementTypeId}; use dom::element::{ElementTypeId, HTMLAnchorElementTypeId, HTMLBodyElementTypeId, HTMLImageElementTypeId};
use dom::element::{HTMLIFrameElementTypeId, HTMLObjectElementTypeId, HTMLStyleElementTypeId}; use dom::element::{HTMLIFrameElementTypeId, HTMLObjectElementTypeId, HTMLStyleElementTypeId};
use dom::event::Event; use dom::event::Event;
use dom::htmlanchorelement::HTMLAnchorElement; use dom::htmlanchorelement::HTMLAnchorElement;
use dom::htmlbodyelement::HTMLBodyElement;
use dom::htmlelement::HTMLElement; use dom::htmlelement::HTMLElement;
use dom::htmliframeelement::HTMLIFrameElement; use dom::htmliframeelement::HTMLIFrameElement;
use dom::htmlimageelement::HTMLImageElement; use dom::htmlimageelement::HTMLImageElement;
@ -93,6 +95,10 @@ pub fn vtable_for<'a>(node: &'a mut JSRef<Node>) -> &'a mut VirtualMethods: {
let element: &mut JSRef<HTMLAnchorElement> = HTMLAnchorElementCast::to_mut_ref(node).unwrap(); let element: &mut JSRef<HTMLAnchorElement> = HTMLAnchorElementCast::to_mut_ref(node).unwrap();
element as &mut VirtualMethods: element as &mut VirtualMethods:
} }
ElementNodeTypeId(HTMLBodyElementTypeId) => {
let element: &mut JSRef<HTMLBodyElement> = HTMLBodyElementCast::to_mut_ref(node).unwrap();
element as &mut VirtualMethods:
}
ElementNodeTypeId(HTMLImageElementTypeId) => { ElementNodeTypeId(HTMLImageElementTypeId) => {
let element: &mut JSRef<HTMLImageElement> = HTMLImageElementCast::to_mut_ref(node).unwrap(); let element: &mut JSRef<HTMLImageElement> = HTMLImageElementCast::to_mut_ref(node).unwrap();
element as &mut VirtualMethods: element as &mut VirtualMethods:

View file

@ -68,3 +68,4 @@ partial interface Document {
}; };
Document implements ParentNode; Document implements ParentNode;
Document implements GlobalEventHandlers;

View file

@ -0,0 +1,45 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* http://www.whatwg.org/specs/web-apps/current-work/#eventhandler
*
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
* Opera Software ASA. You are granted a license to use, reproduce
* and create derivative works of this document.
*/
[TreatNonObjectAsNull]
callback EventHandlerNonNull = any (Event event);
typedef EventHandlerNonNull? EventHandler;
[TreatNonObjectAsNull]
callback OnErrorEventHandlerNonNull = boolean ((Event or DOMString) event, optional DOMString source, optional unsigned long lineno, optional unsigned long column, optional any error);
typedef OnErrorEventHandlerNonNull? OnErrorEventHandler;
[NoInterfaceObject]
interface GlobalEventHandlers {
attribute EventHandler onload;
};
[NoInterfaceObject]
interface WindowEventHandlers {
attribute EventHandler onunload;
};
// The spec has |attribute OnErrorEventHandler onerror;| on
// GlobalEventHandlers, and calls the handler differently depending on
// whether an ErrorEvent was fired. We don't do that, and until we do we'll
// need to distinguish between onerror on Window or on nodes.
[NoInterfaceObject]
interface OnErrorEventHandlerForNodes {
attribute EventHandler onerror;
};
[NoInterfaceObject]
interface OnErrorEventHandlerForWindow {
attribute OnErrorEventHandler onerror;
};

View file

@ -23,4 +23,4 @@ partial interface HTMLBodyElement {
[SetterThrows] attribute DOMString background; [SetterThrows] attribute DOMString background;
}; };
//HTMLBodyElement implements WindowEventHandlers; HTMLBodyElement implements WindowEventHandlers;

View file

@ -54,3 +54,5 @@ partial interface HTMLElement {
readonly attribute long offsetWidth; readonly attribute long offsetWidth;
readonly attribute long offsetHeight; readonly attribute long offsetHeight;
}; };
HTMLElement implements GlobalEventHandlers;
//HTMLElement implements OnErrorEventHandlerForNodes;

View file

@ -70,7 +70,7 @@ interface TestBinding {
attribute TestEnum enumAttribute; attribute TestEnum enumAttribute;
attribute Blob interfaceAttribute; attribute Blob interfaceAttribute;
// attribute (HTMLElement or long) unionAttribute; // attribute (HTMLElement or long) unionAttribute;
// attribute (DOMString or FormData) union2Attribute; // attribute (Event or DOMString) union2Attribute;
attribute any anyAttribute; attribute any anyAttribute;
attribute boolean? booleanAttributeNullable; attribute boolean? booleanAttributeNullable;
@ -89,7 +89,7 @@ interface TestBinding {
readonly attribute TestEnum? enumAttributeNullable; readonly attribute TestEnum? enumAttributeNullable;
attribute Blob? interfaceAttributeNullable; attribute Blob? interfaceAttributeNullable;
// attribute (HTMLElement or long)? unionAttributeNullable; // attribute (HTMLElement or long)? unionAttributeNullable;
// attribute (DOMString or FormData)? union2AttributeNullable; // attribute (Event or DOMString)? union2AttributeNullable;
void receiveVoid(); void receiveVoid();
boolean receiveBoolean(); boolean receiveBoolean();
@ -137,7 +137,7 @@ interface TestBinding {
void passEnum(TestEnum arg); void passEnum(TestEnum arg);
void passInterface(Blob arg); void passInterface(Blob arg);
void passUnion((HTMLElement or long) arg); void passUnion((HTMLElement or long) arg);
void passUnion2((DOMString or FormData) data); void passUnion2((Event or DOMString) data);
void passAny(any arg); void passAny(any arg);
void passNullableBoolean(boolean? arg); void passNullableBoolean(boolean? arg);
@ -156,7 +156,7 @@ interface TestBinding {
// void passNullableEnum(TestEnum? arg); // void passNullableEnum(TestEnum? arg);
void passNullableInterface(Blob? arg); void passNullableInterface(Blob? arg);
void passNullableUnion((HTMLElement or long)? arg); void passNullableUnion((HTMLElement or long)? arg);
void passNullableUnion2((DOMString or FormData)? data); void passNullableUnion2((Event or DOMString)? data);
void passOptionalBoolean(optional boolean arg); void passOptionalBoolean(optional boolean arg);
void passOptionalByte(optional byte arg); void passOptionalByte(optional byte arg);
@ -174,7 +174,7 @@ interface TestBinding {
void passOptionalEnum(optional TestEnum arg); void passOptionalEnum(optional TestEnum arg);
void passOptionalInterface(optional Blob arg); void passOptionalInterface(optional Blob arg);
void passOptionalUnion(optional (HTMLElement or long) arg); void passOptionalUnion(optional (HTMLElement or long) arg);
void passOptionalUnion2(optional (DOMString or FormData) data); void passOptionalUnion2(optional (Event or DOMString) data);
void passOptionalAny(optional any arg); void passOptionalAny(optional any arg);
void passOptionalNullableBoolean(optional boolean? arg); void passOptionalNullableBoolean(optional boolean? arg);
@ -193,7 +193,7 @@ interface TestBinding {
// void passOptionalNullableEnum(optional TestEnum? arg); // void passOptionalNullableEnum(optional TestEnum? arg);
void passOptionalNullableInterface(optional Blob? arg); void passOptionalNullableInterface(optional Blob? arg);
void passOptionalNullableUnion(optional (HTMLElement or long)? arg); void passOptionalNullableUnion(optional (HTMLElement or long)? arg);
void passOptionalNullableUnion2(optional (DOMString or FormData)? data); void passOptionalNullableUnion2(optional (Event or DOMString)? data);
void passOptionalBooleanWithDefault(optional boolean arg = false); void passOptionalBooleanWithDefault(optional boolean arg = false);
void passOptionalByteWithDefault(optional byte arg = 0); void passOptionalByteWithDefault(optional byte arg = 0);
@ -207,7 +207,7 @@ interface TestBinding {
void passOptionalStringWithDefault(optional DOMString arg = ""); void passOptionalStringWithDefault(optional DOMString arg = "");
void passOptionalEnumWithDefault(optional TestEnum arg = "foo"); void passOptionalEnumWithDefault(optional TestEnum arg = "foo");
// void passOptionalUnionWithDefault(optional (HTMLElement or long) arg = 9); // void passOptionalUnionWithDefault(optional (HTMLElement or long) arg = 9);
// void passOptionalUnion2WithDefault(optional(DOMString or FormData)? data = "foo"); // void passOptionalUnion2WithDefault(optional(Event or DOMString)? data = "foo");
void passOptionalNullableBooleanWithDefault(optional boolean? arg = null); void passOptionalNullableBooleanWithDefault(optional boolean? arg = null);
void passOptionalNullableByteWithDefault(optional byte? arg = null); void passOptionalNullableByteWithDefault(optional byte? arg = null);
@ -223,7 +223,7 @@ interface TestBinding {
// void passOptionalNullableEnumWithDefault(optional TestEnum? arg = null); // void passOptionalNullableEnumWithDefault(optional TestEnum? arg = null);
void passOptionalNullableInterfaceWithDefault(optional Blob? arg = null); void passOptionalNullableInterfaceWithDefault(optional Blob? arg = null);
void passOptionalNullableUnionWithDefault(optional (HTMLElement or long)? arg = null); void passOptionalNullableUnionWithDefault(optional (HTMLElement or long)? arg = null);
void passOptionalNullableUnion2WithDefault(optional (DOMString or FormData)? data = null); void passOptionalNullableUnion2WithDefault(optional (Event or DOMString)? data = null);
void passOptionalAnyWithDefault(optional any arg = null); void passOptionalAnyWithDefault(optional any arg = null);
void passOptionalNullableBooleanWithNonNullDefault(optional boolean? arg = false); void passOptionalNullableBooleanWithNonNullDefault(optional boolean? arg = false);
@ -240,5 +240,5 @@ interface TestBinding {
void passOptionalNullableStringWithNonNullDefault(optional DOMString? arg = ""); void passOptionalNullableStringWithNonNullDefault(optional DOMString? arg = "");
// void passOptionalNullableEnumWithNonNullDefault(optional TestEnum? arg = "foo"); // void passOptionalNullableEnumWithNonNullDefault(optional TestEnum? arg = "foo");
// void passOptionalNullableUnionWithNonNullDefault(optional (HTMLElement or long)? arg = 7); // void passOptionalNullableUnionWithNonNullDefault(optional (HTMLElement or long)? arg = 7);
// void passOptionalNullableUnion2WithNonNullDefault(optional (DOMString or FormData)? data = "foo"); // void passOptionalNullableUnion2WithNonNullDefault(optional (Event or DOMString)? data = "foo");
}; };

View file

@ -84,6 +84,9 @@ interface WindowTimers {
void clearInterval(optional long handle = 0);*/ void clearInterval(optional long handle = 0);*/
}; };
Window implements WindowTimers; Window implements WindowTimers;
Window implements GlobalEventHandlers;
Window implements WindowEventHandlers;
Window implements OnErrorEventHandlerForWindow;
// Proprietary extensions. // Proprietary extensions.
partial interface Window { partial interface Window {

View file

@ -3,13 +3,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::BindingDeclarations::WindowBinding; use dom::bindings::codegen::BindingDeclarations::WindowBinding;
use dom::bindings::codegen::EventHandlerBinding::{OnErrorEventHandlerNonNull, EventHandlerNonNull};
use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable}; use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable};
use dom::bindings::trace::{Traceable, Untraceable}; use dom::bindings::trace::{Traceable, Untraceable};
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::browsercontext::BrowserContext; use dom::browsercontext::BrowserContext;
use dom::document::Document; use dom::document::Document;
use dom::element::Element; use dom::element::Element;
use dom::eventtarget::{EventTarget, WindowTypeId}; use dom::eventtarget::{EventTarget, WindowTypeId, EventTargetHelpers};
use dom::console::Console; use dom::console::Console;
use dom::location::Location; use dom::location::Location;
use dom::navigator::Navigator; use dom::navigator::Navigator;
@ -140,6 +142,12 @@ pub trait WindowMethods {
fn Window(&self) -> Temporary<Window>; fn Window(&self) -> Temporary<Window>;
fn Self(&self) -> Temporary<Window>; fn Self(&self) -> Temporary<Window>;
fn Performance(&mut self) -> Temporary<Performance>; fn Performance(&mut self) -> Temporary<Performance>;
fn GetOnload(&self) -> Option<EventHandlerNonNull>;
fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>);
fn GetOnunload(&self) -> Option<EventHandlerNonNull>;
fn SetOnunload(&mut self, listener: Option<EventHandlerNonNull>);
fn GetOnerror(&self) -> Option<OnErrorEventHandlerNonNull>;
fn SetOnerror(&mut self, listener: Option<OnErrorEventHandlerNonNull>);
fn Debug(&self, message: DOMString); fn Debug(&self, message: DOMString);
fn Gc(&self); fn Gc(&self);
} }
@ -268,6 +276,36 @@ impl<'a> WindowMethods for JSRef<'a, Window> {
Temporary::new(self.performance.get_ref().clone()) Temporary::new(self.performance.get_ref().clone())
} }
fn GetOnload(&self) -> Option<EventHandlerNonNull> {
let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
eventtarget.get_event_handler_common("load")
}
fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>) {
let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self);
eventtarget.set_event_handler_common("load", listener)
}
fn GetOnunload(&self) -> Option<EventHandlerNonNull> {
let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
eventtarget.get_event_handler_common("unload")
}
fn SetOnunload(&mut self, listener: Option<EventHandlerNonNull>) {
let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self);
eventtarget.set_event_handler_common("unload", listener)
}
fn GetOnerror(&self) -> Option<OnErrorEventHandlerNonNull> {
let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
eventtarget.get_event_handler_common("error")
}
fn SetOnerror(&mut self, listener: Option<OnErrorEventHandlerNonNull>) {
let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self);
eventtarget.set_event_handler_common("error", listener)
}
fn Debug(&self, message: DOMString) { fn Debug(&self, message: DOMString) {
debug!("{:s}", message); debug!("{:s}", message);
} }

View file

@ -48,6 +48,7 @@ pub mod dom {
pub mod str; pub mod str;
pub mod trace; pub mod trace;
pub mod codegen { pub mod codegen {
pub mod EventHandlerBinding;
pub mod InterfaceTypes; pub mod InterfaceTypes;
pub mod InheritTypes; pub mod InheritTypes;
pub mod PrototypeList; pub mod PrototypeList;

View file

@ -0,0 +1,18 @@
<html>
<head>
<script src="harness.js"></script>
<script>
function check_onload(listener) {
is(listener, window);
var ev = new Event('click', {bubbles: true, cancelable: true});
document.body.dispatchEvent(ev);
}
function check_onclick(listener) {
is(listener, document.body);
finish();
}
</script>
</head>
<body onload="check_onload(this)" onclick="check_onclick(this)">
</body>
</html>

View file

@ -0,0 +1,9 @@
<html>
<head>
<script src="harness.js"></script>
</head>
<body onload="is_a(event, Event); finish()">
<script>
</script>
</body>
</html>

View file

@ -4,13 +4,30 @@
</head> </head>
<body> <body>
<script> <script>
addEventListener("load", function(ev) { var onloads = 0;
function check(ev) {
is_a(ev, Event); is_a(ev, Event);
ev.preventDefault(); ev.preventDefault();
is(ev.defaultPrevented, false); is(ev.defaultPrevented, false);
is(ev.target, document); is(ev.target, document);
is(ev.currentTarget, window); is(ev.currentTarget, window);
if (onloads == 2) {
finish(); finish();
}
}
window.onload = function(ev) {
_fail("this inline handler should be overwritten");
}
window.onload = function(ev) {
onloads++;
is(onloads, 1);
check(ev);
}
addEventListener("load", function(ev) {
onloads++;
is(onloads, 2);
check(ev);
}); });
</script> </script>
</body> </body>