mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Forward WebDriver CompositionEvent
Dispatch composition events in JS. Insert characters from composition events to text input. CompositionEvents currently can only be created by WebDriver and not by embedders.
This commit is contained in:
parent
1ac6f435c8
commit
b936fea79d
12 changed files with 118 additions and 26 deletions
|
@ -64,7 +64,7 @@ image = "0.20"
|
|||
ipc-channel = "0.11"
|
||||
itertools = "0.7.6"
|
||||
jstraceable_derive = {path = "../jstraceable_derive"}
|
||||
keyboard-types = "0.4.3"
|
||||
keyboard-types = "0.4.4"
|
||||
lazy_static = "1"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
|
|
|
@ -59,6 +59,10 @@ impl CompositionEvent {
|
|||
);
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &str {
|
||||
&*self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl CompositionEventMethods for CompositionEvent {
|
||||
|
|
|
@ -38,6 +38,7 @@ use crate::dom::bindings::xmlname::{
|
|||
};
|
||||
use crate::dom::closeevent::CloseEvent;
|
||||
use crate::dom::comment::Comment;
|
||||
use crate::dom::compositionevent::CompositionEvent;
|
||||
use crate::dom::cssstylesheet::CSSStyleSheet;
|
||||
use crate::dom::customelementregistry::CustomElementDefinition;
|
||||
use crate::dom::customevent::CustomEvent;
|
||||
|
@ -1465,6 +1466,37 @@ impl Document {
|
|||
self.window.reflow(ReflowGoal::Full, ReflowReason::KeyEvent);
|
||||
}
|
||||
|
||||
pub fn dispatch_composition_event(
|
||||
&self,
|
||||
composition_event: ::keyboard_types::CompositionEvent,
|
||||
) {
|
||||
// spec: https://w3c.github.io/uievents/#compositionstart
|
||||
// spec: https://w3c.github.io/uievents/#compositionupdate
|
||||
// spec: https://w3c.github.io/uievents/#compositionend
|
||||
// > Event.target : focused element processing the composition
|
||||
let focused = self.get_focused_element();
|
||||
let target = if let Some(elem) = &focused {
|
||||
elem.upcast()
|
||||
} else {
|
||||
// Event is only dispatched if there is a focused element.
|
||||
return;
|
||||
};
|
||||
|
||||
let cancelable = composition_event.state == keyboard_types::CompositionState::Start;
|
||||
|
||||
let compositionevent = CompositionEvent::new(
|
||||
&self.window,
|
||||
DOMString::from(composition_event.state.to_string()),
|
||||
true,
|
||||
cancelable,
|
||||
Some(&self.window),
|
||||
0,
|
||||
DOMString::from(composition_event.data),
|
||||
);
|
||||
let event = compositionevent.upcast::<Event>();
|
||||
event.fire(target);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#converting-nodes-into-a-node
|
||||
pub fn node_from_nodes_and_strings(
|
||||
&self,
|
||||
|
|
|
@ -17,6 +17,7 @@ use crate::dom::bindings::inheritance::Castable;
|
|||
use crate::dom::bindings::reflector::DomObject;
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, RootedReference};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::compositionevent::CompositionEvent;
|
||||
use crate::dom::document::Document;
|
||||
use crate::dom::element::{
|
||||
AttributeMutation, Element, LayoutElementHelpers, RawLayoutElementHelpers,
|
||||
|
@ -1526,6 +1527,23 @@ impl VirtualMethods for HTMLInputElement {
|
|||
&window,
|
||||
);
|
||||
}
|
||||
} else if (event.type_() == atom!("compositionstart") ||
|
||||
event.type_() == atom!("compositionupdate") ||
|
||||
event.type_() == atom!("compositionend")) &&
|
||||
self.input_type().is_textual_or_password()
|
||||
{
|
||||
// TODO: Update DOM on start and continue
|
||||
// and generally do proper CompositionEvent handling.
|
||||
if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
|
||||
if event.type_() == atom!("compositionend") {
|
||||
let _ = self
|
||||
.textinput
|
||||
.borrow_mut()
|
||||
.handle_compositionend(compositionevent);
|
||||
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||
}
|
||||
event.mark_as_handled();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::dom::bindings::error::ErrorResult;
|
|||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::compositionevent::CompositionEvent;
|
||||
use crate::dom::document::Document;
|
||||
use crate::dom::element::RawLayoutElementHelpers;
|
||||
use crate::dom::element::{AttributeMutation, Element};
|
||||
|
@ -576,6 +577,22 @@ impl VirtualMethods for HTMLTextAreaElement {
|
|||
&window,
|
||||
);
|
||||
}
|
||||
} else if event.type_() == atom!("compositionstart") ||
|
||||
event.type_() == atom!("compositionupdate") ||
|
||||
event.type_() == atom!("compositionend")
|
||||
{
|
||||
// TODO: Update DOM on start and continue
|
||||
// and generally do proper CompositionEvent handling.
|
||||
if let Some(compositionevent) = event.downcast::<CompositionEvent>() {
|
||||
if event.type_() == atom!("compositionend") {
|
||||
let _ = self
|
||||
.textinput
|
||||
.borrow_mut()
|
||||
.handle_compositionend(compositionevent);
|
||||
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||
}
|
||||
event.mark_as_handled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ use profile_traits::time::{self as profile_time, profile, ProfilerCategory};
|
|||
use script_layout_interface::message::{self, Msg, NewLayoutThreadInfo, ReflowGoal};
|
||||
use script_traits::webdriver_msg::WebDriverScriptCommand;
|
||||
use script_traits::CompositorEvent::{
|
||||
KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent,
|
||||
CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent,
|
||||
};
|
||||
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
||||
use script_traits::{DiscardBrowsingContext, DocumentActivity, EventResult};
|
||||
|
@ -2881,6 +2881,14 @@ impl ScriptThread {
|
|||
};
|
||||
document.dispatch_key_event(key_event);
|
||||
},
|
||||
|
||||
CompositionEvent(composition_event) => {
|
||||
let document = match { self.documents.borrow().find_document(pipeline_id) } {
|
||||
Some(document) => document,
|
||||
None => return warn!("Message sent to closed pipeline {}.", pipeline_id),
|
||||
};
|
||||
document.dispatch_composition_event(composition_event);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
use crate::clipboard_provider::ClipboardProvider;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::compositionevent::CompositionEvent;
|
||||
use crate::dom::keyboardevent::KeyboardEvent;
|
||||
use keyboard_types::{Key, KeyState, Modifiers, ShortcutMatcher};
|
||||
use std::borrow::ToOwned;
|
||||
|
@ -831,6 +832,11 @@ impl<T: ClipboardProvider> TextInput<T> {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn handle_compositionend(&mut self, event: &CompositionEvent) -> KeyReaction {
|
||||
self.insert_string(event.data());
|
||||
KeyReaction::DispatchInput
|
||||
}
|
||||
|
||||
/// Whether the content is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.lines.len() <= 1 && self.lines.get(0).map_or(true, |line| line.is_empty())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue