mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
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:
commit
c2a99933c9
27 changed files with 2593 additions and 171 deletions
105
src/components/script/dom/bindings/callback.rs
Normal file
105
src/components/script/dom/bindings/callback.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -182,28 +182,21 @@ DOMInterfaces = {
|
|||
},
|
||||
|
||||
'Event': {
|
||||
'nativeType': 'AbstractEvent',
|
||||
'concreteType': 'Event',
|
||||
'pointerType': '',
|
||||
},
|
||||
|
||||
'EventListener': [
|
||||
{
|
||||
'EventListener': {
|
||||
'nativeType': 'EventListenerBinding::EventListener',
|
||||
},
|
||||
{
|
||||
'workers': True,
|
||||
}],
|
||||
|
||||
'EventTarget': [
|
||||
{
|
||||
# 'nativeType': 'nsDOMEventTargetHelper',
|
||||
# 'hasInstanceInterface': 'nsIDOMEventTarget',
|
||||
# 'concrete': False,
|
||||
# 'prefable': True,
|
||||
'EventTarget': {
|
||||
'nativeType': 'AbstractEventTarget',
|
||||
'concreteType': 'EventTarget',
|
||||
'pointerType': '',
|
||||
'needsAbstract': ['dispatchEvent']
|
||||
},
|
||||
#{
|
||||
# 'workers': True,
|
||||
# 'headerFile': 'mozilla/dom/workers/bindings/EventTarget.h',
|
||||
# 'concrete': False
|
||||
#}
|
||||
],
|
||||
|
||||
'FileList': [
|
||||
{
|
||||
|
@ -291,6 +284,9 @@ DOMInterfaces = {
|
|||
}],
|
||||
|
||||
'MouseEvent': {
|
||||
'nativeType': 'AbstractEvent',
|
||||
'concreteType': 'MouseEvent',
|
||||
'pointerType': '',
|
||||
},
|
||||
|
||||
'Navigator': {
|
||||
|
@ -388,6 +384,9 @@ DOMInterfaces = {
|
|||
}],
|
||||
|
||||
'UIEvent': {
|
||||
'nativeType': 'AbstractEvent',
|
||||
'concreteType': 'UIEvent',
|
||||
'pointerType': '',
|
||||
},
|
||||
|
||||
'ValidityState': {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -42,6 +42,8 @@ class Configuration:
|
|||
|
||||
self.enums = [e for e in parseData if e.isEnum()]
|
||||
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.
|
||||
self.descriptors.sort(lambda x,y: cmp(x.name, y.name))
|
||||
|
@ -66,14 +68,34 @@ class Configuration:
|
|||
getter = lambda x: x.interface.isCallback()
|
||||
elif key == 'isExternal':
|
||||
getter = lambda x: x.interface.isExternal()
|
||||
elif key == 'isJSImplemented':
|
||||
getter = lambda x: x.interface.isJSImplemented()
|
||||
else:
|
||||
getter = lambda x: getattr(x, key)
|
||||
curr = filter(lambda x: getter(x) == val, curr)
|
||||
return curr
|
||||
def getEnums(self, webIDLFile):
|
||||
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):
|
||||
"""
|
||||
Gets the appropriate descriptor for the given interface name
|
||||
|
|
|
@ -58,8 +58,8 @@ interface Document : Node {
|
|||
[Throws]
|
||||
Node adoptNode(Node node);*/
|
||||
|
||||
// [Creator, Throws]
|
||||
// Event createEvent(DOMString interface_);
|
||||
[Creator, Throws]
|
||||
Event createEvent(DOMString interface_);
|
||||
|
||||
/*[Creator, Throws]
|
||||
Range createRange();*/
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -11,4 +11,12 @@
|
|||
*/
|
||||
|
||||
interface EventTarget {
|
||||
void addEventListener(DOMString type,
|
||||
EventListener? listener,
|
||||
optional boolean capture = false);
|
||||
void removeEventListener(DOMString type,
|
||||
EventListener? listener,
|
||||
optional boolean capture = false);
|
||||
[Throws]
|
||||
boolean dispatchEvent(Event event);
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
interface URI;
|
||||
interface UserDataHandler;*/
|
||||
|
||||
interface Node /*: EventTarget*/ {
|
||||
interface Node : EventTarget {
|
||||
const unsigned short ELEMENT_NODE = 1;
|
||||
const unsigned short ATTRIBUTE_NODE = 2; // historical
|
||||
const unsigned short TEXT_NODE = 3;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
[NamedPropertiesObject]
|
||||
/*sealed*/ interface Window /*: EventTarget*/ {
|
||||
/*sealed*/ interface Window : EventTarget {
|
||||
// the current browsing context
|
||||
/*[Unforgeable] readonly attribute WindowProxy window;
|
||||
[Replaceable] readonly attribute WindowProxy self;*/
|
||||
|
|
|
@ -146,6 +146,23 @@ class IDLObject(object):
|
|||
def isCallback(self):
|
||||
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):
|
||||
return False
|
||||
|
||||
|
@ -167,6 +184,38 @@ class IDLObject(object):
|
|||
def handleExtendedAttribute(self, attr):
|
||||
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):
|
||||
def __init__(self, location, parentScope, identifier):
|
||||
IDLObject.__init__(self, location)
|
||||
|
@ -428,6 +477,15 @@ class IDLExternalInterface(IDLObjectWithIdentifier):
|
|||
def resolve(self, parentScope):
|
||||
pass
|
||||
|
||||
def getJSImplementation(self):
|
||||
return None
|
||||
|
||||
def isJSImplemented(self):
|
||||
return False
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set()
|
||||
|
||||
class IDLInterface(IDLObjectWithScope):
|
||||
def __init__(self, location, parentScope, name, parent, members,
|
||||
isPartial):
|
||||
|
@ -777,6 +835,24 @@ class IDLInterface(IDLObjectWithScope):
|
|||
# Put the new members at the beginning
|
||||
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):
|
||||
def __init__(self, location, parentScope, name, parent, members):
|
||||
assert isinstance(parentScope, IDLScope)
|
||||
|
@ -847,6 +923,11 @@ class IDLDictionary(IDLObjectWithScope):
|
|||
def addExtendedAttributes(self, attrs):
|
||||
assert len(attrs) == 0
|
||||
|
||||
def _getDependentObjects(self):
|
||||
deps = set(self.members)
|
||||
if (self.parent):
|
||||
deps.add(self.parent)
|
||||
return deps
|
||||
|
||||
class IDLEnum(IDLObjectWithIdentifier):
|
||||
def __init__(self, location, parentScope, name, values):
|
||||
|
@ -875,6 +956,9 @@ class IDLEnum(IDLObjectWithIdentifier):
|
|||
def addExtendedAttributes(self, attrs):
|
||||
assert len(attrs) == 0
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set()
|
||||
|
||||
class IDLType(IDLObject):
|
||||
Tags = enum(
|
||||
# The integer types
|
||||
|
@ -893,6 +977,7 @@ class IDLType(IDLObject):
|
|||
# Other types
|
||||
'any',
|
||||
'domstring',
|
||||
'bytestring',
|
||||
'object',
|
||||
'date',
|
||||
'void',
|
||||
|
@ -930,6 +1015,12 @@ class IDLType(IDLObject):
|
|||
def isString(self):
|
||||
return False
|
||||
|
||||
def isByteString(self):
|
||||
return False
|
||||
|
||||
def isDOMString(self):
|
||||
return False
|
||||
|
||||
def isVoid(self):
|
||||
return self.name == "Void"
|
||||
|
||||
|
@ -1075,6 +1166,12 @@ class IDLNullableType(IDLType):
|
|||
def isString(self):
|
||||
return self.inner.isString()
|
||||
|
||||
def isByteString(self):
|
||||
return self.inner.isByteString()
|
||||
|
||||
def isDOMString(self):
|
||||
return self.inner.isDOMString()
|
||||
|
||||
def isFloat(self):
|
||||
return self.inner.isFloat()
|
||||
|
||||
|
@ -1163,6 +1260,9 @@ class IDLNullableType(IDLType):
|
|||
return False
|
||||
return self.inner.isDistinguishableFrom(other)
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return self.inner._getDependentObjects()
|
||||
|
||||
class IDLSequenceType(IDLType):
|
||||
def __init__(self, location, parameterType):
|
||||
assert not parameterType.isVoid()
|
||||
|
@ -1231,6 +1331,9 @@ class IDLSequenceType(IDLType):
|
|||
other.isDictionary() or other.isDate() or
|
||||
other.isNonCallbackInterface())
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return self.inner._getDependentObjects()
|
||||
|
||||
class IDLUnionType(IDLType):
|
||||
def __init__(self, location, memberTypes):
|
||||
IDLType.__init__(self, location, "")
|
||||
|
@ -1317,6 +1420,9 @@ class IDLUnionType(IDLType):
|
|||
return False
|
||||
return True
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set(self.memberTypes)
|
||||
|
||||
class IDLArrayType(IDLType):
|
||||
def __init__(self, location, parameterType):
|
||||
assert not parameterType.isVoid()
|
||||
|
@ -1393,6 +1499,9 @@ class IDLArrayType(IDLType):
|
|||
other.isDictionary() or other.isDate() or
|
||||
other.isNonCallbackInterface())
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return self.inner._getDependentObjects()
|
||||
|
||||
class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
|
||||
def __init__(self, location, innerType, name):
|
||||
IDLType.__init__(self, location, innerType.name)
|
||||
|
@ -1478,6 +1587,9 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
|
|||
def isDistinguishableFrom(self, other):
|
||||
return self.inner.isDistinguishableFrom(other)
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return self.inner._getDependentObjects()
|
||||
|
||||
class IDLWrapperType(IDLType):
|
||||
def __init__(self, location, inner):
|
||||
IDLType.__init__(self, location, inner.identifier.name)
|
||||
|
@ -1583,6 +1695,23 @@ class IDLWrapperType(IDLType):
|
|||
assert other.isObject()
|
||||
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):
|
||||
|
||||
Types = enum(
|
||||
|
@ -1602,6 +1731,7 @@ class IDLBuiltinType(IDLType):
|
|||
# Other types
|
||||
'any',
|
||||
'domstring',
|
||||
'bytestring',
|
||||
'object',
|
||||
'date',
|
||||
'void',
|
||||
|
@ -1633,6 +1763,7 @@ class IDLBuiltinType(IDLType):
|
|||
Types.double: IDLType.Tags.double,
|
||||
Types.any: IDLType.Tags.any,
|
||||
Types.domstring: IDLType.Tags.domstring,
|
||||
Types.bytestring: IDLType.Tags.bytestring,
|
||||
Types.object: IDLType.Tags.object,
|
||||
Types.date: IDLType.Tags.date,
|
||||
Types.void: IDLType.Tags.void,
|
||||
|
@ -1658,6 +1789,13 @@ class IDLBuiltinType(IDLType):
|
|||
return self._typeTag <= IDLBuiltinType.Types.double
|
||||
|
||||
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
|
||||
|
||||
def isInteger(self):
|
||||
|
@ -1733,6 +1871,9 @@ class IDLBuiltinType(IDLType):
|
|||
(self.isTypedArray() and not other.isArrayBufferView() and not
|
||||
(other.isTypedArray() and other.name == self.name)))))
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set()
|
||||
|
||||
BuiltinTypes = {
|
||||
IDLBuiltinType.Types.byte:
|
||||
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte",
|
||||
|
@ -1877,6 +2018,9 @@ class IDLValue(IDLObject):
|
|||
raise WebIDLError("Cannot coerce type %s to type %s." %
|
||||
(self.type, type), [location])
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set()
|
||||
|
||||
class IDLNullValue(IDLObject):
|
||||
def __init__(self, location):
|
||||
IDLObject.__init__(self, location)
|
||||
|
@ -1895,6 +2039,9 @@ class IDLNullValue(IDLObject):
|
|||
nullValue.type = type
|
||||
return nullValue
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set()
|
||||
|
||||
|
||||
class IDLInterfaceMember(IDLObjectWithIdentifier):
|
||||
|
||||
|
@ -1966,6 +2113,9 @@ class IDLConst(IDLInterfaceMember):
|
|||
def validate(self):
|
||||
pass
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set([self.type, self.value])
|
||||
|
||||
class IDLAttribute(IDLInterfaceMember):
|
||||
def __init__(self, location, identifier, type, readonly, inherit,
|
||||
static=False):
|
||||
|
@ -2052,6 +2202,9 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
def hasLenientThis(self):
|
||||
return self.lenientThis
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set([self.type])
|
||||
|
||||
class IDLArgument(IDLObjectWithIdentifier):
|
||||
def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False):
|
||||
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
|
||||
|
@ -2124,6 +2277,12 @@ class IDLArgument(IDLObjectWithIdentifier):
|
|||
self.location)
|
||||
assert self.defaultValue
|
||||
|
||||
def _getDependentObjects(self):
|
||||
deps = set([self.type])
|
||||
if self.defaultValue:
|
||||
deps.add(self.defaultValue)
|
||||
return deps
|
||||
|
||||
class IDLCallbackType(IDLType, IDLObjectWithScope):
|
||||
def __init__(self, location, parentScope, identifier, returnType, arguments):
|
||||
assert isinstance(returnType, IDLType)
|
||||
|
@ -2179,6 +2338,9 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
|
|||
return (other.isPrimitive() or other.isString() or other.isEnum() or
|
||||
other.isNonCallbackInterface() or other.isDate())
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set([self._returnType] + self._arguments)
|
||||
|
||||
class IDLMethodOverload:
|
||||
"""
|
||||
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.location = location
|
||||
|
||||
def _getDependentObjects(self):
|
||||
deps = set(self.arguments)
|
||||
deps.add(self.returnType)
|
||||
return deps
|
||||
|
||||
class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
|
||||
Special = enum(
|
||||
|
@ -2494,6 +2661,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||
[attr.location, self.location])
|
||||
IDLInterfaceMember.handleExtendedAttribute(self, attr)
|
||||
|
||||
def _getDependentObjects(self):
|
||||
deps = set()
|
||||
for overload in self._overloads:
|
||||
deps.union(overload._getDependentObjects())
|
||||
return deps
|
||||
|
||||
class IDLImplementsStatement(IDLObject):
|
||||
def __init__(self, location, implementor, implementee):
|
||||
IDLObject.__init__(self, location)
|
||||
|
|
|
@ -50,7 +50,7 @@ impl Traceable for Node<ScriptView> {
|
|||
}
|
||||
}
|
||||
}
|
||||
debug!("tracing {:p}?:", self.reflector_.get_jsobject());
|
||||
debug!("tracing {:p}?:", self.reflector().get_jsobject());
|
||||
trace_node(tracer, self.parent_node, "parent");
|
||||
trace_node(tracer, self.first_child, "first child");
|
||||
trace_node(tracer, self.last_child, "last child");
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
use dom::bindings::codegen::PrototypeList;
|
||||
use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH;
|
||||
use dom::window;
|
||||
use dom::node::{AbstractNode, ScriptView};
|
||||
use dom::window;
|
||||
|
||||
use std::libc::c_uint;
|
||||
use std::cast;
|
||||
|
@ -22,6 +22,7 @@ use js::glue::{js_IsObjectProxyClass, js_IsFunctionProxyClass, IsProxyHandlerFam
|
|||
use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewObject, JS_NewFunction, JS_GetGlobalObject};
|
||||
use js::jsapi::{JS_DefineProperties, JS_WrapValue, JS_ForwardGetPropertyTo};
|
||||
use js::jsapi::{JS_GetClass, JS_LinkConstructorAndPrototype, JS_GetStringCharsAndLength};
|
||||
use js::jsapi::{JS_ObjectIsRegExp, JS_ObjectIsDate};
|
||||
use js::jsapi::{JS_GetFunctionPrototype, JS_InternString, JS_GetFunctionObject};
|
||||
use js::jsapi::{JS_HasPropertyById, JS_GetPrototype, JS_GetGlobalForObject};
|
||||
use js::jsapi::{JS_NewUCStringCopyN, JS_DefineFunctions, JS_DefineProperty};
|
||||
|
@ -30,7 +31,7 @@ use js::jsapi::{JSContext, JSObject, JSBool, jsid, JSClass, JSNative, JSTracer};
|
|||
use js::jsapi::{JSFunctionSpec, JSPropertySpec, JSVal, JSPropertyDescriptor};
|
||||
use js::jsapi::{JSPropertyOp, JSStrictPropertyOp, JS_NewGlobalObject, JS_InitStandardClasses};
|
||||
use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType;
|
||||
use js::{JSPROP_ENUMERATE, JSVAL_NULL};
|
||||
use js::{JSPROP_ENUMERATE, JSVAL_NULL, JSCLASS_IS_GLOBAL, JSCLASS_IS_DOMJSCLASS};
|
||||
use js::{JSPROP_PERMANENT, JSID_VOID, JSPROP_NATIVE_ACCESSORS, JSPROP_GETTER};
|
||||
use js::{JSPROP_SETTER, JSVAL_VOID, JSVAL_TRUE, JSVAL_FALSE};
|
||||
use js::{JS_THIS_OBJECT, JSFUN_CONSTRUCTOR, JS_CALLEE, JSPROP_READONLY};
|
||||
|
@ -767,6 +768,8 @@ pub enum Error {
|
|||
NotFound,
|
||||
HierarchyRequest,
|
||||
InvalidCharacter,
|
||||
NotSupported,
|
||||
InvalidState
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#[fixed_stack_segment]
|
||||
pub fn IsConvertibleToCallbackInterface(cx: *JSContext, obj: *JSObject) -> bool {
|
||||
unsafe {
|
||||
JS_ObjectIsDate(cx, obj) == 0 && JS_ObjectIsRegExp(cx, obj) == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[fixed_stack_segment]
|
||||
pub fn CreateDOMGlobal(cx: *JSContext, class: *JSClass) -> *JSObject {
|
||||
unsafe {
|
||||
|
@ -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
|
||||
/// for details.
|
||||
pub fn is_valid_element_name(name: &str) -> bool {
|
||||
|
|
|
@ -5,15 +5,20 @@
|
|||
use dom::comment::Comment;
|
||||
use dom::bindings::codegen::DocumentBinding;
|
||||
use dom::bindings::utils::{DOMString, ErrorResult, Fallible};
|
||||
use dom::bindings::utils::{Reflectable, Reflector, DerivedWrapper};
|
||||
use dom::bindings::utils::{is_valid_element_name, InvalidCharacter, Traceable, null_str_as_empty, null_str_as_word_null};
|
||||
use dom::bindings::utils::{Reflectable, Reflector, DerivedWrapper, NotSupported};
|
||||
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::element::{Element};
|
||||
use dom::element::{HTMLHeadElementTypeId, HTMLTitleElementTypeId};
|
||||
use dom::event::{AbstractEvent, Event, HTMLEventTypeId, UIEventTypeId};
|
||||
use dom::htmlcollection::HTMLCollection;
|
||||
use dom::htmldocument::HTMLDocument;
|
||||
use dom::htmlelement::HTMLElement;
|
||||
use dom::mouseevent::MouseEvent;
|
||||
use dom::node::{AbstractNode, ScriptView, Node, ElementNodeTypeId, DocumentNodeTypeId};
|
||||
use dom::text::Text;
|
||||
use dom::uievent::UIEvent;
|
||||
use dom::window::Window;
|
||||
use dom::htmltitleelement::HTMLTitleElement;
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
let mut title = ~"";
|
||||
match self.doctype {
|
||||
|
|
|
@ -2,17 +2,23 @@
|
|||
* 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::eventtarget::EventTarget;
|
||||
use dom::eventtarget::AbstractEventTarget;
|
||||
use dom::window::Window;
|
||||
use dom::bindings::codegen::EventBinding;
|
||||
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
|
||||
use dom::bindings::utils::{DOMString, ErrorResult, Fallible};
|
||||
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object, DerivedWrapper};
|
||||
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 js::jsapi::{JSObject, JSContext};
|
||||
use js::jsapi::{JSObject, JSContext, JSVal};
|
||||
use js::glue::RUST_OBJECT_TO_JSVAL;
|
||||
|
||||
use script_task::page_from_context;
|
||||
|
||||
use std::cast;
|
||||
use std::unstable::raw::Box;
|
||||
|
||||
pub enum Event_ {
|
||||
ResizeEvent(uint, uint),
|
||||
ReflowEvent,
|
||||
|
@ -21,45 +27,194 @@ pub enum Event_ {
|
|||
MouseUpEvent(uint, Point2D<f32>),
|
||||
}
|
||||
|
||||
pub struct AbstractEvent {
|
||||
event: *mut Box<Event>
|
||||
}
|
||||
|
||||
pub enum EventPhase {
|
||||
Phase_None = 0,
|
||||
Phase_Capturing,
|
||||
Phase_At_Target,
|
||||
Phase_Bubbling
|
||||
}
|
||||
|
||||
impl AbstractEvent {
|
||||
pub fn from_box(box: *mut Box<Event>) -> AbstractEvent {
|
||||
AbstractEvent {
|
||||
event: box
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 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,
|
||||
type_: DOMString,
|
||||
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_: &DOMString) -> Event {
|
||||
pub fn new_inherited(type_id: EventTypeId) -> Event {
|
||||
Event {
|
||||
type_id: type_id,
|
||||
reflector_: Reflector::new(),
|
||||
type_: (*type_).clone(),
|
||||
current_target: None,
|
||||
target: None,
|
||||
phase: Phase_None,
|
||||
type_: ~"",
|
||||
default_prevented: false,
|
||||
cancelable: true,
|
||||
bubbles: true,
|
||||
trusted: false
|
||||
trusted: false,
|
||||
dispatching: false,
|
||||
stop_propagation: false,
|
||||
stop_immediate: false,
|
||||
initialized: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(window: @mut Window, type_: &DOMString) -> @mut Event {
|
||||
reflect_dom_object(@mut Event::new_inherited(type_), window, EventBinding::Wrap)
|
||||
//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 {
|
||||
0
|
||||
self.phase as u16
|
||||
}
|
||||
|
||||
pub fn Type(&self) -> DOMString {
|
||||
self.type_.clone()
|
||||
Some(self.type_.clone())
|
||||
}
|
||||
|
||||
pub fn GetTarget(&self) -> Option<@mut EventTarget> {
|
||||
None
|
||||
pub fn GetTarget(&self) -> Option<AbstractEventTarget> {
|
||||
self.target
|
||||
}
|
||||
|
||||
pub fn GetCurrentTarget(&self) -> Option<@mut EventTarget> {
|
||||
None
|
||||
pub fn GetCurrentTarget(&self) -> Option<AbstractEventTarget> {
|
||||
self.current_target
|
||||
}
|
||||
|
||||
pub fn DefaultPrevented(&self) -> bool {
|
||||
|
@ -67,13 +222,18 @@ impl Event {
|
|||
}
|
||||
|
||||
pub fn PreventDefault(&mut self) {
|
||||
if self.cancelable {
|
||||
self.default_prevented = true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn StopPropagation(&mut self) {
|
||||
self.stop_propagation = true;
|
||||
}
|
||||
|
||||
pub fn StopImmediatePropagation(&mut self) {
|
||||
self.stop_immediate = true;
|
||||
self.stop_propagation = true;
|
||||
}
|
||||
|
||||
pub fn Bubbles(&self) -> bool {
|
||||
|
@ -92,9 +252,10 @@ impl Event {
|
|||
type_: &DOMString,
|
||||
bubbles: bool,
|
||||
cancelable: bool) -> ErrorResult {
|
||||
self.type_ = (*type_).clone();
|
||||
self.type_ = null_str_as_word_null(type_);
|
||||
self.cancelable = cancelable;
|
||||
self.bubbles = bubbles;
|
||||
self.initialized = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -104,8 +265,10 @@ impl Event {
|
|||
|
||||
pub fn Constructor(global: @mut Window,
|
||||
type_: &DOMString,
|
||||
_init: &EventBinding::EventInit) -> Fallible<@mut Event> {
|
||||
Ok(Event::new(global, type_))
|
||||
init: &EventBinding::EventInit) -> Fallible<AbstractEvent> {
|
||||
let ev = Event::new(global, HTMLEventTypeId);
|
||||
ev.mut_event().InitEvent(type_, init.bubbles, init.cancelable);
|
||||
Ok(ev)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
111
src/components/script/dom/eventdispatcher.rs
Normal file
111
src/components/script/dom/eventdispatcher.rs
Normal 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()
|
||||
}
|
|
@ -3,24 +3,196 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
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 js::jsapi::{JSObject, JSContext};
|
||||
use js::jsapi::{JSObject, JSContext, JSVal};
|
||||
use js::glue::RUST_OBJECT_TO_JSVAL;
|
||||
|
||||
use std::cast;
|
||||
use std::hashmap::HashMap;
|
||||
use std::unstable::raw::Box;
|
||||
|
||||
#[deriving(Eq)]
|
||||
pub enum ListenerPhase {
|
||||
Capturing,
|
||||
Bubbling,
|
||||
}
|
||||
|
||||
#[deriving(Eq)]
|
||||
pub enum EventTargetTypeId {
|
||||
WindowTypeId,
|
||||
NodeTypeId
|
||||
}
|
||||
|
||||
#[deriving(Eq)]
|
||||
struct EventListenerEntry {
|
||||
phase: ListenerPhase,
|
||||
listener: EventListener
|
||||
}
|
||||
|
||||
pub struct EventTarget {
|
||||
reflector_: Reflector
|
||||
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 from_node(node: AbstractNode<ScriptView>) -> AbstractEventTarget {
|
||||
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() -> ~EventTarget {
|
||||
~EventTarget {
|
||||
reflector_: Reflector::new()
|
||||
pub fn new_inherited(type_id: EventTargetTypeId) -> EventTarget {
|
||||
EventTarget {
|
||||
type_id: type_id,
|
||||
reflector_: Reflector::new(),
|
||||
handlers: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) {
|
||||
self.wrap_object_shared(cx, scope);
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
use dom::bindings::codegen::MouseEventBinding;
|
||||
use dom::bindings::utils::{ErrorResult, Fallible, DOMString};
|
||||
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::window::Window;
|
||||
use dom::windowproxy::WindowProxy;
|
||||
|
@ -23,38 +24,42 @@ pub struct MouseEvent {
|
|||
alt_key: bool,
|
||||
meta_key: bool,
|
||||
button: u16,
|
||||
related_target: Option<@mut EventTarget>
|
||||
related_target: Option<AbstractEventTarget>
|
||||
}
|
||||
|
||||
impl MouseEvent {
|
||||
pub fn new(window: @mut Window, 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>) -> @mut MouseEvent {
|
||||
let ev = @mut MouseEvent {
|
||||
parent: UIEvent::new_inherited(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
|
||||
};
|
||||
reflect_dom_object(ev, window, MouseEventBinding::Wrap)
|
||||
pub fn new_inherited() -> MouseEvent {
|
||||
MouseEvent {
|
||||
parent: UIEvent::new_inherited(MouseEventTypeId),
|
||||
screen_x: 0,
|
||||
screen_y: 0,
|
||||
client_x: 0,
|
||||
client_y: 0,
|
||||
ctrl_key: false,
|
||||
shift_key: false,
|
||||
alt_key: false,
|
||||
meta_key: false,
|
||||
button: 0,
|
||||
related_target: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(window: @mut Window) -> AbstractEvent {
|
||||
Event::as_abstract(reflect_dom_object(@mut MouseEvent::new_inherited(),
|
||||
window,
|
||||
MouseEventBinding::Wrap))
|
||||
}
|
||||
|
||||
pub fn Constructor(owner: @mut Window,
|
||||
type_: &DOMString,
|
||||
init: &MouseEventBinding::MouseEventInit) -> Fallible<@mut MouseEvent> {
|
||||
Ok(MouseEvent::new(owner, 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))
|
||||
init: &MouseEventBinding::MouseEventInit) -> Fallible<AbstractEvent> {
|
||||
let ev = MouseEvent::new(owner);
|
||||
ev.mut_mouseevent().InitMouseEvent(type_, init.bubbles, init.cancelable, init.view,
|
||||
init.detail, init.screenX, init.screenY,
|
||||
init.clientX, init.clientY, init.ctrlKey,
|
||||
init.altKey, init.shiftKey, init.metaKey,
|
||||
init.button, init.relatedTarget);
|
||||
Ok(ev)
|
||||
}
|
||||
|
||||
pub fn ScreenX(&self) -> i32 {
|
||||
|
@ -98,7 +103,7 @@ impl MouseEvent {
|
|||
0
|
||||
}
|
||||
|
||||
pub fn GetRelatedTarget(&self) -> Option<@mut EventTarget> {
|
||||
pub fn GetRelatedTarget(&self) -> Option<AbstractEventTarget> {
|
||||
self.related_target
|
||||
}
|
||||
|
||||
|
@ -122,7 +127,7 @@ impl MouseEvent {
|
|||
shiftKeyArg: bool,
|
||||
metaKeyArg: bool,
|
||||
buttonArg: u16,
|
||||
relatedTargetArg: Option<@mut EventTarget>) -> ErrorResult {
|
||||
relatedTargetArg: Option<AbstractEventTarget>) -> ErrorResult {
|
||||
self.parent.InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg);
|
||||
self.screen_x = screenXArg;
|
||||
self.screen_y = screenYArg;
|
||||
|
|
|
@ -12,6 +12,7 @@ use dom::document::{AbstractDocument, DocumentTypeId};
|
|||
use dom::documenttype::DocumentType;
|
||||
use dom::element::{Element, ElementTypeId, HTMLImageElementTypeId, HTMLIframeElementTypeId};
|
||||
use dom::element::{HTMLStyleElementTypeId};
|
||||
use dom::eventtarget::{AbstractEventTarget, EventTarget, NodeTypeId};
|
||||
use dom::nodelist::{NodeList};
|
||||
use dom::htmlimageelement::HTMLImageElement;
|
||||
use dom::htmliframeelement::HTMLIFrameElement;
|
||||
|
@ -63,7 +64,7 @@ pub struct AbstractNodeChildrenIterator<View> {
|
|||
/// `LayoutData`.
|
||||
pub struct Node<View> {
|
||||
/// The JavaScript reflector for this node.
|
||||
reflector_: Reflector,
|
||||
eventtarget: EventTarget,
|
||||
|
||||
/// The type of node that this is.
|
||||
type_id: NodeTypeId,
|
||||
|
@ -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
|
||||
|
||||
/// 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> {
|
||||
Node {
|
||||
reflector_: Reflector::new(),
|
||||
eventtarget: EventTarget::new_inherited(NodeTypeId),
|
||||
type_id: type_id,
|
||||
|
||||
abstract: None,
|
||||
|
@ -1042,11 +1050,11 @@ impl Node<ScriptView> {
|
|||
|
||||
impl Reflectable for Node<ScriptView> {
|
||||
fn reflector<'a>(&'a self) -> &'a Reflector {
|
||||
&self.reflector_
|
||||
self.eventtarget.reflector()
|
||||
}
|
||||
|
||||
fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
|
||||
&mut self.reflector_
|
||||
self.eventtarget.mut_reflector()
|
||||
}
|
||||
|
||||
fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject {
|
||||
|
|
|
@ -6,7 +6,7 @@ use dom::bindings::codegen::UIEventBinding;
|
|||
use dom::bindings::utils::{DOMString, Fallible};
|
||||
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
|
||||
use dom::node::{AbstractNode, ScriptView};
|
||||
use dom::event::Event;
|
||||
use dom::event::{AbstractEvent, Event, EventTypeId, UIEventTypeId};
|
||||
use dom::window::Window;
|
||||
use dom::windowproxy::WindowProxy;
|
||||
|
||||
|
@ -14,36 +14,33 @@ use js::jsapi::{JSObject, JSContext};
|
|||
|
||||
pub struct UIEvent {
|
||||
parent: Event,
|
||||
can_bubble: bool,
|
||||
cancelable: bool,
|
||||
view: Option<@mut WindowProxy>,
|
||||
detail: i32
|
||||
}
|
||||
|
||||
impl UIEvent {
|
||||
pub fn new_inherited(type_: &DOMString, can_bubble: bool, cancelable: bool,
|
||||
view: Option<@mut WindowProxy>, detail: i32) -> UIEvent {
|
||||
pub fn new_inherited(type_id: EventTypeId) -> UIEvent {
|
||||
UIEvent {
|
||||
parent: Event::new_inherited(type_),
|
||||
can_bubble: can_bubble,
|
||||
cancelable: cancelable,
|
||||
view: view,
|
||||
detail: detail
|
||||
parent: Event::new_inherited(type_id),
|
||||
view: None,
|
||||
detail: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(window: @mut Window, type_: &DOMString, can_bubble: bool, cancelable: bool,
|
||||
view: Option<@mut WindowProxy>, detail: i32) -> @mut UIEvent {
|
||||
reflect_dom_object(@mut UIEvent::new_inherited(type_, can_bubble, cancelable, view, detail),
|
||||
pub fn new(window: @mut Window, type_id: EventTypeId) -> AbstractEvent {
|
||||
let ev = reflect_dom_object(@mut UIEvent::new_inherited(type_id),
|
||||
window,
|
||||
UIEventBinding::Wrap)
|
||||
UIEventBinding::Wrap);
|
||||
Event::as_abstract(ev)
|
||||
}
|
||||
|
||||
pub fn Constructor(owner: @mut Window,
|
||||
type_: &DOMString,
|
||||
init: &UIEventBinding::UIEventInit) -> Fallible<@mut UIEvent> {
|
||||
Ok(UIEvent::new(owner, type_, init.parent.bubbles, init.parent.cancelable,
|
||||
init.view, init.detail))
|
||||
init: &UIEventBinding::UIEventInit) -> Fallible<AbstractEvent> {
|
||||
let ev = UIEvent::new(owner, UIEventTypeId);
|
||||
ev.mut_uievent().InitUIEvent(type_, init.parent.bubbles, init.parent.cancelable,
|
||||
init.view, init.detail);
|
||||
Ok(ev)
|
||||
}
|
||||
|
||||
pub fn GetView(&self) -> Option<@mut WindowProxy> {
|
||||
|
@ -61,8 +58,6 @@ impl UIEvent {
|
|||
view: Option<@mut WindowProxy>,
|
||||
detail: i32) {
|
||||
self.parent.InitEvent(type_, can_bubble, cancelable);
|
||||
self.can_bubble = can_bubble;
|
||||
self.cancelable = cancelable;
|
||||
self.view = view;
|
||||
self.detail = detail;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use dom::bindings::codegen::WindowBinding;
|
|||
use dom::bindings::utils::{Reflectable, Reflector};
|
||||
use dom::bindings::utils::{DOMString, null_str_as_empty, Traceable};
|
||||
use dom::document::AbstractDocument;
|
||||
use dom::eventtarget::{EventTarget, WindowTypeId};
|
||||
use dom::node::{AbstractNode, ScriptView};
|
||||
use dom::navigator::Navigator;
|
||||
|
||||
|
@ -37,10 +38,10 @@ pub enum TimerControlMsg {
|
|||
}
|
||||
|
||||
pub struct Window {
|
||||
eventtarget: EventTarget,
|
||||
page: @mut Page,
|
||||
script_chan: ScriptChan,
|
||||
compositor: @ScriptListener,
|
||||
reflector_: Reflector,
|
||||
timer_chan: SharedChan<TimerControlMsg>,
|
||||
navigator: Option<@mut Navigator>,
|
||||
image_cache_task: ImageCacheTask,
|
||||
|
@ -140,11 +141,11 @@ impl Window {
|
|||
|
||||
impl Reflectable for Window {
|
||||
fn reflector<'a>(&'a self) -> &'a Reflector {
|
||||
&self.reflector_
|
||||
self.eventtarget.reflector()
|
||||
}
|
||||
|
||||
fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
|
||||
&mut self.reflector_
|
||||
self.eventtarget.mut_reflector()
|
||||
}
|
||||
|
||||
fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject {
|
||||
|
@ -204,10 +205,10 @@ impl Window {
|
|||
image_cache_task: ImageCacheTask)
|
||||
-> @mut Window {
|
||||
let win = @mut Window {
|
||||
eventtarget: EventTarget::new_inherited(WindowTypeId),
|
||||
page: page,
|
||||
script_chan: script_chan.clone(),
|
||||
compositor: compositor,
|
||||
reflector_: Reflector::new(),
|
||||
timer_chan: {
|
||||
let (timer_port, timer_chan) = comm::stream::<TimerControlMsg>();
|
||||
let id = page.id.clone();
|
||||
|
|
|
@ -28,6 +28,7 @@ pub mod dom {
|
|||
pub mod element;
|
||||
pub mod node;
|
||||
pub mod utils;
|
||||
pub mod callback;
|
||||
pub mod conversions;
|
||||
pub mod proxyhandler;
|
||||
pub mod codegen {
|
||||
|
@ -54,6 +55,7 @@ pub mod dom {
|
|||
pub mod domparser;
|
||||
pub mod element;
|
||||
pub mod event;
|
||||
pub mod eventdispatcher;
|
||||
pub mod eventtarget;
|
||||
pub mod formdata;
|
||||
pub mod htmlanchorelement;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 1df26877d82d9722785de91ff59ab069a5acc180
|
||||
Subproject commit 566c2af971abaa5e8c51b59fa400a7e07835b257
|
|
@ -4,7 +4,7 @@
|
|||
<script>
|
||||
is_function(Event, "Event");
|
||||
|
||||
let ev = new Event("foopy");
|
||||
let ev = new Event("foopy", {cancelable: true});
|
||||
is_a(ev, Event);
|
||||
|
||||
is(ev.type, 'foopy');
|
||||
|
|
51
src/test/html/content/test_event_dispatch.html
Normal file
51
src/test/html/content/test_event_dispatch.html
Normal 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>
|
21
src/test/html/content/test_event_dispatch_dynamic.html
Normal file
21
src/test/html/content/test_event_dispatch_dynamic.html
Normal 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>
|
42
src/test/html/content/test_event_dispatch_order.html
Normal file
42
src/test/html/content/test_event_dispatch_order.html
Normal 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>
|
39
src/test/html/content/test_event_listener.html
Normal file
39
src/test/html/content/test_event_listener.html
Normal 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>
|
Loading…
Add table
Add a link
Reference in a new issue