1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/keyboard/ui/keyboard_ui_controller.h"
6
7 #include <set>
8
9 #include "ash/keyboard/ui/container_floating_behavior.h"
10 #include "ash/keyboard/ui/container_full_width_behavior.h"
11 #include "ash/keyboard/ui/display_util.h"
12 #include "ash/keyboard/ui/keyboard_layout_manager.h"
13 #include "ash/keyboard/ui/keyboard_ui.h"
14 #include "ash/keyboard/ui/keyboard_ui_factory.h"
15 #include "ash/keyboard/ui/keyboard_util.h"
16 #include "ash/keyboard/ui/notification_manager.h"
17 #include "ash/keyboard/ui/queued_container_type.h"
18 #include "ash/keyboard/ui/queued_display_change.h"
19 #include "ash/keyboard/ui/shaped_window_targeter.h"
20 #include "ash/public/cpp/keyboard/keyboard_controller_observer.h"
21 #include "ash/public/cpp/keyboard/keyboard_switches.h"
22 #include "base/bind.h"
23 #include "base/command_line.h"
24 #include "base/macros.h"
25 #include "base/metrics/histogram_functions.h"
26 #include "base/metrics/histogram_macros.h"
27 #include "base/stl_util.h"
28 #include "base/threading/thread_task_runner_handle.h"
29 #include "base/time/time.h"
30 #include "ui/aura/client/aura_constants.h"
31 #include "ui/aura/env.h"
32 #include "ui/aura/window.h"
33 #include "ui/aura/window_delegate.h"
34 #include "ui/aura/window_observer.h"
35 #include "ui/base/cursor/cursor.h"
36 #include "ui/base/hit_test.h"
37 #include "ui/base/ime/text_input_client.h"
38 #include "ui/base/ime/text_input_flags.h"
39 #include "ui/compositor/layer_animation_observer.h"
40 #include "ui/compositor/scoped_layer_animation_settings.h"
41 #include "ui/display/types/display_constants.h"
42 #include "ui/events/base_event_utils.h"
43 #include "ui/events/gestures/gesture_recognizer.h"
44 #include "ui/gfx/geometry/rect.h"
45 #include "ui/gfx/geometry/vector2d.h"
46 #include "ui/ozone/public/input_controller.h"
47 #include "ui/ozone/public/ozone_platform.h"
48 #include "ui/wm/core/coordinate_conversion.h"
49 #include "ui/wm/core/window_animations.h"
50
51 namespace keyboard {
52
53 namespace {
54
55 // Owned by ash::Shell.
56 KeyboardUIController* g_keyboard_controller = nullptr;
57
58 // How long the keyboard stays in WILL_HIDE state before moving to HIDDEN.
59 constexpr base::TimeDelta kHideKeyboardDelay =
60 base::TimeDelta::FromMilliseconds(100);
61
62 // Reports an error histogram if the keyboard state is lingering in an
63 // intermediate state for more than 5 seconds.
64 constexpr base::TimeDelta kReportLingeringStateDelay =
65 base::TimeDelta::FromMilliseconds(5000);
66
67 // Delay threshold after the keyboard enters the WILL_HIDE state. If text focus
68 // is regained during this threshold, the keyboard will show again, even if it
69 // is an asynchronous event. This is for the benefit of things like login flow
70 // where the password field may get text focus after an animation that plays
71 // after the user enters their username.
72 constexpr base::TimeDelta kTransientBlurThreshold =
73 base::TimeDelta::FromMilliseconds(3500);
74
SetTouchEventLogging(bool enable)75 void SetTouchEventLogging(bool enable) {
76 ui::InputController* controller =
77 ui::OzonePlatform::GetInstance()->GetInputController();
78 if (controller)
79 controller->SetTouchEventLoggingEnabled(enable);
80 }
81
82 // An enumeration of different keyboard control events that should be logged.
83 // These values are persisted to logs. Entries should not be renumbered and
84 // numeric values should never be reused.
85 enum class KeyboardControlEvent {
86 kShow = 0,
87 kHideAuto = 1,
88 kHideUser = 2,
89 kMaxValue = kHideUser
90 };
91
LogKeyboardControlEvent(KeyboardControlEvent event)92 void LogKeyboardControlEvent(KeyboardControlEvent event) {
93 UMA_HISTOGRAM_ENUMERATION("VirtualKeyboard.KeyboardControlEvent", event);
94 }
95
96 class InputMethodKeyboardController : public ui::InputMethodKeyboardController {
97 public:
InputMethodKeyboardController(KeyboardUIController * keyboard_ui_controller)98 explicit InputMethodKeyboardController(
99 KeyboardUIController* keyboard_ui_controller)
100 : keyboard_ui_controller_(keyboard_ui_controller) {}
101
102 ~InputMethodKeyboardController() override = default;
103
104 // ui::InputMethodKeyboardController
DisplayVirtualKeyboard()105 bool DisplayVirtualKeyboard() override {
106 // Calling |ShowKeyboardInternal| may move the keyboard to another display.
107 if (keyboard_ui_controller_->IsEnabled() &&
108 !keyboard_ui_controller_->keyboard_locked()) {
109 keyboard_ui_controller_->ShowKeyboard(false /* locked */);
110 return true;
111 }
112 return false;
113 }
114
DismissVirtualKeyboard()115 void DismissVirtualKeyboard() override {
116 keyboard_ui_controller_->HideKeyboardByUser();
117 }
118
AddObserver(ui::InputMethodKeyboardControllerObserver * observer)119 void AddObserver(
120 ui::InputMethodKeyboardControllerObserver* observer) override {
121 // TODO(shend): Implement.
122 }
123
RemoveObserver(ui::InputMethodKeyboardControllerObserver * observer)124 void RemoveObserver(
125 ui::InputMethodKeyboardControllerObserver* observer) override {
126 // TODO(shend): Implement.
127 }
128
IsKeyboardVisible()129 bool IsKeyboardVisible() override {
130 return keyboard_ui_controller_->IsKeyboardVisible();
131 }
132
133 private:
134 KeyboardUIController* keyboard_ui_controller_;
135 };
136
137 } // namespace
138
139 // Observer for both keyboard show and hide animations. It should be owned by
140 // KeyboardUIController.
141 class CallbackAnimationObserver : public ui::ImplicitAnimationObserver {
142 public:
CallbackAnimationObserver(base::OnceClosure callback)143 explicit CallbackAnimationObserver(base::OnceClosure callback)
144 : callback_(std::move(callback)) {}
145
146 private:
147 // ui::ImplicitAnimationObserver:
OnImplicitAnimationsCompleted()148 void OnImplicitAnimationsCompleted() override {
149 if (WasAnimationAbortedForProperty(ui::LayerAnimationElement::TRANSFORM) ||
150 WasAnimationAbortedForProperty(ui::LayerAnimationElement::OPACITY)) {
151 return;
152 }
153 DCHECK(
154 WasAnimationCompletedForProperty(ui::LayerAnimationElement::TRANSFORM));
155 DCHECK(
156 WasAnimationCompletedForProperty(ui::LayerAnimationElement::OPACITY));
157 std::move(callback_).Run();
158 }
159
160 base::OnceClosure callback_;
161
162 DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
163 };
164
KeyboardUIController()165 KeyboardUIController::KeyboardUIController()
166 : input_method_keyboard_controller_(
167 std::make_unique<InputMethodKeyboardController>(this)) {
168 DCHECK_EQ(g_keyboard_controller, nullptr);
169 g_keyboard_controller = this;
170 }
171
~KeyboardUIController()172 KeyboardUIController::~KeyboardUIController() {
173 DCHECK(g_keyboard_controller);
174 DCHECK(!ui_) << "Keyboard UI must be destroyed before KeyboardUIController "
175 "is destroyed";
176 g_keyboard_controller = nullptr;
177 }
178
179 // static
Get()180 KeyboardUIController* KeyboardUIController::Get() {
181 DCHECK(g_keyboard_controller);
182 return g_keyboard_controller;
183 }
184
185 // static
HasInstance()186 bool KeyboardUIController::HasInstance() {
187 return g_keyboard_controller;
188 }
189
Initialize(std::unique_ptr<KeyboardUIFactory> ui_factory,KeyboardLayoutDelegate * layout_delegate)190 void KeyboardUIController::Initialize(
191 std::unique_ptr<KeyboardUIFactory> ui_factory,
192 KeyboardLayoutDelegate* layout_delegate) {
193 DCHECK(ui_factory);
194 DCHECK(layout_delegate);
195
196 ui_factory_ = std::move(ui_factory);
197 layout_delegate_ = layout_delegate;
198
199 DCHECK(!IsKeyboardEnableRequested());
200 }
201
Shutdown()202 void KeyboardUIController::Shutdown() {
203 keyboard_enable_flags_.clear();
204 EnableFlagsChanged();
205
206 DCHECK(!IsKeyboardEnableRequested());
207 DisableKeyboard();
208 }
209
EnableKeyboard()210 void KeyboardUIController::EnableKeyboard() {
211 if (ui_)
212 return;
213
214 ui_ = ui_factory_->CreateKeyboardUI();
215 DCHECK(ui_);
216
217 show_on_keyboard_window_load_ = false;
218 keyboard_locked_ = false;
219 DCHECK_EQ(model_.state(), KeyboardUIState::kInitial);
220 ui_->SetController(this);
221 SetContainerBehaviorInternal(ContainerType::kFullWidth);
222 visual_bounds_in_root_ = gfx::Rect();
223 time_of_last_blur_ = base::Time::UnixEpoch();
224 UpdateInputMethodObserver();
225
226 ActivateKeyboardInContainer(
227 layout_delegate_->GetContainerForDefaultDisplay());
228
229 // Start preloading the virtual keyboard UI in the background, so that it
230 // shows up faster when needed.
231 LoadKeyboardWindowInBackground();
232
233 // Notify observers after the keyboard window has a root window.
234 for (auto& observer : observer_list_)
235 observer.OnKeyboardEnabledChanged(true);
236 }
237
DisableKeyboard()238 void KeyboardUIController::DisableKeyboard() {
239 if (!ui_)
240 return;
241
242 if (parent_container_)
243 DeactivateKeyboard();
244
245 aura::Window* keyboard_window = GetKeyboardWindow();
246 if (keyboard_window)
247 keyboard_window->RemoveObserver(this);
248
249 // Return to the INITIAL state to ensure that transitions entering a state
250 // is equal to transitions leaving the state.
251 if (model_.state() != KeyboardUIState::kInitial)
252 ChangeState(KeyboardUIState::kInitial);
253
254 // TODO(https://crbug.com/731537): Move KeyboardUIController members into a
255 // subobject so we can just put this code into the subobject destructor.
256 queued_display_change_.reset();
257 queued_container_type_.reset();
258 container_behavior_.reset();
259 animation_observer_.reset();
260
261 ime_observer_.RemoveAll();
262 ui_->SetController(nullptr);
263 ui_.reset();
264
265 // Notify observers after |ui_| is reset so that IsEnabled() is false.
266 for (auto& observer : observer_list_)
267 observer.OnKeyboardEnabledChanged(false);
268 }
269
ActivateKeyboardInContainer(aura::Window * parent)270 void KeyboardUIController::ActivateKeyboardInContainer(aura::Window* parent) {
271 DCHECK(parent);
272 DCHECK(!parent_container_);
273 parent_container_ = parent;
274 // Observe changes to root window bounds.
275 parent_container_->GetRootWindow()->AddObserver(this);
276
277 UpdateInputMethodObserver();
278
279 if (GetKeyboardWindow()) {
280 DCHECK(!GetKeyboardWindow()->parent());
281 parent_container_->AddChild(GetKeyboardWindow());
282 }
283 }
284
DeactivateKeyboard()285 void KeyboardUIController::DeactivateKeyboard() {
286 DCHECK(parent_container_);
287
288 // Ensure the keyboard is not visible before deactivating it.
289 HideKeyboardExplicitlyBySystem();
290
291 aura::Window* keyboard_window = GetKeyboardWindow();
292 if (keyboard_window) {
293 keyboard_window->RemovePreTargetHandler(&event_handler_);
294 if (keyboard_window->parent()) {
295 DCHECK_EQ(parent_container_, keyboard_window->parent());
296 parent_container_->RemoveChild(keyboard_window);
297 }
298 }
299 parent_container_->GetRootWindow()->RemoveObserver(this);
300 parent_container_ = nullptr;
301 }
302
GetKeyboardWindow() const303 aura::Window* KeyboardUIController::GetKeyboardWindow() const {
304 return ui_ ? ui_->GetKeyboardWindow() : nullptr;
305 }
306
GetGestureConsumer() const307 ui::GestureConsumer* KeyboardUIController::GetGestureConsumer() const {
308 return ui_ ? ui_->GetGestureConsumer() : nullptr;
309 }
310
GetRootWindow() const311 aura::Window* KeyboardUIController::GetRootWindow() const {
312 return parent_container_ ? parent_container_->GetRootWindow() : nullptr;
313 }
314
MoveToParentContainer(aura::Window * parent)315 void KeyboardUIController::MoveToParentContainer(aura::Window* parent) {
316 DCHECK(parent);
317 if (parent_container_ == parent)
318 return;
319
320 TRACE_EVENT0("vk", "MoveKeyboardToDisplayInternal");
321
322 DeactivateKeyboard();
323 ActivateKeyboardInContainer(parent);
324 }
325
326 // private
NotifyKeyboardBoundsChanging(const gfx::Rect & new_bounds_in_root)327 void KeyboardUIController::NotifyKeyboardBoundsChanging(
328 const gfx::Rect& new_bounds_in_root) {
329 gfx::Rect occluded_bounds_in_screen;
330 aura::Window* window = GetKeyboardWindow();
331 if (window && window->IsVisible()) {
332 visual_bounds_in_root_ = new_bounds_in_root;
333
334 // |visual_bounds_in_root_| affects the result of
335 // GetWorkspaceOccludedBoundsInScreen. Calculate |occluded_bounds_in_screen|
336 // after updating |visual_bounds_in_root_|.
337 // TODO(andrewxu): Add the unit test case for issue 960174.
338 occluded_bounds_in_screen = GetWorkspaceOccludedBoundsInScreen();
339
340 // TODO(https://crbug.com/943446): Use screen bounds for visual bounds.
341 notification_manager_.SendNotifications(
342 container_behavior_->OccludedBoundsAffectWorkspaceLayout(),
343 new_bounds_in_root, occluded_bounds_in_screen, observer_list_);
344 } else {
345 visual_bounds_in_root_ = gfx::Rect();
346 occluded_bounds_in_screen = GetWorkspaceOccludedBoundsInScreen();
347 }
348
349 EnsureCaretInWorkArea(occluded_bounds_in_screen);
350 }
351
SetKeyboardWindowBounds(const gfx::Rect & new_bounds_in_root)352 void KeyboardUIController::SetKeyboardWindowBounds(
353 const gfx::Rect& new_bounds_in_root) {
354 ui::LayerAnimator* animator = GetKeyboardWindow()->layer()->GetAnimator();
355 // Stops previous animation if a window resize is requested during animation.
356 if (animator->is_animating())
357 animator->StopAnimating();
358
359 GetKeyboardWindow()->SetBounds(new_bounds_in_root);
360 }
361
NotifyKeyboardWindowLoaded()362 void KeyboardUIController::NotifyKeyboardWindowLoaded() {
363 const bool should_show = show_on_keyboard_window_load_;
364 if (model_.state() == KeyboardUIState::kLoading)
365 ChangeState(KeyboardUIState::kHidden);
366 if (should_show) {
367 // The window height is set to 0 initially or before switch to an IME in a
368 // different extension. Virtual keyboard window may wait for this bounds
369 // change to correctly animate in.
370 if (keyboard_locked_) {
371 // Do not move the keyboard to another display after switch to an IME in
372 // a different extension.
373 ShowKeyboardInDisplay(
374 display_util_.GetNearestDisplayToWindow(GetKeyboardWindow()));
375 } else {
376 ShowKeyboard(false /* lock */);
377 }
378 }
379 }
380
Reload()381 void KeyboardUIController::Reload() {
382 if (!GetKeyboardWindow())
383 return;
384
385 ui_->ReloadKeyboardIfNeeded();
386 }
387
RebuildKeyboardIfEnabled()388 void KeyboardUIController::RebuildKeyboardIfEnabled() {
389 if (!IsEnabled())
390 return;
391
392 DisableKeyboard();
393 EnableKeyboard();
394 }
395
AddObserver(ash::KeyboardControllerObserver * observer)396 void KeyboardUIController::AddObserver(
397 ash::KeyboardControllerObserver* observer) {
398 observer_list_.AddObserver(observer);
399 }
400
HasObserver(ash::KeyboardControllerObserver * observer) const401 bool KeyboardUIController::HasObserver(
402 ash::KeyboardControllerObserver* observer) const {
403 return observer_list_.HasObserver(observer);
404 }
405
RemoveObserver(ash::KeyboardControllerObserver * observer)406 void KeyboardUIController::RemoveObserver(
407 ash::KeyboardControllerObserver* observer) {
408 observer_list_.RemoveObserver(observer);
409 }
410
UpdateKeyboardConfig(const KeyboardConfig & config)411 bool KeyboardUIController::UpdateKeyboardConfig(const KeyboardConfig& config) {
412 if (config == keyboard_config_)
413 return false;
414 keyboard_config_ = config;
415 if (IsEnabled())
416 NotifyKeyboardConfigChanged();
417 return true;
418 }
419
SetEnableFlag(KeyboardEnableFlag flag)420 void KeyboardUIController::SetEnableFlag(KeyboardEnableFlag flag) {
421 if (!base::Contains(keyboard_enable_flags_, flag))
422 keyboard_enable_flags_.insert(flag);
423
424 // If there is a flag that is mutually exclusive with |flag|, clear it.
425 switch (flag) {
426 case KeyboardEnableFlag::kPolicyEnabled:
427 keyboard_enable_flags_.erase(KeyboardEnableFlag::kPolicyDisabled);
428 break;
429 case KeyboardEnableFlag::kPolicyDisabled:
430 keyboard_enable_flags_.erase(KeyboardEnableFlag::kPolicyEnabled);
431 break;
432 case KeyboardEnableFlag::kExtensionEnabled:
433 keyboard_enable_flags_.erase(KeyboardEnableFlag::kExtensionDisabled);
434 break;
435 case KeyboardEnableFlag::kExtensionDisabled:
436 keyboard_enable_flags_.erase(KeyboardEnableFlag::kExtensionEnabled);
437 break;
438 default:
439 break;
440 }
441
442 EnableFlagsChanged();
443
444 UpdateKeyboardAsRequestedBy(flag);
445 }
446
ClearEnableFlag(KeyboardEnableFlag flag)447 void KeyboardUIController::ClearEnableFlag(KeyboardEnableFlag flag) {
448 if (!IsEnableFlagSet(flag))
449 return;
450
451 keyboard_enable_flags_.erase(flag);
452 EnableFlagsChanged();
453
454 UpdateKeyboardAsRequestedBy(flag);
455 }
456
IsEnableFlagSet(KeyboardEnableFlag flag) const457 bool KeyboardUIController::IsEnableFlagSet(KeyboardEnableFlag flag) const {
458 return base::Contains(keyboard_enable_flags_, flag);
459 }
460
IsKeyboardEnableRequested() const461 bool KeyboardUIController::IsKeyboardEnableRequested() const {
462 // Accessibility setting prioritized over policy/arc overrides.
463 if (IsEnableFlagSet(KeyboardEnableFlag::kAccessibilityEnabled))
464 return true;
465
466 // Keyboard can be enabled temporarily by the shelf.
467 if (IsEnableFlagSet(KeyboardEnableFlag::kShelfEnabled))
468 return true;
469
470 if (IsEnableFlagSet(KeyboardEnableFlag::kAndroidDisabled) ||
471 IsEnableFlagSet(KeyboardEnableFlag::kPolicyDisabled)) {
472 return false;
473 }
474 if (IsEnableFlagSet(KeyboardEnableFlag::kPolicyEnabled))
475 return true;
476
477 // Command line overrides extension and touch enabled flags.
478 if (IsEnableFlagSet(KeyboardEnableFlag::kCommandLineEnabled))
479 return true;
480
481 if (IsEnableFlagSet(KeyboardEnableFlag::kExtensionDisabled))
482 return false;
483
484 return IsEnableFlagSet(KeyboardEnableFlag::kExtensionEnabled) ||
485 IsEnableFlagSet(KeyboardEnableFlag::kTouchEnabled);
486 }
487
UpdateKeyboardAsRequestedBy(KeyboardEnableFlag flag)488 void KeyboardUIController::UpdateKeyboardAsRequestedBy(
489 KeyboardEnableFlag flag) {
490 if (IsKeyboardEnableRequested()) {
491 // Note that there are two versions of the on-screen keyboard. A full layout
492 // is provided for accessibility, which includes sticky modifier keys to
493 // enable typing of hotkeys. A compact version is used in tablet mode to
494 // provide a layout with larger keys to facilitate touch typing. In the
495 // event that the a11y keyboard is being disabled, an on-screen keyboard
496 // might still be enabled and a forced reset is required to pick up the
497 // layout change.
498 if (IsEnabled() && flag == KeyboardEnableFlag::kAccessibilityEnabled)
499 RebuildKeyboardIfEnabled();
500 else
501 EnableKeyboard();
502 } else {
503 DisableKeyboard();
504 }
505 }
506
IsKeyboardOverscrollEnabled() const507 bool KeyboardUIController::IsKeyboardOverscrollEnabled() const {
508 if (!IsEnabled())
509 return false;
510
511 // Users of the sticky accessibility on-screen keyboard are likely to be using
512 // mouse input, which may interfere with overscrolling.
513 if (IsEnabled() && !IsOverscrollAllowed())
514 return false;
515
516 // If overscroll enabled behavior is set, use it instead. Currently
517 // login / out-of-box disable keyboard overscroll. http://crbug.com/363635
518 if (keyboard_config_.overscroll_behavior !=
519 KeyboardOverscrollBehavior::kDefault) {
520 return keyboard_config_.overscroll_behavior ==
521 KeyboardOverscrollBehavior::kEnabled;
522 }
523
524 return true;
525 }
526
527 // private
HideKeyboard(HideReason reason)528 void KeyboardUIController::HideKeyboard(HideReason reason) {
529 TRACE_EVENT0("vk", "HideKeyboard");
530
531 // Decide whether regaining focus in a web-based text field should cause
532 // the keyboard to come back.
533 switch (reason) {
534 case HIDE_REASON_SYSTEM_IMPLICIT:
535 time_of_last_blur_ = base::Time::Now();
536 break;
537
538 case HIDE_REASON_SYSTEM_TEMPORARY:
539 case HIDE_REASON_SYSTEM_EXPLICIT:
540 case HIDE_REASON_USER_EXPLICIT:
541 case HIDE_REASON_USER_IMPLICIT:
542 time_of_last_blur_ = base::Time::UnixEpoch();
543 break;
544 }
545
546 switch (model_.state()) {
547 case KeyboardUIState::kUnknown:
548 case KeyboardUIState::kInitial:
549 case KeyboardUIState::kHidden:
550 return;
551 case KeyboardUIState::kLoading:
552 show_on_keyboard_window_load_ = false;
553 return;
554
555 case KeyboardUIState::kWillHide:
556 case KeyboardUIState::kShown: {
557 SetTouchEventLogging(true /* enable */);
558
559 // Log whether this was a user or system (automatic) action.
560 switch (reason) {
561 case HIDE_REASON_SYSTEM_EXPLICIT:
562 case HIDE_REASON_SYSTEM_IMPLICIT:
563 case HIDE_REASON_SYSTEM_TEMPORARY:
564 LogKeyboardControlEvent(KeyboardControlEvent::kHideAuto);
565 break;
566 case HIDE_REASON_USER_EXPLICIT:
567 case HIDE_REASON_USER_IMPLICIT:
568 LogKeyboardControlEvent(KeyboardControlEvent::kHideUser);
569 break;
570 }
571
572 NotifyKeyboardBoundsChanging(gfx::Rect());
573
574 set_keyboard_locked(false);
575
576 aura::Window* window = GetKeyboardWindow();
577 DCHECK(window);
578
579 animation_observer_ = std::make_unique<CallbackAnimationObserver>(
580 base::BindOnce(&KeyboardUIController::HideAnimationFinished,
581 base::Unretained(this)));
582 ui::ScopedLayerAnimationSettings layer_animation_settings(
583 window->layer()->GetAnimator());
584 layer_animation_settings.AddObserver(animation_observer_.get());
585
586 {
587 // Scoped settings go into effect when scope ends.
588 ::wm::ScopedHidingAnimationSettings hiding_settings(window);
589 container_behavior_->DoHidingAnimation(window, &hiding_settings);
590 }
591
592 ui_->HideKeyboardWindow();
593 ChangeState(KeyboardUIState::kHidden);
594
595 for (auto& observer : observer_list_)
596 observer.OnKeyboardHidden(reason == HIDE_REASON_SYSTEM_TEMPORARY);
597
598 break;
599 }
600 }
601 }
602
HideKeyboardByUser()603 void KeyboardUIController::HideKeyboardByUser() {
604 HideKeyboard(HIDE_REASON_USER_EXPLICIT);
605 }
606
HideKeyboardImplicitlyByUser()607 void KeyboardUIController::HideKeyboardImplicitlyByUser() {
608 if (!keyboard_locked_)
609 HideKeyboard(HIDE_REASON_USER_IMPLICIT);
610 }
611
HideKeyboardTemporarilyForTransition()612 void KeyboardUIController::HideKeyboardTemporarilyForTransition() {
613 HideKeyboard(HIDE_REASON_SYSTEM_TEMPORARY);
614 }
615
HideKeyboardExplicitlyBySystem()616 void KeyboardUIController::HideKeyboardExplicitlyBySystem() {
617 HideKeyboard(HIDE_REASON_SYSTEM_EXPLICIT);
618 }
619
HideKeyboardImplicitlyBySystem()620 void KeyboardUIController::HideKeyboardImplicitlyBySystem() {
621 if (model_.state() != KeyboardUIState::kShown || keyboard_locked_)
622 return;
623
624 ChangeState(KeyboardUIState::kWillHide);
625
626 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
627 FROM_HERE,
628 base::BindOnce(&KeyboardUIController::HideKeyboard,
629 weak_factory_will_hide_.GetWeakPtr(),
630 HIDE_REASON_SYSTEM_IMPLICIT),
631 kHideKeyboardDelay);
632 }
633
634 // private
HideAnimationFinished()635 void KeyboardUIController::HideAnimationFinished() {
636 if (model_.state() == KeyboardUIState::kHidden) {
637 if (queued_container_type_) {
638 SetContainerBehaviorInternal(queued_container_type_->container_type());
639 // The position of the container window will be adjusted shortly in
640 // |PopulateKeyboardContent| before showing animation, so we can set the
641 // passed bounds directly.
642 SetKeyboardWindowBounds(queued_container_type_->target_bounds());
643 ShowKeyboard(false /* lock */);
644 }
645
646 if (queued_display_change_) {
647 ShowKeyboardInDisplay(queued_display_change_->new_display());
648 SetKeyboardWindowBounds(queued_display_change_->new_bounds_in_local());
649 queued_display_change_ = nullptr;
650 }
651 }
652 }
653
654 // private
ShowAnimationFinished()655 void KeyboardUIController::ShowAnimationFinished() {
656 MarkKeyboardLoadFinished();
657
658 // Notify observers after animation finished to prevent reveal desktop
659 // background during animation.
660 // If the current state is not SHOWN, it means the state was changed after the
661 // animation started. Do not tell the observers the stale bounds.
662 if (model_.state() == KeyboardUIState::kShown)
663 NotifyKeyboardBoundsChanging(GetKeyboardWindow()->GetBoundsInRootWindow());
664 }
665
666 // private
SetContainerBehaviorInternal(ContainerType type)667 void KeyboardUIController::SetContainerBehaviorInternal(ContainerType type) {
668 // Reset the hit test event targeter because the hit test bounds will
669 // be wrong when container type changes and may cause the UI to be unusable.
670 if (GetKeyboardWindow())
671 GetKeyboardWindow()->SetEventTargeter(nullptr);
672
673 switch (type) {
674 case ContainerType::kFullWidth:
675 container_behavior_ = std::make_unique<ContainerFullWidthBehavior>(this);
676 break;
677 case ContainerType::kFloating:
678 container_behavior_ = std::make_unique<ContainerFloatingBehavior>(this);
679 break;
680 }
681 }
682
ShowKeyboard(bool lock)683 void KeyboardUIController::ShowKeyboard(bool lock) {
684 DVLOG(1) << "ShowKeyboard";
685 set_keyboard_locked(lock);
686 ShowKeyboardInternal(layout_delegate_->GetContainerForDefaultDisplay());
687 }
688
ShowKeyboardInDisplay(const display::Display & display)689 void KeyboardUIController::ShowKeyboardInDisplay(
690 const display::Display& display) {
691 DVLOG(1) << "ShowKeyboardInDisplay: " << display.id();
692 set_keyboard_locked(true);
693 ShowKeyboardInternal(layout_delegate_->GetContainerForDisplay(display));
694 }
695
GetVisualBoundsInScreen() const696 gfx::Rect KeyboardUIController::GetVisualBoundsInScreen() const {
697 gfx::Rect visual_bounds_in_screen = visual_bounds_in_root_;
698 ::wm::ConvertRectToScreen(GetRootWindow(), &visual_bounds_in_screen);
699 return visual_bounds_in_screen;
700 }
701
LoadKeyboardWindowInBackground()702 void KeyboardUIController::LoadKeyboardWindowInBackground() {
703 DCHECK_EQ(model_.state(), KeyboardUIState::kInitial);
704
705 TRACE_EVENT0("vk", "LoadKeyboardWindowInBackground");
706
707 // For now, using Unretained is safe here because the |ui_| is owned by
708 // |this| and the callback does not outlive |ui_|.
709 // TODO(https://crbug.com/845780): Use a weak ptr here in case this
710 // assumption changes.
711 DVLOG(1) << "LoadKeyboardWindow";
712 aura::Window* keyboard_window = ui_->LoadKeyboardWindow(
713 base::BindOnce(&KeyboardUIController::NotifyKeyboardWindowLoaded,
714 base::Unretained(this)));
715 keyboard_window->AddPreTargetHandler(&event_handler_);
716 keyboard_window->AddObserver(this);
717 parent_container_->AddChild(keyboard_window);
718
719 ChangeState(KeyboardUIState::kLoading);
720 }
721
GetInputMethodForTest()722 ui::InputMethod* KeyboardUIController::GetInputMethodForTest() {
723 return ui_->GetInputMethod();
724 }
725
EnsureCaretInWorkAreaForTest(const gfx::Rect & occluded_bounds_in_screen)726 void KeyboardUIController::EnsureCaretInWorkAreaForTest(
727 const gfx::Rect& occluded_bounds_in_screen) {
728 EnsureCaretInWorkArea(occluded_bounds_in_screen);
729 }
730
731 // ContainerBehavior::Delegate overrides
732
IsKeyboardLocked() const733 bool KeyboardUIController::IsKeyboardLocked() const {
734 return keyboard_locked_;
735 }
736
GetBoundsInScreen() const737 gfx::Rect KeyboardUIController::GetBoundsInScreen() const {
738 return GetKeyboardWindow()->GetBoundsInScreen();
739 }
740
MoveKeyboardWindow(const gfx::Rect & new_bounds)741 void KeyboardUIController::MoveKeyboardWindow(const gfx::Rect& new_bounds) {
742 DCHECK(IsKeyboardVisible());
743 SetKeyboardWindowBounds(new_bounds);
744 }
745
MoveKeyboardWindowToDisplay(const display::Display & display,const gfx::Rect & new_bounds_in_root)746 void KeyboardUIController::MoveKeyboardWindowToDisplay(
747 const display::Display& display,
748 const gfx::Rect& new_bounds_in_root) {
749 queued_display_change_ =
750 std::make_unique<QueuedDisplayChange>(display, new_bounds_in_root);
751 HideKeyboardTemporarilyForTransition();
752 }
753
TransferGestureEventToShelf(const ui::GestureEvent & e)754 void KeyboardUIController::TransferGestureEventToShelf(
755 const ui::GestureEvent& e) {
756 layout_delegate_->TransferGestureEventToShelf(e);
757 }
758
759 // aura::WindowObserver overrides
760
OnWindowAddedToRootWindow(aura::Window * window)761 void KeyboardUIController::OnWindowAddedToRootWindow(aura::Window* window) {
762 container_behavior_->SetCanonicalBounds(GetKeyboardWindow(),
763 GetRootWindow()->bounds());
764 }
765
OnWindowBoundsChanged(aura::Window * window,const gfx::Rect & old_bounds_in_root,const gfx::Rect & new_bounds_in_root,ui::PropertyChangeReason reason)766 void KeyboardUIController::OnWindowBoundsChanged(
767 aura::Window* window,
768 const gfx::Rect& old_bounds_in_root,
769 const gfx::Rect& new_bounds_in_root,
770 ui::PropertyChangeReason reason) {
771 if (!GetKeyboardWindow())
772 return;
773
774 // |window| could be the root window (for detecting screen rotations) or the
775 // keyboard window (for detecting keyboard bounds changes).
776 if (window == GetRootWindow())
777 container_behavior_->SetCanonicalBounds(GetKeyboardWindow(),
778 new_bounds_in_root);
779 else if (window == GetKeyboardWindow())
780 NotifyKeyboardBoundsChanging(new_bounds_in_root);
781 }
782
783 // InputMethodObserver overrides
784
OnInputMethodDestroyed(const ui::InputMethod * input_method)785 void KeyboardUIController::OnInputMethodDestroyed(
786 const ui::InputMethod* input_method) {
787 ime_observer_.RemoveAll();
788 OnTextInputStateChanged(nullptr);
789 }
790
OnTextInputStateChanged(const ui::TextInputClient * client)791 void KeyboardUIController::OnTextInputStateChanged(
792 const ui::TextInputClient* client) {
793 TRACE_EVENT0("vk", "OnTextInputStateChanged");
794
795 bool focused =
796 client && (client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE &&
797 client->GetTextInputMode() != ui::TEXT_INPUT_MODE_NONE);
798 bool should_hide = !focused && container_behavior_->TextBlurHidesKeyboard();
799 bool is_web =
800 client && client->GetTextInputFlags() != ui::TEXT_INPUT_FLAG_NONE;
801
802 if (should_hide) {
803 switch (model_.state()) {
804 case KeyboardUIState::kLoading:
805 show_on_keyboard_window_load_ = false;
806 return;
807 case KeyboardUIState::kShown:
808 HideKeyboardImplicitlyBySystem();
809 return;
810 default:
811 return;
812 }
813 } else {
814 switch (model_.state()) {
815 case KeyboardUIState::kWillHide:
816 // Abort a pending keyboard hide.
817 ChangeState(KeyboardUIState::kShown);
818 return;
819 case KeyboardUIState::kHidden:
820 if (focused && is_web)
821 ShowKeyboardIfWithinTransientBlurThreshold();
822 return;
823 default:
824 break;
825 }
826 // Do not explicitly show the Virtual keyboard unless it is in the process
827 // of hiding or the hide duration was very short (transient blur). Instead,
828 // the virtual keyboard is shown in response to a user gesture (mouse or
829 // touch) that is received while an element has input focus. Showing the
830 // keyboard requires an explicit call to OnShowVirtualKeyboardIfEnabled.
831 }
832 }
833
ShowKeyboardIfWithinTransientBlurThreshold()834 void KeyboardUIController::ShowKeyboardIfWithinTransientBlurThreshold() {
835 if (base::Time::Now() - time_of_last_blur_ < kTransientBlurThreshold)
836 ShowKeyboard(false);
837 }
838
OnShowVirtualKeyboardIfEnabled()839 void KeyboardUIController::OnShowVirtualKeyboardIfEnabled() {
840 DVLOG(1) << "OnShowVirtualKeyboardIfEnabled: " << IsEnabled();
841 // Calling |ShowKeyboardInternal| may move the keyboard to another display.
842 if (IsEnabled() && !keyboard_locked_)
843 ShowKeyboardInternal(layout_delegate_->GetContainerForDefaultDisplay());
844 }
845
ShowKeyboardInternal(aura::Window * target_container)846 void KeyboardUIController::ShowKeyboardInternal(
847 aura::Window* target_container) {
848 MarkKeyboardLoadStarted();
849 PopulateKeyboardContent(target_container);
850 UpdateInputMethodObserver();
851 }
852
PopulateKeyboardContent(aura::Window * target_container)853 void KeyboardUIController::PopulateKeyboardContent(
854 aura::Window* target_container) {
855 DCHECK_NE(model_.state(), KeyboardUIState::kInitial);
856
857 DVLOG(1) << "PopulateKeyboardContent: " << StateToStr(model_.state());
858 TRACE_EVENT0("vk", "PopulateKeyboardContent");
859
860 MoveToParentContainer(target_container);
861
862 aura::Window* keyboard_window = GetKeyboardWindow();
863 DCHECK(keyboard_window);
864 DCHECK_EQ(parent_container_, keyboard_window->parent());
865
866 switch (model_.state()) {
867 case KeyboardUIState::kShown:
868 return;
869 case KeyboardUIState::kLoading:
870 show_on_keyboard_window_load_ = true;
871 return;
872 default:
873 break;
874 }
875
876 ui_->ReloadKeyboardIfNeeded();
877
878 SetTouchEventLogging(false /* enable */);
879
880 switch (model_.state()) {
881 case KeyboardUIState::kWillHide:
882 ChangeState(KeyboardUIState::kShown);
883 return;
884 default:
885 break;
886 }
887
888 DCHECK_EQ(model_.state(), KeyboardUIState::kHidden);
889
890 // If the container is not animating, makes sure the position and opacity
891 // are at begin states for animation.
892 container_behavior_->InitializeShowAnimationStartingState(keyboard_window);
893
894 LogKeyboardControlEvent(KeyboardControlEvent::kShow);
895 RecordUkmKeyboardShown();
896
897 ui::LayerAnimator* container_animator =
898 keyboard_window->layer()->GetAnimator();
899 container_animator->set_preemption_strategy(
900 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
901
902 ui_->ShowKeyboardWindow();
903
904 animation_observer_ = std::make_unique<CallbackAnimationObserver>(
905 base::BindOnce(&KeyboardUIController::ShowAnimationFinished,
906 base::Unretained(this)));
907 ui::ScopedLayerAnimationSettings settings(container_animator);
908 settings.AddObserver(animation_observer_.get());
909
910 container_behavior_->DoShowingAnimation(keyboard_window, &settings);
911
912 // the queued container behavior will notify JS to change layout when it
913 // gets destroyed.
914 queued_container_type_ = nullptr;
915
916 ChangeState(KeyboardUIState::kShown);
917
918 UMA_HISTOGRAM_ENUMERATION("InputMethod.VirtualKeyboard.ContainerBehavior",
919 GetActiveContainerType());
920 }
921
WillHideKeyboard() const922 bool KeyboardUIController::WillHideKeyboard() const {
923 bool res = weak_factory_will_hide_.HasWeakPtrs();
924 DCHECK_EQ(res, model_.state() == KeyboardUIState::kWillHide);
925 return res;
926 }
927
NotifyKeyboardConfigChanged()928 void KeyboardUIController::NotifyKeyboardConfigChanged() {
929 for (auto& observer : observer_list_)
930 observer.OnKeyboardConfigChanged(keyboard_config_);
931 }
932
ChangeState(KeyboardUIState state)933 void KeyboardUIController::ChangeState(KeyboardUIState state) {
934 model_.ChangeState(state);
935
936 if (state != KeyboardUIState::kWillHide)
937 weak_factory_will_hide_.InvalidateWeakPtrs();
938 if (state != KeyboardUIState::kLoading)
939 show_on_keyboard_window_load_ = false;
940
941 weak_factory_report_lingering_state_.InvalidateWeakPtrs();
942 switch (model_.state()) {
943 case KeyboardUIState::kLoading:
944 case KeyboardUIState::kWillHide:
945 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
946 FROM_HERE,
947 base::BindOnce(&KeyboardUIController::ReportLingeringState,
948 weak_factory_report_lingering_state_.GetWeakPtr()),
949 kReportLingeringStateDelay);
950 break;
951 default:
952 // Do nothing
953 break;
954 }
955 }
956
ReportLingeringState()957 void KeyboardUIController::ReportLingeringState() {
958 LOG(ERROR) << "KeyboardUIController lingering in "
959 << StateToStr(model_.state());
960 UMA_HISTOGRAM_ENUMERATION("VirtualKeyboard.LingeringIntermediateState",
961 model_.state());
962 }
963
GetWorkspaceOccludedBoundsInScreen() const964 gfx::Rect KeyboardUIController::GetWorkspaceOccludedBoundsInScreen() const {
965 if (!ui_)
966 return gfx::Rect();
967
968 const gfx::Rect visual_bounds_in_window(visual_bounds_in_root_.size());
969
970 gfx::Rect occluded_bounds_in_screen =
971 container_behavior_->GetOccludedBounds(visual_bounds_in_window);
972 ::wm::ConvertRectToScreen(GetKeyboardWindow(), &occluded_bounds_in_screen);
973
974 return occluded_bounds_in_screen;
975 }
976
GetKeyboardLockScreenOffsetBounds() const977 gfx::Rect KeyboardUIController::GetKeyboardLockScreenOffsetBounds() const {
978 // Overscroll is generally dependent on lock state, however, its behavior
979 // temporarily overridden by a static field in certain lock screen contexts.
980 // Furthermore, floating keyboard should never affect layout.
981 if (!IsKeyboardOverscrollEnabled() &&
982 container_behavior_->GetType() != ContainerType::kFloating) {
983 return visual_bounds_in_root_;
984 }
985 return gfx::Rect();
986 }
987
SetOccludedBounds(const gfx::Rect & bounds_in_window)988 void KeyboardUIController::SetOccludedBounds(
989 const gfx::Rect& bounds_in_window) {
990 container_behavior_->SetOccludedBounds(bounds_in_window);
991
992 // Notify that only the occluded bounds have changed.
993 if (IsKeyboardVisible())
994 NotifyKeyboardBoundsChanging(visual_bounds_in_root_);
995 }
996
SetHitTestBounds(const std::vector<gfx::Rect> & bounds_in_window)997 void KeyboardUIController::SetHitTestBounds(
998 const std::vector<gfx::Rect>& bounds_in_window) {
999 if (!GetKeyboardWindow())
1000 return;
1001
1002 GetKeyboardWindow()->SetEventTargeter(
1003 std::make_unique<ShapedWindowTargeter>(bounds_in_window));
1004 }
1005
SetAreaToRemainOnScreen(const gfx::Rect & bounds_in_window)1006 bool KeyboardUIController::SetAreaToRemainOnScreen(
1007 const gfx::Rect& bounds_in_window) {
1008 gfx::Rect window_bounds_in_screen = GetKeyboardWindow()->GetBoundsInScreen();
1009 gfx::Rect bounds_in_screen =
1010 gfx::Rect(window_bounds_in_screen.x() + bounds_in_window.x(),
1011 window_bounds_in_screen.y() + bounds_in_window.y(),
1012 bounds_in_window.width(), bounds_in_window.height());
1013
1014 if (!window_bounds_in_screen.Contains(bounds_in_screen))
1015 return false;
1016
1017 container_behavior_->SetAreaToRemainOnScreen(bounds_in_window);
1018 return true;
1019 }
1020
SetKeyboardWindowBoundsInScreen(const gfx::Rect & bounds_in_screen)1021 bool KeyboardUIController::SetKeyboardWindowBoundsInScreen(
1022 const gfx::Rect& bounds_in_screen) {
1023 const display::Display& current_display =
1024 display_util_.GetNearestDisplayToWindow(GetRootWindow());
1025
1026 gfx::Rect display_bounds = current_display.bounds();
1027 if (bounds_in_screen.width() > display_bounds.width() ||
1028 bounds_in_screen.height() > display_bounds.height()) {
1029 return false;
1030 }
1031
1032 gfx::Rect constrained_bounds_in_screen =
1033 AdjustSetBoundsRequest(current_display.bounds(), bounds_in_screen);
1034
1035 GetKeyboardWindow()->SetBoundsInScreen(constrained_bounds_in_screen,
1036 current_display);
1037 return true;
1038 }
1039
AdjustSetBoundsRequest(const gfx::Rect & display_bounds,const gfx::Rect & requested_bounds_in_screen) const1040 gfx::Rect KeyboardUIController::AdjustSetBoundsRequest(
1041 const gfx::Rect& display_bounds,
1042 const gfx::Rect& requested_bounds_in_screen) const {
1043 return container_behavior_->AdjustSetBoundsRequest(
1044 display_bounds, requested_bounds_in_screen);
1045 }
1046
IsOverscrollAllowed() const1047 bool KeyboardUIController::IsOverscrollAllowed() const {
1048 return container_behavior_->IsOverscrollAllowed();
1049 }
1050
HandlePointerEvent(const ui::LocatedEvent & event)1051 bool KeyboardUIController::HandlePointerEvent(const ui::LocatedEvent& event) {
1052 const display::Display& current_display =
1053 display_util_.GetNearestDisplayToWindow(GetRootWindow());
1054 return container_behavior_->HandlePointerEvent(event, current_display);
1055 }
1056
HandleGestureEvent(const ui::GestureEvent & event)1057 bool KeyboardUIController::HandleGestureEvent(const ui::GestureEvent& event) {
1058 return container_behavior_->HandleGestureEvent(event, GetBoundsInScreen());
1059 }
1060
SetContainerType(ContainerType type,const gfx::Rect & target_bounds_in_root,base::OnceCallback<void (bool)> callback)1061 void KeyboardUIController::SetContainerType(
1062 ContainerType type,
1063 const gfx::Rect& target_bounds_in_root,
1064 base::OnceCallback<void(bool)> callback) {
1065 if (container_behavior_->GetType() == type) {
1066 std::move(callback).Run(false);
1067 return;
1068 }
1069
1070 if (model_.state() == KeyboardUIState::kShown) {
1071 // Keyboard is already shown. Hiding the keyboard at first then switching
1072 // container type.
1073 queued_container_type_ = std::make_unique<QueuedContainerType>(
1074 this, type, target_bounds_in_root, std::move(callback));
1075 HideKeyboard(HIDE_REASON_SYSTEM_TEMPORARY);
1076 } else {
1077 // Keyboard is hidden. Switching the container type immediately and invoking
1078 // the passed callback now.
1079 SetContainerBehaviorInternal(type);
1080 SetKeyboardWindowBounds(target_bounds_in_root);
1081 DCHECK_EQ(GetActiveContainerType(), type);
1082 std::move(callback).Run(true /* change_successful */);
1083 }
1084 }
1085
RecordUkmKeyboardShown()1086 void KeyboardUIController::RecordUkmKeyboardShown() {
1087 ui::TextInputClient* text_input_client = GetTextInputClient();
1088 if (!text_input_client)
1089 return;
1090
1091 keyboard::RecordUkmKeyboardShown(
1092 text_input_client->GetClientSourceForMetrics(),
1093 text_input_client->GetTextInputType());
1094 }
1095
SetDraggableArea(const gfx::Rect & rect)1096 void KeyboardUIController::SetDraggableArea(const gfx::Rect& rect) {
1097 container_behavior_->SetDraggableArea(rect);
1098 }
1099
IsKeyboardVisible()1100 bool KeyboardUIController::IsKeyboardVisible() {
1101 if (model_.state() == KeyboardUIState::kShown) {
1102 DCHECK(IsEnabled());
1103 return true;
1104 }
1105 return false;
1106 }
1107
GetTextInputClient()1108 ui::TextInputClient* KeyboardUIController::GetTextInputClient() {
1109 return ui_->GetInputMethod()->GetTextInputClient();
1110 }
1111
UpdateInputMethodObserver()1112 void KeyboardUIController::UpdateInputMethodObserver() {
1113 ui::InputMethod* ime = ui_->GetInputMethod();
1114
1115 // IME could be null during initialization. Ignoring the case is okay because
1116 // UpdateInputMethodObserver() will be called later on.
1117 if (!ime)
1118 return;
1119
1120 if (ime_observer_.IsObserving(ime))
1121 return;
1122
1123 // Only observes the current active IME.
1124 ime_observer_.RemoveAll();
1125 ime_observer_.Add(ime);
1126
1127 // Note: We used to call OnTextInputStateChanged(ime->GetTextInputClient())
1128 // here, but that can trigger HideKeyboardImplicitlyBySystem() from a call to
1129 // ShowKeyboard() when using mojo APIs in Chrome (SingleProcessMash) if
1130 // ime->GetTextInputClient() isn't focused.
1131 }
1132
EnsureCaretInWorkArea(const gfx::Rect & occluded_bounds_in_screen)1133 void KeyboardUIController::EnsureCaretInWorkArea(
1134 const gfx::Rect& occluded_bounds_in_screen) {
1135 ui::InputMethod* ime = ui_->GetInputMethod();
1136 if (!ime)
1137 return;
1138
1139 TRACE_EVENT0("vk", "EnsureCaretInWorkArea");
1140
1141 if (IsOverscrollAllowed()) {
1142 ime->SetOnScreenKeyboardBounds(occluded_bounds_in_screen);
1143 } else if (ime->GetTextInputClient()) {
1144 ime->GetTextInputClient()->EnsureCaretNotInRect(occluded_bounds_in_screen);
1145 }
1146 }
1147
MarkKeyboardLoadStarted()1148 void KeyboardUIController::MarkKeyboardLoadStarted() {
1149 if (!keyboard_load_time_logged_)
1150 keyboard_load_time_start_ = base::Time::Now();
1151 }
1152
MarkKeyboardLoadFinished()1153 void KeyboardUIController::MarkKeyboardLoadFinished() {
1154 // Possible to get a load finished without a start if navigating directly to
1155 // chrome://keyboard.
1156 if (keyboard_load_time_start_.is_null())
1157 return;
1158
1159 if (keyboard_load_time_logged_)
1160 return;
1161
1162 // Log the delta only once.
1163 UMA_HISTOGRAM_TIMES("VirtualKeyboard.InitLatency.FirstLoad",
1164 base::Time::Now() - keyboard_load_time_start_);
1165 keyboard_load_time_logged_ = true;
1166 }
1167
EnableFlagsChanged()1168 void KeyboardUIController::EnableFlagsChanged() {
1169 for (auto& observer : observer_list_)
1170 observer.OnKeyboardEnableFlagsChanged(keyboard_enable_flags_);
1171 }
1172
1173 } // namespace keyboard
1174