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, ¶ms); |
- 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, ¶ms); |
- 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, ¶ms); |
- 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, ¶ms); |
+ 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, ¶ms); |
+ 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, ¶ms); |
+ 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 |