diff options
Diffstat (limited to 'src')
18 files changed, 441 insertions, 222 deletions
diff --git a/src/components/script/dom/bindings/callback.rs b/src/components/script/dom/bindings/callback.rs index 8a29c382ca9..deebc53d6e3 100644 --- a/src/components/script/dom/bindings/callback.rs +++ b/src/components/script/dom/bindings/callback.rs @@ -2,13 +2,13 @@ * 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/. */ -use dom::bindings::trace::trace_object; -use dom::bindings::utils::Reflectable; +use dom::bindings::js::JSRef; +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::{JS_GetProperty, JSTracer}; +use js::jsapi::JS_GetProperty; use js::jsval::{JSVal, UndefinedValue}; -use std::cast; use std::ptr; use serialize::{Encodable, Encoder}; @@ -24,42 +24,61 @@ pub enum ExceptionHandling { RethrowExceptions } -#[deriving(Clone,Eq)] -pub struct CallbackInterface { - pub callback: *mut JSObject +#[deriving(Clone,Eq,Encodable)] +pub struct CallbackFunction { + object: CallbackObject } -impl<S: Encoder<E>, E> Encodable<S, E> for CallbackInterface { - fn encode(&self, s: &mut S) -> Result<(), E> { - unsafe { - let tracer: *mut JSTracer = cast::transmute(s); - trace_object(tracer, "callback", self.callback); +impl CallbackFunction { + pub fn new(callback: *mut JSObject) -> CallbackFunction { + CallbackFunction { + object: CallbackObject { + 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 { + fn new(callback: *mut JSObject) -> Self; fn callback(&self) -> *mut JSObject; } -impl CallbackContainer for CallbackInterface { - fn callback(&self) -> *mut JSObject { - self.callback +impl CallbackInterface { + pub fn callback(&self) -> *mut JSObject { + *self.object.callback + } +} + +impl CallbackFunction { + pub fn callback(&self) -> *mut JSObject { + *self.object.callback } } impl CallbackInterface { pub fn new(callback: *mut JSObject) -> CallbackInterface { CallbackInterface { - callback: callback + object: CallbackObject { + callback: Traceable::new(callback) + } } } pub fn GetCallableProperty(&self, cx: *mut JSContext, name: &str) -> Result<JSVal, ()> { let mut callable = UndefinedValue(); 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(()); } @@ -73,14 +92,9 @@ impl CallbackInterface { } } -pub fn GetJSObjectFromCallback<T: CallbackContainer>(callback: &T) -> *mut JSObject { - callback.callback() -} - -pub fn WrapCallThisObject<T: 'static + CallbackContainer + Reflectable>(cx: *mut JSContext, - _scope: *mut JSObject, - p: Box<T>) -> *mut JSObject { - let mut obj = GetJSObjectFromCallback(p); +pub fn WrapCallThisObject<T: Reflectable>(cx: *mut JSContext, + p: &JSRef<T>) -> *mut JSObject { + let mut obj = p.reflector().get_jsobject(); assert!(obj.is_not_null()); unsafe { @@ -98,7 +112,9 @@ pub struct 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 { cx: cx, handling: handling diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 9fca81cd287..a6da30bad29 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -7,9 +7,10 @@ import os import string import operator +import itertools from WebIDL import * -from Configuration import Descriptor +from Configuration import getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor AUTOGENERATED_WARNING_COMMENT = \ "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n" @@ -406,6 +407,7 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None, isEnforceRange=False, isClamp=False, exceptionCode=None, + allowTreatNonObjectAsNull=False, isCallbackReturnValue=False, 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 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: 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, exceptionCode))), 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 # default value, if it exists, is null. @@ -699,23 +712,39 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None, if type.isCallback(): 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: - raise TypeError("Can't handle member callbacks; need to sort out " - "rooting issues") - # XXXbz we're going to assume that callback types are always - # nullable and always have [TreatNonCallableAsNull] for now. - haveCallable = "${val}.is_object() && JS_ObjectIsCallable(cx, ${val}.to_object()) != 0" - if defaultValue is not None: - assert(isinstance(defaultValue, IDLNullValue)) - haveCallable = "${haveValue} && " + haveCallable - return ( - "if (%s) {\n" - " ${val}.to_object()\n" - "} else {\n" - " ptr::mut_null()\n" - "}" % haveCallable, - CGGeneric("*mut JSObject"), needsRooting) + declType = CGGeneric('%s::%s' % (type.unroll().module(), type.unroll().identifier.name)) + + conversion = CGCallbackTempRoot(declType.define()) + + if type.nullable(): + 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: + assert isinstance(defaultValue, IDLNullValue) + haveObject = "${haveValue} && " + haveObject + template = CGIfElseWrapper(haveObject, + conversion, + CGGeneric("None")).define() + else: + template = conversion + else: + 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(): assert not isEnforceRange and not isClamp @@ -874,7 +903,8 @@ class CGArgumentConverter(CGThing): defaultValue=argument.defaultValue, treatNullAs=argument.treatNullAs, isEnforceRange=argument.enforceRange, - isClamp=argument.clamp) + isClamp=argument.clamp, + allowTreatNonObjectAsNull=argument.allowTreatNonCallableAsNull()) if argument.optional and not argument.defaultValue: 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) if retVal and type.isSpiderMonkeyInterface(): return True - return type.isCallback() or type.isAny() or type.isObject() + return type.isAny() or type.isObject() def typeRetValNeedsRooting(type): if type is None: @@ -958,9 +988,11 @@ def getRetvalDeclarationForType(returnType, descriptorProvider): result = CGWrapper(result, pre="Option<", post=">") return result if returnType.isCallback(): - # XXXbz we're going to assume that callback types are always - # nullable for now. - return CGGeneric("*mut JSObject") + result = CGGeneric('%s::%s' % (returnType.unroll().module(), + returnType.unroll().identifier.name)) + if returnType.nullable(): + result = CGWrapper(result, pre="Option<", post=">") + return result if returnType.isAny(): return CGGeneric("JSVal") if returnType.isObject() or returnType.isSpiderMonkeyInterface(): @@ -1347,12 +1379,8 @@ class CGIfWrapper(CGWrapper): post="\n}") class CGTemplatedType(CGWrapper): - def __init__(self, templateName, child, isConst=False, isReference=False): - const = "const " if isConst else "" - pre = "%s%s<" % (const, templateName) - ref = "&" if isReference else "" - post = ">%s" % ref - CGWrapper.__init__(self, child, pre=pre, post=post) + def __init__(self, templateName, child): + CGWrapper.__init__(self, child, pre=templateName + "<", post=">") class CGNamespace(CGWrapper): def __init__(self, namespace, child, public=False): @@ -1573,23 +1601,32 @@ class CGGeneric(CGThing): def define(self): return self.text -def getTypes(descriptor): +class CGCallbackTempRoot(CGGeneric): + def __init__(self, name): + val = "%s::new(tempRoot)" % name + define = """{ + let tempRoot = ${val}.to_object(); + %s +}""" % val + CGGeneric.__init__(self, define) + + +def getAllTypes(descriptors, dictionaries, callbacks): """ - Get all argument and return types for all members of the descriptor + 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. """ - members = [m for m in descriptor.interface.members] - if descriptor.interface.ctor(): - members.append(descriptor.interface.ctor()) - 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 + 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): """ @@ -1606,24 +1643,23 @@ def SortedDictValues(d): # We're only interested in the values. 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 - 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. + Returns a CGList containing CGUnionStructs for every union. """ # Now find all the things we'll need as arguments and return values because # we need to wrap or unwrap them. unionStructs = dict() - for d in descriptors: - for t in getTypes(d): - t = t.unroll() - if t.isUnion(): - name = str(t) - if not name in unionStructs: - unionStructs[name] = CGList([CGUnionStruct(t, d), CGUnionConversionStruct(t, d)]) + for (t, descriptor, dictionary) in getAllTypes(descriptors, dictionaries, callbacks): + assert not descriptor or not dictionary + t = t.unroll() + if not t.isUnion(): + continue + name = str(t) + if not name in unionStructs: + provider = descriptor or config.getDescriptorProvider() + unionStructs[name] = CGList([CGUnionStruct(t, provider), CGUnionConversionStruct(t, provider)]) return CGList(SortedDictValues(unionStructs), "\n\n") @@ -2309,15 +2345,19 @@ class FakeArgument(): A class that quacks like an IDLArgument. This is used to make 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.optional = False self.variadic = False self.defaultValue = None + self._allowTreatNonObjectAsNull = allowTreatNonObjectAsNull self.treatNullAs = interfaceMember.treatNullAs self.enforceRange = False self.clamp = False + def allowTreatNonCallableAsNull(self): + return self._allowTreatNonObjectAsNull + class CGSetterCall(CGPerSignatureCall): """ 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): CGPerSignatureCall.__init__(self, None, argsPre, - [FakeArgument(argType, attr)], + [FakeArgument(argType, attr, allowTreatNonObjectAsNull=True)], nativeMethodName, False, descriptor, attr, setter=True) def wrap_return_value(self): @@ -2710,9 +2750,22 @@ class CGUnionStruct(CGThing): enumValues = [ " e%s(%s)," % (v["name"], v["typeName"]) for v in templateVars ] - return ("pub enum %s {\n" - "%s\n" - "}\n") % (self.type, "\n".join(enumValues)) + enumConversions = [ + " e%s(ref inner) => inner.to_jsval(cx)," % v["name"] for v in templateVars + ] + 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): @@ -4170,14 +4223,17 @@ class CGBindingRoot(CGThing): for d in dictionaries]) # 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) # Do codegen for all the descriptors cgthings.extend([CGDescriptor(x) for x in descriptors]) # 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 curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") @@ -4236,7 +4292,7 @@ class CGBindingRoot(CGThing): 'dom::bindings::utils::VoidVal', 'dom::bindings::utils::get_dictionary_property', 'dom::bindings::trace::JSTraceable', - 'dom::bindings::callback::{CallbackContainer,CallbackInterface}', + 'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}', 'dom::bindings::callback::{CallSetup,ExceptionHandling}', 'dom::bindings::callback::{WrapCallThisObject}', 'dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible}', @@ -4322,8 +4378,9 @@ class CGNativeMember(ClassMethod): elif type.isPrimitive() and type.tag() in builtinNames: result = CGGeneric(builtinNames[type.tag()]) if type.nullable(): - result = CGTemplatedType("Nullable", result) - typeDecl, template = result.define(), "return ${declName};" + raise TypeError("Nullable primitives are not supported here.") + + typeDecl, template = result.define(), "return Ok(${declName});" elif type.isDOMString(): if isMember: # No need for a third element in the isMember case @@ -4363,9 +4420,9 @@ class CGNativeMember(ClassMethod): ("already_AddRefed<%s>" % type.unroll().identifier.name, "return ${declName}.forget();") elif type.isAny(): - typeDecl, template = "JS::Value", "return ${declName};" + typeDecl, template = "JSVal", "return Ok(${declName});" elif type.isObject(): - typeDecl, template = "*JSObject", "return ${declName};" + typeDecl, template = "JSObject*", "return ${declName};" elif type.isSpiderMonkeyInterface(): if type.nullable(): returnCode = "return ${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();" @@ -4462,7 +4519,7 @@ class CGNativeMember(ClassMethod): if type.isUnion(): if type.nullable(): type = type.inner - return str(type), True, True + return str(type), False, True if type.isGeckoInterface() and not type.isCallbackInterface(): iface = type.unroll().inner @@ -4492,10 +4549,7 @@ class CGNativeMember(ClassMethod): return type.name, True, True if type.isDOMString(): - if isMember: - declType = "nsString" - else: - declType = "nsAString" + declType = "DOMString" return declType, True, False if type.isByteString(): @@ -4528,7 +4582,7 @@ class CGNativeMember(ClassMethod): if isMember: declType = "JS::Value" else: - declType = "JS::Handle<JS::Value>" + declType = "JSVal" return declType, False, False if type.isObject(): @@ -4570,7 +4624,7 @@ class CGNativeMember(ClassMethod): # Note: All variadic args claim to be optional, but we can just use # empty arrays to represent them not being present. decl = CGTemplatedType("Option", decl) - ref = True + ref = False return (decl, ref) def getArg(self, arg): @@ -4581,7 +4635,7 @@ class CGNativeMember(ClassMethod): arg.optional and not arg.defaultValue, "Variadic" if arg.variadic else False) if ref: - decl = CGWrapper(decl, pre="const ", post="&") + decl = CGWrapper(decl, pre="&") return Argument(decl.define(), arg.identifier.name) @@ -4649,39 +4703,37 @@ class CGCallback(CGClass): # And now insert our template argument. argsWithoutThis = list(args) - args.insert(0, Argument("Box<T>", "thisObj")) + args.insert(0, Argument("&JSRef<T>", "thisObj")) # And the self argument method.args.insert(0, Argument(None, "&self")) args.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" " return Err(FailureUnknown);\n" "}\n") bodyWithThis = string.Template( 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" " return Err(FailureUnknown);\n" "}\n" "return ${methodName}(${callArgs});").substitute({ "callArgs" : ", ".join(argnamesWithThis), "methodName": 'self.' + method.name, - "cxProvider": 'thisObj' }) bodyWithoutThis = string.Template( setupCall + "return ${methodName}(${callArgs});").substitute({ "callArgs" : ", ".join(argnamesWithoutThis), "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, bodyInHeader=True, - templateArgs=["T: 'static+CallbackContainer+Reflectable"], + templateArgs=["T: Reflectable"], body=bodyWithThis, visibility='pub'), ClassMethod(method.name+'__', method.returnType, argsWithoutThis, @@ -4707,15 +4759,27 @@ class CGCallbackFunction(CGCallback): methods=[CallCallback(callback, descriptorProvider)]) def getConstructors(self): - return CGCallback.getConstructors(self) + [ - ClassConstructor( - [Argument("CallbackFunction*", "aOther")], - bodyInHeader=True, - visibility="pub", - explicit=True, - baseConstructors=[ - "CallbackFunction(aOther)" - ])] + return CGCallback.getConstructors(self) + +class CGCallbackFunctionImpl(CGGeneric): + def __init__(self, callback): + impl = string.Template("""impl CallbackContainer for ${type} { + fn new(callback: *mut JSObject) -> ${type} { + ${type}::new(callback) + } + + 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): def __init__(self, descriptor): @@ -4817,7 +4881,7 @@ class CallbackMember(CGNativeMember): return CGList([ CGGeneric(pre), CGWrapper(CGIndenter(CGGeneric(body)), - pre="with_compartment(cx, self.parent.callback, || {\n", + pre="with_compartment(cx, self.parent.callback(), || {\n", post="})") ], "\n").define() @@ -4876,19 +4940,10 @@ class CallbackMember(CGNativeMember): else: jsvalIndex = "%d" % i if arg.optional and not arg.defaultValue: - argval += ".Value()" - 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 = "" + argval += ".clone().unwrap()" - conversion = prepend + wrapForType("*argv.get_mut(%s)" % jsvalIndex, - result=result, + conversion = wrapForType("*argv.get_mut(%s)" % jsvalIndex, + result=argval, successCode="continue;" if arg.variadic else "break;") if arg.variadic: conversion = string.Template( @@ -4899,12 +4954,12 @@ class CallbackMember(CGNativeMember): elif arg.optional and not arg.defaultValue: conversion = ( CGIfWrapper(CGGeneric(conversion), - "%s.WasPassed()" % arg.identifier.name).define() + + "%s.is_some()" % arg.identifier.name).define() + " else if (argc == %d) {\n" " // This is our current trailing argument; reduce argc\n" - " --argc;\n" + " argc -= 1;\n" "} else {\n" - " argv[%d] = JS::UndefinedValue();\n" + " *argv.get_mut(%d) = UndefinedValue();\n" "}" % (i+1, i)) return conversion @@ -4946,7 +5001,7 @@ class CallbackMember(CGNativeMember): }) def getArgcDecl(self): - return CGGeneric("let argc = %su32;" % self.argCountStr); + return CGGeneric("let mut argc = %su32;" % self.argCountStr); @staticmethod def ensureASCIIName(idlObject): @@ -4999,7 +5054,7 @@ class CallCallback(CallbackMethod): return "aThisObj" 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): """ @@ -5012,11 +5067,11 @@ class CallbackOperationBase(CallbackMethod): def getThisObj(self): if not self.singleOperation: - return "self.parent.callback" + return "self.parent.callback()" # This relies on getCallableDecl declaring a boolean # isCallable in the case when we're a single-operation # interface. - return "if isCallable { aThisObj } else { self.parent.callback }" + return "if isCallable { aThisObj } else { self.parent.callback() }" def getCallableDecl(self): replacements = { @@ -5030,11 +5085,11 @@ class CallbackOperationBase(CallbackMethod): if not self.singleOperation: return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp 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' + CGIndenter( CGIfElseWrapper('isCallable', - CGGeneric('unsafe { ObjectValue(&*self.parent.callback) }'), + CGGeneric('unsafe { ObjectValue(&*self.parent.callback()) }'), CGGeneric(getCallableFromProp))).define() + ';\n') class CallbackOperation(CallbackOperationBase): @@ -5242,12 +5297,16 @@ class GlobalGenRoots(): @staticmethod def UnionTypes(config): - curr = UnionTypes(config.getDescriptors()) + curr = UnionTypes(config.getDescriptors(), + config.getDictionaries(), + config.getCallbacks(), + config) curr = CGImports(curr, [], [ 'dom::bindings::utils::unwrap_jsmanaged', 'dom::bindings::codegen::PrototypeList', 'dom::bindings::conversions::FromJSValConvertible', + 'dom::bindings::conversions::ToJSValConvertible', 'dom::bindings::conversions::Default', 'dom::bindings::error::throw_not_in_union', 'dom::bindings::js::JS', diff --git a/src/components/script/dom/bindings/codegen/Configuration.py b/src/components/script/dom/bindings/codegen/Configuration.py index 1272867f542..42d3b56f91c 100644 --- a/src/components/script/dom/bindings/codegen/Configuration.py +++ b/src/components/script/dom/bindings/codegen/Configuration.py @@ -76,7 +76,11 @@ class Configuration: @staticmethod def _filterForFile(items, webIDLFile=""): """Gets the items that match the given filters.""" + if not webIDLFile: + return items + return filter(lambda x: x.filename() == webIDLFile, items) + def getDictionaries(self, webIDLFile=""): return self._filterForFile(self.dictionaries, webIDLFile=webIDLFile) def getCallbacks(self, webIDLFile=""): @@ -269,3 +273,54 @@ class Descriptor(DescriptorProvider): throws = member.getExtendedAttribute(throwsAttr) maybeAppendInfallibleToAttrs(attrs, throws) 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 diff --git a/src/components/script/dom/bindings/codegen/parser/WebIDL.py b/src/components/script/dom/bindings/codegen/parser/WebIDL.py index bee73d5c489..5740a21ca52 100644 --- a/src/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/src/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -464,6 +464,10 @@ class IDLInterface(IDLObjectWithScope): self._callback = False self._finished = False 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._consequential = False self._isPartial = True @@ -696,6 +700,9 @@ class IDLInterface(IDLObjectWithScope): if identifier == "TreatNonCallableAsNull": raise WebIDLError("TreatNonCallableAsNull cannot be specified on interfaces", [attr.location, self.location]) + if identifier == "TreatNonObjectAsNull": + raise WebIDLError("TreatNonObjectAsNull cannot be specified on interfaces", + [attr.location, self.location]) elif identifier == "NoInterfaceObject": if not attr.noArguments(): raise WebIDLError("[NoInterfaceObject] must take no arguments", @@ -1049,11 +1056,12 @@ class IDLType(IDLObject): assert False # Override me! def treatNonCallableAsNull(self): - if not (self.nullable() and self.tag() == IDLType.Tags.callback): - raise WebIDLError("Type %s cannot be TreatNonCallableAsNull" % self, - [self.location]) + assert self.tag() == IDLType.Tags.callback + return self.nullable() and self.inner._treatNonCallableAsNull - return hasattr(self, "_treatNonCallableAsNull") + def treatNonObjectAsNull(self): + assert self.tag() == IDLType.Tags.callback + return self.nullable() and self.inner._treatNonObjectAsNull def markTreatNonCallableAsNull(self): assert not self.treatNonCallableAsNull() @@ -2189,6 +2197,7 @@ class IDLArgument(IDLObjectWithIdentifier): self._isComplete = False self.enforceRange = False self.clamp = False + self._allowTreatNonCallableAsNull = False assert not variadic or optional @@ -2215,6 +2224,8 @@ class IDLArgument(IDLObjectWithIdentifier): raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", [self.location]); self.enforceRange = True + elif identifier == "TreatNonCallableAsNull": + self._allowTreatNonCallableAsNull = True else: raise WebIDLError("Unhandled extended attribute on an argument", [attribute.location]) @@ -2247,6 +2258,9 @@ class IDLArgument(IDLObjectWithIdentifier): self.location) assert self.defaultValue + def allowTreatNonCallableAsNull(self): + return self._allowTreatNonCallableAsNull + def _getDependentObjects(self): deps = set([self.type]) if self.defaultValue: @@ -2269,6 +2283,12 @@ class IDLCallbackType(IDLType, IDLObjectWithScope): for argument in arguments: 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): return True @@ -2308,6 +2328,21 @@ class IDLCallbackType(IDLType, IDLObjectWithScope): return (other.isPrimitive() or other.isString() or other.isEnum() or 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): return set([self._returnType] + self._arguments) diff --git a/src/components/script/dom/bindings/conversions.rs b/src/components/script/dom/bindings/conversions.rs index e1a155fa457..867e07ade30 100644 --- a/src/components/script/dom/bindings/conversions.rs +++ b/src/components/script/dom/bindings/conversions.rs @@ -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> { fn to_jsval(&self, cx: *mut JSContext) -> JSVal { match self { @@ -350,7 +356,11 @@ impl<X: Default, T: FromJSValConvertible<X>> FromJSValConvertible<()> for Option } impl ToJSValConvertible for *mut JSObject { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { - ObjectOrNullValue(*self) + fn to_jsval(&self, cx: *mut JSContext) -> JSVal { + let mut wrapped = ObjectOrNullValue(*self); + unsafe { + assert!(JS_WrapValue(cx, &mut wrapped) != 0); + } + wrapped } } diff --git a/src/components/script/dom/bindings/trace.rs b/src/components/script/dom/bindings/trace.rs index d18f6a64448..26decbc450a 100644 --- a/src/components/script/dom/bindings/trace.rs +++ b/src/components/script/dom/bindings/trace.rs @@ -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 /// (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. +#[deriving(Eq, Clone)] pub struct Traceable<T> { inner: T } diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index 68ea97d1335..f6f67f93747 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -2,6 +2,7 @@ * 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/. */ +use dom::bindings::codegen::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::InheritTypes::{DocumentDerived, EventCast, HTMLElementCast}; use dom::bindings::codegen::InheritTypes::{HTMLHeadElementCast, TextCast, ElementCast}; 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 collections::hashmap::HashMap; -use js::jsapi::{JSObject, JSContext}; +use js::jsapi::JSContext; use std::ascii::StrAsciiExt; use url::{Url, from_str}; @@ -326,8 +327,8 @@ pub trait DocumentMethods { fn Applets(&self) -> Temporary<HTMLCollection>; fn Location(&mut self) -> Temporary<Location>; fn Children(&self) -> Temporary<HTMLCollection>; - fn GetOnload(&self, _cx: *mut JSContext) -> *mut JSObject; - fn SetOnload(&mut self, _cx: *mut JSContext, listener: *mut JSObject); + fn GetOnload(&self) -> Option<EventHandlerNonNull>; + fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>); } 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)) } - fn GetOnload(&self, _cx: *mut JSContext) -> *mut JSObject { + fn GetOnload(&self) -> Option<EventHandlerNonNull> { let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); 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); eventtarget.set_event_handler_common("load", listener) } diff --git a/src/components/script/dom/eventdispatcher.rs b/src/components/script/dom/eventdispatcher.rs index cc5eb66f577..11db1eb7b2b 100644 --- a/src/components/script/dom/eventdispatcher.rs +++ b/src/components/script/dom/eventdispatcher.rs @@ -50,7 +50,7 @@ pub fn dispatch_event<'a, 'b>(target: &JSRef<'a, EventTarget>, for listener in listeners.iter() { //FIXME: this should have proper error handling, or explicitly // 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 { break; @@ -80,7 +80,7 @@ pub fn dispatch_event<'a, 'b>(target: &JSRef<'a, EventTarget>, for listener in listeners.iter() { //FIXME: this should have proper error handling, or explicitly drop the // exception on the floor. - assert!(listener.HandleEvent__(event, ReportExceptions).is_ok()); + assert!(listener.HandleEvent_(target, event, ReportExceptions).is_ok()); if event.deref().stop_immediate { break; } @@ -99,7 +99,7 @@ pub fn dispatch_event<'a, 'b>(target: &JSRef<'a, EventTarget>, for listener in listeners.iter() { //FIXME: this should have proper error handling or explicitly // 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 { break; diff --git a/src/components/script/dom/eventtarget.rs b/src/components/script/dom/eventtarget.rs index b0b8a9bad33..db92255cb59 100644 --- a/src/components/script/dom/eventtarget.rs +++ b/src/components/script/dom/eventtarget.rs @@ -4,18 +4,21 @@ 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::utils::{Reflectable, Reflector}; use dom::event::Event; use dom::eventdispatcher::dispatch_event; -use dom::node::{Node, NodeTypeId, window_from_node}; +use dom::node::NodeTypeId; use dom::xmlhttprequest::XMLHttpRequestId; 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 libc::{c_char, size_t}; use std::ptr; +use url::Url; use collections::hashmap::HashMap; @@ -92,11 +95,14 @@ pub trait EventTargetHelpers { listener: Option<EventListener>); fn get_inline_event_listener(&self, ty: DOMString) -> Option<EventListener>; fn set_event_handler_uncompiled(&mut self, - content: &JSRef<Node>, + cx: *mut JSContext, + url: Url, + scope: *mut JSObject, ty: &str, source: DOMString); - fn set_event_handler_common(&mut self, ty: &str, listener: *mut JSObject); - fn get_event_handler_common(&self, ty: &str) -> *mut JSObject; + 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> { @@ -132,7 +138,7 @@ impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> { None => { if listener.is_some() { entries.push(EventListenerEntry { - phase: Capturing, //XXXjdm no idea when inline handlers should run + phase: Bubbling, listener: Inline(listener.unwrap()), }); } @@ -151,12 +157,11 @@ impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> { } fn set_event_handler_uncompiled(&mut self, - content: &JSRef<Node>, + cx: *mut JSContext, + url: Url, + scope: *mut JSObject, ty: &str, 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 name = ty.to_c_str(); 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()); JS_GetFunctionObject(fun) }})}); - let scope = win.deref().reflector().get_jsobject(); let funobj = unsafe { JS_CloneFunctionObject(cx, handler, scope) }; 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) { - let listener = EventListener::new(listener); - self.set_inline_event_listener(ty.to_owned(), Some(listener)); + 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(&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()); - listener.map(|listener| listener.parent.callback()).unwrap_or(ptr::mut_null()) + listener.map(|listener| CallbackContainer::new(listener.parent.callback())) } } diff --git a/src/components/script/dom/htmlbodyelement.rs b/src/components/script/dom/htmlbodyelement.rs index 7f0b8efc66b..6ba82e252f0 100644 --- a/src/components/script/dom/htmlbodyelement.rs +++ b/src/components/script/dom/htmlbodyelement.rs @@ -3,10 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::BindingDeclarations::HTMLBodyElementBinding; +use dom::bindings::codegen::EventHandlerBinding::EventHandlerNonNull; 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::js::{JSRef, Temporary}; +use dom::bindings::utils::Reflectable; use dom::document::Document; use dom::element::HTMLBodyElementTypeId; use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers}; @@ -14,7 +16,6 @@ use dom::htmlelement::HTMLElement; use dom::node::{Node, ElementNodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; use dom::window::WindowMethods; -use js::jsapi::{JSContext, JSObject}; use servo_util::str::DOMString; #[deriving(Encodable)] @@ -54,8 +55,8 @@ pub trait HTMLBodyElementMethods { fn SetBgColor(&self, _bg_color: DOMString) -> ErrorResult; fn Background(&self) -> DOMString; fn SetBackground(&self, _background: DOMString) -> ErrorResult; - fn GetOnunload(&self, cx: *mut JSContext) -> *mut JSObject; - fn SetOnunload(&mut self, cx: *mut JSContext, listener: *mut JSObject); + fn GetOnunload(&self) -> Option<EventHandlerNonNull>; + fn SetOnunload(&mut self, listener: Option<EventHandlerNonNull>); } impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> { @@ -107,14 +108,14 @@ impl<'a> HTMLBodyElementMethods for JSRef<'a, HTMLBodyElement> { Ok(()) } - fn GetOnunload(&self, cx: *mut JSContext) -> *mut JSObject { + fn GetOnunload(&self) -> Option<EventHandlerNonNull> { 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(); - win.SetOnunload(cx, listener) + win.SetOnunload(listener) } } @@ -131,11 +132,22 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLBodyElement> { } 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 evtarget: &mut JSRef<EventTarget> = EventTargetCast::from_mut_ref(&mut *window); - let content: &mut JSRef<Node> = NodeCast::from_mut_ref(self); - evtarget.set_event_handler_uncompiled(content, + 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); } diff --git a/src/components/script/dom/htmlelement.rs b/src/components/script/dom/htmlelement.rs index c5f3fd3ed48..85d2d2bee67 100644 --- a/src/components/script/dom/htmlelement.rs +++ b/src/components/script/dom/htmlelement.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::BindingDeclarations::HTMLElementBinding; +use dom::bindings::codegen::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFrameSetElementDerived}; use dom::bindings::codegen::InheritTypes::{HTMLElementDerived, HTMLBodyElementDerived}; 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::virtualmethods::VirtualMethods; use dom::window::WindowMethods; -use js::jsapi::{JSContext, JSObject}; +use js::jsapi::JSContext; use js::jsval::{JSVal, NullValue}; use servo_util::namespace; use servo_util::str::DOMString; -use std::ptr; #[deriving(Encodable)] pub struct HTMLElement { @@ -90,8 +90,8 @@ pub trait HTMLElementMethods { fn OffsetLeft(&self) -> i32; fn OffsetWidth(&self) -> i32; fn OffsetHeight(&self) -> i32; - fn GetOnload(&self, cx: *mut JSContext) -> *mut JSObject; - fn SetOnload(&mut self, cx: *mut JSContext, listener: *mut JSObject); + fn GetOnload(&self) -> Option<EventHandlerNonNull>; + fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>); } impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> { @@ -212,19 +212,19 @@ impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> { 0 } - fn GetOnload(&self, cx: *mut JSContext) -> *mut JSObject { + fn GetOnload(&self) -> Option<EventHandlerNonNull> { if self.is_body_or_frameset() { let win = window_from_node(self).root(); - win.deref().GetOnload(cx) + win.deref().GetOnload() } 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() { let mut win = window_from_node(self).root(); - win.SetOnload(cx, listener) + win.SetOnload(listener) } } } diff --git a/src/components/script/dom/testbinding.rs b/src/components/script/dom/testbinding.rs index 115fbcadaa5..4b753785e10 100644 --- a/src/components/script/dom/testbinding.rs +++ b/src/components/script/dom/testbinding.rs @@ -5,7 +5,7 @@ use dom::bindings::js::{JS, JSRef, Temporary}; use dom::bindings::codegen::BindingDeclarations::TestBindingBinding::TestEnum; 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::utils::{Reflector, Reflectable}; use dom::blob::Blob; @@ -136,7 +136,7 @@ pub trait TestBindingMethods { fn PassEnum(&self, _: TestEnum) {} fn PassInterface(&self, _: &JSRef<Blob>) {} fn PassUnion(&self, _: HTMLElementOrLong) {} - fn PassUnion2(&self, _: StringOrFormData) {} + fn PassUnion2(&self, _: EventOrString) {} fn PassAny(&self, _: *mut JSContext, _: JSVal) {} fn PassNullableBoolean(&self, _: Option<bool>) {} @@ -155,7 +155,7 @@ pub trait TestBindingMethods { // fn PassNullableEnum(&self, _: Option<TestEnum>) {} fn PassNullableInterface(&self, _: Option<JSRef<Blob>>) {} fn PassNullableUnion(&self, _: Option<HTMLElementOrLong>) {} - fn PassNullableUnion2(&self, _: Option<StringOrFormData>) {} + fn PassNullableUnion2(&self, _: Option<EventOrString>) {} fn PassNullableAny(&self, _: *mut JSContext, _: Option<JSVal>) {} fn PassOptionalBoolean(&self, _: Option<bool>) {} @@ -174,7 +174,7 @@ pub trait TestBindingMethods { fn PassOptionalEnum(&self, _: Option<TestEnum>) {} fn PassOptionalInterface(&self, _: Option<JSRef<Blob>>) {} fn PassOptionalUnion(&self, _: Option<HTMLElementOrLong>) {} - fn PassOptionalUnion2(&self, _: Option<StringOrFormData>) {} + fn PassOptionalUnion2(&self, _: Option<EventOrString>) {} fn PassOptionalAny(&self, _: *mut JSContext, _: Option<JSVal>) {} fn PassOptionalNullableBoolean(&self, _: Option<Option<bool>>) {} @@ -193,7 +193,7 @@ pub trait TestBindingMethods { // fn PassOptionalNullableEnum(&self, _: Option<Option<TestEnum>>) {} fn PassOptionalNullableInterface(&self, _: Option<Option<JSRef<Blob>>>) {} fn PassOptionalNullableUnion(&self, _: Option<Option<HTMLElementOrLong>>) {} - fn PassOptionalNullableUnion2(&self, _: Option<Option<StringOrFormData>>) {} + fn PassOptionalNullableUnion2(&self, _: Option<Option<EventOrString>>) {} fn PassOptionalBooleanWithDefault(&self, _: bool) {} fn PassOptionalByteWithDefault(&self, _: i8) {} @@ -223,7 +223,7 @@ pub trait TestBindingMethods { // fn PassOptionalNullableEnumWithDefault(&self, _: Option<TestEnum>) {} fn PassOptionalNullableInterfaceWithDefault(&self, _: Option<JSRef<Blob>>) {} fn PassOptionalNullableUnionWithDefault(&self, _: Option<HTMLElementOrLong>) {} - fn PassOptionalNullableUnion2WithDefault(&self, _: Option<StringOrFormData>) {} + fn PassOptionalNullableUnion2WithDefault(&self, _: Option<EventOrString>) {} fn PassOptionalAnyWithDefault(&self, _: *mut JSContext, _: JSVal) {} fn PassOptionalNullableBooleanWithNonNullDefault(&self, _: Option<bool>) {} diff --git a/src/components/script/dom/webidls/EventHandler.webidl b/src/components/script/dom/webidls/EventHandler.webidl index ee760bcd05b..dd922cc3f75 100644 --- a/src/components/script/dom/webidls/EventHandler.webidl +++ b/src/components/script/dom/webidls/EventHandler.webidl @@ -11,11 +11,11 @@ * and create derivative works of this document. */ -//[TreatNonObjectAsNull] //XXXjdm webidl.py assertion +[TreatNonObjectAsNull] callback EventHandlerNonNull = any (Event event); 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); typedef OnErrorEventHandlerNonNull? OnErrorEventHandler; diff --git a/src/components/script/dom/webidls/TestBinding.webidl b/src/components/script/dom/webidls/TestBinding.webidl index 101e4d554e5..467abce3328 100644 --- a/src/components/script/dom/webidls/TestBinding.webidl +++ b/src/components/script/dom/webidls/TestBinding.webidl @@ -70,7 +70,7 @@ interface TestBinding { attribute TestEnum enumAttribute; attribute Blob interfaceAttribute; // attribute (HTMLElement or long) unionAttribute; - // attribute (DOMString or FormData) union2Attribute; + // attribute (Event or DOMString) union2Attribute; attribute any anyAttribute; attribute boolean? booleanAttributeNullable; @@ -89,7 +89,7 @@ interface TestBinding { readonly attribute TestEnum? enumAttributeNullable; attribute Blob? interfaceAttributeNullable; // attribute (HTMLElement or long)? unionAttributeNullable; - // attribute (DOMString or FormData)? union2AttributeNullable; + // attribute (Event or DOMString)? union2AttributeNullable; void receiveVoid(); boolean receiveBoolean(); @@ -137,7 +137,7 @@ interface TestBinding { void passEnum(TestEnum arg); void passInterface(Blob arg); void passUnion((HTMLElement or long) arg); - void passUnion2((DOMString or FormData) data); + void passUnion2((Event or DOMString) data); void passAny(any arg); void passNullableBoolean(boolean? arg); @@ -156,7 +156,7 @@ interface TestBinding { // void passNullableEnum(TestEnum? arg); void passNullableInterface(Blob? 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 passOptionalByte(optional byte arg); @@ -174,7 +174,7 @@ interface TestBinding { void passOptionalEnum(optional TestEnum arg); void passOptionalInterface(optional Blob 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 passOptionalNullableBoolean(optional boolean? arg); @@ -193,7 +193,7 @@ interface TestBinding { // void passOptionalNullableEnum(optional TestEnum? arg); void passOptionalNullableInterface(optional Blob? 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 passOptionalByteWithDefault(optional byte arg = 0); @@ -207,7 +207,7 @@ interface TestBinding { void passOptionalStringWithDefault(optional DOMString arg = ""); void passOptionalEnumWithDefault(optional TestEnum arg = "foo"); // 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 passOptionalNullableByteWithDefault(optional byte? arg = null); @@ -223,7 +223,7 @@ interface TestBinding { // void passOptionalNullableEnumWithDefault(optional TestEnum? arg = null); void passOptionalNullableInterfaceWithDefault(optional Blob? 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 passOptionalNullableBooleanWithNonNullDefault(optional boolean? arg = false); @@ -240,5 +240,5 @@ interface TestBinding { void passOptionalNullableStringWithNonNullDefault(optional DOMString? arg = ""); // void passOptionalNullableEnumWithNonNullDefault(optional TestEnum? arg = "foo"); // void passOptionalNullableUnionWithNonNullDefault(optional (HTMLElement or long)? arg = 7); - // void passOptionalNullableUnion2WithNonNullDefault(optional (DOMString or FormData)? data = "foo"); + // void passOptionalNullableUnion2WithNonNullDefault(optional (Event or DOMString)? data = "foo"); }; diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs index ecb08928832..0c71edb69a0 100644 --- a/src/components/script/dom/window.rs +++ b/src/components/script/dom/window.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 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::trace::{Traceable, Untraceable}; @@ -24,7 +25,7 @@ use servo_util::str::DOMString; use servo_util::task::{spawn_named}; use servo_util::url::parse_url; -use js::jsapi::{JSContext, JSObject}; +use js::jsapi::JSContext; use js::jsapi::{JS_GC, JS_GetRuntime}; use js::jsval::{NullValue, JSVal}; @@ -141,12 +142,12 @@ pub trait WindowMethods { fn Window(&self) -> Temporary<Window>; fn Self(&self) -> Temporary<Window>; fn Performance(&mut self) -> Temporary<Performance>; - fn GetOnload(&self, _cx: *mut JSContext) -> *mut JSObject; - fn SetOnload(&mut self, _cx: *mut JSContext, listener: *mut JSObject); - fn GetOnunload(&self, _cx: *mut JSContext) -> *mut JSObject; - fn SetOnunload(&mut self, _cx: *mut JSContext, listener: *mut JSObject); - fn GetOnerror(&self, _cx: *mut JSContext) -> *mut JSObject; - fn SetOnerror(&mut self, _cx: *mut JSContext, listener: *mut JSObject); + 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 Gc(&self); } @@ -275,32 +276,32 @@ impl<'a> WindowMethods for JSRef<'a, Window> { 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); 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); 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); 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); 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); 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); eventtarget.set_event_handler_common("error", listener) } @@ -429,6 +430,7 @@ impl<'a> PrivateWindowHelpers for JSRef<'a, Window> { handle } } + impl Window { pub fn new(cx: *mut JSContext, page: Rc<Page>, diff --git a/src/components/script/script.rs b/src/components/script/script.rs index 4e560c44691..1a6f179d518 100644 --- a/src/components/script/script.rs +++ b/src/components/script/script.rs @@ -48,6 +48,7 @@ pub mod dom { pub mod str; pub mod trace; pub mod codegen { + pub mod EventHandlerBinding; pub mod InterfaceTypes; pub mod InheritTypes; pub mod PrototypeList; diff --git a/src/test/content/test_body_listener.html b/src/test/content/test_body_listener.html new file mode 100644 index 00000000000..dd125e7271c --- /dev/null +++ b/src/test/content/test_body_listener.html @@ -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> diff --git a/src/test/content/test_load_event.html b/src/test/content/test_load_event.html index e3e193bfd72..595ed3b44c7 100644 --- a/src/test/content/test_load_event.html +++ b/src/test/content/test_load_event.html @@ -21,10 +21,12 @@ } window.onload = function(ev) { onloads++; + is(onloads, 1); check(ev); } addEventListener("load", function(ev) { onloads++; + is(onloads, 2); check(ev); }); </script> |