auto merge of #1171 : jdm/servo/events2, r=jdm,metajack

Now with a split between commits that just pull in unmodified Gecko code and those that modify it.
This commit is contained in:
bors-servo 2013-11-05 10:49:21 -08:00
commit c2a99933c9
27 changed files with 2593 additions and 171 deletions

View file

@ -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
}
}

View file

@ -182,28 +182,21 @@ DOMInterfaces = {
}, },
'Event': { 'Event': {
'nativeType': 'AbstractEvent',
'concreteType': 'Event',
'pointerType': '',
}, },
'EventListener': [ 'EventListener': {
{ 'nativeType': 'EventListenerBinding::EventListener',
}, },
{
'workers': True,
}],
'EventTarget': [ 'EventTarget': {
{ 'nativeType': 'AbstractEventTarget',
# 'nativeType': 'nsDOMEventTargetHelper', 'concreteType': 'EventTarget',
# 'hasInstanceInterface': 'nsIDOMEventTarget', 'pointerType': '',
# 'concrete': False, 'needsAbstract': ['dispatchEvent']
# 'prefable': True,
}, },
#{
# 'workers': True,
# 'headerFile': 'mozilla/dom/workers/bindings/EventTarget.h',
# 'concrete': False
#}
],
'FileList': [ 'FileList': [
{ {
@ -291,6 +284,9 @@ DOMInterfaces = {
}], }],
'MouseEvent': { 'MouseEvent': {
'nativeType': 'AbstractEvent',
'concreteType': 'MouseEvent',
'pointerType': '',
}, },
'Navigator': { 'Navigator': {
@ -388,6 +384,9 @@ DOMInterfaces = {
}], }],
'UIEvent': { 'UIEvent': {
'nativeType': 'AbstractEvent',
'concreteType': 'UIEvent',
'pointerType': '',
}, },
'ValidityState': { 'ValidityState': {

File diff suppressed because it is too large Load diff

View file

@ -42,6 +42,8 @@ class Configuration:
self.enums = [e for e in parseData if e.isEnum()] self.enums = [e for e in parseData if e.isEnum()]
self.dictionaries = [d for d in parseData if d.isDictionary()] self.dictionaries = [d for d in parseData if d.isDictionary()]
self.callbacks = [c for c in parseData if
c.isCallback() and not c.isInterface()]
# Keep the descriptor list sorted for determinism. # Keep the descriptor list sorted for determinism.
self.descriptors.sort(lambda x,y: cmp(x.name, y.name)) self.descriptors.sort(lambda x,y: cmp(x.name, y.name))
@ -66,14 +68,34 @@ class Configuration:
getter = lambda x: x.interface.isCallback() getter = lambda x: x.interface.isCallback()
elif key == 'isExternal': elif key == 'isExternal':
getter = lambda x: x.interface.isExternal() getter = lambda x: x.interface.isExternal()
elif key == 'isJSImplemented':
getter = lambda x: x.interface.isJSImplemented()
else: else:
getter = lambda x: getattr(x, key) getter = lambda x: getattr(x, key)
curr = filter(lambda x: getter(x) == val, curr) curr = filter(lambda x: getter(x) == val, curr)
return curr return curr
def getEnums(self, webIDLFile): def getEnums(self, webIDLFile):
return filter(lambda e: e.filename() == webIDLFile, self.enums) return filter(lambda e: e.filename() == webIDLFile, self.enums)
def getDictionaries(self, webIDLFile):
return filter(lambda d: d.filename() == webIDLFile, self.dictionaries) @staticmethod
def _filterForFileAndWorkers(items, filters):
"""Gets the items that match the given filters."""
for key, val in filters.iteritems():
if key == 'webIDLFile':
items = filter(lambda x: x.filename() == val, items)
elif key == 'workers':
if val:
items = filter(lambda x: x.getUserData("workers", False), items)
else:
items = filter(lambda x: x.getUserData("mainThread", False), items)
else:
assert(0) # Unknown key
return items
def getDictionaries(self, **filters):
return self._filterForFileAndWorkers(self.dictionaries, filters)
def getCallbacks(self, **filters):
return self._filterForFileAndWorkers(self.callbacks, filters)
def getDescriptor(self, interfaceName, workers): def getDescriptor(self, interfaceName, workers):
""" """
Gets the appropriate descriptor for the given interface name Gets the appropriate descriptor for the given interface name

View file

@ -58,8 +58,8 @@ interface Document : Node {
[Throws] [Throws]
Node adoptNode(Node node);*/ Node adoptNode(Node node);*/
// [Creator, Throws] [Creator, Throws]
// Event createEvent(DOMString interface_); Event createEvent(DOMString interface_);
/*[Creator, Throws] /*[Creator, Throws]
Range createRange();*/ Range createRange();*/

View file

@ -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);
};

View file

@ -11,4 +11,12 @@
*/ */
interface EventTarget { 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);
}; };

View file

