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