Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Unified Diff: content/renderer/render_view_browsertest.cc

Issue 112203003: Fix renderer crashes when frame gets detached while injectng user scripts. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Defer frame destruction until event loop. Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/render_view_browsertest.cc
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 19c3d430d99da31ce3a64eb507245292d71c4be1..f6f25f44a551e2ffd56c04a5b3312b05c0fa958a 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -1,2253 +1,2307 @@
-// Copyright (c) 2012 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/bind.h"
-#include "base/callback.h"
-#include "base/memory/shared_memory.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/win/windows_version.h"
-#include "content/common/frame_messages.h"
-#include "content/common/ssl_status_serialization.h"
-#include "content/common/view_messages.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/native_web_keyboard_event.h"
-#include "content/public/browser/web_ui_controller_factory.h"
-#include "content/public/common/bindings_policy.h"
-#include "content/public/common/page_zoom.h"
-#include "content/public/common/url_constants.h"
-#include "content/public/common/url_utils.h"
-#include "content/public/renderer/document_state.h"
-#include "content/public/renderer/history_item_serialization.h"
-#include "content/public/renderer/navigation_state.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/render_view_test.h"
-#include "content/public/test/test_utils.h"
-#include "content/renderer/render_view_impl.h"
-#include "content/shell/browser/shell.h"
-#include "content/shell/browser/shell_browser_context.h"
-#include "content/test/mock_keyboard.h"
-#include "net/base/net_errors.h"
-#include "net/cert/cert_status_flags.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/public/platform/WebData.h"
-#include "third_party/WebKit/public/platform/WebHTTPBody.h"
-#include "third_party/WebKit/public/platform/WebString.h"
-#include "third_party/WebKit/public/platform/WebURLResponse.h"
-#include "third_party/WebKit/public/web/WebDataSource.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
-#include "third_party/WebKit/public/web/WebHistoryItem.h"
-#include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
-#include "third_party/WebKit/public/web/WebView.h"
-#include "third_party/WebKit/public/web/WebWindowFeatures.h"
-#include "ui/events/keycodes/keyboard_codes.h"
-#include "ui/gfx/codec/jpeg_codec.h"
-#include "ui/gfx/range/range.h"
-
-#if defined(OS_LINUX) && !defined(USE_AURA)
-#include "ui/base/gtk/event_synthesis_gtk.h"
-#endif
-
-#if defined(USE_AURA)
-#include "ui/events/event.h"
-#endif
-
-#if defined(USE_AURA) && defined(USE_X11)
-#include <X11/Xlib.h>
-#include "ui/events/event_constants.h"
-#include "ui/events/keycodes/keyboard_code_conversion.h"
-#include "ui/events/test/events_test_utils_x11.h"
-#endif
-
-#if defined(USE_OZONE)
-#include "ui/events/keycodes/keyboard_code_conversion.h"
-#endif
-
-using blink::WebFrame;
-using blink::WebInputEvent;
-using blink::WebMouseEvent;
-using blink::WebRuntimeFeatures;
-using blink::WebString;
-using blink::WebTextDirection;
-using blink::WebURLError;
-
-namespace content {
-
-namespace {
-
-#if (defined(USE_AURA) && defined(USE_X11)) || defined(USE_OZONE)
-// Converts MockKeyboard::Modifiers to ui::EventFlags.
-int ConvertMockKeyboardModifier(MockKeyboard::Modifiers modifiers) {
- static struct ModifierMap {
- MockKeyboard::Modifiers src;
- int dst;
- } kModifierMap[] = {
- { MockKeyboard::LEFT_SHIFT, ui::EF_SHIFT_DOWN },
- { MockKeyboard::RIGHT_SHIFT, ui::EF_SHIFT_DOWN },
- { MockKeyboard::LEFT_CONTROL, ui::EF_CONTROL_DOWN },
- { MockKeyboard::RIGHT_CONTROL, ui::EF_CONTROL_DOWN },
- { MockKeyboard::LEFT_ALT, ui::EF_ALT_DOWN },
- { MockKeyboard::RIGHT_ALT, ui::EF_ALT_DOWN },
- };
- int flags = 0;
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kModifierMap); ++i) {
- if (kModifierMap[i].src & modifiers) {
- flags |= kModifierMap[i].dst;
- }
- }
- return flags;
-}
-#endif
-
-class WebUITestWebUIControllerFactory : public WebUIControllerFactory {
- public:
- virtual WebUIController* CreateWebUIControllerForURL(
- WebUI* web_ui, const GURL& url) const OVERRIDE {
- return NULL;
- }
- virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
- const GURL& url) const OVERRIDE {
- return WebUI::kNoWebUI;
- }
- virtual bool UseWebUIForURL(BrowserContext* browser_context,
- const GURL& url) const OVERRIDE {
- return HasWebUIScheme(url);
- }
- virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
- const GURL& url) const OVERRIDE {
- return HasWebUIScheme(url);
- }
-};
-
-class RenderViewImplTest : public RenderViewTest {
- public:
- RenderViewImplTest() {
- // Attach a pseudo keyboard device to this object.
- mock_keyboard_.reset(new MockKeyboard());
- }
-
- virtual ~RenderViewImplTest() {}
-
- virtual void SetUp() OVERRIDE {
- RenderViewTest::SetUp();
- // Enable Blink's experimental and test only features so that test code
- // does not have to bother enabling each feature.
- WebRuntimeFeatures::enableExperimentalFeatures(true);
- WebRuntimeFeatures::enableTestOnlyFeatures(true);
- }
-
- RenderViewImpl* view() {
- return static_cast<RenderViewImpl*>(view_);
- }
-
- RenderFrameImpl* frame() {
- return static_cast<RenderFrameImpl*>(view()->GetMainRenderFrame());
- }
-
- // Sends IPC messages that emulates a key-press event.
- int SendKeyEvent(MockKeyboard::Layout layout,
- int key_code,
- MockKeyboard::Modifiers modifiers,
- base::string16* 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.
- MSG msg1 = { NULL, WM_KEYDOWN, key_code, 0 };
-#if defined(USE_AURA)
- ui::KeyEvent evt1(msg1, false);
- NativeWebKeyboardEvent keydown_event(&evt1);
-#else
- NativeWebKeyboardEvent keydown_event(msg1);
-#endif
- SendNativeKeyEvent(keydown_event);
-
- MSG msg2 = { NULL, WM_CHAR, (*output)[0], 0 };
-#if defined(USE_AURA)
- ui::KeyEvent evt2(msg2, true);
- NativeWebKeyboardEvent char_event(&evt2);
-#else
- NativeWebKeyboardEvent char_event(msg2);
-#endif
- SendNativeKeyEvent(char_event);
-
- MSG msg3 = { NULL, WM_KEYUP, key_code, 0 };
-#if defined(USE_AURA)
- ui::KeyEvent evt3(msg3, false);
- NativeWebKeyboardEvent keyup_event(&evt3);
-#else
- NativeWebKeyboardEvent keyup_event(msg3);
-#endif
- SendNativeKeyEvent(keyup_event);
-
- return length;
-#elif defined(USE_AURA) && defined(USE_X11)
- // We ignore |layout|, which means we are only testing the layout of the
- // current locale. TODO(mazda): fix this to respect |layout|.
- CHECK(output);
- const int flags = ConvertMockKeyboardModifier(modifiers);
-
- ui::ScopedXI2Event xevent;
- xevent.InitKeyEvent(ui::ET_KEY_PRESSED,
- static_cast<ui::KeyboardCode>(key_code),
- flags);
- ui::KeyEvent event1(xevent, false);
- NativeWebKeyboardEvent keydown_event(&event1);
- SendNativeKeyEvent(keydown_event);
-
- xevent.InitKeyEvent(ui::ET_KEY_PRESSED,
- static_cast<ui::KeyboardCode>(key_code),
- flags);
- ui::KeyEvent event2(xevent, true);
- NativeWebKeyboardEvent char_event(&event2);
- SendNativeKeyEvent(char_event);
-
- xevent.InitKeyEvent(ui::ET_KEY_RELEASED,
- static_cast<ui::KeyboardCode>(key_code),
- flags);
- ui::KeyEvent event3(xevent, false);
- NativeWebKeyboardEvent keyup_event(&event3);
- SendNativeKeyEvent(keyup_event);
-
- long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
- flags);
- output->assign(1, static_cast<base::char16>(c));
- return 1;
-#elif defined(USE_OZONE)
- const int flags = ConvertMockKeyboardModifier(modifiers);
-
- // Ozone's native events are ui::Events. So first create the "native" event,
- // then create the actual ui::KeyEvent with the native event.
- ui::KeyEvent keydown_native_event(ui::ET_KEY_PRESSED,
- static_cast<ui::KeyboardCode>(key_code),
- flags,
- true);
- ui::KeyEvent keydown_event(&keydown_native_event, false);
- NativeWebKeyboardEvent keydown_web_event(&keydown_event);
- SendNativeKeyEvent(keydown_web_event);
-
- ui::KeyEvent char_native_event(ui::ET_KEY_PRESSED,
- static_cast<ui::KeyboardCode>(key_code),
- flags,
- true);
- ui::KeyEvent char_event(&char_native_event, true);
- NativeWebKeyboardEvent char_web_event(&char_event);
- SendNativeKeyEvent(char_web_event);
-
- ui::KeyEvent keyup_native_event(ui::ET_KEY_RELEASED,
- static_cast<ui::KeyboardCode>(key_code),
- flags,
- true);
- ui::KeyEvent keyup_event(&keyup_native_event, false);
- NativeWebKeyboardEvent keyup_web_event(&keyup_event);
- SendNativeKeyEvent(keyup_web_event);
-
- long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
- flags);
- output->assign(1, static_cast<base::char16>(c));
- return 1;
-#elif defined(TOOLKIT_GTK)
- // We ignore |layout|, which means we are only testing the layout of the
- // current locale. TODO(estade): fix this to respect |layout|.
- std::vector<GdkEvent*> events;
- ui::SynthesizeKeyPressEvents(
- NULL, static_cast<ui::KeyboardCode>(key_code),
- modifiers & (MockKeyboard::LEFT_CONTROL | MockKeyboard::RIGHT_CONTROL),
- modifiers & (MockKeyboard::LEFT_SHIFT | MockKeyboard::RIGHT_SHIFT),
- modifiers & (MockKeyboard::LEFT_ALT | MockKeyboard::RIGHT_ALT),
- &events);
-
- guint32 unicode_key = 0;
- for (size_t i = 0; i < events.size(); ++i) {
- // Only send the up/down events for key press itself (skip the up/down
- // events for the modifier keys).
- if ((i + 1) == (events.size() / 2) || i == (events.size() / 2)) {
- unicode_key = gdk_keyval_to_unicode(events[i]->key.keyval);
- NativeWebKeyboardEvent webkit_event(events[i]);
- SendNativeKeyEvent(webkit_event);
-
- // Need to add a char event after the key down.
- if (webkit_event.type == blink::WebInputEvent::RawKeyDown) {
- NativeWebKeyboardEvent char_event = webkit_event;
- char_event.type = blink::WebInputEvent::Char;
- char_event.skip_in_browser = true;
- SendNativeKeyEvent(char_event);
- }
- }
- gdk_event_free(events[i]);
- }
-
- output->assign(1, static_cast<base::char16>(unicode_key));
- return 1;
-#else
- NOTIMPLEMENTED();
- return L'\0';
-#endif
- }
-
- private:
- scoped_ptr<MockKeyboard> mock_keyboard_;
-};
-
-} // namespace
-
-// Test that we get form state change notifications when input fields change.
-TEST_F(RenderViewImplTest, DISABLED_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_F(RenderViewImplTest, OnNavigationHttpPost) {
- FrameMsg_Navigate_Params nav_params;
-
- // An http url will trigger a resource load so cannot be used here.
- nav_params.url = GURL("data:text/html,<div>Page</div>");
- nav_params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- nav_params.transition = PAGE_TRANSITION_TYPED;
- nav_params.page_id = -1;
- nav_params.is_post = true;
-
- // Set up post data.
- const unsigned char* raw_data = reinterpret_cast<const unsigned char*>(
- "post \0\ndata");
- const unsigned int length = 11;
- const std::vector<unsigned char> post_data(raw_data, raw_data + length);
- nav_params.browser_initiated_post_data = post_data;
-
- frame()->OnNavigate(nav_params);
- ProcessPendingMessages();
-
- const IPC::Message* frame_navigate_msg =
- render_thread_->sink().GetUniqueMessageMatching(
- FrameHostMsg_DidCommitProvisionalLoad::ID);
- EXPECT_TRUE(frame_navigate_msg);
-
- FrameHostMsg_DidCommitProvisionalLoad::Param host_nav_params;
- FrameHostMsg_DidCommitProvisionalLoad::Read(frame_navigate_msg,
- &host_nav_params);
- EXPECT_TRUE(host_nav_params.a.is_post);
-
- // Check post data sent to browser matches
- EXPECT_TRUE(host_nav_params.a.page_state.IsValid());
- const blink::WebHistoryItem item = PageStateToHistoryItem(
- host_nav_params.a.page_state);
- blink::WebHTTPBody body = item.httpBody();
- blink::WebHTTPBody::Element element;
- bool successful = body.elementAt(0, element);
- EXPECT_TRUE(successful);
- EXPECT_EQ(blink::WebHTTPBody::Element::TypeData, element.type);
- EXPECT_EQ(length, element.data.size());
- EXPECT_EQ(0, memcmp(raw_data, element.data.data(), length));
-}
-
-TEST_F(RenderViewImplTest, DecideNavigationPolicy) {
- WebUITestWebUIControllerFactory factory;
- WebUIControllerFactory::RegisterFactory(&factory);
-
- DocumentState state;
- state.set_navigation_state(NavigationState::CreateContentInitiated());
-
- // Navigations to normal HTTP URLs can be handled locally.
- blink::WebURLRequest request(GURL("http://foo.com"));
- blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
- GetMainFrame(),
- &state,
- request,
- blink::WebNavigationTypeLinkClicked,
- blink::WebNavigationPolicyCurrentTab,
- false);
- EXPECT_EQ(blink::WebNavigationPolicyCurrentTab, policy);
-
- // Verify that form posts to WebUI URLs will be sent to the browser process.
- blink::WebURLRequest form_request(GURL("chrome://foo"));
- form_request.setHTTPMethod("POST");
- policy = frame()->decidePolicyForNavigation(
- GetMainFrame(),
- &state,
- form_request,
- blink::WebNavigationTypeFormSubmitted,
- blink::WebNavigationPolicyCurrentTab,
- false);
- EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
-
- // Verify that popup links to WebUI URLs also are sent to browser.
- blink::WebURLRequest popup_request(GURL("chrome://foo"));
- policy = frame()->decidePolicyForNavigation(
- GetMainFrame(),
- &state,
- popup_request,
- blink::WebNavigationTypeLinkClicked,
- blink::WebNavigationPolicyNewForegroundTab,
- false);
- EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
-}
-
-TEST_F(RenderViewImplTest, DecideNavigationPolicyHandlesAllTopLevel) {
- DocumentState state;
- state.set_navigation_state(NavigationState::CreateContentInitiated());
-
- RendererPreferences prefs = view()->renderer_preferences();
- prefs.browser_handles_all_top_level_requests = true;
- view()->OnSetRendererPrefs(prefs);
-
- const blink::WebNavigationType kNavTypes[] = {
- blink::WebNavigationTypeLinkClicked,
- blink::WebNavigationTypeFormSubmitted,
- blink::WebNavigationTypeBackForward,
- blink::WebNavigationTypeReload,
- blink::WebNavigationTypeFormResubmitted,
- blink::WebNavigationTypeOther,
- };
-
- blink::WebURLRequest request(GURL("http://foo.com"));
- for (size_t i = 0; i < arraysize(kNavTypes); ++i) {
- blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
- GetMainFrame(),
- &state,
- request,
- kNavTypes[i],
- blink::WebNavigationPolicyCurrentTab,
- false);
- EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
- }
-}
-
-TEST_F(RenderViewImplTest, DecideNavigationPolicyForWebUI) {
- // Enable bindings to simulate a WebUI view.
- view()->OnAllowBindings(BINDINGS_POLICY_WEB_UI);
-
- DocumentState state;
- state.set_navigation_state(NavigationState::CreateContentInitiated());
-
- // Navigations to normal HTTP URLs will be sent to browser process.
- blink::WebURLRequest request(GURL("http://foo.com"));
- blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
- GetMainFrame(),
- &state,
- request,
- blink::WebNavigationTypeLinkClicked,
- blink::WebNavigationPolicyCurrentTab,
- false);
- EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
-
- // Navigations to WebUI URLs will also be sent to browser process.
- blink::WebURLRequest webui_request(GURL("chrome://foo"));
- policy = frame()->decidePolicyForNavigation(
- GetMainFrame(),
- &state,
- webui_request,
- blink::WebNavigationTypeLinkClicked,
- blink::WebNavigationPolicyCurrentTab,
- false);
- EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
-
- // Verify that form posts to data URLs will be sent to the browser process.
- blink::WebURLRequest data_request(GURL("data:text/html,foo"));
- data_request.setHTTPMethod("POST");
- policy = frame()->decidePolicyForNavigation(
- GetMainFrame(),
- &state,
- data_request,
- blink::WebNavigationTypeFormSubmitted,
- blink::WebNavigationPolicyCurrentTab,
- false);
- EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
-
- // Verify that a popup that creates a view first and then navigates to a
- // normal HTTP URL will be sent to the browser process, even though the
- // new view does not have any enabled_bindings_.
- blink::WebURLRequest popup_request(GURL("http://foo.com"));
- blink::WebView* new_web_view = view()->createView(
- GetMainFrame(), popup_request, blink::WebWindowFeatures(), "foo",
- blink::WebNavigationPolicyNewForegroundTab, false);
- RenderViewImpl* new_view = RenderViewImpl::FromWebView(new_web_view);
- policy = static_cast<RenderFrameImpl*>(new_view->GetMainRenderFrame())->
- decidePolicyForNavigation(
- new_web_view->mainFrame(),
- &state,
- popup_request,
- blink::WebNavigationTypeLinkClicked,
- blink::WebNavigationPolicyNewForegroundTab,
- false);
- EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
-
- // Clean up after the new view so we don't leak it.
- new_view->Close();
- new_view->Release();
-}
-
-// Ensure the RenderViewImpl sends an ACK to a SwapOut request, even if it is
-// already swapped out. http://crbug.com/93427.
-TEST_F(RenderViewImplTest, SendSwapOutACK) {
- LoadHTML("<div>Page A</div>");
- int initial_page_id = view()->GetPageId();
-
- // Respond to a swap out request.
- view()->OnSwapOut();
-
- // Ensure the swap out commits synchronously.
- EXPECT_NE(initial_page_id, view()->GetPageId());
-
- // Check for a valid OnSwapOutACK.
- const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_SwapOut_ACK::ID);
- ASSERT_TRUE(msg);
-
- // It is possible to get another swap out request. Ensure that we send
- // an ACK, even if we don't have to do anything else.
- render_thread_->sink().ClearMessages();
- view()->OnSwapOut();
- const IPC::Message* msg2 = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_SwapOut_ACK::ID);
- ASSERT_TRUE(msg2);
-
- // If we navigate back to this RenderView, ensure we don't send a state
- // update for the swapped out URL. (http://crbug.com/72235)
- FrameMsg_Navigate_Params nav_params;
- nav_params.url = GURL("data:text/html,<div>Page B</div>");
- nav_params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- nav_params.transition = PAGE_TRANSITION_TYPED;
- nav_params.current_history_list_length = 1;
- nav_params.current_history_list_offset = 0;
- nav_params.pending_history_list_offset = 1;
- nav_params.page_id = -1;
- frame()->OnNavigate(nav_params);
- ProcessPendingMessages();
- const IPC::Message* msg3 = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_UpdateState::ID);
- EXPECT_FALSE(msg3);
-}
-
-// Ensure the RenderViewImpl reloads the previous page if a reload request
-// arrives while it is showing swappedout://. http://crbug.com/143155.
-TEST_F(RenderViewImplTest, ReloadWhileSwappedOut) {
- // 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.
- ProcessPendingMessages();
- const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_UpdateState::ID);
- ASSERT_TRUE(msg_A);
- int page_id_A;
- PageState state_A;
- ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
- EXPECT_EQ(1, page_id_A);
- render_thread_->sink().ClearMessages();
-
- // Back to page A (page_id 1) and commit.
- FrameMsg_Navigate_Params params_A;
- params_A.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
- params_A.current_history_list_length = 2;
- params_A.current_history_list_offset = 1;
- params_A.pending_history_list_offset = 0;
- params_A.page_id = 1;
- params_A.page_state = state_A;
- frame()->OnNavigate(params_A);
- ProcessPendingMessages();
-
- // Respond to a swap out request.
- view()->OnSwapOut();
-
- // Check for a OnSwapOutACK.
- const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_SwapOut_ACK::ID);
- ASSERT_TRUE(msg);
- render_thread_->sink().ClearMessages();
-
- // It is possible to get a reload request at this point, containing the
- // params.page_state of the initial page (e.g., if the new page fails the
- // provisional load in the renderer process, after we unload the old page).
- // Ensure the old page gets reloaded, not swappedout://.
- FrameMsg_Navigate_Params nav_params;
- nav_params.url = GURL("data:text/html,<div>Page A</div>");
- nav_params.navigation_type = FrameMsg_Navigate_Type::RELOAD;
- nav_params.transition = PAGE_TRANSITION_RELOAD;
- nav_params.current_history_list_length = 2;
- nav_params.current_history_list_offset = 0;
- nav_params.pending_history_list_offset = 0;
- nav_params.page_id = 1;
- nav_params.page_state = state_A;
- frame()->OnNavigate(nav_params);
- ProcessPendingMessages();
-
- // Verify page A committed, not swappedout://.
- const IPC::Message* frame_navigate_msg =
- render_thread_->sink().GetUniqueMessageMatching(
- FrameHostMsg_DidCommitProvisionalLoad::ID);
- EXPECT_TRUE(frame_navigate_msg);
-
- // Read URL out of the parent trait of the params object.
- FrameHostMsg_DidCommitProvisionalLoad::Param commit_params;
- FrameHostMsg_DidCommitProvisionalLoad::Read(frame_navigate_msg,
- &commit_params);
- EXPECT_NE(GURL("swappedout://"), commit_params.a.url);
-}
-
-
-// Test that we get the correct UpdateState message when we go back twice
-// quickly without committing. Regression test for http://crbug.com/58082.
-// Disabled: http://crbug.com/157357 .
-TEST_F(RenderViewImplTest, DISABLED_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.
- ProcessPendingMessages();
- const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_UpdateState::ID);
- ASSERT_TRUE(msg_A);
- int page_id_A;
- PageState 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.
- ProcessPendingMessages();
- const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_UpdateState::ID);
- ASSERT_TRUE(msg_B);
- int page_id_B;
- PageState 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.
- ProcessPendingMessages();
- const IPC::Message* msg_C = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_UpdateState::ID);
- ASSERT_TRUE(msg_C);
- int page_id_C;
- PageState 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.
- FrameMsg_Navigate_Params params_C;
- params_C.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params_C.transition = PAGE_TRANSITION_FORWARD_BACK;
- params_C.current_history_list_length = 4;
- params_C.current_history_list_offset = 3;
- params_C.pending_history_list_offset = 2;
- params_C.page_id = 3;
- params_C.page_state = state_C;
- frame()->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.
- FrameMsg_Navigate_Params params_B;
- params_B.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
- params_B.current_history_list_length = 4;
- params_B.current_history_list_offset = 2;
- params_B.pending_history_list_offset = 1;
- params_B.page_id = 2;
- params_B.page_state = state_B;
- frame()->OnNavigate(params_B);
-
- // Back to page A (page_id 1) and commit.
- FrameMsg_Navigate_Params params;
- params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params.transition = PAGE_TRANSITION_FORWARD_BACK;
- params_B.current_history_list_length = 4;
- params_B.current_history_list_offset = 2;
- params_B.pending_history_list_offset = 0;
- params.page_id = 1;
- params.page_state = state_A;
- frame()->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;
- PageState 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 the history_page_ids_ list can reveal when a stale back/forward
-// navigation arrives from the browser and can be ignored. See
-// http://crbug.com/86758.
-TEST_F(RenderViewImplTest, StaleNavigationsIgnored) {
- // Load page A.
- LoadHTML("<div>Page A</div>");
- EXPECT_EQ(1, view()->history_list_length_);
- EXPECT_EQ(0, view()->history_list_offset_);
- EXPECT_EQ(1, view()->history_page_ids_[0]);
-
- // Load page B, which will trigger an UpdateState message for page A.
- LoadHTML("<div>Page B</div>");
- EXPECT_EQ(2, view()->history_list_length_);
- EXPECT_EQ(1, view()->history_list_offset_);
- EXPECT_EQ(2, view()->history_page_ids_[1]);
-
- // Check for a valid UpdateState message for page A.
- ProcessPendingMessages();
- const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_UpdateState::ID);
- ASSERT_TRUE(msg_A);
- int page_id_A;
- PageState state_A;
- ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
- EXPECT_EQ(1, page_id_A);
- render_thread_->sink().ClearMessages();
-
- // Back to page A (page_id 1) and commit.
- FrameMsg_Navigate_Params params_A;
- params_A.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
- params_A.current_history_list_length = 2;
- params_A.current_history_list_offset = 1;
- params_A.pending_history_list_offset = 0;
- params_A.page_id = 1;
- params_A.page_state = state_A;
- frame()->OnNavigate(params_A);
- ProcessPendingMessages();
-
- // A new navigation commits, clearing the forward history.
- LoadHTML("<div>Page C</div>");
- EXPECT_EQ(2, view()->history_list_length_);
- EXPECT_EQ(1, view()->history_list_offset_);
- EXPECT_EQ(3, view()->history_page_ids_[1]);
-
- // The browser then sends a stale navigation to B, which should be ignored.
- FrameMsg_Navigate_Params params_B;
- params_B.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
- params_B.current_history_list_length = 2;
- params_B.current_history_list_offset = 0;
- params_B.pending_history_list_offset = 1;
- params_B.page_id = 2;
- params_B.page_state = state_A; // Doesn't matter, just has to be present.
- frame()->OnNavigate(params_B);
-
- // State should be unchanged.
- EXPECT_EQ(2, view()->history_list_length_);
- EXPECT_EQ(1, view()->history_list_offset_);
- EXPECT_EQ(3, view()->history_page_ids_[1]);
-}
-
-// Test that we do not ignore navigations after the entry limit is reached,
-// in which case the browser starts dropping entries from the front. In this
-// case, we'll see a page_id mismatch but the RenderView's id will be older,
-// not newer, than params.page_id. Use this as a cue that we should update the
-// state and not treat it like a navigation to a cropped forward history item.
-// See http://crbug.com/89798.
-TEST_F(RenderViewImplTest, DontIgnoreBackAfterNavEntryLimit) {
- // Load page A.
- LoadHTML("<div>Page A</div>");
- EXPECT_EQ(1, view()->history_list_length_);
- EXPECT_EQ(0, view()->history_list_offset_);
- EXPECT_EQ(1, view()->history_page_ids_[0]);
-
- // Load page B, which will trigger an UpdateState message for page A.
- LoadHTML("<div>Page B</div>");
- EXPECT_EQ(2, view()->history_list_length_);
- EXPECT_EQ(1, view()->history_list_offset_);
- EXPECT_EQ(2, view()->history_page_ids_[1]);
-
- // Check for a valid UpdateState message for page A.
- ProcessPendingMessages();
- const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_UpdateState::ID);
- ASSERT_TRUE(msg_A);
- int page_id_A;
- PageState 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>");
- EXPECT_EQ(3, view()->history_list_length_);
- EXPECT_EQ(2, view()->history_list_offset_);
- EXPECT_EQ(3, view()->history_page_ids_[2]);
-
- // Check for a valid UpdateState message for page B.
- ProcessPendingMessages();
- const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_UpdateState::ID);
- ASSERT_TRUE(msg_B);
- int page_id_B;
- PageState state_B;
- ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
- EXPECT_EQ(2, page_id_B);
- render_thread_->sink().ClearMessages();
-
- // Suppose the browser has limited the number of NavigationEntries to 2.
- // It has now dropped the first entry, but the renderer isn't notified.
- // Ensure that going back to page B (page_id 2) at offset 0 is successful.
- FrameMsg_Navigate_Params params_B;
- params_B.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
- params_B.current_history_list_length = 2;
- params_B.current_history_list_offset = 1;
- params_B.pending_history_list_offset = 0;
- params_B.page_id = 2;
- params_B.page_state = state_B;
- frame()->OnNavigate(params_B);
- ProcessPendingMessages();
-
- EXPECT_EQ(2, view()->history_list_length_);
- EXPECT_EQ(0, view()->history_list_offset_);
- EXPECT_EQ(2, view()->history_page_ids_[0]);
-}
-
-// Test that our IME backend sends a notification message when the input focus
-// changes.
-TEST_F(RenderViewImplTest, OnImeTypeChanged) {
- // 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\" value=\"some text\"></input>"
- "<input id=\"test2\" type=\"password\"></input>"
- "<input id=\"test3\" type=\"text\" inputmode=\"verbatim\"></input>"
- "<input id=\"test4\" type=\"text\" inputmode=\"latin\"></input>"
- "<input id=\"test5\" type=\"text\" inputmode=\"latin-name\"></input>"
- "<input id=\"test6\" type=\"text\" inputmode=\"latin-prose\">"
- "</input>"
- "<input id=\"test7\" type=\"text\" inputmode=\"full-width-latin\">"
- "</input>"
- "<input id=\"test8\" type=\"text\" inputmode=\"kana\"></input>"
- "<input id=\"test9\" type=\"text\" inputmode=\"katakana\"></input>"
- "<input id=\"test10\" type=\"text\" inputmode=\"numeric\"></input>"
- "<input id=\"test11\" type=\"text\" inputmode=\"tel\"></input>"
- "<input id=\"test12\" type=\"text\" inputmode=\"email\"></input>"
- "<input id=\"test13\" type=\"text\" inputmode=\"url\"></input>"
- "<input id=\"test14\" type=\"text\" inputmode=\"unknown\"></input>"
- "<input id=\"test15\" type=\"text\" inputmode=\"verbatim\"></input>"
- "</body>"
- "</html>");
- render_thread_->sink().ClearMessages();
-
- struct InputModeTestCase {
- const char* input_id;
- ui::TextInputMode expected_mode;
- };
- static const InputModeTestCase kInputModeTestCases[] = {
- {"test1", ui::TEXT_INPUT_MODE_DEFAULT},
- {"test3", ui::TEXT_INPUT_MODE_VERBATIM},
- {"test4", ui::TEXT_INPUT_MODE_LATIN},
- {"test5", ui::TEXT_INPUT_MODE_LATIN_NAME},
- {"test6", ui::TEXT_INPUT_MODE_LATIN_PROSE},
- {"test7", ui::TEXT_INPUT_MODE_FULL_WIDTH_LATIN},
- {"test8", ui::TEXT_INPUT_MODE_KANA},
- {"test9", ui::TEXT_INPUT_MODE_KATAKANA},
- {"test10", ui::TEXT_INPUT_MODE_NUMERIC},
- {"test11", ui::TEXT_INPUT_MODE_TEL},
- {"test12", ui::TEXT_INPUT_MODE_EMAIL},
- {"test13", ui::TEXT_INPUT_MODE_URL},
- {"test14", ui::TEXT_INPUT_MODE_DEFAULT},
- {"test15", ui::TEXT_INPUT_MODE_VERBATIM},
- };
-
- 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()->UpdateTextInputType();
- const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
- EXPECT_TRUE(msg != NULL);
- EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
- ui::TextInputType type;
- bool can_compose_inline = false;
- ui::TextInputMode input_mode = ui::TEXT_INPUT_MODE_DEFAULT;
- ViewHostMsg_TextInputTypeChanged::Read(msg,
- &type,
- &input_mode,
- &can_compose_inline);
- EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, type);
- EXPECT_EQ(true, can_compose_inline);
-
- // 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()->UpdateTextInputType();
- msg = render_thread_->sink().GetMessageAt(0);
- EXPECT_TRUE(msg != NULL);
- EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
- ViewHostMsg_TextInputTypeChanged::Read(msg,
- &type,
- &input_mode,
- &can_compose_inline);
- EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, type);
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInputModeTestCases); i++) {
- const InputModeTestCase* test_case = &kInputModeTestCases[i];
- std::string javascript =
- base::StringPrintf("document.getElementById('%s').focus();",
- test_case->input_id);
- // Move the input focus to the target <input> element, where we should
- // activate IMEs.
- ExecuteJavaScriptAndReturnIntValue(base::ASCIIToUTF16(javascript), NULL);
- ProcessPendingMessages();
- render_thread_->sink().ClearMessages();
-
- // Update the IME status and verify if our IME backend sends an IPC
- // message to activate IMEs.
- view()->UpdateTextInputType();
- const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
- EXPECT_TRUE(msg != NULL);
- EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
- ViewHostMsg_TextInputTypeChanged::Read(msg,
- &type,
- &input_mode,
- &can_compose_inline);
- EXPECT_EQ(test_case->expected_mode, input_mode);
- }
- }
-}
-
-// 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(RenderViewImplTest, 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(
- base::WideToUTF16Hack(ime_message->ime_string),
- std::vector<blink::WebCompositionUnderline>(),
- ime_message->selection_start,
- ime_message->selection_end);
- break;
-
- case IME_CONFIRMCOMPOSITION:
- view()->OnImeConfirmComposition(
- base::WideToUTF16Hack(ime_message->ime_string),
- gfx::Range::InvalidRange(),
- false);
- break;
-
- case IME_CANCELCOMPOSITION:
- view()->OnImeSetComposition(
- base::string16(),
- std::vector<blink::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()->UpdateTextInputType();
- 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 = base::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(RenderViewImplTest, 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[] = {
- { blink::WebTextDirectionRightToLeft, L"\x000A" L"rtl,rtl" },
- { blink::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 = base::UTF16ToWideHack(
- GetMainFrame()->contentAsText(kMaxOutputCharacters));
- EXPECT_EQ(output, kTextDirection[i].expected_result);
- }
-}
-
-// see http://crbug.com/238750
-#if defined(OS_WIN)
-#define MAYBE_OnHandleKeyboardEvent DISABLED_OnHandleKeyboardEvent
-#else
-#define MAYBE_OnHandleKeyboardEvent OnHandleKeyboardEvent
-#endif
-
-// Test that we can receive correct DOM events when we send input events
-// through the RenderWidget::OnHandleInputEvent() function.
-TEST_F(RenderViewImplTest, MAYBE_OnHandleKeyboardEvent) {
-#if !defined(OS_MACOSX)
- // 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];
- base::string16 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 = base::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.
-// see http://crbug.com/244562
-#if defined(OS_WIN)
-#define MAYBE_InsertCharacters DISABLED_InsertCharacters
-#else
-#define MAYBE_InsertCharacters InsertCharacters
-#endif
-TEST_F(RenderViewImplTest, MAYBE_InsertCharacters) {
-#if !defined(OS_MACOSX)
- 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];
- base::string16 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 = base::UTF16ToWideHack(
- GetMainFrame()->contentAsText(kMaxOutputCharacters));
- EXPECT_EQ(kLayouts[i].expected_result, output);
- }
-#else
- NOTIMPLEMENTED();
-#endif
-}
-
-// Crashy, http://crbug.com/53247.
-TEST_F(RenderViewImplTest, DISABLED_DidFailProvisionalLoadWithErrorForError) {
- GetMainFrame()->enableViewSourceMode(true);
- WebURLError error;
- error.domain = WebString::fromUTF8(net::kErrorDomain);
- error.reason = net::ERR_FILE_NOT_FOUND;
- error.unreachableURL = GURL("http://foo");
- WebFrame* web_frame = GetMainFrame();
-
- // Start a load that will reach provisional state synchronously,
- // but won't complete synchronously.
- FrameMsg_Navigate_Params params;
- params.page_id = -1;
- params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params.url = GURL("data:text/html,test data");
- frame()->OnNavigate(params);
-
- // An error occurred.
- view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
- // Frame should exit view-source mode.
- EXPECT_FALSE(web_frame->isViewSourceModeEnabled());
-}
-
-TEST_F(RenderViewImplTest, DidFailProvisionalLoadWithErrorForCancellation) {
- GetMainFrame()->enableViewSourceMode(true);
- WebURLError error;
- error.domain = WebString::fromUTF8(net::kErrorDomain);
- error.reason = net::ERR_ABORTED;
- error.unreachableURL = GURL("http://foo");
- WebFrame* web_frame = GetMainFrame();
-
- // Start a load that will reach provisional state synchronously,
- // but won't complete synchronously.
- FrameMsg_Navigate_Params params;
- params.page_id = -1;
- params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params.url = GURL("data:text/html,test data");
- frame()->OnNavigate(params);
-
- // A cancellation occurred.
- view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
- // Frame should stay in view-source mode.
- EXPECT_TRUE(web_frame->isViewSourceModeEnabled());
-}
-
-// Regression test for http://crbug.com/41562
-TEST_F(RenderViewImplTest, UpdateTargetURLWithInvalidURL) {
- const GURL invalid_gurl("http://");
- view()->setMouseOverURL(blink::WebURL(invalid_gurl));
- EXPECT_EQ(invalid_gurl, view()->target_url_);
-}
-
-TEST_F(RenderViewImplTest, SetHistoryLengthAndPrune) {
- int expected_page_id = -1;
-
- // No history to merge and no committed pages.
- view()->OnSetHistoryLengthAndPrune(0, -1);
- EXPECT_EQ(0, view()->history_list_length_);
- EXPECT_EQ(-1, view()->history_list_offset_);
-
- // History to merge and no committed pages.
- view()->OnSetHistoryLengthAndPrune(2, -1);
- EXPECT_EQ(2, view()->history_list_length_);
- EXPECT_EQ(1, view()->history_list_offset_);
- EXPECT_EQ(-1, view()->history_page_ids_[0]);
- EXPECT_EQ(-1, view()->history_page_ids_[1]);
- ClearHistory();
-
- // No history to merge and a committed page to be kept.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
- EXPECT_EQ(1, view()->history_list_length_);
- EXPECT_EQ(0, view()->history_list_offset_);
- EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
- ClearHistory();
-
- // No history to merge and a committed page to be pruned.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- view()->OnSetHistoryLengthAndPrune(0, expected_page_id + 1);
- EXPECT_EQ(0, view()->history_list_length_);
- EXPECT_EQ(-1, view()->history_list_offset_);
- ClearHistory();
-
- // No history to merge and a committed page that the browser was unaware of.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- view()->OnSetHistoryLengthAndPrune(0, -1);
- EXPECT_EQ(1, view()->history_list_length_);
- EXPECT_EQ(0, view()->history_list_offset_);
- EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
- ClearHistory();
-
- // History to merge and a committed page to be kept.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
- EXPECT_EQ(3, view()->history_list_length_);
- EXPECT_EQ(2, view()->history_list_offset_);
- EXPECT_EQ(-1, view()->history_page_ids_[0]);
- EXPECT_EQ(-1, view()->history_page_ids_[1]);
- EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
- ClearHistory();
-
- // History to merge and a committed page to be pruned.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- view()->OnSetHistoryLengthAndPrune(2, expected_page_id + 1);
- EXPECT_EQ(2, view()->history_list_length_);
- EXPECT_EQ(1, view()->history_list_offset_);
- EXPECT_EQ(-1, view()->history_page_ids_[0]);
- EXPECT_EQ(-1, view()->history_page_ids_[1]);
- ClearHistory();
-
- // History to merge and a committed page that the browser was unaware of.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- view()->OnSetHistoryLengthAndPrune(2, -1);
- EXPECT_EQ(3, view()->history_list_length_);
- EXPECT_EQ(2, view()->history_list_offset_);
- EXPECT_EQ(-1, view()->history_page_ids_[0]);
- EXPECT_EQ(-1, view()->history_page_ids_[1]);
- EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
- ClearHistory();
-
- int expected_page_id_2 = -1;
-
- // No history to merge and two committed pages, both to be kept.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id_2 = view()->page_id_;
- EXPECT_GT(expected_page_id_2, expected_page_id);
- view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
- EXPECT_EQ(2, view()->history_list_length_);
- EXPECT_EQ(1, view()->history_list_offset_);
- EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
- EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
- ClearHistory();
-
- // No history to merge and two committed pages, and only the second is kept.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id_2 = view()->page_id_;
- EXPECT_GT(expected_page_id_2, expected_page_id);
- view()->OnSetHistoryLengthAndPrune(0, expected_page_id_2);
- EXPECT_EQ(1, view()->history_list_length_);
- EXPECT_EQ(0, view()->history_list_offset_);
- EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[0]);
- ClearHistory();
-
- // No history to merge and two committed pages, both of which the browser was
- // unaware of.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id_2 = view()->page_id_;
- EXPECT_GT(expected_page_id_2, expected_page_id);
- view()->OnSetHistoryLengthAndPrune(0, -1);
- EXPECT_EQ(2, view()->history_list_length_);
- EXPECT_EQ(1, view()->history_list_offset_);
- EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
- EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
- ClearHistory();
-
- // History to merge and two committed pages, both to be kept.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id_2 = view()->page_id_;
- EXPECT_GT(expected_page_id_2, expected_page_id);
- view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
- EXPECT_EQ(4, view()->history_list_length_);
- EXPECT_EQ(3, view()->history_list_offset_);
- EXPECT_EQ(-1, view()->history_page_ids_[0]);
- EXPECT_EQ(-1, view()->history_page_ids_[1]);
- EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
- EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
- ClearHistory();
-
- // History to merge and two committed pages, and only the second is kept.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id_2 = view()->page_id_;
- EXPECT_GT(expected_page_id_2, expected_page_id);
- view()->OnSetHistoryLengthAndPrune(2, expected_page_id_2);
- EXPECT_EQ(3, view()->history_list_length_);
- EXPECT_EQ(2, view()->history_list_offset_);
- EXPECT_EQ(-1, view()->history_page_ids_[0]);
- EXPECT_EQ(-1, view()->history_page_ids_[1]);
- EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[2]);
- ClearHistory();
-
- // History to merge and two committed pages, both of which the browser was
- // unaware of.
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id = view()->page_id_;
- frame()->didCommitProvisionalLoad(GetMainFrame(), true);
- expected_page_id_2 = view()->page_id_;
- EXPECT_GT(expected_page_id_2, expected_page_id);
- view()->OnSetHistoryLengthAndPrune(2, -1);
- EXPECT_EQ(4, view()->history_list_length_);
- EXPECT_EQ(3, view()->history_list_offset_);
- EXPECT_EQ(-1, view()->history_page_ids_[0]);
- EXPECT_EQ(-1, view()->history_page_ids_[1]);
- EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
- EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
-}
-
-TEST_F(RenderViewImplTest, ContextMenu) {
- LoadHTML("<div>Page A</div>");
-
- // Create a right click in the center of the iframe. (I'm hoping this will
- // make this a bit more robust in case of some other formatting or other bug.)
- WebMouseEvent mouse_event;
- mouse_event.type = WebInputEvent::MouseDown;
- mouse_event.button = WebMouseEvent::ButtonRight;
- mouse_event.x = 250;
- mouse_event.y = 250;
- mouse_event.globalX = 250;
- mouse_event.globalY = 250;
-
- SendWebMouseEvent(mouse_event);
-
- // Now simulate the corresponding up event which should display the menu
- mouse_event.type = WebInputEvent::MouseUp;
- SendWebMouseEvent(mouse_event);
-
- EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
- FrameHostMsg_ContextMenu::ID));
-}
-
-TEST_F(RenderViewImplTest, TestBackForward) {
- LoadHTML("<div id=pagename>Page A</div>");
- blink::WebHistoryItem page_a_item = GetMainFrame()->currentHistoryItem();
- int was_page_a = -1;
- base::string16 check_page_a =
- base::ASCIIToUTF16(
- "Number(document.getElementById('pagename').innerHTML == 'Page A')");
- EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
- EXPECT_EQ(1, was_page_a);
-
- LoadHTML("<div id=pagename>Page B</div>");
- int was_page_b = -1;
- base::string16 check_page_b =
- base::ASCIIToUTF16(
- "Number(document.getElementById('pagename').innerHTML == 'Page B')");
- EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
- EXPECT_EQ(1, was_page_b);
-
- LoadHTML("<div id=pagename>Page C</div>");
- int was_page_c = -1;
- base::string16 check_page_c =
- base::ASCIIToUTF16(
- "Number(document.getElementById('pagename').innerHTML == 'Page C')");
- EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
- EXPECT_EQ(1, was_page_b);
-
- blink::WebHistoryItem forward_item = GetMainFrame()->currentHistoryItem();
- GoBack(GetMainFrame()->previousHistoryItem());
- EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
- EXPECT_EQ(1, was_page_b);
-
- GoForward(forward_item);
- EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
- EXPECT_EQ(1, was_page_c);
-
- GoBack(GetMainFrame()->previousHistoryItem());
- EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
- EXPECT_EQ(1, was_page_b);
-
- forward_item = GetMainFrame()->currentHistoryItem();
- GoBack(page_a_item);
- EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
- EXPECT_EQ(1, was_page_a);
-
- GoForward(forward_item);
- EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
- EXPECT_EQ(1, was_page_b);
-}
-
-#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
-TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) {
-
-#if defined(OS_WIN)
- // http://crbug.com/304193
- if (base::win::GetVersion() < base::win::VERSION_VISTA)
- return;
-#endif
-
- LoadHTML("<textarea id=\"test\"></textarea>");
- ExecuteJavaScript("document.getElementById('test').focus();");
-
- const base::string16 empty_string;
- const std::vector<blink::WebCompositionUnderline> empty_underline;
- std::vector<gfx::Rect> bounds;
- view()->OnSetFocus(true);
- view()->OnSetInputMethodActive(true);
-
- // ASCII composition
- const base::string16 ascii_composition = base::UTF8ToUTF16("aiueo");
- view()->OnImeSetComposition(ascii_composition, empty_underline, 0, 0);
- view()->GetCompositionCharacterBounds(&bounds);
- ASSERT_EQ(ascii_composition.size(), bounds.size());
- for (size_t i = 0; i < bounds.size(); ++i)
- EXPECT_LT(0, bounds[i].width());
- view()->OnImeConfirmComposition(
- empty_string, gfx::Range::InvalidRange(), false);
-
- // Non surrogate pair unicode character.
- const base::string16 unicode_composition = base::UTF8ToUTF16(
- "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
- view()->OnImeSetComposition(unicode_composition, empty_underline, 0, 0);
- view()->GetCompositionCharacterBounds(&bounds);
- ASSERT_EQ(unicode_composition.size(), bounds.size());
- for (size_t i = 0; i < bounds.size(); ++i)
- EXPECT_LT(0, bounds[i].width());
- view()->OnImeConfirmComposition(
- empty_string, gfx::Range::InvalidRange(), false);
-
- // Surrogate pair character.
- const base::string16 surrogate_pair_char =
- base::UTF8ToUTF16("\xF0\xA0\xAE\x9F");
- view()->OnImeSetComposition(surrogate_pair_char,
- empty_underline,
- 0,
- 0);
- view()->GetCompositionCharacterBounds(&bounds);
- ASSERT_EQ(surrogate_pair_char.size(), bounds.size());
- EXPECT_LT(0, bounds[0].width());
- EXPECT_EQ(0, bounds[1].width());
- view()->OnImeConfirmComposition(
- empty_string, gfx::Range::InvalidRange(), false);
-
- // Mixed string.
- const base::string16 surrogate_pair_mixed_composition =
- surrogate_pair_char + base::UTF8ToUTF16("\xE3\x81\x82") +
- surrogate_pair_char + base::UTF8ToUTF16("b") + surrogate_pair_char;
- const size_t utf16_length = 8UL;
- const bool is_surrogate_pair_empty_rect[8] = {
- false, true, false, false, true, false, false, true };
- view()->OnImeSetComposition(surrogate_pair_mixed_composition,
- empty_underline,
- 0,
- 0);
- view()->GetCompositionCharacterBounds(&bounds);
- ASSERT_EQ(utf16_length, bounds.size());
- for (size_t i = 0; i < utf16_length; ++i) {
- if (is_surrogate_pair_empty_rect[i]) {
- EXPECT_EQ(0, bounds[i].width());
- } else {
- EXPECT_LT(0, bounds[i].width());
- }
- }
- view()->OnImeConfirmComposition(
- empty_string, gfx::Range::InvalidRange(), false);
-}
-#endif
-
-TEST_F(RenderViewImplTest, ZoomLimit) {
- const double kMinZoomLevel = ZoomFactorToZoomLevel(kMinimumZoomFactor);
- const double kMaxZoomLevel = ZoomFactorToZoomLevel(kMaximumZoomFactor);
-
- FrameMsg_Navigate_Params params;
- params.page_id = -1;
- params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
-
- // Verifies navigation to a URL with preset zoom level indeed sets the level.
- // Regression test for http://crbug.com/139559, where the level was not
- // properly set when it is out of the default zoom limits of WebView.
- params.url = GURL("data:text/html,min_zoomlimit_test");
- view()->OnSetZoomLevelForLoadingURL(params.url, kMinZoomLevel);
- frame()->OnNavigate(params);
- ProcessPendingMessages();
- EXPECT_DOUBLE_EQ(kMinZoomLevel, view()->GetWebView()->zoomLevel());
-
- // It should work even when the zoom limit is temporarily changed in the page.
- view()->GetWebView()->zoomLimitsChanged(ZoomFactorToZoomLevel(1.0),
- ZoomFactorToZoomLevel(1.0));
- params.url = GURL("data:text/html,max_zoomlimit_test");
- view()->OnSetZoomLevelForLoadingURL(params.url, kMaxZoomLevel);
- frame()->OnNavigate(params);
- ProcessPendingMessages();
- EXPECT_DOUBLE_EQ(kMaxZoomLevel, view()->GetWebView()->zoomLevel());
-}
-
-TEST_F(RenderViewImplTest, SetEditableSelectionAndComposition) {
- // Load an HTML page consisting of an input field.
- LoadHTML("<html>"
- "<head>"
- "</head>"
- "<body>"
- "<input id=\"test1\" value=\"some test text hello\"></input>"
- "</body>"
- "</html>");
- ExecuteJavaScript("document.getElementById('test1').focus();");
- view()->OnSetEditableSelectionOffsets(4, 8);
- const std::vector<blink::WebCompositionUnderline> empty_underline;
- view()->OnSetCompositionFromExistingText(7, 10, empty_underline);
- blink::WebTextInputInfo info = view()->webview()->textInputInfo();
- EXPECT_EQ(4, info.selectionStart);
- EXPECT_EQ(8, info.selectionEnd);
- EXPECT_EQ(7, info.compositionStart);
- EXPECT_EQ(10, info.compositionEnd);
- view()->OnUnselect();
- info = view()->webview()->textInputInfo();
- EXPECT_EQ(0, info.selectionStart);
- EXPECT_EQ(0, info.selectionEnd);
-}
-
-
-TEST_F(RenderViewImplTest, OnExtendSelectionAndDelete) {
- // Load an HTML page consisting of an input field.
- LoadHTML("<html>"
- "<head>"
- "</head>"
- "<body>"
- "<input id=\"test1\" value=\"abcdefghijklmnopqrstuvwxyz\"></input>"
- "</body>"
- "</html>");
- ExecuteJavaScript("document.getElementById('test1').focus();");
- view()->OnSetEditableSelectionOffsets(10, 10);
- view()->OnExtendSelectionAndDelete(3, 4);
- blink::WebTextInputInfo info = view()->webview()->textInputInfo();
- EXPECT_EQ("abcdefgopqrstuvwxyz", info.value);
- EXPECT_EQ(7, info.selectionStart);
- EXPECT_EQ(7, info.selectionEnd);
- view()->OnSetEditableSelectionOffsets(4, 8);
- view()->OnExtendSelectionAndDelete(2, 5);
- info = view()->webview()->textInputInfo();
- EXPECT_EQ("abuvwxyz", info.value);
- EXPECT_EQ(2, info.selectionStart);
- EXPECT_EQ(2, info.selectionEnd);
-}
-
-// Test that the navigating specific frames works correctly.
-TEST_F(RenderViewImplTest, NavigateFrame) {
- // Load page A.
- LoadHTML("hello <iframe srcdoc='fail' name='frame'></iframe>");
-
- // Navigate the frame only.
- FrameMsg_Navigate_Params nav_params;
- nav_params.url = GURL("data:text/html,world");
- nav_params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- nav_params.transition = PAGE_TRANSITION_TYPED;
- nav_params.current_history_list_length = 1;
- nav_params.current_history_list_offset = 0;
- nav_params.pending_history_list_offset = 1;
- nav_params.page_id = -1;
- nav_params.frame_to_navigate = "frame";
- frame()->OnNavigate(nav_params);
- ProcessPendingMessages();
-
- // Copy the document content to std::wstring and compare with the
- // expected result.
- const int kMaxOutputCharacters = 256;
- std::wstring output = base::UTF16ToWideHack(
- GetMainFrame()->contentAsText(kMaxOutputCharacters));
- EXPECT_EQ(output, L"hello \n\nworld");
-}
-
-// This test ensures that a RenderFrame object is created for the top level
-// frame in the RenderView.
-TEST_F(RenderViewImplTest, BasicRenderFrame) {
- EXPECT_TRUE(view()->main_render_frame_.get());
-}
-
-TEST_F(RenderViewImplTest, GetSSLStatusOfFrame) {
- LoadHTML("<!DOCTYPE html><html><body></body></html>");
-
- WebFrame* frame = GetMainFrame();
- SSLStatus ssl_status = view()->GetSSLStatusOfFrame(frame);
- EXPECT_FALSE(net::IsCertStatusError(ssl_status.cert_status));
-
- const_cast<blink::WebURLResponse&>(frame->dataSource()->response()).
- setSecurityInfo(
- SerializeSecurityInfo(0, net::CERT_STATUS_ALL_ERRORS, 0, 0,
- SignedCertificateTimestampIDStatusList()));
- ssl_status = view()->GetSSLStatusOfFrame(frame);
- EXPECT_TRUE(net::IsCertStatusError(ssl_status.cert_status));
-}
-
-TEST_F(RenderViewImplTest, MessageOrderInDidChangeSelection) {
- view()->OnSetInputMethodActive(true);
- view()->set_send_content_state_immediately(true);
- LoadHTML("<textarea id=\"test\"></textarea>");
-
- view()->handling_input_event_ = true;
- ExecuteJavaScript("document.getElementById('test').focus();");
-
- bool is_input_type_called = false;
- bool is_selection_called = false;
- size_t last_input_type = 0;
- size_t last_selection = 0;
-
- for (size_t i = 0; i < render_thread_->sink().message_count(); ++i) {
- const uint32 type = render_thread_->sink().GetMessageAt(i)->type();
- if (type == ViewHostMsg_TextInputTypeChanged::ID) {
- is_input_type_called = true;
- last_input_type = i;
- } else if (type == ViewHostMsg_SelectionChanged::ID) {
- is_selection_called = true;
- last_selection = i;
- }
- }
-
- EXPECT_TRUE(is_input_type_called);
- EXPECT_TRUE(is_selection_called);
-
- // InputTypeChange shold be called earlier than SelectionChanged.
- EXPECT_LT(last_input_type, last_selection);
-}
-
-class SuppressErrorPageTest : public RenderViewTest {
- public:
- virtual void SetUp() OVERRIDE {
- SetRendererClientForTesting(&client_);
- RenderViewTest::SetUp();
- }
-
- RenderViewImpl* view() {
- return static_cast<RenderViewImpl*>(view_);
- }
-
- RenderFrameImpl* frame() {
- return static_cast<RenderFrameImpl*>(view()->GetMainRenderFrame());
- }
-
- private:
- class TestContentRendererClient : public ContentRendererClient {
- public:
- virtual bool ShouldSuppressErrorPage(RenderFrame* render_frame,
- const GURL& url) OVERRIDE {
- return url == GURL("http://example.com/suppress");
- }
-
- virtual void GetNavigationErrorStrings(
- content::RenderView* render_view,
- blink::WebFrame* frame,
- const blink::WebURLRequest& failed_request,
- const blink::WebURLError& error,
- std::string* error_html,
- base::string16* error_description) OVERRIDE {
- if (error_html)
- *error_html = "A suffusion of yellow.";
- }
- };
-
- TestContentRendererClient client_;
-};
-
-#if defined(OS_ANDROID)
-// Crashing on Android: http://crbug.com/311341
-#define MAYBE_Suppresses DISABLED_Suppresses
-#else
-#define MAYBE_Suppresses Suppresses
-#endif
-
-TEST_F(SuppressErrorPageTest, MAYBE_Suppresses) {
- WebURLError error;
- error.domain = WebString::fromUTF8(net::kErrorDomain);
- error.reason = net::ERR_FILE_NOT_FOUND;
- error.unreachableURL = GURL("http://example.com/suppress");
- WebFrame* web_frame = GetMainFrame();
-
- // Start a load that will reach provisional state synchronously,
- // but won't complete synchronously.
- FrameMsg_Navigate_Params params;
- params.page_id = -1;
- params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params.url = GURL("data:text/html,test data");
- frame()->OnNavigate(params);
-
- // An error occurred.
- view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
- const int kMaxOutputCharacters = 22;
- EXPECT_EQ("", UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
-}
-
-#if defined(OS_ANDROID)
-// Crashing on Android: http://crbug.com/311341
-#define MAYBE_DoesNotSuppress DISABLED_DoesNotSuppress
-#else
-#define MAYBE_DoesNotSuppress DoesNotSuppress
-#endif
-
-TEST_F(SuppressErrorPageTest, MAYBE_DoesNotSuppress) {
- WebURLError error;
- error.domain = WebString::fromUTF8(net::kErrorDomain);
- error.reason = net::ERR_FILE_NOT_FOUND;
- error.unreachableURL = GURL("http://example.com/dont-suppress");
- WebFrame* web_frame = GetMainFrame();
-
- // Start a load that will reach provisional state synchronously,
- // but won't complete synchronously.
- FrameMsg_Navigate_Params params;
- params.page_id = -1;
- params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
- params.url = GURL("data:text/html,test data");
- frame()->OnNavigate(params);
-
- // An error occurred.
- view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
- ProcessPendingMessages();
- const int kMaxOutputCharacters = 22;
- EXPECT_EQ("A suffusion of yellow.",
- UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
-}
-
-// Tests if IME API's candidatewindow* events sent from browser are handled
-// in renderer.
-TEST_F(RenderViewImplTest, SendCandidateWindowEvents) {
- // Sends an HTML with an <input> element and scripts to the renderer.
- // The script handles all 3 of candidatewindow* events for an
- // InputMethodContext object and once it received 'show', 'update', 'hide'
- // should appear in the result div.
- LoadHTML("<input id='test'>"
- "<div id='result'>Result: </div>"
- "<script>"
- "window.onload = function() {"
- " var result = document.getElementById('result');"
- " var test = document.getElementById('test');"
- " test.focus();"
- " var context = test.inputMethodContext;"
- " if (context) {"
- " context.oncandidatewindowshow = function() {"
- " result.innerText += 'show'; };"
- " context.oncandidatewindowupdate = function(){"
- " result.innerText += 'update'; };"
- " context.oncandidatewindowhide = function(){"
- " result.innerText += 'hide'; };"
- " }"
- "};"
- "</script>");
-
- // Fire candidatewindow events.
- view()->OnCandidateWindowShown();
- view()->OnCandidateWindowUpdated();
- view()->OnCandidateWindowHidden();
-
- // Retrieve the content and check if it is expected.
- const int kMaxOutputCharacters = 50;
- std::string output = base::UTF16ToUTF8(
- GetMainFrame()->contentAsText(kMaxOutputCharacters));
- EXPECT_EQ(output, "\nResult:showupdatehide");
-}
-
-// Ensure the render view sends favicon url update events correctly.
-TEST_F(RenderViewImplTest, SendFaviconURLUpdateEvent) {
- // An event should be sent when a favicon url exists.
- LoadHTML("<html>"
- "<head>"
- "<link rel='icon' href='http://www.google.com/favicon.ico'>"
- "</head>"
- "</html>");
- EXPECT_TRUE(render_thread_->sink().GetFirstMessageMatching(
- ViewHostMsg_UpdateFaviconURL::ID));
- render_thread_->sink().ClearMessages();
-
- // An event should not be sent if no favicon url exists. This is an assumption
- // made by some of Chrome's favicon handling.
- LoadHTML("<html>"
- "<head>"
- "</head>"
- "</html>");
- EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
- ViewHostMsg_UpdateFaviconURL::ID));
-}
-
-TEST_F(RenderViewImplTest, FocusElementCallsFocusedNodeChanged) {
- LoadHTML("<input id='test1' value='hello1'></input>"
- "<input id='test2' value='hello2'></input>");
-
- ExecuteJavaScript("document.getElementById('test1').focus();");
- const IPC::Message* msg1 = render_thread_->sink().GetFirstMessageMatching(
- ViewHostMsg_FocusedNodeChanged::ID);
- EXPECT_TRUE(msg1);
-
- ViewHostMsg_FocusedNodeChanged::Param params;
- ViewHostMsg_FocusedNodeChanged::Read(msg1, &params);
- EXPECT_TRUE(params.a);
- render_thread_->sink().ClearMessages();
-
- ExecuteJavaScript("document.getElementById('test2').focus();");
- const IPC::Message* msg2 = render_thread_->sink().GetFirstMessageMatching(
- ViewHostMsg_FocusedNodeChanged::ID);
- EXPECT_TRUE(msg2);
- ViewHostMsg_FocusedNodeChanged::Read(msg2, &params);
- EXPECT_TRUE(params.a);
- render_thread_->sink().ClearMessages();
-
- view()->webview()->clearFocusedNode();
- const IPC::Message* msg3 = render_thread_->sink().GetFirstMessageMatching(
- ViewHostMsg_FocusedNodeChanged::ID);
- EXPECT_TRUE(msg3);
- ViewHostMsg_FocusedNodeChanged::Read(msg3, &params);
- EXPECT_FALSE(params.a);
- render_thread_->sink().ClearMessages();
-}
-
-} // namespace content
+// Copyright (c) 2012 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/bind.h"
+#include "base/callback.h"
+#include "base/memory/shared_memory.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/windows_version.h"
+#include "content/common/frame_messages.h"
+#include "content/common/ssl_status_serialization.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/web_ui_controller_factory.h"
+#include "content/public/common/bindings_policy.h"
+#include "content/public/common/page_zoom.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/common/url_utils.h"
+#include "content/public/renderer/document_state.h"
+#include "content/public/renderer/history_item_serialization.h"
+#include "content/public/renderer/navigation_state.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/render_view_test.h"
+#include "content/public/test/test_utils.h"
+#include "content/renderer/render_view_impl.h"
+#include "content/shell/browser/shell.h"
+#include "content/shell/browser/shell_browser_context.h"
+#include "content/test/mock_keyboard.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_status_flags.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebData.h"
+#include "third_party/WebKit/public/platform/WebHTTPBody.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebURLResponse.h"
+#include "third_party/WebKit/public/web/WebDataSource.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebHistoryItem.h"
+#include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
+#include "third_party/WebKit/public/web/WebScriptSource.h"
+#include "third_party/WebKit/public/web/WebView.h"
+#include "third_party/WebKit/public/web/WebWindowFeatures.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/range/range.h"
+
+#if defined(OS_LINUX) && !defined(USE_AURA)
+#include "ui/base/gtk/event_synthesis_gtk.h"
+#endif
+
+#if defined(USE_AURA)
+#include "ui/events/event.h"
+#endif
+
+#if defined(USE_AURA) && defined(USE_X11)
+#include <X11/Xlib.h>
+#include "ui/events/event_constants.h"
+#include "ui/events/keycodes/keyboard_code_conversion.h"
+#include "ui/events/test/events_test_utils_x11.h"
+#endif
+
+#if defined(USE_OZONE)
+#include "ui/events/keycodes/keyboard_code_conversion.h"
+#endif
+
+using blink::WebFrame;
+using blink::WebInputEvent;
+using blink::WebMouseEvent;
+using blink::WebRuntimeFeatures;
+using blink::WebString;
+using blink::WebTextDirection;
+using blink::WebURLError;
+
+namespace content {
+
+namespace {
+
+#if (defined(USE_AURA) && defined(USE_X11)) || defined(USE_OZONE)
+// Converts MockKeyboard::Modifiers to ui::EventFlags.
+int ConvertMockKeyboardModifier(MockKeyboard::Modifiers modifiers) {
+ static struct ModifierMap {
+ MockKeyboard::Modifiers src;
+ int dst;
+ } kModifierMap[] = {
+ { MockKeyboard::LEFT_SHIFT, ui::EF_SHIFT_DOWN },
+ { MockKeyboard::RIGHT_SHIFT, ui::EF_SHIFT_DOWN },
+ { MockKeyboard::LEFT_CONTROL, ui::EF_CONTROL_DOWN },
+ { MockKeyboard::RIGHT_CONTROL, ui::EF_CONTROL_DOWN },
+ { MockKeyboard::LEFT_ALT, ui::EF_ALT_DOWN },
+ { MockKeyboard::RIGHT_ALT, ui::EF_ALT_DOWN },
+ };
+ int flags = 0;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kModifierMap); ++i) {
+ if (kModifierMap[i].src & modifiers) {
+ flags |= kModifierMap[i].dst;
+ }
+ }
+ return flags;
+}
+#endif
+
+class WebUITestWebUIControllerFactory : public WebUIControllerFactory {
+ public:
+ virtual WebUIController* CreateWebUIControllerForURL(
+ WebUI* web_ui, const GURL& url) const OVERRIDE {
+ return NULL;
+ }
+ virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
+ const GURL& url) const OVERRIDE {
+ return WebUI::kNoWebUI;
+ }
+ virtual bool UseWebUIForURL(BrowserContext* browser_context,
+ const GURL& url) const OVERRIDE {
+ return HasWebUIScheme(url);
+ }
+ virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
+ const GURL& url) const OVERRIDE {
+ return HasWebUIScheme(url);
+ }
+};
+
+class RenderViewImplTest : public RenderViewTest {
+ public:
+ RenderViewImplTest() {
+ // Attach a pseudo keyboard device to this object.
+ mock_keyboard_.reset(new MockKeyboard());
+ }
+
+ virtual ~RenderViewImplTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ RenderViewTest::SetUp();
+ // Enable Blink's experimental and test only features so that test code
+ // does not have to bother enabling each feature.
+ WebRuntimeFeatures::enableExperimentalFeatures(true);
+ WebRuntimeFeatures::enableTestOnlyFeatures(true);
+ }
+
+ RenderViewImpl* view() {
+ return static_cast<RenderViewImpl*>(view_);
+ }
+
+ RenderFrameImpl* frame() {
+ return static_cast<RenderFrameImpl*>(view()->GetMainRenderFrame());
+ }
+
+ // Sends IPC messages that emulates a key-press event.
+ int SendKeyEvent(MockKeyboard::Layout layout,
+ int key_code,
+ MockKeyboard::Modifiers modifiers,
+ base::string16* 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.
+ MSG msg1 = { NULL, WM_KEYDOWN, key_code, 0 };
+#if defined(USE_AURA)
+ ui::KeyEvent evt1(msg1, false);
+ NativeWebKeyboardEvent keydown_event(&evt1);
+#else
+ NativeWebKeyboardEvent keydown_event(msg1);
+#endif
+ SendNativeKeyEvent(keydown_event);
+
+ MSG msg2 = { NULL, WM_CHAR, (*output)[0], 0 };
+#if defined(USE_AURA)
+ ui::KeyEvent evt2(msg2, true);
+ NativeWebKeyboardEvent char_event(&evt2);
+#else
+ NativeWebKeyboardEvent char_event(msg2);
+#endif
+ SendNativeKeyEvent(char_event);
+
+ MSG msg3 = { NULL, WM_KEYUP, key_code, 0 };
+#if defined(USE_AURA)
+ ui::KeyEvent evt3(msg3, false);
+ NativeWebKeyboardEvent keyup_event(&evt3);
+#else
+ NativeWebKeyboardEvent keyup_event(msg3);
+#endif
+ SendNativeKeyEvent(keyup_event);
+
+ return length;
+#elif defined(USE_AURA) && defined(USE_X11)
+ // We ignore |layout|, which means we are only testing the layout of the
+ // current locale. TODO(mazda): fix this to respect |layout|.
+ CHECK(output);
+ const int flags = ConvertMockKeyboardModifier(modifiers);
+
+ ui::ScopedXI2Event xevent;
+ xevent.InitKeyEvent(ui::ET_KEY_PRESSED,
+ static_cast<ui::KeyboardCode>(key_code),
+ flags);
+ ui::KeyEvent event1(xevent, false);
+ NativeWebKeyboardEvent keydown_event(&event1);
+ SendNativeKeyEvent(keydown_event);
+
+ xevent.InitKeyEvent(ui::ET_KEY_PRESSED,
+ static_cast<ui::KeyboardCode>(key_code),
+ flags);
+ ui::KeyEvent event2(xevent, true);
+ NativeWebKeyboardEvent char_event(&event2);
+ SendNativeKeyEvent(char_event);
+
+ xevent.InitKeyEvent(ui::ET_KEY_RELEASED,
+ static_cast<ui::KeyboardCode>(key_code),
+ flags);
+ ui::KeyEvent event3(xevent, false);
+ NativeWebKeyboardEvent keyup_event(&event3);
+ SendNativeKeyEvent(keyup_event);
+
+ long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
+ flags);
+ output->assign(1, static_cast<base::char16>(c));
+ return 1;
+#elif defined(USE_OZONE)
+ const int flags = ConvertMockKeyboardModifier(modifiers);
+
+ // Ozone's native events are ui::Events. So first create the "native" event,
+ // then create the actual ui::KeyEvent with the native event.
+ ui::KeyEvent keydown_native_event(ui::ET_KEY_PRESSED,
+ static_cast<ui::KeyboardCode>(key_code),
+ flags,
+ true);
+ ui::KeyEvent keydown_event(&keydown_native_event, false);
+ NativeWebKeyboardEvent keydown_web_event(&keydown_event);
+ SendNativeKeyEvent(keydown_web_event);
+
+ ui::KeyEvent char_native_event(ui::ET_KEY_PRESSED,
+ static_cast<ui::KeyboardCode>(key_code),
+ flags,
+ true);
+ ui::KeyEvent char_event(&char_native_event, true);
+ NativeWebKeyboardEvent char_web_event(&char_event);
+ SendNativeKeyEvent(char_web_event);
+
+ ui::KeyEvent keyup_native_event(ui::ET_KEY_RELEASED,
+ static_cast<ui::KeyboardCode>(key_code),
+ flags,
+ true);
+ ui::KeyEvent keyup_event(&keyup_native_event, false);
+ NativeWebKeyboardEvent keyup_web_event(&keyup_event);
+ SendNativeKeyEvent(keyup_web_event);
+
+ long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
+ flags);
+ output->assign(1, static_cast<base::char16>(c));
+ return 1;
+#elif defined(TOOLKIT_GTK)
+ // We ignore |layout|, which means we are only testing the layout of the
+ // current locale. TODO(estade): fix this to respect |layout|.
+ std::vector<GdkEvent*> events;
+ ui::SynthesizeKeyPressEvents(
+ NULL, static_cast<ui::KeyboardCode>(key_code),
+ modifiers & (MockKeyboard::LEFT_CONTROL | MockKeyboard::RIGHT_CONTROL),
+ modifiers & (MockKeyboard::LEFT_SHIFT | MockKeyboard::RIGHT_SHIFT),
+ modifiers & (MockKeyboard::LEFT_ALT | MockKeyboard::RIGHT_ALT),
+ &events);
+
+ guint32 unicode_key = 0;
+ for (size_t i = 0; i < events.size(); ++i) {
+ // Only send the up/down events for key press itself (skip the up/down
+ // events for the modifier keys).
+ if ((i + 1) == (events.size() / 2) || i == (events.size() / 2)) {
+ unicode_key = gdk_keyval_to_unicode(events[i]->key.keyval);
+ NativeWebKeyboardEvent webkit_event(events[i]);
+ SendNativeKeyEvent(webkit_event);
+
+ // Need to add a char event after the key down.
+ if (webkit_event.type == blink::WebInputEvent::RawKeyDown) {
+ NativeWebKeyboardEvent char_event = webkit_event;
+ char_event.type = blink::WebInputEvent::Char;
+ char_event.skip_in_browser = true;
+ SendNativeKeyEvent(char_event);
+ }
+ }
+ gdk_event_free(events[i]);
+ }
+
+ output->assign(1, static_cast<base::char16>(unicode_key));
+ return 1;
+#else
+ NOTIMPLEMENTED();
+ return L'\0';
+#endif
+ }
+
+ private:
+ scoped_ptr<MockKeyboard> mock_keyboard_;
+};
+
+} // namespace
+
+// Test that we get form state change notifications when input fields change.
+TEST_F(RenderViewImplTest, DISABLED_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_F(RenderViewImplTest, OnNavigationHttpPost) {
+ FrameMsg_Navigate_Params nav_params;
+
+ // An http url will trigger a resource load so cannot be used here.
+ nav_params.url = GURL("data:text/html,<div>Page</div>");
+ nav_params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ nav_params.transition = PAGE_TRANSITION_TYPED;
+ nav_params.page_id = -1;
+ nav_params.is_post = true;
+
+ // Set up post data.
+ const unsigned char* raw_data = reinterpret_cast<const unsigned char*>(
+ "post \0\ndata");
+ const unsigned int length = 11;
+ const std::vector<unsigned char> post_data(raw_data, raw_data + length);
+ nav_params.browser_initiated_post_data = post_data;
+
+ frame()->OnNavigate(nav_params);
+ ProcessPendingMessages();
+
+ const IPC::Message* frame_navigate_msg =
+ render_thread_->sink().GetUniqueMessageMatching(
+ FrameHostMsg_DidCommitProvisionalLoad::ID);
+ EXPECT_TRUE(frame_navigate_msg);
+
+ FrameHostMsg_DidCommitProvisionalLoad::Param host_nav_params;
+ FrameHostMsg_DidCommitProvisionalLoad::Read(frame_navigate_msg,
+ &host_nav_params);
+ EXPECT_TRUE(host_nav_params.a.is_post);
+
+ // Check post data sent to browser matches
+ EXPECT_TRUE(host_nav_params.a.page_state.IsValid());
+ const blink::WebHistoryItem item = PageStateToHistoryItem(
+ host_nav_params.a.page_state);
+ blink::WebHTTPBody body = item.httpBody();
+ blink::WebHTTPBody::Element element;
+ bool successful = body.elementAt(0, element);
+ EXPECT_TRUE(successful);
+ EXPECT_EQ(blink::WebHTTPBody::Element::TypeData, element.type);
+ EXPECT_EQ(length, element.data.size());
+ EXPECT_EQ(0, memcmp(raw_data, element.data.data(), length));
+}
+
+TEST_F(RenderViewImplTest, DecideNavigationPolicy) {
+ WebUITestWebUIControllerFactory factory;
+ WebUIControllerFactory::RegisterFactory(&factory);
+
+ DocumentState state;
+ state.set_navigation_state(NavigationState::CreateContentInitiated());
+
+ // Navigations to normal HTTP URLs can be handled locally.
+ blink::WebURLRequest request(GURL("http://foo.com"));
+ blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
+ GetMainFrame(),
+ &state,
+ request,
+ blink::WebNavigationTypeLinkClicked,
+ blink::WebNavigationPolicyCurrentTab,
+ false);
+ EXPECT_EQ(blink::WebNavigationPolicyCurrentTab, policy);
+
+ // Verify that form posts to WebUI URLs will be sent to the browser process.
+ blink::WebURLRequest form_request(GURL("chrome://foo"));
+ form_request.setHTTPMethod("POST");
+ policy = frame()->decidePolicyForNavigation(
+ GetMainFrame(),
+ &state,
+ form_request,
+ blink::WebNavigationTypeFormSubmitted,
+ blink::WebNavigationPolicyCurrentTab,
+ false);
+ EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
+
+ // Verify that popup links to WebUI URLs also are sent to browser.
+ blink::WebURLRequest popup_request(GURL("chrome://foo"));
+ policy = frame()->decidePolicyForNavigation(
+ GetMainFrame(),
+ &state,
+ popup_request,
+ blink::WebNavigationTypeLinkClicked,
+ blink::WebNavigationPolicyNewForegroundTab,
+ false);
+ EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
+}
+
+TEST_F(RenderViewImplTest, DecideNavigationPolicyHandlesAllTopLevel) {
+ DocumentState state;
+ state.set_navigation_state(NavigationState::CreateContentInitiated());
+
+ RendererPreferences prefs = view()->renderer_preferences();
+ prefs.browser_handles_all_top_level_requests = true;
+ view()->OnSetRendererPrefs(prefs);
+
+ const blink::WebNavigationType kNavTypes[] = {
+ blink::WebNavigationTypeLinkClicked,
+ blink::WebNavigationTypeFormSubmitted,
+ blink::WebNavigationTypeBackForward,
+ blink::WebNavigationTypeReload,
+ blink::WebNavigationTypeFormResubmitted,
+ blink::WebNavigationTypeOther,
+ };
+
+ blink::WebURLRequest request(GURL("http://foo.com"));
+ for (size_t i = 0; i < arraysize(kNavTypes); ++i) {
+ blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
+ GetMainFrame(),
+ &state,
+ request,
+ kNavTypes[i],
+ blink::WebNavigationPolicyCurrentTab,
+ false);
+ EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
+ }
+}
+
+TEST_F(RenderViewImplTest, DecideNavigationPolicyForWebUI) {
+ // Enable bindings to simulate a WebUI view.
+ view()->OnAllowBindings(BINDINGS_POLICY_WEB_UI);
+
+ DocumentState state;
+ state.set_navigation_state(NavigationState::CreateContentInitiated());
+
+ // Navigations to normal HTTP URLs will be sent to browser process.
+ blink::WebURLRequest request(GURL("http://foo.com"));
+ blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
+ GetMainFrame(),
+ &state,
+ request,
+ blink::WebNavigationTypeLinkClicked,
+ blink::WebNavigationPolicyCurrentTab,
+ false);
+ EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
+
+ // Navigations to WebUI URLs will also be sent to browser process.
+ blink::WebURLRequest webui_request(GURL("chrome://foo"));
+ policy = frame()->decidePolicyForNavigation(
+ GetMainFrame(),
+ &state,
+ webui_request,
+ blink::WebNavigationTypeLinkClicked,
+ blink::WebNavigationPolicyCurrentTab,
+ false);
+ EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
+
+ // Verify that form posts to data URLs will be sent to the browser process.
+ blink::WebURLRequest data_request(GURL("data:text/html,foo"));
+ data_request.setHTTPMethod("POST");
+ policy = frame()->decidePolicyForNavigation(
+ GetMainFrame(),
+ &state,
+ data_request,
+ blink::WebNavigationTypeFormSubmitted,
+ blink::WebNavigationPolicyCurrentTab,
+ false);
+ EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
+
+ // Verify that a popup that creates a view first and then navigates to a
+ // normal HTTP URL will be sent to the browser process, even though the
+ // new view does not have any enabled_bindings_.
+ blink::WebURLRequest popup_request(GURL("http://foo.com"));
+ blink::WebView* new_web_view = view()->createView(
+ GetMainFrame(), popup_request, blink::WebWindowFeatures(), "foo",
+ blink::WebNavigationPolicyNewForegroundTab, false);
+ RenderViewImpl* new_view = RenderViewImpl::FromWebView(new_web_view);
+ policy = static_cast<RenderFrameImpl*>(new_view->GetMainRenderFrame())->
+ decidePolicyForNavigation(
+ new_web_view->mainFrame(),
+ &state,
+ popup_request,
+ blink::WebNavigationTypeLinkClicked,
+ blink::WebNavigationPolicyNewForegroundTab,
+ false);
+ EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
+
+ // Clean up after the new view so we don't leak it.
+ new_view->Close();
+ new_view->Release();
+}
+
+// Ensure the RenderViewImpl sends an ACK to a SwapOut request, even if it is
+// already swapped out. http://crbug.com/93427.
+TEST_F(RenderViewImplTest, SendSwapOutACK) {
+ LoadHTML("<div>Page A</div>");
+ int initial_page_id = view()->GetPageId();
+
+ // Respond to a swap out request.
+ view()->OnSwapOut();
+
+ // Ensure the swap out commits synchronously.
+ EXPECT_NE(initial_page_id, view()->GetPageId());
+
+ // Check for a valid OnSwapOutACK.
+ const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_SwapOut_ACK::ID);
+ ASSERT_TRUE(msg);
+
+ // It is possible to get another swap out request. Ensure that we send
+ // an ACK, even if we don't have to do anything else.
+ render_thread_->sink().ClearMessages();
+ view()->OnSwapOut();
+ const IPC::Message* msg2 = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_SwapOut_ACK::ID);
+ ASSERT_TRUE(msg2);
+
+ // If we navigate back to this RenderView, ensure we don't send a state
+ // update for the swapped out URL. (http://crbug.com/72235)
+ FrameMsg_Navigate_Params nav_params;
+ nav_params.url = GURL("data:text/html,<div>Page B</div>");
+ nav_params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ nav_params.transition = PAGE_TRANSITION_TYPED;
+ nav_params.current_history_list_length = 1;
+ nav_params.current_history_list_offset = 0;
+ nav_params.pending_history_list_offset = 1;
+ nav_params.page_id = -1;
+ frame()->OnNavigate(nav_params);
+ ProcessPendingMessages();
+ const IPC::Message* msg3 = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ EXPECT_FALSE(msg3);
+}
+
+// Ensure the RenderViewImpl reloads the previous page if a reload request
+// arrives while it is showing swappedout://. http://crbug.com/143155.
+TEST_F(RenderViewImplTest, ReloadWhileSwappedOut) {
+ // 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.
+ ProcessPendingMessages();
+ const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg_A);
+ int page_id_A;
+ PageState state_A;
+ ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
+ EXPECT_EQ(1, page_id_A);
+ render_thread_->sink().ClearMessages();
+
+ // Back to page A (page_id 1) and commit.
+ FrameMsg_Navigate_Params params_A;
+ params_A.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
+ params_A.current_history_list_length = 2;
+ params_A.current_history_list_offset = 1;
+ params_A.pending_history_list_offset = 0;
+ params_A.page_id = 1;
+ params_A.page_state = state_A;
+ frame()->OnNavigate(params_A);
+ ProcessPendingMessages();
+
+ // Respond to a swap out request.
+ view()->OnSwapOut();
+
+ // Check for a OnSwapOutACK.
+ const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_SwapOut_ACK::ID);
+ ASSERT_TRUE(msg);
+ render_thread_->sink().ClearMessages();
+
+ // It is possible to get a reload request at this point, containing the
+ // params.page_state of the initial page (e.g., if the new page fails the
+ // provisional load in the renderer process, after we unload the old page).
+ // Ensure the old page gets reloaded, not swappedout://.
+ FrameMsg_Navigate_Params nav_params;
+ nav_params.url = GURL("data:text/html,<div>Page A</div>");
+ nav_params.navigation_type = FrameMsg_Navigate_Type::RELOAD;
+ nav_params.transition = PAGE_TRANSITION_RELOAD;
+ nav_params.current_history_list_length = 2;
+ nav_params.current_history_list_offset = 0;
+ nav_params.pending_history_list_offset = 0;
+ nav_params.page_id = 1;
+ nav_params.page_state = state_A;
+ frame()->OnNavigate(nav_params);
+ ProcessPendingMessages();
+
+ // Verify page A committed, not swappedout://.
+ const IPC::Message* frame_navigate_msg =
+ render_thread_->sink().GetUniqueMessageMatching(
+ FrameHostMsg_DidCommitProvisionalLoad::ID);
+ EXPECT_TRUE(frame_navigate_msg);
+
+ // Read URL out of the parent trait of the params object.
+ FrameHostMsg_DidCommitProvisionalLoad::Param commit_params;
+ FrameHostMsg_DidCommitProvisionalLoad::Read(frame_navigate_msg,
+ &commit_params);
+ EXPECT_NE(GURL("swappedout://"), commit_params.a.url);
+}
+
+
+// Test that we get the correct UpdateState message when we go back twice
+// quickly without committing. Regression test for http://crbug.com/58082.
+// Disabled: http://crbug.com/157357 .
+TEST_F(RenderViewImplTest, DISABLED_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.
+ ProcessPendingMessages();
+ const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg_A);
+ int page_id_A;
+ PageState 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.
+ ProcessPendingMessages();
+ const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg_B);
+ int page_id_B;
+ PageState 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.
+ ProcessPendingMessages();
+ const IPC::Message* msg_C = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg_C);
+ int page_id_C;
+ PageState 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.
+ FrameMsg_Navigate_Params params_C;
+ params_C.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params_C.transition = PAGE_TRANSITION_FORWARD_BACK;
+ params_C.current_history_list_length = 4;
+ params_C.current_history_list_offset = 3;
+ params_C.pending_history_list_offset = 2;
+ params_C.page_id = 3;
+ params_C.page_state = state_C;
+ frame()->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.
+ FrameMsg_Navigate_Params params_B;
+ params_B.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
+ params_B.current_history_list_length = 4;
+ params_B.current_history_list_offset = 2;
+ params_B.pending_history_list_offset = 1;
+ params_B.page_id = 2;
+ params_B.page_state = state_B;
+ frame()->OnNavigate(params_B);
+
+ // Back to page A (page_id 1) and commit.
+ FrameMsg_Navigate_Params params;
+ params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params.transition = PAGE_TRANSITION_FORWARD_BACK;
+ params_B.current_history_list_length = 4;
+ params_B.current_history_list_offset = 2;
+ params_B.pending_history_list_offset = 0;
+ params.page_id = 1;
+ params.page_state = state_A;
+ frame()->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;
+ PageState 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 the history_page_ids_ list can reveal when a stale back/forward
+// navigation arrives from the browser and can be ignored. See
+// http://crbug.com/86758.
+TEST_F(RenderViewImplTest, StaleNavigationsIgnored) {
+ // Load page A.
+ LoadHTML("<div>Page A</div>");
+ EXPECT_EQ(1, view()->history_list_length_);
+ EXPECT_EQ(0, view()->history_list_offset_);
+ EXPECT_EQ(1, view()->history_page_ids_[0]);
+
+ // Load page B, which will trigger an UpdateState message for page A.
+ LoadHTML("<div>Page B</div>");
+ EXPECT_EQ(2, view()->history_list_length_);
+ EXPECT_EQ(1, view()->history_list_offset_);
+ EXPECT_EQ(2, view()->history_page_ids_[1]);
+
+ // Check for a valid UpdateState message for page A.
+ ProcessPendingMessages();
+ const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg_A);
+ int page_id_A;
+ PageState state_A;
+ ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
+ EXPECT_EQ(1, page_id_A);
+ render_thread_->sink().ClearMessages();
+
+ // Back to page A (page_id 1) and commit.
+ FrameMsg_Navigate_Params params_A;
+ params_A.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
+ params_A.current_history_list_length = 2;
+ params_A.current_history_list_offset = 1;
+ params_A.pending_history_list_offset = 0;
+ params_A.page_id = 1;
+ params_A.page_state = state_A;
+ frame()->OnNavigate(params_A);
+ ProcessPendingMessages();
+
+ // A new navigation commits, clearing the forward history.
+ LoadHTML("<div>Page C</div>");
+ EXPECT_EQ(2, view()->history_list_length_);
+ EXPECT_EQ(1, view()->history_list_offset_);
+ EXPECT_EQ(3, view()->history_page_ids_[1]);
+
+ // The browser then sends a stale navigation to B, which should be ignored.
+ FrameMsg_Navigate_Params params_B;
+ params_B.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
+ params_B.current_history_list_length = 2;
+ params_B.current_history_list_offset = 0;
+ params_B.pending_history_list_offset = 1;
+ params_B.page_id = 2;
+ params_B.page_state = state_A; // Doesn't matter, just has to be present.
+ frame()->OnNavigate(params_B);
+
+ // State should be unchanged.
+ EXPECT_EQ(2, view()->history_list_length_);
+ EXPECT_EQ(1, view()->history_list_offset_);
+ EXPECT_EQ(3, view()->history_page_ids_[1]);
+}
+
+// Test that we do not ignore navigations after the entry limit is reached,
+// in which case the browser starts dropping entries from the front. In this
+// case, we'll see a page_id mismatch but the RenderView's id will be older,
+// not newer, than params.page_id. Use this as a cue that we should update the
+// state and not treat it like a navigation to a cropped forward history item.
+// See http://crbug.com/89798.
+TEST_F(RenderViewImplTest, DontIgnoreBackAfterNavEntryLimit) {
+ // Load page A.
+ LoadHTML("<div>Page A</div>");
+ EXPECT_EQ(1, view()->history_list_length_);
+ EXPECT_EQ(0, view()->history_list_offset_);
+ EXPECT_EQ(1, view()->history_page_ids_[0]);
+
+ // Load page B, which will trigger an UpdateState message for page A.
+ LoadHTML("<div>Page B</div>");
+ EXPECT_EQ(2, view()->history_list_length_);
+ EXPECT_EQ(1, view()->history_list_offset_);
+ EXPECT_EQ(2, view()->history_page_ids_[1]);
+
+ // Check for a valid UpdateState message for page A.
+ ProcessPendingMessages();
+ const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg_A);
+ int page_id_A;
+ PageState 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>");
+ EXPECT_EQ(3, view()->history_list_length_);
+ EXPECT_EQ(2, view()->history_list_offset_);
+ EXPECT_EQ(3, view()->history_page_ids_[2]);
+
+ // Check for a valid UpdateState message for page B.
+ ProcessPendingMessages();
+ const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
+ ViewHostMsg_UpdateState::ID);
+ ASSERT_TRUE(msg_B);
+ int page_id_B;
+ PageState state_B;
+ ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
+ EXPECT_EQ(2, page_id_B);
+ render_thread_->sink().ClearMessages();
+
+ // Suppose the browser has limited the number of NavigationEntries to 2.
+ // It has now dropped the first entry, but the renderer isn't notified.
+ // Ensure that going back to page B (page_id 2) at offset 0 is successful.
+ FrameMsg_Navigate_Params params_B;
+ params_B.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
+ params_B.current_history_list_length = 2;
+ params_B.current_history_list_offset = 1;
+ params_B.pending_history_list_offset = 0;
+ params_B.page_id = 2;
+ params_B.page_state = state_B;
+ frame()->OnNavigate(params_B);
+ ProcessPendingMessages();
+
+ EXPECT_EQ(2, view()->history_list_length_);
+ EXPECT_EQ(0, view()->history_list_offset_);
+ EXPECT_EQ(2, view()->history_page_ids_[0]);
+}
+
+// Test that our IME backend sends a notification message when the input focus
+// changes.
+TEST_F(RenderViewImplTest, OnImeTypeChanged) {
+ // 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\" value=\"some text\"></input>"
+ "<input id=\"test2\" type=\"password\"></input>"
+ "<input id=\"test3\" type=\"text\" inputmode=\"verbatim\"></input>"
+ "<input id=\"test4\" type=\"text\" inputmode=\"latin\"></input>"
+ "<input id=\"test5\" type=\"text\" inputmode=\"latin-name\"></input>"
+ "<input id=\"test6\" type=\"text\" inputmode=\"latin-prose\">"
+ "</input>"
+ "<input id=\"test7\" type=\"text\" inputmode=\"full-width-latin\">"
+ "</input>"
+ "<input id=\"test8\" type=\"text\" inputmode=\"kana\"></input>"
+ "<input id=\"test9\" type=\"text\" inputmode=\"katakana\"></input>"
+ "<input id=\"test10\" type=\"text\" inputmode=\"numeric\"></input>"
+ "<input id=\"test11\" type=\"text\" inputmode=\"tel\"></input>"
+ "<input id=\"test12\" type=\"text\" inputmode=\"email\"></input>"
+ "<input id=\"test13\" type=\"text\" inputmode=\"url\"></input>"
+ "<input id=\"test14\" type=\"text\" inputmode=\"unknown\"></input>"
+ "<input id=\"test15\" type=\"text\" inputmode=\"verbatim\"></input>"
+ "</body>"
+ "</html>");
+ render_thread_->sink().ClearMessages();
+
+ struct InputModeTestCase {
+ const char* input_id;
+ ui::TextInputMode expected_mode;
+ };
+ static const InputModeTestCase kInputModeTestCases[] = {
+ {"test1", ui::TEXT_INPUT_MODE_DEFAULT},
+ {"test3", ui::TEXT_INPUT_MODE_VERBATIM},
+ {"test4", ui::TEXT_INPUT_MODE_LATIN},
+ {"test5", ui::TEXT_INPUT_MODE_LATIN_NAME},
+ {"test6", ui::TEXT_INPUT_MODE_LATIN_PROSE},
+ {"test7", ui::TEXT_INPUT_MODE_FULL_WIDTH_LATIN},
+ {"test8", ui::TEXT_INPUT_MODE_KANA},
+ {"test9", ui::TEXT_INPUT_MODE_KATAKANA},
+ {"test10", ui::TEXT_INPUT_MODE_NUMERIC},
+ {"test11", ui::TEXT_INPUT_MODE_TEL},
+ {"test12", ui::TEXT_INPUT_MODE_EMAIL},
+ {"test13", ui::TEXT_INPUT_MODE_URL},
+ {"test14", ui::TEXT_INPUT_MODE_DEFAULT},
+ {"test15", ui::TEXT_INPUT_MODE_VERBATIM},
+ };
+
+ 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()->UpdateTextInputType();
+ const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
+ EXPECT_TRUE(msg != NULL);
+ EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
+ ui::TextInputType type;
+ bool can_compose_inline = false;
+ ui::TextInputMode input_mode = ui::TEXT_INPUT_MODE_DEFAULT;
+ ViewHostMsg_TextInputTypeChanged::Read(msg,
+ &type,
+ &input_mode,
+ &can_compose_inline);
+ EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, type);
+ EXPECT_EQ(true, can_compose_inline);
+
+ // 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()->UpdateTextInputType();
+ msg = render_thread_->sink().GetMessageAt(0);
+ EXPECT_TRUE(msg != NULL);
+ EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
+ ViewHostMsg_TextInputTypeChanged::Read(msg,
+ &type,
+ &input_mode,
+ &can_compose_inline);
+ EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, type);
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInputModeTestCases); i++) {
+ const InputModeTestCase* test_case = &kInputModeTestCases[i];
+ std::string javascript =
+ base::StringPrintf("document.getElementById('%s').focus();",
+ test_case->input_id);
+ // Move the input focus to the target <input> element, where we should
+ // activate IMEs.
+ ExecuteJavaScriptAndReturnIntValue(base::ASCIIToUTF16(javascript), NULL);
+ ProcessPendingMessages();
+ render_thread_->sink().ClearMessages();
+
+ // Update the IME status and verify if our IME backend sends an IPC
+ // message to activate IMEs.
+ view()->UpdateTextInputType();
+ const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
+ EXPECT_TRUE(msg != NULL);
+ EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
+ ViewHostMsg_TextInputTypeChanged::Read(msg,
+ &type,
+ &input_mode,
+ &can_compose_inline);
+ EXPECT_EQ(test_case->expected_mode, input_mode);
+ }
+ }
+}
+
+// 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(RenderViewImplTest, 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(
+ base::WideToUTF16Hack(ime_message->ime_string),
+ std::vector<blink::WebCompositionUnderline>(),
+ ime_message->selection_start,
+ ime_message->selection_end);
+ break;
+
+ case IME_CONFIRMCOMPOSITION:
+ view()->OnImeConfirmComposition(
+ base::WideToUTF16Hack(ime_message->ime_string),
+ gfx::Range::InvalidRange(),
+ false);
+ break;
+
+ case IME_CANCELCOMPOSITION:
+ view()->OnImeSetComposition(
+ base::string16(),
+ std::vector<blink::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()->UpdateTextInputType();
+ 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 = base::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(RenderViewImplTest, 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[] = {
+ { blink::WebTextDirectionRightToLeft, L"\x000A" L"rtl,rtl" },
+ { blink::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 = base::UTF16ToWideHack(
+ GetMainFrame()->contentAsText(kMaxOutputCharacters));
+ EXPECT_EQ(output, kTextDirection[i].expected_result);
+ }
+}
+
+// see http://crbug.com/238750
+#if defined(OS_WIN)
+#define MAYBE_OnHandleKeyboardEvent DISABLED_OnHandleKeyboardEvent
+#else
+#define MAYBE_OnHandleKeyboardEvent OnHandleKeyboardEvent
+#endif
+
+// Test that we can receive correct DOM events when we send input events
+// through the RenderWidget::OnHandleInputEvent() function.
+TEST_F(RenderViewImplTest, MAYBE_OnHandleKeyboardEvent) {
+#if !defined(OS_MACOSX)
+ // 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];
+ base::string16 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 = base::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.
+// see http://crbug.com/244562
+#if defined(OS_WIN)
+#define MAYBE_InsertCharacters DISABLED_InsertCharacters
+#else
+#define MAYBE_InsertCharacters InsertCharacters
+#endif
+TEST_F(RenderViewImplTest, MAYBE_InsertCharacters) {
+#if !defined(OS_MACOSX)
+ 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];
+ base::string16 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 = base::UTF16ToWideHack(
+ GetMainFrame()->contentAsText(kMaxOutputCharacters));
+ EXPECT_EQ(kLayouts[i].expected_result, output);
+ }
+#else
+ NOTIMPLEMENTED();
+#endif
+}
+
+// Crashy, http://crbug.com/53247.
+TEST_F(RenderViewImplTest, DISABLED_DidFailProvisionalLoadWithErrorForError) {
+ GetMainFrame()->enableViewSourceMode(true);
+ WebURLError error;
+ error.domain = WebString::fromUTF8(net::kErrorDomain);
+ error.reason = net::ERR_FILE_NOT_FOUND;
+ error.unreachableURL = GURL("http://foo");
+ WebFrame* web_frame = GetMainFrame();
+
+ // Start a load that will reach provisional state synchronously,
+ // but won't complete synchronously.
+ FrameMsg_Navigate_Params params;
+ params.page_id = -1;
+ params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params.url = GURL("data:text/html,test data");
+ frame()->OnNavigate(params);
+
+ // An error occurred.
+ view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
+ // Frame should exit view-source mode.
+ EXPECT_FALSE(web_frame->isViewSourceModeEnabled());
+}
+
+TEST_F(RenderViewImplTest, DidFailProvisionalLoadWithErrorForCancellation) {
+ GetMainFrame()->enableViewSourceMode(true);
+ WebURLError error;
+ error.domain = WebString::fromUTF8(net::kErrorDomain);
+ error.reason = net::ERR_ABORTED;
+ error.unreachableURL = GURL("http://foo");
+ WebFrame* web_frame = GetMainFrame();
+
+ // Start a load that will reach provisional state synchronously,
+ // but won't complete synchronously.
+ FrameMsg_Navigate_Params params;
+ params.page_id = -1;
+ params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params.url = GURL("data:text/html,test data");
+ frame()->OnNavigate(params);
+
+ // A cancellation occurred.
+ view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
+ // Frame should stay in view-source mode.
+ EXPECT_TRUE(web_frame->isViewSourceModeEnabled());
+}
+
+// Regression test for http://crbug.com/41562
+TEST_F(RenderViewImplTest, UpdateTargetURLWithInvalidURL) {
+ const GURL invalid_gurl("http://");
+ view()->setMouseOverURL(blink::WebURL(invalid_gurl));
+ EXPECT_EQ(invalid_gurl, view()->target_url_);
+}
+
+TEST_F(RenderViewImplTest, SetHistoryLengthAndPrune) {
+ int expected_page_id = -1;
+
+ // No history to merge and no committed pages.
+ view()->OnSetHistoryLengthAndPrune(0, -1);
+ EXPECT_EQ(0, view()->history_list_length_);
+ EXPECT_EQ(-1, view()->history_list_offset_);
+
+ // History to merge and no committed pages.
+ view()->OnSetHistoryLengthAndPrune(2, -1);
+ EXPECT_EQ(2, view()->history_list_length_);
+ EXPECT_EQ(1, view()->history_list_offset_);
+ EXPECT_EQ(-1, view()->history_page_ids_[0]);
+ EXPECT_EQ(-1, view()->history_page_ids_[1]);
+ ClearHistory();
+
+ // No history to merge and a committed page to be kept.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
+ EXPECT_EQ(1, view()->history_list_length_);
+ EXPECT_EQ(0, view()->history_list_offset_);
+ EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
+ ClearHistory();
+
+ // No history to merge and a committed page to be pruned.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ view()->OnSetHistoryLengthAndPrune(0, expected_page_id + 1);
+ EXPECT_EQ(0, view()->history_list_length_);
+ EXPECT_EQ(-1, view()->history_list_offset_);
+ ClearHistory();
+
+ // No history to merge and a committed page that the browser was unaware of.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ view()->OnSetHistoryLengthAndPrune(0, -1);
+ EXPECT_EQ(1, view()->history_list_length_);
+ EXPECT_EQ(0, view()->history_list_offset_);
+ EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
+ ClearHistory();
+
+ // History to merge and a committed page to be kept.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
+ EXPECT_EQ(3, view()->history_list_length_);
+ EXPECT_EQ(2, view()->history_list_offset_);
+ EXPECT_EQ(-1, view()->history_page_ids_[0]);
+ EXPECT_EQ(-1, view()->history_page_ids_[1]);
+ EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
+ ClearHistory();
+
+ // History to merge and a committed page to be pruned.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ view()->OnSetHistoryLengthAndPrune(2, expected_page_id + 1);
+ EXPECT_EQ(2, view()->history_list_length_);
+ EXPECT_EQ(1, view()->history_list_offset_);
+ EXPECT_EQ(-1, view()->history_page_ids_[0]);
+ EXPECT_EQ(-1, view()->history_page_ids_[1]);
+ ClearHistory();
+
+ // History to merge and a committed page that the browser was unaware of.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ view()->OnSetHistoryLengthAndPrune(2, -1);
+ EXPECT_EQ(3, view()->history_list_length_);
+ EXPECT_EQ(2, view()->history_list_offset_);
+ EXPECT_EQ(-1, view()->history_page_ids_[0]);
+ EXPECT_EQ(-1, view()->history_page_ids_[1]);
+ EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
+ ClearHistory();
+
+ int expected_page_id_2 = -1;
+
+ // No history to merge and two committed pages, both to be kept.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id_2 = view()->page_id_;
+ EXPECT_GT(expected_page_id_2, expected_page_id);
+ view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
+ EXPECT_EQ(2, view()->history_list_length_);
+ EXPECT_EQ(1, view()->history_list_offset_);
+ EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
+ EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
+ ClearHistory();
+
+ // No history to merge and two committed pages, and only the second is kept.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id_2 = view()->page_id_;
+ EXPECT_GT(expected_page_id_2, expected_page_id);
+ view()->OnSetHistoryLengthAndPrune(0, expected_page_id_2);
+ EXPECT_EQ(1, view()->history_list_length_);
+ EXPECT_EQ(0, view()->history_list_offset_);
+ EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[0]);
+ ClearHistory();
+
+ // No history to merge and two committed pages, both of which the browser was
+ // unaware of.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id_2 = view()->page_id_;
+ EXPECT_GT(expected_page_id_2, expected_page_id);
+ view()->OnSetHistoryLengthAndPrune(0, -1);
+ EXPECT_EQ(2, view()->history_list_length_);
+ EXPECT_EQ(1, view()->history_list_offset_);
+ EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
+ EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
+ ClearHistory();
+
+ // History to merge and two committed pages, both to be kept.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id_2 = view()->page_id_;
+ EXPECT_GT(expected_page_id_2, expected_page_id);
+ view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
+ EXPECT_EQ(4, view()->history_list_length_);
+ EXPECT_EQ(3, view()->history_list_offset_);
+ EXPECT_EQ(-1, view()->history_page_ids_[0]);
+ EXPECT_EQ(-1, view()->history_page_ids_[1]);
+ EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
+ EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
+ ClearHistory();
+
+ // History to merge and two committed pages, and only the second is kept.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id_2 = view()->page_id_;
+ EXPECT_GT(expected_page_id_2, expected_page_id);
+ view()->OnSetHistoryLengthAndPrune(2, expected_page_id_2);
+ EXPECT_EQ(3, view()->history_list_length_);
+ EXPECT_EQ(2, view()->history_list_offset_);
+ EXPECT_EQ(-1, view()->history_page_ids_[0]);
+ EXPECT_EQ(-1, view()->history_page_ids_[1]);
+ EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[2]);
+ ClearHistory();
+
+ // History to merge and two committed pages, both of which the browser was
+ // unaware of.
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id = view()->page_id_;
+ frame()->didCommitProvisionalLoad(GetMainFrame(), true);
+ expected_page_id_2 = view()->page_id_;
+ EXPECT_GT(expected_page_id_2, expected_page_id);
+ view()->OnSetHistoryLengthAndPrune(2, -1);
+ EXPECT_EQ(4, view()->history_list_length_);
+ EXPECT_EQ(3, view()->history_list_offset_);
+ EXPECT_EQ(-1, view()->history_page_ids_[0]);
+ EXPECT_EQ(-1, view()->history_page_ids_[1]);
+ EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
+ EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
+}
+
+TEST_F(RenderViewImplTest, ContextMenu) {
+ LoadHTML("<div>Page A</div>");
+
+ // Create a right click in the center of the iframe. (I'm hoping this will
+ // make this a bit more robust in case of some other formatting or other bug.)
+ WebMouseEvent mouse_event;
+ mouse_event.type = WebInputEvent::MouseDown;
+ mouse_event.button = WebMouseEvent::ButtonRight;
+ mouse_event.x = 250;
+ mouse_event.y = 250;
+ mouse_event.globalX = 250;
+ mouse_event.globalY = 250;
+
+ SendWebMouseEvent(mouse_event);
+
+ // Now simulate the corresponding up event which should display the menu
+ mouse_event.type = WebInputEvent::MouseUp;
+ SendWebMouseEvent(mouse_event);
+
+ EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
+ FrameHostMsg_ContextMenu::ID));
+}
+
+TEST_F(RenderViewImplTest, TestBackForward) {
+ LoadHTML("<div id=pagename>Page A</div>");
+ blink::WebHistoryItem page_a_item = GetMainFrame()->currentHistoryItem();
+ int was_page_a = -1;
+ base::string16 check_page_a =
+ base::ASCIIToUTF16(
+ "Number(document.getElementById('pagename').innerHTML == 'Page A')");
+ EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
+ EXPECT_EQ(1, was_page_a);
+
+ LoadHTML("<div id=pagename>Page B</div>");
+ int was_page_b = -1;
+ base::string16 check_page_b =
+ base::ASCIIToUTF16(
+ "Number(document.getElementById('pagename').innerHTML == 'Page B')");
+ EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
+ EXPECT_EQ(1, was_page_b);
+
+ LoadHTML("<div id=pagename>Page C</div>");
+ int was_page_c = -1;
+ base::string16 check_page_c =
+ base::ASCIIToUTF16(
+ "Number(document.getElementById('pagename').innerHTML == 'Page C')");
+ EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
+ EXPECT_EQ(1, was_page_b);
+
+ blink::WebHistoryItem forward_item = GetMainFrame()->currentHistoryItem();
+ GoBack(GetMainFrame()->previousHistoryItem());
+ EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
+ EXPECT_EQ(1, was_page_b);
+
+ GoForward(forward_item);
+ EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
+ EXPECT_EQ(1, was_page_c);
+
+ GoBack(GetMainFrame()->previousHistoryItem());
+ EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
+ EXPECT_EQ(1, was_page_b);
+
+ forward_item = GetMainFrame()->currentHistoryItem();
+ GoBack(page_a_item);
+ EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
+ EXPECT_EQ(1, was_page_a);
+
+ GoForward(forward_item);
+ EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
+ EXPECT_EQ(1, was_page_b);
+}
+
+#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
+TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) {
+
+#if defined(OS_WIN)
+ // http://crbug.com/304193
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return;
+#endif
+
+ LoadHTML("<textarea id=\"test\"></textarea>");
+ ExecuteJavaScript("document.getElementById('test').focus();");
+
+ const base::string16 empty_string;
+ const std::vector<blink::WebCompositionUnderline> empty_underline;
+ std::vector<gfx::Rect> bounds;
+ view()->OnSetFocus(true);
+ view()->OnSetInputMethodActive(true);
+
+ // ASCII composition
+ const base::string16 ascii_composition = base::UTF8ToUTF16("aiueo");
+ view()->OnImeSetComposition(ascii_composition, empty_underline, 0, 0);
+ view()->GetCompositionCharacterBounds(&bounds);
+ ASSERT_EQ(ascii_composition.size(), bounds.size());
+ for (size_t i = 0; i < bounds.size(); ++i)
+ EXPECT_LT(0, bounds[i].width());
+ view()->OnImeConfirmComposition(
+ empty_string, gfx::Range::InvalidRange(), false);
+
+ // Non surrogate pair unicode character.
+ const base::string16 unicode_composition = base::UTF8ToUTF16(
+ "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
+ view()->OnImeSetComposition(unicode_composition, empty_underline, 0, 0);
+ view()->GetCompositionCharacterBounds(&bounds);
+ ASSERT_EQ(unicode_composition.size(), bounds.size());
+ for (size_t i = 0; i < bounds.size(); ++i)
+ EXPECT_LT(0, bounds[i].width());
+ view()->OnImeConfirmComposition(
+ empty_string, gfx::Range::InvalidRange(), false);
+
+ // Surrogate pair character.
+ const base::string16 surrogate_pair_char =
+ base::UTF8ToUTF16("\xF0\xA0\xAE\x9F");
+ view()->OnImeSetComposition(surrogate_pair_char,
+ empty_underline,
+ 0,
+ 0);
+ view()->GetCompositionCharacterBounds(&bounds);
+ ASSERT_EQ(surrogate_pair_char.size(), bounds.size());
+ EXPECT_LT(0, bounds[0].width());
+ EXPECT_EQ(0, bounds[1].width());
+ view()->OnImeConfirmComposition(
+ empty_string, gfx::Range::InvalidRange(), false);
+
+ // Mixed string.
+ const base::string16 surrogate_pair_mixed_composition =
+ surrogate_pair_char + base::UTF8ToUTF16("\xE3\x81\x82") +
+ surrogate_pair_char + base::UTF8ToUTF16("b") + surrogate_pair_char;
+ const size_t utf16_length = 8UL;
+ const bool is_surrogate_pair_empty_rect[8] = {
+ false, true, false, false, true, false, false, true };
+ view()->OnImeSetComposition(surrogate_pair_mixed_composition,
+ empty_underline,
+ 0,
+ 0);
+ view()->GetCompositionCharacterBounds(&bounds);
+ ASSERT_EQ(utf16_length, bounds.size());
+ for (size_t i = 0; i < utf16_length; ++i) {
+ if (is_surrogate_pair_empty_rect[i]) {
+ EXPECT_EQ(0, bounds[i].width());
+ } else {
+ EXPECT_LT(0, bounds[i].width());
+ }
+ }
+ view()->OnImeConfirmComposition(
+ empty_string, gfx::Range::InvalidRange(), false);
+}
+#endif
+
+TEST_F(RenderViewImplTest, ZoomLimit) {
+ const double kMinZoomLevel = ZoomFactorToZoomLevel(kMinimumZoomFactor);
+ const double kMaxZoomLevel = ZoomFactorToZoomLevel(kMaximumZoomFactor);
+
+ FrameMsg_Navigate_Params params;
+ params.page_id = -1;
+ params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+
+ // Verifies navigation to a URL with preset zoom level indeed sets the level.
+ // Regression test for http://crbug.com/139559, where the level was not
+ // properly set when it is out of the default zoom limits of WebView.
+ params.url = GURL("data:text/html,min_zoomlimit_test");
+ view()->OnSetZoomLevelForLoadingURL(params.url, kMinZoomLevel);
+ frame()->OnNavigate(params);
+ ProcessPendingMessages();
+ EXPECT_DOUBLE_EQ(kMinZoomLevel, view()->GetWebView()->zoomLevel());
+
+ // It should work even when the zoom limit is temporarily changed in the page.
+ view()->GetWebView()->zoomLimitsChanged(ZoomFactorToZoomLevel(1.0),
+ ZoomFactorToZoomLevel(1.0));
+ params.url = GURL("data:text/html,max_zoomlimit_test");
+ view()->OnSetZoomLevelForLoadingURL(params.url, kMaxZoomLevel);
+ frame()->OnNavigate(params);
+ ProcessPendingMessages();
+ EXPECT_DOUBLE_EQ(kMaxZoomLevel, view()->GetWebView()->zoomLevel());
+}
+
+TEST_F(RenderViewImplTest, SetEditableSelectionAndComposition) {
+ // Load an HTML page consisting of an input field.
+ LoadHTML("<html>"
+ "<head>"
+ "</head>"
+ "<body>"
+ "<input id=\"test1\" value=\"some test text hello\"></input>"
+ "</body>"
+ "</html>");
+ ExecuteJavaScript("document.getElementById('test1').focus();");
+ view()->OnSetEditableSelectionOffsets(4, 8);
+ const std::vector<blink::WebCompositionUnderline> empty_underline;
+ view()->OnSetCompositionFromExistingText(7, 10, empty_underline);
+ blink::WebTextInputInfo info = view()->webview()->textInputInfo();
+ EXPECT_EQ(4, info.selectionStart);
+ EXPECT_EQ(8, info.selectionEnd);
+ EXPECT_EQ(7, info.compositionStart);
+ EXPECT_EQ(10, info.compositionEnd);
+ view()->OnUnselect();
+ info = view()->webview()->textInputInfo();
+ EXPECT_EQ(0, info.selectionStart);
+ EXPECT_EQ(0, info.selectionEnd);
+}
+
+
+TEST_F(RenderViewImplTest, OnExtendSelectionAndDelete) {
+ // Load an HTML page consisting of an input field.
+ LoadHTML("<html>"
+ "<head>"
+ "</head>"
+ "<body>"
+ "<input id=\"test1\" value=\"abcdefghijklmnopqrstuvwxyz\"></input>"
+ "</body>"
+ "</html>");
+ ExecuteJavaScript("document.getElementById('test1').focus();");
+ view()->OnSetEditableSelectionOffsets(10, 10);
+ view()->OnExtendSelectionAndDelete(3, 4);
+ blink::WebTextInputInfo info = view()->webview()->textInputInfo();
+ EXPECT_EQ("abcdefgopqrstuvwxyz", info.value);
+ EXPECT_EQ(7, info.selectionStart);
+ EXPECT_EQ(7, info.selectionEnd);
+ view()->OnSetEditableSelectionOffsets(4, 8);
+ view()->OnExtendSelectionAndDelete(2, 5);
+ info = view()->webview()->textInputInfo();
+ EXPECT_EQ("abuvwxyz", info.value);
+ EXPECT_EQ(2, info.selectionStart);
+ EXPECT_EQ(2, info.selectionEnd);
+}
+
+// Test that the navigating specific frames works correctly.
+TEST_F(RenderViewImplTest, NavigateFrame) {
+ // Load page A.
+ LoadHTML("hello <iframe srcdoc='fail' name='frame'></iframe>");
+
+ // Navigate the frame only.
+ FrameMsg_Navigate_Params nav_params;
+ nav_params.url = GURL("data:text/html,world");
+ nav_params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ nav_params.transition = PAGE_TRANSITION_TYPED;
+ nav_params.current_history_list_length = 1;
+ nav_params.current_history_list_offset = 0;
+ nav_params.pending_history_list_offset = 1;
+ nav_params.page_id = -1;
+ nav_params.frame_to_navigate = "frame";
+ frame()->OnNavigate(nav_params);
+ ProcessPendingMessages();
+
+ // Copy the document content to std::wstring and compare with the
+ // expected result.
+ const int kMaxOutputCharacters = 256;
+ std::wstring output = base::UTF16ToWideHack(
+ GetMainFrame()->contentAsText(kMaxOutputCharacters));
+ EXPECT_EQ(output, L"hello \n\nworld");
+}
+
+// This test ensures that a RenderFrame object is created for the top level
+// frame in the RenderView.
+TEST_F(RenderViewImplTest, BasicRenderFrame) {
+ EXPECT_TRUE(view()->main_render_frame_.get());
+}
+
+TEST_F(RenderViewImplTest, GetSSLStatusOfFrame) {
+ LoadHTML("<!DOCTYPE html><html><body></body></html>");
+
+ WebFrame* frame = GetMainFrame();
+ SSLStatus ssl_status = view()->GetSSLStatusOfFrame(frame);
+ EXPECT_FALSE(net::IsCertStatusError(ssl_status.cert_status));
+
+ const_cast<blink::WebURLResponse&>(frame->dataSource()->response()).
+ setSecurityInfo(
+ SerializeSecurityInfo(0, net::CERT_STATUS_ALL_ERRORS, 0, 0,
+ SignedCertificateTimestampIDStatusList()));
+ ssl_status = view()->GetSSLStatusOfFrame(frame);
+ EXPECT_TRUE(net::IsCertStatusError(ssl_status.cert_status));
+}
+
+TEST_F(RenderViewImplTest, MessageOrderInDidChangeSelection) {
+ view()->OnSetInputMethodActive(true);
+ view()->set_send_content_state_immediately(true);
+ LoadHTML("<textarea id=\"test\"></textarea>");
+
+ view()->handling_input_event_ = true;
+ ExecuteJavaScript("document.getElementById('test').focus();");
+
+ bool is_input_type_called = false;
+ bool is_selection_called = false;
+ size_t last_input_type = 0;
+ size_t last_selection = 0;
+
+ for (size_t i = 0; i < render_thread_->sink().message_count(); ++i) {
+ const uint32 type = render_thread_->sink().GetMessageAt(i)->type();
+ if (type == ViewHostMsg_TextInputTypeChanged::ID) {
+ is_input_type_called = true;
+ last_input_type = i;
+ } else if (type == ViewHostMsg_SelectionChanged::ID) {
+ is_selection_called = true;
+ last_selection = i;
+ }
+ }
+
+ EXPECT_TRUE(is_input_type_called);
+ EXPECT_TRUE(is_selection_called);
+
+ // InputTypeChange shold be called earlier than SelectionChanged.
+ EXPECT_LT(last_input_type, last_selection);
+}
+
+class SuppressErrorPageTest : public RenderViewTest {
+ public:
+ virtual void SetUp() OVERRIDE {
+ SetRendererClientForTesting(&client_);
+ RenderViewTest::SetUp();
+ }
+
+ RenderViewImpl* view() {
+ return static_cast<RenderViewImpl*>(view_);
+ }
+
+ RenderFrameImpl* frame() {
+ return static_cast<RenderFrameImpl*>(view()->GetMainRenderFrame());
+ }
+
+ private:
+ class TestContentRendererClient : public ContentRendererClient {
+ public:
+ virtual bool ShouldSuppressErrorPage(RenderFrame* render_frame,
+ const GURL& url) OVERRIDE {
+ return url == GURL("http://example.com/suppress");
+ }
+
+ virtual void GetNavigationErrorStrings(
+ content::RenderView* render_view,
+ blink::WebFrame* frame,
+ const blink::WebURLRequest& failed_request,
+ const blink::WebURLError& error,
+ std::string* error_html,
+ base::string16* error_description) OVERRIDE {
+ if (error_html)
+ *error_html = "A suffusion of yellow.";
+ }
+ };
+
+ TestContentRendererClient client_;
+};
+
+#if defined(OS_ANDROID)
+// Crashing on Android: http://crbug.com/311341
+#define MAYBE_Suppresses DISABLED_Suppresses
+#else
+#define MAYBE_Suppresses Suppresses
+#endif
+
+TEST_F(SuppressErrorPageTest, MAYBE_Suppresses) {
+ WebURLError error;
+ error.domain = WebString::fromUTF8(net::kErrorDomain);
+ error.reason = net::ERR_FILE_NOT_FOUND;
+ error.unreachableURL = GURL("http://example.com/suppress");
+ WebFrame* web_frame = GetMainFrame();
+
+ // Start a load that will reach provisional state synchronously,
+ // but won't complete synchronously.
+ FrameMsg_Navigate_Params params;
+ params.page_id = -1;
+ params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params.url = GURL("data:text/html,test data");
+ frame()->OnNavigate(params);
+
+ // An error occurred.
+ view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
+ const int kMaxOutputCharacters = 22;
+ EXPECT_EQ("", UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
+}
+
+#if defined(OS_ANDROID)
+// Crashing on Android: http://crbug.com/311341
+#define MAYBE_DoesNotSuppress DISABLED_DoesNotSuppress
+#else
+#define MAYBE_DoesNotSuppress DoesNotSuppress
+#endif
+
+TEST_F(SuppressErrorPageTest, MAYBE_DoesNotSuppress) {
+ WebURLError error;
+ error.domain = WebString::fromUTF8(net::kErrorDomain);
+ error.reason = net::ERR_FILE_NOT_FOUND;
+ error.unreachableURL = GURL("http://example.com/dont-suppress");
+ WebFrame* web_frame = GetMainFrame();
+
+ // Start a load that will reach provisional state synchronously,
+ // but won't complete synchronously.
+ FrameMsg_Navigate_Params params;
+ params.page_id = -1;
+ params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
+ params.url = GURL("data:text/html,test data");
+ frame()->OnNavigate(params);
+
+ // An error occurred.
+ view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
+ ProcessPendingMessages();
+ const int kMaxOutputCharacters = 22;
+ EXPECT_EQ("A suffusion of yellow.",
+ UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
+}
+
+// Tests if IME API's candidatewindow* events sent from browser are handled
+// in renderer.
+TEST_F(RenderViewImplTest, SendCandidateWindowEvents) {
+ // Sends an HTML with an <input> element and scripts to the renderer.
+ // The script handles all 3 of candidatewindow* events for an
+ // InputMethodContext object and once it received 'show', 'update', 'hide'
+ // should appear in the result div.
+ LoadHTML("<input id='test'>"
+ "<div id='result'>Result: </div>"
+ "<script>"
+ "window.onload = function() {"
+ " var result = document.getElementById('result');"
+ " var test = document.getElementById('test');"
+ " test.focus();"
+ " var context = test.inputMethodContext;"
+ " if (context) {"
+ " context.oncandidatewindowshow = function() {"
+ " result.innerText += 'show'; };"
+ " context.oncandidatewindowupdate = function(){"
+ " result.innerText += 'update'; };"
+ " context.oncandidatewindowhide = function(){"
+ " result.innerText += 'hide'; };"
+ " }"
+ "};"
+ "</script>");
+
+ // Fire candidatewindow events.
+ view()->OnCandidateWindowShown();
+ view()->OnCandidateWindowUpdated();
+ view()->OnCandidateWindowHidden();
+
+ // Retrieve the content and check if it is expected.
+ const int kMaxOutputCharacters = 50;
+ std::string output = base::UTF16ToUTF8(
+ GetMainFrame()->contentAsText(kMaxOutputCharacters));
+ EXPECT_EQ(output, "\nResult:showupdatehide");
+}
+
+// Ensure the render view sends favicon url update events correctly.
+TEST_F(RenderViewImplTest, SendFaviconURLUpdateEvent) {
+ // An event should be sent when a favicon url exists.
+ LoadHTML("<html>"
+ "<head>"
+ "<link rel='icon' href='http://www.google.com/favicon.ico'>"
+ "</head>"
+ "</html>");
+ EXPECT_TRUE(render_thread_->sink().GetFirstMessageMatching(
+ ViewHostMsg_UpdateFaviconURL::ID));
+ render_thread_->sink().ClearMessages();
+
+ // An event should not be sent if no favicon url exists. This is an assumption
+ // made by some of Chrome's favicon handling.
+ LoadHTML("<html>"
+ "<head>"
+ "</head>"
+ "</html>");
+ EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
+ ViewHostMsg_UpdateFaviconURL::ID));
+}
+
+TEST_F(RenderViewImplTest, FocusElementCallsFocusedNodeChanged) {
+ LoadHTML("<input id='test1' value='hello1'></input>"
+ "<input id='test2' value='hello2'></input>");
+
+ ExecuteJavaScript("document.getElementById('test1').focus();");
+ const IPC::Message* msg1 = render_thread_->sink().GetFirstMessageMatching(
+ ViewHostMsg_FocusedNodeChanged::ID);
+ EXPECT_TRUE(msg1);
+
+ ViewHostMsg_FocusedNodeChanged::Param params;
+ ViewHostMsg_FocusedNodeChanged::Read(msg1, &params);
+ EXPECT_TRUE(params.a);
+ render_thread_->sink().ClearMessages();
+
+ ExecuteJavaScript("document.getElementById('test2').focus();");
+ const IPC::Message* msg2 = render_thread_->sink().GetFirstMessageMatching(
+ ViewHostMsg_FocusedNodeChanged::ID);
+ EXPECT_TRUE(msg2);
+ ViewHostMsg_FocusedNodeChanged::Read(msg2, &params);
+ EXPECT_TRUE(params.a);
+ render_thread_->sink().ClearMessages();
+
+ view()->webview()->clearFocusedNode();
+ const IPC::Message* msg3 = render_thread_->sink().GetFirstMessageMatching(
+ ViewHostMsg_FocusedNodeChanged::ID);
+ EXPECT_TRUE(msg3);
+ ViewHostMsg_FocusedNodeChanged::Read(msg3, &params);
+ EXPECT_FALSE(params.a);
+ render_thread_->sink().ClearMessages();
+}
+
+class SynchronousFrameRemovalOnLoadTest : public RenderViewImplTest {
+ protected:
+ // Helper render view observer class that tries to remove
+ // element with id 'frame' from top frame/document DOM
+ // when non-top frame finishes loading.
+ class OnLoadFrameRemover : public RenderViewObserver {
+ public:
+ explicit OnLoadFrameRemover(RenderView* render_view) :
+ RenderViewObserver(render_view) {}
+ virtual ~OnLoadFrameRemover() {}
+
+ virtual void DidFinishDocumentLoad(blink::WebFrame* frame) OVERRIDE {
+ if (frame->top() != frame) {
+ frame->top()->executeScript(blink::WebScriptSource(
+ WebString::fromUTF8(
+ "document.getElementById('frame').remove();")));
+ }
+ }
+ };
+};
+
+// Tests if synchronously removing a frame on its load does not cause crashes.
+TEST_F(SynchronousFrameRemovalOnLoadTest, DynamicallyInsertedFrame) {
+ OnLoadFrameRemover remover(view());
+ LoadHTML("<!DOCTYPE html>"
+ "<html>"
+ "<head>"
+ "<title></title>"
+ "<script type='text/javascript' language='javascript'>"
+ "window.onload = function () {"
+ " frame = document.createElement('iframe');"
+ " frame.id = 'frame';"
+ " document.body.appendChild(frame);"
+ "}"
+ "</script>"
+ "</head>"
+ "<body></body>"
+ "</html>");
+}
+
+TEST_F(SynchronousFrameRemovalOnLoadTest, StaticFrame) {
+ OnLoadFrameRemover remover(view());
+ LoadHTML("<!DOCTYPE html>"
+ "<html>"
+ "<head>"
+ "<title></title>"
+ "</head>"
+ "<body>"
+ "<iframe id='frame' src='about:blank'></iframe>"
+ "</body>"
+ "</html>");
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698