@ -14,7 +14,7 @@
interface URI; interface URI;
interface UserDataHandler;*/ interface UserDataHandler;*/
interface Node /*: EventTarget*/ { interface Node : EventTarget {
const unsigned short ELEMENT_NODE = 1; const unsigned short ELEMENT_NODE = 1;
const unsigned short ATTRIBUTE_NODE = 2; // historical const unsigned short ATTRIBUTE_NODE = 2; // historical
const unsigned short TEXT_NODE = 3; const unsigned short TEXT_NODE = 3;

View file

@ -8,7 +8,7 @@
*/ */
[NamedPropertiesObject] [NamedPropertiesObject]
/*sealed*/ interface Window /*: EventTarget*/ { /*sealed*/ interface Window : EventTarget {
// the current browsing context // the current browsing context
/*[Unforgeable] readonly attribute WindowProxy window; /*[Unforgeable] readonly attribute WindowProxy window;
[Replaceable] readonly attribute WindowProxy self;*/ [Replaceable] readonly attribute WindowProxy self;*/

View file

@ -146,6 +146,23 @@ class IDLObject(object):
def isCallback(self): def isCallback(self):
return False return False
def isSingleOperationInterface(self):
assert self.isCallback() or self.isJSImplemented()
return (
# JS-implemented things should never need the
# this-handling weirdness of single-operation interfaces.
not self.isJSImplemented() and
# Not inheriting from another interface
not self.parent and
# No consequential interfaces
len(self.getConsequentialInterfaces()) == 0 and
# No attributes of any kinds
not any(m.isAttr() for m in self.members) and
# There is at least one regular operation, and all regular
# operations have the same identifier
len(set(m.identifier.name for m in self.members if
m.isMethod() and not m.isStatic())) == 1)
def isType(self): def isType(self):
return False return False
@ -167,6 +184,38 @@ class IDLObject(object):
def handleExtendedAttribute(self, attr): def handleExtendedAttribute(self, attr):
assert False # Override me! assert False # Override me!
def _getDependentObjects(self):
assert False # Override me!
def getDeps(self, visited=None):
""" Return a set of files that this object depends on. If any of
these files are changed the parser needs to be rerun to regenerate
a new IDLObject.
The visited argument is a set of all the objects already visited.
We must test to see if we are in it, and if so, do nothing. This
prevents infinite recursion."""
# NB: We can't use visited=set() above because the default value is
# evaluated when the def statement is evaluated, not when the function
# is executed, so there would be one set for all invocations.
if visited == None:
visited = set()
if self in visited:
return set()
visited.add(self)
deps = set()
if self.filename() != "<builtin>":
deps.add(self.filename())
for d in self._getDependentObjects():
deps = deps.union(d.getDeps(visited))
return deps
class IDLScope(IDLObject): class IDLScope(IDLObject):
def __init__(self, location, parentScope, identifier): def __init__(self, location, parentScope, identifier):
IDLObject.__init__(self, location) IDLObject.__init__(self, location)
@ -428,6 +477,15 @@ class IDLExternalInterface(IDLObjectWithIdentifier):
def resolve(self, parentScope): def resolve(self, parentScope):
pass pass
def getJSImplementation(self):
return None
def isJSImplemented(self):
return False
def _getDependentObjects(self):
return set()
class IDLInterface(IDLObjectWithScope): class IDLInterface(IDLObjectWithScope):
def __init__(self, location, parentScope, name, parent, members, def __init__(self, location, parentScope, name, parent, members,
isPartial): isPartial):
@ -777,6 +835,24 @@ class IDLInterface(IDLObjectWithScope):
# Put the new members at the beginning # Put the new members at the beginning
self.members = members + self.members self.members = members + self.members
def getJSImplementation(self):
classId = self.getExtendedAttribute("JSImplementation")
if not classId:
return classId
assert isinstance(classId, list)
assert len(classId) == 1
return classId[0]
def isJSImplemented(self):
return bool(self.getJSImplementation())
def _getDependentObjects(self):
deps = set(self.members)
deps.union(self.implementedInterfaces)
if self.parent:
deps.add(self.parent)
return deps
class IDLDictionary(IDLObjectWithScope): class IDLDictionary(IDLObjectWithScope):
def __init__(self, location, parentScope, name, parent, members): def __init__(self, location, parentScope, name, parent, members):
assert isinstance(parentScope, IDLScope) assert isinstance(parentScope, IDLScope)
@ -847,6 +923,11 @@ class IDLDictionary(IDLObjectWithScope):
def addExtendedAttributes(self, attrs): def addExtendedAttributes(self, attrs):
assert len(attrs) == 0 assert len(attrs) == 0
def _getDependentObjects(self):
deps = set(self.members)
if (self.parent):
deps.add(self.parent)
return deps
class IDLEnum(IDLObjectWithIdentifier): class IDLEnum(IDLObjectWithIdentifier):
def __init__(self, location, parentScope, name, values): def __init__(self, location, parentScope, name, values):
@ -875,6 +956,9 @@ class IDLEnum(IDLObjectWithIdentifier):
def addExtendedAttributes(self, attrs): def addExtendedAttributes(self, attrs):
assert len(attrs) == 0 assert len(attrs) == 0
def _getDependentObjects(self):
return set()
class IDLType(IDLObject): class IDLType(IDLObject):
Tags = enum( Tags = enum(
# The integer types # The integer types
@ -893,6 +977,7 @@ class IDLType(IDLObject):
# Other types # Other types
'any', 'any',
'domstring', 'domstring',
'bytestring',
'object', 'object',
'date', 'date',
'void', 'void',
@ -930,6 +1015,12 @@ class IDLType(IDLObject):
def isString(self): def isString(self):
return False return False
def isByteString(self):
return False
def isDOMString(self):
return False
def isVoid(self): def isVoid(self):
return self.name == "Void" return self.name == "Void"
@ -1075,6 +1166,12 @@ class IDLNullableType(IDLType):
def isString(self): def isString(self):
return self.inner.isString() return self.inner.isString()
def isByteString(self):
return self.inner.isByteString()
def isDOMString(self):
return self.inner.isDOMString()
def isFloat(self): def isFloat(self):
return self.inner.isFloat() return self.inner.isFloat()
@ -1163,6 +1260,9 @@ class IDLNullableType(IDLType):
return False return False
return self.inner.isDistinguishableFrom(other) return self.inner.isDistinguishableFrom(other)
def _getDependentObjects(self):
return self.inner._getDependentObjects()
class IDLSequenceType(IDLType): class IDLSequenceType(IDLType):
def __init__(self, location, parameterType): def __init__(self, location, parameterType):
assert not parameterType.isVoid() assert not parameterType.isVoid()
@ -1231,6 +1331,9 @@ class IDLSequenceType(IDLType):
other.isDictionary() or other.isDate() or other.isDictionary() or other.isDate() or
other.isNonCallbackInterface()) other.isNonCallbackInterface())
def _getDependentObjects(self):
return self.inner._getDependentObjects()
class IDLUnionType(IDLType): class IDLUnionType(IDLType):
def __init__(self, location, memberTypes): def __init__(self, location, memberTypes):
IDLType.__init__(self, location, "") IDLType.__init__(self, location, "")
@ -1317,6 +1420,9 @@ class IDLUnionType(IDLType):
return False return False
return True return True
def _getDependentObjects(self):
return set(self.memberTypes)
class IDLArrayType(IDLType): class IDLArrayType(IDLType):
def __init__(self, location, parameterType): def __init__(self, location, parameterType):
assert not parameterType.isVoid() assert not parameterType.isVoid()
@ -1393,6 +1499,9 @@ class IDLArrayType(IDLType):
other.isDictionary() or other.isDate() or other.isDictionary() or other.isDate() or
other.isNonCallbackInterface()) other.isNonCallbackInterface())
def _getDependentObjects(self):
return self.inner._getDependentObjects()
class IDLTypedefType(IDLType, IDLObjectWithIdentifier): class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
def __init__(self, location, innerType, name): def __init__(self, location, innerType, name):
IDLType.__init__(self, location, innerType.name) IDLType.__init__(self, location, innerType.name)
@ -1478,6 +1587,9 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
def isDistinguishableFrom(self, other): def isDistinguishableFrom(self, other):
return self.inner.isDistinguishableFrom(other) return self.inner.isDistinguishableFrom(other)
def _getDependentObjects(self):
return self.inner._getDependentObjects()
class IDLWrapperType(IDLType): class IDLWrapperType(IDLType):
def __init__(self, location, inner): def __init__(self, location, inner):
IDLType.__init__(self, location, inner.identifier.name) IDLType.__init__(self, location, inner.identifier.name)
@ -1583,6 +1695,23 @@ class IDLWrapperType(IDLType):
assert other.isObject() assert other.isObject()
return False return False
def _getDependentObjects(self):
# NB: The codegen for an interface type depends on
# a) That the identifier is in fact an interface (as opposed to
# a dictionary or something else).
# b) The native type of the interface.
# If we depend on the interface object we will also depend on
# anything the interface depends on which is undesirable. We
# considered implementing a dependency just on the interface type
# file, but then every modification to an interface would cause this
# to be regenerated which is still undesirable. We decided not to
# depend on anything, reasoning that:
# 1) Changing the concrete type of the interface requires modifying
# Bindings.conf, which is still a global dependency.
# 2) Changing an interface to a dictionary (or vice versa) with the
# same identifier should be incredibly rare.
return set()
class IDLBuiltinType(IDLType): class IDLBuiltinType(IDLType):
Types = enum( Types = enum(
@ -1602,6 +1731,7 @@ class IDLBuiltinType(IDLType):
# Other types # Other types
'any', 'any',
'domstring', 'domstring',
'bytestring',
'object', 'object',
'date', 'date',
'void', 'void',
@ -1633,6 +1763,7 @@ class IDLBuiltinType(IDLType):
Types.double: IDLType.Tags.double, Types.double: IDLType.Tags.double,
Types.any: IDLType.Tags.any, Types.any: IDLType.Tags.any,
Types.domstring: IDLType.Tags.domstring, Types.domstring: IDLType.Tags.domstring,
Types.bytestring: IDLType.Tags.bytestring,
Types.object: IDLType.Tags.object, Types.object: IDLType.Tags.object,
Types.date: IDLType.Tags.date, Types.date: IDLType.Tags.date,
Types.void: IDLType.Tags.void, Types.void: IDLType.Tags.void,
@ -1658,6 +1789,13 @@ class IDLBuiltinType(IDLType):
return self._typeTag <= IDLBuiltinType.Types.double return self._typeTag <= IDLBuiltinType.Types.double
def isString(self): def isString(self):
return self._typeTag == IDLBuiltinType.Types.domstring or \
self._typeTag == IDLBuiltinType.Types.bytestring
def isByteString(self):
return self._typeTag == IDLBuiltinType.Types.bytestring
def isDOMString(self):
return self._typeTag == IDLBuiltinType.Types.domstring return self._typeTag == IDLBuiltinType.Types.domstring
def isInteger(self): def isInteger(self):
@ -1733,6 +1871,9 @@ class IDLBuiltinType(IDLType):
(self.isTypedArray() and not other.isArrayBufferView() and not (self.isTypedArray() and not other.isArrayBufferView() and not
(other.isTypedArray() and other.name == self.name))))) (other.isTypedArray() and other.name == self.name)))))
def _getDependentObjects(self):
return set()
BuiltinTypes = { BuiltinTypes = {
IDLBuiltinType.Types.byte: IDLBuiltinType.Types.byte:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte", IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte",
@ -1877,6 +2018,9 @@ class IDLValue(IDLObject):
raise WebIDLError("Cannot coerce type %s to type %s." % raise WebIDLError("Cannot coerce type %s to type %s." %
(self.type, type), [location]) (self.type, type), [location])
def _getDependentObjects(self):
return set()
class IDLNullValue(IDLObject): class IDLNullValue(IDLObject):
def __init__(self, location): def __init__(self, location):
IDLObject.__init__(self, location) IDLObject.__init__(self, location)
@ -1895,6 +2039,9 @@ class IDLNullValue(IDLObject):
nullValue.type = type nullValue.type = type
return nullValue return nullValue
def _getDependentObjects(self):
return set()
class IDLInterfaceMember(IDLObjectWithIdentifier): class IDLInterfaceMember(IDLObjectWithIdentifier):
@ -1966,6 +2113,9 @@ class IDLConst(IDLInterfaceMember):
def validate(self): def validate(self):
pass pass
def _getDependentObjects(self):
return set([self.type, self.value])
class IDLAttribute(IDLInterfaceMember): class IDLAttribute(IDLInterfaceMember):
def __init__(self, location, identifier, type, readonly, inherit, def __init__(self, location, identifier, type, readonly, inherit,
static=False): static=False):
@ -2052,6 +2202,9 @@ class IDLAttribute(IDLInterfaceMember):
def hasLenientThis(self): def hasLenientThis(self):
return self.lenientThis return self.lenientThis
def _getDependentObjects(self):
return set([self.type])
class IDLArgument(IDLObjectWithIdentifier): class IDLArgument(IDLObjectWithIdentifier):
def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False): def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False):
IDLObjectWithIdentifier.__init__(self, location, None, identifier) IDLObjectWithIdentifier.__init__(self, location, None, identifier)
@ -2124,6 +2277,12 @@ class IDLArgument(IDLObjectWithIdentifier):
self.location) self.location)
assert self.defaultValue assert self.defaultValue
def _getDependentObjects(self):
deps = set([self.type])
if self.defaultValue:
deps.add(self.defaultValue)
return deps
class IDLCallbackType(IDLType, IDLObjectWithScope): class IDLCallbackType(IDLType, IDLObjectWithScope):
def __init__(self, location, parentScope, identifier, returnType, arguments): def __init__(self, location, parentScope, identifier, returnType, arguments):
assert isinstance(returnType, IDLType) assert isinstance(returnType, IDLType)
@ -2179,6 +2338,9 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
return (other.isPrimitive() or other.isString() or other.isEnum() or return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isNonCallbackInterface() or other.isDate()) other.isNonCallbackInterface() or other.isDate())
def _getDependentObjects(self):
return set([self._returnType] + self._arguments)
class IDLMethodOverload: class IDLMethodOverload:
""" """
A class that represents a single overload of a WebIDL method. This is not A class that represents a single overload of a WebIDL method. This is not
@ -2194,6 +2356,11 @@ class IDLMethodOverload:
self.arguments = list(arguments) self.arguments = list(arguments)
self.location = location self.location = location
def _getDependentObjects(self):
deps = set(self.arguments)
deps.add(self.returnType)
return deps
class IDLMethod(IDLInterfaceMember, IDLScope): class IDLMethod(IDLInterfaceMember, IDLScope):
Special = enum( Special = enum(
@ -2494,6 +2661,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
[attr.location, self.location]) [attr.location, self.location])
IDLInterfaceMember.handleExtendedAttribute(self, attr) IDLInterfaceMember.handleExtendedAttribute(self, attr)
def _getDependentObjects(self):
deps = set()
for overload in self._overloads:
deps.union(overload._getDependentObjects())
return deps
class IDLImplementsStatement(IDLObject): class IDLImplementsStatement(IDLObject):
def __init__(self, location, implementor, implementee): def __init__(self, location, implementor, implementee):
IDLObject.__init__(self, location) IDLObject.__init__(self, location)

View file

@ -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.parent_node, "parent");
trace_node(tracer, self.first_child, "first child"); trace_node(tracer, self.first_child, "first child");
trace_node(tracer, self.last_child, "last child"); trace_node(tracer, self.last_child, "last child");

View file

@ -4,8 +4,8 @@
use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList;
use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH; use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH;
use dom::window;
use dom::node::{AbstractNode, ScriptView}; use dom::node::{AbstractNode, ScriptView};
use dom::window;
use std::libc::c_uint; use std::libc::c_uint;
use std::cast; 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_AlreadyHasOwnProperty, JS_NewObject, JS_NewFunction, JS_GetGlobalObject};
use js::jsapi::{JS_DefineProperties, JS_WrapValue, JS_ForwardGetPropertyTo}; use js::jsapi::{JS_DefineProperties, JS_WrapValue, JS_ForwardGetPropertyTo};
use js::jsapi::{JS_GetClass, JS_LinkConstructorAndPrototype, JS_GetStringCharsAndLength}; 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_GetFunctionPrototype, JS_InternString, JS_GetFunctionObject};
use js::jsapi::{JS_HasPropertyById, JS_GetPrototype, JS_GetGlobalForObject}; use js::jsapi::{JS_HasPropertyById, JS_GetPrototype, JS_GetGlobalForObject};
use js::jsapi::{JS_NewUCStringCopyN, JS_DefineFunctions, JS_DefineProperty}; 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::{JSFunctionSpec, JSPropertySpec, JSVal, JSPropertyDescriptor};
use js::jsapi::{JSPropertyOp, JSStrictPropertyOp, JS_NewGlobalObject, JS_InitStandardClasses}; use js::jsapi::{JSPropertyOp, JSStrictPropertyOp, JS_NewGlobalObject, JS_InitStandardClasses};
use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType; 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_PERMANENT, JSID_VOID, JSPROP_NATIVE_ACCESSORS, JSPROP_GETTER};
use js::{JSPROP_SETTER, JSVAL_VOID, JSVAL_TRUE, JSVAL_FALSE}; use js::{JSPROP_SETTER, JSVAL_VOID, JSVAL_TRUE, JSVAL_FALSE};
use js::{JS_THIS_OBJECT, JSFUN_CONSTRUCTOR, JS_CALLEE, JSPROP_READONLY}; use js::{JS_THIS_OBJECT, JSFUN_CONSTRUCTOR, JS_CALLEE, JSPROP_READONLY};
@ -767,6 +768,8 @@ pub enum Error {
NotFound, NotFound,
HierarchyRequest, HierarchyRequest,
InvalidCharacter, InvalidCharacter,
NotSupported,
InvalidState
} }
pub type Fallible<T> = Result<T, Error>; pub type Fallible<T> = Result<T, Error>;
@ -819,6 +822,13 @@ pub fn HasPropertyOnPrototype(cx: *JSContext, proxy: *JSObject, id: jsid) -> boo
return !GetPropertyOnPrototype(cx, proxy, id, &mut found, ptr::null()) || found; return !GetPropertyOnPrototype(cx, proxy, id, &mut found, ptr::null()) || found;
} }
#[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] #[fixed_stack_segment]
pub fn CreateDOMGlobal(cx: *JSContext, class: *JSClass) -> *JSObject { pub fn CreateDOMGlobal(cx: *JSContext, class: *JSClass) -> *JSObject {
unsafe { unsafe {
@ -832,6 +842,30 @@ pub fn CreateDOMGlobal(cx: *JSContext, class: *JSClass) -> *JSObject {
} }
} }
#[fixed_stack_segment]
fn cx_for_dom_reflector(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_reflector(obj.reflector().get_jsobject())
}
/// Check if an element name is valid. See http://www.w3.org/TR/xml/#NT-Name /// Check if an element name is valid. See http://www.w3.org/TR/xml/#NT-Name
/// for details. /// for details.
pub fn is_valid_element_name(name: &str) -> bool { pub fn is_valid_element_name(name: &str) -> bool {

View file

@ -5,15 +5,20 @@
use dom::comment::Comment; use dom::comment::Comment;
use dom::bindings::codegen::DocumentBinding; use dom::bindings::codegen::DocumentBinding;
use dom::bindings::utils::{DOMString, ErrorResult, Fallible}; use dom::bindings::utils::{DOMString, ErrorResult, Fallible};
use dom::bindings::utils::{Reflectable, Reflector, DerivedWrapper}; use dom::bindings::utils::{Reflectable, Reflector, DerivedWrapper, NotSupported};
use dom::bindings::utils::{is_valid_element_name, InvalidCharacter, Traceable, null_str_as_empty, null_str_as_word_null}; use dom::bindings::utils::{is_valid_element_name, InvalidCharacter, Traceable};
use dom::bindings::utils::{null_str_as_empty_ref, null_str_as_empty, null_str_as_word_null};
use dom::documentfragment::DocumentFragment; use dom::documentfragment::DocumentFragment;
use dom::element::{Element}; use dom::element::{Element};
use dom::element::{HTMLHeadElementTypeId, HTMLTitleElementTypeId}; use dom::element::{HTMLHeadElementTypeId, HTMLTitleElementTypeId};
use dom::event::{AbstractEvent, Event, HTMLEventTypeId, UIEventTypeId};
use dom::htmlcollection::HTMLCollection; use dom::htmlcollection::HTMLCollection;
use dom::htmldocument::HTMLDocument; use dom::htmldocument::HTMLDocument;
use dom::htmlelement::HTMLElement;
use dom::mouseevent::MouseEvent;
use dom::node::{AbstractNode, ScriptView, Node, ElementNodeTypeId, DocumentNodeTypeId}; use dom::node::{AbstractNode, ScriptView, Node, ElementNodeTypeId, DocumentNodeTypeId};
use dom::text::Text; use dom::text::Text;
use dom::uievent::UIEvent;
use dom::window::Window; use dom::window::Window;
use dom::htmltitleelement::HTMLTitleElement; use dom::htmltitleelement::HTMLTitleElement;
use html::hubbub_html_parser::build_element_from_tag; use html::hubbub_html_parser::build_element_from_tag;
@ -255,6 +260,15 @@ impl Document {
Comment::new(null_str_as_word_null(data), abstract_self) Comment::new(null_str_as_word_null(data), abstract_self)
} }
pub fn CreateEvent(&self, interface: &DOMString) -> Fallible<AbstractEvent> {
match null_str_as_empty_ref(interface) {
"UIEvents" => Ok(UIEvent::new(self.window, UIEventTypeId)),
"MouseEvents" => Ok(MouseEvent::new(self.window)),
"HTMLEvents" => Ok(Event::new(self.window, HTMLEventTypeId)),
_ => Err(NotSupported)
}
}
pub fn Title(&self, _: AbstractDocument) -> DOMString { pub fn Title(&self, _: AbstractDocument) -> DOMString {
let mut title = ~""; let mut title = ~"";
match self.doctype { match self.doctype {

View file

@ -2,17 +2,23 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::eventtarget::EventTarget; use dom::eventtarget::AbstractEventTarget;
use dom::window::Window; use dom::window::Window;
use dom::bindings::codegen::EventBinding; use dom::bindings::codegen::EventBinding;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object, DerivedWrapper};
use dom::bindings::utils::{DOMString, ErrorResult, Fallible}; use dom::bindings::utils::{DOMString, ErrorResult, Fallible, null_str_as_word_null};
use dom::mouseevent::MouseEvent;
use dom::uievent::UIEvent;
use geom::point::Point2D; use geom::point::Point2D;
use js::jsapi::{JSObject, JSContext}; use js::jsapi::{JSObject, JSContext, JSVal};
use js::glue::RUST_OBJECT_TO_JSVAL;
use script_task::page_from_context; use script_task::page_from_context;
use std::cast;
use std::unstable::raw::Box;
pub enum Event_ { pub enum Event_ {
ResizeEvent(uint, uint), ResizeEvent(uint, uint),
ReflowEvent, ReflowEvent,
@ -21,45 +27,194 @@ pub enum Event_ {
MouseUpEvent(uint, Point2D<f32>), MouseUpEvent(uint, Point2D<f32>),
} }
pub struct Event { pub struct AbstractEvent {
reflector_: Reflector, event: *mut Box<Event>
type_: DOMString,
default_prevented: bool,
cancelable: bool,
bubbles: bool,
trusted: bool,
} }
impl Event { pub enum EventPhase {
pub fn new_inherited(type_: &DOMString) -> Event { Phase_None = 0,
Event { Phase_Capturing,
reflector_: Reflector::new(), Phase_At_Target,
type_: (*type_).clone(), Phase_Bubbling
default_prevented: false, }
cancelable: true,
bubbles: true, impl AbstractEvent {
trusted: false pub fn from_box(box: *mut Box<Event>) -> AbstractEvent {
AbstractEvent {
event: box
} }
} }
pub fn new(window: @mut Window, type_: &DOMString) -> @mut Event { //
reflect_dom_object(@mut Event::new_inherited(type_), window, EventBinding::Wrap) // Downcasting borrows
//
fn transmute<'a, T>(&'a self) -> &'a T {
unsafe {
let box: *Box<T> = self.event as *Box<T>;
&(*box).data
}
}
fn transmute_mut<'a, T>(&'a self) -> &'a mut T {
unsafe {
let box: *mut Box<T> = self.event as *mut Box<T>;
&mut (*box).data
}
}
pub fn type_id(&self) -> EventTypeId {
self.event().type_id
}
pub fn event<'a>(&'a self) -> &'a Event {
self.transmute()
}
pub fn mut_event<'a>(&'a self) -> &'a mut Event {
self.transmute_mut()
}
pub fn is_uievent(&self) -> bool {
self.type_id() == UIEventTypeId
}
pub fn uievent<'a>(&'a self) -> &'a UIEvent {
assert!(self.is_uievent());
self.transmute()
}
pub fn mut_uievent<'a>(&'a self) -> &'a mut UIEvent {
assert!(self.is_uievent());
self.transmute_mut()
}
pub fn is_mouseevent(&self) -> bool {
self.type_id() == MouseEventTypeId
}
pub fn mouseevent<'a>(&'a self) -> &'a MouseEvent {
assert!(self.is_mouseevent());
self.transmute()
}
pub fn mut_mouseevent<'a>(&'a self) -> &'a mut MouseEvent {
assert!(self.is_mouseevent());
self.transmute_mut()
}
pub fn propagation_stopped(&self) -> bool {
self.event().stop_propagation
}
pub fn bubbles(&self) -> bool {
self.event().bubbles
}
}
impl DerivedWrapper for AbstractEvent {
#[fixed_stack_segment]
fn wrap(&mut self, _cx: *JSContext, _scope: *JSObject, vp: *mut JSVal) -> i32 {
let wrapper = self.reflector().get_jsobject();
if wrapper.is_not_null() {
unsafe { *vp = RUST_OBJECT_TO_JSVAL(wrapper) };
return 1;
}
unreachable!()
}
}
impl Reflectable for AbstractEvent {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.event().reflector()
}
fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
self.mut_event().mut_reflector()
}
fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject {
fail!(~"doesn't make any sense");
}
fn GetParentObject(&self, cx: *JSContext) -> Option<@mut Reflectable> {
self.event().GetParentObject(cx)
}
}
#[deriving(Eq)]
pub enum EventTypeId {
HTMLEventTypeId,
UIEventTypeId,
MouseEventTypeId,
KeyEventTypeId
}
pub struct Event {
type_id: EventTypeId,
reflector_: Reflector,
current_target: Option<AbstractEventTarget>,
target: Option<AbstractEventTarget>,
type_: ~str,
phase: EventPhase,
default_prevented: bool,
stop_propagation: bool,
stop_immediate: bool,
cancelable: bool,
bubbles: bool,
trusted: bool,
dispatching: bool,
initialized: bool
}
impl Event {
pub fn new_inherited(type_id: EventTypeId) -> Event {
Event {
type_id: type_id,
reflector_: Reflector::new(),
current_target: None,
target: None,
phase: Phase_None,
type_: ~"",
default_prevented: false,
cancelable: true,
bubbles: true,
trusted: false,
dispatching: false,
stop_propagation: false,
stop_immediate: false,
initialized: false,
}
}
//FIXME: E should be bounded by some trait that is only implemented for Event types
pub fn as_abstract<E>(event: @mut E) -> AbstractEvent {
// This surrenders memory management of the event!
AbstractEvent {
event: unsafe { cast::transmute(event) },
}
}
pub fn new(window: @mut Window, type_id: EventTypeId) -> AbstractEvent {
let ev = reflect_dom_object(@mut Event::new_inherited(type_id), window,
EventBinding::Wrap);
Event::as_abstract(ev)
} }
pub fn EventPhase(&self) -> u16 { pub fn EventPhase(&self) -> u16 {
0 self.phase as u16
} }
pub fn Type(&self) -> DOMString { pub fn Type(&self) -> DOMString {
self.type_.clone() Some(self.type_.clone())
} }
pub fn GetTarget(&self) -> Option<@mut EventTarget> { pub fn GetTarget(&self) -> Option<AbstractEventTarget> {
None self.target
} }
pub fn GetCurrentTarget(&self) -> Option<@mut EventTarget> { pub fn GetCurrentTarget(&self) -> Option<AbstractEventTarget> {
None self.current_target
} }
pub fn DefaultPrevented(&self) -> bool { pub fn DefaultPrevented(&self) -> bool {
@ -67,13 +222,18 @@ impl Event {
} }
pub fn PreventDefault(&mut self) { pub fn PreventDefault(&mut self) {
self.default_prevented = true if self.cancelable {
self.default_prevented = true
}
} }
pub fn StopPropagation(&mut self) { pub fn StopPropagation(&mut self) {
self.stop_propagation = true;
} }
pub fn StopImmediatePropagation(&mut self) { pub fn StopImmediatePropagation(&mut self) {
self.stop_immediate = true;
self.stop_propagation = true;
} }
pub fn Bubbles(&self) -> bool { pub fn Bubbles(&self) -> bool {
@ -92,9 +252,10 @@ impl Event {
type_: &DOMString, type_: &DOMString,
bubbles: bool, bubbles: bool,
cancelable: bool) -> ErrorResult { cancelable: bool) -> ErrorResult {
self.type_ = (*type_).clone(); self.type_ = null_str_as_word_null(type_);
self.cancelable = cancelable; self.cancelable = cancelable;
self.bubbles = bubbles; self.bubbles = bubbles;
self.initialized = true;
Ok(()) Ok(())
} }
@ -103,9 +264,11 @@ impl Event {
} }
pub fn Constructor(global: @mut Window, pub fn Constructor(global: @mut Window,
type_: &DOMString, type_: &DOMString,
_init: &EventBinding::EventInit) -> Fallible<@mut Event> { init: &EventBinding::EventInit) -> Fallible<AbstractEvent> {
Ok(Event::new(global, type_)) let ev = Event::new(global, HTMLEventTypeId);
ev.mut_event().InitEvent(type_, init.bubbles, init.cancelable);
Ok(ev)
} }
} }

