mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
script: Fire selectionchange events for textcontrol elements (#44461)
Part of #7492 Testing: WPT Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
committed by
GitHub
parent
6f43bba0f4
commit
6d70fcda1b
@@ -25,6 +25,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTex
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||||
use crate::dom::bindings::error::ErrorResult;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
|
||||
@@ -33,6 +34,8 @@ use crate::dom::document::Document;
|
||||
use crate::dom::document_embedder_controls::ControlElement;
|
||||
use crate::dom::element::{AttributeMutation, Element};
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::html::htmlelement::HTMLElement;
|
||||
use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
|
||||
use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
|
||||
@@ -69,6 +72,9 @@ pub(crate) struct HTMLTextAreaElement {
|
||||
#[no_trace]
|
||||
#[conditional_malloc_size_of]
|
||||
shared_selection: SharedSelection,
|
||||
|
||||
/// <https://w3c.github.io/selection-api/#dfn-has-scheduled-selectionchange-event>
|
||||
has_scheduled_selectionchange_event: Cell<bool>,
|
||||
}
|
||||
|
||||
impl LayoutDom<'_, HTMLTextAreaElement> {
|
||||
@@ -131,6 +137,7 @@ impl HTMLTextAreaElement {
|
||||
validity_state: Default::default(),
|
||||
text_input_widget: Default::default(),
|
||||
shared_selection: Default::default(),
|
||||
has_scheduled_selectionchange_event: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +239,41 @@ impl HTMLTextAreaElement {
|
||||
self.maybe_update_shared_selection();
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/selection-api/#dfn-schedule-a-selectionchange-event>
|
||||
fn schedule_a_selection_change_event(&self) {
|
||||
// Step 1. If target's has scheduled selectionchange event is true, abort these steps.
|
||||
if self.has_scheduled_selectionchange_event.get() {
|
||||
return;
|
||||
}
|
||||
// Step 2. Set target's has scheduled selectionchange event to true.
|
||||
self.has_scheduled_selectionchange_event.set(true);
|
||||
// Step 3. Queue a task on the user interaction task source to fire a selectionchange event on target.
|
||||
let this = Trusted::new(self);
|
||||
self.owner_global()
|
||||
.task_manager()
|
||||
.user_interaction_task_source()
|
||||
.queue(
|
||||
// https://w3c.github.io/selection-api/#firing-selectionchange-event
|
||||
task!(selectionchange_task_steps: move |cx| {
|
||||
let this = this.root();
|
||||
// Step 1. Set target's has scheduled selectionchange event to false.
|
||||
this.has_scheduled_selectionchange_event.set(false);
|
||||
// Step 2. If target is an element, fire an event named selectionchange, which bubbles and not cancelable, at target.
|
||||
this.upcast::<EventTarget>().fire_event_with_params(
|
||||
atom!("selectionchange"),
|
||||
EventBubbles::Bubbles,
|
||||
EventCancelable::NotCancelable,
|
||||
EventComposed::Composed,
|
||||
CanGc::from_cx(cx),
|
||||
);
|
||||
// Step 3. Otherwise, if target is a document, fire an event named selectionchange,
|
||||
// which does not bubble and not cancelable, at target.
|
||||
//
|
||||
// n/a
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl TextControlElement for HTMLTextAreaElement {
|
||||
@@ -263,9 +305,19 @@ impl TextControlElement for HTMLTextAreaElement {
|
||||
let enabled = self.upcast::<Element>().focus_state();
|
||||
|
||||
let mut shared_selection = self.shared_selection.borrow_mut();
|
||||
if range == shared_selection.range && enabled == shared_selection.enabled {
|
||||
let range_remained_equal = range == shared_selection.range;
|
||||
if range_remained_equal && enabled == shared_selection.enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
if !range_remained_equal {
|
||||
// https://w3c.github.io/selection-api/#selectionchange-event
|
||||
// > When an input or textarea element provide a text selection and its selection changes
|
||||
// > (in either extent or direction),
|
||||
// > the user agent must schedule a selectionchange event on the element.
|
||||
self.schedule_a_selection_change_event();
|
||||
}
|
||||
|
||||
*shared_selection = ScriptSelection {
|
||||
range,
|
||||
character_range: self
|
||||
|
||||
@@ -44,6 +44,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputE
|
||||
use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
|
||||
use crate::dom::bindings::error::{Error, ErrorResult};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
|
||||
use crate::dom::bindings::str::{DOMString, USVString};
|
||||
use crate::dom::clipboardevent::{ClipboardEvent, ClipboardEventType};
|
||||
@@ -52,6 +53,7 @@ use crate::dom::document::Document;
|
||||
use crate::dom::document_embedder_controls::ControlElement;
|
||||
use crate::dom::element::{AttributeMutation, Element};
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::event::event::{EventBubbles, EventCancelable, EventComposed};
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::filelist::FileList;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
@@ -156,6 +158,9 @@ pub(crate) struct HTMLInputElement {
|
||||
validity_state: MutNullableDom<ValidityState>,
|
||||
#[no_trace]
|
||||
pending_webdriver_response: RefCell<Option<PendingWebDriverResponse>>,
|
||||
|
||||
/// <https://w3c.github.io/selection-api/#dfn-has-scheduled-selectionchange-event>
|
||||
has_scheduled_selectionchange_event: Cell<bool>,
|
||||
}
|
||||
|
||||
#[derive(JSTraceable)]
|
||||
@@ -212,6 +217,7 @@ impl HTMLInputElement {
|
||||
labels_node_list: MutNullableDom::new(None),
|
||||
validity_state: Default::default(),
|
||||
pending_webdriver_response: Default::default(),
|
||||
has_scheduled_selectionchange_event: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,6 +899,41 @@ impl HTMLInputElement {
|
||||
fn textinput_mut(&self) -> RefMut<'_, TextInput<EmbedderClipboardProvider>> {
|
||||
self.textinput.borrow_mut()
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/selection-api/#dfn-schedule-a-selectionchange-event>
|
||||
fn schedule_a_selection_change_event(&self) {
|
||||
// Step 1. If target's has scheduled selectionchange event is true, abort these steps.
|
||||
if self.has_scheduled_selectionchange_event.get() {
|
||||
return;
|
||||
}
|
||||
// Step 2. Set target's has scheduled selectionchange event to true.
|
||||
self.has_scheduled_selectionchange_event.set(true);
|
||||
// Step 3. Queue a task on the user interaction task source to fire a selectionchange event on target.
|
||||
let this = Trusted::new(self);
|
||||
self.owner_global()
|
||||
.task_manager()
|
||||
.user_interaction_task_source()
|
||||
.queue(
|
||||
// https://w3c.github.io/selection-api/#firing-selectionchange-event
|
||||
task!(selectionchange_task_steps: move |cx| {
|
||||
let this = this.root();
|
||||
// Step 1. Set target's has scheduled selectionchange event to false.
|
||||
this.has_scheduled_selectionchange_event.set(false);
|
||||
// Step 2. If target is an element, fire an event named selectionchange, which bubbles and not cancelable, at target.
|
||||
this.upcast::<EventTarget>().fire_event_with_params(
|
||||
atom!("selectionchange"),
|
||||
EventBubbles::Bubbles,
|
||||
EventCancelable::NotCancelable,
|
||||
EventComposed::Composed,
|
||||
CanGc::from_cx(cx),
|
||||
);
|
||||
// Step 3. Otherwise, if target is a document, fire an event named selectionchange,
|
||||
// which does not bubble and not cancelable, at target.
|
||||
//
|
||||
// n/a
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'dom> LayoutDom<'dom, HTMLInputElement> {
|
||||
@@ -961,10 +1002,19 @@ impl TextControlElement for HTMLInputElement {
|
||||
let enabled = self.is_textual_or_password() && self.upcast::<Element>().focus_state();
|
||||
|
||||
let mut shared_selection = self.shared_selection.borrow_mut();
|
||||
if range == shared_selection.range && enabled == shared_selection.enabled {
|
||||
let range_remained_equal = range == shared_selection.range;
|
||||
if range_remained_equal && enabled == shared_selection.enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
if !range_remained_equal {
|
||||
// https://w3c.github.io/selection-api/#selectionchange-event
|
||||
// > When an input or textarea element provide a text selection and its selection changes
|
||||
// > (in either extent or direction),
|
||||
// > the user agent must schedule a selectionchange event on the element.
|
||||
self.schedule_a_selection_change_event();
|
||||
}
|
||||
|
||||
*shared_selection = ScriptSelection {
|
||||
range,
|
||||
character_range: self
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[fire-selectionchange-event-on-textcontrol-element-on-pressing-backspace.html]
|
||||
[selectionchange event fired on an input element]
|
||||
expected: FAIL
|
||||
@@ -1,6 +0,0 @@
|
||||
[onselectionchange-on-distinct-text-controls.html]
|
||||
[selectionchange event on each input element fires independently]
|
||||
expected: FAIL
|
||||
|
||||
[selectionchange event on each textarea element fires independently]
|
||||
expected: FAIL
|
||||
@@ -1,13 +0,0 @@
|
||||
[selectionchange-bubble.html]
|
||||
expected: TIMEOUT
|
||||
[selectionchange bubbles from input]
|
||||
expected: TIMEOUT
|
||||
|
||||
[selectionchange bubbles from input when focused]
|
||||
expected: NOTRUN
|
||||
|
||||
[selectionchange bubbles from textarea]
|
||||
expected: NOTRUN
|
||||
|
||||
[selectionchange bubbles from textarea when focused]
|
||||
expected: NOTRUN
|
||||
@@ -1,3 +0,0 @@
|
||||
[selectionchange-on-shadow-dom.html]
|
||||
[selectionchange event fired on a shadow dom bubble to the document]
|
||||
expected: FAIL
|
||||
@@ -1,132 +0,0 @@
|
||||
[selectionchange.html]
|
||||
[Modifying selectionStart value of the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Modifying selectionEnd value of the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setSelectionRange() on the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling select() on the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() on the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selectionStart value twice on the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selectionEnd value twice on the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selection range twice on the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling select() twice on the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() after select() on the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() repeatedly on the input element]
|
||||
expected: FAIL
|
||||
|
||||
[Modifying selectionStart value of the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Modifying selectionEnd value of the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setSelectionRange() on the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling select() on the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() on the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selectionStart value twice on the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selectionEnd value twice on the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selection range twice on the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling select() twice on the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() after select() on the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() repeatedly on the disconnected input element]
|
||||
expected: FAIL
|
||||
|
||||
[Modifying selectionStart value of the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Modifying selectionEnd value of the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setSelectionRange() on the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling select() on the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() on the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selectionStart value twice on the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selectionEnd value twice on the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selection range twice on the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling select() twice on the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() after select() on the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() repeatedly on the textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Modifying selectionStart value of the disconnected textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Modifying selectionEnd value of the disconnected textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setSelectionRange() on the disconnected textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling select() on the disconnected textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() on the disconnected textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selectionStart value twice on the disconnected textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selectionEnd value twice on the disconnected textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Setting the same selection range twice on the disconnected textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling select() twice on the disconnected textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() after select() on the disconnected textarea element]
|
||||
expected: FAIL
|
||||
|
||||
[Calling setRangeText() repeatedly on the disconnected textarea element]
|
||||
expected: FAIL
|
||||
Reference in New Issue
Block a user