summaryrefslogtreecommitdiffstats
path: root/content/renderer/render_view_browsertest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/renderer/render_view_browsertest.cc')
-rw-r--r--content/renderer/render_view_browsertest.cc1241
1 files changed, 1241 insertions, 0 deletions
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
new file mode 100644
index 0000000..0efb6de
--- /dev/null
+++ b/content/renderer/render_view_browsertest.cc
@@ -0,0 +1,1241 @@
+// 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/shared_memory.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/common/autofill_messages.h"
+#include "chrome/common/content_settings.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/renderer/autofill/autofill_agent.h"
+#include "chrome/renderer/print_web_view_helper.h"
+#include "chrome/test/render_view_test.h"
+#include "content/common/native_web_keyboard_event.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/Source/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "webkit/glue/form_data.h"
+#include "webkit/glue/form_field.h"
+#include "webkit/glue/web_io_operators.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;
+
+namespace {
+
+const char kPrintWithJSHTML[] =
+ "<body>Hello<script>window.print()</script>World</body>";
+
+} // namespace
+
+// 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("<input type=\"text\" id=\"elt_text\"></input>");
+
+ // 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 we get the correct UpdateState message when we go back twice
+// quickly without committing. Regression test for http://crbug.com/58082.
+TEST_F(RenderViewTest, LastCommittedUpdateState) {
+ // Load page A.
+ LoadHTML("<div>Page A</div>");
+
+ // Load page B, which will trigger an UpdateState message for page A.
+ LoadHTML("<div>Page B</div>");
+
+ // Check for a valid UpdateState message for page A.
+ const IPC::Message* msg_A = render_thread_.sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg_A);
+ int page_id_A;
+ std::string state_A;
+ ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
+ EXPECT_EQ(1, page_id_A);
+ render_thread_.sink().ClearMessages();
+
+ // Load page C, which will trigger an UpdateState message for page B.
+ LoadHTML("<div>Page C</div>");
+
+ // Check for a valid UpdateState for page B.
+ const IPC::Message* msg_B = render_thread_.sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg_B);
+ int page_id_B;
+ std::string state_B;
+ ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
+ EXPECT_EQ(2, page_id_B);
+ EXPECT_NE(state_A, state_B);
+ render_thread_.sink().ClearMessages();
+
+ // Load page D, which will trigger an UpdateState message for page C.
+ LoadHTML("<div>Page D</div>");
+
+ // Check for a valid UpdateState for page C.
+ const IPC::Message* msg_C = render_thread_.sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg_C);
+ int page_id_C;
+ std::string state_C;
+ ViewHostMsg_UpdateState::Read(msg_C, &page_id_C, &state_C);
+ EXPECT_EQ(3, page_id_C);
+ EXPECT_NE(state_B, state_C);
+ render_thread_.sink().ClearMessages();
+
+ // Go back to C and commit, preparing for our real test.
+ ViewMsg_Navigate_Params params_C;
+ params_C.transition = PageTransition::FORWARD_BACK;
+ params_C.page_id = 3;
+ params_C.state = state_C;
+ view_->OnNavigate(params_C);
+ ProcessPendingMessages();
+ render_thread_.sink().ClearMessages();
+
+ // Go back twice quickly, such that page B does not have a chance to commit.
+ // This leads to two changes to the back/forward list but only one change to
+ // the RenderView's page ID.
+
+ // Back to page B (page_id 2), without committing.
+ ViewMsg_Navigate_Params params_B;
+ params_B.transition = PageTransition::FORWARD_BACK;
+ params_B.page_id = 2;
+ params_B.state = state_B;
+ view_->OnNavigate(params_B);
+
+ // Back to page A (page_id 1) and commit.
+ ViewMsg_Navigate_Params params;
+ params.transition = PageTransition::FORWARD_BACK;
+ params.page_id = 1;
+ params.state = state_A;
+ view_->OnNavigate(params);
+ ProcessPendingMessages();
+
+ // Now ensure that the UpdateState message we receive is consistent
+ // and represents page C in both page_id and state.
+ const IPC::Message* msg = render_thread_.sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg);
+ int page_id;
+ std::string state;
+ ViewHostMsg_UpdateState::Read(msg, &page_id, &state);
+ EXPECT_EQ(page_id_C, page_id);
+ EXPECT_NE(state_A, state);
+ EXPECT_NE(state_B, state);
+ EXPECT_EQ(state_C, state);
+}
+
+// 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("<html>"
+ "<head>"
+ "</head>"
+ "<body>"
+ "<input id=\"test1\" type=\"text\"></input>"
+ "<input id=\"test2\" type=\"password\"></input>"
+ "</body>"
+ "</html>");
+ render_thread_.sink().ClearMessages();
+
+ const int kRepeatCount = 10;
+ for (int i = 0; i < kRepeatCount; i++) {
+ // Move the input focus to the first <input> 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, &params);
+ EXPECT_EQ(params.a, WebKit::WebTextInputTypeText);
+ EXPECT_TRUE(params.b.x() > 0 && params.b.y() > 0);
+
+ // Move the input focus to the second <input> 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, &params);
+ 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_CONFIRMCOMPOSITION, false, -1, -1, L"\x4F60\x597D", 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, L"", 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, L"", 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, L"", 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 <div> element,
+ // and move the input focus to the <div> element, where we can use
+ // IMEs.
+ view_->OnSetInputMethodActive(ime_message->enable);
+ view_->set_send_content_state_immediately(true);
+ LoadHTML("<html>"
+ "<head>"
+ "</head>"
+ "<body>"
+ "<div id=\"test1\" contenteditable=\"true\"></div>"
+ "</body>"
+ "</html>");
+ 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<WebKit::WebCompositionUnderline>(),
+ ime_message->selection_start,
+ ime_message->selection_end);
+ break;
+
+ case IME_CONFIRMCOMPOSITION:
+ view_->OnImeConfirmComposition(
+ WideToUTF16Hack(ime_message->ime_string));
+ break;
+
+ case IME_CANCELCOMPOSITION:
+ view_->OnImeSetComposition(string16(),
+ std::vector<WebKit::WebCompositionUnderline>(),
+ 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 <textarea> element and a <div> element.
+ // This test changes the text direction of the <textarea> element, and
+ // writes the values of its 'dir' attribute and its 'direction' property to
+ // verify that the text direction is changed.
+ view_->set_send_content_state_immediately(true);
+ LoadHTML("<html>"
+ "<head>"
+ "</head>"
+ "<body>"
+ "<textarea id=\"test\"></textarea>"
+ "<div id=\"result\" contenteditable=\"true\"></div>"
+ "</body>"
+ "</html>");
+ 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 <textarea> element.
+ ExecuteJavaScript("document.getElementById('test').focus();");
+ view_->OnSetTextDirection(kTextDirection[i].direction);
+
+ // Write the values of its DOM 'dir' attribute and its CSS 'direction'
+ // property to the <div> element.
+ ExecuteJavaScript("var result = document.getElementById('result');"
+ "var node = document.getElementById('test');"
+ "var style = getComputedStyle(node, null);"
+ "result.innerText ="
+ " node.getAttribute('dir') + ',' +"
+ " style.getPropertyValue('direction');");
+
+ // Copy the document content to std::wstring and compare with the
+ // expected result.
+ const int kMaxOutputCharacters = 16;
+ std::wstring output = UTF16ToWideHack(
+ GetMainFrame()->contentAsText(kMaxOutputCharacters));
+ EXPECT_EQ(output, kTextDirection[i].expected_result);
+ }
+}
+
+// Tests that printing pages work and sending and receiving messages through
+// that channel all works.
+TEST_F(RenderViewTest, OnPrintPages) {
+ // Lets simulate a print pages with Hello world.
+ LoadHTML("<body><p>Hello World!</p></body>");
+ view_->print_helper_->OnPrintPages();
+
+ VerifyPageCount(1);
+ VerifyPagesPrinted(true);
+}
+
+// Duplicate of OnPrintPagesTest only using javascript to print.
+TEST_F(RenderViewTest, PrintWithJavascript) {
+ // HTML contains a call to window.print()
+ LoadHTML(kPrintWithJSHTML);
+
+ VerifyPageCount(1);
+ VerifyPagesPrinted(true);
+}
+
+// Tests that the renderer blocks window.print() calls if they occur too
+// frequently.
+TEST_F(RenderViewTest, BlockScriptInitiatedPrinting) {
+ // Pretend user will cancel printing.
+ render_thread_.set_print_dialog_user_response(false);
+ // Try to print with window.print() a few times.
+ LoadHTML(kPrintWithJSHTML);
+ LoadHTML(kPrintWithJSHTML);
+ LoadHTML(kPrintWithJSHTML);
+ VerifyPagesPrinted(false);
+
+ // Pretend user will print. (but printing is blocked.)
+ render_thread_.set_print_dialog_user_response(true);
+ LoadHTML(kPrintWithJSHTML);
+ VerifyPagesPrinted(false);
+
+ // Unblock script initiated printing and verify printing works.
+ view_->print_helper_->ResetScriptedPrintCount();
+ render_thread_.printer()->ResetPrinter();
+ LoadHTML(kPrintWithJSHTML);
+ VerifyPageCount(1);
+ VerifyPagesPrinted(true);
+}
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+// TODO(estade): I don't think this test is worth porting to Linux. We will have
+// to rip out and replace most of the IPC code if we ever plan to improve
+// printing, and the comment below by sverrir suggests that it doesn't do much
+// for us anyway.
+TEST_F(RenderViewTest, PrintWithIframe) {
+ // Document that populates an iframe.
+ const char html[] =
+ "<html><body>Lorem Ipsum:"
+ "<iframe name=\"sub1\" id=\"sub1\"></iframe><script>"
+ " document.write(frames['sub1'].name);"
+ " frames['sub1'].document.write("
+ " '<p>Cras tempus ante eu felis semper luctus!</p>');"
+ "</script></body></html>";
+
+ LoadHTML(html);
+
+ // Find the frame and set it as the focused one. This should mean that that
+ // the printout should only contain the contents of that frame.
+ WebFrame* sub1_frame =
+ view_->webview()->findFrameByName(WebString::fromUTF8("sub1"));
+ ASSERT_TRUE(sub1_frame);
+ view_->webview()->setFocusedFrame(sub1_frame);
+ ASSERT_NE(view_->webview()->focusedFrame(),
+ view_->webview()->mainFrame());
+
+ // Initiate printing.
+ view_->print_helper_->OnPrintPages();
+
+ // Verify output through MockPrinter.
+ const MockPrinter* printer(render_thread_.printer());
+ ASSERT_EQ(1, printer->GetPrintedPages());
+ const printing::Image& image1(printer->GetPrintedPage(0)->image());
+
+ // TODO(sverrir): Figure out a way to improve this test to actually print
+ // only the content of the iframe. Currently image1 will contain the full
+ // page.
+ EXPECT_NE(0, image1.size().width());
+ EXPECT_NE(0, image1.size().height());
+}
+#endif
+
+// Tests if we can print a page and verify its results.
+// This test prints HTML pages into a pseudo printer and check their outputs,
+// i.e. a simplified version of the PrintingLayoutTextTest UI test.
+namespace {
+// Test cases used in this test.
+struct TestPageData {
+ const char* page;
+ size_t printed_pages;
+ int width;
+ int height;
+ const char* checksum;
+ const wchar_t* file;
+};
+
+const TestPageData kTestPages[] = {
+ {"<html>"
+ "<head>"
+ "<meta"
+ " http-equiv=\"Content-Type\""
+ " content=\"text/html; charset=utf-8\"/>"
+ "<title>Test 1</title>"
+ "</head>"
+ "<body style=\"background-color: white;\">"
+ "<p style=\"font-family: arial;\">Hello World!</p>"
+ "</body>",
+#if defined(OS_MACOSX)
+ // Mac printing code compensates for the WebKit scale factor while generating
+ // the metafile, so we expect smaller pages.
+ 1, 540, 720,
+#else
+ 1, 675, 900,
+#endif
+ NULL,
+ NULL,
+ },
+};
+} // namespace
+
+// TODO(estade): need to port MockPrinter to get this on Linux. This involves
+// hooking up Cairo to read a pdf stream, or accessing the cairo surface in the
+// metafile directly.
+#if defined(OS_WIN) || defined(OS_MACOSX)
+TEST_F(RenderViewTest, PrintLayoutTest) {
+ bool baseline = false;
+
+ EXPECT_TRUE(render_thread_.printer() != NULL);
+ for (size_t i = 0; i < arraysize(kTestPages); ++i) {
+ // Load an HTML page and print it.
+ LoadHTML(kTestPages[i].page);
+ view_->print_helper_->OnPrintPages();
+
+ // MockRenderThread::Send() just calls MockRenderThread::OnMsgReceived().
+ // So, all IPC messages sent in the above RenderView::OnPrintPages() call
+ // has been handled by the MockPrinter object, i.e. this printing job
+ // has been already finished.
+ // So, we can start checking the output pages of this printing job.
+ // Retrieve the number of pages actually printed.
+ size_t pages = render_thread_.printer()->GetPrintedPages();
+ EXPECT_EQ(kTestPages[i].printed_pages, pages);
+
+ // Retrieve the width and height of the output page.
+ int width = render_thread_.printer()->GetWidth(0);
+ int height = render_thread_.printer()->GetHeight(0);
+
+ // Check with margin for error. This has been failing with a one pixel
+ // offset on our buildbot.
+ const int kErrorMargin = 5; // 5%
+ EXPECT_GT(kTestPages[i].width * (100 + kErrorMargin) / 100, width);
+ EXPECT_LT(kTestPages[i].width * (100 - kErrorMargin) / 100, width);
+ EXPECT_GT(kTestPages[i].height * (100 + kErrorMargin) / 100, height);
+ EXPECT_LT(kTestPages[i].height* (100 - kErrorMargin) / 100, height);
+
+ // Retrieve the checksum of the bitmap data from the pseudo printer and
+ // compare it with the expected result.
+ std::string bitmap_actual;
+ EXPECT_TRUE(render_thread_.printer()->GetBitmapChecksum(0, &bitmap_actual));
+ if (kTestPages[i].checksum)
+ EXPECT_EQ(kTestPages[i].checksum, bitmap_actual);
+
+ if (baseline) {
+ // Save the source data and the bitmap data into temporary files to
+ // create base-line results.
+ FilePath source_path;
+ file_util::CreateTemporaryFile(&source_path);
+ render_thread_.printer()->SaveSource(0, source_path);
+
+ FilePath bitmap_path;
+ file_util::CreateTemporaryFile(&bitmap_path);
+ render_thread_.printer()->SaveBitmap(0, bitmap_path);
+ }
+ }
+}
+#endif
+
+// Test that we can receive correct DOM events when we send input events
+// through the RenderWidget::OnHandleInputEvent() function.
+TEST_F(RenderViewTest, OnHandleKeyboardEvent) {
+#if defined(OS_WIN) || defined(OS_LINUX)
+ // 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>".
+ // 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_send_content_state_immediately(true);
+ LoadHTML("<html>"
+ "<head>"
+ "<title></title>"
+ "<script type='text/javascript' language='javascript'>"
+ "function OnKeyEvent(ev) {"
+ " var result = document.getElementById(ev.type);"
+ " result.innerText ="
+ " (ev.which || ev.keyCode) + ',' +"
+ " ev.shiftKey + ',' +"
+ " ev.ctrlKey + ',' +"
+ " ev.altKey;"
+ " return true;"
+ "}"
+ "</script>"
+ "</head>"
+ "<body>"
+ "<input id='test' type='text'"
+ " onkeydown='return OnKeyEvent(event);'"
+ " onkeypress='return OnKeyEvent(event);'"
+ " onkeyup='return OnKeyEvent(event);'>"
+ "</input>"
+ "<div id='keydown' contenteditable='true'>"
+ "</div>"
+ "<div id='keypress' contenteditable='true'>"
+ "</div>"
+ "<div id='keyup' contenteditable='true'>"
+ "</div>"
+ "</body>"
+ "</html>");
+ ExecuteJavaScript("document.getElementById('test').focus();");
+ render_thread_.sink().ClearMessages();
+
+ static const MockKeyboard::Layout kLayouts[] = {
+#if defined(OS_WIN)
+ // Since we ignore the mock keyboard layout on Linux and instead just use
+ // the screen's keyboard layout, these trivially pass. They are commented
+ // out to avoid the illusion that they work.
+ MockKeyboard::LAYOUT_ARABIC,
+ MockKeyboard::LAYOUT_CANADIAN_FRENCH,
+ MockKeyboard::LAYOUT_FRENCH,
+ MockKeyboard::LAYOUT_HEBREW,
+ MockKeyboard::LAYOUT_RUSSIAN,
+#endif
+ MockKeyboard::LAYOUT_UNITED_STATES,
+ };
+
+ 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 char* expected_result;
+ } kModifierData[] = {
+ {MockKeyboard::NONE, "false,false,false"},
+ {MockKeyboard::LEFT_SHIFT, "true,false,false"},
+#if defined(OS_WIN)
+ {MockKeyboard::RIGHT_ALT, "false,false,true"},
+#endif
+ };
+
+ 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',
+ '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',
+ ui::VKEY_OEM_1,
+ ui::VKEY_OEM_PLUS,
+ ui::VKEY_OEM_COMMA,
+ ui::VKEY_OEM_MINUS,
+ ui::VKEY_OEM_PERIOD,
+ ui::VKEY_OEM_2,
+ ui::VKEY_OEM_3,
+ ui::VKEY_OEM_4,
+ ui::VKEY_OEM_5,
+ ui::VKEY_OEM_6,
+ ui::VKEY_OEM_7,
+#if defined(OS_WIN)
+ // Not sure how to handle this key on Linux.
+ ui::VKEY_OEM_8,
+#endif
+ };
+
+ 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 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 char expected_result[1024];
+ expected_result[0] = 0;
+ base::snprintf(&expected_result[0],
+ sizeof(expected_result),
+ "\n" // texts in the <input> element
+ "%d,%s\n" // texts in the first <div> element
+ "%d,%s\n" // texts in the second <div> element
+ "%d,%s", // texts in the third <div> element
+ key_code, kModifierData[j].expected_result,
+ static_cast<int>(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
+ // modifier-key status.
+ const int kMaxOutputCharacters = 1024;
+ std::string output = UTF16ToUTF8(
+ GetMainFrame()->contentAsText(kMaxOutputCharacters));
+ EXPECT_EQ(expected_result, output);
+ }
+ }
+ }
+#else
+ NOTIMPLEMENTED();
+#endif
+}
+
+// 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) || defined(OS_LINUX)
+ 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
+#if defined(OS_WIN)
+ // On Linux, the only way to test alternate keyboard layouts is to change
+ // the keyboard layout of the whole screen. I'm worried about the side
+ // effects this may have on the buildbots.
+ {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"
+ },
+#endif // defined(OS_WIN)
+ {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"
+#if defined(OS_WIN)
+ // This is ifdefed out for Linux to correspond to the fact that we don't
+ // test alt+keystroke for now.
+ 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"
+#endif
+ },
+ };
+
+ 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_send_content_state_immediately(true);
+ 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,
+#if defined(OS_WIN)
+ MockKeyboard::RIGHT_ALT,
+#endif
+ };
+
+ 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',
+ ui::VKEY_OEM_1,
+ ui::VKEY_OEM_PLUS,
+ ui::VKEY_OEM_COMMA,
+ ui::VKEY_OEM_MINUS,
+ ui::VKEY_OEM_PERIOD,
+ ui::VKEY_OEM_2,
+ ui::VKEY_OEM_3,
+ ui::VKEY_OEM_4,
+ ui::VKEY_OEM_5,
+ ui::VKEY_OEM_6,
+ ui::VKEY_OEM_7,
+#if defined(OS_WIN)
+ // Unclear how to handle this on Linux.
+ ui::VKEY_OEM_8,
+#endif
+ };
+
+ 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 = UTF16ToWideHack(
+ GetMainFrame()->contentAsText(kMaxOutputCharacters));
+ EXPECT_EQ(kLayouts[i].expected_result, output);
+ }
+#else
+ NOTIMPLEMENTED();
+#endif
+}
+
+// Crashy, http://crbug.com/53247.
+TEST_F(RenderViewTest, DISABLED_DidFailProvisionalLoadWithErrorForError) {
+ GetMainFrame()->enableViewSourceMode(true);
+ WebURLError error;
+ error.domain.fromUTF8("test_domain");
+ error.reason = net::ERR_FILE_NOT_FOUND;
+ error.unreachableURL = GURL("http://foo");
+ WebFrame* web_frame = GetMainFrame();
+ // An error occurred.
+ view_->didFailProvisionalLoad(web_frame, error);
+ // Frame should exit view-source mode.
+ EXPECT_FALSE(web_frame->isViewSourceModeEnabled());
+}
+
+TEST_F(RenderViewTest, DidFailProvisionalLoadWithErrorForCancellation) {
+ GetMainFrame()->enableViewSourceMode(true);
+ WebURLError error;
+ error.domain.fromUTF8("test_domain");
+ error.reason = net::ERR_ABORTED;
+ error.unreachableURL = GURL("http://foo");
+ WebFrame* web_frame = GetMainFrame();
+ // A cancellation occurred.
+ view_->didFailProvisionalLoad(web_frame, error);
+ // Frame should stay in view-source mode.
+ EXPECT_TRUE(web_frame->isViewSourceModeEnabled());
+}
+
+// Regression test for http://crbug.com/35011
+TEST_F(RenderViewTest, JSBlockSentAfterPageLoad) {
+ // 1. Load page with JS.
+ std::string html = "<html>"
+ "<head>"
+ "<script>document.createElement('div');</script>"
+ "</head>"
+ "<body>"
+ "</body>"
+ "</html>";
+ render_thread_.sink().ClearMessages();
+ LoadHTML(html.c_str());
+
+ // 2. Block JavaScript.
+ ContentSettings settings;
+ for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i)
+ settings.settings[i] = CONTENT_SETTING_ALLOW;
+ settings.settings[CONTENT_SETTINGS_TYPE_JAVASCRIPT] = CONTENT_SETTING_BLOCK;
+ view_->SetContentSettings(settings);
+
+ // Make sure no pending messages are in the queue.
+ ProcessPendingMessages();
+ render_thread_.sink().ClearMessages();
+
+ // 3. Reload page.
+ ViewMsg_Navigate_Params params;
+ std::string url_str = "data:text/html;charset=utf-8,";
+ url_str.append(html);
+ GURL url(url_str);
+ params.url = url;
+ params.navigation_type = ViewMsg_Navigate_Params::RELOAD;
+ view_->OnNavigate(params);
+ ProcessPendingMessages();
+
+ // 4. Verify that the notification that javascript was blocked is sent after
+ // the navigation notifiction is sent.
+ int navigation_index = -1;
+ int block_index = -1;
+ for (size_t i = 0; i < render_thread_.sink().message_count(); ++i) {
+ const IPC::Message* msg = render_thread_.sink().GetMessageAt(i);
+ if (msg->type() == ViewHostMsg_FrameNavigate::ID)
+ navigation_index = i;
+ if (msg->type() == ViewHostMsg_ContentBlocked::ID)
+ block_index = i;
+ }
+ EXPECT_NE(-1, navigation_index);
+ EXPECT_NE(-1, block_index);
+ EXPECT_LT(navigation_index, block_index);
+}
+
+// Regression test for http://crbug.com/41562
+TEST_F(RenderViewTest, UpdateTargetURLWithInvalidURL) {
+ const GURL invalid_gurl("http://");
+ view_->setMouseOverURL(WebKit::WebURL(invalid_gurl));
+ EXPECT_EQ(invalid_gurl, view_->target_url_);
+}
+
+TEST_F(RenderViewTest, SendForms) {
+ // 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("<form method=\"POST\">"
+ " <input type=\"text\" id=\"firstname\"/>"
+ " <input type=\"text\" id=\"middlename\" autoComplete=\"off\"/>"
+ " <input type=\"hidden\" id=\"lastname\"/>"
+ " <select id=\"state\"/>"
+ " <option>?</option>"
+ " <option>California</option>"
+ " <option>Texas</option>"
+ " </select>"
+ "</form>");
+
+ // Verify that "FormsSeen" sends the expected number of fields.
+ ProcessPendingMessages();
+ const IPC::Message* message = render_thread_.sink().GetFirstMessageMatching(
+ AutoFillHostMsg_FormsSeen::ID);
+ ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
+ AutoFillHostMsg_FormsSeen::Param params;
+ AutoFillHostMsg_FormsSeen::Read(message, &params);
+ const std::vector<FormData>& forms = params.a;
+ ASSERT_EQ(1UL, forms.size());
+ ASSERT_EQ(3UL, forms[0].fields.size());
+ EXPECT_TRUE(forms[0].fields[0].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("firstname"),
+ string16(),
+ ASCIIToUTF16("text"),
+ WebInputElement::defaultMaxLength(),
+ false))) << forms[0].fields[0];
+ EXPECT_TRUE(forms[0].fields[1].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("middlename"),
+ string16(),
+ ASCIIToUTF16("text"),
+ WebInputElement::defaultMaxLength(),
+ false))) << forms[0].fields[1];
+ EXPECT_TRUE(forms[0].fields[2].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("state"),
+ ASCIIToUTF16("?"),
+ ASCIIToUTF16("select-one"),
+ 0,
+ false))) << forms[0].fields[2];
+
+ // Verify that |didAcceptAutoFillSuggestion()| sends the expected number of
+ // fields.
+ WebFrame* web_frame = GetMainFrame();
+ WebDocument document = web_frame->document();
+ WebInputElement firstname =
+ document.getElementById("firstname").to<WebInputElement>();
+
+ // Accept suggestion that contains a label. Labeled items indicate AutoFill
+ // as opposed to Autocomplete. We're testing this distinction below with
+ // the |AutoFillHostMsg_FillAutoFillFormData::ID| message.
+ autofill_agent_->didAcceptAutoFillSuggestion(
+ firstname,
+ WebKit::WebString::fromUTF8("Johnny"),
+ WebKit::WebString::fromUTF8("Home"),
+ 1,
+ -1);
+
+ ProcessPendingMessages();
+ const IPC::Message* message2 = render_thread_.sink().GetUniqueMessageMatching(
+ AutoFillHostMsg_FillAutoFillFormData::ID);
+ ASSERT_NE(static_cast<IPC::Message*>(NULL), message2);
+ AutoFillHostMsg_FillAutoFillFormData::Param params2;
+ AutoFillHostMsg_FillAutoFillFormData::Read(message2, &params2);
+ const FormData& form2 = params2.b;
+ ASSERT_EQ(3UL, form2.fields.size());
+ EXPECT_TRUE(form2.fields[0].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("firstname"),
+ string16(),
+ ASCIIToUTF16("text"),
+ WebInputElement::defaultMaxLength(),
+ false))) << form2.fields[0];
+ EXPECT_TRUE(form2.fields[1].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("middlename"),
+ string16(),
+ ASCIIToUTF16("text"),
+ WebInputElement::defaultMaxLength(),
+ false))) << form2.fields[1];
+ EXPECT_TRUE(form2.fields[2].StrictlyEqualsHack(
+ FormField(string16(),
+ ASCIIToUTF16("state"),
+ ASCIIToUTF16("?"),
+ ASCIIToUTF16("select-one"),
+ 0,
+ false))) << form2.fields[2];
+}
+
+TEST_F(RenderViewTest, FillFormElement) {
+ // 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("<form method=\"POST\">"
+ " <input type=\"text\" id=\"firstname\"/>"
+ " <input type=\"text\" id=\"middlename\"/>"
+ "</form>");
+
+ // Verify that "FormsSeen" isn't sent, as there are too few fields.
+ ProcessPendingMessages();
+ const IPC::Message* message = render_thread_.sink().GetFirstMessageMatching(
+ AutoFillHostMsg_FormsSeen::ID);
+ ASSERT_EQ(static_cast<IPC::Message*>(NULL), message);
+
+ // Verify that |didAcceptAutoFillSuggestion()| sets the value of the expected
+ // field.
+ WebFrame* web_frame = GetMainFrame();
+ WebDocument document = web_frame->document();
+ WebInputElement firstname =
+ document.getElementById("firstname").to<WebInputElement>();
+ WebInputElement middlename =
+ document.getElementById("middlename").to<WebInputElement>();
+ middlename.setAutofilled(true);
+
+ // Accept a suggestion in a form that has been auto-filled. This triggers
+ // the direct filling of the firstname element with value parameter.
+ autofill_agent_->didAcceptAutoFillSuggestion(firstname,
+ WebString::fromUTF8("David"),
+ WebString(),
+ 0,
+ 0);
+
+ ProcessPendingMessages();
+ const IPC::Message* message2 = render_thread_.sink().GetUniqueMessageMatching(
+ AutoFillHostMsg_FillAutoFillFormData::ID);
+
+ // No message should be sent in this case. |firstname| is filled directly.
+ ASSERT_EQ(static_cast<IPC::Message*>(NULL), message2);
+ EXPECT_EQ(firstname.value(), WebKit::WebString::fromUTF8("David"));
+}
+
+// Tests that we send the right translatable for a page and that we respect the
+// "no translate" meta-tag.
+TEST_F(RenderViewTest, TranslatablePage) {
+ // Suppress the normal delay that occurs when the page is loaded before which
+ // the renderer sends the page contents to the browser.
+ view_->set_send_content_state_immediately(true);
+
+ LoadHTML("<html><body>A random page with random content.</body></html>");
+ ProcessPendingMessages();
+ const IPC::Message* message = render_thread_.sink().GetUniqueMessageMatching(
+ ViewHostMsg_PageContents::ID);
+ ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
+ ViewHostMsg_PageContents::Param params;
+ ViewHostMsg_PageContents::Read(message, &params);
+ EXPECT_TRUE(params.e); // Translatable should be true.
+ render_thread_.sink().ClearMessages();
+
+ // Now the page specifies the META tag to prevent translation.
+ LoadHTML("<html><head><meta name=\"google\" value=\"notranslate\"></head>"
+ "<body>A random page with random content.</body></html>");
+ ProcessPendingMessages();
+ message = render_thread_.sink().GetUniqueMessageMatching(
+ ViewHostMsg_PageContents::ID);
+ ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
+ ViewHostMsg_PageContents::Read(message, &params);
+ EXPECT_FALSE(params.e); // Translatable should be false.
+ render_thread_.sink().ClearMessages();
+
+ // Try the alternate version of the META tag (content instead of value).
+ LoadHTML("<html><head><meta name=\"google\" content=\"notranslate\"></head>"
+ "<body>A random page with random content.</body></html>");
+ ProcessPendingMessages();
+ message = render_thread_.sink().GetUniqueMessageMatching(
+ ViewHostMsg_PageContents::ID);
+ ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
+ ViewHostMsg_PageContents::Read(message, &params);
+ EXPECT_FALSE(params.e); // Translatable should be false.
+}
+
+// Tests that the language meta tag takes precedence over the CLD when reporting
+// the page's language.
+TEST_F(RenderViewTest, LanguageMetaTag) {
+ // Suppress the normal delay that occurs when the page is loaded before which
+ // the renderer sends the page contents to the browser.
+ view_->set_send_content_state_immediately(true);
+
+ LoadHTML("<html><head><meta http-equiv=\"content-language\" content=\"es\">"
+ "</head><body>A random page with random content.</body></html>");
+ ProcessPendingMessages();
+ const IPC::Message* message = render_thread_.sink().GetUniqueMessageMatching(
+ ViewHostMsg_PageContents::ID);
+ ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
+ ViewHostMsg_PageContents::Param params;
+ ViewHostMsg_PageContents::Read(message, &params);
+ EXPECT_EQ("es", params.d);
+ render_thread_.sink().ClearMessages();
+
+ // Makes sure we support multiple languages specified.
+ LoadHTML("<html><head><meta http-equiv=\"content-language\" "
+ "content=\" fr , es,en \">"
+ "</head><body>A random page with random content.</body></html>");
+ ProcessPendingMessages();
+ message = render_thread_.sink().GetUniqueMessageMatching(
+ ViewHostMsg_PageContents::ID);
+ ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
+ ViewHostMsg_PageContents::Read(message, &params);
+ EXPECT_EQ("fr", params.d);
+}