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:
Pyfisch 2018-11-17 17:02:31 +01:00
parent 1ac6f435c8
commit b936fea79d
12 changed files with 118 additions and 26 deletions

View file

@ -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"

View file

@ -59,6 +59,10 @@ impl CompositionEvent {
);
Ok(event)
}
pub fn data(&self) -> &str {
&*self.data
}
}
impl CompositionEventMethods for CompositionEvent {

View file

@ -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,

View file

@ -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();
}
}
}
}

View file

@ -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();
}
}
}

View file

@ -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);
},
}
}

View file

@ -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())