mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Consider shadow dom when dispatching events (#34788)
* Implement EventTarget::get_the_parent Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Add spec steps to Event::init_event Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Rewrite Event::composedPath to be spec compliant Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Retarget EventTargets instead of Nodes Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Rewrite event dispatch/invocation to better match the spec Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Add spec comments to Event struct Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Don't traverse shadow roots when calculating an events path We can't do this correctly yet, and assuming that an events composed flag is never set is correct 99% of the time. Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Fix typo in event dispatch Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * fix comment Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Update WPT expectations Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * allow crown error Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * fmt Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Reduce item visibility where possible Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Simplify code a bit Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Fix Step 5.10 of Event::invoke Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Fix renamed method calls Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> --------- Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
parent
cd39b1de64
commit
270df6e263
10 changed files with 728 additions and 293 deletions
File diff suppressed because it is too large
Load diff
|
@ -24,7 +24,6 @@ use libc::c_char;
|
|||
use servo_atoms::Atom;
|
||||
use servo_url::ServoUrl;
|
||||
|
||||
use super::bindings::trace::HashMapTracedValues;
|
||||
use crate::dom::beforeunloadevent::BeforeUnloadEvent;
|
||||
use crate::dom::bindings::callback::{CallbackContainer, CallbackFunction, ExceptionHandling};
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
|
@ -38,6 +37,9 @@ use crate::dom::bindings::codegen::Bindings::EventListenerBinding::EventListener
|
|||
use crate::dom::bindings::codegen::Bindings::EventTargetBinding::{
|
||||
AddEventListenerOptions, EventListenerOptions, EventTargetMethods,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::GetRootNodeOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
use crate::dom::bindings::codegen::UnionTypes::{
|
||||
AddEventListenerOptionsOrBoolean, EventListenerOptionsOrBoolean, EventOrString,
|
||||
|
@ -47,12 +49,15 @@ use crate::dom::bindings::inheritance::Castable;
|
|||
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomObject, Reflector};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::bindings::trace::HashMapTracedValues;
|
||||
use crate::dom::document::Document;
|
||||
use crate::dom::element::Element;
|
||||
use crate::dom::errorevent::ErrorEvent;
|
||||
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::htmlformelement::FormControlElementHelpers;
|
||||
use crate::dom::node::NodeTraits;
|
||||
use crate::dom::node::{Node, NodeTraits};
|
||||
use crate::dom::shadowroot::ShadowRoot;
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::dom::window::Window;
|
||||
use crate::dom::workerglobalscope::WorkerGlobalScope;
|
||||
|
@ -781,6 +786,67 @@ impl EventTarget {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#get-the-parent>
|
||||
pub(crate) fn get_the_parent(&self, event: &Event) -> Option<DomRoot<EventTarget>> {
|
||||
if let Some(document) = self.downcast::<Document>() {
|
||||
if event.type_() == atom!("load") || !document.has_browsing_context() {
|
||||
return None;
|
||||
} else {
|
||||
return Some(DomRoot::from_ref(document.window().upcast::<EventTarget>()));
|
||||
}
|
||||
}
|
||||
|
||||
if self.downcast::<ShadowRoot>().is_some() {
|
||||
// FIXME: Handle event composed flag here
|
||||
// We currently assume that events are never composed (so events may never
|
||||
// cross a shadow boundary)
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(node) = self.downcast::<Node>() {
|
||||
// FIXME: Handle slottables here
|
||||
let parent = node.GetParentNode()?;
|
||||
return Some(DomRoot::from_ref(parent.upcast::<EventTarget>()));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// FIXME: This algorithm operates on "objects", which may not be event targets.
|
||||
// All our current use-cases only work on event targets, but this might change in the future
|
||||
/// <https://dom.spec.whatwg.org/#retarget>
|
||||
pub(crate) fn retarget(&self, b: &Self) -> DomRoot<EventTarget> {
|
||||
// To retarget an object A against an object B, repeat these steps until they return an object:
|
||||
let mut a = DomRoot::from_ref(self);
|
||||
loop {
|
||||
// Step 1. If one of the following is true
|
||||
// * A is not a node
|
||||
// * A’s root is not a shadow root
|
||||
// * B is a node and A’s root is a shadow-including inclusive ancestor of B
|
||||
let Some(a_node) = a.downcast::<Node>() else {
|
||||
return a;
|
||||
};
|
||||
let a_root = a_node.GetRootNode(&GetRootNodeOptions::empty());
|
||||
if !a_root.is::<ShadowRoot>() {
|
||||
return a;
|
||||
}
|
||||
if let Some(b_node) = b.downcast::<Node>() {
|
||||
if a_root.is_shadow_including_inclusive_ancestor_of(b_node) {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2. Set A to A’s root’s host.
|
||||
a = DomRoot::from_ref(
|
||||
a_root
|
||||
.downcast::<ShadowRoot>()
|
||||
.unwrap()
|
||||
.Host()
|
||||
.upcast::<EventTarget>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventTargetMethods<crate::DomTypeHolder> for EventTarget {
|
||||
|
|
|
@ -612,6 +612,7 @@ impl Node {
|
|||
self.flags.get().contains(NodeFlags::IS_IN_A_DOCUMENT_TREE)
|
||||
}
|
||||
|
||||
/// Return true iff node's root is a shadow-root.
|
||||
pub fn is_in_a_shadow_tree(&self) -> bool {
|
||||
self.flags.get().contains(NodeFlags::IS_IN_SHADOW_TREE)
|
||||
}
|
||||
|
@ -1281,27 +1282,6 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#retarget>
|
||||
pub fn retarget(&self, b: &Node) -> DomRoot<Node> {
|
||||
let mut a = DomRoot::from_ref(self);
|
||||
loop {
|
||||
// Step 1.
|
||||
let a_root = a.GetRootNode(&GetRootNodeOptions::empty());
|
||||
if !a_root.is::<ShadowRoot>() || a_root.is_shadow_including_inclusive_ancestor_of(b) {
|
||||
return DomRoot::from_ref(&a);
|
||||
}
|
||||
|
||||
// Step 2.
|
||||
a = DomRoot::from_ref(
|
||||
a_root
|
||||
.downcast::<ShadowRoot>()
|
||||
.unwrap()
|
||||
.Host()
|
||||
.upcast::<Node>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_styled(&self) -> bool {
|
||||
self.style_data.borrow().is_some()
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ use crate::dom::node::{
|
|||
BindContext, Node, NodeDamage, NodeFlags, NodeTraits, ShadowIncluding, UnbindContext,
|
||||
};
|
||||
use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
|
||||
use crate::dom::types::EventTarget;
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::dom::window::Window;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
@ -218,7 +219,9 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
|
|||
can_gc,
|
||||
) {
|
||||
Some(e) => {
|
||||
let retargeted_node = self.upcast::<Node>().retarget(e.upcast::<Node>());
|
||||
let retargeted_node = self
|
||||
.upcast::<EventTarget>()
|
||||
.retarget(e.upcast::<EventTarget>());
|
||||
retargeted_node.downcast::<Element>().map(DomRoot::from_ref)
|
||||
},
|
||||
None => None,
|
||||
|
@ -240,7 +243,9 @@ impl ShadowRootMethods<crate::DomTypeHolder> for ShadowRoot {
|
|||
.elements_from_point(x, y, None, self.document.has_browsing_context(), can_gc)
|
||||
.iter()
|
||||
{
|
||||
let retargeted_node = self.upcast::<Node>().retarget(e.upcast::<Node>());
|
||||
let retargeted_node = self
|
||||
.upcast::<EventTarget>()
|
||||
.retarget(e.upcast::<EventTarget>());
|
||||
if let Some(element) = retargeted_node.downcast::<Element>().map(DomRoot::from_ref) {
|
||||
elements.push(element);
|
||||
}
|
||||
|
|
|
@ -1601,12 +1601,15 @@ impl Window {
|
|||
window_named_properties::create(cx, proto, object)
|
||||
}
|
||||
|
||||
pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
|
||||
let current = self
|
||||
.current_event
|
||||
pub(crate) fn current_event(&self) -> Option<DomRoot<Event>> {
|
||||
self.current_event
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map(|e| DomRoot::from_ref(&**e));
|
||||
.map(|e| DomRoot::from_ref(&**e))
|
||||
}
|
||||
|
||||
pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
|
||||
let current = self.current_event();
|
||||
*self.current_event.borrow_mut() = event.map(Dom::from_ref);
|
||||
current
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
[Event-dispatch-click.html]
|
||||
[event state during post-click handling]
|
||||
expected: FAIL
|
||||
|
||||
[submit button should not activate if the event listener disables it]
|
||||
expected: FAIL
|
|
@ -1,8 +0,0 @@
|
|||
[EventTarget-constructible.any.worker.html]
|
||||
[A constructed EventTarget implements dispatch correctly]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[EventTarget-constructible.any.html]
|
||||
[A constructed EventTarget implements dispatch correctly]
|
||||
expected: FAIL
|
|
@ -1,6 +1,7 @@
|
|||
[event-global.html]
|
||||
expected: TIMEOUT
|
||||
[window.event is undefined if the target is in a shadow tree (event dispatched inside shadow tree)]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[window.event is undefined inside window.onerror if the target is in a shadow tree (ErrorEvent dispatched inside shadow tree)]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
[Extensions-to-Event-Interface.html]
|
||||
[composedPath() must return an empty array when the event is no longer dispatched]
|
||||
expected: FAIL
|
||||
|
||||
[composed on EventInit must default to false]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,28 +1,13 @@
|
|||
[event-post-dispatch.html]
|
||||
[Event properties post dispatch without ShadowRoots (composed: true).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch without ShadowRoots (composed: false).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with an open ShadowRoot (composed: true).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with an open ShadowRoot (composed: false).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with a closed ShadowRoot (composed: true).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with a closed ShadowRoot (composed: false).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with nested ShadowRoots (composed: true).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with nested ShadowRoots (composed: false).]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch with relatedTarget in the same shadow tree. (composed: true)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -43,6 +28,3 @@
|
|||
|
||||
[Event properties post dispatch when target get moved out of the shadow tree by event listener]
|
||||
expected: FAIL
|
||||
|
||||
[Event properties post dispatch when target get moved into the the shadow tree by event listener]
|
||||
expected: FAIL
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue