diff options
18 files changed, 489 insertions, 329 deletions
diff --git a/src/components/script/dom/bindings/callback.rs b/src/components/script/dom/bindings/callback.rs new file mode 100644 index 00000000000..6cf88b353ed --- /dev/null +++ b/src/components/script/dom/bindings/callback.rs @@ -0,0 +1,105 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use dom::bindings::utils::{WrapNativeParent, Reflectable}; +use js::jsapi::{JSContext, JSObject, JS_WrapObject, JSVal, JS_ObjectIsCallable}; +use js::jsapi::JS_GetProperty; +use js::{JSVAL_IS_OBJECT, JSVAL_TO_OBJECT}; + +use std::libc; +use std::ptr; + +pub enum ExceptionHandling { + // Report any exception and don't throw it to the caller code. + eReportExceptions, + // Throw an exception to the caller code if the thrown exception is a + // binding object for a DOMError from the caller's scope, otherwise report + // it. + eRethrowContentExceptions, + // Throw any exception to the caller code. + eRethrowExceptions +} + +#[deriving(Clone,Eq)] +pub struct CallbackInterface { + callback: *JSObject +} + +pub trait CallbackContainer { + fn callback(&self) -> *JSObject; +} + +impl CallbackContainer for CallbackInterface { + fn callback(&self) -> *JSObject { + self.callback + } +} + +impl CallbackInterface { + pub fn new(callback: *JSObject) -> CallbackInterface { + CallbackInterface { + callback: callback + } + } + + #[fixed_stack_segment] + pub fn GetCallableProperty(&self, cx: *JSContext, name: *libc::c_char, callable: &mut JSVal) -> bool { + unsafe { + if JS_GetProperty(cx, self.callback, name, &*callable) == 0 { + return false; + } + + if !JSVAL_IS_OBJECT(*callable) || + JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(*callable)) == 0 { + //ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get()); + return false; + } + + return true; + } + } +} + +pub fn GetJSObjectFromCallback<T: CallbackContainer>(callback: &T) -> *JSObject { + callback.callback() +} + +#[fixed_stack_segment] +pub fn WrapCallThisObject<T: 'static + CallbackContainer + Reflectable>(cx: *JSContext, + scope: *JSObject, + p: @mut T) -> *JSObject { + let mut obj = GetJSObjectFromCallback(p); + if obj.is_null() { + obj = WrapNativeParent(cx, scope, Some(p as @mut Reflectable)); + if obj.is_null() { + return ptr::null(); + } + } + + unsafe { + if JS_WrapObject(cx, &obj) == 0 { + return ptr::null(); + } + } + + return obj; +} + +pub struct CallSetup { + cx: *JSContext, + handling: ExceptionHandling +} + +impl CallSetup { + pub fn new(cx: *JSContext, handling: ExceptionHandling) -> CallSetup { + CallSetup { + cx: cx, + handling: handling + } + } + + pub fn GetContext(&self) -> *JSContext { + self.cx + } +} diff --git a/src/components/script/dom/bindings/codegen/Bindings.conf b/src/components/script/dom/bindings/codegen/Bindings.conf index 770bea35c33..d1760f27901 100644 --- a/src/components/script/dom/bindings/codegen/Bindings.conf +++ b/src/components/script/dom/bindings/codegen/Bindings.conf @@ -184,26 +184,12 @@ DOMInterfaces = { 'Event': { }, -'EventListener': [ -{ +'EventListener': { + 'nativeType': 'EventListenerBinding::EventListener', }, -{ - 'workers': True, -}], -'EventTarget': [ -{ -# 'nativeType': 'nsDOMEventTargetHelper', -# 'hasInstanceInterface': 'nsIDOMEventTarget', -# 'concrete': False, -# 'prefable': True, +'EventTarget': { }, -#{ -# 'workers': True, -# 'headerFile': 'mozilla/dom/workers/bindings/EventTarget.h', -# 'concrete': False -#} -], 'FileList': [ { diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 0d012d2b1c8..8252135b0a5 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -525,7 +525,7 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None, # failureCode will prevent pending exceptions from being set in cases when # they really should be! if exceptionCode is None: - exceptionCode = "return false;" + exceptionCode = "return 0;" # We often want exceptionCode to be indented, since it often appears in an # if body. exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode)) @@ -915,19 +915,14 @@ for (uint32_t i = 0; i < length; ++i) { type.unroll().inner.identifier.name) if descriptor.interface.isCallback(): - name = descriptor.interface.identifier.name - if type.nullable() or isCallbackReturnValue: - declType = CGGeneric("nsRefPtr<%s>" % name); - else: - declType = CGGeneric("OwningNonNull<%s>" % name) - conversion = ( - " ${declName} = new %s(&${val}.toObject());\n" % name) + name = descriptor.nativeType + declType = CGGeneric("Option<%s>" % name); + conversion = (" ${declName} = Some(%s::new(JSVAL_TO_OBJECT(${val})));\n" % name) template = wrapObjectTemplate(conversion, type, - "${declName} = nullptr", + "${declName} = None", failureCode) - return JSToNativeConversionInfo(template, declType=declType, - dealWithOptional=isOptional) + return (template, declType, None, isOptional, None) # This is an interface that we implement as a concrete class # or an XPCOM interface. @@ -980,13 +975,6 @@ for (uint32_t i = 0; i < length; ++i) { "JSVAL_TO_OBJECT(${val})", "${declName}", isOptional or argIsPointer or type.nullable())) - elif descriptor.interface.isCallback() and False: - #XXXjdm unfinished - templateBody += str(CallbackObjectUnwrapper( - descriptor, - "&${val}.toObject()", - "${declName}", - codeOnFailure=failureCode)) elif descriptor.workers: templateBody += "${declName} = &${val}.toObject();" else: @@ -1265,7 +1253,7 @@ for (uint32_t i = 0; i < length; ++i) { assert not isOptional # This one only happens for return values, and its easy: Just # ignore the jsval. - return JSToNativeConversionInfo("") + return ("", None, None, False, None) if not type.isPrimitive(): raise TypeError("Need conversion for argument type '%s'" % str(type)) @@ -1280,16 +1268,20 @@ for (uint32_t i = 0; i < length; ++i) { if type.nullable(): dataLoc = "${declName}.SetValue()" - nullCondition = "${val}.isNullOrUndefined()" + nullCondition = "(RUST_JSVAL_IS_NULL(${val}) != 0 || RUST_JSVAL_IS_VOID(${val}) != 0)" if defaultValue is not None and isinstance(defaultValue, IDLNullValue): nullCondition = "!(${haveValue}) || " + nullCondition + #XXXjdm support conversionBehavior here template = ( "if (%s) {\n" - " ${declName}.SetNull();\n" - "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n" - " return false;\n" - "}" % (nullCondition, typeName, conversionBehavior, dataLoc)) - declType = CGGeneric("Nullable<" + typeName + ">") + " ${declName} = None;\n" + "} else {\n" + " match JSValConvertible::from_jsval(${val}) {\n" + " Some(val_) => ${declName} = Some(val_),\n" + " None => return 0\n" + " }\n" + "}" % nullCondition) + declType = CGGeneric("Option<" + typeName + ">") else: assert(defaultValue is None or not isinstance(defaultValue, IDLNullValue)) @@ -1317,10 +1309,10 @@ for (uint32_t i = 0; i < length; ++i) { " %s = %s;\n" "}" % (dataLoc, defaultStr))).define() - if typeName != "bool": - return (template, declType, None, isOptional, "0 as %s" % typeName) - else: - return (template, declType, None, isOptional, "false") + initialVal = "false" if typeName == "bool" else ("0 as %s" % typeName) + if type.nullable(): + initialVal = "Some(%s)" % initialVal + return (template, declType, None, isOptional, initialVal) def instantiateJSToNativeConversionTemplate(templateTuple, replacements, argcAndIndex=None): @@ -1593,8 +1585,8 @@ for (uint32_t i = 0; i < length; ++i) { # Non-prefable bindings can only fail to wrap as a new-binding object # if they already threw an exception. Same thing for # non-prefable bindings. - failed = ("//MOZ_ASSERT(JS_IsExceptionPending(cx));\n" + - "return 0;") + failed = ("assert!(unsafe { JS_IsExceptionPending(cx) != 0 });\n" + + "%s" % exceptionCode) else: if descriptor.notflattened: raise TypeError("%s is prefable but not flattened; " @@ -2423,9 +2415,10 @@ class Argument(): self.name = name self.default = default def declare(self): - string = self.argType + ' ' + self.name - if self.default is not None: - string += " = " + self.default + string = self.name + ((': ' + self.argType) if self.argType else '') + #XXXjdm Support default arguments somehow :/ + #if self.default is not None: + # string += " = " + self.default return string def define(self): return self.argType + ' ' + self.name @@ -2470,7 +2463,7 @@ class CGAbstractMethod(CGThing): self.pub = pub; self.unsafe = unsafe def _argstring(self, declare): - return ', '.join([a.declare() if declare else a.define() for a in self.args]) + return ', '.join([a.declare() for a in self.args]) def _template(self): if self.templateArgs is None: return '' @@ -3575,7 +3568,7 @@ class ClassItem: assert False class ClassBase(ClassItem): - def __init__(self, name, visibility='public'): + def __init__(self, name, visibility='pub'): ClassItem.__init__(self, name, visibility) def declare(self, cgClass): return '%s %s' % (self.visibility, self.name) @@ -3595,11 +3588,11 @@ class ClassMethod(ClassItem): assert not override or virtual self.returnType = returnType self.args = args - self.inline = inline or bodyInHeader + self.inline = False self.static = static self.virtual = virtual self.const = const - self.bodyInHeader = bodyInHeader + self.bodyInHeader = True self.templateArgs = templateArgs self.body = body self.breakAfterReturnDecl = breakAfterReturnDecl @@ -3608,7 +3601,7 @@ class ClassMethod(ClassItem): ClassItem.__init__(self, name, visibility) def getDecorators(self, declaring): - decorators = [] + decorators = ['#[fixed_stack_segment]'] if self.inline: decorators.append('inline') if declaring: @@ -3626,62 +3619,32 @@ class ClassMethod(ClassItem): return self.body def declare(self, cgClass): - templateClause = 'template <%s>\n' % ', '.join(self.templateArgs) \ + templateClause = '<%s>' % ', '.join(self.templateArgs) \ if self.bodyInHeader and self.templateArgs else '' args = ', '.join([a.declare() for a in self.args]) if self.bodyInHeader: body = CGIndenter(CGGeneric(self.getBody())).define() - body = '\n{\n' + body + '\n}' + body = ' {\n' + body + '\n}' else: body = ';' - return string.Template("${templateClause}${decorators}${returnType}%s" - "${name}(${args})${const}${override}${body}%s" % + return string.Template("${decorators}%s" + "${visibility}fn ${name}${templateClause}(${args})${returnType}${const}${override}${body}%s" % (self.breakAfterReturnDecl, self.breakAfterSelf) ).substitute({ 'templateClause': templateClause, 'decorators': self.getDecorators(True), - 'returnType': self.returnType, + 'returnType': (" -> %s" % self.returnType) if self.returnType else "", 'name': self.name, 'const': ' const' if self.const else '', 'override': ' MOZ_OVERRIDE' if self.override else '', 'args': args, - 'body': body + 'body': body, + 'visibility': self.visibility + ' ' if self.visibility is not 'priv' else '' }) def define(self, cgClass): - if self.bodyInHeader: - return '' - - templateArgs = cgClass.templateArgs - if templateArgs: - if cgClass.templateSpecialization: - templateArgs = \ - templateArgs[len(cgClass.templateSpecialization):] - - if templateArgs: - templateClause = \ - 'template <%s>\n' % ', '.join([str(a) for a in templateArgs]) - else: - templateClause = '' - - args = ', '.join([a.define() for a in self.args]) - - body = CGIndenter(CGGeneric(self.getBody())).define() - - return string.Template("""${templateClause}${decorators}${returnType} -${className}::${name}(${args})${const} -{ -${body} -} -""").substitute({ 'templateClause': templateClause, - 'decorators': self.getDecorators(False), - 'returnType': self.returnType, - 'className': cgClass.getNameString(), - 'name': self.name, - 'args': args, - 'const': ' const' if self.const else '', - 'body': body }) + pass class ClassUsingDeclaration(ClassItem): """" @@ -3729,10 +3692,10 @@ class ClassConstructor(ClassItem): body contains a string with the code for the constructor, defaults to empty. """ def __init__(self, args, inline=False, bodyInHeader=False, - visibility="private", explicit=False, baseConstructors=None, + visibility="priv", explicit=False, baseConstructors=None, body=""): self.args = args - self.inline = inline or bodyInHeader + self.inline = False self.bodyInHeader = bodyInHeader self.explicit = explicit self.baseConstructors = baseConstructors or [] @@ -3761,21 +3724,22 @@ class ClassConstructor(ClassItem): return '\n : ' + ',\n '.join(items) return '' - def getBody(self): - return self.body + def getBody(self, cgClass): + initializers = [" parent: %s" % str(self.baseConstructors[0])] + return (self.body + ( + "%s {\n" + "%s\n" + "}") % (cgClass.name, '\n'.join(initializers))) def declare(self, cgClass): args = ', '.join([a.declare() for a in self.args]) - if self.bodyInHeader: - body = ' ' + self.getBody(); - body = stripTrailingWhitespace(body.replace('\n', '\n ')) - if len(body) > 0: - body += '\n' - body = self.getInitializationList(cgClass) + '\n{\n' + body + '}' - else: - body = ';' + body = ' ' + self.getBody(cgClass); + body = stripTrailingWhitespace(body.replace('\n', '\n ')) + if len(body) > 0: + body += '\n' + body = ' {\n' + body + '}' - return string.Template("""${decorators}${className}(${args})${body} + return string.Template("""pub fn ${decorators}new(${args}) -> ${className}${body} """).substitute({ 'decorators': self.getDecorators(True), 'className': cgClass.getNameString(), 'args': args, @@ -3870,7 +3834,7 @@ ${className}::~${className}() 'body': body }) class ClassMember(ClassItem): - def __init__(self, name, type, visibility="private", static=False, + def __init__(self, name, type, visibility="priv", static=False, body=None): self.type = type; self.static = static @@ -3878,8 +3842,7 @@ class ClassMember(ClassItem): ClassItem.__init__(self, name, visibility) def declare(self, cgClass): - return '%s%s %s;\n' % ('static ' if self.static else '', self.type, - self.name) + return '%s: %s,\n' % (self.name, self.type) def define(self, cgClass): if not self.static: @@ -3962,7 +3925,7 @@ class CGClass(CGThing): self.isStruct = isStruct self.disallowCopyConstruction = disallowCopyConstruction self.indent = indent - self.defaultVisibility ='public' if isStruct else 'private' + self.defaultVisibility ='pub' if isStruct else 'priv' self.decorators = decorators self.extradeclarations = extradeclarations self.extradefinitions = extradefinitions @@ -3983,68 +3946,36 @@ class CGClass(CGThing): result = result + self.indent + 'template <%s>\n' \ % ','.join([str(a) for a in templateArgs]) - type = 'struct' if self.isStruct else 'class' - if self.templateSpecialization: specialization = \ '<%s>' % ', '.join([str(a) for a in self.templateSpecialization]) else: specialization = '' - myself = '%s%s %s%s' % (self.indent, type, self.name, specialization) + myself = '' if self.decorators != '': - myself += " " + self.decorators + myself += self.decorators + '\n' + myself += '%spub struct %s%s' % (self.indent, self.name, specialization) result += myself + assert len(self.bases) == 1 #XXjdm Can we support multiple inheritance? + + result += '{\n%s\n' % self.indent + if self.bases: - inherit = ' : ' - result += inherit - # Grab our first base - baseItems = [CGGeneric(b.declare(self)) for b in self.bases] - bases = baseItems[:1] - # Indent the rest - bases.extend(CGIndenter(b, len(myself) + len(inherit)) for - b in baseItems[1:]) - result += ",\n".join(b.define() for b in bases) - - result = result + '\n%s{\n' % self.indent + self.members = [ClassMember("parent", self.bases[0].name, "pub")] + self.members result += CGIndenter(CGGeneric(self.extradeclarations), len(self.indent)).define() - def declareMembers(cgClass, memberList, defaultVisibility, itemCount, - separator=''): - members = { 'private': [], 'protected': [], 'public': [] } - - for member in memberList: - members[member.visibility].append(member) - - - if defaultVisibility == 'public': - order = [ 'public', 'protected', 'private' ] - else: - order = [ 'private', 'protected', 'public' ] - + def declareMembers(cgClass, memberList): result = '' - lastVisibility = defaultVisibility - for visibility in order: - list = members[visibility] - if list: - if visibility != lastVisibility: - if itemCount: - result = result + '\n' - result = result + visibility + ':\n' - itemCount = 0 - for member in list: - if itemCount != 0: - result = result + separator - declaration = member.declare(cgClass) - declaration = CGIndenter(CGGeneric(declaration)).define() - result = result + declaration - itemCount = itemCount + 1 - lastVisibility = visibility - return (result, lastVisibility, itemCount) + for member in memberList: + declaration = member.declare(cgClass) + declaration = CGIndenter(CGGeneric(declaration)).define() + result = result + declaration + return result if self.disallowCopyConstruction: class DisallowedCopyConstructor(object): @@ -4059,48 +3990,33 @@ class CGClass(CGThing): disallowedCopyConstructors = [] order = [(self.enums, ''), (self.unions, ''), - (self.typedefs, ''), (self.members, ''), - (self.constructors + disallowedCopyConstructors, '\n'), - (self.destructors, '\n'), (self.methods, '\n')] + (self.typedefs, ''), (self.members, '')] - lastVisibility = self.defaultVisibility - itemCount = 0 for (memberList, separator) in order: - (memberString, lastVisibility, itemCount) = \ - declareMembers(self, memberList, lastVisibility, itemCount, - separator) + memberString = declareMembers(self, memberList) if self.indent: memberString = CGIndenter(CGGeneric(memberString), len(self.indent)).define() result = result + memberString - result = result + self.indent + '};\n' - return result + result += self.indent + '}\n\n' + result += 'impl %s {\n' % self.name - def define(self): - def defineMembers(cgClass, memberList, itemCount, separator=''): - result = '' - for member in memberList: - if itemCount != 0: - result = result + separator - definition = member.define(cgClass) - if definition: - # Member variables would only produce empty lines here. - result += definition - itemCount += 1 - return (result, itemCount) - - order = [(self.members, ''), (self.constructors, '\n'), - (self.destructors, '\n'), (self.methods, '\n')] - - result = self.extradefinitions - itemCount = 0 + order = [(self.constructors + disallowedCopyConstructors, '\n'), + (self.destructors, '\n'), (self.methods, '\n)')] for (memberList, separator) in order: - (memberString, itemCount) = defineMembers(self, memberList, - itemCount, separator) + memberString = declareMembers(self, memberList) + if self.indent: + memberString = CGIndenter(CGGeneric(memberString), + len(self.indent)).define() result = result + memberString + + result += "}" return result + def define(self): + return '' + class CGXrayHelper(CGAbstractExternMethod): def __init__(self, descriptor, name, args, properties): CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) @@ -5301,10 +5217,10 @@ class CGBindingRoot(CGThing): 'js::glue::*', 'dom::types::*', 'dom::bindings::utils::*', + 'dom::bindings::callback::*', 'dom::bindings::conversions::*', 'dom::bindings::codegen::*', #XXXjdm 'script_task::{JSPageInfo, page_from_context}', - 'dom::bindings::utils::EnumEntry', 'dom::bindings::proxyhandler', 'dom::bindings::proxyhandler::*', 'dom::document::AbstractDocument', @@ -5383,29 +5299,31 @@ class CGNativeMember(ClassMethod): never examine this value. """ if type.isVoid(): - return "void", "", "" - if type.isPrimitive() and type.tag() in builtinNames: + typeDecl, errorDefault, template = "", "", "" + elif type.isPrimitive() and type.tag() in builtinNames: result = CGGeneric(builtinNames[type.tag()]) defaultReturnArg = "0" if type.nullable(): result = CGTemplatedType("Nullable", result) defaultReturnArg = "" - return (result.define(), - "%s(%s)" % (result.define(), defaultReturnArg), - "return ${declName};") - if type.isDOMString(): + typeDecl, errorDefault, template = \ + (result.define(), + "%s(%s)" % (result.define(), defaultReturnArg), + "return ${declName};") + elif type.isDOMString(): if isMember: # No need for a third element in the isMember case - return "nsString", None, None + typeDecl, errorDefault, template = "nsString", None, None # Outparam - return "void", "", "retval = ${declName};" - if type.isByteString(): + else: + typeDecl, errorDefault, template = "void", "", "retval = ${declName};" + elif type.isByteString(): if isMember: # No need for a third element in the isMember case - return "nsCString", None, None + typeDecl, errorDefault, template = "nsCString", None, None # Outparam - return "void", "", "retval = ${declName};" - if type.isEnum(): + typeDecl, errorDefault, template = "void", "", "retval = ${declName};" + elif type.isEnum(): enumName = type.unroll().inner.identifier.name if type.nullable(): enumName = CGTemplatedType("Nullable", @@ -5413,8 +5331,8 @@ class CGNativeMember(ClassMethod): defaultValue = "%s()" % enumName else: defaultValue = "%s(0)" % enumName - return enumName, defaultValue, "return ${declName};" - if type.isGeckoInterface(): + typeDecl, errorDefault, template = enumName, defaultValue, "return ${declName};" + elif type.isGeckoInterface(): iface = type.unroll().inner; nativeType = self.descriptorProvider.getDescriptor( iface.identifier.name).nativeType @@ -5442,21 +5360,25 @@ class CGNativeMember(ClassMethod): # Since we always force an owning type for callback return values, # our ${declName} is an OwningNonNull or nsRefPtr. So we can just # .forget() to get our already_AddRefed. - return result.define(), "nullptr", "return ${declName}.forget();" - if type.isCallback(): - return ("already_AddRefed<%s>" % type.unroll().identifier.name, - "nullptr", "return ${declName}.forget();") - if type.isAny(): - return "JS::Value", "JS::UndefinedValue()", "return ${declName};" - if type.isObject(): - return "JSObject*", "nullptr", "return ${declName};" - if type.isSpiderMonkeyInterface(): + typeDecl, errorDefault, template = \ + result.define(), "nullptr", "return ${declName}.forget();" + elif type.isCallback(): + typeDecl, errorDefault, template = \ + ("already_AddRefed<%s>" % type.unroll().identifier.name, + "nullptr", "return ${declName}.forget();") + elif type.isAny(): + typeDecl, errorDefault, template = \ + "JS::Value", "JS::UndefinedValue()", "return ${declName};" + elif type.isObject(): + typeDecl, errorDefault, template = \ + "JSObject*", "nullptr", "return ${declName};" + elif type.isSpiderMonkeyInterface(): if type.nullable(): returnCode = "return ${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();" else: returnCode = "return ${declName}.Obj();" - return "JSObject*", "nullptr", returnCode - if type.isSequence(): + typeDecl, errorDefault, template = "JSObject*", "nullptr", returnCode + elif type.isSequence(): # If we want to handle sequence-of-sequences return values, we're # going to need to fix example codegen to not produce nsTArray<void> # for the relevant argument... @@ -5470,15 +5392,26 @@ class CGNativeMember(ClassMethod): "}") else: returnCode = "retval.SwapElements(${declName});" - return "void", "", returnCode - if type.isDate(): + typeDecl, errorDefault, template = "void", "", returnCode + elif type.isDate(): result = CGGeneric("Date") if type.nullable(): result = CGTemplatedType("Nullable", result) - return (result.define(), "%s()" % result.define(), - "return ${declName};") - raise TypeError("Don't know how to declare return value for %s" % - type) + typeDecl, errorDefault, template = \ + (result.define(), "%s()" % result.define(), "return ${declName};") + else: + raise TypeError("Don't know how to declare return value for %s" % type) + + if not 'infallible' in self.extendedAttrs: + if typeDecl: + typeDecl = "Fallible<%s>" % typeDecl + else: + typeDecl = "ErrorResult" + if not errorDefault: + errorDefault = "Err(FailureUnknown)" + if not template: + template = "return Ok(());" + return typeDecl, errorDefault, template def getArgs(self, returnType, argList): args = [self.getArg(arg) for arg in argList] @@ -5497,10 +5430,6 @@ class CGNativeMember(ClassMethod): if nullable: type = CGTemplatedType("Nullable", type) args.append(Argument("%s&" % type.define(), "retval")) - # And the ErrorResult - if not 'infallible' in self.extendedAttrs: - # Use aRv so it won't conflict with local vars named "rv" - args.append(Argument("ErrorResult&", "aRv")) # The legacycaller thisval if self.member.isMethod() and self.member.isLegacycaller(): # If it has an identifier, we can't deal with it yet @@ -5552,7 +5481,7 @@ class CGNativeMember(ClassMethod): if (optional or isMember) and forceOwningType: typeDecl = "nsRefPtr<%s>" else: - typeDecl = "%s*" + typeDecl = "*%s" else: if optional or isMember: if forceOwningType: @@ -5560,9 +5489,9 @@ class CGNativeMember(ClassMethod): else: typeDecl = "NonNull<%s>" else: - typeDecl = "%s&" - return ((typeDecl % - self.descriptorProvider.getDescriptor(iface.identifier.name).nativeType), + typeDecl = "%s%s" + descriptor = self.descriptorProvider.getDescriptor(iface.identifier.name) + return (typeDecl % (descriptor.pointerType, descriptor.nativeType), False, False) if type.isSpiderMonkeyInterface(): @@ -5692,16 +5621,17 @@ class CGCallback(CGClass): CGClass.__init__(self, name, bases=[ClassBase(baseName)], constructors=self.getConstructors(), - methods=realMethods+getters+setters) + methods=realMethods+getters+setters, + decorators="#[deriving(Eq,Clone)]") def getConstructors(self): return [ClassConstructor( - [Argument("JSObject*", "aCallback")], + [Argument("*JSObject", "aCallback")], bodyInHeader=True, - visibility="public", - explicit=True, + visibility="pub", + explicit=False, baseConstructors=[ - "%s(aCallback)" % self.baseName + "%s::new(aCallback)" % self.baseName ])] def getMethodImpls(self, method): @@ -5709,14 +5639,14 @@ class CGCallback(CGClass): args = list(method.args) # Strip out the JSContext*/JSObject* args # that got added. - assert args[0].name == "cx" and args[0].argType == "JSContext*" - assert args[1].name == "aThisObj" and args[1].argType == "JS::Handle<JSObject*>" + assert args[0].name == "cx" and args[0].argType == "*JSContext" + assert args[1].name == "aThisObj" and args[1].argType == "*JSObject" args = args[2:] # Record the names of all the arguments, so we can use them when we call # the private method. argnames = [arg.name for arg in args] argnamesWithThis = ["s.GetContext()", "thisObjJS"] + argnames - argnamesWithoutThis = ["s.GetContext()", "JS::NullPtr()"] + argnames + argnamesWithoutThis = ["s.GetContext()", "JSVAL_TO_OBJECT(JSVAL_NULL)"] + argnames # Now that we've recorded the argnames for our call to our private # method, insert our optional argument for deciding whether the # CallSetup should re-throw exceptions on aRv. @@ -5724,63 +5654,52 @@ class CGCallback(CGClass): "eReportExceptions")) # And now insert our template argument. argsWithoutThis = list(args) - args.insert(0, Argument("const T&", "thisObj")) + args.insert(0, Argument("@mut 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 = ("CallSetup s(CallbackPreserveColor(), aRv, aExceptionHandling);\n" - "if (!s.GetContext()) {\n" - " aRv.Throw(NS_ERROR_UNEXPECTED);\n" + setupCall = ("let s = CallSetup::new(cx_for_dom_object(${cxProvider}), aExceptionHandling);\n" + "if s.GetContext().is_null() {\n" " return${errorReturn};\n" "}\n") bodyWithThis = string.Template( setupCall+ - "JS::Rooted<JSObject*> thisObjJS(s.GetContext(),\n" - " WrapCallThisObject(s.GetContext(), CallbackPreserveColor(), thisObj));\n" - "if (!thisObjJS) {\n" - " aRv.Throw(NS_ERROR_FAILURE);\n" + "let thisObjJS = WrapCallThisObject(s.GetContext(), ptr::null() /*XXXjdm proper scope*/, thisObj);\n" + "if thisObjJS.is_null() {\n" " return${errorReturn};\n" "}\n" "return ${methodName}(${callArgs});").substitute({ "errorReturn" : method.getDefaultRetval(), "callArgs" : ", ".join(argnamesWithThis), - "methodName": method.name, + "methodName": 'self.' + method.name, + "cxProvider": 'thisObj' }) bodyWithoutThis = string.Template( setupCall + "return ${methodName}(${callArgs});").substitute({ "errorReturn" : method.getDefaultRetval(), "callArgs" : ", ".join(argnamesWithoutThis), - "methodName": 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, - templateArgs=["typename T"], - body=bodyWithThis), - ClassMethod(method.name, method.returnType, argsWithoutThis, + templateArgs=["T: 'static+CallbackContainer+Reflectable"], + body=bodyWithThis, + visibility='pub'), + ClassMethod(method.name+'__', method.returnType, argsWithoutThis, bodyInHeader=True, - body=bodyWithoutThis), + body=bodyWithoutThis, + visibility='pub'), method] def deps(self): return self._deps -class CGCallbackFunction(CGCallback): - def __init__(self, callback, descriptorProvider): - CGCallback.__init__(self, callback, descriptorProvider, - "CallbackFunction", - methods=[CallCallback(callback, descriptorProvider)]) - - def getConstructors(self): - return CGCallback.getConstructors(self) + [ - ClassConstructor( - [Argument("CallbackFunction*", "aOther")], - bodyInHeader=True, - visibility="public", - explicit=True, - baseConstructors=[ - "CallbackFunction(aOther)" - ])] - # We're always fallible def callbackGetterName(attr): return "Get" + MakeNativeName(attr.identifier.name) @@ -5799,7 +5718,7 @@ class CGCallbackFunction(CGCallback): ClassConstructor( [Argument("CallbackFunction*", "aOther")], bodyInHeader=True, - visibility="public", + visibility="pub", explicit=True, baseConstructors=[ "CallbackFunction(aOther)" @@ -5865,7 +5784,7 @@ class CallbackMember(CGNativeMember): self.needThisHandling = needThisHandling # If needThisHandling, we generate ourselves as private and the caller # will handle generating public versions that handle the "this" stuff. - visibility = "private" if needThisHandling else "public" + visibility = "priv" if needThisHandling else "pub" self.rethrowContentException = rethrowContentException # We don't care, for callback codegen, whether our original member was # a method or attribute or whatnot. Just always pass FakeMember() @@ -5878,8 +5797,7 @@ class CallbackMember(CGNativeMember): jsObjectsArePtr=True) # We have to do all the generation of our body now, because # the caller relies on us throwing if we can't manage it. - self.exceptionCode=("aRv.Throw(NS_ERROR_UNEXPECTED);\n" - "return%s;" % self.getDefaultRetval()) + self.exceptionCode= "return Err(FailureUnknown);\n" self.body = self.getImpl() def getImpl(self): @@ -5894,11 +5812,7 @@ class CallbackMember(CGNativeMember): if self.argCount > 0: replacements["argCount"] = self.argCountStr replacements["argvDecl"] = string.Template( - "JS::AutoValueVector argv(cx);\n" - "if (!argv.resize(${argCount})) {\n" - " aRv.Throw(NS_ERROR_OUT_OF_MEMORY);\n" - " return${errorReturn};\n" - "}\n" + "let mut argv = vec::from_elem(${argCount}, JSVAL_VOID);\n" ).substitute(replacements) else: # Avoid weird 0-sized arrays @@ -5929,13 +5843,13 @@ class CallbackMember(CGNativeMember): isCallbackReturnValue = "JSImpl" else: isCallbackReturnValue = "Callback" - convertType = instantiateJSToNativeConversion( - getJSToNativeConversionInfo(self.retvalType, - self.descriptorProvider, - exceptionCode=self.exceptionCode, - isCallbackReturnValue=isCallbackReturnValue, - # XXXbz we should try to do better here - sourceDescription="return value"), + convertType = instantiateJSToNativeConversionTemplate( + getJSToNativeConversionTemplate(self.retvalType, + self.descriptorProvider, + exceptionCode=self.exceptionCode, + isCallbackReturnValue=isCallbackReturnValue, + # XXXbz we should try to do better here + sourceDescription="return value"), replacements) assignRetval = string.Template( self.getRetvalInfo(self.retvalType, @@ -5954,8 +5868,8 @@ class CallbackMember(CGNativeMember): # Wrap each one in a scope so that any locals it has don't leak out, and # also so that we can just "break;" for our successCode. argConversions = [CGWrapper(CGIndenter(CGGeneric(c)), - pre="do {\n", - post="\n} while (0);") + pre="loop {\n", + post="\nbreak;}\n") for c in argConversions] if self.argCount > 0: argConversions.insert(0, self.getArgcDecl()) @@ -5989,10 +5903,11 @@ class CallbackMember(CGNativeMember): 'successCode' : "continue;" if arg.variadic else "break;", 'jsvalRef' : "argv.handleAt(%s)" % jsvalIndex, 'jsvalHandle' : "argv.handleAt(%s)" % jsvalIndex, + 'jsvalPtr': "&mut argv[%s]" % jsvalIndex, # XXXbz we don't have anything better to use for 'obj', # really... It's OK to use CallbackPreserveColor because # CallSetup already handled the unmark-gray bits for us. - 'obj' : 'CallbackPreserveColor()', + 'obj' : 'ptr::null() /*XXXjdm proper scope*/', #XXXjdm 'CallbackPreserveColor()', 'returnsNewObject': False, 'exceptionCode' : self.exceptionCode }) @@ -6033,8 +5948,8 @@ class CallbackMember(CGNativeMember): return args # We want to allow the caller to pass in a "this" object, as # well as a JSContext. - return [Argument("JSContext*", "cx"), - Argument("JS::Handle<JSObject*>", "aThisObj")] + args + return [Argument("*JSContext", "cx"), + Argument("*JSObject", "aThisObj")] + args def getCallSetup(self): if self.needThisHandling: @@ -6052,7 +5967,6 @@ class CallbackMember(CGNativeMember): "${callSetup}\n" "JSContext* cx = s.GetContext();\n" "if (!cx) {\n" - " aRv.Throw(NS_ERROR_UNEXPECTED);\n" " return${errorReturn};\n" "}\n").substitute({ "callSetup": callSetup, @@ -6060,7 +5974,7 @@ class CallbackMember(CGNativeMember): }) def getArgcDecl(self): - return CGGeneric("unsigned argc = %s;" % self.argCountStr); + return CGGeneric("let argc = %su32;" % self.argCountStr); @staticmethod def ensureASCIIName(idlObject): @@ -6082,7 +5996,7 @@ class CallbackMethod(CallbackMember): CallbackMember.__init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException) def getRvalDecl(self): - return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" + return "let mut rval = JSVAL_VOID;\n" def getCall(self): replacements = { @@ -6091,15 +6005,14 @@ class CallbackMethod(CallbackMember): "getCallable": self.getCallableDecl() } if self.argCount > 0: - replacements["argv"] = "argv.begin()" + replacements["argv"] = "&argv[0]" replacements["argc"] = "argc" else: replacements["argv"] = "nullptr" replacements["argc"] = "0" return string.Template("${getCallable}" - "if (!JS_CallFunctionValue(cx, ${thisObj}, callable,\n" - " ${argc}, ${argv}, rval.address())) {\n" - " aRv.Throw(NS_ERROR_UNEXPECTED);\n" + "if unsafe { JS_CallFunctionValue(cx, ${thisObj}, callable,\n" + " ${argc}, ${argv}, &rval) == 0 } {\n" " return${errorReturn};\n" "}\n").substitute(replacements) @@ -6125,11 +6038,11 @@ class CallbackOperationBase(CallbackMethod): def getThisObj(self): if not self.singleOperation: - return "mCallback" + return "self.parent.callback" # This relies on getCallableDecl declaring a boolean # isCallable in the case when we're a single-operation # interface. - return "isCallable ? aThisObj.get() : mCallback" + return "if isCallable { aThisObj } else { self.parent.callback }" def getCallableDecl(self): replacements = { @@ -6137,17 +6050,16 @@ class CallbackOperationBase(CallbackMethod): "methodName": self.methodName } getCallableFromProp = string.Template( - 'if (!GetCallableProperty(cx, "${methodName}", &callable)) {\n' - ' aRv.Throw(NS_ERROR_UNEXPECTED);\n' + 'if "${methodName}".to_c_str().with_ref(|name| !self.parent.GetCallableProperty(cx, name, &mut callable)) {\n' ' return${errorReturn};\n' '}\n').substitute(replacements) if not self.singleOperation: return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp return ( - 'bool isCallable = JS_ObjectIsCallable(cx, mCallback);\n' - 'JS::Rooted<JS::Value> callable(cx);\n' - 'if (isCallable) {\n' - ' callable = JS::ObjectValue(*mCallback);\n' + 'let isCallable = unsafe { JS_ObjectIsCallable(cx, self.parent.callback) != 0 };\n' + 'let mut callable = JSVAL_VOID;\n' + 'if isCallable {\n' + ' callable = unsafe { RUST_OBJECT_TO_JSVAL(self.parent.callback) };\n' '} else {\n' '%s' '}\n' % CGIndenter(CGGeneric(getCallableFromProp)).define()) @@ -6304,7 +6216,12 @@ class GlobalGenRoots(): @staticmethod def InterfaceTypes(config): - descriptors = [d.name for d in config.getDescriptors(register=True)] + def pathToType(descriptor): + if descriptor.interface.isCallback(): + return "dom::bindings::codegen::%sBinding" % descriptor.name + return "dom::%s" % descriptor.name.lower() + + descriptors = [d.name for d in config.getDescriptors(register=True, hasInterfaceObject=True)] curr = CGList([CGGeneric(declare="pub use dom::%s::%s;\n" % (name.lower(), name)) for name in descriptors]) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) return curr diff --git a/src/components/script/dom/bindings/codegen/Document.webidl b/src/components/script/dom/bindings/codegen/Document.webidl index ef479f01a97..10d212910fc 100644 --- a/src/components/script/dom/bindings/codegen/Document.webidl +++ b/src/components/script/dom/bindings/codegen/Document.webidl @@ -58,8 +58,8 @@ interface Document : Node { [Throws] Node adoptNode(Node node);*/ - // [Creator, Throws] - // Event createEvent(DOMString interface_); + [Creator, Throws] + Event createEvent(DOMString interface_); /*[Creator, Throws] Range createRange();*/ diff --git a/src/components/script/dom/bindings/codegen/EventListener.webidl b/src/components/script/dom/bindings/codegen/EventListener.webidl new file mode 100644 index 00000000000..05e1684d31e --- /dev/null +++ b/src/components/script/dom/bindings/codegen/EventListener.webidl @@ -0,0 +1,16 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * http://www.w3.org/TR/2012/WD-dom-20120105/ + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +callback interface EventListener { + void handleEvent(Event event); +}; + diff --git a/src/components/script/dom/bindings/codegen/EventTarget.webidl b/src/components/script/dom/bindings/codegen/EventTarget.webidl index f4e1ba00f70..897756fa273 100644 --- a/src/components/script/dom/bindings/codegen/EventTarget.webidl +++ b/src/components/script/dom/bindings/codegen/EventTarget.webidl @@ -11,4 +11,12 @@ */ interface EventTarget { + void addEventListener(DOMString type, + EventListener? listener, + optional boolean capture = false); + void removeEventListener(DOMString type, + EventListener? listener, + optional boolean capture = false); + [Throws] + boolean dispatchEvent(Event event); }; diff --git a/src/components/script/dom/bindings/codegen/Node.webidl b/src/components/script/dom/bindings/codegen/Node.webidl index 5f32ff93f67..1a9d9d53bb3 100644 --- a/src/components/script/dom/bindings/codegen/Node.webidl +++ b/src/components/script/dom/bindings/codegen/Node.webidl @@ -14,7 +14,7 @@ interface URI; interface UserDataHandler;*/ -interface Node /*: EventTarget*/ { +interface Node : EventTarget { const unsigned short ELEMENT_NODE = 1; const unsigned short ATTRIBUTE_NODE = 2; // historical const unsigned short TEXT_NODE = 3; diff --git a/src/components/script/dom/bindings/codegen/Window.webidl b/src/components/script/dom/bindings/codegen/Window.webidl index b85890676eb..59ea26308e3 100644 --- a/src/components/script/dom/bindings/codegen/Window.webidl +++ b/src/components/script/dom/bindings/codegen/Window.webidl @@ -8,7 +8,7 @@ */ [NamedPropertiesObject] -/*sealed*/ interface Window /*: EventTarget*/ { +/*sealed*/ interface Window : EventTarget { // the current browsing context /*[Unforgeable] readonly attribute WindowProxy window; [Replaceable] readonly attribute WindowProxy self;*/ diff --git a/src/components/script/dom/bindings/node.rs b/src/components/script/dom/bindings/node.rs index 0ec5b13e80b..8574c084062 100644 --- a/src/components/script/dom/bindings/node.rs +++ b/src/components/script/dom/bindings/node.rs @@ -50,7 +50,7 @@ impl Traceable for Node<ScriptView> { } } } - debug!("tracing {:p}?:", self.reflector_.get_jsobject()); + debug!("tracing {:p}?:", self.reflector().get_jsobject()); trace_node(tracer, self.parent_node, "parent"); trace_node(tracer, self.first_child, "first child"); trace_node(tracer, self.last_child, "last child"); diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs index 42f1eb7f4fc..8e8200fb18e 100644 --- a/src/components/script/dom/bindings/utils.rs +++ b/src/components/script/dom/bindings/utils.rs @@ -4,8 +4,8 @@ use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH; -use dom::window; use dom::node::{AbstractNode, ScriptView}; +use dom::window; use std::libc::c_uint; use std::cast; @@ -22,6 +22,7 @@ use js::glue::{js_IsObjectProxyClass, js_IsFunctionProxyClass, IsProxyHandlerFam use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewObject, JS_NewFunction, JS_GetGlobalObject}; use js::jsapi::{JS_DefineProperties, JS_WrapValue, JS_ForwardGetPropertyTo}; use js::jsapi::{JS_GetClass, JS_LinkConstructorAndPrototype, JS_GetStringCharsAndLength}; +use js::jsapi::{JS_ObjectIsRegExp, JS_ObjectIsDate}; use js::jsapi::{JS_GetFunctionPrototype, JS_InternString, JS_GetFunctionObject}; use js::jsapi::{JS_HasPropertyById, JS_GetPrototype, JS_GetGlobalForObject}; use js::jsapi::{JS_NewUCStringCopyN, JS_DefineFunctions, JS_DefineProperty}; @@ -30,7 +31,7 @@ use js::jsapi::{JSContext, JSObject, JSBool, jsid, JSClass, JSNative, JSTracer}; use js::jsapi::{JSFunctionSpec, JSPropertySpec, JSVal, JSPropertyDescriptor}; use js::jsapi::{JSPropertyOp, JSStrictPropertyOp, JS_NewGlobalObject, JS_InitStandardClasses}; use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType; -use js::{JSPROP_ENUMERATE, JSVAL_NULL}; +use js::{JSPROP_ENUMERATE, JSVAL_NULL, JSCLASS_IS_GLOBAL, JSCLASS_IS_DOMJSCLASS}; use js::{JSPROP_PERMANENT, JSID_VOID, JSPROP_NATIVE_ACCESSORS, JSPROP_GETTER}; use js::{JSPROP_SETTER, JSVAL_VOID, JSVAL_TRUE, JSVAL_FALSE}; use js::{JS_THIS_OBJECT, JSFUN_CONSTRUCTOR, JS_CALLEE, JSPROP_READONLY}; @@ -820,6 +821,13 @@ pub fn HasPropertyOnPrototype(cx: *JSContext, proxy: *JSObject, id: jsid) -> boo } #[fixed_stack_segment] +pub fn IsConvertibleToCallbackInterface(cx: *JSContext, obj: *JSObject) -> bool { + unsafe { + JS_ObjectIsDate(cx, obj) == 0 && JS_ObjectIsRegExp(cx, obj) == 0 + } +} + +#[fixed_stack_segment] pub fn CreateDOMGlobal(cx: *JSContext, class: *JSClass) -> *JSObject { unsafe { let obj = JS_NewGlobalObject(cx, class, ptr::null()); @@ -832,6 +840,30 @@ pub fn CreateDOMGlobal(cx: *JSContext, class: *JSClass) -> *JSObject { } } +#[fixed_stack_segment] +fn cx_for_dom_wrapper(obj: *JSObject) -> *JSContext { + unsafe { + let global = GetGlobalForObjectCrossCompartment(obj); + let clasp = JS_GetClass(global); + assert!(((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)) != 0); + //XXXjdm either don't hardcode or sanity assert prototype stuff + let win = unwrap_object::<*Box<window::Window>>(global, PrototypeList::id::Window, 1); + match win { + Ok(win) => { + match (*win).data.page.js_info { + Some(ref info) => info.js_context.ptr, + None => fail!("no JS context for DOM global") + } + } + Err(_) => fail!("found DOM global that doesn't unwrap to Window") + } + } +} + +pub fn cx_for_dom_object<T: Reflectable>(obj: @mut T) -> *JSContext { + cx_for_dom_wrapper(obj.reflector().get_jsobject()) +} + /// Check if an element name is valid. See http://www.w3.org/TR/xml/#NT-Name /// for details. pub fn is_valid_element_name(name: &str) -> bool { diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index 9805ea71dfa..72f0c378a95 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -10,6 +10,7 @@ use dom::bindings::utils::{is_valid_element_name, InvalidCharacter, Traceable, n use dom::documentfragment::DocumentFragment; use dom::element::{Element}; use dom::element::{HTMLHeadElementTypeId, HTMLTitleElementTypeId}; +use dom::event::Event; use dom::htmlcollection::HTMLCollection; use dom::htmldocument::HTMLDocument; use dom::node::{AbstractNode, ScriptView, Node, ElementNodeTypeId, DocumentNodeTypeId}; @@ -255,6 +256,11 @@ impl Document { Comment::new(null_str_as_word_null(data), abstract_self) } + pub fn CreateEvent(&self, interface: &DOMString) -> Fallible<@mut Event> { + //FIXME: We need to do a proper Event inheritance simulation + Ok(Event::new(self.window, interface)) + } + pub fn Title(&self, _: AbstractDocument) -> DOMString { let mut title = ~""; match self.doctype { diff --git a/src/components/script/dom/event.rs b/src/components/script/dom/event.rs index 43ee6f894c1..1fd709f2dfe 100644 --- a/src/components/script/dom/event.rs +++ b/src/components/script/dom/event.rs @@ -6,7 +6,7 @@ use dom::eventtarget::EventTarget; use dom::window::Window; use dom::bindings::codegen::EventBinding; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; -use dom::bindings::utils::{DOMString, ErrorResult, Fallible}; +use dom::bindings::utils::{DOMString, ErrorResult, Fallible, null_str_as_word_null}; use geom::point::Point2D; use js::jsapi::{JSObject, JSContext}; @@ -23,7 +23,7 @@ pub enum Event_ { pub struct Event { reflector_: Reflector, - type_: DOMString, + type_: ~str, default_prevented: bool, cancelable: bool, bubbles: bool, @@ -34,7 +34,7 @@ impl Event { pub fn new_inherited(type_: &DOMString) -> Event { Event { reflector_: Reflector::new(), - type_: (*type_).clone(), + type_: null_str_as_word_null(type_), default_prevented: false, cancelable: true, bubbles: true, @@ -51,7 +51,7 @@ impl Event { } pub fn Type(&self) -> DOMString { - self.type_.clone() + Some(self.type_.clone()) } pub fn GetTarget(&self) -> Option<@mut EventTarget> { @@ -92,7 +92,7 @@ impl Event { type_: &DOMString, bubbles: bool, cancelable: bool) -> ErrorResult { - self.type_ = (*type_).clone(); + self.type_ = type_.to_str(); self.cancelable = cancelable; self.bubbles = bubbles; Ok(()) diff --git a/src/components/script/dom/eventtarget.rs b/src/components/script/dom/eventtarget.rs index 35a1fe6e6cc..834c1db767c 100644 --- a/src/components/script/dom/eventtarget.rs +++ b/src/components/script/dom/eventtarget.rs @@ -2,26 +2,96 @@ * 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::callback::eReportExceptions; use dom::bindings::codegen::EventTargetBinding; -use dom::bindings::utils::{Reflectable, Reflector}; +use dom::bindings::utils::{Reflectable, Reflector, DOMString, Fallible}; +use dom::bindings::codegen::EventListenerBinding::EventListener; +use dom::event::Event; use script_task::page_from_context; use js::jsapi::{JSObject, JSContext}; +use std::hashmap::HashMap; + pub struct EventTarget { - reflector_: Reflector + reflector_: Reflector, + capturing_handlers: HashMap<~str, ~[EventListener]>, + bubbling_handlers: HashMap<~str, ~[EventListener]> } impl EventTarget { - pub fn new() -> ~EventTarget { - ~EventTarget { - reflector_: Reflector::new() + pub fn new() -> EventTarget { + EventTarget { + reflector_: Reflector::new(), + capturing_handlers: HashMap::new(), + bubbling_handlers: HashMap::new(), } } pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { self.wrap_object_shared(cx, scope); } + + pub fn AddEventListener(&mut self, + ty: &DOMString, + listener: Option<EventListener>, + capture: bool) { + // TODO: Handle adding a listener during event dispatch: should not be invoked during + // current phase. + // (https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener#Adding_a_listener_during_event_dispatch) + + for listener in listener.iter() { + let handlers = if capture { + &mut self.capturing_handlers + } else { + &mut self.bubbling_handlers + }; + let entry = handlers.find_or_insert_with(ty.to_str(), |_| ~[]); + if entry.position_elem(listener).is_none() { + entry.push((*listener).clone()); + } + } + } + + pub fn RemoveEventListener(&mut self, + ty: &DOMString, + listener: Option<EventListener>, + capture: bool) { + for listener in listener.iter() { + let handlers = if capture { + &mut self.capturing_handlers + } else { + &mut self.bubbling_handlers + }; + let mut entry = handlers.find_mut(&ty.to_str()); + for entry in entry.mut_iter() { + let position = entry.position_elem(listener); + for &position in position.iter() { + entry.remove(position); + } + } + } + } + + pub fn DispatchEvent(&self, event: @mut Event) -> Fallible<bool> { + //FIXME: get proper |this| object + + let maybe_handlers = self.capturing_handlers.find(&event.type_); + for handlers in maybe_handlers.iter() { + for handler in handlers.iter() { + handler.HandleEvent__(event, eReportExceptions); + } + } + if event.bubbles { + let maybe_handlers = self.bubbling_handlers.find(&event.type_); + for handlers in maybe_handlers.iter() { + for handler in handlers.iter() { + handler.HandleEvent__(event, eReportExceptions); + } + } + } + Ok(!event.DefaultPrevented()) + } } impl Reflectable for EventTarget { diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index 2caa76eed69..2e331cc82f9 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -12,6 +12,7 @@ use dom::document::{AbstractDocument, DocumentTypeId}; use dom::documenttype::DocumentType; use dom::element::{Element, ElementTypeId, HTMLImageElementTypeId, HTMLIframeElementTypeId}; use dom::element::{HTMLStyleElementTypeId}; +use dom::eventtarget::EventTarget; use dom::nodelist::{NodeList}; use dom::htmlimageelement::HTMLImageElement; use dom::htmliframeelement::HTMLIFrameElement; @@ -63,7 +64,7 @@ pub struct AbstractNodeChildrenIterator<View> { /// `LayoutData`. pub struct Node<View> { /// The JavaScript reflector for this node. - reflector_: Reflector, + eventtarget: EventTarget, /// The type of node that this is. type_id: NodeTypeId, @@ -521,7 +522,7 @@ impl Node<ScriptView> { fn new_(type_id: NodeTypeId, doc: Option<AbstractDocument>) -> Node<ScriptView> { Node { - reflector_: Reflector::new(), + eventtarget: EventTarget::new(), type_id: type_id, abstract: None, @@ -961,11 +962,11 @@ impl Node<ScriptView> { impl Reflectable for Node<ScriptView> { fn reflector<'a>(&'a self) -> &'a Reflector { - &self.reflector_ + self.eventtarget.reflector() } fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector { - &mut self.reflector_ + self.eventtarget.mut_reflector() } fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs index 0a5bb19d3ec..46565b7fb09 100644 --- a/src/components/script/dom/window.rs +++ b/src/components/script/dom/window.rs @@ -6,6 +6,7 @@ use dom::bindings::codegen::WindowBinding; use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{DOMString, null_str_as_empty, Traceable}; use dom::document::AbstractDocument; +use dom::eventtarget::EventTarget; use dom::node::{AbstractNode, ScriptView}; use dom::navigator::Navigator; @@ -37,10 +38,10 @@ pub enum TimerControlMsg { } pub struct Window { + eventtarget: EventTarget, page: @mut Page, script_chan: ScriptChan, compositor: @ScriptListener, - reflector_: Reflector, timer_chan: SharedChan<TimerControlMsg>, navigator: Option<@mut Navigator>, image_cache_task: ImageCacheTask, @@ -140,11 +141,11 @@ impl Window { impl Reflectable for Window { fn reflector<'a>(&'a self) -> &'a Reflector { - &self.reflector_ + self.eventtarget.reflector() } fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector { - &mut self.reflector_ + self.eventtarget.mut_reflector() } fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { @@ -204,10 +205,10 @@ impl Window { image_cache_task: ImageCacheTask) -> @mut Window { let win = @mut Window { + eventtarget: EventTarget::new(), page: page, script_chan: script_chan.clone(), compositor: compositor, - reflector_: Reflector::new(), timer_chan: { let (timer_port, timer_chan) = comm::stream::<TimerControlMsg>(); let id = page.id.clone(); diff --git a/src/components/script/script.rc b/src/components/script/script.rc index bef239a5ec9..675796e2c47 100644 --- a/src/components/script/script.rc +++ b/src/components/script/script.rc @@ -28,6 +28,7 @@ pub mod dom { pub mod element; pub mod node; pub mod utils; + pub mod callback; pub mod conversions; pub mod proxyhandler; pub mod codegen { diff --git a/src/support/spidermonkey/rust-mozjs b/src/support/spidermonkey/rust-mozjs -Subproject 1df26877d82d9722785de91ff59ab069a5acc18 +Subproject 566c2af971abaa5e8c51b59fa400a7e07835b25 diff --git a/src/test/html/content/test_event_listener.html b/src/test/html/content/test_event_listener.html new file mode 100644 index 00000000000..5feeda8a374 --- /dev/null +++ b/src/test/html/content/test_event_listener.html @@ -0,0 +1,17 @@ +<html> +<head> +<script src="harness.js"></script> +</head> +<body> +<script> + function onFoopy() { + window.removeEventListener('foopy', onFoopy); + finish(); + } + window.addEventListener('foopy', onFoopy); + var ev = document.createEvent('Event'); + ev.initEvent('foopy', true, true); + window.dispatchEvent(ev); +</script> +</body> +</html> |