View file

@ -0,0 +1,111 @@
/* 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::callback::eReportExceptions;
use dom::eventtarget::{AbstractEventTarget, Capturing, Bubbling};
use dom::event::{AbstractEvent, Phase_At_Target, Phase_None, Phase_Bubbling, Phase_Capturing};
use dom::node::AbstractNode;
use servo_util::tree::{TreeNodeRef};
// See http://dom.spec.whatwg.org/#concept-event-dispatch for the full dispatch algorithm
pub fn dispatch_event(target: AbstractEventTarget, event: AbstractEvent) -> bool {
assert!(!event.event().dispatching);
{
let event = event.mut_event();
event.target = Some(target);
event.dispatching = true;
}
let type_ = event.event().type_.clone();
let mut chain = ~[];
//TODO: no chain if not participating in a tree
if target.is_node() {
for ancestor in AbstractNode::from_eventtarget(target).ancestors() {
chain.push(AbstractEventTarget::from_node(ancestor));
}
}
event.mut_event().phase = Phase_Capturing;
//FIXME: The "callback this value" should be currentTarget
/* capturing */
for &cur_target in chain.rev_iter() {
//XXX bad clone
let stopped = match cur_target.eventtarget().get_listeners_for(type_.clone(), Capturing) {
Some(listeners) => {
event.mut_event().current_target = Some(cur_target);
for listener in listeners.iter() {
listener.HandleEvent__(event, eReportExceptions);
if event.event().stop_immediate {
break;
}
}
event.propagation_stopped()
}
None => false
};
if stopped {
break;
}
}
/* at target */
if !event.propagation_stopped() {
{
let event = event.mut_event();
event.phase = Phase_At_Target;
event.current_target = Some(target);
}
let opt_listeners = target.eventtarget().get_listeners(type_.clone());
for listeners in opt_listeners.iter() {
for listener in listeners.iter() {
listener.HandleEvent__(event, eReportExceptions);
if event.event().stop_immediate {
break;
}
}
}
}
/* bubbling */
if event.bubbles() && !event.propagation_stopped() {
event.mut_event().phase = Phase_Bubbling;
for &cur_target in chain.iter() {
//XXX bad clone
let stopped = match cur_target.eventtarget().get_listeners_for(type_.clone(), Bubbling) {
Some(listeners) => {
event.mut_event().current_target = Some(cur_target);
for listener in listeners.iter() {
listener.HandleEvent__(event, eReportExceptions);
if event.event().stop_immediate {
break;
}
}
event.propagation_stopped()
}
None => false
};
if stopped {
break;
}
}
}
let event = event.mut_event();
event.dispatching = false;
event.phase = Phase_None;
event.current_target = None;
!event.DefaultPrevented()
}

