From e1c406f594dc86a75b0bb02136214a4263e4878f Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Tue, 9 Jul 2013 14:33:55 -0400 Subject: [PATCH 1/8] Avoid endlessly regenerating bindings after modifying CodegenRust.py. --- .../dom/bindings/codegen/CodegenRust.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 055cb0ce488..0cad50c7719 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -24,16 +24,21 @@ def replaceFileIfChanged(filename, newContents): Read a copy of the old file, so that we don't touch it if it hasn't changed. Returns True if the file was updated, false otherwise. """ - oldFileContents = "" - try: - oldFile = open(filename, 'rb') - oldFileContents = ''.join(oldFile.readlines()) - oldFile.close() - except: - pass + #XXXjdm This doesn't play well with make right now. + # Force the file to always be updated, or else changing CodegenRust.py + # will cause many autogenerated bindings to be regenerated perpetually + # until the result is actually different. - if newContents == oldFileContents: - return False + #oldFileContents = "" + #try: + # oldFile = open(filename, 'rb') + # oldFileContents = ''.join(oldFile.readlines()) + # oldFile.close() + #except: + # pass + + #if newContents == oldFileContents: + # return False f = open(filename, 'wb') f.write(newContents) From ea983cf8e4eefd38a06762d682e2a70904a06309 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 10 Jul 2013 16:18:02 -0400 Subject: [PATCH 2/8] Import partial interface support for WebIDL parsing from Gecko. --- .../dom/bindings/codegen/parser/WebIDL.py | 79 +++++++++++++++++-- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/src/components/script/dom/bindings/codegen/parser/WebIDL.py b/src/components/script/dom/bindings/codegen/parser/WebIDL.py index 98273a419e6..1aab3debc6a 100644 --- a/src/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/src/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -429,17 +429,19 @@ class IDLExternalInterface(IDLObjectWithIdentifier): pass class IDLInterface(IDLObjectWithScope): - def __init__(self, location, parentScope, name, parent, members): + def __init__(self, location, parentScope, name, parent, members, + isPartial): assert isinstance(parentScope, IDLScope) assert isinstance(name, IDLUnresolvedIdentifier) - assert not parent or isinstance(parent, IDLIdentifierPlaceholder) + assert not isPartial or not parent - self.parent = parent + self.parent = None self._callback = False self._finished = False - self.members = list(members) # clone the list + self.members = [] self.implementedInterfaces = set() self._consequential = False + self._isPartial = True # self.interfacesBasedOnSelf is the set of interfaces that inherit from # self or have self as a consequential interface, including self itself. # Used for distinguishability checking. @@ -447,6 +449,12 @@ class IDLInterface(IDLObjectWithScope): IDLObjectWithScope.__init__(self, location, parentScope, name) + if not isPartial: + self.setNonPartial(location, parent, members) + else: + # Just remember our members for now + self.members = list(members) # clone the list + def __str__(self): return "Interface '%s'" % self.identifier.name @@ -482,6 +490,11 @@ class IDLInterface(IDLObjectWithScope): self._finished = True + if self._isPartial: + raise WebIDLError("Interface %s does not have a non-partial " + "declaration" % self.identifier.name, + [self.location]) + assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder) parent = self.parent.finish(scope) if self.parent else None if parent and isinstance(parent, IDLExternalInterface): @@ -749,6 +762,21 @@ class IDLInterface(IDLObjectWithScope): def getExtendedAttribute(self, name): return self._extendedAttrDict.get(name, None) + def setNonPartial(self, location, parent, members): + assert not parent or isinstance(parent, IDLIdentifierPlaceholder) + if not self._isPartial: + raise WebIDLError("Two non-partial definitions for the " + "same interface", + [location, self.location]) + self._isPartial = False + # Now make it look like we were parsed at this new location, since + # that's the place where the interface is "really" defined + self.location = location + assert not self.parent + self.parent = parent + # Put the new members at the beginning + self.members = members + self.members + class IDLDictionary(IDLObjectWithScope): def __init__(self, location, parentScope, name, parent, members): assert isinstance(parentScope, IDLScope) @@ -2742,9 +2770,25 @@ class Parser(Tokenizer): """ location = self.getLocation(p, 1) identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - members = p[5] - p[0] = IDLInterface(location, self.globalScope(), identifier, p[3], members) + parent = p[3] + + try: + if self.globalScope()._lookupIdentifier(identifier): + p[0] = self.globalScope()._lookupIdentifier(identifier) + if not isinstance(p[0], IDLInterface): + raise WebIDLError("Partial interface has the same name as " + "non-interface object", + [location, p[0].location]) + p[0].setNonPartial(location, parent, members) + return + except Exception, ex: + if isinstance(ex, WebIDLError): + raise ex + pass + + p[0] = IDLInterface(location, self.globalScope(), identifier, parent, + members, isPartial=False) def p_InterfaceForwardDecl(self, p): """ @@ -2766,6 +2810,29 @@ class Parser(Tokenizer): """ PartialInterface : PARTIAL INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON """ + location = self.getLocation(p, 2) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) + members = p[5] + + try: + if self.globalScope()._lookupIdentifier(identifier): + p[0] = self.globalScope()._lookupIdentifier(identifier) + if not isinstance(p[0], IDLInterface): + raise WebIDLError("Partial interface has the same name as " + "non-interface object", + [location, p[0].location]) + # Just throw our members into the existing IDLInterface. If we + # have extended attributes, those will get added to it + # automatically. + p[0].members.extend(members) + return + except Exception, ex: + if isinstance(ex, WebIDLError): + raise ex + pass + + p[0] = IDLInterface(location, self.globalScope(), identifier, None, + members, isPartial=True) pass def p_Inheritance(self, p): From d0ddca732334c019b6e76e483a81b36750e13154 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 10 Jul 2013 16:26:27 -0400 Subject: [PATCH 3/8] WebIDL codegen: Add setter support. --- .../dom/bindings/codegen/CodegenRust.py | 75 ++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 0cad50c7719..5c9a0ab8726 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -3110,6 +3110,25 @@ class FakeArgument(): self.enforceRange = False self.clamp = False +class CGSetterCall(CGPerSignatureCall): + """ + A class to generate a native object setter call for a particular IDL + setter. + """ + def __init__(self, argType, nativeMethodName, descriptor, attr): + CGPerSignatureCall.__init__(self, None, [], + [FakeArgument(argType, attr)], + nativeMethodName, False, descriptor, attr, + setter=True) + def wrap_return_value(self): + # We have no return value + return "\nreturn 1;" + def getArgc(self): + return "1" + def getArgvDecl(self): + # We just get our stuff from our last arg no matter what + return "" + class CGAbstractBindingMethod(CGAbstractExternMethod): """ Common class to generate the JSNatives for all our methods, getters, and @@ -3141,7 +3160,7 @@ class CGAbstractBindingMethod(CGAbstractExternMethod): def getThis(self): return CGIndenter( - CGGeneric("let obj: *JSObject = JS_THIS_OBJECT(cx, vp);\n" + CGGeneric("let obj: *JSObject = JS_THIS_OBJECT(cx, cast::transmute(vp));\n" "if obj.is_null() {\n" " return false as JSBool;\n" "}\n" @@ -3251,6 +3270,56 @@ class CGSpecializedGetter(CGAbstractExternMethod): self.descriptor, self.attr)), pre=" let obj = (*obj.unnamed);\n").define() +class CGGenericSetter(CGAbstractBindingMethod): + """ + A class for generating the Rust code for an IDL attribute setter. + """ + def __init__(self, descriptor, lenientThis=False): + args = [Argument('*JSContext', 'cx'), Argument('uint', 'argc'), + Argument('*mut JSVal', 'vp')] + if lenientThis: + name = "genericLenientSetter" + unwrapFailureCode = ( + "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n" + "return true;") + else: + name = "genericSetter" + unwrapFailureCode = None + CGAbstractBindingMethod.__init__(self, descriptor, name, args, + unwrapFailureCode) + + def generate_code(self): + return CGIndenter(CGGeneric( + "let undef = JSVAL_VOID;\n" + "let argv: *JSVal = if argc != 0 { JS_ARGV(cx, cast::transmute(vp)) } else { &undef as *JSVal };\n" + "let info: *JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, cast::transmute(vp)));\n" + "if CallJitPropertyOp(info, cx, obj, ptr::to_unsafe_ptr(&(*this).payload) as *libc::c_void, cast::transmute(vp)) == 0 {" + " return 0;\n" + "}\n" + "*vp = JSVAL_VOID;\n" + "return 1;")) + +class CGSpecializedSetter(CGAbstractExternMethod): + """ + A class for generating the code for a specialized attribute setter + that the JIT can call with lower overhead. + """ + def __init__(self, descriptor, attr): + self.attr = attr + name = 'set_' + attr.identifier.name + args = [ Argument('*JSContext', 'cx'), + Argument('JSHandleObject', 'obj'), + Argument('*mut %s' % descriptor.nativeType, 'this'), + Argument('*mut JSVal', 'argv')] + CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args) + + def definition_body(self): + name = self.attr.identifier.name + nativeName = "Set" + MakeNativeName(self.descriptor.binaryNames.get(name, name)) + return CGWrapper(CGIndenter(CGSetterCall(self.attr.type, nativeName, + self.descriptor, self.attr)), + pre=" let obj = (*obj.unnamed);\n").define() + def infallibleForMember(member, type, descriptorProvider): """ Determine the fallibility of changing a C++ value of IDL type "type" into @@ -3789,7 +3858,7 @@ class CGDescriptor(CGThing): else: hasGetter = True if not m.readonly: - #cgThings.append(CGSpecializedSetter(descriptor, m)) + cgThings.append(CGSpecializedSetter(descriptor, m)) if m.hasLenientThis(): hasLenientSetter = True else: @@ -3799,7 +3868,7 @@ class CGDescriptor(CGThing): if hasGetter: cgThings.append(CGGenericGetter(descriptor)) #if hasLenientGetter: cgThings.append(CGGenericGetter(descriptor, # lenientThis=True)) - #if hasSetter: cgThings.append(CGGenericSetter(descriptor)) + if hasSetter: cgThings.append(CGGenericSetter(descriptor)) #if hasLenientSetter: cgThings.append(CGGenericSetter(descriptor, # lenientThis=True)) From 354ac9b8cb0c6f08f3ce4ad602c1a7cc7937333a Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 10 Jul 2013 16:27:43 -0400 Subject: [PATCH 4/8] Add missing license header. --- .../script/dom/bindings/codegen/Event.webidl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/script/dom/bindings/codegen/Event.webidl b/src/components/script/dom/bindings/codegen/Event.webidl index 48af319ada0..f60fee5d785 100644 --- a/src/components/script/dom/bindings/codegen/Event.webidl +++ b/src/components/script/dom/bindings/codegen/Event.webidl @@ -1,3 +1,15 @@ +/* -*- 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/. + * + * For more information on this interface please see + * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + [Constructor(DOMString type, optional EventInit eventInitDict)] interface Event { readonly attribute DOMString type; From 0d39bd1ba9bbc1e273cb68e27b3837a68eade2ef Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 10 Jul 2013 16:34:02 -0400 Subject: [PATCH 5/8] DOM bindings: Add support for fallible JS unwrapping. --- .../dom/bindings/codegen/CodegenRust.py | 58 ++++++++----------- src/components/script/dom/bindings/utils.rs | 51 +++++++++++++++- src/support/spidermonkey/rust-mozjs | 2 +- 3 files changed, 75 insertions(+), 36 deletions(-) diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 5c9a0ab8726..8926674bd18 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -94,6 +94,8 @@ class CastableObjectUnwrapper(): assert descriptor.castable self.substitution = { "type" : descriptor.nativeType, + "depth": descriptor.interface.inheritanceDepth(), + "prototype": "prototypes::id::" + descriptor.name, "protoID" : "prototypes::id::" + descriptor.name + " as uint", "source" : source, "target" : target, @@ -118,8 +120,14 @@ class CastableObjectUnwrapper(): def __str__(self): return string.Template( -"""${target} = unwrap(${source}); +"""match unwrap_object(${source}, ${prototype}, ${depth}) { + Ok(val) => ${target} = val, + Err(()) => { + ${codeOnFailure} + } +} """).substitute(self.substitution) + #"""{ # nsresult rv = UnwrapObject<${protoID}, ${type}>(cx, ${source}, ${target}); # if (NS_FAILED(rv)) { @@ -133,7 +141,7 @@ class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper): """ def __init__(self, descriptor, source, target): CastableObjectUnwrapper.__init__(self, descriptor, source, target, - "return Throw<%s>(cx, rv);" % + "return 0; //XXXjdm return Throw<%s>(cx, rv);" % toStringBool(not descriptor.workers)) class CGThing(): @@ -397,6 +405,10 @@ class FakeCastableDescriptor(): self.pointerType = descriptor.pointerType self.name = descriptor.name self.hasXPConnectImpls = descriptor.hasXPConnectImpls + class FakeInterface: + def inheritanceDepth(self): + return descriptor.interface.inheritanceDepth() + self.interface = FakeInterface() def dictionaryHasSequenceMember(dictionary): return (any(typeIsSequenceOrHasSequenceMember(m.type) for m in @@ -934,41 +946,19 @@ for (uint32_t i = 0; i < length; ++i) { elif descriptor.workers: templateBody += "${declName} = &${val}.toObject();" else: - # Either external, or new-binding non-castable. We always have a - # holder for these, because we don't actually know whether we have - # to addref when unwrapping or not. So we just pass an - # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release - # it'll put a non-null pointer in there. - if forceOwningType: - # Don't return a holderType in this case; our declName - # will just own stuff. - templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n" - else: - holderType = "nsRefPtr<" + typeName + ">" templateBody += ( - "jsval tmpVal = ${val};\n" + - typePtr + " tmp;\n" - "if (NS_FAILED(xpc_qsUnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n") + "match unwrap_value::<" + typePtr + ">(&${val} as *JSVal, " + "prototypes::id::%s, %d) {\n" % (descriptor.name, descriptor.interface.inheritanceDepth() if descriptor.concrete else 0) + + " Err(()) => {") templateBody += CGIndenter(onFailureBadType(failureCode, descriptor.interface.identifier.name)).define() - templateBody += ("}\n" - "MOZ_ASSERT(tmp);\n") - - if not isDefinitelyObject: - # Our tmpVal will go out of scope, so we can't rely on it - # for rooting - templateBody += ( - "if (tmpVal != ${val} && !${holderName}) {\n" - " // We have to have a strong ref, because we got this off\n" - " // some random object that might get GCed\n" - " ${holderName} = tmp;\n" - "}\n") - - # And store our tmp, before it goes out of scope. - templateBody += "${declName} = tmp;" + templateBody += ( + " }\n" + " Ok(unwrapped) => ${declName} = Some(unwrapped)\n" + "}\n") templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject, - type, "${declName} = NULL", + type, "${declName} = None", failureCode) declType = CGGeneric(declType) @@ -2849,7 +2839,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): getPrototypeOf: ptr::null() }; (*script_context).dom_static.proxy_handlers.insert(prototypes::id::%s as uint, - CreateProxyHandler(ptr::to_unsafe_ptr(&traps))); + CreateProxyHandler(ptr::to_unsafe_ptr(&traps), ptr::to_unsafe_ptr(&Class) as *libc::c_void)); """ % self.descriptor.name else: @@ -3141,7 +3131,7 @@ class CGAbstractBindingMethod(CGAbstractExternMethod): CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args) if unwrapFailureCode is None: - self.unwrapFailureCode = ("return Throw<%s>(cx, rv);" % + self.unwrapFailureCode = ("return 0; //XXXjdm return Throw<%s>(cx, rv);" % toStringBool(not descriptor.workers)) else: self.unwrapFailureCode = unwrapFailureCode diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs index 5fc2fcfc647..4a9d26ee36f 100644 --- a/src/components/script/dom/bindings/utils.rs +++ b/src/components/script/dom/bindings/utils.rs @@ -17,6 +17,7 @@ use std::uint; use std::unstable::intrinsics; use js::glue::*; use js::glue::{DefineFunctionWithReserved, GetObjectJSClass, RUST_OBJECT_TO_JSVAL}; +use js::glue::{js_IsObjectProxyClass, js_IsFunctionProxyClass, IsProxyHandlerFamily}; use js::glue::{PROPERTY_STUB, STRICT_PROPERTY_STUB, ENUMERATE_STUB, CONVERT_STUB, RESOLVE_STUB}; use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewObject, JS_NewFunction}; use js::jsapi::{JS_DefineProperties, JS_WrapValue, JS_ForwardGetPropertyTo}; @@ -117,16 +118,62 @@ fn is_dom_class(clasp: *JSClass) -> bool { } } +fn is_dom_proxy(obj: *JSObject) -> bool { + unsafe { + (js_IsObjectProxyClass(obj) || js_IsFunctionProxyClass(obj)) && + IsProxyHandlerFamily(obj) + } +} + pub unsafe fn unwrap(obj: *JSObject) -> T { - let slot = if is_dom_class(JS_GetClass(obj)) { + let clasp = JS_GetClass(obj); + let slot = if is_dom_class(clasp) { DOM_OBJECT_SLOT } else { + assert!(is_dom_proxy(obj)); DOM_PROXY_OBJECT_SLOT } as u32; let val = JS_GetReservedSlot(obj, slot); cast::transmute(RUST_JSVAL_TO_PRIVATE(val)) } +pub unsafe fn get_dom_class(obj: *JSObject) -> Result { + let clasp = JS_GetClass(obj); + if is_dom_class(clasp) { + debug!("plain old dom object"); + let domjsclass: *DOMJSClass = cast::transmute(clasp); + return Ok((*domjsclass).dom_class); + } + if is_dom_proxy(obj) { + debug!("proxy dom object"); + let dom_class: *DOMClass = cast::transmute(GetProxyHandlerExtra(obj)); + return Ok(*dom_class); + } + debug!("not a dom object"); + return Err(()); +} + +pub fn unwrap_object(obj: *JSObject, proto_id: prototypes::id::Prototype, proto_depth: uint) -> Result { + unsafe { + do get_dom_class(obj).chain |dom_class| { + if dom_class.interface_chain[proto_depth] == proto_id { + debug!("good prototype"); + Ok(unwrap(obj)) + } else { + debug!("bad prototype"); + Err(()) + } + } + } +} + +pub fn unwrap_value(val: *JSVal, proto_id: prototypes::id::Prototype, proto_depth: uint) -> Result { + unsafe { + let obj = RUST_JSVAL_TO_OBJECT(*val); + unwrap_object(obj, proto_id, proto_depth) + } +} + pub unsafe fn squirrel_away(x: @mut T) -> *rust_box { let y: *rust_box = cast::transmute(x); cast::forget(x); @@ -373,7 +420,9 @@ pub fn GetProtoOrIfaceArray(global: *JSObject) -> **JSObject { pub mod prototypes { pub mod id { + #[deriving(Eq)] pub enum Prototype { + Blob, ClientRect, ClientRectList, DOMParser, diff --git a/src/support/spidermonkey/rust-mozjs b/src/support/spidermonkey/rust-mozjs index 26dc2e896a5..372622906d1 160000 --- a/src/support/spidermonkey/rust-mozjs +++ b/src/support/spidermonkey/rust-mozjs @@ -1 +1 @@ -Subproject commit 26dc2e896a57a28f03be43df46868e1a41a15807 +Subproject commit 372622906d112ae28825be1d5fcd8737cd03ae58 From f2ab39376abe3ef09a0d02191bd8383ebf8c37c4 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 10 Jul 2013 16:37:05 -0400 Subject: [PATCH 6/8] WebIDL codegen: Add support for dictionary inheritance. --- .../dom/bindings/codegen/CodegenRust.py | 84 +++++++++++-------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 8926674bd18..eab2855189b 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -2066,7 +2066,7 @@ class CGNativePropertyHooks(CGThing): parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::NativeHooks" if parent else '0 as *NativePropertyHooks') return """ -static NativeHooks: NativePropertyHooks = NativePropertyHooks { resolve_own_property: /*%s*/ 0 as *u8, resolve_property: ResolveProperty, enumerate_own_properties: /*%s*/ 0 as *u8, enumerate_properties: /*EnumerateProperties*/ 0 as *u8, proto_hooks: %s }; +static NativeHooks: NativePropertyHooks = NativePropertyHooks { resolve_own_property: /*%s*/ 0 as *u8, resolve_property: ResolveProperty, enumerate_own_properties: /*%s*/ 0 as *u8, enumerate_properties: /*EnumerateProperties*/ 0 as *u8, proto_hooks: /*%s*/ 0 as *NativePropertyHooks }; """ % (resolveOwnProperty, enumerateOwnProperties, parentHooks) # We'll want to insert the indent at the beginnings of lines, but we @@ -2722,11 +2722,11 @@ class CGGetPerInterfaceObject(CGAbstractMethod): A method for getting a per-interface object (a prototype object or interface constructor object). """ - def __init__(self, descriptor, name, idPrefix=""): + def __init__(self, descriptor, name, idPrefix="", pub=False): args = [Argument('*JSContext', 'aCx'), Argument('*JSObject', 'aGlobal'), Argument('*JSObject', 'aReceiver')] CGAbstractMethod.__init__(self, descriptor, name, - '*JSObject', args, inline=True) + '*JSObject', args, inline=True, pub=pub) self.id = idPrefix + "id::" + self.descriptor.name def definition_body(self): return """ @@ -2758,7 +2758,7 @@ class CGGetProtoObjectMethod(CGGetPerInterfaceObject): """ def __init__(self, descriptor): CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject", - "prototypes::") + "prototypes::", pub=True) def definition_body(self): return """ /* Get the interface prototype object for this class. This will create the @@ -3988,7 +3988,8 @@ class CGDictionary(CGThing): return "" d = self.dictionary if d.parent: - inheritance = ": public %s " % self.makeClassName(d.parent) #XXXjdm + inheritance = " parent: %s::%s,\n" % (self.makeModuleName(d.parent), + self.makeClassName(d.parent)) else: inheritance = "" memberDecls = [" %s: %s," % @@ -3996,7 +3997,8 @@ class CGDictionary(CGThing): for m in self.memberInfo] return (string.Template( - "pub struct ${selfName} {\n" + #XXXjdm deal with inheritance + "pub struct ${selfName} {\n" + + "${inheritance}" + "\n".join(memberDecls) + "\n" + # NOTE: jsids are per-runtime, so don't use them in workers "\n".join(" //static jsid " + @@ -4011,13 +4013,13 @@ class CGDictionary(CGThing): d = self.dictionary if d.parent: initParent = ("// Per spec, we init the parent's members first\n" - "if (!%s::Init(cx, val)) {\n" - " return false;\n" - "}\n" % self.makeClassName(d.parent)) + "if self.parent.Init(cx, val) == 0 {\n" + " return 0;\n" + "}\n") else: initParent = "" - memberInits = [CGIndenter(self.getMemberConversion(m)).define() + memberInits = [CGIndenter(self.getMemberConversion(m), indentLevel=6).define() for m in self.memberInfo] idinit = [CGGeneric('!InternJSString(cx, %s, "%s")' % (m.identifier.name + "_id", m.identifier.name)) @@ -4049,42 +4051,43 @@ class CGDictionary(CGThing): for m in d.members) + "\n" "\n" "impl ${selfName} {\n" - "fn new() -> ${selfName} {\n" - " ${selfName} {\n" + - "\n".join(" %s: %s," % (m[0].identifier.name, defaultValue(self.getMemberType(m))) for m in self.memberInfo) + "\n" + " pub fn new() -> ${selfName} {\n" + " ${selfName} {\n" + + ((" parent: %s::%s::new(),\n" % (self.makeModuleName(d.parent), + self.makeClassName(d.parent))) if d.parent else "") + + "\n".join(" %s: %s," % (m[0].identifier.name, defaultValue(self.getMemberType(m))) for m in self.memberInfo) + "\n" + " }\n" " }\n" - "}\n" "\n" - "fn InitIds(cx: *JSContext) -> bool {\n" - " //MOZ_ASSERT(!initedIds);\n" - "/*${idInit}\n" - " initedIds = true;*/ //XXXjdm\n" - " return true;\n" - "}\n" + " pub fn InitIds(&mut self, cx: *JSContext) -> bool {\n" + " //MOZ_ASSERT(!initedIds);\n" + " /*${idInit}\n" + " initedIds = true;*/ //XXXjdm\n" + " return true;\n" + " }\n" "\n" if not self.workers else "") + - "fn Init(&mut self, cx: *JSContext, val: JSVal) -> JSBool\n" - "{\n" - " unsafe {\n" + + " pub fn Init(&mut self, cx: *JSContext, val: JSVal) -> JSBool {\n" + " unsafe {\n" + # NOTE: jsids are per-runtime, so don't use them in workers - (" if (!initedIds && !${selfName}::InitIds(cx)) {\n" - " return 0;\n" - " }\n" if not self.workers else "") + + (" if (!initedIds && !self.InitIds(cx)) {\n" + " return 0;\n" + " }\n" if not self.workers else "") + "${initParent}" - " let mut found: JSBool = 0;\n" - " let temp: JSVal = JSVAL_NULL;\n" - " let isNull = RUST_JSVAL_IS_NULL(val) != 0 || RUST_JSVAL_IS_VOID(val) != 0;\n" - " if !isNull && RUST_JSVAL_IS_PRIMITIVE(val) != 0 {\n" - " return 0; //XXXjdm throw properly here\n" - " //return Throw<${isMainThread}>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n" - " }\n" + " let mut found: JSBool = 0;\n" + " let temp: JSVal = JSVAL_NULL;\n" + " let isNull = RUST_JSVAL_IS_NULL(val) != 0 || RUST_JSVAL_IS_VOID(val) != 0;\n" + " if !isNull && RUST_JSVAL_IS_PRIMITIVE(val) != 0 {\n" + " return 0; //XXXjdm throw properly here\n" + " //return Throw<${isMainThread}>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n" + " }\n" "\n" "${initMembers}\n" - " return 1;\n" + " return 1;\n" + " }\n" " }\n" - "}\n" "}").substitute({ "selfName": self.makeClassName(d), - "initParent": CGIndenter(CGGeneric(initParent)).define(), + "initParent": CGIndenter(CGGeneric(initParent), indentLevel=6).define(), "initMembers": "\n\n".join(memberInits), "idInit": CGIndenter(idinit).define(), "isMainThread": toStringBool(not self.workers) @@ -4098,6 +4101,15 @@ class CGDictionary(CGThing): def makeClassName(self, dictionary): return self.makeDictionaryName(dictionary, self.workers) + @staticmethod + def makeModuleName(dictionary): + name = dictionary.identifier.name + if name.endswith('Init'): + return toBindingNamespace(name.replace('Init', '')) + #XXXjdm This breaks on the test webidl files, sigh. + #raise TypeError("No idea how to find this dictionary's definition: " + name) + return "/* uh oh */ %s" % name + def getMemberType(self, memberInfo): (member, (templateBody, declType, holderType, dealWithOptional, initialValue)) = memberInfo From 8787c1ac1f80d1431823947e64ca9a6da20994b0 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 10 Jul 2013 16:40:02 -0400 Subject: [PATCH 7/8] WebIDL codegen: Remove unneeded C++ goop. --- .../dom/bindings/codegen/RegisterBindings.cpp | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 src/components/script/dom/bindings/codegen/RegisterBindings.cpp diff --git a/src/components/script/dom/bindings/codegen/RegisterBindings.cpp b/src/components/script/dom/bindings/codegen/RegisterBindings.cpp deleted file mode 100644 index e078a35bbdb..00000000000 --- a/src/components/script/dom/bindings/codegen/RegisterBindings.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "BlobBinding.h" -#include "ClientRectBinding.h" -#include "ClientRectListBinding.h" -#include "DOMParserBinding.h" -#include "EventBinding.h" -#include "EventTargetBinding.h" -#include "FormDataBinding.h" -#include "HTMLCollectionBinding.h" -#include "nsScriptNameSpaceManager.h" - -namespace mozilla { -namespace dom { -void -Register(nsScriptNameSpaceManager* aNameSpaceManager) -{ - -#define REGISTER_PROTO(_dom_class, _pref_check) \ - aNameSpaceManager->RegisterDefineDOMInterface(NS_LITERAL_STRING(#_dom_class), _dom_class##Binding::DefineDOMInterface, _pref_check); - -REGISTER_PROTO(Blob, nullptr); -REGISTER_PROTO(ClientRect, nullptr); -REGISTER_PROTO(ClientRectList, nullptr); -REGISTER_PROTO(DOMParser, nullptr); -REGISTER_PROTO(Event, nullptr); -REGISTER_PROTO(EventTarget, nullptr); -REGISTER_PROTO(FormData, nullptr); -REGISTER_PROTO(HTMLCollection, nullptr); - -#undef REGISTER_PROTO -} - -} // namespace dom -} // namespace mozilla - From 65f9aefb78791467fcd54971e0296c520e23d641 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 10 Jul 2013 16:41:08 -0400 Subject: [PATCH 8/8] Generate DOM bindings for UIEvent and MouseEvent. --- .../script/dom/bindings/codegen/Bindings.conf | 11 +- .../dom/bindings/codegen/CodegenRust.py | 35 ++-- .../dom/bindings/codegen/MouseEvent.webidl | 76 ++++++++ .../dom/bindings/codegen/UIEvent.webidl | 48 +++++ .../script/dom/bindings/conversions.rs | 28 +++ src/components/script/dom/bindings/utils.rs | 7 +- src/components/script/dom/mouseevent.rs | 184 ++++++++++++++++++ src/components/script/dom/node.rs | 9 + src/components/script/dom/uievent.rs | 158 +++++++++++++++ src/components/script/dom/windowproxy.rs | 39 ++++ src/components/script/script.rc | 7 +- src/test/html/test_bindings.js | 21 +- 12 files changed, 601 insertions(+), 22 deletions(-) create mode 100644 src/components/script/dom/bindings/codegen/MouseEvent.webidl create mode 100644 src/components/script/dom/bindings/codegen/UIEvent.webidl create mode 100644 src/components/script/dom/mouseevent.rs create mode 100644 src/components/script/dom/uievent.rs create mode 100644 src/components/script/dom/windowproxy.rs diff --git a/src/components/script/dom/bindings/codegen/Bindings.conf b/src/components/script/dom/bindings/codegen/Bindings.conf index 81751f6cf1f..11b4b245c71 100644 --- a/src/components/script/dom/bindings/codegen/Bindings.conf +++ b/src/components/script/dom/bindings/codegen/Bindings.conf @@ -256,6 +256,9 @@ DOMInterfaces = { 'workers': True, }], +'MouseEvent': { +}, + 'NodeList': [ { 'nativeType': 'nsINodeList', @@ -333,6 +336,9 @@ DOMInterfaces = { 'resultNotAddRefed': [ 'getItem' ] }], +'UIEvent': { +}, + 'WebGLRenderingContext': { 'nativeType': 'mozilla::WebGLContext', 'headerFile': 'WebGLContext.h', @@ -499,7 +505,7 @@ def addExternalIface(iface, nativeType=None, headerFile=None, pointerType=None): # If you add one of these, you need to make sure nsDOMQS.h has the relevant # macros added for it def addExternalHTMLElement(element): - nativeElement = 'ns' + element + nativeElement = element addExternalIface(element, nativeType=nativeElement, headerFile=nativeElement + '.h') @@ -520,7 +526,7 @@ addExternalIface('File') addExternalIface('HitRegionOptions', nativeType='nsISupports') addExternalIface('HTMLElement') addExternalIface('ImageData', nativeType='mozilla::dom::ImageData') -addExternalIface('Node', nativeType='nsINode') +addExternalIface('Node', nativeType='AbstractNode', pointerType='') addExternalIface('PaintRequest') addExternalIface('SVGLength') addExternalIface('SVGMatrix') @@ -552,4 +558,5 @@ addExternalIface('WebGLShaderPrecisionFormat', addExternalIface('WebGLTexture', nativeType='mozilla::WebGLTexture', headerFile='WebGLContext.h') addExternalIface('Window') +addExternalIface('WindowProxy', nativeType='WindowProxy') addExternalIface('XULElement') diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index eab2855189b..e82c4167485 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -558,7 +558,7 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None, CGIndenter(CGGeneric(templateBody)).define() + "\n") if type.nullable(): templateBody += ( - "} else if (${val}.isNullOrUndefined()) {\n" + "} else if RUST_JSVAL_IS_NULL(${val}) != 0 || RUST_JSVAL_IS_VOID(${val}) != 0 {\n" " %s;\n" % codeToSetNull) templateBody += ( "} else {\n" + @@ -893,7 +893,7 @@ for (uint32_t i = 0; i < length; ++i) { not descriptor.workers) or isMember typeName = descriptor.nativeType - typePtr = typeName + "*" + typePtr = descriptor.pointerType + typeName # Compute a few things: # - declType is the type we want to return as the first element of our @@ -904,15 +904,9 @@ for (uint32_t i = 0; i < length; ++i) { # Set up some sensible defaults for these things insofar as we can. holderType = None if argIsPointer: - if forceOwningType: - declType = "nsRefPtr<" + typeName + ">" - else: - declType = typePtr + declType = "Option<" + typePtr + ">" else: - if forceOwningType: - declType = "OwningNonNull<" + typeName + ">" - else: - declType = descriptor.pointerType + typeName + declType = typePtr templateBody = "" if descriptor.castable: @@ -1298,10 +1292,15 @@ def instantiateJSToNativeConversionTemplate(templateTuple, replacements, (holderType.define(), originalHolderName)) mutableHolderType = CGWrapper(holderType, pre="Optional< ", post=" >") holderType = CGWrapper(mutableHolderType, pre="const ") - result.append( - CGList([holderType, CGGeneric(" "), - CGGeneric(originalHolderName), - CGGeneric(";")])) + tmpresult = [CGGeneric("let "), + CGGeneric(originalHolderName), + CGGeneric(": "), + holderType] + if initialValue: + tmpresult += [CGGeneric(" = "), + initialValue] + tmpresult += [CGGeneric(";")] + result.append(CGList(tmpresult)) originalDeclName = replacements["declName"] if declType is not None: @@ -4034,11 +4033,11 @@ class CGDictionary(CGThing): def defaultValue(ty): if ty is "bool": return "false" - elif ty in ["i32", "u32"]: + elif ty in ["i32", "u32", "i16", "u16"]: return "0" elif ty is "nsString": return "\"\"" - elif ty.startswith("Optional"): + elif ty.startswith("Option"): return "None" else: return "/* uh oh: %s */" % ty @@ -4283,6 +4282,10 @@ class CGBindingRoot(CGThing): 'dom::event::*', #XXXjdm 'dom::eventtarget::*', #XXXjdm 'dom::formdata::*', #XXXjdm + 'dom::mouseevent::*', #XXXjdm + 'dom::uievent::*', #XXXjdm + 'dom::windowproxy::*', #XXXjdm + 'dom::bindings::codegen::*', #XXXjdm 'script_task::task_from_context', 'dom::bindings::utils::EnumEntry', 'dom::node::ScriptView', diff --git a/src/components/script/dom/bindings/codegen/MouseEvent.webidl b/src/components/script/dom/bindings/codegen/MouseEvent.webidl new file mode 100644 index 00000000000..68a273ebaaf --- /dev/null +++ b/src/components/script/dom/bindings/codegen/MouseEvent.webidl @@ -0,0 +1,76 @@ +/* -*- 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/. + * + * For more information on this interface please see + * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +interface MouseEvent : UIEvent { + readonly attribute long screenX; + readonly attribute long screenY; + readonly attribute long clientX; + readonly attribute long clientY; + readonly attribute boolean ctrlKey; + readonly attribute boolean shiftKey; + readonly attribute boolean altKey; + readonly attribute boolean metaKey; + readonly attribute unsigned short button; + readonly attribute unsigned short buttons; + readonly attribute EventTarget? relatedTarget; + // Deprecated in DOM Level 3: + [Throws] + void initMouseEvent(DOMString typeArg, + boolean canBubbleArg, + boolean cancelableArg, + WindowProxy? viewArg, + long detailArg, + long screenXArg, + long screenYArg, + long clientXArg, + long clientYArg, + boolean ctrlKeyArg, + boolean altKeyArg, + boolean shiftKeyArg, + boolean metaKeyArg, + unsigned short buttonArg, + EventTarget? relatedTargetArg); + // Introduced in DOM Level 3: + boolean getModifierState(DOMString keyArg); +}; + + +// Event Constructor Syntax: +[Constructor(DOMString typeArg, optional MouseEventInit mouseEventInitDict)] +partial interface MouseEvent +{ +}; + +// Suggested initMouseEvent replacement initializer: +dictionary MouseEventInit { + // Attributes from Event: + boolean bubbles = false; + boolean cancelable = false; + + // Attributes from UIEvent: + WindowProxy? view = null; + long detail = 0; + + // Attributes for MouseEvent: + long screenX = 0; + long screenY = 0; + long clientX = 0; + long clientY = 0; + boolean ctrlKey = false; + boolean shiftKey = false; + boolean altKey = false; + boolean metaKey = false; + unsigned short button = 0; + // Note: "buttons" was not previously initializable through initMouseEvent! + unsigned short buttons = 0; + EventTarget? relatedTarget = null; +}; diff --git a/src/components/script/dom/bindings/codegen/UIEvent.webidl b/src/components/script/dom/bindings/codegen/UIEvent.webidl new file mode 100644 index 00000000000..9672ea1090c --- /dev/null +++ b/src/components/script/dom/bindings/codegen/UIEvent.webidl @@ -0,0 +1,48 @@ +/* -*- 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/. + * + * For more information on this interface please see + * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html + * + * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +interface WindowProxy; +interface Node; + +[Constructor(DOMString type, optional UIEventInit eventInitDict)] +interface UIEvent : Event +{ + readonly attribute WindowProxy? view; + readonly attribute long detail; + void initUIEvent(DOMString aType, + boolean aCanBubble, + boolean aCancelable, + WindowProxy? aView, + long aDetail); +}; + +// Additional DOM0 properties. +partial interface UIEvent { + const long SCROLL_PAGE_UP = -32768; + const long SCROLL_PAGE_DOWN = 32768; + + readonly attribute long layerX; + readonly attribute long layerY; + readonly attribute long pageX; + readonly attribute long pageY; + readonly attribute unsigned long which; + readonly attribute Node? rangeParent; + readonly attribute long rangeOffset; + attribute boolean cancelBubble; + readonly attribute boolean isChar; +}; + +dictionary UIEventInit : EventInit +{ + WindowProxy? view = null; + long detail = 0; +}; diff --git a/src/components/script/dom/bindings/conversions.rs b/src/components/script/dom/bindings/conversions.rs index 48839401bcd..0092ea66bff 100644 --- a/src/components/script/dom/bindings/conversions.rs +++ b/src/components/script/dom/bindings/conversions.rs @@ -25,6 +25,34 @@ impl JSValConvertible for u32 { } } +impl JSValConvertible for i32 { + fn to_jsval(&self) -> JSVal { + unsafe { + RUST_UINT_TO_JSVAL(*self as u32) + } + } + + fn from_jsval(val: JSVal) -> Option { + unsafe { + Some(RUST_JSVAL_TO_INT(val) as i32) + } + } +} + +impl JSValConvertible for u16 { + fn to_jsval(&self) -> JSVal { + unsafe { + RUST_UINT_TO_JSVAL(*self as u32) + } + } + + fn from_jsval(val: JSVal) -> Option { + unsafe { + Some(RUST_JSVAL_TO_INT(val) as u16) + } + } +} + impl JSValConvertible for bool { fn to_jsval(&self) -> JSVal { if *self { diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs index 4a9d26ee36f..1d8ece126f3 100644 --- a/src/components/script/dom/bindings/utils.rs +++ b/src/components/script/dom/bindings/utils.rs @@ -400,7 +400,7 @@ pub struct ConstantSpec { pub struct DOMClass { // A list of interfaces that this object implements, in order of decreasing // derivedness. - interface_chain: [prototypes::id::Prototype, ..2 /*max prototype chain length*/], + interface_chain: [prototypes::id::Prototype, ..3 /*max prototype chain length*/], unused: bool, // DOMObjectIsISupports (always false) native_hooks: *NativePropertyHooks @@ -430,6 +430,9 @@ pub mod prototypes { Event, EventTarget, FormData, + UIEvent, + MouseEvent, + WindowProxy, _ID_Count } } @@ -632,7 +635,7 @@ pub extern fn ThrowingConstructor(_cx: *JSContext, _argc: uint, _vp: *JSVal) -> } pub fn initialize_global(global: *JSObject) { - let protoArray = @mut ([0 as *JSObject, ..7]); //XXXjdm prototypes::_ID_COUNT + let protoArray = @mut ([0 as *JSObject, ..10]); //XXXjdm prototypes::_ID_COUNT unsafe { //XXXjdm we should be storing the box pointer instead of the inner let box = squirrel_away(protoArray); diff --git a/src/components/script/dom/mouseevent.rs b/src/components/script/dom/mouseevent.rs new file mode 100644 index 00000000000..7ea73d27429 --- /dev/null +++ b/src/components/script/dom/mouseevent.rs @@ -0,0 +1,184 @@ +/* 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::codegen::MouseEventBinding; +use dom::bindings::utils::{ErrorResult, DOMString}; +use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper}; +use dom::eventtarget::EventTarget; +use dom::uievent::UIEvent; +use dom::window::Window; +use dom::windowproxy::WindowProxy; +use script_task::{global_script_context}; + +use js::glue::RUST_OBJECT_TO_JSVAL; +use js::jsapi::{JSObject, JSContext, JSVal}; + +pub struct MouseEvent { + parent: UIEvent, + screen_x: i32, + screen_y: i32, + client_x: i32, + client_y: i32, + ctrl_key: bool, + shift_key: bool, + alt_key: bool, + meta_key: bool, + button: u16, + related_target: Option<@mut EventTarget> +} + +impl MouseEvent { + pub fn new(type_: DOMString, can_bubble: bool, cancelable: bool, + view: Option<@mut WindowProxy>, detail: i32, screen_x: i32, + screen_y: i32, client_x: i32, client_y: i32, ctrl_key: bool, + shift_key: bool, alt_key: bool, meta_key: bool, button: u16, + _buttons: u16, related_target: Option<@mut EventTarget>) -> MouseEvent { + MouseEvent { + parent: UIEvent::new(type_, can_bubble, cancelable, view, detail), + screen_x: screen_x, + screen_y: screen_y, + client_x: client_x, + client_y: client_y, + ctrl_key: ctrl_key, + shift_key: shift_key, + alt_key: alt_key, + meta_key: meta_key, + button: button, + related_target: related_target + } + } + + pub fn init_wrapper(@mut self) { + let script_context = global_script_context(); + let cx = script_context.js_compartment.cx.ptr; + let owner = script_context.root_frame.get_ref().window; + let cache = owner.get_wrappercache(); + let scope = cache.get_wrapper(); + self.wrap_object_shared(cx, scope); + } + + pub fn Constructor(_owner: @mut Window, + type_: DOMString, + init: &MouseEventBinding::MouseEventInit, + _rv: &mut ErrorResult) -> @mut MouseEvent { + @mut MouseEvent::new(type_, init.bubbles, init.cancelable, init.view, init.detail, + init.screenX, init.screenY, init.clientX, init.clientY, + init.ctrlKey, init.shiftKey, init.altKey, init.metaKey, + init.button, init.buttons, init.relatedTarget) + } + + pub fn ScreenX(&self) -> i32 { + self.screen_x + } + + pub fn ScreenY(&self) -> i32 { + self.screen_y + } + + pub fn ClientX(&self) -> i32 { + self.client_x + } + + pub fn ClientY(&self) -> i32 { + self.client_y + } + + pub fn CtrlKey(&self) -> bool { + self.ctrl_key + } + + pub fn ShiftKey(&self) -> bool { + self.shift_key + } + + pub fn AltKey(&self) -> bool { + self.alt_key + } + + pub fn MetaKey(&self) -> bool { + self.meta_key + } + + pub fn Button(&self) -> u16 { + self.button + } + + pub fn Buttons(&self)-> u16 { + //TODO + 0 + } + + pub fn GetRelatedTarget(&self) -> Option<@mut EventTarget> { + self.related_target + } + + pub fn GetModifierState(&self, _keyArg: DOMString) -> bool { + //TODO + false + } + + pub fn InitMouseEvent(&mut self, + typeArg: DOMString, + canBubbleArg: bool, + cancelableArg: bool, + viewArg: Option<@mut WindowProxy>, + detailArg: i32, + screenXArg: i32, + screenYArg: i32, + clientXArg: i32, + clientYArg: i32, + ctrlKeyArg: bool, + altKeyArg: bool, + shiftKeyArg: bool, + metaKeyArg: bool, + buttonArg: u16, + relatedTargetArg: Option<@mut EventTarget>, + _rv: &mut ErrorResult) { + self.parent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg); + self.screen_x = screenXArg; + self.screen_y = screenYArg; + self.client_x = clientXArg; + self.client_y = clientYArg; + self.ctrl_key = ctrlKeyArg; + self.alt_key = altKeyArg; + self.shift_key = shiftKeyArg; + self.meta_key = metaKeyArg; + self.button = buttonArg; + self.related_target = relatedTargetArg; + } +} + +impl CacheableWrapper for MouseEvent { + fn get_wrappercache(&mut self) -> &mut WrapperCache { + return self.parent.get_wrappercache() + } + + fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { + let mut unused = false; + MouseEventBinding::Wrap(cx, scope, self, &mut unused) + } +} + +impl BindingObject for MouseEvent { + fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { + self.parent.GetParentObject(cx) + } +} + +impl DerivedWrapper for MouseEvent { + fn wrap(&mut self, _cx: *JSContext, _scope: *JSObject, _vp: *mut JSVal) -> i32 { + fail!(~"nyi") + } + + fn wrap_shared(@mut self, cx: *JSContext, scope: *JSObject, vp: *mut JSVal) -> i32 { + let obj = self.wrap_object_shared(cx, scope); + if obj.is_null() { + return 0; + } else { + unsafe { *vp = RUST_OBJECT_TO_JSVAL(obj) }; + return 1; + } + } + +} \ No newline at end of file diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index 87204414e4a..8ba9fc9a520 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -496,4 +496,13 @@ pub fn define_bindings(compartment: @mut Compartment) { assert!(codegen::EventTargetBinding::DefineDOMInterface(compartment.cx.ptr, compartment.global_obj.ptr, &mut unused)); + assert!(codegen::FormDataBinding::DefineDOMInterface(compartment.cx.ptr, + compartment.global_obj.ptr, + &mut unused)); + assert!(codegen::MouseEventBinding::DefineDOMInterface(compartment.cx.ptr, + compartment.global_obj.ptr, + &mut unused)); + assert!(codegen::UIEventBinding::DefineDOMInterface(compartment.cx.ptr, + compartment.global_obj.ptr, + &mut unused)); } diff --git a/src/components/script/dom/uievent.rs b/src/components/script/dom/uievent.rs new file mode 100644 index 00000000000..65bf468b0bb --- /dev/null +++ b/src/components/script/dom/uievent.rs @@ -0,0 +1,158 @@ +/* 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::codegen::UIEventBinding; +use dom::bindings::utils::{ErrorResult, DOMString}; +use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper}; +use dom::node::{AbstractNode, ScriptView}; +use dom::event::Event_; +use dom::window::Window; +use dom::windowproxy::WindowProxy; + +use script_task::global_script_context; + +use js::glue::RUST_OBJECT_TO_JSVAL; +use js::jsapi::{JSObject, JSContext, JSVal}; + +pub struct UIEvent { + parent: Event_, + can_bubble: bool, + cancelable: bool, + view: Option<@mut WindowProxy>, + detail: i32 +} + +impl UIEvent { + pub fn new(type_: DOMString, can_bubble: bool, cancelable: bool, + view: Option<@mut WindowProxy>, detail: i32) -> UIEvent { + UIEvent { + parent: Event_::new(type_), + can_bubble: can_bubble, + cancelable: cancelable, + view: view, + detail: detail + } + } + + pub fn init_wrapper(@mut self) { + let script_context = global_script_context(); + let cx = script_context.js_compartment.cx.ptr; + let owner = script_context.root_frame.get_ref().window; + let cache = owner.get_wrappercache(); + let scope = cache.get_wrapper(); + self.wrap_object_shared(cx, scope); + } + + pub fn Constructor(_owner: @mut Window, + type_: DOMString, + init: &UIEventBinding::UIEventInit, + _rv: &mut ErrorResult) -> @mut UIEvent { + @mut UIEvent::new(type_, init.parent.bubbles, init.parent.cancelable, + init.view, init.detail) + } + + pub fn GetView(&self) -> Option<@mut WindowProxy> { + self.view + } + + pub fn Detail(&self) -> i32 { + self.detail + } + + pub fn InitUIEvent(&mut self, + type_: DOMString, + can_bubble: bool, + cancelable: bool, + view: Option<@mut WindowProxy>, + detail: i32) { + let mut rv = Ok(()); + self.parent.InitEvent(type_, can_bubble, cancelable, &mut rv); + self.can_bubble = can_bubble; + self.cancelable = cancelable; + self.view = view; + self.detail = detail; + } + + pub fn LayerX(&self) -> i32 { + //TODO + 0 + } + + pub fn LayerY(&self) -> i32 { + //TODO + 0 + } + + pub fn PageX(&self) -> i32 { + //TODO + 0 + } + + pub fn PageY(&self) -> i32 { + //TODO + 0 + } + + pub fn Which(&self) -> u32 { + //TODO + 0 + } + + pub fn GetRangeParent(&self) -> Option> { + //TODO + None + } + + pub fn RangeOffset(&self) -> i32 { + //TODO + 0 + } + + pub fn CancelBubble(&self) -> bool { + //TODO + false + } + + pub fn SetCancelBubble(&mut self, _val: bool) { + //TODO + } + + pub fn IsChar(&self) -> bool { + //TODO + false + } +} + +impl CacheableWrapper for UIEvent { + fn get_wrappercache(&mut self) -> &mut WrapperCache { + return self.parent.get_wrappercache() + } + + fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { + let mut unused = false; + UIEventBinding::Wrap(cx, scope, self, &mut unused) + } +} + +impl BindingObject for UIEvent { + fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { + self.parent.GetParentObject(cx) + } +} + +impl DerivedWrapper for UIEvent { + fn wrap(&mut self, _cx: *JSContext, _scope: *JSObject, _vp: *mut JSVal) -> i32 { + fail!(~"nyi") + } + + fn wrap_shared(@mut self, cx: *JSContext, scope: *JSObject, vp: *mut JSVal) -> i32 { + let obj = self.wrap_object_shared(cx, scope); + if obj.is_null() { + return 0; + } else { + unsafe { *vp = RUST_OBJECT_TO_JSVAL(obj) }; + return 1; + } + } +} diff --git a/src/components/script/dom/windowproxy.rs b/src/components/script/dom/windowproxy.rs new file mode 100644 index 00000000000..08edb063afc --- /dev/null +++ b/src/components/script/dom/windowproxy.rs @@ -0,0 +1,39 @@ +/* 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::{CacheableWrapper, WrapperCache}; +use script_task::global_script_context; + +use js::jsapi::{JSContext, JSObject}; + +pub struct WindowProxy { + wrapper: WrapperCache +} + +impl WindowProxy { + pub fn new() -> @mut WindowProxy { + @mut WindowProxy { + wrapper: WrapperCache::new() + } + } + + pub fn init_wrapper(@mut self) { + let script_context = global_script_context(); + let cx = script_context.js_compartment.cx.ptr; + let owner = script_context.root_frame.get_ref().window; + let cache = owner.get_wrappercache(); + let scope = cache.get_wrapper(); + self.wrap_object_shared(cx, scope); + } +} + +impl CacheableWrapper for WindowProxy { + fn get_wrappercache(&mut self) -> &mut WrapperCache { + return self.get_wrappercache() + } + + fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { + fail!("not yet implemented") + } +} diff --git a/src/components/script/script.rc b/src/components/script/script.rc index a9f6a5c8a00..a4ca2c517e5 100644 --- a/src/components/script/script.rc +++ b/src/components/script/script.rc @@ -39,8 +39,10 @@ pub mod dom { pub mod DOMParserBinding; pub mod EventBinding; pub mod EventTargetBinding; - pub mod HTMLCollectionBinding; pub mod FormDataBinding; + pub mod HTMLCollectionBinding; + pub mod MouseEventBinding; + pub mod UIEventBinding; } } pub mod blob; @@ -54,8 +56,11 @@ pub mod dom { pub mod eventtarget; pub mod formdata; pub mod htmlcollection; + pub mod mouseevent; pub mod node; + pub mod uievent; pub mod window; + pub mod windowproxy; } pub mod html { diff --git a/src/test/html/test_bindings.js b/src/test/html/test_bindings.js index ceee47fe488..35bbfd4c772 100644 --- a/src/test/html/test_bindings.js +++ b/src/test/html/test_bindings.js @@ -56,4 +56,23 @@ let ev = new Event("foopy"); window.alert(ev.type); window.alert(ev.defaultPrevented); ev.preventDefault(); -window.alert(ev.defaultPrevented); \ No newline at end of file +window.alert(ev.defaultPrevented); + +window.alert("MouseEvent:"); +window.alert(MouseEvent); +let ev2 = new MouseEvent("press", {bubbles: true, screenX: 150, detail: 100}); +window.alert(ev2); +window.alert(ev2.screenX); +window.alert(ev2.detail); +window.alert(ev2.getModifierState("ctrl")); +window.alert(ev2 instanceof Event); +window.alert(ev2 instanceof UIEvent); + +//TODO: Doesn't work until we throw proper exceptions instead of returning 0 on +// unwrap failure. +/*try { + Object.getOwnPropertyDescriptor(Object.getPrototypeOf(rects), "length").get.call(window); + window.alert("hmm?"); +} catch (x) { + window.alert("ok"); +}*/