// Copyright (c) 2010 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 "base/basictypes.h" #include "base/file_util.h" #include "base/keyboard_codes.h" #include "base/shared_memory.h" #include "base/utf_string_conversions.h" #include "chrome/common/content_settings.h" #include "chrome/common/native_web_keyboard_event.h" #include "chrome/common/render_messages.h" #include "chrome/renderer/print_web_view_helper.h" #include "chrome/test/render_view_test.h" #include "gfx/codec/jpeg_codec.h" #include "net/base/net_errors.h" #include "printing/image.h" #include "printing/native_metafile.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h" #include "third_party/WebKit/WebKit/chromium/public/WebString.h" #include "third_party/WebKit/WebKit/chromium/public/WebURLError.h" #include "third_party/WebKit/WebKit/chromium/public/WebView.h" #include "webkit/glue/form_data.h" #include "webkit/glue/form_field.h" using WebKit::WebDocument; using WebKit::WebFrame; using WebKit::WebInputElement; using WebKit::WebString; using WebKit::WebTextDirection; using WebKit::WebURLError; using webkit_glue::FormData; using webkit_glue::FormField; // Test that we get form state change notifications when input fields change. TEST_F(RenderViewTest, OnNavStateChanged) { // Don't want any delay for form state sync changes. This will still post a // message so updates will get coalesced, but as soon as we spin the message // loop, it will generate an update. view_->set_send_content_state_immediately(true); LoadHTML(""); // We should NOT have gotten a form state change notification yet. EXPECT_FALSE(render_thread_.sink().GetFirstMessageMatching( ViewHostMsg_UpdateState::ID)); render_thread_.sink().ClearMessages(); // Change the value of the input. We should have gotten an update state // notification. We need to spin the message loop to catch this update. ExecuteJavaScript("document.getElementById('elt_text').value = 'foo';"); ProcessPendingMessages(); EXPECT_TRUE(render_thread_.sink().GetUniqueMessageMatching( ViewHostMsg_UpdateState::ID)); } // Test that our IME backend sends a notification message when the input focus // changes. TEST_F(RenderViewTest, OnImeStateChanged) { // Enable our IME backend code. view_->OnSetInputMethodActive(true); // Load an HTML page consisting of two input fields. view_->set_send_content_state_immediately(true); LoadHTML("" "" "" "" "" "" "" ""); render_thread_.sink().ClearMessages(); const int kRepeatCount = 10; for (int i = 0; i < kRepeatCount; i++) { // Move the input focus to the first element, where we should // activate IMEs. ExecuteJavaScript("document.getElementById('test1').focus();"); ProcessPendingMessages(); render_thread_.sink().ClearMessages(); // Update the IME status and verify if our IME backend sends an IPC message // to activate IMEs. view_->UpdateInputMethod(); const IPC::Message* msg = render_thread_.sink().GetMessageAt(0); EXPECT_TRUE(msg != NULL); EXPECT_EQ(ViewHostMsg_ImeUpdateTextInputState::ID, msg->type()); ViewHostMsg_ImeUpdateTextInputState::Param params; ViewHostMsg_ImeUpdateTextInputState::Read(msg, ¶ms); EXPECT_EQ(params.a, WebKit::WebTextInputTypeText); EXPECT_TRUE(params.b.x() > 0 && params.b.y() > 0); // Move the input focus to the second element, where we should // de-activate IMEs. ExecuteJavaScript("document.getElementById('test2').focus();"); ProcessPendingMessages(); render_thread_.sink().ClearMessages(); // Update the IME status and verify if our IME backend sends an IPC message // to de-activate IMEs. view_->UpdateInputMethod(); msg = render_thread_.sink().GetMessageAt(0); EXPECT_TRUE(msg != NULL); EXPECT_EQ(ViewHostMsg_ImeUpdateTextInputState::ID, msg->type()); ViewHostMsg_ImeUpdateTextInputState::Read(msg, ¶ms); EXPECT_EQ(params.a, WebKit::WebTextInputTypePassword); } } // Test that our IME backend can compose CJK words. // Our IME front-end sends many platform-independent messages to the IME backend // while it composes CJK words. This test sends the minimal messages captured // on my local environment directly to the IME backend to verify if the backend // can compose CJK words without any problems. // This test uses an array of command sets because an IME composotion does not // only depends on IME events, but also depends on window events, e.g. moving // the window focus while composing a CJK text. To handle such complicated // cases, this test should not only call IME-related functions in the // RenderWidget class, but also call some RenderWidget members, e.g. // ExecuteJavaScript(), RenderWidget::OnSetFocus(), etc. TEST_F(RenderViewTest, ImeComposition) { enum ImeCommand { IME_INITIALIZE, IME_SETINPUTMODE, IME_SETFOCUS, IME_SETCOMPOSITION, IME_CONFIRMCOMPOSITION, IME_CANCELCOMPOSITION }; struct ImeMessage { ImeCommand command; bool enable; int selection_start; int selection_end; const wchar_t* ime_string; const wchar_t* result; }; static const ImeMessage kImeMessages[] = { // Scenario 1: input a Chinese word with Microsoft IME (on Vista). {IME_INITIALIZE, true, 0, 0, NULL, NULL}, {IME_SETINPUTMODE, true, 0, 0, NULL, NULL}, {IME_SETFOCUS, true, 0, 0, NULL, NULL}, {IME_SETCOMPOSITION, false, 1, 1, L"n", L"n"}, {IME_SETCOMPOSITION, false, 2, 2, L"ni", L"ni"}, {IME_SETCOMPOSITION, false, 3, 3, L"nih", L"nih"}, {IME_SETCOMPOSITION, false, 4, 4, L"niha", L"niha"}, {IME_SETCOMPOSITION, false, 5, 5, L"nihao", L"nihao"}, {IME_SETCOMPOSITION, false, 2, 2, L"\x4F60\x597D", L"\x4F60\x597D"}, {IME_CONFIRMCOMPOSITION, false, -1, -1, NULL, L"\x4F60\x597D"}, {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\x4F60\x597D"}, // Scenario 2: input a Japanese word with Microsoft IME (on Vista). {IME_INITIALIZE, true, 0, 0, NULL, NULL}, {IME_SETINPUTMODE, true, 0, 0, NULL, NULL}, {IME_SETFOCUS, true, 0, 0, NULL, NULL}, {IME_SETCOMPOSITION, false, 0, 1, L"\xFF4B", L"\xFF4B"}, {IME_SETCOMPOSITION, false, 0, 1, L"\x304B", L"\x304B"}, {IME_SETCOMPOSITION, false, 0, 2, L"\x304B\xFF4E", L"\x304B\xFF4E"}, {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\xFF4A", L"\x304B\x3093\xFF4A"}, {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\x3058", L"\x304B\x3093\x3058"}, {IME_SETCOMPOSITION, false, 0, 2, L"\x611F\x3058", L"\x611F\x3058"}, {IME_SETCOMPOSITION, false, 0, 2, L"\x6F22\x5B57", L"\x6F22\x5B57"}, {IME_CONFIRMCOMPOSITION, false, -1, -1, NULL, L"\x6F22\x5B57"}, {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"}, // Scenario 3: input a Korean word with Microsot IME (on Vista). {IME_INITIALIZE, true, 0, 0, NULL, NULL}, {IME_SETINPUTMODE, true, 0, 0, NULL, NULL}, {IME_SETFOCUS, true, 0, 0, NULL, NULL}, {IME_SETCOMPOSITION, false, 0, 1, L"\x3147", L"\x3147"}, {IME_SETCOMPOSITION, false, 0, 1, L"\xC544", L"\xC544"}, {IME_SETCOMPOSITION, false, 0, 1, L"\xC548", L"\xC548"}, {IME_CONFIRMCOMPOSITION, false, -1, -1, NULL, L"\xC548"}, {IME_SETCOMPOSITION, false, 0, 1, L"\x3134", L"\xC548\x3134"}, {IME_SETCOMPOSITION, false, 0, 1, L"\xB140", L"\xC548\xB140"}, {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"}, {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\xC548"}, {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"}, {IME_CONFIRMCOMPOSITION, false, -1, -1, NULL, L"\xC548\xB155"}, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kImeMessages); i++) { const ImeMessage* ime_message = &kImeMessages[i]; switch (ime_message->command) { case IME_INITIALIZE: // Load an HTML page consisting of a content-editable
element, // and move the input focus to the
element, where we can use // IMEs. view_->OnSetInputMethodActive(ime_message->enable); view_->set_send_content_state_immediately(true); LoadHTML("" "" "" "" "
" "" ""); ExecuteJavaScript("document.getElementById('test1').focus();"); break; case IME_SETINPUTMODE: // Activate (or deactivate) our IME back-end. view_->OnSetInputMethodActive(ime_message->enable); break; case IME_SETFOCUS: // Update the window focus. view_->OnSetFocus(ime_message->enable); break; case IME_SETCOMPOSITION: view_->OnImeSetComposition( WideToUTF16Hack(ime_message->ime_string), std::vector(), ime_message->selection_start, ime_message->selection_end); break; case IME_CONFIRMCOMPOSITION: view_->OnImeConfirmComposition(); break; case IME_CANCELCOMPOSITION: view_->OnImeSetComposition(string16(), std::vector(), 0, 0); break; } // Update the status of our IME back-end. // TODO(hbono): we should verify messages to be sent from the back-end. view_->UpdateInputMethod(); ProcessPendingMessages(); render_thread_.sink().ClearMessages(); if (ime_message->result) { // Retrieve the content of this page and compare it with the expected // result. const int kMaxOutputCharacters = 128; std::wstring output = UTF16ToWideHack( GetMainFrame()->contentAsText(kMaxOutputCharacters)); EXPECT_EQ(output, ime_message->result); } } } // Test that the RenderView::OnSetTextDirection() function can change the text // direction of the selected input element. TEST_F(RenderViewTest, OnSetTextDirection) { // Load an HTML page consisting of a " "
" "" ""); render_thread_.sink().ClearMessages(); static const struct { WebTextDirection direction; const wchar_t* expected_result; } kTextDirection[] = { { WebKit::WebTextDirectionRightToLeft, L"\x000A" L"rtl,rtl" }, { WebKit::WebTextDirectionLeftToRight, L"\x000A" L"ltr,ltr" }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTextDirection); ++i) { // Set the text direction of the