View file

@ -3,24 +3,196 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::EventTargetBinding; use dom::bindings::codegen::EventTargetBinding;
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector, DOMString, Fallible, DerivedWrapper};
use dom::bindings::utils::{null_str_as_word_null, InvalidState};
use dom::bindings::codegen::EventListenerBinding::EventListener;
use dom::event::AbstractEvent;
use dom::eventdispatcher::dispatch_event;
use dom::node::{AbstractNode, ScriptView};
use script_task::page_from_context; use script_task::page_from_context;
use js::jsapi::{JSObject, JSContext}; use js::jsapi::{JSObject, JSContext, JSVal};
use js::glue::RUST_OBJECT_TO_JSVAL;
pub struct EventTarget { use std::cast;
reflector_: Reflector use std::hashmap::HashMap;
use std::unstable::raw::Box;
#[deriving(Eq)]
pub enum ListenerPhase {
Capturing,
Bubbling,
} }
impl EventTarget { #[deriving(Eq)]
pub fn new() -> ~EventTarget { pub enum EventTargetTypeId {
~EventTarget { WindowTypeId,
reflector_: Reflector::new() NodeTypeId
}
#[deriving(Eq)]
struct EventListenerEntry {
phase: ListenerPhase,
listener: EventListener
}
pub struct EventTarget {
type_id: EventTargetTypeId,
reflector_: Reflector,
handlers: HashMap<~str, ~[EventListenerEntry]>,
}
pub struct AbstractEventTarget {
eventtarget: *mut Box<EventTarget>
}
impl AbstractEventTarget {
pub fn from_box<T>(box: *mut Box<T>) -> AbstractEventTarget {
AbstractEventTarget {
eventtarget: box as *mut Box<EventTarget>
} }
} }
pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) { pub fn from_node(node: AbstractNode<ScriptView>) -> AbstractEventTarget {
self.wrap_object_shared(cx, scope); unsafe {
cast::transmute(node)
}
}
pub fn type_id(&self) -> EventTargetTypeId {
self.eventtarget().type_id
}
pub fn is_window(&self) -> bool {
self.type_id() == WindowTypeId
}
pub fn is_node(&self) -> bool {
self.type_id() == NodeTypeId
}
//
// Downcasting borrows
//
fn transmute<'a, T>(&'a self) -> &'a T {
unsafe {
let box: *Box<T> = self.eventtarget as *Box<T>;
&(*box).data
}
}
fn transmute_mut<'a, T>(&'a mut self) -> &'a mut T {
unsafe {
let box: *mut Box<T> = self.eventtarget as *mut Box<T>;
&mut (*box).data
}
}
pub fn eventtarget<'a>(&'a self) -> &'a EventTarget {
self.transmute()
}
pub fn mut_eventtarget<'a>(&'a mut self) -> &'a mut EventTarget {
self.transmute_mut()
}
}
impl DerivedWrapper for AbstractEventTarget {
#[fixed_stack_segment]
fn wrap(&mut self, _cx: *JSContext, _scope: *JSObject, vp: *mut JSVal) -> i32 {
let wrapper = self.reflector().get_jsobject();
if wrapper.is_not_null() {
unsafe { *vp = RUST_OBJECT_TO_JSVAL(wrapper) };
return 1;
}
unreachable!()
}
}
impl Reflectable for AbstractEventTarget {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.eventtarget().reflector()
}
fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
self.mut_eventtarget().mut_reflector()
}
fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject {
unreachable!()
}
fn GetParentObject(&self, cx: *JSContext) -> Option<@mut Reflectable> {
self.eventtarget().GetParentObject(cx)
}
}
impl EventTarget {
pub fn new_inherited(type_id: EventTargetTypeId) -> EventTarget {
EventTarget {
type_id: type_id,
reflector_: Reflector::new(),
handlers: HashMap::new(),
}
}
pub fn get_listeners(&self, type_: ~str) -> Option<~[EventListener]> {
do self.handlers.find_equiv(&type_).map |listeners| {
listeners.iter().map(|entry| entry.listener).collect()
}
}
pub fn get_listeners_for(&self, type_: ~str, desired_phase: ListenerPhase)
-> Option<~[EventListener]> {
do self.handlers.find_equiv(&type_).map |listeners| {
let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
filtered.map(|entry| entry.listener).collect()
}
}
pub fn AddEventListener(&mut self,
ty: &DOMString,
listener: Option<EventListener>,
capture: bool) {
for &listener in listener.iter() {
let entry = self.handlers.find_or_insert_with(null_str_as_word_null(ty), |_| ~[]);
let phase = if capture { Capturing } else { Bubbling };
let new_entry = EventListenerEntry {
phase: phase,
listener: listener
};
if entry.position_elem(&new_entry).is_none() {
entry.push(new_entry);
}
}
}
pub fn RemoveEventListener(&mut self,
ty: &DOMString,
listener: Option<EventListener>,
capture: bool) {
for &listener in listener.iter() {
let mut entry = self.handlers.find_mut(&null_str_as_word_null(ty));
for entry in entry.mut_iter() {
let phase = if capture { Capturing } else { Bubbling };
let old_entry = EventListenerEntry {
phase: phase,
listener: listener
};
let position = entry.position_elem(&old_entry);
for &position in position.iter() {
entry.remove(position);
}
}
}
}
pub fn DispatchEvent(&self, abstract_self: AbstractEventTarget, event: AbstractEvent) -> Fallible<bool> {
if event.event().dispatching || !event.event().initialized {
return Err(InvalidState);
}
Ok(dispatch_event(abstract_self, event))
} }
} }

