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.
5 #include "ui/keyboard/keyboard_util.h"
9 #include "base/command_line.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/string16.h"
14 #include "grit/keyboard_resources.h"
15 #include "grit/keyboard_resources_map.h"
16 #include "ui/aura/client/aura_constants.h"
17 #include "ui/aura/window_tree_host.h"
18 #include "ui/base/ime/input_method.h"
19 #include "ui/base/ime/text_input_client.h"
20 #include "ui/events/event_processor.h"
21 #include "ui/keyboard/keyboard_switches.h"
26 const char kKeyDown
[] ="keydown";
27 const char kKeyUp
[] = "keyup";
29 void SendProcessKeyEvent(ui::EventType type
,
30 aura::WindowTreeHost
* host
) {
31 ui::KeyEvent
event(type
, ui::VKEY_PROCESSKEY
, ui::EF_NONE
);
32 event
.SetTranslated(true);
33 ui::EventDispatchDetails details
=
34 host
->event_processor()->OnEventFromSource(&event
);
35 CHECK(!details
.dispatcher_destroyed
);
38 base::LazyInstance
<base::Time
> g_keyboard_load_time_start
=
39 LAZY_INSTANCE_INITIALIZER
;
41 bool g_accessibility_keyboard_enabled
= false;
43 base::LazyInstance
<GURL
> g_override_content_url
= LAZY_INSTANCE_INITIALIZER
;
45 bool g_touch_keyboard_enabled
= false;
47 keyboard::KeyboardOverscrolOverride g_keyboard_overscroll_override
=
48 keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_NONE
;
50 keyboard::KeyboardShowOverride g_keyboard_show_override
=
51 keyboard::KEYBOARD_SHOW_OVERRIDE_NONE
;
57 gfx::Rect
DefaultKeyboardBoundsFromWindowBounds(
58 const gfx::Rect
& window_bounds
) {
59 // Initialize default keyboard height to 0. The keyboard window height should
60 // only be set by window.resizeTo in virtual keyboard web contents. Otherwise,
61 // the default height may conflict with the new height and causing some
62 // strange animation issues. For keyboard usability experiments, a full screen
63 // virtual keyboard window is always preferred.
65 keyboard::IsKeyboardUsabilityExperimentEnabled() ?
66 window_bounds
.height() : 0;
68 return KeyboardBoundsFromWindowBounds(window_bounds
, keyboard_height
);
71 gfx::Rect
KeyboardBoundsFromWindowBounds(const gfx::Rect
& window_bounds
,
72 int keyboard_height
) {
75 window_bounds
.bottom() - keyboard_height
,
76 window_bounds
.width(),
80 void SetAccessibilityKeyboardEnabled(bool enabled
) {
81 g_accessibility_keyboard_enabled
= enabled
;
84 bool GetAccessibilityKeyboardEnabled() {
85 return g_accessibility_keyboard_enabled
;
88 void SetTouchKeyboardEnabled(bool enabled
) {
89 g_touch_keyboard_enabled
= enabled
;
92 bool GetTouchKeyboardEnabled() {
93 return g_touch_keyboard_enabled
;
96 std::string
GetKeyboardLayout() {
97 // TODO(bshe): layout string is currently hard coded. We should use more
98 // standard keyboard layouts.
99 return GetAccessibilityKeyboardEnabled() ? "system-qwerty" : "qwerty";
102 bool IsKeyboardEnabled() {
103 // Accessibility setting prioritized over policy setting.
104 if (g_accessibility_keyboard_enabled
)
106 // Policy strictly disables showing a virtual keyboard.
107 if (g_keyboard_show_override
== keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED
)
109 // Check if any of the flags are enabled.
110 return CommandLine::ForCurrentProcess()->HasSwitch(
111 switches::kEnableVirtualKeyboard
) ||
112 IsKeyboardUsabilityExperimentEnabled() ||
113 g_touch_keyboard_enabled
||
114 (g_keyboard_show_override
== keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED
);
117 bool IsKeyboardUsabilityExperimentEnabled() {
118 return CommandLine::ForCurrentProcess()->HasSwitch(
119 switches::kKeyboardUsabilityExperiment
);
122 bool IsKeyboardOverscrollEnabled() {
123 if (!IsKeyboardEnabled())
126 // Users of the accessibility on-screen keyboard are likely to be using mouse
127 // input, which may interfere with overscrolling.
128 if (g_accessibility_keyboard_enabled
)
131 // If overscroll enabled override is set, use it instead. Currently
132 // login / out-of-box disable keyboard overscroll. http://crbug.com/363635
133 if (g_keyboard_overscroll_override
!= KEYBOARD_OVERSCROLL_OVERRIDE_NONE
) {
134 return g_keyboard_overscroll_override
==
135 KEYBOARD_OVERSCROLL_OVERRIDE_ENABLED
;
138 if (CommandLine::ForCurrentProcess()->HasSwitch(
139 switches::kDisableVirtualKeyboardOverscroll
)) {
145 void SetKeyboardOverscrollOverride(KeyboardOverscrolOverride override
) {
146 g_keyboard_overscroll_override
= override
;
149 void SetKeyboardShowOverride(KeyboardShowOverride override
) {
150 g_keyboard_show_override
= override
;
153 bool IsInputViewEnabled() {
154 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableInputView
))
156 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableInputView
))
158 // Default value if no command line flags specified.
162 bool IsExperimentalInputViewEnabled() {
163 if (CommandLine::ForCurrentProcess()->HasSwitch(
164 switches::kEnableExperimentalInputViewFeatures
)) {
170 bool InsertText(const base::string16
& text
, aura::Window
* root_window
) {
174 ui::InputMethod
* input_method
= root_window
->GetProperty(
175 aura::client::kRootWindowInputMethodKey
);
179 ui::TextInputClient
* tic
= input_method
->GetTextInputClient();
180 if (!tic
|| tic
->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE
)
183 tic
->InsertText(text
);
188 // TODO(varunjain): It would be cleaner to have something in the
189 // ui::TextInputClient interface, say MoveCaretInDirection(). The code in
190 // here would get the ui::InputMethod from the root_window, and the
191 // ui::TextInputClient from that (see above in InsertText()).
192 bool MoveCursor(int swipe_direction
,
194 aura::WindowTreeHost
* host
) {
197 ui::KeyboardCode codex
= ui::VKEY_UNKNOWN
;
198 ui::KeyboardCode codey
= ui::VKEY_UNKNOWN
;
199 if (swipe_direction
& kCursorMoveRight
)
200 codex
= ui::VKEY_RIGHT
;
201 else if (swipe_direction
& kCursorMoveLeft
)
202 codex
= ui::VKEY_LEFT
;
204 if (swipe_direction
& kCursorMoveUp
)
206 else if (swipe_direction
& kCursorMoveDown
)
207 codey
= ui::VKEY_DOWN
;
209 // First deal with the x movement.
210 if (codex
!= ui::VKEY_UNKNOWN
) {
211 ui::KeyEvent
press_event(ui::ET_KEY_PRESSED
, codex
, modifier_flags
);
212 ui::EventDispatchDetails details
=
213 host
->event_processor()->OnEventFromSource(&press_event
);
214 CHECK(!details
.dispatcher_destroyed
);
215 ui::KeyEvent
release_event(ui::ET_KEY_RELEASED
, codex
, modifier_flags
);
216 details
= host
->event_processor()->OnEventFromSource(&release_event
);
217 CHECK(!details
.dispatcher_destroyed
);
220 // Then deal with the y movement.
221 if (codey
!= ui::VKEY_UNKNOWN
) {
222 ui::KeyEvent
press_event(ui::ET_KEY_PRESSED
, codey
, modifier_flags
);
223 ui::EventDispatchDetails details
=
224 host
->event_processor()->OnEventFromSource(&press_event
);
225 CHECK(!details
.dispatcher_destroyed
);
226 ui::KeyEvent
release_event(ui::ET_KEY_RELEASED
, codey
, modifier_flags
);
227 details
= host
->event_processor()->OnEventFromSource(&release_event
);
228 CHECK(!details
.dispatcher_destroyed
);
233 bool SendKeyEvent(const std::string type
,
236 std::string key_name
,
238 aura::WindowTreeHost
* host
) {
239 ui::EventType event_type
= ui::ET_UNKNOWN
;
240 if (type
== kKeyDown
)
241 event_type
= ui::ET_KEY_PRESSED
;
242 else if (type
== kKeyUp
)
243 event_type
= ui::ET_KEY_RELEASED
;
244 if (event_type
== ui::ET_UNKNOWN
)
247 ui::KeyboardCode code
= static_cast<ui::KeyboardCode
>(key_code
);
249 if (code
== ui::VKEY_UNKNOWN
) {
250 // Handling of special printable characters (e.g. accented characters) for
251 // which there is no key code.
252 if (event_type
== ui::ET_KEY_RELEASED
) {
253 ui::InputMethod
* input_method
= host
->window()->GetProperty(
254 aura::client::kRootWindowInputMethodKey
);
258 ui::TextInputClient
* tic
= input_method
->GetTextInputClient();
260 SendProcessKeyEvent(ui::ET_KEY_PRESSED
, host
);
261 tic
->InsertChar(static_cast<uint16
>(key_value
), ui::EF_NONE
);
262 SendProcessKeyEvent(ui::ET_KEY_RELEASED
, host
);
265 if (event_type
== ui::ET_KEY_RELEASED
) {
266 // The number of key press events seen since the last backspace.
267 static int keys_seen
= 0;
268 if (code
== ui::VKEY_BACK
) {
269 // Log the rough lengths of characters typed between backspaces. This
270 // metric will be used to determine the error rate for the keyboard.
271 UMA_HISTOGRAM_CUSTOM_COUNTS(
272 "VirtualKeyboard.KeystrokesBetweenBackspaces",
273 keys_seen
, 1, 1000, 50);
280 ui::KeyEvent
event(event_type
, code
, key_name
, modifiers
);
281 ui::EventDispatchDetails details
=
282 host
->event_processor()->OnEventFromSource(&event
);
283 CHECK(!details
.dispatcher_destroyed
);
288 const void MarkKeyboardLoadStarted() {
289 if (!g_keyboard_load_time_start
.Get().ToInternalValue())
290 g_keyboard_load_time_start
.Get() = base::Time::Now();
293 const void MarkKeyboardLoadFinished() {
294 // Possible to get a load finished without a start if navigating directly to
295 // chrome://keyboard.
296 if (!g_keyboard_load_time_start
.Get().ToInternalValue())
299 // It should not be possible to finish loading the keyboard without starting
301 DCHECK(g_keyboard_load_time_start
.Get().ToInternalValue());
303 static bool logged
= false;
305 // Log the delta only once.
307 "VirtualKeyboard.FirstLoadTime",
308 base::Time::Now() - g_keyboard_load_time_start
.Get());
313 const GritResourceMap
* GetKeyboardExtensionResources(size_t* size
) {
314 // This looks a lot like the contents of a resource map; however it is
315 // necessary to have a custom path for the extension path, so the resource
316 // map cannot be used directly.
317 static const GritResourceMap kKeyboardResources
[] = {
318 {"keyboard/layouts/function-key-row.html", IDR_KEYBOARD_FUNCTION_KEY_ROW
},
319 {"keyboard/images/back.svg", IDR_KEYBOARD_IMAGES_BACK
},
320 {"keyboard/images/backspace.png", IDR_KEYBOARD_IMAGES_BACKSPACE
},
321 {"keyboard/images/brightness-down.svg",
322 IDR_KEYBOARD_IMAGES_BRIGHTNESS_DOWN
},
323 {"keyboard/images/brightness-up.svg", IDR_KEYBOARD_IMAGES_BRIGHTNESS_UP
},
324 {"keyboard/images/change-window.svg", IDR_KEYBOARD_IMAGES_CHANGE_WINDOW
},
325 {"keyboard/images/down.svg", IDR_KEYBOARD_IMAGES_DOWN
},
326 {"keyboard/images/forward.svg", IDR_KEYBOARD_IMAGES_FORWARD
},
327 {"keyboard/images/fullscreen.svg", IDR_KEYBOARD_IMAGES_FULLSCREEN
},
328 {"keyboard/images/hide-keyboard.png", IDR_KEYBOARD_IMAGES_HIDE_KEYBOARD
},
329 {"keyboard/images/keyboard.svg", IDR_KEYBOARD_IMAGES_KEYBOARD
},
330 {"keyboard/images/left.svg", IDR_KEYBOARD_IMAGES_LEFT
},
331 {"keyboard/images/microphone.svg", IDR_KEYBOARD_IMAGES_MICROPHONE
},
332 {"keyboard/images/microphone-green.svg",
333 IDR_KEYBOARD_IMAGES_MICROPHONE_GREEN
},
334 {"keyboard/images/mute.svg", IDR_KEYBOARD_IMAGES_MUTE
},
335 {"keyboard/images/reload.svg", IDR_KEYBOARD_IMAGES_RELOAD
},
336 {"keyboard/images/return.png", IDR_KEYBOARD_IMAGES_RETURN
},
337 {"keyboard/images/right.svg", IDR_KEYBOARD_IMAGES_RIGHT
},
338 {"keyboard/images/search.png", IDR_KEYBOARD_IMAGES_SEARCH
},
339 {"keyboard/images/shift.png", IDR_KEYBOARD_IMAGES_SHIFT
},
340 {"keyboard/images/shutdown.svg", IDR_KEYBOARD_IMAGES_SHUTDOWN
},
341 {"keyboard/images/tab.png", IDR_KEYBOARD_IMAGES_TAB
},
342 {"keyboard/images/up.svg", IDR_KEYBOARD_IMAGES_UP
},
343 {"keyboard/images/volume-down.svg", IDR_KEYBOARD_IMAGES_VOLUME_DOWN
},
344 {"keyboard/images/volume-up.svg", IDR_KEYBOARD_IMAGES_VOLUME_UP
},
345 {"keyboard/index.html", IDR_KEYBOARD_INDEX
},
346 {"keyboard/keyboard.js", IDR_KEYBOARD_JS
},
347 {"keyboard/layouts/numeric.html", IDR_KEYBOARD_LAYOUTS_NUMERIC
},
348 {"keyboard/layouts/qwerty.html", IDR_KEYBOARD_LAYOUTS_QWERTY
},
349 {"keyboard/layouts/system-qwerty.html", IDR_KEYBOARD_LAYOUTS_SYSTEM_QWERTY
},
350 {"keyboard/layouts/spacebar-row.html", IDR_KEYBOARD_SPACEBAR_ROW
},
351 {"keyboard/manifest.json", IDR_KEYBOARD_MANIFEST
},
352 {"keyboard/main.css", IDR_KEYBOARD_MAIN_CSS
},
353 {"keyboard/polymer_loader.js", IDR_KEYBOARD_POLYMER_LOADER
},
354 {"keyboard/roboto_bold.ttf", IDR_KEYBOARD_ROBOTO_BOLD_TTF
},
355 {"keyboard/sounds/keypress-delete.wav",
356 IDR_KEYBOARD_SOUNDS_KEYPRESS_DELETE
},
357 {"keyboard/sounds/keypress-return.wav",
358 IDR_KEYBOARD_SOUNDS_KEYPRESS_RETURN
},
359 {"keyboard/sounds/keypress-spacebar.wav",
360 IDR_KEYBOARD_SOUNDS_KEYPRESS_SPACEBAR
},
361 {"keyboard/sounds/keypress-standard.wav",
362 IDR_KEYBOARD_SOUNDS_KEYPRESS_STANDARD
},
364 static const size_t kKeyboardResourcesSize
= arraysize(kKeyboardResources
);
365 *size
= kKeyboardResourcesSize
;
366 return kKeyboardResources
;
369 void SetOverrideContentUrl(const GURL
& url
) {
370 g_override_content_url
.Get() = url
;
373 const GURL
& GetOverrideContentUrl() {
374 return g_override_content_url
.Get();
377 void LogKeyboardControlEvent(KeyboardControlEvent event
) {
378 UMA_HISTOGRAM_ENUMERATION(
379 "VirtualKeyboard.KeyboardControlEvent",
381 keyboard::KEYBOARD_CONTROL_MAX
);
384 } // namespace keyboard