Port modern callback handling code from Gecko, and copy related WebIDL parser bits too.

This commit is contained in:
Josh Matthews 2014-04-22 17:14:48 -04:00 committed by Ms2ger
parent 04931adf70
commit 003e5bcd46
18 changed files with 442 additions and 223 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

@ -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}.is_object() && JS_ObjectIsCallable(cx, ${val}.to_object()) != 0" 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,
" ${val}.to_object()\n" CGGeneric("None")).define()
"} else {\n" else:
" ptr::mut_null()\n" template = conversion
"}" % haveCallable, else:
CGGeneric("*mut JSObject"), 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():
@ -1347,12 +1379,8 @@ class CGIfWrapper(CGWrapper):
post="\n}") post="\n}")
class CGTemplatedType(CGWrapper): class CGTemplatedType(CGWrapper):
def __init__(self, templateName, child, isConst=False, isReference=False): def __init__(self, templateName, child):
const = "const " if isConst else "" CGWrapper.__init__(self, child, pre=templateName + "<", post=">")
pre = "%s%s<" % (const, templateName)
ref = "&" if isReference else ""
post = ">%s" % ref
CGWrapper.__init__(self, child, pre=pre, post=post)
class CGNamespace(CGWrapper): class CGNamespace(CGWrapper):
def __init__(self, namespace, child, public=False): def __init__(self, namespace, child, public=False):
@ -1573,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):
""" """
@ -1606,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")
@ -2309,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
@ -2325,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):
@ -2710,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):
@ -4170,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")
@ -4236,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}',
@ -4322,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
@ -4363,9 +4420,9 @@ 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():
if type.nullable(): if type.nullable():
returnCode = "return ${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();" returnCode = "return ${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();"
@ -4462,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
@ -4492,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():
@ -4528,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():
@ -4570,7 +4624,7 @@ class CGNativeMember(ClassMethod):
# 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("Option", decl) decl = CGTemplatedType("Option", decl)
ref = True ref = False
return (decl, ref) return (decl, ref)
def getArg(self, arg): def getArg(self, arg):
@ -4581,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)
@ -4649,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,
@ -4707,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):
@ -4817,7 +4881,7 @@ 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()
@ -4876,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(
@ -4899,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
@ -4946,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):
@ -4999,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):
""" """
@ -5012,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 = {
@ -5030,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):
@ -5242,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

@ -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 {
@ -350,7 +356,11 @@ impl<X: Default, T: FromJSValConvertible<X>> FromJSValConvertible<()> for Option
} }
impl ToJSValConvertible for *mut JSObject { impl ToJSValConvertible for *mut JSObject {
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
ObjectOrNullValue(*self) 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,6 +2,7 @@
* 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};
@ -45,7 +46,7 @@ use servo_util::namespace::{Namespace, Null};
use servo_util::str::{DOMString, null_str_as_empty_ref}; use servo_util::str::{DOMString, null_str_as_empty_ref};
use collections::hashmap::HashMap; use collections::hashmap::HashMap;
use js::jsapi::{JSObject, JSContext}; use js::jsapi::JSContext;
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
use url::{Url, from_str}; use url::{Url, from_str};
@ -326,8 +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, _cx: *mut JSContext) -> *mut JSObject; fn GetOnload(&self) -> Option<EventHandlerNonNull>;
fn SetOnload(&mut self, _cx: *mut JSContext, listener: *mut JSObject); fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>);
} }
impl<'a> DocumentMethods for JSRef<'a, Document> { impl<'a> DocumentMethods for JSRef<'a, Document> {
@ -808,12 +809,12 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
HTMLCollection::children(&*window, NodeCast::from_ref(self)) HTMLCollection::children(&*window, NodeCast::from_ref(self))
} }
fn GetOnload(&self, _cx: *mut JSContext) -> *mut JSObject { fn GetOnload(&self) -> Option<EventHandlerNonNull> {
let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
eventtarget.get_event_handler_common("load") eventtarget.get_event_handler_common("load")
} }
fn SetOnload(&mut self, _cx: *mut JSContext, listener: *mut JSObject) { fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>) {
let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self); let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self);
eventtarget.set_event_handler_common("load", listener) 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