View file

@ -5,7 +5,8 @@
use dom::bindings::codegen::MouseEventBinding; use dom::bindings::codegen::MouseEventBinding;
use dom::bindings::utils::{ErrorResult, Fallible, DOMString}; use dom::bindings::utils::{ErrorResult, Fallible, DOMString};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::eventtarget::EventTarget; use dom::event::{AbstractEvent, Event, MouseEventTypeId};
use dom::eventtarget::AbstractEventTarget;
use dom::uievent::UIEvent; use dom::uievent::UIEvent;
use dom::window::Window; use dom::window::Window;
use dom::windowproxy::WindowProxy; use dom::windowproxy::WindowProxy;
@ -23,38 +24,42 @@ pub struct MouseEvent {
alt_key: bool, alt_key: bool,
meta_key: bool, meta_key: bool,
button: u16, button: u16,
related_target: Option<@mut EventTarget> related_target: Option<AbstractEventTarget>
} }
impl MouseEvent { impl MouseEvent {
pub fn new(window: @mut Window, type_: &DOMString, can_bubble: bool, cancelable: bool, pub fn new_inherited() -> MouseEvent {
view: Option<@mut WindowProxy>, detail: i32, screen_x: i32, MouseEvent {
screen_y: i32, client_x: i32, client_y: i32, ctrl_key: bool, parent: UIEvent::new_inherited(MouseEventTypeId),
shift_key: bool, alt_key: bool, meta_key: bool, button: u16, screen_x: 0,
_buttons: u16, related_target: Option<@mut EventTarget>) -> @mut MouseEvent { screen_y: 0,
let ev = @mut MouseEvent { client_x: 0,
parent: UIEvent::new_inherited(type_, can_bubble, cancelable, view, detail), client_y: 0,
screen_x: screen_x, ctrl_key: false,
screen_y: screen_y, shift_key: false,
client_x: client_x, alt_key: false,
client_y: client_y, meta_key: false,
ctrl_key: ctrl_key, button: 0,
shift_key: shift_key, related_target: None
alt_key: alt_key, }
meta_key: meta_key, }
button: button,
related_target: related_target pub fn new(window: @mut Window) -> AbstractEvent {
}; Event::as_abstract(reflect_dom_object(@mut MouseEvent::new_inherited(),
reflect_dom_object(ev, window, MouseEventBinding::Wrap) window,
MouseEventBinding::Wrap))
} }
pub fn Constructor(owner: @mut Window, pub fn Constructor(owner: @mut Window,
type_: &DOMString, type_: &DOMString,
init: &MouseEventBinding::MouseEventInit) -> Fallible<@mut MouseEvent> { init: &MouseEventBinding::MouseEventInit) -> Fallible<AbstractEvent> {
Ok(MouseEvent::new(owner, type_, init.bubbles, init.cancelable, init.view, init.detail, let ev = MouseEvent::new(owner);
init.screenX, init.screenY, init.clientX, init.clientY, ev.mut_mouseevent().InitMouseEvent(type_, init.bubbles, init.cancelable, init.view,
init.ctrlKey, init.shiftKey, init.altKey, init.metaKey, init.detail, init.screenX, init.screenY,
init.button, init.buttons, init.relatedTarget)) init.clientX, init.clientY, init.ctrlKey,
init.altKey, init.shiftKey, init.metaKey,
init.button, init.relatedTarget);
Ok(ev)
} }
pub fn ScreenX(&self) -> i32 { pub fn ScreenX(&self) -> i32 {
@ -98,7 +103,7 @@ impl MouseEvent {
0 0
} }
pub fn GetRelatedTarget(&self) -> Option<@mut EventTarget> { pub fn GetRelatedTarget(&self) -> Option<AbstractEventTarget> {
self.related_target self.related_target
} }
@ -122,7 +127,7 @@ impl MouseEvent {
shiftKeyArg: bool, shiftKeyArg: bool,
metaKeyArg: bool, metaKeyArg: bool,
buttonArg: u16, buttonArg: u16,
relatedTargetArg: Option<@mut EventTarget>) -> ErrorResult { relatedTargetArg: Option<AbstractEventTarget>) -> ErrorResult {
self.parent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg); self.parent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg);
self.screen_x = screenXArg; self.screen_x = screenXArg;
self.screen_y = screenYArg; self.screen_y = screenYArg;

View file

@ -12,6 +12,7 @@ use dom::document::{AbstractDocument, DocumentTypeId};
use dom::documenttype::DocumentType; use dom::documenttype::DocumentType;
use dom::element::{Element, ElementTypeId, HTMLImageElementTypeId, HTMLIframeElementTypeId}; use dom::element::{Element, ElementTypeId, HTMLImageElementTypeId, HTMLIframeElementTypeId};
use dom::element::{HTMLStyleElementTypeId}; use dom::element::{HTMLStyleElementTypeId};
use dom::eventtarget::{AbstractEventTarget, EventTarget, NodeTypeId};
use dom::nodelist::{NodeList}; use dom::nodelist::{NodeList};
use dom::htmlimageelement::HTMLImageElement; use dom::htmlimageelement::HTMLImageElement;
use dom::htmliframeelement::HTMLIFrameElement; use dom::htmliframeelement::HTMLIFrameElement;
@ -63,7 +64,7 @@ pub struct AbstractNodeChildrenIterator<View> {
/// `LayoutData`. /// `LayoutData`.
pub struct Node<View> { pub struct Node<View> {
/// The JavaScript reflector for this node. /// The JavaScript reflector for this node.
reflector_: Reflector, eventtarget: EventTarget,
/// The type of node that this is. /// The type of node that this is.
type_id: NodeTypeId, type_id: NodeTypeId,
@ -210,6 +211,13 @@ impl<'self, View> AbstractNode<View> {
} }
} }
pub fn from_eventtarget(target: AbstractEventTarget) -> AbstractNode<View> {
assert!(target.is_node());
unsafe {
cast::transmute(target)
}
}
// Convenience accessors // Convenience accessors
/// Returns the type ID of this node. Fails if this node is borrowed mutably. /// Returns the type ID of this node. Fails if this node is borrowed mutably.
@ -521,7 +529,7 @@ impl Node<ScriptView> {
fn new_(type_id: NodeTypeId, doc: Option<AbstractDocument>) -> Node<ScriptView> { fn new_(type_id: NodeTypeId, doc: Option<AbstractDocument>) -> Node<ScriptView> {
Node { Node {
reflector_: Reflector::new(), eventtarget: EventTarget::new_inherited(NodeTypeId),
type_id: type_id, type_id: type_id,
abstract: None, abstract: None,
@ -1042,11 +1050,11 @@ impl Node<ScriptView> {
impl Reflectable for Node<ScriptView> { impl Reflectable for Node<ScriptView> {
fn reflector<'a>(&'a self) -> &'a Reflector { fn reflector<'a>(&'a self) -> &'a Reflector {
&self.reflector_ self.eventtarget.reflector()
} }
fn mut_reflector<'a>(&'a mut self) -> &'a mut 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 { fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject {

View file

@ -6,7 +6,7 @@ use dom::bindings::codegen::UIEventBinding;
use dom::bindings::utils::{DOMString, Fallible}; use dom::bindings::utils::{DOMString, Fallible};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::node::{AbstractNode, ScriptView}; use dom::node::{AbstractNode, ScriptView};
use dom::event::Event; use dom::event::{AbstractEvent, Event, EventTypeId, UIEventTypeId};
use dom::window::Window; use dom::window::Window;
use dom::windowproxy::WindowProxy; use dom::windowproxy::WindowProxy;
@ -14,36 +14,33 @@ use js::jsapi::{JSObject, JSContext};
pub struct UIEvent { pub struct UIEvent {
parent: Event, parent: Event,
can_bubble: bool,
cancelable: bool,
view: Option<@mut WindowProxy>, view: Option<@mut WindowProxy>,
detail: i32 detail: i32
} }
impl UIEvent { impl UIEvent {
pub fn new_inherited(type_: &DOMString, can_bubble: bool, cancelable: bool, pub fn new_inherited(type_id: EventTypeId) -> UIEvent {
view: Option<@mut WindowProxy>, detail: i32) -> UIEvent {
UIEvent { UIEvent {
parent: Event::new_inherited(type_), parent: Event::new_inherited(type_id),
can_bubble: can_bubble, view: None,
cancelable: cancelable, detail: 0
view: view,
detail: detail
} }
} }
pub fn new(window: @mut Window, type_: &DOMString, can_bubble: bool, cancelable: bool, pub fn new(window: @mut Window, type_id: EventTypeId) -> AbstractEvent {
view: Option<@mut WindowProxy>, detail: i32) -> @mut UIEvent { let ev = reflect_dom_object(@mut UIEvent::new_inherited(type_id),
reflect_dom_object(@mut UIEvent::new_inherited(type_, can_bubble, cancelable, view, detail), window,
window, UIEventBinding::Wrap);
UIEventBinding::Wrap) Event::as_abstract(ev)
} }
pub fn Constructor(owner: @mut Window, pub fn Constructor(owner: @mut Window,
type_: &DOMString, type_: &DOMString,
init: &UIEventBinding::UIEventInit) -> Fallible<@mut UIEvent> { init: &UIEventBinding::UIEventInit) -> Fallible<AbstractEvent> {
Ok(UIEvent::new(owner, type_, init.parent.bubbles, init.parent.cancelable, let ev = UIEvent::new(owner, UIEventTypeId);
init.view, init.detail)) ev.mut_uievent().InitUIEvent(type_, init.parent.bubbles, init.parent.cancelable,
init.view, init.detail);
Ok(ev)
} }
pub fn GetView(&self) -> Option<@mut WindowProxy> { pub fn GetView(&self) -> Option<@mut WindowProxy> {
@ -61,8 +58,6 @@ impl UIEvent {
view: Option<@mut WindowProxy>, view: Option<@mut WindowProxy>,
detail: i32) { detail: i32) {
self.parent.InitEvent(type_, can_bubble, cancelable); self.parent.InitEvent(type_, can_bubble, cancelable);
self.can_bubble = can_bubble;
self.cancelable = cancelable;
self.view = view; self.view = view;
self.detail = detail; self.detail = detail;
} }

View file

@ -6,6 +6,7 @@ use dom::bindings::codegen::WindowBinding;
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::bindings::utils::{DOMString, null_str_as_empty, Traceable}; use dom::bindings::utils::{DOMString, null_str_as_empty, Traceable};
use dom::document::AbstractDocument; use dom::document::AbstractDocument;
use dom::eventtarget::{EventTarget, WindowTypeId};
use dom::node::{AbstractNode, ScriptView}; use dom::node::{AbstractNode, ScriptView};
use dom::navigator::Navigator; use dom::navigator::Navigator;
@ -37,10 +38,10 @@ pub enum TimerControlMsg {
} }
pub struct Window { pub struct Window {
eventtarget: EventTarget,
page: @mut Page, page: @mut Page,
script_chan: ScriptChan, script_chan: ScriptChan,
compositor: @ScriptListener, compositor: @ScriptListener,
reflector_: Reflector,
timer_chan: SharedChan<TimerControlMsg>, timer_chan: SharedChan<TimerControlMsg>,
navigator: Option<@mut Navigator>, navigator: Option<@mut Navigator>,
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
@ -140,11 +141,11 @@ impl Window {
impl Reflectable for Window { impl Reflectable for Window {
fn reflector<'a>(&'a self) -> &'a Reflector { fn reflector<'a>(&'a self) -> &'a Reflector {
&self.reflector_ self.eventtarget.reflector()
} }
fn mut_reflector<'a>(&'a mut self) -> &'a mut 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 { fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject {
@ -204,10 +205,10 @@ impl Window {
image_cache_task: ImageCacheTask) image_cache_task: ImageCacheTask)
-> @mut Window { -> @mut Window {
let win = @mut Window { let win = @mut Window {
eventtarget: EventTarget::new_inherited(WindowTypeId),
page: page, page: page,
script_chan: script_chan.clone(), script_chan: script_chan.clone(),
compositor: compositor, compositor: compositor,
reflector_: Reflector::new(),
timer_chan: { timer_chan: {
let (timer_port, timer_chan) = comm::stream::<TimerControlMsg>(); let (timer_port, timer_chan) = comm::stream::<TimerControlMsg>();
let id = page.id.clone(); let id = page.id.clone();

View file

@ -28,6 +28,7 @@ pub mod dom {
pub mod element; pub mod element;
pub mod node; pub mod node;
pub mod utils; pub mod utils;
pub mod callback;
pub mod conversions; pub mod conversions;
pub mod proxyhandler; pub mod proxyhandler;
pub mod codegen { pub mod codegen {
@ -54,6 +55,7 @@ pub mod dom {
pub mod domparser; pub mod domparser;
pub mod element; pub mod element;
pub mod event; pub mod event;
pub mod eventdispatcher;
pub mod eventtarget; pub mod eventtarget;
pub mod formdata; pub mod formdata;
pub mod htmlanchorelement; pub mod htmlanchorelement;

@ -1 +1 @@
Subproject commit 1df26877d82d9722785de91ff59ab069a5acc180 Subproject commit 566c2af971abaa5e8c51b59fa400a7e07835b257

View file

@ -4,7 +4,7 @@
<script> <script>
is_function(Event, "Event"); is_function(Event, "Event");
let ev = new Event("foopy"); let ev = new Event("foopy", {cancelable: true});
is_a(ev, Event); is_a(ev, Event);
is(ev.type, 'foopy'); is(ev.type, 'foopy');

View file

@ -0,0 +1,51 @@
<html>
<head>
<script src="harness.js"></script>
</head>
<body>
<span>Paragraph containing <div>event listener</div>.</span>
<script>
var bodyTimes = 0;
function bodyListener(ev) {
bodyTimes++;
is(ev.currentTarget, document.getElementsByTagName('body')[0]);
is(ev.target, document.getElementsByTagName('div')[0]);
if (bodyTimes == 1) {
is(ev.eventPhase, ev.CAPTURING_PHASE);
} else if (bodyTimes == 2) {
is(ev.eventPhase, ev.BUBBLING_PHASE);
}
}
var spanTimes = 0;
function spanListener(ev) {
is(ev.currentTarget, document.getElementsByTagName('span')[0]);
is(ev.target, document.getElementsByTagName('div')[0]);
is(ev.eventPhase, ev.BUBBLING_PHASE);
spanTimes++;
}
var divTimes = 0;
function divListener(ev) {
var self = document.getElementsByTagName('div')[0];
is(ev.currentTarget, self);
is(ev.target, self);
is(ev.eventPhase, ev.AT_TARGET);
divTimes++;
}
document.getElementsByTagName('body')[0].addEventListener("foopy", bodyListener, true);
document.getElementsByTagName('body')[0].addEventListener("foopy", bodyListener, false);
document.getElementsByTagName('span')[0].addEventListener("foopy", spanListener, false);
document.getElementsByTagName('div')[0].addEventListener("foopy", divListener, false);
var ev = new Event('foopy', {bubbles: true});
is(ev.bubbles, true);
document.getElementsByTagName('div')[0].dispatchEvent(ev);
is(bodyTimes, 2, 'body listener should be called multiple times');
is(divTimes, 1, 'target listener should be called once');
is(spanTimes, 1, 'span listener should be called while bubbling');
finish();
</script>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<script src="harness.js"></script>
<b><b><b></b></b></b>
<script>
var sawmiddle = -1;
var sawouter = -1;
var step = 0;
var outerb = document.getElementsByTagName('b')[0];
var middleb = outerb.firstChild;
var innerb = middleb.firstChild;
outerb.addEventListener("x", function() {
middleb.addEventListener("x", function() {
sawmiddle = step++;
}, true);
sawouter = step++;
}, true);
innerb.dispatchEvent(new Event("x"));
is(sawmiddle, 1);
is(sawouter, 0);
finish();
</script>

View file

@ -0,0 +1,42 @@
<html>
<head>
<script src="harness.js"></script>
</head>
<body>
<div id="foo"></div>
<script>
var sawBubble = false;
var sawCapture = false;
var sawBubbleTwice = false;
function handler(ev) {
is(ev.eventPhase, ev.AT_TARGET);
is(sawBubble, false);
is(sawCapture, false);
sawBubble = true;
}
function handler2(ev) {
is(ev.eventPhase, ev.AT_TARGET);
is(sawBubble, true);
is(sawCapture, false);
sawCapture = true;
}
function handler3(ev) {
is(ev.eventPhase, ev.AT_TARGET);
is(sawBubble, true);
is(sawCapture, true);
sawBubbleTwice = true;
}
var target = document.getElementById('foo');
target.addEventListener('foopy', handler, false);
target.addEventListener('foopy', handler2, true);
target.addEventListener('foopy', handler3, false);
var ev = new Event('foopy', {bubbles: true});
target.dispatchEvent(ev);
is(sawBubble, true);
is(sawCapture, true);
is(sawBubbleTwice, true);
finish();
</script>
</body>
</html>

View file

@ -0,0 +1,39 @@
<html>
<head>
<script src="harness.js"></script>
</head>
<body>
<script>
function onFoopy(ev) {
window.removeEventListener('foopy', onFoopy);
is(ev instanceof expected, true);
is(ev.type, 'foopy');
}
var expected;
var events = [['HTMLEvents', Event, function(ev) { ev.initEvent('foopy', true, true); }],
['UIEvents', UIEvent, function(ev) { ev.initUIEvent('foopy', true, true, null, 0); }],
['MouseEvents', MouseEvent,
function(ev) { ev.initMouseEvent('foopy', true, true, null, 0,
0, 0, 0, 0, false, false,
false, false, 0, null); }]];
for (var i = 0; i < events.length; i++) {
addEventListener('foopy', onFoopy);
expected = events[i][1];
var ev = document.createEvent(events[i][0]);
events[i][2](ev);
window.dispatchEvent(ev);
}
var constructors = [Event, UIEvent, MouseEvent];
for (var i = 0; i < constructors.length; i++) {
addEventListener('foopy', onFoopy);
expected = constructors[i];
var ev = new constructors[i]('foopy', {cancelable: true, bubbles: true});
window.dispatchEvent(ev);
}
finish();
</script>
</body>
</html>