blob: 77eb8546301e54e094adc8372a95d085507646ae [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/widget/input/frame_widget_input_handler_impl.h"
#include <utility>
#include "base/bind.h"
#include "base/check.h"
#include "base/memory/weak_ptr.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/renderer/platform/widget/frame_widget.h"
#include "third_party/blink/renderer/platform/widget/input/ime_event_guard.h"
#include "third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h"
#include "third_party/blink/renderer/platform/widget/widget_base.h"
#include "third_party/blink/renderer/platform/widget/widget_base_client.h"
namespace blink {
FrameWidgetInputHandlerImpl::FrameWidgetInputHandlerImpl(
base::WeakPtr<WidgetBase> widget,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler>
frame_widget_input_handler,
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner,
scoped_refptr<MainThreadEventQueue> input_event_queue)
: widget_(std::move(widget)),
main_thread_frame_widget_input_handler_(
std::move(frame_widget_input_handler)),
input_event_queue_(input_event_queue),
main_thread_task_runner_(main_thread_task_runner) {}
FrameWidgetInputHandlerImpl::~FrameWidgetInputHandlerImpl() = default;
void FrameWidgetInputHandlerImpl::RunOnMainThread(base::OnceClosure closure) {
if (input_event_queue_) {
input_event_queue_->QueueClosure(std::move(closure));
} else {
std::move(closure).Run();
}
}
void FrameWidgetInputHandlerImpl::AddImeTextSpansToExistingText(
uint32_t start,
uint32_t end,
const Vector<ui::ImeTextSpan>& ui_ime_text_spans) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<WidgetBase> widget,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
uint32_t start, uint32_t end,
const Vector<ui::ImeTextSpan>& ui_ime_text_spans) {
DCHECK_EQ(!!widget, !!handler);
if (!widget)
return;
ImeEventGuard guard(widget);
handler->AddImeTextSpansToExistingText(start, end, ui_ime_text_spans);
},
widget_, main_thread_frame_widget_input_handler_, start, end,
ui_ime_text_spans));
}
void FrameWidgetInputHandlerImpl::ClearImeTextSpansByType(
uint32_t start,
uint32_t end,
ui::ImeTextSpan::Type type) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<WidgetBase> widget,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
uint32_t start, uint32_t end, ui::ImeTextSpan::Type type) {
DCHECK_EQ(!!widget, !!handler);
if (!widget)
return;
ImeEventGuard guard(widget);
handler->ClearImeTextSpansByType(start, end, type);
},
widget_, main_thread_frame_widget_input_handler_, start, end, type));
}
void FrameWidgetInputHandlerImpl::SetCompositionFromExistingText(
int32_t start,
int32_t end,
const Vector<ui::ImeTextSpan>& ui_ime_text_spans) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<WidgetBase> widget,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
int32_t start, int32_t end,
const Vector<ui::ImeTextSpan>& ui_ime_text_spans) {
DCHECK_EQ(!!widget, !!handler);
if (!widget)
return;
ImeEventGuard guard(widget);
handler->SetCompositionFromExistingText(start, end, ui_ime_text_spans);
},
widget_, main_thread_frame_widget_input_handler_, start, end,
ui_ime_text_spans));
}
void FrameWidgetInputHandlerImpl::ExtendSelectionAndDelete(int32_t before,
int32_t after) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
int32_t before, int32_t after) {
if (handler)
handler->ExtendSelectionAndDelete(before, after);
},
main_thread_frame_widget_input_handler_, before, after));
}
void FrameWidgetInputHandlerImpl::DeleteSurroundingText(int32_t before,
int32_t after) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
int32_t before, int32_t after) {
if (handler)
handler->DeleteSurroundingText(before, after);
},
main_thread_frame_widget_input_handler_, before, after));
}
void FrameWidgetInputHandlerImpl::DeleteSurroundingTextInCodePoints(
int32_t before,
int32_t after) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
int32_t before, int32_t after) {
if (handler)
handler->DeleteSurroundingTextInCodePoints(before, after);
},
main_thread_frame_widget_input_handler_, before, after));
}
void FrameWidgetInputHandlerImpl::SetEditableSelectionOffsets(int32_t start,
int32_t end) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<WidgetBase> widget,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
int32_t start, int32_t end) {
DCHECK_EQ(!!widget, !!handler);
if (!widget)
return;
HandlingState handling_state(widget, UpdateState::kIsSelectingRange);
handler->SetEditableSelectionOffsets(start, end);
},
widget_, main_thread_frame_widget_input_handler_, start, end));
}
void FrameWidgetInputHandlerImpl::ExecuteEditCommand(const String& command,
const String& value) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
const String& command, const String& value) {
if (handler)
handler->ExecuteEditCommand(command, value);
},
main_thread_frame_widget_input_handler_, command.IsolatedCopy(),
value.IsolatedCopy()));
}
void FrameWidgetInputHandlerImpl::Undo() {
RunOnMainThread(base::BindOnce(
&FrameWidgetInputHandlerImpl::ExecuteCommandOnMainThread, widget_,
main_thread_frame_widget_input_handler_, "Undo", UpdateState::kNone));
}
void FrameWidgetInputHandlerImpl::Redo() {
RunOnMainThread(base::BindOnce(
&FrameWidgetInputHandlerImpl::ExecuteCommandOnMainThread, widget_,
main_thread_frame_widget_input_handler_, "Redo", UpdateState::kNone));
}
void FrameWidgetInputHandlerImpl::Cut() {
RunOnMainThread(
base::BindOnce(&FrameWidgetInputHandlerImpl::ExecuteCommandOnMainThread,
widget_, main_thread_frame_widget_input_handler_, "Cut",
UpdateState::kIsSelectingRange));
}
void FrameWidgetInputHandlerImpl::Copy() {
RunOnMainThread(
base::BindOnce(&FrameWidgetInputHandlerImpl::ExecuteCommandOnMainThread,
widget_, main_thread_frame_widget_input_handler_, "Copy",
UpdateState::kIsSelectingRange));
}
void FrameWidgetInputHandlerImpl::CopyToFindPboard() {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler) {
if (handler)
handler->CopyToFindPboard();
},
main_thread_frame_widget_input_handler_));
}
void FrameWidgetInputHandlerImpl::Paste() {
RunOnMainThread(
base::BindOnce(&FrameWidgetInputHandlerImpl::ExecuteCommandOnMainThread,
widget_, main_thread_frame_widget_input_handler_, "Paste",
UpdateState::kIsPasting));
}
void FrameWidgetInputHandlerImpl::PasteAndMatchStyle() {
RunOnMainThread(
base::BindOnce(&FrameWidgetInputHandlerImpl::ExecuteCommandOnMainThread,
widget_, main_thread_frame_widget_input_handler_,
"PasteAndMatchStyle", UpdateState::kIsPasting));
}
void FrameWidgetInputHandlerImpl::Replace(const String& word) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
const String& word) {
if (handler)
handler->Replace(word);
},
main_thread_frame_widget_input_handler_, word.IsolatedCopy()));
}
void FrameWidgetInputHandlerImpl::ReplaceMisspelling(const String& word) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
const String& word) {
if (handler)
handler->ReplaceMisspelling(word);
},
main_thread_frame_widget_input_handler_, word.IsolatedCopy()));
}
void FrameWidgetInputHandlerImpl::Delete() {
RunOnMainThread(base::BindOnce(
&FrameWidgetInputHandlerImpl::ExecuteCommandOnMainThread, widget_,
main_thread_frame_widget_input_handler_, "Delete", UpdateState::kNone));
}
void FrameWidgetInputHandlerImpl::SelectAll() {
RunOnMainThread(
base::BindOnce(&FrameWidgetInputHandlerImpl::ExecuteCommandOnMainThread,
widget_, main_thread_frame_widget_input_handler_,
"SelectAll", UpdateState::kIsSelectingRange));
}
void FrameWidgetInputHandlerImpl::CollapseSelection() {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<WidgetBase> widget,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler) {
DCHECK_EQ(!!widget, !!handler);
if (!widget)
return;
HandlingState handling_state(widget, UpdateState::kIsSelectingRange);
handler->CollapseSelection();
},
widget_, main_thread_frame_widget_input_handler_));
}
void FrameWidgetInputHandlerImpl::SelectRange(const gfx::Point& base,
const gfx::Point& extent) {
// TODO(dtapuska): This event should be coalesced. Chrome IPC uses
// one outstanding event and an ACK to handle coalescing on the browser
// side. We should be able to clobber them in the main thread event queue.
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<WidgetBase> widget,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
const gfx::Point& base, const gfx::Point& extent) {
DCHECK_EQ(!!widget, !!handler);
if (!widget)
return;
HandlingState handling_state(widget, UpdateState::kIsSelectingRange);
handler->SelectRange(base, extent);
},
widget_, main_thread_frame_widget_input_handler_, base, extent));
}
#if defined(OS_ANDROID)
void FrameWidgetInputHandlerImpl::SelectWordAroundCaret(
SelectWordAroundCaretCallback callback) {
// If the mojom channel is registered with compositor thread, we have to run
// the callback on compositor thread. Otherwise run it on main thread. Mojom
// requires the callback runs on the same thread.
if (!main_thread_task_runner_->BelongsToCurrentThread()) {
callback = base::BindOnce(
[](scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner,
SelectWordAroundCaretCallback callback, bool did_select,
int start_adjust, int end_adjust) {
callback_task_runner->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), did_select,
start_adjust, end_adjust));
},
base::ThreadTaskRunnerHandle::Get(), std::move(callback));
}
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
SelectWordAroundCaretCallback callback) {
if (handler) {
handler->SelectWordAroundCaret(std::move(callback));
} else {
std::move(callback).Run(false, 0, 0);
}
},
main_thread_frame_widget_input_handler_, std::move(callback)));
}
#endif // defined(OS_ANDROID)
void FrameWidgetInputHandlerImpl::AdjustSelectionByCharacterOffset(
int32_t start,
int32_t end,
blink::mojom::SelectionMenuBehavior selection_menu_behavior) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<WidgetBase> widget,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
int32_t start, int32_t end,
blink::mojom::SelectionMenuBehavior selection_menu_behavior) {
DCHECK_EQ(!!widget, !!handler);
if (!widget)
return;
HandlingState handling_state(widget, UpdateState::kIsSelectingRange);
handler->AdjustSelectionByCharacterOffset(start, end,
selection_menu_behavior);
},
widget_, main_thread_frame_widget_input_handler_, start, end,
selection_menu_behavior));
}
void FrameWidgetInputHandlerImpl::MoveRangeSelectionExtent(
const gfx::Point& extent) {
// TODO(dtapuska): This event should be coalesced. Chrome IPC uses
// one outstanding event and an ACK to handle coalescing on the browser
// side. We should be able to clobber them in the main thread event queue.
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<WidgetBase> widget,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
const gfx::Point& extent) {
DCHECK_EQ(!!widget, !!handler);
if (!widget)
return;
HandlingState handling_state(widget, UpdateState::kIsSelectingRange);
handler->MoveRangeSelectionExtent(extent);
},
widget_, main_thread_frame_widget_input_handler_, extent));
}
void FrameWidgetInputHandlerImpl::ScrollFocusedEditableNodeIntoRect(
const gfx::Rect& rect) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
const gfx::Rect& rect) {
if (handler)
handler->ScrollFocusedEditableNodeIntoRect(rect);
},
main_thread_frame_widget_input_handler_, rect));
}
void FrameWidgetInputHandlerImpl::MoveCaret(const gfx::Point& point) {
RunOnMainThread(base::BindOnce(
[](base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
const gfx::Point& point) {
if (handler)
handler->MoveCaret(point);
},
main_thread_frame_widget_input_handler_, point));
}
void FrameWidgetInputHandlerImpl::ExecuteCommandOnMainThread(
base::WeakPtr<WidgetBase> widget,
base::WeakPtr<mojom::blink::FrameWidgetInputHandler> handler,
const char* command,
UpdateState update_state) {
DCHECK_EQ(!!widget, !!handler);
if (!widget)
return;
HandlingState handling_state(widget, update_state);
handler->ExecuteEditCommand(command, String());
}
FrameWidgetInputHandlerImpl::HandlingState::HandlingState(
const base::WeakPtr<WidgetBase>& widget,
UpdateState state)
: widget_(widget),
original_select_range_value_(widget->handling_select_range()),
original_pasting_value_(widget->is_pasting()) {
switch (state) {
case UpdateState::kIsPasting:
widget->set_is_pasting(true);
FALLTHROUGH; // Set both
case UpdateState::kIsSelectingRange:
widget->set_handling_select_range(true);
break;
case UpdateState::kNone:
break;
}
}
FrameWidgetInputHandlerImpl::HandlingState::~HandlingState() {
// FrameWidget may have been destroyed while this object was on the stack.
if (!widget_)
return;
widget_->set_handling_select_range(original_select_range_value_);
widget_->set_is_pasting(original_pasting_value_);
}
} // namespace blink