• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SendProcessKeyEvent(ui::EventType type,aura::WindowTreeHost * host)29 void SendProcessKeyEvent(ui::EventType type,
30                          aura::WindowTreeHost* host) {
31   ui::KeyEvent event(type, ui::VKEY_PROCESSKEY, ui::EF_NONE, false);
32   event.SetTranslated(true);
33   ui::EventDispatchDetails details =
34       host->event_processor()->OnEventFromSource(&event);
35   CHECK(!details.dispatcher_destroyed);
36 }
37 
38 base::LazyInstance<base::Time> g_keyboard_load_time_start =
39     LAZY_INSTANCE_INITIALIZER;
40 
41 bool g_accessibility_keyboard_enabled = false;
42 
43 base::LazyInstance<GURL> g_override_content_url = LAZY_INSTANCE_INITIALIZER;
44 
45 bool g_touch_keyboard_enabled = false;
46 
47 keyboard::KeyboardOverscrolOverride g_keyboard_overscroll_override =
48     keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_NONE;
49 
50 keyboard::KeyboardShowOverride g_keyboard_show_override =
51     keyboard::KEYBOARD_SHOW_OVERRIDE_NONE;
52 
53 }  // namespace
54 
55 namespace keyboard {
56 
DefaultKeyboardBoundsFromWindowBounds(const gfx::Rect & window_bounds)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.
64   int keyboard_height =
65       keyboard::IsKeyboardUsabilityExperimentEnabled() ?
66           window_bounds.height() : 0;
67 
68   return KeyboardBoundsFromWindowBounds(window_bounds, keyboard_height);
69 }
70 
KeyboardBoundsFromWindowBounds(const gfx::Rect & window_bounds,int keyboard_height)71 gfx::Rect KeyboardBoundsFromWindowBounds(const gfx::Rect& window_bounds,
72                                          int keyboard_height) {
73   return gfx::Rect(
74       window_bounds.x(),
75       window_bounds.bottom() - keyboard_height,
76       window_bounds.width(),
77       keyboard_height);
78 }
79 
SetAccessibilityKeyboardEnabled(bool enabled)80 void SetAccessibilityKeyboardEnabled(bool enabled) {
81   g_accessibility_keyboard_enabled = enabled;
82 }
83 
GetAccessibilityKeyboardEnabled()84 bool GetAccessibilityKeyboardEnabled() {
85   return g_accessibility_keyboard_enabled;
86 }
87 
SetTouchKeyboardEnabled(bool enabled)88 void SetTouchKeyboardEnabled(bool enabled) {
89   g_touch_keyboard_enabled = enabled;
90 }
91 
GetTouchKeyboardEnabled()92 bool GetTouchKeyboardEnabled() {
93   return g_touch_keyboard_enabled;
94 }
95 
GetKeyboardLayout()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";
100 }
101 
IsKeyboardEnabled()102 bool IsKeyboardEnabled() {
103   // Accessibility setting prioritized over policy setting.
104   if (g_accessibility_keyboard_enabled)
105     return true;
106   // Policy strictly disables showing a virtual keyboard.
107   if (g_keyboard_show_override == keyboard::KEYBOARD_SHOW_OVERRIDE_DISABLED)
108     return false;
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);
115 }
116 
IsKeyboardUsabilityExperimentEnabled()117 bool IsKeyboardUsabilityExperimentEnabled() {
118   return CommandLine::ForCurrentProcess()->HasSwitch(
119       switches::kKeyboardUsabilityExperiment);
120 }
121 
IsKeyboardOverscrollEnabled()122 bool IsKeyboardOverscrollEnabled() {
123   if (!IsKeyboardEnabled())
124     return false;
125 
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)
129     return false;
130 
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;
136   }
137 
138   if (CommandLine::ForCurrentProcess()->HasSwitch(
139       switches::kDisableVirtualKeyboardOverscroll)) {
140     return false;
141   }
142   return true;
143 }
144 
SetKeyboardOverscrollOverride(KeyboardOverscrolOverride override)145 void SetKeyboardOverscrollOverride(KeyboardOverscrolOverride override) {
146   g_keyboard_overscroll_override = override;
147 }
148 
SetKeyboardShowOverride(KeyboardShowOverride override)149 void SetKeyboardShowOverride(KeyboardShowOverride override) {
150   g_keyboard_show_override = override;
151 }
152 
IsInputViewEnabled()153 bool IsInputViewEnabled() {
154   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableInputView))
155     return true;
156   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableInputView))
157     return false;
158   // Default value if no command line flags specified.
159   return true;
160 }
161 
IsExperimentalInputViewEnabled()162 bool IsExperimentalInputViewEnabled() {
163   if (CommandLine::ForCurrentProcess()->HasSwitch(
164       switches::kEnableExperimentalInputViewFeatures)) {
165     return true;
166   }
167   return false;
168 }
169 
InsertText(const base::string16 & text,aura::Window * root_window)170 bool InsertText(const base::string16& text, aura::Window* root_window) {
171   if (!root_window)
172     return false;
173 
174   ui::InputMethod* input_method = root_window->GetProperty(
175       aura::client::kRootWindowInputMethodKey);
176   if (!input_method)
177     return false;
178 
179   ui::TextInputClient* tic = input_method->GetTextInputClient();
180   if (!tic || tic->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
181     return false;
182 
183   tic->InsertText(text);
184 
185   return true;
186 }
187 
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()).
MoveCursor(int swipe_direction,int modifier_flags,aura::WindowTreeHost * host)192 bool MoveCursor(int swipe_direction,
193                 int modifier_flags,
194                 aura::WindowTreeHost* host) {
195   if (!host)
196     return false;
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;
203 
204   if (swipe_direction & kCursorMoveUp)
205     codey = ui::VKEY_UP;
206   else if (swipe_direction & kCursorMoveDown)
207     codey = ui::VKEY_DOWN;
208 
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, 0);
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, 0);
216     details = host->event_processor()->OnEventFromSource(&release_event);
217     CHECK(!details.dispatcher_destroyed);
218   }
219 
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, 0);
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, 0);
227     details = host->event_processor()->OnEventFromSource(&release_event);
228     CHECK(!details.dispatcher_destroyed);
229   }
230   return true;
231 }
232 
SendKeyEvent(const std::string type,int key_value,int key_code,std::string key_name,int modifiers,aura::WindowTreeHost * host)233 bool SendKeyEvent(const std::string type,
234                   int key_value,
235                   int key_code,
236                   std::string key_name,
237                   int modifiers,
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)
245     return false;
246 
247   ui::KeyboardCode code = static_cast<ui::KeyboardCode>(key_code);
248 
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);
255       if (!input_method)
256         return false;
257 
258       ui::TextInputClient* tic = input_method->GetTextInputClient();
259 
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);
263     }
264   } else {
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);
274         keys_seen = 0;
275       } else {
276         ++keys_seen;
277       }
278     }
279 
280     ui::KeyEvent event(event_type, code, key_name, modifiers, false);
281     ui::EventDispatchDetails details =
282         host->event_processor()->OnEventFromSource(&event);
283     CHECK(!details.dispatcher_destroyed);
284   }
285   return true;
286 }
287 
MarkKeyboardLoadStarted()288 const void MarkKeyboardLoadStarted() {
289   if (!g_keyboard_load_time_start.Get().ToInternalValue())
290     g_keyboard_load_time_start.Get() = base::Time::Now();
291 }
292 
MarkKeyboardLoadFinished()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())
297     return;
298 
299   // It should not be possible to finish loading the keyboard without starting
300   // to load it first.
301   DCHECK(g_keyboard_load_time_start.Get().ToInternalValue());
302 
303   static bool logged = false;
304   if (!logged) {
305     // Log the delta only once.
306     UMA_HISTOGRAM_TIMES(
307         "VirtualKeyboard.FirstLoadTime",
308         base::Time::Now() - g_keyboard_load_time_start.Get());
309     logged = true;
310   }
311 }
312 
GetKeyboardExtensionResources(size_t * size)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},
363   };
364   static const size_t kKeyboardResourcesSize = arraysize(kKeyboardResources);
365   *size = kKeyboardResourcesSize;
366   return kKeyboardResources;
367 }
368 
SetOverrideContentUrl(const GURL & url)369 void SetOverrideContentUrl(const GURL& url) {
370   g_override_content_url.Get() = url;
371 }
372 
GetOverrideContentUrl()373 const GURL& GetOverrideContentUrl() {
374   return g_override_content_url.Get();
375 }
376 
LogKeyboardControlEvent(KeyboardControlEvent event)377 void LogKeyboardControlEvent(KeyboardControlEvent event) {
378   UMA_HISTOGRAM_ENUMERATION(
379       "VirtualKeyboard.KeyboardControlEvent",
380       event,
381       keyboard::KEYBOARD_CONTROL_MAX);
382 }
383 
384 }  // namespace keyboard
385