Commit 7a680366 authored by Joel Einbinder's avatar Joel Einbinder Committed by Commit Bot

DevTools: Add a protocol method to insertText

This method emulates sending text into the page that doesn't come from
a key press. This can be used to simulate text from an emoji keyboard or
an IME.

Change-Id: I643a0ac2ba2faa4988975d87641414b6f3896301
Reviewed-on: https://chromium-review.googlesource.com/1128398Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarDave Tapuska <dtapuska@chromium.org>
Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Commit-Queue: Joel Einbinder <einbinder@chromium.org>
Cr-Commit-Position: refs/heads/master@{#578934}
parent b82f4477
......@@ -916,7 +916,7 @@ void BrowserPluginGuest::OnImeCommitText(
->GetWidget()
->GetWidgetInputHandler()
->ImeCommitText(text, ui_ime_text_spans, replacement_range,
relative_cursor_pos);
relative_cursor_pos, base::OnceClosure());
}
void BrowserPluginGuest::OnImeFinishComposingText(
......
......@@ -24,6 +24,7 @@
#include "ui/events/gesture_detection/gesture_provider_config_helper.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/range/range.h"
namespace content {
namespace protocol {
......@@ -535,6 +536,31 @@ void InputHandler::DispatchKeyEvent(
EnsureInjector(widget_host)->InjectKeyboardEvent(event, std::move(callback));
}
void InputHandler::InsertText(const std::string& text,
std::unique_ptr<InsertTextCallback> callback) {
base::string16 text16 = base::UTF8ToUTF16(text);
base::OnceClosure closure =
base::BindOnce(&InsertTextCallback::sendSuccess, std::move(callback));
if (!host_ || !host_->GetRenderWidgetHost()) {
callback->sendFailure(Response::InternalError());
return;
}
RenderWidgetHostImpl* widget_host = host_->GetRenderWidgetHost();
if (!host_->GetParent() && widget_host->delegate()) {
RenderWidgetHostImpl* target_host =
widget_host->delegate()->GetFocusedRenderWidgetHost(widget_host);
if (target_host)
widget_host = target_host;
}
widget_host->Focus();
widget_host->GetWidgetInputHandler()->ImeCommitText(
text16, std::vector<ui::ImeTextSpan>(), gfx::Range::InvalidRange(), 0,
std::move(closure));
}
void InputHandler::DispatchMouseEvent(
const std::string& event_type,
double x,
......
......@@ -56,6 +56,9 @@ class InputHandler : public DevToolsDomainHandler, public Input::Backend {
Maybe<int> location,
std::unique_ptr<DispatchKeyEventCallback> callback) override;
void InsertText(const std::string& text,
std::unique_ptr<InsertTextCallback> callback) override;
void DispatchMouseEvent(
const std::string& type,
double x,
......
......@@ -24,7 +24,7 @@
},
{
"domain": "Input",
"async": ["dispatchKeyEvent", "dispatchMouseEvent", "dispatchTouchEvent", "synthesizePinchGesture", "synthesizeScrollGesture", "synthesizeTapGesture"]
"async": ["dispatchKeyEvent", "insertText", "dispatchMouseEvent", "dispatchTouchEvent", "synthesizePinchGesture", "synthesizeScrollGesture", "synthesizeTapGesture"]
},
{
"domain": "Inspector"
......
......@@ -292,7 +292,8 @@ class UnboundWidgetInputHandler : public mojom::WidgetInputHandler {
void ImeCommitText(const base::string16& text,
const std::vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& range,
int32_t relative_cursor_position) override {
int32_t relative_cursor_position,
ImeCommitTextCallback callback) override {
DLOG(WARNING) << "Input request on unbound interface";
}
void ImeFinishComposingText(bool keep_selection) override {
......@@ -1982,8 +1983,9 @@ void RenderWidgetHostImpl::ImeCommitText(
const std::vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& replacement_range,
int relative_cursor_pos) {
GetWidgetInputHandler()->ImeCommitText(
text, ime_text_spans, replacement_range, relative_cursor_pos);
GetWidgetInputHandler()->ImeCommitText(text, ime_text_spans,
replacement_range, relative_cursor_pos,
base::OnceClosure());
}
void RenderWidgetHostImpl::ImeFinishComposingText(bool keep_selection) {
......
......@@ -229,7 +229,7 @@ interface WidgetInputHandler {
// moves the cursor.
ImeCommitText(mojo_base.mojom.String16 text,
array<ui.mojom.ImeTextSpan> ime_text_spans,
gfx.mojom.Range range, int32 relative_cursor_position);
gfx.mojom.Range range, int32 relative_cursor_position) => ();
// This message inserts the ongoing composition.
ImeFinishComposingText(bool keep_selection);
......
......@@ -19,6 +19,7 @@
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/public/platform/web_coalesced_input_event.h"
#include "third_party/blink/public/platform/web_keyboard_event.h"
#include "third_party/blink/public/web/web_ime_text_span.h"
#include "third_party/blink/public/web/web_local_frame.h"
namespace content {
......@@ -100,15 +101,32 @@ void WidgetInputHandlerImpl::ImeSetComposition(
range, start, end));
}
static void ImeCommitTextOnMainThread(
base::WeakPtr<RenderWidget> render_widget,
scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner,
const base::string16& text,
const std::vector<blink::WebImeTextSpan>& ime_text_spans,
const gfx::Range& range,
int32_t relative_cursor_position,
WidgetInputHandlerImpl::ImeCommitTextCallback callback) {
if (render_widget) {
render_widget->OnImeCommitText(text, ime_text_spans, range,
relative_cursor_position);
}
callback_task_runner->PostTask(FROM_HERE, std::move(callback));
}
void WidgetInputHandlerImpl::ImeCommitText(
const base::string16& text,
const std::vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& range,
int32_t relative_cursor_position) {
int32_t relative_cursor_position,
ImeCommitTextCallback callback) {
RunOnMainThread(
base::BindOnce(&RenderWidget::OnImeCommitText, render_widget_, text,
base::BindOnce(&ImeCommitTextOnMainThread, render_widget_,
base::ThreadTaskRunnerHandle::Get(), text,
ConvertUiImeTextSpansToBlinkImeTextSpans(ime_text_spans),
range, relative_cursor_position));
range, relative_cursor_position, std::move(callback)));
}
void WidgetInputHandlerImpl::ImeFinishComposingText(bool keep_selection) {
......
......@@ -45,7 +45,8 @@ class WidgetInputHandlerImpl : public mojom::WidgetInputHandler {
void ImeCommitText(const base::string16& text,
const std::vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& range,
int32_t relative_cursor_position) override;
int32_t relative_cursor_position,
ImeCommitTextCallback callback) override;
void ImeFinishComposingText(bool keep_selection) override;
void RequestTextInputStateUpdate() override;
void RequestCompositionUpdates(bool immediate_request,
......
......@@ -61,10 +61,12 @@ void MockWidgetInputHandler::ImeCommitText(
const base::string16& text,
const std::vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& range,
int32_t relative_cursor_position) {
int32_t relative_cursor_position,
ImeCommitTextCallback callback) {
dispatched_messages_.emplace_back(std::make_unique<DispatchedIMEMessage>(
"CommitText", text, ime_text_spans, range, relative_cursor_position,
relative_cursor_position));
std::move(callback).Run();
}
void MockWidgetInputHandler::ImeFinishComposingText(bool keep_selection) {
......
......@@ -205,7 +205,8 @@ class MockWidgetInputHandler : public mojom::WidgetInputHandler {
void ImeCommitText(const base::string16& text,
const std::vector<ui::ImeTextSpan>& ime_text_spans,
const gfx::Range& range,
int32_t relative_cursor_position) override;
int32_t relative_cursor_position,
ImeCommitTextCallback callback) override;
void ImeFinishComposingText(bool keep_selection) override;
void RequestTextInputStateUpdate() override;
void RequestCompositionUpdates(bool immediate_request,
......
Tests that Input.insertText waits for JavaScript event handlers to finish.
Dispatching event
inputEventPromise has not resolved yet
Paused on debugger statement
Input Value: Hello World! ❤️🚀
Resumed
Recieved ack for input event
(async function(testRunner) {
let {page, session, dp} = await testRunner.startBlank(`Tests that Input.insertText waits for JavaScript event handlers to finish.`);
await session.evaluate(`
window.input = document.createElement('input');
document.body.appendChild(window.input);
window.input.focus();
window.input.addEventListener('input', pauseEvent);
function pauseEvent(event) {
debugger;
}
`);
function dumpError(message) {
if (message.error)
testRunner.log('Error: ' + message.error.message);
}
let resolved = false;
await dp.Debugger.enable();
testRunner.log('Dispatching event');
let inputEventPromise = dp.Input.insertText({
text: 'Hello World! ❤️🚀'
});
inputEventPromise.then(() => resolved = true);
await dp.Debugger.oncePaused();
await Promise.resolve(); // just in case
testRunner.log(resolved ? `inputEventPromise was resolved too early` : `inputEventPromise has not resolved yet`)
testRunner.log('Paused on debugger statement');
testRunner.log(`Input Value: ${await session.evaluate(`window.input.value`)}`);
await dp.Debugger.resume();
testRunner.log('Resumed');
dumpError(await inputEventPromise);
testRunner.log(`Recieved ack for input event`);
testRunner.completeTest();
})
......@@ -2909,6 +2909,13 @@ domain Input
# 0).
optional integer location
# This method emulates inserting text that doesn't come from a key press,
# for example an emoji keyboard or an IME.
experimental command insertText
parameters
# The text to insert.
string text
# Dispatches a mouse event to the page.
command dispatchMouseEvent
parameters
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment