From ce302c78c10e54ca592a3af0b4e728cb6b0073ff Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Mon, 25 Apr 2016 19:34:44 -0400 Subject: [PATCH 1/4] Rename ToggleEventRunnable to DetailsNotificationRunnable --- components/script/dom/htmldetailselement.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/script/dom/htmldetailselement.rs b/components/script/dom/htmldetailselement.rs index 3f00e53220c..724c9cff614 100644 --- a/components/script/dom/htmldetailselement.rs +++ b/components/script/dom/htmldetailselement.rs @@ -74,7 +74,7 @@ impl VirtualMethods for HTMLDetailsElement { let window = window.r(); let task_source = window.dom_manipulation_task_source(); let details = Trusted::new(self); - let runnable = box ToggleEventRunnable { + let runnable = box DetailsNotificationRunnable { element: details, toggle_number: counter }; @@ -83,13 +83,13 @@ impl VirtualMethods for HTMLDetailsElement { } } -pub struct ToggleEventRunnable { +pub struct DetailsNotificationRunnable { element: Trusted, toggle_number: u32 } -impl Runnable for ToggleEventRunnable { - fn handler(self: Box) { +impl Runnable for DetailsNotificationRunnable { + fn handler(self: Box) { let target = self.element.root(); if target.check_toggle_count(self.toggle_number) { target.upcast::().fire_simple_event("toggle"); From 12a96f71945dbd27336469f69f8e679a2b54b40b Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Mon, 25 Apr 2016 19:46:21 -0400 Subject: [PATCH 2/4] Rename DOM manipulation messages to tasks --- components/script/script_thread.rs | 4 ++-- components/script/task_source/dom_manipulation.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index a46c1a28b18..5891bd35c7c 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -932,8 +932,8 @@ impl ScriptThread { LiveDOMReferences::cleanup(addr), MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => self.collect_reports(reports_chan), - MainThreadScriptMsg::DOMManipulation(msg) => - msg.handle_msg(self), + MainThreadScriptMsg::DOMManipulation(task) => + task.handle_task(self), } } diff --git a/components/script/task_source/dom_manipulation.rs b/components/script/task_source/dom_manipulation.rs index 5993825a1be..de3fc19710e 100644 --- a/components/script/task_source/dom_manipulation.rs +++ b/components/script/task_source/dom_manipulation.rs @@ -42,7 +42,7 @@ pub enum DOMManipulationTask { } impl DOMManipulationTask { - pub fn handle_msg(self, script_thread: &ScriptThread) { + pub fn handle_task(self, script_thread: &ScriptThread) { use self::DOMManipulationTask::*; match self { From cc049515dc77d8975351d04074310874c486c512 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Sat, 12 Mar 2016 17:58:52 -0500 Subject: [PATCH 3/4] Implement user interaction task source --- components/script/dom/bindings/global.rs | 9 ---- components/script/dom/htmlinputelement.rs | 42 +++------------ components/script/dom/htmltextareaelement.rs | 14 +++-- components/script/dom/window.rs | 2 +- components/script/script_thread.rs | 6 ++- .../script/task_source/dom_manipulation.rs | 4 +- components/script/task_source/mod.rs | 1 - .../script/task_source/user_interaction.rs | 52 ++++++++++++++++--- 8 files changed, 71 insertions(+), 59 deletions(-) diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index 17a2688a910..7dea10fd0a0 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -173,15 +173,6 @@ impl<'a> GlobalRef<'a> { } } - /// `ScriptChan` used to send messages to the event loop of this global's - /// thread. - pub fn user_interaction_task_source(&self) -> Box { - match *self { - GlobalRef::Window(ref window) => window.user_interaction_task_source(), - GlobalRef::Worker(ref worker) => worker.script_chan(), - } - } - /// `ScriptChan` used to send messages to the event loop of this global's /// thread. pub fn networking_task_source(&self) -> Box { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 0e675b35b27..ed9f4826fb6 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -12,10 +12,8 @@ use dom::bindings::codegen::Bindings::HTMLInputElementBinding; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods; use dom::bindings::error::{Error, ErrorResult}; -use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, LayoutJS, Root, RootedReference}; -use dom::bindings::refcounted::Trusted; use dom::document::Document; use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers, LayoutElementHelpers}; use dom::event::{Event, EventBubbles, EventCancelable}; @@ -31,9 +29,7 @@ use dom::nodelist::NodeList; use dom::validation::Validatable; use dom::virtualmethods::VirtualMethods; use msg::constellation_msg::ConstellationChan; -use script_runtime::CommonScriptMsg; -use script_runtime::ScriptThreadEventCategory::InputEvent; -use script_thread::Runnable; +use script_runtime::ScriptChan; use script_traits::ScriptMsg as ConstellationMsg; use std::borrow::ToOwned; use std::cell::Cell; @@ -919,7 +915,13 @@ impl VirtualMethods for HTMLInputElement { self.value_changed.set(true); if event.IsTrusted() { - ChangeEventRunnable::send(self.upcast::()); + let window = window_from_node(self); + let task_source = window.user_interaction_task_source(); + let _ = task_source.queue_event( + &self.upcast(), + atom!("input"), + EventBubbles::Bubbles, + EventCancelable::NotCancelable); } self.upcast::().dirty(NodeDamage::OtherNodeDamage); @@ -1148,31 +1150,3 @@ impl Activatable for HTMLInputElement { } } } - -pub struct ChangeEventRunnable { - element: Trusted, -} - -impl ChangeEventRunnable { - pub fn send(node: &Node) { - let handler = Trusted::new(node); - let dispatcher = ChangeEventRunnable { - element: handler, - }; - let chan = window_from_node(node).user_interaction_task_source(); - let _ = chan.send(CommonScriptMsg::RunnableMsg(InputEvent, box dispatcher)); - } -} - -impl Runnable for ChangeEventRunnable { - fn handler(self: Box) { - let target = self.element.root(); - let window = window_from_node(target.r()); - let window = window.r(); - let event = Event::new(GlobalRef::Window(window), - atom!("input"), - EventBubbles::Bubbles, - EventCancelable::NotCancelable); - target.upcast::().dispatch_event(&event); - } -} diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index 597c9a2bb60..44e331f4a9d 100644 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -14,18 +14,18 @@ use dom::bindings::reflector::{Reflectable}; use dom::document::Document; use dom::element::RawLayoutElementHelpers; use dom::element::{AttributeMutation, Element}; -use dom::event::{Event}; +use dom::event::{Event, EventBubbles, EventCancelable}; use dom::htmlelement::HTMLElement; use dom::htmlfieldsetelement::HTMLFieldSetElement; use dom::htmlformelement::{FormControl, HTMLFormElement}; -use dom::htmlinputelement::ChangeEventRunnable; use dom::keyboardevent::KeyboardEvent; use dom::node::{ChildrenMutation, Node, NodeDamage, UnbindContext}; -use dom::node::{document_from_node}; +use dom::node::{document_from_node, window_from_node}; use dom::nodelist::NodeList; use dom::validation::Validatable; use dom::virtualmethods::VirtualMethods; use msg::constellation_msg::ConstellationChan; +use script_runtime::ScriptChan; use script_traits::ScriptMsg as ConstellationMsg; use std::cell::Cell; use std::ops::Range; @@ -373,7 +373,13 @@ impl VirtualMethods for HTMLTextAreaElement { self.value_changed.set(true); if event.IsTrusted() { - ChangeEventRunnable::send(self.upcast::()); + let window = window_from_node(self); + let task_source = window.user_interaction_task_source(); + let _ = task_source.queue_event( + &self.upcast(), + atom!("input"), + EventBubbles::Bubbles, + EventCancelable::NotCancelable); } self.upcast::().dirty(NodeDamage::OtherNodeDamage); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index dc6fb8704e8..4d60facbe84 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -277,7 +277,7 @@ impl Window { self.dom_manipulation_task_source.clone() } - pub fn user_interaction_task_source(&self) -> Box { + pub fn user_interaction_task_source(&self) -> UserInteractionTaskSource { self.user_interaction_task_source.clone() } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 5891bd35c7c..d615a937fcf 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -100,7 +100,7 @@ use task_source::dom_manipulation::{DOMManipulationTaskSource, DOMManipulationTa use task_source::file_reading::FileReadingTaskSource; use task_source::history_traversal::HistoryTraversalTaskSource; use task_source::networking::NetworkingTaskSource; -use task_source::user_interaction::UserInteractionTaskSource; +use task_source::user_interaction::{UserInteractionTaskSource, UserInteractionTask}; use time::Tm; use url::{Url, Position}; use util::opts; @@ -222,6 +222,8 @@ pub enum MainThreadScriptMsg { Navigate(PipelineId, LoadData), /// Tasks that originate from the DOM manipulation task source DOMManipulation(DOMManipulationTask), + /// Tasks that originate from the user interaction task source + UserInteraction(UserInteractionTask), } impl OpaqueSender for Box { @@ -934,6 +936,8 @@ impl ScriptThread { self.collect_reports(reports_chan), MainThreadScriptMsg::DOMManipulation(task) => task.handle_task(self), + MainThreadScriptMsg::UserInteraction(task) => + task.handle_task(), } } diff --git a/components/script/task_source/dom_manipulation.rs b/components/script/task_source/dom_manipulation.rs index de3fc19710e..05b0722f889 100644 --- a/components/script/task_source/dom_manipulation.rs +++ b/components/script/task_source/dom_manipulation.rs @@ -18,8 +18,10 @@ impl TaskSource for DOMManipulationTaskSource { fn queue(&self, msg: DOMManipulationTask) -> Result<(), ()> { self.0.send(MainThreadScriptMsg::DOMManipulation(msg)).map_err(|_| ()) } +} - fn clone(&self) -> Box + Send> { +impl DOMManipulationTaskSource { + pub fn clone(&self) -> Box + Send> { box DOMManipulationTaskSource((&self.0).clone()) } } diff --git a/components/script/task_source/mod.rs b/components/script/task_source/mod.rs index 74dd7347e9e..fe2d159b04a 100644 --- a/components/script/task_source/mod.rs +++ b/components/script/task_source/mod.rs @@ -12,5 +12,4 @@ use std::result::Result; pub trait TaskSource { fn queue(&self, msg: T) -> Result<(), ()>; - fn clone(&self) -> Box + Send>; } diff --git a/components/script/task_source/user_interaction.rs b/components/script/task_source/user_interaction.rs index 8f79b8ddaed..59d7d728185 100644 --- a/components/script/task_source/user_interaction.rs +++ b/components/script/task_source/user_interaction.rs @@ -2,19 +2,55 @@ * 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 script_runtime::{CommonScriptMsg, ScriptChan}; +use dom::bindings::refcounted::Trusted; +use dom::event::{EventBubbles, EventCancelable}; +use dom::eventtarget::EventTarget; +use script_runtime::ScriptChan; use script_thread::MainThreadScriptMsg; +use std::result::Result; use std::sync::mpsc::Sender; +use string_cache::Atom; +use task_source::TaskSource; #[derive(JSTraceable)] pub struct UserInteractionTaskSource(pub Sender); -impl ScriptChan for UserInteractionTaskSource { - fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> { - self.0.send(MainThreadScriptMsg::Common(msg)).map_err(|_| ()) - } - - fn clone(&self) -> Box { - box UserInteractionTaskSource((&self.0).clone()) +impl TaskSource for UserInteractionTaskSource { + fn queue(&self, msg: UserInteractionTask) -> Result<(), ()> { + self.0.send(MainThreadScriptMsg::UserInteraction(msg)).map_err(|_| ()) + } +} + +impl UserInteractionTaskSource { + pub fn queue_event(&self, + target: &EventTarget, + name: Atom, + bubbles: EventBubbles, + cancelable: EventCancelable) { + let target = Trusted::new(target); + let _ = self.0.send(MainThreadScriptMsg::UserInteraction(UserInteractionTask::FireEvent( + target, name, bubbles, cancelable))); + } + + pub fn clone(&self) -> UserInteractionTaskSource { + UserInteractionTaskSource((&self.0).clone()) + } +} + +pub enum UserInteractionTask { + // https://dom.spec.whatwg.org/#concept-event-fire + FireEvent(Trusted, Atom, EventBubbles, EventCancelable), +} + +impl UserInteractionTask { + pub fn handle_task(self) { + use self::UserInteractionTask::*; + + match self { + FireEvent(element, name, bubbles, cancelable) => { + let target = element.root(); + target.fire_event(&*name, bubbles, cancelable); + } + } } } From f60de52aae628a114935ec5e7272596fd798fa1a Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Mon, 25 Apr 2016 20:05:55 -0400 Subject: [PATCH 4/4] Fire 'select' event in SetSelectionRange --- components/script/dom/htmlinputelement.rs | 9 +++++-- components/script/dom/htmltextareaelement.rs | 9 +++++-- .../textfieldselection-setSelectionRange.html | 26 +++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index ed9f4826fb6..a226a2e224c 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -545,6 +545,12 @@ impl HTMLInputElementMethods for HTMLInputElement { let direction = direction.map_or(SelectionDirection::None, |d| SelectionDirection::from(d)); self.textinput.borrow_mut().selection_direction = direction; self.textinput.borrow_mut().set_selection_range(start, end); + let window = window_from_node(self); + let _ = window.user_interaction_task_source().queue_event( + &self.upcast(), + atom!("select"), + EventBubbles::Bubbles, + EventCancelable::NotCancelable); self.upcast::().dirty(NodeDamage::OtherNodeDamage); } } @@ -916,8 +922,7 @@ impl VirtualMethods for HTMLInputElement { if event.IsTrusted() { let window = window_from_node(self); - let task_source = window.user_interaction_task_source(); - let _ = task_source.queue_event( + let _ = window.user_interaction_task_source().queue_event( &self.upcast(), atom!("input"), EventBubbles::Bubbles, diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index 44e331f4a9d..122d73e8553 100644 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -256,6 +256,12 @@ impl HTMLTextAreaElementMethods for HTMLTextAreaElement { let direction = direction.map_or(SelectionDirection::None, |d| SelectionDirection::from(d)); self.textinput.borrow_mut().selection_direction = direction; self.textinput.borrow_mut().set_selection_range(start, end); + let window = window_from_node(self); + let _ = window.user_interaction_task_source().queue_event( + &self.upcast(), + atom!("select"), + EventBubbles::Bubbles, + EventCancelable::NotCancelable); self.upcast::().dirty(NodeDamage::OtherNodeDamage); } } @@ -374,8 +380,7 @@ impl VirtualMethods for HTMLTextAreaElement { if event.IsTrusted() { let window = window_from_node(self); - let task_source = window.user_interaction_task_source(); - let _ = task_source.queue_event( + let _ = window.user_interaction_task_source().queue_event( &self.upcast(), atom!("input"), EventBubbles::Bubbles, diff --git a/tests/wpt/web-platform-tests/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html b/tests/wpt/web-platform-tests/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html index 06915a81a8b..5f525736201 100644 --- a/tests/wpt/web-platform-tests/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html +++ b/tests/wpt/web-platform-tests/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html @@ -116,6 +116,19 @@ test(function() { },'input setSelectionRange(undefined,1)'); },"test of input.setSelectionRange"); +async_test(function() { + var q = false; + var input = document.getElementById("a"); + input.addEventListener("select", this.step_func_done(function(e) { + assert_true(q, "event should be queued"); + assert_true(e.isTrusted, "event is trusted"); + assert_true(e.bubbles, "event bubbles"); + assert_false(e.cancelable, "event is not cancelable"); + })); + input.setSelectionRange(0, 1); + q = true; +}, "input setSelectionRange fires a select event"); + test(function() { var textarea = document.getElementById("b"); test(function() { @@ -221,4 +234,17 @@ test(function() { assert_equals(textarea.selectionEnd, 1, "element.selectionStart should be 1"); },'textarea setSelectionRange(undefined,1)'); },"test of textarea.setSelectionRange"); + +async_test(function() { + var q = false; + var textarea = document.getElementById("b"); + textarea.addEventListener("select", this.step_func_done(function(e) { + assert_true(q, "event should be queued"); + assert_true(e.isTrusted, "event is trusted"); + assert_true(e.bubbles, "event bubbles"); + assert_false(e.cancelable, "event is not cancelable"); + })); + textarea.setSelectionRange(0, 1); + q = true; +}, "textarea setSelectionRange fires a select event");