Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / keyboard / keyboard_util.cc
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 "ui/keyboard/keyboard_util.h"
6
7 #include <string>
8
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"
22 #include "url/gurl.h"
23
24 namespace {
25
26 const char kKeyDown[] ="keydown";
27 const char kKeyUp[] = "keyup";
28
29 void SendProcessKeyEvent(ui::EventType type,
30                          aura::WindowTreeHost* host) {
31   ui::TranslatedKeyEvent event(type == ui::ET_KEY_PRESSED,
32                                ui::VKEY_PROCESSKEY,
33                                ui::EF_NONE);
34   ui::EventDispatchDetails details =
35       host->event_processor()->OnEventFromSource(&event);
36   CHECK(!details.dispatcher_destroyed);
37 }
38
39 base::LazyInstance<base::Time> g_keyboard_load_time_start =
40     LAZY_INSTANCE_INITIALIZER;
41
42 bool g_accessibility_keyboard_enabled = false;
43
44 base::LazyInstance<GURL> g_override_content_url = LAZY_INSTANCE_INITIALIZER;
45
46 bool g_touch_keyboard_enabled = false;
47
48 }  // namespace
49
50 namespace keyboard {
51
52 gfx::Rect DefaultKeyboardBoundsFromWindowBounds(
53     const gfx::Rect& window_bounds) {
54   // Initialize default keyboard height to 0. The keyboard window height should
55   // only be set by window.resizeTo in virtual keyboard web contents. Otherwise,
56   // the default height may conflict with the new height and causing some
57   // strange animation issues. For keyboard usability experiments, a full screen
58   // virtual keyboard window is always preferred.
59   int keyboard_height =
60       keyboard::IsKeyboardUsabilityExperimentEnabled() ?
61           window_bounds.height() : 0;
62
63   return KeyboardBoundsFromWindowBounds(window_bounds, keyboard_height);
64 }
65
66 gfx::Rect KeyboardBoundsFromWindowBounds(const gfx::Rect& window_bounds,
67                                          int keyboard_height) {
68   return gfx::Rect(
69       window_bounds.x(),
70       window_bounds.bottom() - keyboard_height,
71       window_bounds.width(),
72       keyboard_height);
73 }
74
75 void SetAccessibilityKeyboardEnabled(bool enabled) {
76   g_accessibility_keyboard_enabled = enabled;
77 }
78
79 bool GetAccessibilityKeyboardEnabled() {
80   return g_accessibility_keyboard_enabled;
81 }
82
83 void SetTouchKeyboardEnabled(bool enabled) {
84   g_touch_keyboard_enabled = enabled;
85 }
86
87 bool GetTouchKeyboardEnabled() {
88   return g_touch_keyboard_enabled;
89 }
90
91 std::string GetKeyboardLayout() {
92   // TODO(bshe): layout string is currently hard coded. We should use more
93   // standard keyboard layouts.
94   return GetAccessibilityKeyboardEnabled() ? "system-qwerty" : "qwerty";
95 }
96
97 bool IsKeyboardEnabled() {
98   return g_accessibility_keyboard_enabled ||
99       CommandLine::ForCurrentProcess()->HasSwitch(
100           switches::kEnableVirtualKeyboard) ||
101       IsKeyboardUsabilityExperimentEnabled() ||
102       g_touch_keyboard_enabled;
103 }
104
105 bool IsKeyboardUsabilityExperimentEnabled() {
106   return CommandLine::ForCurrentProcess()->HasSwitch(
107       switches::kKeyboardUsabilityExperiment);
108 }
109
110 bool IsKeyboardOverscrollEnabled() {
111   if (!IsKeyboardEnabled())
112     return false;
113   // Users of the accessibility on-screen keyboard are likely to be using mouse
114   // input, which may interfere with overscrolling.
115   if (g_accessibility_keyboard_enabled)
116     return false;
117   if (CommandLine::ForCurrentProcess()->HasSwitch(
118       switches::kDisableVirtualKeyboardOverscroll)) {
119     return false;
120   }
121   return true;
122 }
123
124 bool IsInputViewEnabled() {
125   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableInputView))
126     return true;
127   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableInputView))
128     return false;
129   // Default value if no command line flags specified.
130   return true;
131 }
132
133 bool InsertText(const base::string16& text, aura::Window* root_window) {
134   if (!root_window)
135     return false;
136
137   ui::InputMethod* input_method = root_window->GetProperty(
138       aura::client::kRootWindowInputMethodKey);
139   if (!input_method)
140     return false;
141
142   ui::TextInputClient* tic = input_method->GetTextInputClient();
143   if (!tic || tic->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
144     return false;
145
146   tic->InsertText(text);
147
148   return true;
149 }
150
151 // TODO(varunjain): It would be cleaner to have something in the
152 // ui::TextInputClient interface, say MoveCaretInDirection(). The code in
153 // here would get the ui::InputMethod from the root_window, and the
154 // ui::TextInputClient from that (see above in InsertText()).
155 bool MoveCursor(int swipe_direction,
156                 int modifier_flags,
157                 aura::WindowTreeHost* host) {
158   if (!host)
159     return false;
160   ui::KeyboardCode codex = ui::VKEY_UNKNOWN;
161   ui::KeyboardCode codey = ui::VKEY_UNKNOWN;
162   if (swipe_direction & kCursorMoveRight)
163     codex = ui::VKEY_RIGHT;
164   else if (swipe_direction & kCursorMoveLeft)
165     codex = ui::VKEY_LEFT;
166
167   if (swipe_direction & kCursorMoveUp)
168     codey = ui::VKEY_UP;
169   else if (swipe_direction & kCursorMoveDown)
170     codey = ui::VKEY_DOWN;
171
172   // First deal with the x movement.
173   if (codex != ui::VKEY_UNKNOWN) {
174     ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codex, modifier_flags, 0);
175     ui::EventDispatchDetails details =
176         host->event_processor()->OnEventFromSource(&press_event);
177     CHECK(!details.dispatcher_destroyed);
178     ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codex, modifier_flags, 0);
179     details = host->event_processor()->OnEventFromSource(&release_event);
180     CHECK(!details.dispatcher_destroyed);
181   }
182
183   // Then deal with the y movement.
184   if (codey != ui::VKEY_UNKNOWN) {
185     ui::KeyEvent press_event(ui::ET_KEY_PRESSED, codey, modifier_flags, 0);
186     ui::EventDispatchDetails details =
187         host->event_processor()->OnEventFromSource(&press_event);
188     CHECK(!details.dispatcher_destroyed);
189     ui::KeyEvent release_event(ui::ET_KEY_RELEASED, codey, modifier_flags, 0);
190     details = host->event_processor()->OnEventFromSource(&release_event);
191     CHECK(!details.dispatcher_destroyed);
192   }
193   return true;
194 }
195
196 bool SendKeyEvent(const std::string type,
197                   int key_value,
198                   int key_code,
199                   std::string key_name,
200                   int modifiers,
201                   aura::WindowTreeHost* host) {
202   ui::EventType event_type = ui::ET_UNKNOWN;
203   if (type == kKeyDown)
204     event_type = ui::ET_KEY_PRESSED;
205   else if (type == kKeyUp)
206     event_type = ui::ET_KEY_RELEASED;
207   if (event_type == ui::ET_UNKNOWN)
208     return false;
209
210   ui::KeyboardCode code = static_cast<ui::KeyboardCode>(key_code);
211
212   if (code == ui::VKEY_UNKNOWN) {
213     // Handling of special printable characters (e.g. accented characters) for
214     // which there is no key code.
215     if (event_type == ui::ET_KEY_RELEASED) {
216       ui::InputMethod* input_method = host->window()->GetProperty(
217           aura::client::kRootWindowInputMethodKey);
218       if (!input_method)
219         return false;
220
221       ui::TextInputClient* tic = input_method->GetTextInputClient();
222
223       SendProcessKeyEvent(ui::ET_KEY_PRESSED, host);
224       tic->InsertChar(static_cast<uint16>(key_value), ui::EF_NONE);
225       SendProcessKeyEvent(ui::ET_KEY_RELEASED, host);
226     }
227   } else {
228     if (event_type == ui::ET_KEY_RELEASED) {
229       // The number of key press events seen since the last backspace.
230       static int keys_seen = 0;
231       if (code == ui::VKEY_BACK) {
232         // Log the rough lengths of characters typed between backspaces. This
233         // metric will be used to determine the error rate for the keyboard.
234         UMA_HISTOGRAM_CUSTOM_COUNTS(
235             "VirtualKeyboard.KeystrokesBetweenBackspaces",
236             keys_seen, 1, 1000, 50);
237         keys_seen = 0;
238       } else {
239         ++keys_seen;
240       }
241     }
242
243     ui::KeyEvent event(event_type, code, key_name, modifiers, false);
244     ui::EventDispatchDetails details =
245         host->event_processor()->OnEventFromSource(&event);
246     CHECK(!details.dispatcher_destroyed);
247   }
248   return true;
249 }
250
251 const void MarkKeyboardLoadStarted() {
252   if (!g_keyboard_load_time_start.Get().ToInternalValue())
253     g_keyboard_load_time_start.Get() = base::Time::Now();
254 }
255
256 const void MarkKeyboardLoadFinished() {
257   // Possible to get a load finished without a start if navigating directly to
258   // chrome://keyboard.
259   if (!g_keyboard_load_time_start.Get().ToInternalValue())
260     return;
261
262   // It should not be possible to finish loading the keyboard without starting
263   // to load it first.
264   DCHECK(g_keyboard_load_time_start.Get().ToInternalValue());
265
266   static bool logged = false;
267   if (!logged) {
268     // Log the delta only once.
269     UMA_HISTOGRAM_TIMES(
270         "VirtualKeyboard.FirstLoadTime",
271         base::Time::Now() - g_keyboard_load_time_start.Get());
272     logged = true;
273   }
274 }
275
276 const GritResourceMap* GetKeyboardExtensionResources(size_t* size) {
277   // This looks a lot like the contents of a resource map; however it is
278   // necessary to have a custom path for the extension path, so the resource
279   // map cannot be used directly.
280   static const GritResourceMap kKeyboardResources[] = {
281     {"keyboard/layouts/function-key-row.html", IDR_KEYBOARD_FUNCTION_KEY_ROW},
282     {"keyboard/images/back.svg", IDR_KEYBOARD_IMAGES_BACK},
283     {"keyboard/images/backspace.png", IDR_KEYBOARD_IMAGES_BACKSPACE},
284     {"keyboard/images/brightness-down.svg",
285         IDR_KEYBOARD_IMAGES_BRIGHTNESS_DOWN},
286     {"keyboard/images/brightness-up.svg", IDR_KEYBOARD_IMAGES_BRIGHTNESS_UP},
287     {"keyboard/images/change-window.svg", IDR_KEYBOARD_IMAGES_CHANGE_WINDOW},
288     {"keyboard/images/down.svg", IDR_KEYBOARD_IMAGES_DOWN},
289     {"keyboard/images/forward.svg", IDR_KEYBOARD_IMAGES_FORWARD},
290     {"keyboard/images/fullscreen.svg", IDR_KEYBOARD_IMAGES_FULLSCREEN},
291     {"keyboard/images/hide-keyboard.png", IDR_KEYBOARD_IMAGES_HIDE_KEYBOARD},
292     {"keyboard/images/keyboard.svg", IDR_KEYBOARD_IMAGES_KEYBOARD},
293     {"keyboard/images/left.svg", IDR_KEYBOARD_IMAGES_LEFT},
294     {"keyboard/images/microphone.svg", IDR_KEYBOARD_IMAGES_MICROPHONE},
295     {"keyboard/images/microphone-green.svg",
296         IDR_KEYBOARD_IMAGES_MICROPHONE_GREEN},
297     {"keyboard/images/mute.svg", IDR_KEYBOARD_IMAGES_MUTE},
298     {"keyboard/images/reload.svg", IDR_KEYBOARD_IMAGES_RELOAD},
299     {"keyboard/images/return.png", IDR_KEYBOARD_IMAGES_RETURN},
300     {"keyboard/images/right.svg", IDR_KEYBOARD_IMAGES_RIGHT},
301     {"keyboard/images/search.png", IDR_KEYBOARD_IMAGES_SEARCH},
302     {"keyboard/images/shift.png", IDR_KEYBOARD_IMAGES_SHIFT},
303     {"keyboard/images/shutdown.svg", IDR_KEYBOARD_IMAGES_SHUTDOWN},
304     {"keyboard/images/tab.png", IDR_KEYBOARD_IMAGES_TAB},
305     {"keyboard/images/up.svg", IDR_KEYBOARD_IMAGES_UP},
306     {"keyboard/images/volume-down.svg", IDR_KEYBOARD_IMAGES_VOLUME_DOWN},
307     {"keyboard/images/volume-up.svg", IDR_KEYBOARD_IMAGES_VOLUME_UP},
308     {"keyboard/index.html", IDR_KEYBOARD_INDEX},
309     {"keyboard/keyboard.js", IDR_KEYBOARD_JS},
310     {"keyboard/layouts/numeric.html", IDR_KEYBOARD_LAYOUTS_NUMERIC},
311     {"keyboard/layouts/qwerty.html", IDR_KEYBOARD_LAYOUTS_QWERTY},
312     {"keyboard/layouts/system-qwerty.html", IDR_KEYBOARD_LAYOUTS_SYSTEM_QWERTY},
313     {"keyboard/layouts/spacebar-row.html", IDR_KEYBOARD_SPACEBAR_ROW},
314     {"keyboard/manifest.json", IDR_KEYBOARD_MANIFEST},
315     {"keyboard/main.css", IDR_KEYBOARD_MAIN_CSS},
316     {"keyboard/polymer_loader.js", IDR_KEYBOARD_POLYMER_LOADER},
317     {"keyboard/roboto_bold.ttf", IDR_KEYBOARD_ROBOTO_BOLD_TTF},
318     {"keyboard/sounds/keypress-delete.wav",
319         IDR_KEYBOARD_SOUNDS_KEYPRESS_DELETE},
320     {"keyboard/sounds/keypress-return.wav",
321         IDR_KEYBOARD_SOUNDS_KEYPRESS_RETURN},
322     {"keyboard/sounds/keypress-spacebar.wav",
323         IDR_KEYBOARD_SOUNDS_KEYPRESS_SPACEBAR},
324     {"keyboard/sounds/keypress-standard.wav",
325         IDR_KEYBOARD_SOUNDS_KEYPRESS_STANDARD},
326   };
327   static const size_t kKeyboardResourcesSize = arraysize(kKeyboardResources);
328   *size = kKeyboardResourcesSize;
329   return kKeyboardResources;
330 }
331
332 void SetOverrideContentUrl(const GURL& url) {
333   g_override_content_url.Get() = url;
334 }
335
336 const GURL& GetOverrideContentUrl() {
337   return g_override_content_url.Get();
338 }
339
340 void LogKeyboardControlEvent(KeyboardControlEvent event) {
341   UMA_HISTOGRAM_ENUMERATION(
342       "VirtualKeyboard.KeyboardControlEvent",
343       event,
344       keyboard::KEYBOARD_CONTROL_MAX);
345 }
346
347 }  // namespace keyboard