@ -4,18 +4,21 @@
use dom::bindings::callback::CallbackContainer; use dom::bindings::callback::CallbackContainer;
use dom::bindings::codegen::BindingDeclarations::EventListenerBinding::EventListener; use dom::bindings::codegen::BindingDeclarations::EventListenerBinding::EventListener;
use dom::bindings::codegen::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::error::{Fallible, InvalidState}; 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::event::Event; use dom::event::Event;
use dom::eventdispatcher::dispatch_event; use dom::eventdispatcher::dispatch_event;
use dom::node::{Node, NodeTypeId, window_from_node}; use dom::node::NodeTypeId;
use dom::xmlhttprequest::XMLHttpRequestId; use dom::xmlhttprequest::XMLHttpRequestId;
use dom::virtualmethods::VirtualMethods; use dom::virtualmethods::VirtualMethods;
use js::jsapi::{JSObject, JS_CompileUCFunction, JS_GetFunctionObject, JS_CloneFunctionObject}; 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 libc::{c_char, size_t};
use std::ptr; use std::ptr;
use url::Url;
use collections::hashmap::HashMap; use collections::hashmap::HashMap;
@ -92,11 +95,14 @@ pub trait EventTargetHelpers {
listener: Option<EventListener>); listener: Option<EventListener>);
fn get_inline_event_listener(&self, ty: DOMString) -> Option<EventListener>; fn get_inline_event_listener(&self, ty: DOMString) -> Option<EventListener>;
fn set_event_handler_uncompiled(&mut self, fn set_event_handler_uncompiled(&mut self,
content: &JSRef<Node>, cx: *mut JSContext,
url: Url,
scope: *mut JSObject,
ty: &str, ty: &str,
source: DOMString); source: DOMString);
fn set_event_handler_common(&mut self, ty: &str, listener: *mut JSObject); fn set_event_handler_common<T: CallbackContainer>(&mut self, ty: &str,
fn get_event_handler_common(&self, ty: &str) -> *mut JSObject; 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> {
@ -132,7 +138,7 @@ impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> {
None => { None => {
if listener.is_some() { if listener.is_some() {
entries.push(EventListenerEntry { entries.push(EventListenerEntry {
phase: Capturing, //XXXjdm no idea when inline handlers should run phase: Bubbling,
listener: Inline(listener.unwrap()), listener: Inline(listener.unwrap()),
}); });
} }
@ -151,12 +157,11 @@ impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> {
} }
fn set_event_handler_uncompiled(&mut self, fn set_event_handler_uncompiled(&mut self,
content: &JSRef<Node>, cx: *mut JSContext,
url: Url,
scope: *mut JSObject,
ty: &str, ty: &str,
source: DOMString) { source: DOMString) {
let win = window_from_node(content).root();
let cx = win.deref().get_cx();
let url = win.deref().get_url();
let url = url.to_str().to_c_str(); let url = url.to_str().to_c_str();
let name = ty.to_c_str(); let name = ty.to_c_str();
let lineno = 0; //XXXjdm need to get a real number here let lineno = 0; //XXXjdm need to get a real number here
@ -177,20 +182,22 @@ impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> {
assert!(fun.is_not_null()); assert!(fun.is_not_null());
JS_GetFunctionObject(fun) JS_GetFunctionObject(fun)
}})}); }})});
let scope = win.deref().reflector().get_jsobject();
let funobj = unsafe { JS_CloneFunctionObject(cx, handler, scope) }; let funobj = unsafe { JS_CloneFunctionObject(cx, handler, scope) };
assert!(funobj.is_not_null()); assert!(funobj.is_not_null());
self.set_event_handler_common(ty, funobj) self.set_event_handler_common(ty, Some(EventHandlerNonNull::new(funobj)))
} }
fn set_event_handler_common(&mut self, ty: &str, listener: *mut JSObject) { fn set_event_handler_common<T: CallbackContainer>(
let listener = EventListener::new(listener); &mut self, ty: &str, listener: Option<T>)
self.set_inline_event_listener(ty.to_owned(), Some(listener)); {
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(&self, ty: &str) -> *mut JSObject { fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<T> {
let listener = self.get_inline_event_listener(ty.to_owned()); let listener = self.get_inline_event_listener(ty.to_owned());
listener.map(|listener| listener.parent.callback()).unwrap_or(ptr::mut_null()) listener.map(|listener| CallbackContainer::new(listener.parent.callback()))
} }
} }

View file

