| // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "content/test/mock_keyboard_driver_win.h" | 
 |  | 
 | #include <stddef.h> | 
 | #include <string.h> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/stl_util.h" | 
 | #include "content/test/mock_keyboard.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | MockKeyboardDriverWin::MockKeyboardDriverWin() { | 
 |   // Save the keyboard layout and status of the application. | 
 |   // This class changes the keyboard layout and status of this application. | 
 |   // This change may break succeeding tests. To prevent this possible break, we | 
 |   // should save the layout and status here to restore when this instance is | 
 |   // destroyed. | 
 |   original_keyboard_layout_ = GetKeyboardLayout(0); | 
 |   active_keyboard_layout_ = original_keyboard_layout_; | 
 |   GetKeyboardState(&original_keyboard_states_[0]); | 
 |  | 
 |   const UINT num_keyboard_layouts = GetKeyboardLayoutList(0, NULL); | 
 |   DCHECK(num_keyboard_layouts > 0); | 
 |  | 
 |   orig_keyboard_layouts_list_.resize(num_keyboard_layouts); | 
 |   GetKeyboardLayoutList(num_keyboard_layouts, &orig_keyboard_layouts_list_[0]); | 
 |  | 
 |   memset(&keyboard_states_[0], 0, sizeof(keyboard_states_)); | 
 | } | 
 |  | 
 | MockKeyboardDriverWin::~MockKeyboardDriverWin() { | 
 |   // Unload the keyboard-layout driver, restore the keyboard state, and reset | 
 |   // the keyboard layout for succeeding tests. | 
 |   MaybeUnloadActiveLayout(); | 
 |   SetKeyboardState(&original_keyboard_states_[0]); | 
 |   ActivateKeyboardLayout(original_keyboard_layout_, KLF_RESET); | 
 | } | 
 |  | 
 | void MockKeyboardDriverWin::MaybeUnloadActiveLayout() { | 
 |   // Workaround for http://crbug.com/12093 | 
 |   // Only unload a keyboard layout if it was loaded by this mock driver. | 
 |   // Contrary to the documentation on MSDN unloading a keyboard layout | 
 |   // previously loaded by the system causes that layout to stop working. | 
 |   // We have confirmation of this behavior on XP & Vista. | 
 |   for (size_t i = 0; i < orig_keyboard_layouts_list_.size(); ++i) { | 
 |     if (orig_keyboard_layouts_list_[i] == active_keyboard_layout_) | 
 |       return; | 
 |   } | 
 |  | 
 |   // If we got here, this keyboard layout wasn't loaded by the system so it's | 
 |   // safe to unload it ourselve's. | 
 |   UnloadKeyboardLayout(active_keyboard_layout_); | 
 |   active_keyboard_layout_ = original_keyboard_layout_; | 
 | } | 
 |  | 
 | bool MockKeyboardDriverWin::SetLayout(int layout) { | 
 |   // Unload the current keyboard-layout driver and load a new keyboard-layout | 
 |   // driver for mapping a virtual key-code to a Unicode character. | 
 |   MaybeUnloadActiveLayout(); | 
 |  | 
 |   // Scan the mapping table and retrieve a Language ID for the input layout. | 
 |   // Load the keyboard-layout driver when we find a Language ID. | 
 |   // This Language IDs are copied from the registry | 
 |   //   "HKLM\SYSTEM\CurrentControlSet\Control\Keyboard layouts". | 
 |   // TODO(hbono): Add more keyboard-layout drivers. | 
 |   static const struct { | 
 |     const wchar_t* language; | 
 |     MockKeyboard::Layout keyboard_layout; | 
 |   } kLanguageIDs[] = { | 
 |     {L"00000401", MockKeyboard::LAYOUT_ARABIC}, | 
 |     {L"00000402", MockKeyboard::LAYOUT_BULGARIAN}, | 
 |     {L"00000404", MockKeyboard::LAYOUT_CHINESE_TRADITIONAL}, | 
 |     {L"00000405", MockKeyboard::LAYOUT_CZECH}, | 
 |     {L"00000406", MockKeyboard::LAYOUT_DANISH}, | 
 |     {L"00000407", MockKeyboard::LAYOUT_GERMAN}, | 
 |     {L"00000408", MockKeyboard::LAYOUT_GREEK}, | 
 |     {L"00000409", MockKeyboard::LAYOUT_UNITED_STATES}, | 
 |     {L"0000040a", MockKeyboard::LAYOUT_SPANISH}, | 
 |     {L"0000040b", MockKeyboard::LAYOUT_FINNISH}, | 
 |     {L"0000040c", MockKeyboard::LAYOUT_FRENCH}, | 
 |     {L"0000040d", MockKeyboard::LAYOUT_HEBREW}, | 
 |     {L"0000040e", MockKeyboard::LAYOUT_HUNGARIAN}, | 
 |     {L"00000410", MockKeyboard::LAYOUT_ITALIAN}, | 
 |     {L"00000411", MockKeyboard::LAYOUT_JAPANESE}, | 
 |     {L"00000412", MockKeyboard::LAYOUT_KOREAN}, | 
 |     {L"00000415", MockKeyboard::LAYOUT_POLISH}, | 
 |     {L"00000416", MockKeyboard::LAYOUT_PORTUGUESE_BRAZILIAN}, | 
 |     {L"00000418", MockKeyboard::LAYOUT_ROMANIAN}, | 
 |     {L"00000419", MockKeyboard::LAYOUT_RUSSIAN}, | 
 |     {L"0000041a", MockKeyboard::LAYOUT_CROATIAN}, | 
 |     {L"0000041b", MockKeyboard::LAYOUT_SLOVAK}, | 
 |     {L"0000041e", MockKeyboard::LAYOUT_THAI}, | 
 |     {L"0000041d", MockKeyboard::LAYOUT_SWEDISH}, | 
 |     {L"0000041f", MockKeyboard::LAYOUT_TURKISH_Q}, | 
 |     {L"0000042a", MockKeyboard::LAYOUT_VIETNAMESE}, | 
 |     {L"00000439", MockKeyboard::LAYOUT_DEVANAGARI_INSCRIPT}, | 
 |     {L"00000816", MockKeyboard::LAYOUT_PORTUGUESE}, | 
 |     {L"00001409", MockKeyboard::LAYOUT_UNITED_STATES_DVORAK}, | 
 |     {L"00001009", MockKeyboard::LAYOUT_CANADIAN_FRENCH}, | 
 |   }; | 
 |  | 
 |   for (size_t i = 0; i < base::size(kLanguageIDs); ++i) { | 
 |     if (layout == kLanguageIDs[i].keyboard_layout) { | 
 |       HKL new_keyboard_layout = LoadKeyboardLayout(kLanguageIDs[i].language, | 
 |                                                    KLF_ACTIVATE); | 
 |       // loaded_keyboard_layout_ must always have a valid keyboard handle | 
 |       // so we only assign upon success. | 
 |       if (new_keyboard_layout) { | 
 |         active_keyboard_layout_ = new_keyboard_layout; | 
 |         return true; | 
 |       } | 
 |  | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   // Return false if there are not any matching drivers. | 
 |   return false; | 
 | } | 
 |  | 
 | bool MockKeyboardDriverWin::SetModifiers(int modifiers) { | 
 |   // Over-write the keyboard status with our modifier-key status. | 
 |   // WebInputEventFactory::keyboardEvent() uses GetKeyState() to retrive | 
 |   // modifier-key status. So, we update the modifier-key status with this | 
 |   // SetKeyboardState() call before creating NativeWebKeyboardEvent | 
 |   // instances. | 
 |   memset(&keyboard_states_[0], 0, sizeof(keyboard_states_)); | 
 |   static const struct { | 
 |     int key_code; | 
 |     int mask; | 
 |   } kModifierMasks[] = { | 
 |     {VK_SHIFT,    MockKeyboard::LEFT_SHIFT | MockKeyboard::RIGHT_SHIFT}, | 
 |     {VK_CONTROL,  MockKeyboard::LEFT_CONTROL | MockKeyboard::RIGHT_CONTROL}, | 
 |     {VK_MENU,     MockKeyboard::LEFT_ALT | MockKeyboard::RIGHT_ALT}, | 
 |     {VK_LSHIFT,   MockKeyboard::LEFT_SHIFT}, | 
 |     {VK_LCONTROL, MockKeyboard::LEFT_CONTROL}, | 
 |     {VK_LMENU,    MockKeyboard::LEFT_ALT}, | 
 |     {VK_RSHIFT,   MockKeyboard::RIGHT_SHIFT}, | 
 |     {VK_RCONTROL, MockKeyboard::RIGHT_CONTROL}, | 
 |     {VK_RMENU,    MockKeyboard::RIGHT_ALT}, | 
 |   }; | 
 |   for (size_t i = 0; i < base::size(kModifierMasks); ++i) { | 
 |     const int kKeyDownMask = 0x80; | 
 |     if (modifiers & kModifierMasks[i].mask) | 
 |       keyboard_states_[kModifierMasks[i].key_code] = kKeyDownMask; | 
 |   } | 
 |   SetKeyboardState(&keyboard_states_[0]); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | int MockKeyboardDriverWin::GetCharacters(int key_code, | 
 |                                          std::wstring* output) { | 
 |   // Retrieve Unicode characters composed from the input key-code and | 
 |   // the mofifiers. | 
 |   CHECK(output); | 
 |   wchar_t code[16]; | 
 |   int length = | 
 |       ToUnicodeEx(key_code, MapVirtualKey(key_code, 0), &keyboard_states_[0], | 
 |                   &code[0], base::size(code), 0, active_keyboard_layout_); | 
 |   if (length > 0) | 
 |     output->assign(code); | 
 |   return length; | 
 | } | 
 |  | 
 | }  // namespace content |