Update V8 to version 4.7.47.
[chromium-blink-merge.git] / ui / keyboard / keyboard_util.cc
blobecc8e29c52f05036d197dbebf22f2ea59b651f55
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"
7 #include <string>
9 #include "base/basictypes.h"
10 #include "base/command_line.h"
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string16.h"
15 #include "grit/keyboard_resources.h"
16 #include "grit/keyboard_resources_map.h"
17 #include "media/audio/audio_manager.h"
18 #include "ui/aura/client/aura_constants.h"
19 #include "ui/aura/window_tree_host.h"
20 #include "ui/base/ime/input_method.h"
21 #include "ui/base/ime/text_input_client.h"
22 #include "ui/events/event_processor.h"
23 #include "ui/events/event_utils.h"
24 #include "ui/events/keycodes/dom/dom_code.h"
25 #include "ui/events/keycodes/dom/dom_key.h"
26 #include "ui/events/keycodes/dom/keycode_converter.h"
27 #include "ui/events/keycodes/keyboard_code_conversion.h"
28 #include "ui/keyboard/keyboard_controller.h"
29 #include "ui/keyboard/keyboard_controller_proxy.h"
30 #include "ui/keyboard/keyboard_switches.h"
31 #include "url/gurl.h"
33 namespace {
35 const char kKeyDown[] ="keydown";
36 const char kKeyUp[] = "keyup";
38 void SendProcessKeyEvent(ui::EventType type,
39 aura::WindowTreeHost* host) {
40 ui::KeyEvent event(type, ui::VKEY_PROCESSKEY, ui::DomCode::NONE,
41 ui::EF_IS_SYNTHESIZED, ui::DomKey::PROCESS,
42 ui::EventTimeForNow());
43 ui::EventDispatchDetails details =
44 host->event_processor()->OnEventFromSource(&event);
45 CHECK(!details.dispatcher_destroyed);
48 base::LazyInstance<base::Time> g_keyboard_load_time_start =
49 LAZY_INSTANCE_INITIALIZER;
51 bool g_accessibility_keyboard_enabled = false;
53 bool g_hotrod_keyboard_enabled = false;
55 base::LazyInstance<GURL> g_override_content_url = LAZY_INSTANCE_INITIALIZER;
57 bool g_touch_keyboard_enabled = false;
59 keyboard::KeyboardState g_requested_keyboard_state =
60 keyboard::KEYBOARD_STATE_AUTO;
62 keyboard::KeyboardOverscrolOverride g_keyboard_overscroll_override =
63 keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_NONE;
65 keyboard::KeyboardShowOverride g_keyboard_show_override =
66 keyboard::KEYBOARD_SHOW_OVERRIDE_NONE;
68 } // namespace
70 namespace keyboard {
72 gfx::Rect FullWidthKeyboardBoundsFromRootBounds(const gfx::Rect& root_bounds,
73 int keyboard_height) {
74 return gfx::Rect(
75 root_bounds.x(),
76 root_bounds.bottom() - keyboard_height,
77 root_bounds.width(),
78 keyboard_height);
81 void SetAccessibilityKeyboardEnabled(bool enabled) {
82 g_accessibility_keyboard_enabled = enabled;
85 bool GetAccessibilityKeyboardEnabled() {
86 return g_accessibility_keyboard_enabled;
89 void SetHotrodKeyboardEnabled(bool enabled) {
90 g_hotrod_keyboard_enabled = enabled;
93 bool GetHotrodKeyboardEnabled() {
94 return g_hotrod_keyboard_enabled;
97 void SetTouchKeyboardEnabled(bool enabled) {
98 g_touch_keyboard_enabled = enabled;
101 bool GetTouchKeyboardEnabled() {
102 return g_touch_keyboard_enabled;
105 void SetRequestedKeyboardState(KeyboardState state) {
106 g_requested_keyboard_state = state;
109 KeyboardState GetKeyboardRequestedState() {
110 return g_requested_keyboard_state;
113 std::string GetKeyboardLayout() {
114 // TODO(bshe): layout string is currently hard coded. We should use more
115 // standard keyboard layouts.
116 return GetAccessibilityKeyboardEnabled() ? "system-qwerty" : "qwerty";
119 bool IsKeyboardEnabled() {
120 // Accessibility setting prioritized over policy setting.
121 if (g_accessibility_keyboard_enabled)
122 return true;
123 // Policy strictly disables showing a virtual keyboard.
124 if (g_keyboard_show_override == keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED)
125 return false;
126 // Policy strictly enables the keyboard.
127 if (g_keyboard_show_override == keyboard::KEYBOARD_SHOW_OVERRIDE_ENABLED)
128 return true;
129 // Run-time flag to enable keyboard has been included.
130 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
131 switches::kEnableVirtualKeyboard))
132 return true;
133 // Requested state from the application layer.
134 if (g_requested_keyboard_state == keyboard::KEYBOARD_STATE_DISABLED)
135 return false;
136 // Check if any of the other flags are enabled.
137 return g_touch_keyboard_enabled ||
138 g_requested_keyboard_state == keyboard::KEYBOARD_STATE_ENABLED;
141 bool IsKeyboardOverscrollEnabled() {
142 if (!IsKeyboardEnabled())
143 return false;
145 // Users of the accessibility on-screen keyboard are likely to be using mouse
146 // input, which may interfere with overscrolling.
147 if (g_accessibility_keyboard_enabled)
148 return false;
150 // If overscroll enabled override is set, use it instead. Currently
151 // login / out-of-box disable keyboard overscroll. http://crbug.com/363635
152 if (g_keyboard_overscroll_override != KEYBOARD_OVERSCROLL_OVERRIDE_NONE) {
153 return g_keyboard_overscroll_override ==
154 KEYBOARD_OVERSCROLL_OVERRIDE_ENABLED;
157 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
158 switches::kDisableVirtualKeyboardOverscroll)) {
159 return false;
161 return true;
164 void SetKeyboardOverscrollOverride(KeyboardOverscrolOverride override) {
165 g_keyboard_overscroll_override = override;
168 void SetKeyboardShowOverride(KeyboardShowOverride override) {
169 g_keyboard_show_override = override;
172 bool IsInputViewEnabled() {
173 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
174 switches::kEnableInputView))
175 return true;
176 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
177 switches::kDisableInputView))
178 return false;
179 // Default value if no command line flags specified.
180 return true;
183 bool IsExperimentalInputViewEnabled() {
184 return base::CommandLine::ForCurrentProcess()->HasSwitch(
185 switches::kEnableExperimentalInputViewFeatures);
188 bool IsFloatingVirtualKeyboardEnabled() {
189 std::string floating_virtual_keyboard_switch =
190 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
191 switches::kFloatingVirtualKeyboard);
192 return floating_virtual_keyboard_switch ==
193 switches::kFloatingVirtualKeyboardEnabled;
196 bool IsGestureTypingEnabled() {
197 return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
198 switches::kGestureTyping) != switches::kGestureTypingDisabled;
201 bool IsGestureEditingEnabled() {
202 std::string keyboard_switch =
203 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
204 switches::kGestureEditing);
205 return keyboard_switch != switches::kGestureEditingDisabled;
208 bool IsSmartDeployEnabled() {
209 std::string keyboard_switch =
210 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
211 switches::kSmartVirtualKeyboard);
212 return keyboard_switch != switches::kSmartVirtualKeyboardDisabled;
215 bool IsVoiceInputEnabled() {
216 return media::AudioManager::Get()->HasAudioInputDevices() &&
217 !base::CommandLine::ForCurrentProcess()->HasSwitch(
218 switches::kDisableVoiceInput);
221 bool InsertText(const base::string16& text) {
222 keyboard::KeyboardController* controller = KeyboardController::GetInstance();
223 if (!controller)
224 return false;
226 ui::InputMethod* input_method = controller->proxy()->GetInputMethod();
227 if (!input_method)
228 return false;
230 ui::TextInputClient* tic = input_method->GetTextInputClient();
231 if (!tic || tic->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
232 return false;
234 tic->InsertText(text);
236 return true;
239 // TODO(varunjain): It would be cleaner to have something in the
240 // ui::TextInputClient interface, say MoveCaretInDirection(). The code in
241 // here would get the ui::InputMethod from the root_window, and the
242 // ui::TextInputClient from that (see above in InsertText()).
243 bool MoveCursor(int swipe_direction,
244 int modifier_flags,
245 aura::WindowTreeHost* host) {
246 if (!host)
247 return false;
248 ui::DomCode domcodex = ui::DomCode::NONE;
249 ui::DomCode domcodey = ui::DomCode::NONE;
250 if (swipe_direction & kCursorMoveRight)
251 domcodex = ui::DomCode::ARROW_RIGHT;
252 else if (swipe_direction & kCursorMoveLeft)
253 domcodex = ui::DomCode::ARROW_LEFT;
255 if (swipe_direction & kCursorMoveUp)
256 domcodey = ui::DomCode::ARROW_UP;
257 else if (swipe_direction & kCursorMoveDown)
258 domcodey = ui::DomCode::ARROW_DOWN;
260 // First deal with the x movement.
261 if (domcodex != ui::DomCode::NONE) {
262 ui::KeyboardCode codex = ui::VKEY_UNKNOWN;
263 ui::DomKey domkeyx = ui::DomKey::NONE;
264 ignore_result(DomCodeToUsLayoutDomKey(domcodex, ui::EF_NONE, &domkeyx,
265 &codex));
266 ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codex, domcodex,
267 modifier_flags, domkeyx,
268 ui::EventTimeForNow());
269 ui::EventDispatchDetails details =
270 host->event_processor()->OnEventFromSource(&press_event);
271 CHECK(!details.dispatcher_destroyed);
272 ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codex, domcodex,
273 modifier_flags, domkeyx,
274 ui::EventTimeForNow());
275 details = host->event_processor()->OnEventFromSource(&release_event);
276 CHECK(!details.dispatcher_destroyed);
279 // Then deal with the y movement.
280 if (domcodey != ui::DomCode::NONE) {
281 ui::KeyboardCode codey = ui::VKEY_UNKNOWN;
282 ui::DomKey domkeyy = ui::DomKey::NONE;
283 ignore_result(DomCodeToUsLayoutDomKey(domcodey, ui::EF_NONE, &domkeyy,
284 &codey));
285 ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codey, domcodey,
286 modifier_flags, domkeyy,
287 ui::EventTimeForNow());
288 ui::EventDispatchDetails details =
289 host->event_processor()->OnEventFromSource(&press_event);
290 CHECK(!details.dispatcher_destroyed);
291 ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codey, domcodey,
292 modifier_flags, domkeyy,
293 ui::EventTimeForNow());
294 details = host->event_processor()->OnEventFromSource(&release_event);
295 CHECK(!details.dispatcher_destroyed);
297 return true;
300 bool SendKeyEvent(const std::string type,
301 int key_value,
302 int key_code,
303 std::string key_name,
304 int modifiers,
305 aura::WindowTreeHost* host) {
306 ui::EventType event_type = ui::ET_UNKNOWN;
307 if (type == kKeyDown)
308 event_type = ui::ET_KEY_PRESSED;
309 else if (type == kKeyUp)
310 event_type = ui::ET_KEY_RELEASED;
311 if (event_type == ui::ET_UNKNOWN)
312 return false;
314 ui::KeyboardCode code = static_cast<ui::KeyboardCode>(key_code);
316 ui::InputMethod* input_method = host->GetInputMethod();
317 if (code == ui::VKEY_UNKNOWN) {
318 // Handling of special printable characters (e.g. accented characters) for
319 // which there is no key code.
320 if (event_type == ui::ET_KEY_RELEASED) {
321 if (!input_method)
322 return false;
324 ui::TextInputClient* tic = input_method->GetTextInputClient();
326 SendProcessKeyEvent(ui::ET_KEY_PRESSED, host);
327 tic->InsertChar(static_cast<uint16>(key_value), ui::EF_NONE);
328 SendProcessKeyEvent(ui::ET_KEY_RELEASED, host);
330 } else {
331 if (event_type == ui::ET_KEY_RELEASED) {
332 // The number of key press events seen since the last backspace.
333 static int keys_seen = 0;
334 if (code == ui::VKEY_BACK) {
335 // Log the rough lengths of characters typed between backspaces. This
336 // metric will be used to determine the error rate for the keyboard.
337 UMA_HISTOGRAM_CUSTOM_COUNTS(
338 "VirtualKeyboard.KeystrokesBetweenBackspaces",
339 keys_seen, 1, 1000, 50);
340 keys_seen = 0;
341 } else {
342 ++keys_seen;
346 ui::DomCode dom_code = ui::DomCode::NONE;
347 if (!key_name.empty())
348 dom_code = ui::KeycodeConverter::CodeStringToDomCode(key_name.c_str());
349 if (dom_code == ui::DomCode::NONE)
350 dom_code = ui::UsLayoutKeyboardCodeToDomCode(code);
351 CHECK(dom_code != ui::DomCode::NONE);
352 ui::KeyEvent event(
353 event_type,
354 code,
355 dom_code,
356 modifiers);
357 if (input_method) {
358 input_method->DispatchKeyEvent(&event);
359 } else {
360 ui::EventDispatchDetails details =
361 host->event_processor()->OnEventFromSource(&event);
362 CHECK(!details.dispatcher_destroyed);
365 return true;
368 void MarkKeyboardLoadStarted() {
369 if (!g_keyboard_load_time_start.Get().ToInternalValue())
370 g_keyboard_load_time_start.Get() = base::Time::Now();
373 void MarkKeyboardLoadFinished() {
374 // Possible to get a load finished without a start if navigating directly to
375 // chrome://keyboard.
376 if (!g_keyboard_load_time_start.Get().ToInternalValue())
377 return;
379 // It should not be possible to finish loading the keyboard without starting
380 // to load it first.
381 DCHECK(g_keyboard_load_time_start.Get().ToInternalValue());
383 static bool logged = false;
384 if (!logged) {
385 // Log the delta only once.
386 UMA_HISTOGRAM_TIMES(
387 "VirtualKeyboard.FirstLoadTime",
388 base::Time::Now() - g_keyboard_load_time_start.Get());
389 logged = true;
393 const GritResourceMap* GetKeyboardExtensionResources(size_t* size) {
394 // This looks a lot like the contents of a resource map; however it is
395 // necessary to have a custom path for the extension path, so the resource
396 // map cannot be used directly.
397 static const GritResourceMap kKeyboardResources[] = {
398 {"keyboard/locales/en.js", IDR_KEYBOARD_LOCALES_EN},
399 {"keyboard/config/emoji.js", IDR_KEYBOARD_CONFIG_EMOJI},
400 {"keyboard/config/hwt.js", IDR_KEYBOARD_CONFIG_HWT},
401 {"keyboard/config/us.js", IDR_KEYBOARD_CONFIG_US},
402 {"keyboard/emoji.css", IDR_KEYBOARD_CSS_EMOJI},
403 {"keyboard/images/3dots.png", IDR_KEYBOARD_IMAGES_3_DOTS},
404 {"keyboard/images/back_to_keyboard.png",
405 IDR_KEYBOARD_IMAGES_BACK_TO_KEYBOARD},
406 {"keyboard/images/backspace.png", IDR_KEYBOARD_IMAGES_BACKSPACE},
407 {"keyboard/images/car.png", IDR_KEYBOARD_IMAGES_CAR},
408 {"keyboard/images/check.png", IDR_KEYBOARD_IMAGES_CHECK},
409 {"keyboard/images/check_in_menu.png", IDR_KEYBOARD_IMAGES_CHECK_IN_MENU},
410 {"keyboard/images/compact.png", IDR_KEYBOARD_IMAGES_COMPACT},
411 {"keyboard/images/down.png", IDR_KEYBOARD_IMAGES_DOWN},
412 {"keyboard/images/emoji.png", IDR_KEYBOARD_IMAGES_EMOJI},
413 {"keyboard/images/emoji_car.png", IDR_KEYBOARD_IMAGES_EMOJI_CAR},
414 {"keyboard/images/emoji_crown.png", IDR_KEYBOARD_IMAGES_EMOJI_CROWN},
415 {"keyboard/images/emoji_emoticon.png",
416 IDR_KEYBOARD_IMAGES_EMOJI_EMOTICON},
417 {"keyboard/images/emoji_flower.png", IDR_KEYBOARD_IMAGES_EMOJI_FLOWER},
418 {"keyboard/images/emoji_hot.png", IDR_KEYBOARD_IMAGES_EMOJI_HOT},
419 {"keyboard/images/emoji_recent.png", IDR_KEYBOARD_IMAGES_EMOJI_RECENT},
420 {"keyboard/images/emoji_shape.png", IDR_KEYBOARD_IMAGES_EMOJI_SHAPE},
421 {"keyboard/images/emoji_cat_items.png", IDR_KEYBOARD_IMAGES_CAT},
422 {"keyboard/images/emoticon.png", IDR_KEYBOARD_IMAGES_EMOTICON},
423 {"keyboard/images/enter.png", IDR_KEYBOARD_IMAGES_RETURN},
424 {"keyboard/images/error.png", IDR_KEYBOARD_IMAGES_ERROR},
425 {"keyboard/images/favorit.png", IDR_KEYBOARD_IMAGES_FAVORITE},
426 {"keyboard/images/flower.png", IDR_KEYBOARD_IMAGES_FLOWER},
427 {"keyboard/images/globe.png", IDR_KEYBOARD_IMAGES_GLOBE},
428 {"keyboard/images/hide.png", IDR_KEYBOARD_IMAGES_HIDE},
429 {"keyboard/images/hidekeyboard.png", IDR_KEYBOARD_IMAGES_HIDE_KEYBOARD},
430 {"keyboard/images/keyboard.svg", IDR_KEYBOARD_IMAGES_KEYBOARD},
431 {"keyboard/images/left.png", IDR_KEYBOARD_IMAGES_LEFT},
432 {"keyboard/images/penci.png", IDR_KEYBOARD_IMAGES_PENCIL},
433 {"keyboard/images/recent.png", IDR_KEYBOARD_IMAGES_RECENT},
434 {"keyboard/images/regular_size.png", IDR_KEYBOARD_IMAGES_FULLSIZE},
435 {"keyboard/images/menu.png", IDR_KEYBOARD_IMAGES_MENU},
436 {"keyboard/images/pencil.png", IDR_KEYBOARD_IMAGES_PENCIL},
437 {"keyboard/images/right.png", IDR_KEYBOARD_IMAGES_RIGHT},
438 {"keyboard/images/search.png", IDR_KEYBOARD_IMAGES_SEARCH},
439 {"keyboard/images/select_right.png", IDR_KEYBOARD_IMAGES_SELECT_RIGHT},
440 {"keyboard/images/select_left.png", IDR_KEYBOARD_IMAGES_SELECT_LEFT},
441 {"keyboard/images/setting.png", IDR_KEYBOARD_IMAGES_SETTINGS},
442 {"keyboard/images/shift.png", IDR_KEYBOARD_IMAGES_SHIFT},
443 {"keyboard/images/space.png", IDR_KEYBOARD_IMAGES_SPACE},
444 {"keyboard/images/tab.png", IDR_KEYBOARD_IMAGES_TAB},
445 {"keyboard/images/tab_in_fullsize.png",
446 IDR_KEYBOARD_IMAGES_TAB_IN_FULLSIZE},
447 {"keyboard/images/triangle.png", IDR_KEYBOARD_IMAGES_TRIANGLE},
448 {"keyboard/images/up.png", IDR_KEYBOARD_IMAGES_UP},
449 {"keyboard/index.html", IDR_KEYBOARD_INDEX},
450 {"keyboard/inputview_adapter.js", IDR_KEYBOARD_INPUTVIEW_ADAPTER},
451 {"keyboard/inputview.css", IDR_KEYBOARD_INPUTVIEW_CSS},
452 {"keyboard/inputview.js", IDR_KEYBOARD_INPUTVIEW_JS},
453 {"keyboard/inputview_layouts/101kbd.js", IDR_KEYBOARD_LAYOUTS_101},
454 {"keyboard/inputview_layouts/compactkbd-qwerty.js",
455 IDR_KEYBOARD_LAYOUTS_COMPACT_QWERTY},
456 {"keyboard/inputview_layouts/compactkbd-numberpad.js",
457 IDR_KEYBOARD_LAYOUTS_COMPACT_NUMBERPAD},
458 {"keyboard/inputview_layouts/emoji.js", IDR_KEYBOARD_LAYOUTS_EMOJI},
459 {"keyboard/inputview_layouts/handwriting.js", IDR_KEYBOARD_LAYOUTS_HWT},
460 {"keyboard/manifest.json", IDR_KEYBOARD_MANIFEST},
461 {"keyboard/sounds/keypress-delete.wav",
462 IDR_KEYBOARD_SOUNDS_KEYPRESS_DELETE},
463 {"keyboard/sounds/keypress-return.wav",
464 IDR_KEYBOARD_SOUNDS_KEYPRESS_RETURN},
465 {"keyboard/sounds/keypress-spacebar.wav",
466 IDR_KEYBOARD_SOUNDS_KEYPRESS_SPACEBAR},
467 {"keyboard/sounds/keypress-standard.wav",
468 IDR_KEYBOARD_SOUNDS_KEYPRESS_STANDARD},
470 static const size_t kKeyboardResourcesSize = arraysize(kKeyboardResources);
471 *size = kKeyboardResourcesSize;
472 return kKeyboardResources;
475 void SetOverrideContentUrl(const GURL& url) {
476 g_override_content_url.Get() = url;
479 const GURL& GetOverrideContentUrl() {
480 return g_override_content_url.Get();
483 void LogKeyboardControlEvent(KeyboardControlEvent event) {
484 UMA_HISTOGRAM_ENUMERATION(
485 "VirtualKeyboard.KeyboardControlEvent",
486 event,
487 keyboard::KEYBOARD_CONTROL_MAX);
490 } // namespace keyboard