@ -3,10 +3,12 @@
* 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::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLElementCast}; use dom::bindings::codegen::InheritTypes::{HTMLBodyElementDerived, HTMLElementCast};
use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast}; 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, EventTargetHelpers}; use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers};
@ -14,7 +16,6 @@ use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId, window_from_node}; use dom::node::{Node, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods; use dom::virtualmethods::VirtualMethods;
use dom::window::WindowMethods; use dom::window::WindowMethods;
use js::jsapi::{JSContext, JSObject};
use servo_util::str::DOMString; use servo_util::str::DOMString;
#[deriving(Encodable)] #[deriving(Encodable)]
@ -54,8 +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, cx: *mut JSContext) -> *mut JSObject; fn GetOnunload(&self) -> Option<EventHandlerNonNull>;
fn SetOnunload(&mut self, cx: *mut JSContext, listener: *mut JSObject); fn SetOnunload(&mut self, listener: Option<EventHandlerNonNull>);
} }
impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> { impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> {
@ -107,14 +108,14 @@ impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> {
Ok(()) Ok(())
} }
fn GetOnunload(&self, cx: *mut JSContext) -> *mut JSObject { fn GetOnunload(&self) -> Option<EventHandlerNonNull> {
let win = window_from_node(self).root(); let win = window_from_node(self).root();
win.deref().GetOnunload(cx) win.deref().GetOnunload()
} }
fn SetOnunload(&mut self, cx: *mut JSContext, listener: *mut JSObject) { fn SetOnunload(&mut self, listener: Option<EventHandlerNonNull>) {
let mut win = window_from_node(self).root(); let mut win = window_from_node(self).root();
win.SetOnunload(cx, listener) win.SetOnunload(listener)
} }
} }
@ -131,11 +132,22 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLBodyElement> {
} }
if name.starts_with("on") { if name.starts_with("on") {
//XXXjdm This should only forward a subset of event handler names 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 mut window = window_from_node(self).root();
let mut evtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(&mut *window); let (cx, url, reflector) = (window.get_cx(),
let content: &mut JSRef<Node> = NodeCast::from_mut_ref(self); window.get_url(),
evtarget.set_event_handler_uncompiled(content, 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(), name.slice_from(2).to_owned(),
value); value);
} }

View file

@ -3,6 +3,7 @@
* 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::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFrameSetElementDerived}; use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFrameSetElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLElementDerived, HTMLBodyElementDerived}; use dom::bindings::codegen::InheritTypes::{HTMLElementDerived, HTMLBodyElementDerived};
use dom::bindings::codegen::InheritTypes::EventTargetCast; use dom::bindings::codegen::InheritTypes::EventTargetCast;
@ -14,11 +15,10 @@ use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::node::{Node, ElementNodeTypeId, window_from_node}; use dom::node::{Node, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods; use dom::virtualmethods::VirtualMethods;
use dom::window::WindowMethods; use dom::window::WindowMethods;
use js::jsapi::{JSContext, JSObject}; use js::jsapi::JSContext;
use js::jsval::{JSVal, NullValue}; use js::jsval::{JSVal, NullValue};
use servo_util::namespace; use servo_util::namespace;
use servo_util::str::DOMString; use servo_util::str::DOMString;
use std::ptr;
#[deriving(Encodable)] #[deriving(Encodable)]
pub struct HTMLElement { pub struct HTMLElement {
@ -90,8 +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, cx: *mut JSContext) -> *mut JSObject; fn GetOnload(&self) -> Option<EventHandlerNonNull>;
fn SetOnload(&mut self, cx: *mut JSContext, listener: *mut JSObject); fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>);
} }
impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> { impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> {
@ -212,19 +212,19 @@ impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> {
0 0
} }
fn GetOnload(&self, cx: *mut JSContext) -> *mut JSObject { fn GetOnload(&self) -> Option<EventHandlerNonNull> {
if self.is_body_or_frameset() { if self.is_body_or_frameset() {
let win = window_from_node(self).root(); let win = window_from_node(self).root();
win.deref().GetOnload(cx) win.deref().GetOnload()
} else { } else {
ptr::mut_null() None
} }
} }
fn SetOnload(&mut self, cx: *mut JSContext, listener: *mut JSObject) { fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>) {
if self.is_body_or_frameset() { if self.is_body_or_frameset() {
let mut win = window_from_node(self).root(); let mut win = window_from_node(self).root();
win.SetOnload(cx, listener) win.SetOnload(listener)
} }
} }
} }

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

@ -11,11 +11,11 @@
* and create derivative works of this document. * and create derivative works of this document.
*/ */
//[TreatNonObjectAsNull] //XXXjdm webidl.py assertion [TreatNonObjectAsNull]
callback EventHandlerNonNull = any (Event event); callback EventHandlerNonNull = any (Event event);
typedef EventHandlerNonNull? EventHandler; typedef EventHandlerNonNull? EventHandler;
//[TreatNonObjectAsNull] //XXXjdm webidl.py assertion [TreatNonObjectAsNull]
callback OnErrorEventHandlerNonNull = boolean ((Event or DOMString) event, optional DOMString source, optional unsigned long lineno, optional unsigned long column, optional any error); callback OnErrorEventHandlerNonNull = boolean ((Event or DOMString) event, optional DOMString source, optional unsigned long lineno, optional unsigned long column, optional any error);
typedef OnErrorEventHandlerNonNull? OnErrorEventHandler; typedef OnErrorEventHandlerNonNull? OnErrorEventHandler;

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

@ -3,6 +3,7 @@
* 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::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};
@ -24,7 +25,7 @@ use servo_util::str::DOMString;
use servo_util::task::{spawn_named}; use servo_util::task::{spawn_named};
use servo_util::url::parse_url; use servo_util::url::parse_url;
use js::jsapi::{JSContext, JSObject}; use js::jsapi::JSContext;
use js::jsapi::{JS_GC, JS_GetRuntime}; use js::jsapi::{JS_GC, JS_GetRuntime};
use js::jsval::{NullValue, JSVal}; use js::jsval::{NullValue, JSVal};
@ -141,12 +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, _cx: *mut JSContext) -> *mut JSObject; fn GetOnload(&self) -> Option<EventHandlerNonNull>;
fn SetOnload(&mut self, _cx: *mut JSContext, listener: *mut JSObject); fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>);
fn GetOnunload(&self, _cx: *mut JSContext) -> *mut JSObject; fn GetOnunload(&self) -> Option<EventHandlerNonNull>;
fn SetOnunload(&mut self, _cx: *mut JSContext, listener: *mut JSObject); fn SetOnunload(&mut self, listener: Option<EventHandlerNonNull>);
fn GetOnerror(&self, _cx: *mut JSContext) -> *mut JSObject; fn GetOnerror(&self) -> Option<OnErrorEventHandlerNonNull>;
fn SetOnerror(&mut self, _cx: *mut JSContext, listener: *mut JSObject); fn SetOnerror(&mut self, listener: Option<OnErrorEventHandlerNonNull>);
fn Debug(&self, message: DOMString); fn Debug(&self, message: DOMString);
fn Gc(&self); fn Gc(&self);
} }
@ -275,32 +276,32 @@ impl<'a> WindowMethods for JSRef<'a, Window> {
Temporary::new(self.performance.get_ref().clone()) Temporary::new(self.performance.get_ref().clone())
} }
fn GetOnload(&self, _cx: *mut JSContext) -> *mut JSObject { fn GetOnload(&self) -> Option<EventHandlerNonNull> {
let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
eventtarget.get_event_handler_common("load") eventtarget.get_event_handler_common("load")
} }
fn SetOnload(&mut self, _cx: *mut JSContext, listener: *mut JSObject) { fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>) {
let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self); let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self);
eventtarget.set_event_handler_common("load", listener) eventtarget.set_event_handler_common("load", listener)
} }
fn GetOnunload(&self, _cx: *mut JSContext) -> *mut JSObject { fn GetOnunload(&self) -> Option<EventHandlerNonNull> {
let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
eventtarget.get_event_handler_common("unload") eventtarget.get_event_handler_common("unload")
} }
fn SetOnunload(&mut self, _cx: *mut JSContext, listener: *mut JSObject) { fn SetOnunload(&mut self, listener: Option<EventHandlerNonNull>) {
let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self); let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self);
eventtarget.set_event_handler_common("unload", listener) eventtarget.set_event_handler_common("unload", listener)
} }
fn GetOnerror(&self, _cx: *mut JSContext) -> *mut JSObject { fn GetOnerror(&self) -> Option<OnErrorEventHandlerNonNull> {
let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self);
eventtarget.get_event_handler_common("error") eventtarget.get_event_handler_common("error")
} }
fn SetOnerror(&mut self, _cx: *mut JSContext, listener: *mut JSObject) { fn SetOnerror(&mut self, listener: Option<OnErrorEventHandlerNonNull>) {
let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self); let eventtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(self);
eventtarget.set_event_handler_common("error", listener) eventtarget.set_event_handler_common("error", listener)
} }
@ -429,6 +430,7 @@ impl<'a> PrivateWindowHelpers for JSRef<'a, Window> {
handle handle
} }
} }
impl Window { impl Window {
pub fn new(cx: *mut JSContext, pub fn new(cx: *mut JSContext,
page: Rc<Page>, page: Rc<Page>,

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

@ -21,10 +21,12 @@
} }
window.onload = function(ev) { window.onload = function(ev) {
onloads++; onloads++;
is(onloads, 1);
check(ev); check(ev);
} }
addEventListener("load", function(ev) { addEventListener("load", function(ev) {
onloads++; onloads++;
is(onloads, 2);
check(ev); check(ev);
}); });
</script> </script>