blob: 80899eec38e041c4e788efb615a54b0c2de1d3e9 [file] [log] [blame]
// Copyright 2018 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 "ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/win/com_init_util.h"
#include "base/win/core_winrt_util.h"
#include "base/win/windows_version.h"
#include "ui/base/ime/input_method_keyboard_controller_observer.h"
namespace ui {
// VirtualKeyboardInputPane class is used to store all the COM objects and
// control their lifetime, so all the COM processing is on a background
// thread.
class OnScreenKeyboardDisplayManagerInputPane::VirtualKeyboardInputPane
: public base::RefCountedThreadSafe<VirtualKeyboardInputPane> {
public:
explicit VirtualKeyboardInputPane(
const scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: main_task_runner_(task_runner) {}
void InitVirtualKeyboardInputPaneInstance(
base::WeakPtr<OnScreenKeyboardDisplayManagerInputPane>
input_pane_weak_ptr) {
keyboard_input_pane_weak_ptr_ = input_pane_weak_ptr;
}
// Set the virtual keyboard input pane for |OnScreenKeyboardTest| tests.
void SetInputPaneForTestingInBackgroundThread(
Microsoft::WRL::ComPtr<ABI::Windows::UI::ViewManagement::IInputPane>
pane) {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
DCHECK(!input_pane_);
input_pane_ = pane;
HRESULT hr = input_pane_.As(&input_pane2_);
DCHECK(SUCCEEDED(hr));
AddCallbacksOnInputPaneShownOrHiddenInBackgroundThread();
}
void TryShowInBackgroundThread(HWND hwnd) {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
if (!EnsureInputPanePointersInBackgroundThread(hwnd))
return;
boolean res;
input_pane2_->TryShow(&res);
}
void TryHideInBackgroundThread() {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
if (!input_pane2_)
return;
boolean res;
input_pane2_->TryHide(&res);
}
private:
friend class base::RefCountedThreadSafe<VirtualKeyboardInputPane>;
~VirtualKeyboardInputPane() {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
}
bool EnsureInputPanePointersInBackgroundThread(HWND hwnd) {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
if (input_pane2_)
return true;
if (!base::win::ResolveCoreWinRTDelayload() ||
!base::win::ScopedHString::ResolveCoreWinRTStringDelayload()) {
return false;
}
base::win::AssertComApartmentType(base::win::ComApartmentType::STA);
base::win::ScopedHString input_pane_guid = base::win::ScopedHString::Create(
RuntimeClass_Windows_UI_ViewManagement_InputPane);
Microsoft::WRL::ComPtr<IInputPaneInterop> input_pane_interop;
HRESULT hr = base::win::RoGetActivationFactory(
input_pane_guid.get(), IID_PPV_ARGS(&input_pane_interop));
if (FAILED(hr))
return false;
hr = input_pane_interop->GetForWindow(hwnd, IID_PPV_ARGS(&input_pane_));
if (FAILED(hr))
return false;
if (FAILED(input_pane_.As(&input_pane2_)))
return false;
AddCallbacksOnInputPaneShownOrHiddenInBackgroundThread();
return true;
}
// Add callbacks to notify virtual keyboard observers when the virtual
// keyboard is visible or hidden.
void AddCallbacksOnInputPaneShownOrHiddenInBackgroundThread() {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
input_pane_->add_Showing(
Microsoft::WRL::Callback<
OnScreenKeyboardDisplayManagerInputPane::VirtualKeyboardInputPane::
InputPaneEventHandler>(
this, &OnScreenKeyboardDisplayManagerInputPane::
VirtualKeyboardInputPane::OnInputPaneShown)
.Get(),
&show_event_token_);
input_pane_->add_Hiding(
Microsoft::WRL::Callback<
OnScreenKeyboardDisplayManagerInputPane::VirtualKeyboardInputPane::
InputPaneEventHandler>(
this, &OnScreenKeyboardDisplayManagerInputPane::
VirtualKeyboardInputPane::OnInputPaneHidden)
.Get(),
&hide_event_token_);
}
HRESULT OnInputPaneShown(
ABI::Windows::UI::ViewManagement::IInputPane* pane,
ABI::Windows::UI::ViewManagement::IInputPaneVisibilityEventArgs* args) {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
ABI::Windows::Foundation::Rect rect;
input_pane_->get_OccludedRect(&rect);
gfx::Rect dip_rect(rect.X, rect.Y, rect.Width, rect.Height);
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&OnScreenKeyboardDisplayManagerInputPane::
NotifyObserversOnKeyboardShown,
keyboard_input_pane_weak_ptr_, dip_rect));
return S_OK;
}
HRESULT OnInputPaneHidden(
ABI::Windows::UI::ViewManagement::IInputPane* pane,
ABI::Windows::UI::ViewManagement::IInputPaneVisibilityEventArgs* args) {
DCHECK(!main_task_runner_->BelongsToCurrentThread());
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&OnScreenKeyboardDisplayManagerInputPane::
NotifyObserversOnKeyboardHidden,
keyboard_input_pane_weak_ptr_));
return S_OK;
}
using InputPaneEventHandler = ABI::Windows::Foundation::ITypedEventHandler<
ABI::Windows::UI::ViewManagement::InputPane*,
ABI::Windows::UI::ViewManagement::InputPaneVisibilityEventArgs*>;
// InputPane objects are owned by VirtualKeyboardInputPane class and their
// functions are ran on a background thread.
Microsoft::WRL::ComPtr<ABI::Windows::UI::ViewManagement::IInputPane>
input_pane_;
Microsoft::WRL::ComPtr<ABI::Windows::UI::ViewManagement::IInputPane2>
input_pane2_;
EventRegistrationToken show_event_token_;
EventRegistrationToken hide_event_token_;
// |main_task_runner_| and |keyboard_input_pane_weak_ptr_| are owned by
// OnScreenKeyboardDisplayManagerInputPane class, and they are running on the
// main thread, which are used to post task to the main thread from the
// background thread.
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
base::WeakPtr<OnScreenKeyboardDisplayManagerInputPane>
keyboard_input_pane_weak_ptr_;
DISALLOW_COPY_AND_ASSIGN(VirtualKeyboardInputPane);
};
OnScreenKeyboardDisplayManagerInputPane::
OnScreenKeyboardDisplayManagerInputPane(HWND hwnd)
: hwnd_(hwnd),
main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
background_task_runner_(
base::CreateCOMSTATaskRunnerWithTraits({base::MayBlock()})),
virtual_keyboard_input_pane_(
base::MakeRefCounted<OnScreenKeyboardDisplayManagerInputPane::
VirtualKeyboardInputPane>(
main_task_runner_)),
is_keyboard_visible_(false),
weak_factory_(this) {
DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN10_RS1);
DCHECK(main_task_runner_->BelongsToCurrentThread());
// We post the initiation of |virtual_keyboard_input_pane_| to the background
// thread first, and any other tasks posted to the background thread are
// executed after its initiation.
background_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&OnScreenKeyboardDisplayManagerInputPane::VirtualKeyboardInputPane::
InitVirtualKeyboardInputPaneInstance,
base::RetainedRef(virtual_keyboard_input_pane_),
weak_factory_.GetWeakPtr()));
}
bool OnScreenKeyboardDisplayManagerInputPane::DisplayVirtualKeyboard() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
background_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&OnScreenKeyboardDisplayManagerInputPane::
VirtualKeyboardInputPane::TryShowInBackgroundThread,
base::RetainedRef(virtual_keyboard_input_pane_), hwnd_));
return true;
}
void OnScreenKeyboardDisplayManagerInputPane::DismissVirtualKeyboard() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
background_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&OnScreenKeyboardDisplayManagerInputPane::
VirtualKeyboardInputPane::TryHideInBackgroundThread,
base::RetainedRef(virtual_keyboard_input_pane_)));
}
void OnScreenKeyboardDisplayManagerInputPane::AddObserver(
InputMethodKeyboardControllerObserver* observer) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
observers_.AddObserver(observer);
}
void OnScreenKeyboardDisplayManagerInputPane::RemoveObserver(
InputMethodKeyboardControllerObserver* observer) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
observers_.RemoveObserver(observer);
}
bool OnScreenKeyboardDisplayManagerInputPane::IsKeyboardVisible() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
return is_keyboard_visible_;
}
void OnScreenKeyboardDisplayManagerInputPane::SetInputPaneForTesting(
Microsoft::WRL::ComPtr<ABI::Windows::UI::ViewManagement::IInputPane> pane) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
base::CreateCOMSTATaskRunnerWithTraits({base::MayBlock()})
->PostTask(FROM_HERE,
base::BindOnce(
&OnScreenKeyboardDisplayManagerInputPane::
VirtualKeyboardInputPane::
SetInputPaneForTestingInBackgroundThread,
base::RetainedRef(virtual_keyboard_input_pane_), pane));
}
void OnScreenKeyboardDisplayManagerInputPane::NotifyObserversOnKeyboardShown(
gfx::Rect dip_rect) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
is_keyboard_visible_ = true;
for (InputMethodKeyboardControllerObserver& observer : observers_)
observer.OnKeyboardVisible(dip_rect);
}
void OnScreenKeyboardDisplayManagerInputPane::
NotifyObserversOnKeyboardHidden() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
is_keyboard_visible_ = false;
for (InputMethodKeyboardControllerObserver& observer : observers_)
observer.OnKeyboardHidden();
}
OnScreenKeyboardDisplayManagerInputPane::
~OnScreenKeyboardDisplayManagerInputPane() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
if (virtual_keyboard_input_pane_.get()) {
background_task_runner_->ReleaseSoon(
FROM_HERE, std::move(virtual_keyboard_input_pane_));
}
}
} // namespace ui