| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2011 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 "chrome/test/webdriver/webdriver_key_converter.h" |
| 6 |
| 7 #include "base/utf_string_conversions.h" |
| 8 #include "chrome/common/automation_constants.h" |
| 9 |
| 10 namespace { |
| 11 |
| 12 // TODO(kkania): Use this in KeyMap. |
| 13 // Ordered list of all the key codes corresponding to special WebDriver keys. |
| 14 // These WebDriver keys are defined in the Unicode Private Use Area. |
| 15 // http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/e
lement/:id/value |
| 16 const ui::KeyboardCode kSpecialWebDriverKeys[] = { |
| 17 ui::VKEY_UNKNOWN, |
| 18 ui::VKEY_UNKNOWN, |
| 19 ui::VKEY_HELP, |
| 20 ui::VKEY_BACK, |
| 21 ui::VKEY_TAB, |
| 22 ui::VKEY_CLEAR, |
| 23 ui::VKEY_RETURN, |
| 24 ui::VKEY_RETURN, |
| 25 ui::VKEY_SHIFT, |
| 26 ui::VKEY_CONTROL, |
| 27 ui::VKEY_MENU, |
| 28 ui::VKEY_PAUSE, |
| 29 ui::VKEY_ESCAPE, |
| 30 ui::VKEY_SPACE, |
| 31 ui::VKEY_PRIOR, // page up |
| 32 ui::VKEY_NEXT, // page down |
| 33 ui::VKEY_END, |
| 34 ui::VKEY_HOME, |
| 35 ui::VKEY_LEFT, |
| 36 ui::VKEY_UP, |
| 37 ui::VKEY_RIGHT, |
| 38 ui::VKEY_DOWN, |
| 39 ui::VKEY_INSERT, |
| 40 ui::VKEY_DELETE, |
| 41 ui::VKEY_OEM_1, // semicolon |
| 42 ui::VKEY_OEM_PLUS, // equals |
| 43 ui::VKEY_NUMPAD0, |
| 44 ui::VKEY_NUMPAD1, |
| 45 ui::VKEY_NUMPAD2, |
| 46 ui::VKEY_NUMPAD3, |
| 47 ui::VKEY_NUMPAD4, |
| 48 ui::VKEY_NUMPAD5, |
| 49 ui::VKEY_NUMPAD6, |
| 50 ui::VKEY_NUMPAD7, |
| 51 ui::VKEY_NUMPAD8, |
| 52 ui::VKEY_NUMPAD9, |
| 53 ui::VKEY_MULTIPLY, |
| 54 ui::VKEY_ADD, |
| 55 ui::VKEY_SEPARATOR, |
| 56 ui::VKEY_SUBTRACT, |
| 57 ui::VKEY_DECIMAL, |
| 58 ui::VKEY_DIVIDE, |
| 59 ui::VKEY_UNKNOWN, |
| 60 ui::VKEY_UNKNOWN, |
| 61 ui::VKEY_UNKNOWN, |
| 62 ui::VKEY_UNKNOWN, |
| 63 ui::VKEY_UNKNOWN, |
| 64 ui::VKEY_UNKNOWN, |
| 65 ui::VKEY_UNKNOWN, |
| 66 ui::VKEY_F1, |
| 67 ui::VKEY_F2, |
| 68 ui::VKEY_F3, |
| 69 ui::VKEY_F4, |
| 70 ui::VKEY_F5, |
| 71 ui::VKEY_F6, |
| 72 ui::VKEY_F7, |
| 73 ui::VKEY_F8, |
| 74 ui::VKEY_F9, |
| 75 ui::VKEY_F10, |
| 76 ui::VKEY_F11, |
| 77 ui::VKEY_F12}; |
| 78 |
| 79 const char16 kWebDriverNullKey = 0xE000U; |
| 80 const char16 kWebDriverShiftKey = 0xE008U; |
| 81 const char16 kWebDriverControlKey = 0xE009U; |
| 82 const char16 kWebDriverAltKey = 0xE00AU; |
| 83 const char16 kWebDriverCommandKey = 0xE03DU; |
| 84 |
| 85 // Returns whether the given key is a WebDriver key modifier. |
| 86 bool IsModifierKey(char16 key) { |
| 87 switch (key) { |
| 88 case kWebDriverShiftKey: |
| 89 case kWebDriverControlKey: |
| 90 case kWebDriverAltKey: |
| 91 case kWebDriverCommandKey: |
| 92 return true; |
| 93 default: |
| 94 return false; |
| 95 } |
| 96 return false; |
| 97 } |
| 98 |
| 99 // Gets the key code associated with |key|, if it is a special WebDriver key. |
| 100 // Returns whether |key| is a special WebDriver key. If true, |key_code| will |
| 101 // be set. |
| 102 bool KeyCodeFromSpecialWebDriverKey(char16 key, ui::KeyboardCode* key_code) { |
| 103 int index = static_cast<int>(key) - 0xE000U; |
| 104 bool is_special_key = index >= 0 && index < arraysize(kSpecialWebDriverKeys); |
| 105 if (is_special_key) |
| 106 *key_code = kSpecialWebDriverKeys[index]; |
| 107 return is_special_key; |
| 108 } |
| 109 |
| 110 // Converts a character to the key code and modifier set that would |
| 111 // produce the character using the given keyboard layout. |
| 112 bool ConvertCharToKeyCode( |
| 113 char16 key, ui::KeyboardCode* key_code, int *necessary_modifiers) { |
| 114 #if defined(OS_WIN) |
| 115 short vkey_and_modifiers = ::VkKeyScanW(key); |
| 116 bool translated = vkey_and_modifiers != -1 && |
| 117 LOBYTE(vkey_and_modifiers) != -1 && |
| 118 HIBYTE(vkey_and_modifiers) != -1; |
| 119 if (translated) { |
| 120 *key_code = static_cast<ui::KeyboardCode>(LOBYTE(vkey_and_modifiers)); |
| 121 *necessary_modifiers = HIBYTE(vkey_and_modifiers); |
| 122 } |
| 123 return translated; |
| 124 #else |
| 125 // TODO(kkania): Implement. |
| 126 return false; |
| 127 #endif |
| 128 } |
| 129 |
| 130 // Returns the character that would be produced from the given key code and |
| 131 // modifier set, or "" if no character would be produced. |
| 132 std::string ConvertKeyCodeToText(ui::KeyboardCode key_code, int modifiers) { |
| 133 #if defined(OS_WIN) |
| 134 UINT scan_code = ::MapVirtualKeyW(key_code, MAPVK_VK_TO_VSC); |
| 135 BYTE keyboard_state[256]; |
| 136 ::GetKeyboardState(keyboard_state); |
| 137 if (modifiers & automation::kShiftKeyMask) |
| 138 keyboard_state[VK_SHIFT] |= 0x80; |
| 139 wchar_t chars[5]; |
| 140 int code = ::ToUnicode(key_code, scan_code, keyboard_state, chars, 4, 0); |
| 141 if (code <= 0) { |
| 142 return ""; |
| 143 } else { |
| 144 std::string text; |
| 145 WideToUTF8(chars, code, &text); |
| 146 return text; |
| 147 } |
| 148 #else |
| 149 // TODO(kkania): Implement |
| 150 return ""; |
| 151 #endif |
| 152 } |
| 153 |
| 154 } // namespace |
| 155 |
| 156 namespace webdriver { |
| 157 |
| 158 WebKeyEvent CreateKeyDownEvent(ui::KeyboardCode key_code, int modifiers) { |
| 159 return WebKeyEvent(automation::kRawKeyDownType, key_code, "", "", modifiers); |
| 160 } |
| 161 |
| 162 WebKeyEvent CreateKeyUpEvent(ui::KeyboardCode key_code, int modifiers) { |
| 163 return WebKeyEvent(automation::kKeyUpType, key_code, "", "", modifiers); |
| 164 } |
| 165 |
| 166 WebKeyEvent CreateCharEvent(const std::string& unmodified_text, |
| 167 const std::string& modified_text, |
| 168 int modifiers) { |
| 169 return WebKeyEvent(automation::kCharType, |
| 170 ui::VKEY_UNKNOWN, |
| 171 unmodified_text, |
| 172 modified_text, |
| 173 modifiers); |
| 174 } |
| 175 |
| 176 void ConvertKeysToWebKeyEvents(const string16& client_keys, |
| 177 std::vector<WebKeyEvent>* key_events) { |
| 178 // Add an implicit NULL character to the end of the input to depress all |
| 179 // modifiers. |
| 180 string16 keys = client_keys; |
| 181 keys.push_back(kWebDriverNullKey); |
| 182 |
| 183 int sticky_modifiers = 0; |
| 184 for (size_t i = 0; i < keys.size(); ++i) { |
| 185 char16 key = keys[i]; |
| 186 |
| 187 if (key == kWebDriverNullKey) { |
| 188 // Release all modifier keys and clear |stick_modifiers|. |
| 189 if (sticky_modifiers & automation::kShiftKeyMask) |
| 190 key_events->push_back(CreateKeyUpEvent(ui::VKEY_SHIFT, 0)); |
| 191 if (sticky_modifiers & automation::kControlKeyMask) |
| 192 key_events->push_back(CreateKeyUpEvent(ui::VKEY_CONTROL, 0)); |
| 193 if (sticky_modifiers & automation::kAltKeyMask) |
| 194 key_events->push_back(CreateKeyUpEvent(ui::VKEY_MENU, 0)); |
| 195 if (sticky_modifiers & automation::kMetaKeyMask) |
| 196 key_events->push_back(CreateKeyUpEvent(ui::VKEY_COMMAND, 0)); |
| 197 sticky_modifiers = 0; |
| 198 continue; |
| 199 } |
| 200 if (IsModifierKey(key)) { |
| 201 // Press or release the modifier, and adjust |sticky_modifiers|. |
| 202 bool modifier_down = false; |
| 203 ui::KeyboardCode key_code; |
| 204 if (key == kWebDriverShiftKey) { |
| 205 sticky_modifiers ^= automation::kShiftKeyMask; |
| 206 modifier_down = sticky_modifiers & automation::kShiftKeyMask; |
| 207 key_code = ui::VKEY_SHIFT; |
| 208 } else if (key == kWebDriverControlKey) { |
| 209 sticky_modifiers ^= automation::kControlKeyMask; |
| 210 modifier_down = sticky_modifiers & automation::kControlKeyMask; |
| 211 key_code = ui::VKEY_CONTROL; |
| 212 } else if (key == kWebDriverAltKey) { |
| 213 sticky_modifiers ^= automation::kAltKeyMask; |
| 214 modifier_down = sticky_modifiers & automation::kAltKeyMask; |
| 215 key_code = ui::VKEY_MENU; |
| 216 } else if (key == kWebDriverCommandKey) { |
| 217 sticky_modifiers ^= automation::kMetaKeyMask; |
| 218 modifier_down = sticky_modifiers & automation::kMetaKeyMask; |
| 219 key_code = ui::VKEY_COMMAND; |
| 220 } |
| 221 if (modifier_down) |
| 222 key_events->push_back(CreateKeyDownEvent(key_code, sticky_modifiers)); |
| 223 else |
| 224 key_events->push_back(CreateKeyUpEvent(key_code, sticky_modifiers)); |
| 225 continue; |
| 226 } |
| 227 |
| 228 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN; |
| 229 std::string unmodified_text, modified_text; |
| 230 int all_modifiers = sticky_modifiers; |
| 231 |
| 232 bool is_special_key = KeyCodeFromSpecialWebDriverKey(key, &key_code); |
| 233 if (is_special_key && key_code == ui::VKEY_UNKNOWN) { |
| 234 LOG(ERROR) << "Unknown WebDriver key: " << static_cast<int>(key); |
| 235 continue; |
| 236 } |
| 237 if (!is_special_key) { |
| 238 int necessary_modifiers = 0; |
| 239 ConvertCharToKeyCode(key, &key_code, &necessary_modifiers); |
| 240 all_modifiers |= necessary_modifiers; |
| 241 } |
| 242 if (key_code != ui::VKEY_UNKNOWN) { |
| 243 unmodified_text = ConvertKeyCodeToText(key_code, 0); |
| 244 modified_text = ConvertKeyCodeToText(key_code, all_modifiers); |
| 245 } |
| 246 if (!is_special_key && (unmodified_text.empty() || modified_text.empty())) { |
| 247 // Do a best effort and use the raw key we were given. |
| 248 LOG(WARNING) << "No translation for key code. Code point: " |
| 249 << static_cast<int>(key); |
| 250 if (unmodified_text.empty()) |
| 251 unmodified_text = UTF16ToUTF8(keys.substr(i, 1)); |
| 252 if (modified_text.empty()) |
| 253 modified_text = UTF16ToUTF8(keys.substr(i, 1)); |
| 254 } |
| 255 |
| 256 // Create the key events. |
| 257 bool need_shift_key = |
| 258 all_modifiers & automation::kShiftKeyMask && |
| 259 !(sticky_modifiers & automation::kShiftKeyMask); |
| 260 if (need_shift_key) { |
| 261 key_events->push_back( |
| 262 CreateKeyDownEvent(ui::VKEY_SHIFT, sticky_modifiers)); |
| 263 } |
| 264 |
| 265 key_events->push_back(CreateKeyDownEvent(key_code, all_modifiers)); |
| 266 if (unmodified_text.length() || modified_text.length()) { |
| 267 key_events->push_back( |
| 268 CreateCharEvent(unmodified_text, modified_text, all_modifiers)); |
| 269 } |
| 270 key_events->push_back(CreateKeyUpEvent(key_code, all_modifiers)); |
| 271 |
| 272 if (need_shift_key) { |
| 273 key_events->push_back( |
| 274 CreateKeyUpEvent(ui::VKEY_SHIFT, sticky_modifiers)); |
| 275 } |
| 276 } |
| 277 } |
| 278 |
| 279 } // namespace webdriver |
| OLD | NEW |