diff options
-rw-r--r-- | chrome/chrome.gyp | 2 | ||||
-rw-r--r-- | chrome/renderer/mock_keyboard.cc | 47 | ||||
-rw-r--r-- | chrome/renderer/mock_keyboard.h | 106 | ||||
-rw-r--r-- | chrome/renderer/mock_keyboard_driver_win.cc | 140 | ||||
-rw-r--r-- | chrome/renderer/mock_keyboard_driver_win.h | 35 | ||||
-rw-r--r-- | chrome/renderer/render_view.h | 3 | ||||
-rw-r--r-- | chrome/renderer/render_view_unittest.cc | 339 | ||||
-rw-r--r-- | chrome/test/render_view_test.cc | 54 | ||||
-rw-r--r-- | chrome/test/render_view_test.h | 10 | ||||
-rw-r--r-- | chrome/test/unit/unittests.vcproj | 16 |
10 files changed, 665 insertions, 87 deletions
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 81e2aee..564db3b 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -2172,6 +2172,8 @@ 'browser/tab_contents/test_web_contents.h', 'common/ipc_test_sink.cc', 'common/ipc_test_sink.h', + 'renderer/mock_keyboard.h', + 'renderer/mock_keyboard.cc', 'renderer/mock_render_process.h', 'renderer/mock_render_thread.cc', 'renderer/mock_render_thread.h', diff --git a/chrome/renderer/mock_keyboard.cc b/chrome/renderer/mock_keyboard.cc new file mode 100644 index 0000000..b18f3f6 --- /dev/null +++ b/chrome/renderer/mock_keyboard.cc @@ -0,0 +1,47 @@ +// Copyright (c) 2009 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 "chrome/renderer/mock_keyboard.h" + +#include "base/logging.h" + +MockKeyboard::MockKeyboard() + : keyboard_layout_(LAYOUT_NULL), + keyboard_modifiers_(INVALID) { +} + +MockKeyboard::~MockKeyboard() { +} + +int MockKeyboard::GetCharacters(Layout layout, + int key_code, + Modifiers modifiers, + std::wstring* output) { +#if defined(OS_WIN) + CHECK(output); + // Change the keyboard layout only when we have to because it takes a lot of + // time to load a keyboard-layout driver. + // When we change the layout, we reset the modifier status to force updating + // the keyboard status. + if (layout != keyboard_layout_) { + if (!driver_.SetLayout(layout)) + return -1; + keyboard_layout_ = layout; + keyboard_modifiers_ = INVALID; + } + + // Update the keyboard states. + if (modifiers != keyboard_modifiers_) { + if (!driver_.SetModifiers(modifiers)) + return -1; + keyboard_modifiers_ = modifiers; + } + + // Retrieve Unicode characters associate with the key code. + return driver_.GetCharacters(key_code, output); +#else + NOTIMPLEMENTED(); + return -1; +#endif +} diff --git a/chrome/renderer/mock_keyboard.h b/chrome/renderer/mock_keyboard.h new file mode 100644 index 0000000..4ff8d1a --- /dev/null +++ b/chrome/renderer/mock_keyboard.h @@ -0,0 +1,106 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_RENDERER_MOCK_KEYBOARD_H_ +#define CHROME_RENDERER_MOCK_KEYBOARD_H_ + +#include <string> + +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include "chrome/renderer/mock_keyboard_driver_win.h" +#endif + +// A mock keyboard interface. +// This class defines a pseudo keyboard device, which implements mappings from +// a tuple (layout, key code, modifiers) to Unicode characters so that +// engineers can write RenderViewTest cases without taking care of such +// mappings. (This mapping is not trivial when using non-US keyboards.) +// A pseudo keyboard device consists of two parts: a platform-independent part +// and a platform-dependent part. This class implements the platform-independent +// part. The platform-dependet part is implemented in the MockKeyboardWin class. +// This class is usually called from RenderViewTest::SendKeyEvent(). +class MockKeyboard { + public: + // Represents keyboard-layouts. + enum Layout { + LAYOUT_NULL, + LAYOUT_ARABIC, + LAYOUT_BULGARIAN, + LAYOUT_CHINESE_TRADITIONAL, + LAYOUT_CZECH, + LAYOUT_DANISH, + LAYOUT_GERMAN, + LAYOUT_GREEK, + LAYOUT_UNITED_STATES, + LAYOUT_SPANISH, + LAYOUT_FINNISH, + LAYOUT_FRENCH, + LAYOUT_HEBREW, + LAYOUT_HUNGARIAN, + LAYOUT_ICELANDIC, + LAYOUT_ITALIAN, + LAYOUT_JAPANESE, + LAYOUT_KOREAN, + LAYOUT_POLISH, + LAYOUT_PORTUGUESE_BRAZILIAN, + LAYOUT_ROMANIAN, + LAYOUT_RUSSIAN, + LAYOUT_CROATIAN, + LAYOUT_SLOVAK, + LAYOUT_THAI, + LAYOUT_SWEDISH, + LAYOUT_TURKISH_Q, + LAYOUT_VIETNAMESE, + LAYOUT_DEVANAGARI_INSCRIPT, + LAYOUT_PORTUGUESE, + LAYOUT_UNITED_STATES_DVORAK, + LAYOUT_CANADIAN_FRENCH, + }; + + // Enumerates keyboard modifiers. + // These modifiers explicitly distinguish left-keys and right-keys because we + // should emulate AltGr (right-alt) key, used by many European keyboards to + // input alternate graph characters. + enum Modifiers { + INVALID = -1, + NONE = 0, + LEFT_SHIFT = 1 << 0, + LEFT_CONTROL = 1 << 1, + LEFT_ALT = 1 << 2, + LEFT_META = 1 << 3, + RIGHT_SHIFT = 1 << 4, + RIGHT_CONTROL = 1 << 5, + RIGHT_ALT = 1 << 6, + RIGHT_META = 1 << 7, + KEYPAD = 1 << 8, + AUTOREPEAAT = 1 << 9, + }; + + MockKeyboard(); + ~MockKeyboard(); + + // Retrieves Unicode characters composed from the the specified keyboard + // layout, key code, and modifiers, i.e. characters returned when we type + // specified keys on a specified layout. + // This function returns the length of Unicode characters filled in the + // |output| parameter. + int GetCharacters(Layout layout, + int key_code, + Modifiers modifiers, + std::wstring* output); + + private: + Layout keyboard_layout_; + Modifiers keyboard_modifiers_; + +#if defined(OS_WIN) + MockKeyboardDriverWin driver_; +#endif + + DISALLOW_COPY_AND_ASSIGN(MockKeyboard); +}; + +#endif // CHROME_RENDERER_MOCK_KEYBOARD_H_ diff --git a/chrome/renderer/mock_keyboard_driver_win.cc b/chrome/renderer/mock_keyboard_driver_win.cc new file mode 100644 index 0000000..07792e9 --- /dev/null +++ b/chrome/renderer/mock_keyboard_driver_win.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2009 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 "chrome/renderer/mock_keyboard_driver_win.h" + +#include "base/basictypes.h" +#include "base/logging.h" +#include "chrome/renderer/mock_keyboard.h" + +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); + GetKeyboardState(&original_keyboard_states_[0]); + + keyboard_handle_ = NULL; + 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. + if (keyboard_handle_) + UnloadKeyboardLayout(keyboard_handle_); + SetKeyboardState(&original_keyboard_states_[0]); + ActivateKeyboardLayout(original_keyboard_layout_, KLF_RESET); +} + +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. + if (keyboard_handle_) { + UnloadKeyboardLayout(keyboard_handle_); + keyboard_handle_ = NULL; + } + + // 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 < ARRAYSIZE_UNSAFE(kLanguageIDs); ++i) { + if (layout == kLanguageIDs[i].keyboard_layout) { + keyboard_handle_ = LoadKeyboardLayout(kLanguageIDs[i].language, + KLF_ACTIVATE); + if (!keyboard_handle_) + return false; + return true; + } + } + + // 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 < ARRAYSIZE_UNSAFE(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], + ARRAYSIZE_UNSAFE(code), 0, + keyboard_handle_); + if (length > 0) + output->assign(code); + return length; +} diff --git a/chrome/renderer/mock_keyboard_driver_win.h b/chrome/renderer/mock_keyboard_driver_win.h new file mode 100644 index 0000000..5ad0909 --- /dev/null +++ b/chrome/renderer/mock_keyboard_driver_win.h @@ -0,0 +1,35 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_RENDERER_MOCK_KEYBOARD_DRIVER_WIN_H_ +#define CHROME_RENDERER_MOCK_KEYBOARD_DRIVER_WIN_H_ + +#include <windows.h> + +#include <string> + +#include "base/basictypes.h" + +// Implements the platform-dependent part of a pseudo keyboard device for +// Windows. +class MockKeyboardDriverWin { + public: + MockKeyboardDriverWin(); + ~MockKeyboardDriverWin(); + + bool SetLayout(int layout); + bool SetModifiers(int modifiers); + int GetCharacters(int key_code, std::wstring* code); + + private: + HKL original_keyboard_layout_; + BYTE original_keyboard_states_[256]; + + HKL keyboard_handle_; + BYTE keyboard_states_[256]; + + DISALLOW_COPY_AND_ASSIGN(MockKeyboardDriverWin); +}; + +#endif // CHROME_RENDERER_MOCK_KEYBOARD_DRIVER_WIN_H_ diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index dcd2de1..3463ad8 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -383,6 +383,8 @@ class RenderView : public RenderWidget, const gfx::Rect& resizer_rect); private: + // For unit tests. + friend class RenderViewTest; FRIEND_TEST(RenderViewTest, OnLoadAlternateHTMLText); FRIEND_TEST(RenderViewTest, OnNavStateChanged); FRIEND_TEST(RenderViewTest, OnImeStateChanged); @@ -390,6 +392,7 @@ class RenderView : public RenderWidget, FRIEND_TEST(RenderViewTest, OnSetTextDirection); FRIEND_TEST(RenderViewTest, OnPrintPages); FRIEND_TEST(RenderViewTest, OnHandleKeyboardEvent); + FRIEND_TEST(RenderViewTest, InsertCharacters); explicit RenderView(RenderThreadBase* render_thread); diff --git a/chrome/renderer/render_view_unittest.cc b/chrome/renderer/render_view_unittest.cc index 8403746..8a2ed21 100644 --- a/chrome/renderer/render_view_unittest.cc +++ b/chrome/renderer/render_view_unittest.cc @@ -315,19 +315,14 @@ TEST_F(RenderViewTest, OnPrintPages) { // through the RenderWidget::OnHandleInputEvent() function. TEST_F(RenderViewTest, OnHandleKeyboardEvent) { #if defined(OS_WIN) - // Save the keyboard layout and the status. - // This test changes the keyboard layout and status. This may break - // succeeding tests. To prevent this possible break, we should save the - // layout and status here to restore when this test is finished. - HKL original_layout = GetKeyboardLayout(0); - BYTE original_key_states[256]; - GetKeyboardState(&original_key_states[0]); - // Load an HTML page consisting of one <input> element and three // contentediable <div> elements. // The <input> element is used for sending keyboard events, and the <div> // elements are used for writing DOM events in the following format: - // "<keyCode>,<shiftKey>,<controlKey>,<altKey>,<metaKey>". + // "<keyCode>,<shiftKey>,<controlKey>,<altKey>". + // TODO(hbono): <http://crbug.com/2215> Our WebKit port set |ev.metaKey| to + // true when pressing an alt key, i.e. the |ev.metaKey| value is not + // trustworthy. We will check the |ev.metaKey| value when this issue is fixed. view_->set_delay_seconds_for_form_state_sync(0); LoadHTML("<html>" "<head>" @@ -339,8 +334,7 @@ TEST_F(RenderViewTest, OnHandleKeyboardEvent) { " (ev.which || ev.keyCode) + ',' +" " ev.shiftKey + ',' +" " ev.ctrlKey + ',' +" - " ev.altKey + ',' +" - " ev.metaKey;" + " ev.altKey;" " return true;" "}" "</script>" @@ -362,24 +356,33 @@ TEST_F(RenderViewTest, OnHandleKeyboardEvent) { ExecuteJavaScript("document.getElementById('test').focus();"); render_thread_.sink().ClearMessages(); - // Language IDs used in this test. - // This test directly loads keyboard-layout drivers and use them for - // emulating non-US keyboard layouts. - static const wchar_t* kLanguageIDs[] = { - L"00000401", // Arabic - L"00000409", // United States - L"0000040c", // French - L"0000040d", // Hebrew - L"00001009", // Canadian French + static const MockKeyboard::Layout kLayouts[] = { + MockKeyboard::LAYOUT_ARABIC, + MockKeyboard::LAYOUT_CANADIAN_FRENCH, + MockKeyboard::LAYOUT_FRENCH, + MockKeyboard::LAYOUT_HEBREW, + MockKeyboard::LAYOUT_RUSSIAN, + MockKeyboard::LAYOUT_UNITED_STATES, }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLanguageIDs); ++i) { - // Load a keyboard-layout driver. - HKL handle = LoadKeyboardLayout(kLanguageIDs[i], KLF_ACTIVATE); - EXPECT_TRUE(handle != NULL); - - // For each key code, we send two keyboard events: one when we only press - // the key, and one when we press the key and a shift key. - for (int modifiers = 0; modifiers < 2; ++modifiers) { + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) { + // For each key code, we send three keyboard events. + // * we press only the key; + // * we press the key and a left-shift key, and; + // * we press the key and a right-alt (AltGr) key. + // For each modifiers, we need a string used for formatting its expected + // result. (See the above comment for its format.) + static const struct { + MockKeyboard::Modifiers modifiers; + const wchar_t* expected_result; + } kModifierData[] = { + {MockKeyboard::NONE, L"false,false,false"}, + {MockKeyboard::LEFT_SHIFT, L"true,false,false"}, + {MockKeyboard::RIGHT_ALT, L"false,false,true"}, + }; + + MockKeyboard::Layout layout = kLayouts[i]; + for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifierData); ++j) { // Virtual key codes used for this test. static const int kKeyCodes[] = { '0', '1', '2', '3', '4', '5', '6', '7', @@ -401,72 +404,30 @@ TEST_F(RenderViewTest, OnHandleKeyboardEvent) { VK_OEM_8, }; - // 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. - BYTE key_states[256]; - memset(&key_states[0], 0, sizeof(key_states)); - key_states[VK_SHIFT] = (modifiers & 0x01) ? 0x80 : 0; - SetKeyboardState(&key_states[0]); - - for (size_t j = 0; j <= ARRAYSIZE_UNSAFE(kKeyCodes); ++j) { - // Retrieve the Unicode character composed from the virtual-key code - // and our modifier-key status from the keyboard-layout driver. - // This character is used for creating a WM_CHAR message and an - // expected result. - int key_code = kKeyCodes[j]; - wchar_t codes[4]; - int length = ToUnicodeEx(key_code, MapVirtualKey(key_code, 0), - &key_states[0], &codes[0], - ARRAYSIZE_UNSAFE(codes), 0, handle); - if (length != 1) + MockKeyboard::Modifiers modifiers = kModifierData[j].modifiers; + for (size_t k = 0; k <= ARRAYSIZE_UNSAFE(kKeyCodes); ++k) { + // Send a keyboard event to the RenderView object. + // We should test a keyboard event only when the given keyboard-layout + // driver is installed in a PC and the driver can assign a Unicode + // charcter for the given tuple (key-code and modifiers). + int key_code = kKeyCodes[k]; + std::wstring char_code; + if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0) continue; - // Create IPC messages from Windows messages and send them to our - // back-end. - // A keyboard event of Windows consists of three Windows messages: - // WM_KEYDOWN, WM_CHAR, and WM_KEYUP. - // WM_KEYDOWN and WM_KEYUP sends virtual-key codes. On the other hand, - // WM_CHAR sends a composed Unicode character. - NativeWebKeyboardEvent keydown_event(NULL, WM_KEYDOWN, key_code, 0); - scoped_ptr<IPC::Message> keydown_message( - new ViewMsg_HandleInputEvent(0)); - keydown_message->WriteData( - reinterpret_cast<const char*>(&keydown_event), - sizeof(WebKit::WebKeyboardEvent)); - view_->OnHandleInputEvent(*keydown_message); - - NativeWebKeyboardEvent char_event(NULL, WM_CHAR, codes[0], 0); - scoped_ptr<IPC::Message> char_message(new ViewMsg_HandleInputEvent(0)); - char_message->WriteData(reinterpret_cast<const char*>(&char_event), - sizeof(WebKit::WebKeyboardEvent)); - view_->OnHandleInputEvent(*char_message); - - NativeWebKeyboardEvent keyup_event(NULL, WM_KEYUP, key_code, 0); - scoped_ptr<IPC::Message> keyup_message(new ViewMsg_HandleInputEvent(0)); - keyup_message->WriteData(reinterpret_cast<const char*>(&keyup_event), - sizeof(WebKit::WebKeyboardEvent)); - view_->OnHandleInputEvent(*keyup_message); - // Create an expected result from the virtual-key code, the character // code, and the modifier-key status. // We format a string that emulates a DOM-event string produced hy // our JavaScript function. (See the above comment for the format.) - static const wchar_t* kModifiers[] = { - L"false,false,false,false", - L"true,false,false,false", - }; static wchar_t expected_result[1024]; wsprintf(&expected_result[0], L"\x000A" // texts in the <input> element L"%d,%s\x000A" // texts in the first <div> element L"%d,%s\x000A" // texts in the second <div> element L"%d,%s", // texts in the third <div> element - key_code, kModifiers[modifiers], - codes[0], kModifiers[modifiers], - key_code, kModifiers[modifiers]); + key_code, kModifierData[j].expected_result, + char_code[0], kModifierData[j].expected_result, + key_code, kModifierData[j].expected_result); // Retrieve the text in the test page and compare it with the expected // text created from a virtual-key code, a character code, and the @@ -478,13 +439,217 @@ TEST_F(RenderViewTest, OnHandleKeyboardEvent) { EXPECT_EQ(expected_result, output); } } - - UnloadKeyboardLayout(handle); } +#else + NOTIMPLEMENTED(); +#endif +} - // Restore the keyboard layout and status. - SetKeyboardState(&original_key_states[0]); - ActivateKeyboardLayout(original_layout, KLF_RESET); +// Test that our EditorClientImpl class can insert characters when we send +// keyboard events through the RenderWidget::OnHandleInputEvent() function. +// This test is for preventing regressions caused only when we use non-US +// keyboards, such as Issue 10846. +TEST_F(RenderViewTest, InsertCharacters) { +#if defined(OS_WIN) + static const struct { + MockKeyboard::Layout layout; + const wchar_t* expected_result; + } kLayouts[] = { +#if 0 + // Disabled these keyboard layouts because buildbots do not have their + // keyboard-layout drivers installed. + {MockKeyboard::LAYOUT_ARABIC, + L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" + L"\x0038\x0039\x0634\x0624\x064a\x062b\x0628\x0644" + L"\x0627\x0647\x062a\x0646\x0645\x0629\x0649\x062e" + L"\x062d\x0636\x0642\x0633\x0641\x0639\x0631\x0635" + L"\x0621\x063a\x0626\x0643\x003d\x0648\x002d\x0632" + L"\x0638\x0630\x062c\x005c\x062f\x0637\x0028\x0021" + L"\x0040\x0023\x0024\x0025\x005e\x0026\x002a\x0029" + L"\x0650\x007d\x005d\x064f\x005b\x0623\x00f7\x0640" + L"\x060c\x002f\x2019\x0622\x00d7\x061b\x064e\x064c" + L"\x064d\x2018\x007b\x064b\x0652\x0625\x007e\x003a" + L"\x002b\x002c\x005f\x002e\x061f\x0651\x003c\x007c" + L"\x003e\x0022\x0030\x0031\x0032\x0033\x0034\x0035" + L"\x0036\x0037\x0038\x0039\x0634\x0624\x064a\x062b" + L"\x0628\x0644\x0627\x0647\x062a\x0646\x0645\x0629" + L"\x0649\x062e\x062d\x0636\x0642\x0633\x0641\x0639" + L"\x0631\x0635\x0621\x063a\x0626\x0643\x003d\x0648" + L"\x002d\x0632\x0638\x0630\x062c\x005c\x062f\x0637" + }, + {MockKeyboard::LAYOUT_HEBREW, + L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" + L"\x0038\x0039\x05e9\x05e0\x05d1\x05d2\x05e7\x05db" + L"\x05e2\x05d9\x05df\x05d7\x05dc\x05da\x05e6\x05de" + L"\x05dd\x05e4\x002f\x05e8\x05d3\x05d0\x05d5\x05d4" + L"\x0027\x05e1\x05d8\x05d6\x05e3\x003d\x05ea\x002d" + L"\x05e5\x002e\x003b\x005d\x005c\x005b\x002c\x0028" + L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a" + L"\x0029\x0041\x0042\x0043\x0044\x0045\x0046\x0047" + L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f" + L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057" + L"\x0058\x0059\x005a\x003a\x002b\x003e\x005f\x003c" + L"\x003f\x007e\x007d\x007c\x007b\x0022\x0030\x0031" + L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039" + L"\x05e9\x05e0\x05d1\x05d2\x05e7\x05db\x05e2\x05d9" + L"\x05df\x05d7\x05dc\x05da\x05e6\x05de\x05dd\x05e4" + L"\x002f\x05e8\x05d3\x05d0\x05d5\x05d4\x0027\x05e1" + L"\x05d8\x05d6\x05e3\x003d\x05ea\x002d\x05e5\x002e" + L"\x003b\x005d\x005c\x005b\x002c" + }, +#endif + {MockKeyboard::LAYOUT_CANADIAN_FRENCH, + L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" + L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066" + L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e" + L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076" + L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d" + L"\x002e\x00e9\x003c\x0029\x0021\x0022\x002f\x0024" + L"\x0025\x003f\x0026\x002a\x0028\x0041\x0042\x0043" + L"\x0044\x0045\x0046\x0047\x0048\x0049\x004a\x004b" + L"\x004c\x004d\x004e\x004f\x0050\x0051\x0052\x0053" + L"\x0054\x0055\x0056\x0057\x0058\x0059\x005a\x003a" + L"\x002b\x0027\x005f\x002e\x00c9\x003e\x0030\x0031" + L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039" + L"\x0061\x0062\x0063\x0064\x0065\x0066\x0067\x0068" + L"\x0069\x006a\x006b\x006c\x006d\x006e\x006f\x0070" + L"\x0071\x0072\x0073\x0074\x0075\x0076\x0077\x0078" + L"\x0079\x007a\x003b\x003d\x002c\x002d\x002e\x00e9" + L"\x003c" + }, + {MockKeyboard::LAYOUT_FRENCH, + L"\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d\x00e8" + L"\x005f\x00e7\x0061\x0062\x0063\x0064\x0065\x0066" + L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e" + L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076" + L"\x0077\x0078\x0079\x007a\x0024\x003d\x002c\x003b" + L"\x003a\x00f9\x0029\x002a\x0021\x0030\x0031\x0032" + L"\x0033\x0034\x0035\x0036\x0037\x0038\x0039\x0041" + L"\x0042\x0043\x0044\x0045\x0046\x0047\x0048\x0049" + L"\x004a\x004b\x004c\x004d\x004e\x004f\x0050\x0051" + L"\x0052\x0053\x0054\x0055\x0056\x0057\x0058\x0059" + L"\x005a\x00a3\x002b\x003f\x002e\x002f\x0025\x00b0" + L"\x00b5\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d" + L"\x00e8\x005f\x00e7\x0061\x0062\x0063\x0064\x0065" + L"\x0066\x0067\x0068\x0069\x006a\x006b\x006c\x006d" + L"\x006e\x006f\x0070\x0071\x0072\x0073\x0074\x0075" + L"\x0076\x0077\x0078\x0079\x007a\x0024\x003d\x002c" + L"\x003b\x003a\x00f9\x0029\x002a\x0021" + }, + {MockKeyboard::LAYOUT_RUSSIAN, + L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" + L"\x0038\x0039\x0444\x0438\x0441\x0432\x0443\x0430" + L"\x043f\x0440\x0448\x043e\x043b\x0434\x044c\x0442" + L"\x0449\x0437\x0439\x043a\x044b\x0435\x0433\x043c" + L"\x0446\x0447\x043d\x044f\x0436\x003d\x0431\x002d" + L"\x044e\x002e\x0451\x0445\x005c\x044a\x044d\x0029" + L"\x0021\x0022\x2116\x003b\x0025\x003a\x003f\x002a" + L"\x0028\x0424\x0418\x0421\x0412\x0423\x0410\x041f" + L"\x0420\x0428\x041e\x041b\x0414\x042c\x0422\x0429" + L"\x0417\x0419\x041a\x042b\x0415\x0413\x041c\x0426" + L"\x0427\x041d\x042f\x0416\x002b\x0411\x005f\x042e" + L"\x002c\x0401\x0425\x002f\x042a\x042d\x0030\x0031" + L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039" + L"\x0444\x0438\x0441\x0432\x0443\x0430\x043f\x0440" + L"\x0448\x043e\x043b\x0434\x044c\x0442\x0449\x0437" + L"\x0439\x043a\x044b\x0435\x0433\x043c\x0446\x0447" + L"\x043d\x044f\x0436\x003d\x0431\x002d\x044e\x002e" + L"\x0451\x0445\x005c\x044a\x044d" + }, + {MockKeyboard::LAYOUT_UNITED_STATES, + L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" + L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066" + L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e" + L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076" + L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d" + L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027\x0029" + L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a" + L"\x0028\x0041\x0042\x0043\x0044\x0045\x0046\x0047" + L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f" + L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057" + L"\x0058\x0059\x005a\x003a\x002b\x003c\x005f\x003e" + L"\x003f\x007e\x007b\x007c\x007d\x0022\x0030\x0031" + L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039" + L"\x0061\x0062\x0063\x0064\x0065\x0066\x0067\x0068" + L"\x0069\x006a\x006b\x006c\x006d\x006e\x006f\x0070" + L"\x0071\x0072\x0073\x0074\x0075\x0076\x0077\x0078" + L"\x0079\x007a\x003b\x003d\x002c\x002d\x002e\x002f" + L"\x0060\x005b\x005c\x005d\x0027" + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) { + // Load an HTML page consisting of one <div> element. + // This <div> element is used by the EditorClientImpl class to insert + // characters received through the RenderWidget::OnHandleInputEvent() + // function. + view_->set_delay_seconds_for_form_state_sync(0); + LoadHTML("<html>" + "<head>" + "<title></title>" + "</head>" + "<body>" + "<div id='test' contenteditable='true'>" + "</div>" + "</body>" + "</html>"); + ExecuteJavaScript("document.getElementById('test').focus();"); + render_thread_.sink().ClearMessages(); + + // For each key code, we send three keyboard events. + // * Pressing only the key; + // * Pressing the key and a left-shift key, and; + // * Pressing the key and a right-alt (AltGr) key. + static const MockKeyboard::Modifiers kModifiers[] = { + MockKeyboard::NONE, + MockKeyboard::LEFT_SHIFT, + MockKeyboard::RIGHT_ALT, + }; + + MockKeyboard::Layout layout = kLayouts[i].layout; + for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifiers); ++j) { + // Virtual key codes used for this test. + static const int kKeyCodes[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', + VK_OEM_1, + VK_OEM_PLUS, + VK_OEM_COMMA, + VK_OEM_MINUS, + VK_OEM_PERIOD, + VK_OEM_2, + VK_OEM_3, + VK_OEM_4, + VK_OEM_5, + VK_OEM_6, + VK_OEM_7, + VK_OEM_8, + }; + + MockKeyboard::Modifiers modifiers = kModifiers[j]; + for (size_t k = 0; k <= ARRAYSIZE_UNSAFE(kKeyCodes); ++k) { + // Send a keyboard event to the RenderView object. + // We should test a keyboard event only when the given keyboard-layout + // driver is installed in a PC and the driver can assign a Unicode + // charcter for the given tuple (layout, key-code, and modifiers). + int key_code = kKeyCodes[k]; + std::wstring char_code; + if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0) + continue; + } + } + + // Retrieve the text in the test page and compare it with the expected + // text created from a virtual-key code, a character code, and the + // modifier-key status. + const int kMaxOutputCharacters = 4096; + std::wstring output; + GetMainFrame()->GetContentAsPlainText(kMaxOutputCharacters, &output); + EXPECT_EQ(kLayouts[i].expected_result, output); + } #else NOTIMPLEMENTED(); #endif diff --git a/chrome/test/render_view_test.cc b/chrome/test/render_view_test.cc index 9885334..4096238 100644 --- a/chrome/test/render_view_test.cc +++ b/chrome/test/render_view_test.cc @@ -4,12 +4,14 @@ #include "chrome/test/render_view_test.h" +#include "chrome/common/native_web_keyboard_event.h" #include "chrome/common/render_messages.h" #include "chrome/browser/extensions/extension_function_dispatcher.h" #include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/extension_process_bindings.h" #include "chrome/renderer/extensions/renderer_extension_bindings.h" #include "chrome/renderer/js_only_v8_extensions.h" +#include "webkit/api/public/WebInputEvent.h" #include "webkit/api/public/WebKit.h" #include "webkit/api/public/WebScriptSource.h" #include "webkit/glue/weburlrequest.h" @@ -74,6 +76,9 @@ void RenderViewTest::SetUp() { view_ = RenderView::Create(&render_thread_, NULL, NULL, kOpenerId, WebPreferences(), new SharedRenderViewCounter(0), kRouteId); + + // Attach a pseudo keyboard device to this object. + mock_keyboard_.reset(new MockKeyboard()); } void RenderViewTest::TearDown() { render_thread_.SendCloseMessage(); @@ -89,4 +94,53 @@ void RenderViewTest::TearDown() { WebKit::shutdown(); msg_loop_.RunAllPending(); + + mock_keyboard_.reset(); +} + +int RenderViewTest::SendKeyEvent(MockKeyboard::Layout layout, + int key_code, + MockKeyboard::Modifiers modifiers, + std::wstring* output) { +#if defined(OS_WIN) + // Retrieve the Unicode character for the given tuple (keyboard-layout, + // key-code, and modifiers). + // Exit when a keyboard-layout driver cannot assign a Unicode character to + // the tuple to prevent sending an invalid key code to the RenderView object. + CHECK(mock_keyboard_.get()); + CHECK(output); + int length = mock_keyboard_->GetCharacters(layout, key_code, modifiers, + output); + if (length != 1) + return -1; + + // Create IPC messages from Windows messages and send them to our + // back-end. + // A keyboard event of Windows consists of three Windows messages: + // WM_KEYDOWN, WM_CHAR, and WM_KEYUP. + // WM_KEYDOWN and WM_KEYUP sends virtual-key codes. On the other hand, + // WM_CHAR sends a composed Unicode character. + NativeWebKeyboardEvent keydown_event(NULL, WM_KEYDOWN, key_code, 0); + scoped_ptr<IPC::Message> keydown_message(new ViewMsg_HandleInputEvent(0)); + keydown_message->WriteData(reinterpret_cast<const char*>(&keydown_event), + sizeof(WebKit::WebKeyboardEvent)); + view_->OnHandleInputEvent(*keydown_message); + + NativeWebKeyboardEvent char_event(NULL, WM_CHAR, (*output)[0], 0); + scoped_ptr<IPC::Message> char_message(new ViewMsg_HandleInputEvent(0)); + char_message->WriteData(reinterpret_cast<const char*>(&char_event), + sizeof(WebKit::WebKeyboardEvent)); + view_->OnHandleInputEvent(*char_message); + + NativeWebKeyboardEvent keyup_event(NULL, WM_KEYUP, key_code, 0); + scoped_ptr<IPC::Message> keyup_message(new ViewMsg_HandleInputEvent(0)); + keyup_message->WriteData(reinterpret_cast<const char*>(&keyup_event), + sizeof(WebKit::WebKeyboardEvent)); + view_->OnHandleInputEvent(*keyup_message); + + return length; +#else + NOTIMPLEMENTED(); + return L'\0'; +#endif } diff --git a/chrome/test/render_view_test.h b/chrome/test/render_view_test.h index abda898..a399c7b 100644 --- a/chrome/test/render_view_test.h +++ b/chrome/test/render_view_test.h @@ -5,7 +5,10 @@ #ifndef CHROME_TEST_RENDER_VIEW_TEST_H_ #define CHROME_TEST_RENDER_VIEW_TEST_H_ +#include <string> + #include "base/scoped_ptr.h" +#include "chrome/renderer/mock_keyboard.h" #include "chrome/renderer/mock_render_process.h" #include "chrome/renderer/mock_render_thread.h" #include "chrome/renderer/render_view.h" @@ -32,6 +35,12 @@ class RenderViewTest : public testing::Test { // Loads the given HTML into the main frame as a data: URL. void LoadHTML(const char* html); + // Sends IPC messages that emulates a key-press event. + int SendKeyEvent(MockKeyboard::Layout layout, + int key_code, + MockKeyboard::Modifiers key_modifiers, + std::wstring* output); + // testing::Test virtual void SetUp(); @@ -42,6 +51,7 @@ class RenderViewTest : public testing::Test { scoped_ptr<MockProcess> mock_process_; scoped_refptr<RenderView> view_; RendererWebKitClientImpl webkitclient_; + scoped_ptr<MockKeyboard> mock_keyboard_; }; #endif // CHROME_TEST_RENDER_VIEW_TEST_H_ diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj index c0da965..08943c8 100644 --- a/chrome/test/unit/unittests.vcproj +++ b/chrome/test/unit/unittests.vcproj @@ -935,6 +935,22 @@ Name="test_infrastructure" > <File + RelativePath="..\..\renderer\mock_keyboard.cc" + > + </File> + <File + RelativePath="..\..\renderer\mock_keyboard.h" + > + </File> + <File + RelativePath="..\..\renderer\mock_keyboard_driver_win.cc" + > + </File> + <File + RelativePath="..\..\renderer\mock_keyboard_driver_win.h" + > + </File> + <File RelativePath="..\..\renderer\mock_render_process.h" > </File> |