| // 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 |