1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/basictypes.h"
7 #include "base/memory/shared_memory.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/win/windows_version.h"
11 #include "content/common/ssl_status_serialization.h"
12 #include "content/common/view_messages.h"
13 #include "content/public/browser/native_web_keyboard_event.h"
14 #include "content/public/browser/web_ui_controller_factory.h"
15 #include "content/public/common/bindings_policy.h"
16 #include "content/public/common/page_zoom.h"
17 #include "content/public/common/url_constants.h"
18 #include "content/public/common/url_utils.h"
19 #include "content/public/renderer/document_state.h"
20 #include "content/public/renderer/history_item_serialization.h"
21 #include "content/public/renderer/navigation_state.h"
22 #include "content/public/test/render_view_test.h"
23 #include "content/renderer/render_view_impl.h"
24 #include "content/shell/browser/shell_content_browser_client.h"
25 #include "content/shell/common/shell_content_client.h"
26 #include "content/test/mock_keyboard.h"
27 #include "net/base/net_errors.h"
28 #include "net/cert/cert_status_flags.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "third_party/WebKit/public/platform/WebData.h"
31 #include "third_party/WebKit/public/platform/WebHTTPBody.h"
32 #include "third_party/WebKit/public/platform/WebString.h"
33 #include "third_party/WebKit/public/platform/WebURLError.h"
34 #include "third_party/WebKit/public/platform/WebURLResponse.h"
35 #include "third_party/WebKit/public/web/WebDataSource.h"
36 #include "third_party/WebKit/public/web/WebFrame.h"
37 #include "third_party/WebKit/public/web/WebHistoryItem.h"
38 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
39 #include "third_party/WebKit/public/web/WebView.h"
40 #include "third_party/WebKit/public/web/WebWindowFeatures.h"
41 #include "ui/events/keycodes/keyboard_codes.h"
42 #include "ui/gfx/codec/jpeg_codec.h"
43 #include "ui/gfx/range/range.h"
45 #if defined(OS_LINUX) && !defined(USE_AURA)
46 #include "ui/base/gtk/event_synthesis_gtk.h"
50 #include "ui/events/event.h"
53 #if defined(USE_AURA) && defined(USE_X11)
55 #include "ui/events/event_constants.h"
56 #include "ui/events/keycodes/keyboard_code_conversion.h"
57 #include "ui/events/x/events_x_utils.h"
60 using WebKit::WebFrame;
61 using WebKit::WebInputEvent;
62 using WebKit::WebMouseEvent;
63 using WebKit::WebRuntimeFeatures;
64 using WebKit::WebString;
65 using WebKit::WebTextDirection;
66 using WebKit::WebURLError;
72 #if defined(USE_AURA) && defined(USE_X11)
73 // Converts MockKeyboard::Modifiers to ui::EventFlags.
74 int ConvertMockKeyboardModifier(MockKeyboard::Modifiers modifiers) {
75 static struct ModifierMap {
76 MockKeyboard::Modifiers src;
79 { MockKeyboard::LEFT_SHIFT, ui::EF_SHIFT_DOWN },
80 { MockKeyboard::RIGHT_SHIFT, ui::EF_SHIFT_DOWN },
81 { MockKeyboard::LEFT_CONTROL, ui::EF_CONTROL_DOWN },
82 { MockKeyboard::RIGHT_CONTROL, ui::EF_CONTROL_DOWN },
83 { MockKeyboard::LEFT_ALT, ui::EF_ALT_DOWN },
84 { MockKeyboard::RIGHT_ALT, ui::EF_ALT_DOWN },
87 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kModifierMap); ++i) {
88 if (kModifierMap[i].src & modifiers) {
89 flags |= kModifierMap[i].dst;
96 class WebUITestWebUIControllerFactory : public WebUIControllerFactory {
98 virtual WebUIController* CreateWebUIControllerForURL(
99 WebUI* web_ui, const GURL& url) const OVERRIDE {
102 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
103 const GURL& url) const OVERRIDE {
104 return WebUI::kNoWebUI;
106 virtual bool UseWebUIForURL(BrowserContext* browser_context,
107 const GURL& url) const OVERRIDE {
108 return HasWebUIScheme(url);
110 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
111 const GURL& url) const OVERRIDE {
112 return HasWebUIScheme(url);
118 class RenderViewImplTest : public RenderViewTest {
120 RenderViewImplTest() {
121 // Attach a pseudo keyboard device to this object.
122 mock_keyboard_.reset(new MockKeyboard());
125 virtual ~RenderViewImplTest() {}
127 virtual void SetUp() OVERRIDE {
128 RenderViewTest::SetUp();
129 // This test depends on Blink flag InputModeAttribute, which is enabled
130 // under only test. Content browser test doesn't enable the feature so we
131 // need enable it manually.
132 // TODO(yoichio): Remove this if InputMode feature is enabled by default.
133 WebRuntimeFeatures::enableInputModeAttribute(true);
136 RenderViewImpl* view() {
137 return static_cast<RenderViewImpl*>(view_);
140 // Sends IPC messages that emulates a key-press event.
141 int SendKeyEvent(MockKeyboard::Layout layout,
143 MockKeyboard::Modifiers modifiers,
146 // Retrieve the Unicode character for the given tuple (keyboard-layout,
147 // key-code, and modifiers).
148 // Exit when a keyboard-layout driver cannot assign a Unicode character to
149 // the tuple to prevent sending an invalid key code to the RenderView
151 CHECK(mock_keyboard_.get());
153 int length = mock_keyboard_->GetCharacters(layout, key_code, modifiers,
158 // Create IPC messages from Windows messages and send them to our
160 // A keyboard event of Windows consists of three Windows messages:
161 // WM_KEYDOWN, WM_CHAR, and WM_KEYUP.
162 // WM_KEYDOWN and WM_KEYUP sends virtual-key codes. On the other hand,
163 // WM_CHAR sends a composed Unicode character.
164 MSG msg1 = { NULL, WM_KEYDOWN, key_code, 0 };
165 #if defined(USE_AURA)
166 ui::KeyEvent evt1(msg1, false);
167 NativeWebKeyboardEvent keydown_event(&evt1);
169 NativeWebKeyboardEvent keydown_event(msg1);
171 SendNativeKeyEvent(keydown_event);
173 MSG msg2 = { NULL, WM_CHAR, (*output)[0], 0 };
174 #if defined(USE_AURA)
175 ui::KeyEvent evt2(msg2, true);
176 NativeWebKeyboardEvent char_event(&evt2);
178 NativeWebKeyboardEvent char_event(msg2);
180 SendNativeKeyEvent(char_event);
182 MSG msg3 = { NULL, WM_KEYUP, key_code, 0 };
183 #if defined(USE_AURA)
184 ui::KeyEvent evt3(msg3, false);
185 NativeWebKeyboardEvent keyup_event(&evt3);
187 NativeWebKeyboardEvent keyup_event(msg3);
189 SendNativeKeyEvent(keyup_event);
192 #elif defined(USE_AURA) && defined(USE_X11)
193 // We ignore |layout|, which means we are only testing the layout of the
194 // current locale. TODO(mazda): fix this to respect |layout|.
196 const int flags = ConvertMockKeyboardModifier(modifiers);
199 InitXKeyEventForTesting(ui::ET_KEY_PRESSED,
200 static_cast<ui::KeyboardCode>(key_code),
203 ui::KeyEvent event1(&xevent1, false);
204 NativeWebKeyboardEvent keydown_event(&event1);
205 SendNativeKeyEvent(keydown_event);
208 InitXKeyEventForTesting(ui::ET_KEY_PRESSED,
209 static_cast<ui::KeyboardCode>(key_code),
212 ui::KeyEvent event2(&xevent2, true);
213 NativeWebKeyboardEvent char_event(&event2);
214 SendNativeKeyEvent(char_event);
217 InitXKeyEventForTesting(ui::ET_KEY_RELEASED,
218 static_cast<ui::KeyboardCode>(key_code),
221 ui::KeyEvent event3(&xevent3, false);
222 NativeWebKeyboardEvent keyup_event(&event3);
223 SendNativeKeyEvent(keyup_event);
225 long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
227 output->assign(1, static_cast<char16>(c));
229 #elif defined(TOOLKIT_GTK)
230 // We ignore |layout|, which means we are only testing the layout of the
231 // current locale. TODO(estade): fix this to respect |layout|.
232 std::vector<GdkEvent*> events;
233 ui::SynthesizeKeyPressEvents(
234 NULL, static_cast<ui::KeyboardCode>(key_code),
235 modifiers & (MockKeyboard::LEFT_CONTROL | MockKeyboard::RIGHT_CONTROL),
236 modifiers & (MockKeyboard::LEFT_SHIFT | MockKeyboard::RIGHT_SHIFT),
237 modifiers & (MockKeyboard::LEFT_ALT | MockKeyboard::RIGHT_ALT),
240 guint32 unicode_key = 0;
241 for (size_t i = 0; i < events.size(); ++i) {
242 // Only send the up/down events for key press itself (skip the up/down
243 // events for the modifier keys).
244 if ((i + 1) == (events.size() / 2) || i == (events.size() / 2)) {
245 unicode_key = gdk_keyval_to_unicode(events[i]->key.keyval);
246 NativeWebKeyboardEvent webkit_event(events[i]);
247 SendNativeKeyEvent(webkit_event);
249 // Need to add a char event after the key down.
250 if (webkit_event.type == WebKit::WebInputEvent::RawKeyDown) {
251 NativeWebKeyboardEvent char_event = webkit_event;
252 char_event.type = WebKit::WebInputEvent::Char;
253 char_event.skip_in_browser = true;
254 SendNativeKeyEvent(char_event);
257 gdk_event_free(events[i]);
260 output->assign(1, static_cast<char16>(unicode_key));
269 scoped_ptr<MockKeyboard> mock_keyboard_;
272 // Test that we get form state change notifications when input fields change.
273 TEST_F(RenderViewImplTest, DISABLED_OnNavStateChanged) {
274 // Don't want any delay for form state sync changes. This will still post a
275 // message so updates will get coalesced, but as soon as we spin the message
276 // loop, it will generate an update.
277 view()->set_send_content_state_immediately(true);
279 LoadHTML("<input type=\"text\" id=\"elt_text\"></input>");
281 // We should NOT have gotten a form state change notification yet.
282 EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
283 ViewHostMsg_UpdateState::ID));
284 render_thread_->sink().ClearMessages();
286 // Change the value of the input. We should have gotten an update state
287 // notification. We need to spin the message loop to catch this update.
288 ExecuteJavaScript("document.getElementById('elt_text').value = 'foo';");
289 ProcessPendingMessages();
290 EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
291 ViewHostMsg_UpdateState::ID));
294 TEST_F(RenderViewImplTest, OnNavigationHttpPost) {
295 ViewMsg_Navigate_Params nav_params;
297 // An http url will trigger a resource load so cannot be used here.
298 nav_params.url = GURL("data:text/html,<div>Page</div>");
299 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
300 nav_params.transition = PAGE_TRANSITION_TYPED;
301 nav_params.page_id = -1;
302 nav_params.is_post = true;
305 const unsigned char* raw_data = reinterpret_cast<const unsigned char*>(
307 const unsigned int length = 11;
308 const std::vector<unsigned char> post_data(raw_data, raw_data + length);
309 nav_params.browser_initiated_post_data = post_data;
311 view()->OnNavigate(nav_params);
312 ProcessPendingMessages();
314 const IPC::Message* frame_navigate_msg =
315 render_thread_->sink().GetUniqueMessageMatching(
316 ViewHostMsg_FrameNavigate::ID);
317 EXPECT_TRUE(frame_navigate_msg);
319 ViewHostMsg_FrameNavigate::Param host_nav_params;
320 ViewHostMsg_FrameNavigate::Read(frame_navigate_msg, &host_nav_params);
321 EXPECT_TRUE(host_nav_params.a.is_post);
323 // Check post data sent to browser matches
324 EXPECT_TRUE(host_nav_params.a.page_state.IsValid());
325 const WebKit::WebHistoryItem item = PageStateToHistoryItem(
326 host_nav_params.a.page_state);
327 WebKit::WebHTTPBody body = item.httpBody();
328 WebKit::WebHTTPBody::Element element;
329 bool successful = body.elementAt(0, element);
330 EXPECT_TRUE(successful);
331 EXPECT_EQ(WebKit::WebHTTPBody::Element::TypeData, element.type);
332 EXPECT_EQ(length, element.data.size());
333 EXPECT_EQ(0, memcmp(raw_data, element.data.data(), length));
336 TEST_F(RenderViewImplTest, DecideNavigationPolicy) {
337 WebUITestWebUIControllerFactory factory;
338 WebUIControllerFactory::RegisterFactory(&factory);
341 state.set_navigation_state(NavigationState::CreateContentInitiated());
343 // Navigations to normal HTTP URLs can be handled locally.
344 WebKit::WebURLRequest request(GURL("http://foo.com"));
345 WebKit::WebNavigationPolicy policy = view()->decidePolicyForNavigation(
349 WebKit::WebNavigationTypeLinkClicked,
350 WebKit::WebNavigationPolicyCurrentTab,
352 EXPECT_EQ(WebKit::WebNavigationPolicyCurrentTab, policy);
354 // Verify that form posts to WebUI URLs will be sent to the browser process.
355 WebKit::WebURLRequest form_request(GURL("chrome://foo"));
356 form_request.setHTTPMethod("POST");
357 policy = view()->decidePolicyForNavigation(
361 WebKit::WebNavigationTypeFormSubmitted,
362 WebKit::WebNavigationPolicyCurrentTab,
364 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy);
366 // Verify that popup links to WebUI URLs also are sent to browser.
367 WebKit::WebURLRequest popup_request(GURL("chrome://foo"));
368 policy = view()->decidePolicyForNavigation(
372 WebKit::WebNavigationTypeLinkClicked,
373 WebKit::WebNavigationPolicyNewForegroundTab,
375 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy);
378 TEST_F(RenderViewImplTest, DecideNavigationPolicyForWebUI) {
379 // Enable bindings to simulate a WebUI view.
380 view()->OnAllowBindings(BINDINGS_POLICY_WEB_UI);
383 state.set_navigation_state(NavigationState::CreateContentInitiated());
385 // Navigations to normal HTTP URLs will be sent to browser process.
386 WebKit::WebURLRequest request(GURL("http://foo.com"));
387 WebKit::WebNavigationPolicy policy = view()->decidePolicyForNavigation(
391 WebKit::WebNavigationTypeLinkClicked,
392 WebKit::WebNavigationPolicyCurrentTab,
394 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy);
396 // Navigations to WebUI URLs will also be sent to browser process.
397 WebKit::WebURLRequest webui_request(GURL("chrome://foo"));
398 policy = view()->decidePolicyForNavigation(
402 WebKit::WebNavigationTypeLinkClicked,
403 WebKit::WebNavigationPolicyCurrentTab,
405 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy);
407 // Verify that form posts to data URLs will be sent to the browser process.
408 WebKit::WebURLRequest data_request(GURL("data:text/html,foo"));
409 data_request.setHTTPMethod("POST");
410 policy = view()->decidePolicyForNavigation(
414 WebKit::WebNavigationTypeFormSubmitted,
415 WebKit::WebNavigationPolicyCurrentTab,
417 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy);
419 // Verify that a popup that creates a view first and then navigates to a
420 // normal HTTP URL will be sent to the browser process, even though the
421 // new view does not have any enabled_bindings_.
422 WebKit::WebURLRequest popup_request(GURL("http://foo.com"));
423 WebKit::WebView* new_web_view = view()->createView(
424 GetMainFrame(), popup_request, WebKit::WebWindowFeatures(), "foo",
425 WebKit::WebNavigationPolicyNewForegroundTab);
426 RenderViewImpl* new_view = RenderViewImpl::FromWebView(new_web_view);
427 policy = new_view->decidePolicyForNavigation(
428 new_web_view->mainFrame(),
431 WebKit::WebNavigationTypeLinkClicked,
432 WebKit::WebNavigationPolicyNewForegroundTab,
434 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy);
436 // Clean up after the new view so we don't leak it.
441 // Ensure the RenderViewImpl sends an ACK to a SwapOut request, even if it is
442 // already swapped out. http://crbug.com/93427.
443 TEST_F(RenderViewImplTest, SendSwapOutACK) {
444 LoadHTML("<div>Page A</div>");
445 int initial_page_id = view()->GetPageId();
447 // Respond to a swap out request.
450 // Ensure the swap out commits synchronously.
451 EXPECT_NE(initial_page_id, view()->GetPageId());
453 // Check for a valid OnSwapOutACK.
454 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
455 ViewHostMsg_SwapOut_ACK::ID);
458 // It is possible to get another swap out request. Ensure that we send
459 // an ACK, even if we don't have to do anything else.
460 render_thread_->sink().ClearMessages();
462 const IPC::Message* msg2 = render_thread_->sink().GetUniqueMessageMatching(
463 ViewHostMsg_SwapOut_ACK::ID);
466 // If we navigate back to this RenderView, ensure we don't send a state
467 // update for the swapped out URL. (http://crbug.com/72235)
468 ViewMsg_Navigate_Params nav_params;
469 nav_params.url = GURL("data:text/html,<div>Page B</div>");
470 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
471 nav_params.transition = PAGE_TRANSITION_TYPED;
472 nav_params.current_history_list_length = 1;
473 nav_params.current_history_list_offset = 0;
474 nav_params.pending_history_list_offset = 1;
475 nav_params.page_id = -1;
476 view()->OnNavigate(nav_params);
477 ProcessPendingMessages();
478 const IPC::Message* msg3 = render_thread_->sink().GetUniqueMessageMatching(
479 ViewHostMsg_UpdateState::ID);
483 // Ensure the RenderViewImpl reloads the previous page if a reload request
484 // arrives while it is showing swappedout://. http://crbug.com/143155.
485 TEST_F(RenderViewImplTest, ReloadWhileSwappedOut) {
487 LoadHTML("<div>Page A</div>");
489 // Load page B, which will trigger an UpdateState message for page A.
490 LoadHTML("<div>Page B</div>");
492 // Check for a valid UpdateState message for page A.
493 ProcessPendingMessages();
494 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
495 ViewHostMsg_UpdateState::ID);
499 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
500 EXPECT_EQ(1, page_id_A);
501 render_thread_->sink().ClearMessages();
503 // Back to page A (page_id 1) and commit.
504 ViewMsg_Navigate_Params params_A;
505 params_A.navigation_type = ViewMsg_Navigate_Type::NORMAL;
506 params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
507 params_A.current_history_list_length = 2;
508 params_A.current_history_list_offset = 1;
509 params_A.pending_history_list_offset = 0;
510 params_A.page_id = 1;
511 params_A.page_state = state_A;
512 view()->OnNavigate(params_A);
513 ProcessPendingMessages();
515 // Respond to a swap out request.
518 // Check for a OnSwapOutACK.
519 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
520 ViewHostMsg_SwapOut_ACK::ID);
522 render_thread_->sink().ClearMessages();
524 // It is possible to get a reload request at this point, containing the
525 // params.page_state of the initial page (e.g., if the new page fails the
526 // provisional load in the renderer process, after we unload the old page).
527 // Ensure the old page gets reloaded, not swappedout://.
528 ViewMsg_Navigate_Params nav_params;
529 nav_params.url = GURL("data:text/html,<div>Page A</div>");
530 nav_params.navigation_type = ViewMsg_Navigate_Type::RELOAD;
531 nav_params.transition = PAGE_TRANSITION_RELOAD;
532 nav_params.current_history_list_length = 2;
533 nav_params.current_history_list_offset = 0;
534 nav_params.pending_history_list_offset = 0;
535 nav_params.page_id = 1;
536 nav_params.page_state = state_A;
537 view()->OnNavigate(nav_params);
538 ProcessPendingMessages();
540 // Verify page A committed, not swappedout://.
541 const IPC::Message* frame_navigate_msg =
542 render_thread_->sink().GetUniqueMessageMatching(
543 ViewHostMsg_FrameNavigate::ID);
544 EXPECT_TRUE(frame_navigate_msg);
546 // Read URL out of the parent trait of the params object.
547 ViewHostMsg_FrameNavigate::Param commit_params;
548 ViewHostMsg_FrameNavigate::Read(frame_navigate_msg, &commit_params);
549 EXPECT_NE(GURL("swappedout://"), commit_params.a.url);
553 // Test that we get the correct UpdateState message when we go back twice
554 // quickly without committing. Regression test for http://crbug.com/58082.
555 // Disabled: http://crbug.com/157357 .
556 TEST_F(RenderViewImplTest, DISABLED_LastCommittedUpdateState) {
558 LoadHTML("<div>Page A</div>");
560 // Load page B, which will trigger an UpdateState message for page A.
561 LoadHTML("<div>Page B</div>");
563 // Check for a valid UpdateState message for page A.
564 ProcessPendingMessages();
565 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
566 ViewHostMsg_UpdateState::ID);
570 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
571 EXPECT_EQ(1, page_id_A);
572 render_thread_->sink().ClearMessages();
574 // Load page C, which will trigger an UpdateState message for page B.
575 LoadHTML("<div>Page C</div>");
577 // Check for a valid UpdateState for page B.
578 ProcessPendingMessages();
579 const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
580 ViewHostMsg_UpdateState::ID);
584 ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
585 EXPECT_EQ(2, page_id_B);
586 EXPECT_NE(state_A, state_B);
587 render_thread_->sink().ClearMessages();
589 // Load page D, which will trigger an UpdateState message for page C.
590 LoadHTML("<div>Page D</div>");
592 // Check for a valid UpdateState for page C.
593 ProcessPendingMessages();
594 const IPC::Message* msg_C = render_thread_->sink().GetUniqueMessageMatching(
595 ViewHostMsg_UpdateState::ID);
599 ViewHostMsg_UpdateState::Read(msg_C, &page_id_C, &state_C);
600 EXPECT_EQ(3, page_id_C);
601 EXPECT_NE(state_B, state_C);
602 render_thread_->sink().ClearMessages();
604 // Go back to C and commit, preparing for our real test.
605 ViewMsg_Navigate_Params params_C;
606 params_C.navigation_type = ViewMsg_Navigate_Type::NORMAL;
607 params_C.transition = PAGE_TRANSITION_FORWARD_BACK;
608 params_C.current_history_list_length = 4;
609 params_C.current_history_list_offset = 3;
610 params_C.pending_history_list_offset = 2;
611 params_C.page_id = 3;
612 params_C.page_state = state_C;
613 view()->OnNavigate(params_C);
614 ProcessPendingMessages();
615 render_thread_->sink().ClearMessages();
617 // Go back twice quickly, such that page B does not have a chance to commit.
618 // This leads to two changes to the back/forward list but only one change to
619 // the RenderView's page ID.
621 // Back to page B (page_id 2), without committing.
622 ViewMsg_Navigate_Params params_B;
623 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL;
624 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
625 params_B.current_history_list_length = 4;
626 params_B.current_history_list_offset = 2;
627 params_B.pending_history_list_offset = 1;
628 params_B.page_id = 2;
629 params_B.page_state = state_B;
630 view()->OnNavigate(params_B);
632 // Back to page A (page_id 1) and commit.
633 ViewMsg_Navigate_Params params;
634 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
635 params.transition = PAGE_TRANSITION_FORWARD_BACK;
636 params_B.current_history_list_length = 4;
637 params_B.current_history_list_offset = 2;
638 params_B.pending_history_list_offset = 0;
640 params.page_state = state_A;
641 view()->OnNavigate(params);
642 ProcessPendingMessages();
644 // Now ensure that the UpdateState message we receive is consistent
645 // and represents page C in both page_id and state.
646 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
647 ViewHostMsg_UpdateState::ID);
651 ViewHostMsg_UpdateState::Read(msg, &page_id, &state);
652 EXPECT_EQ(page_id_C, page_id);
653 EXPECT_NE(state_A, state);
654 EXPECT_NE(state_B, state);
655 EXPECT_EQ(state_C, state);
658 // Test that the history_page_ids_ list can reveal when a stale back/forward
659 // navigation arrives from the browser and can be ignored. See
660 // http://crbug.com/86758.
661 TEST_F(RenderViewImplTest, StaleNavigationsIgnored) {
663 LoadHTML("<div>Page A</div>");
664 EXPECT_EQ(1, view()->history_list_length_);
665 EXPECT_EQ(0, view()->history_list_offset_);
666 EXPECT_EQ(1, view()->history_page_ids_[0]);
668 // Load page B, which will trigger an UpdateState message for page A.
669 LoadHTML("<div>Page B</div>");
670 EXPECT_EQ(2, view()->history_list_length_);
671 EXPECT_EQ(1, view()->history_list_offset_);
672 EXPECT_EQ(2, view()->history_page_ids_[1]);
674 // Check for a valid UpdateState message for page A.
675 ProcessPendingMessages();
676 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
677 ViewHostMsg_UpdateState::ID);
681 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
682 EXPECT_EQ(1, page_id_A);
683 render_thread_->sink().ClearMessages();
685 // Back to page A (page_id 1) and commit.
686 ViewMsg_Navigate_Params params_A;
687 params_A.navigation_type = ViewMsg_Navigate_Type::NORMAL;
688 params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
689 params_A.current_history_list_length = 2;
690 params_A.current_history_list_offset = 1;
691 params_A.pending_history_list_offset = 0;
692 params_A.page_id = 1;
693 params_A.page_state = state_A;
694 view()->OnNavigate(params_A);
695 ProcessPendingMessages();
697 // A new navigation commits, clearing the forward history.
698 LoadHTML("<div>Page C</div>");
699 EXPECT_EQ(2, view()->history_list_length_);
700 EXPECT_EQ(1, view()->history_list_offset_);
701 EXPECT_EQ(3, view()->history_page_ids_[1]);
703 // The browser then sends a stale navigation to B, which should be ignored.
704 ViewMsg_Navigate_Params params_B;
705 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL;
706 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
707 params_B.current_history_list_length = 2;
708 params_B.current_history_list_offset = 0;
709 params_B.pending_history_list_offset = 1;
710 params_B.page_id = 2;
711 params_B.page_state = state_A; // Doesn't matter, just has to be present.
712 view()->OnNavigate(params_B);
714 // State should be unchanged.
715 EXPECT_EQ(2, view()->history_list_length_);
716 EXPECT_EQ(1, view()->history_list_offset_);
717 EXPECT_EQ(3, view()->history_page_ids_[1]);
720 // Test that we do not ignore navigations after the entry limit is reached,
721 // in which case the browser starts dropping entries from the front. In this
722 // case, we'll see a page_id mismatch but the RenderView's id will be older,
723 // not newer, than params.page_id. Use this as a cue that we should update the
724 // state and not treat it like a navigation to a cropped forward history item.
725 // See http://crbug.com/89798.
726 TEST_F(RenderViewImplTest, DontIgnoreBackAfterNavEntryLimit) {
728 LoadHTML("<div>Page A</div>");
729 EXPECT_EQ(1, view()->history_list_length_);
730 EXPECT_EQ(0, view()->history_list_offset_);
731 EXPECT_EQ(1, view()->history_page_ids_[0]);
733 // Load page B, which will trigger an UpdateState message for page A.
734 LoadHTML("<div>Page B</div>");
735 EXPECT_EQ(2, view()->history_list_length_);
736 EXPECT_EQ(1, view()->history_list_offset_);
737 EXPECT_EQ(2, view()->history_page_ids_[1]);
739 // Check for a valid UpdateState message for page A.
740 ProcessPendingMessages();
741 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
742 ViewHostMsg_UpdateState::ID);
746 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
747 EXPECT_EQ(1, page_id_A);
748 render_thread_->sink().ClearMessages();
750 // Load page C, which will trigger an UpdateState message for page B.
751 LoadHTML("<div>Page C</div>");
752 EXPECT_EQ(3, view()->history_list_length_);
753 EXPECT_EQ(2, view()->history_list_offset_);
754 EXPECT_EQ(3, view()->history_page_ids_[2]);
756 // Check for a valid UpdateState message for page B.
757 ProcessPendingMessages();
758 const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
759 ViewHostMsg_UpdateState::ID);
763 ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
764 EXPECT_EQ(2, page_id_B);
765 render_thread_->sink().ClearMessages();
767 // Suppose the browser has limited the number of NavigationEntries to 2.
768 // It has now dropped the first entry, but the renderer isn't notified.
769 // Ensure that going back to page B (page_id 2) at offset 0 is successful.
770 ViewMsg_Navigate_Params params_B;
771 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL;
772 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
773 params_B.current_history_list_length = 2;
774 params_B.current_history_list_offset = 1;
775 params_B.pending_history_list_offset = 0;
776 params_B.page_id = 2;
777 params_B.page_state = state_B;
778 view()->OnNavigate(params_B);
779 ProcessPendingMessages();
781 EXPECT_EQ(2, view()->history_list_length_);
782 EXPECT_EQ(0, view()->history_list_offset_);
783 EXPECT_EQ(2, view()->history_page_ids_[0]);
786 // Test that our IME backend sends a notification message when the input focus
788 TEST_F(RenderViewImplTest, OnImeTypeChanged) {
789 // Enable our IME backend code.
790 view()->OnSetInputMethodActive(true);
792 // Load an HTML page consisting of two input fields.
793 view()->set_send_content_state_immediately(true);
798 "<input id=\"test1\" type=\"text\" value=\"some text\"></input>"
799 "<input id=\"test2\" type=\"password\"></input>"
800 "<input id=\"test3\" type=\"text\" inputmode=\"verbatim\"></input>"
801 "<input id=\"test4\" type=\"text\" inputmode=\"latin\"></input>"
802 "<input id=\"test5\" type=\"text\" inputmode=\"latin-name\"></input>"
803 "<input id=\"test6\" type=\"text\" inputmode=\"latin-prose\">"
805 "<input id=\"test7\" type=\"text\" inputmode=\"full-width-latin\">"
807 "<input id=\"test8\" type=\"text\" inputmode=\"kana\"></input>"
808 "<input id=\"test9\" type=\"text\" inputmode=\"katakana\"></input>"
809 "<input id=\"test10\" type=\"text\" inputmode=\"numeric\"></input>"
810 "<input id=\"test11\" type=\"text\" inputmode=\"tel\"></input>"
811 "<input id=\"test12\" type=\"text\" inputmode=\"email\"></input>"
812 "<input id=\"test13\" type=\"text\" inputmode=\"url\"></input>"
813 "<input id=\"test14\" type=\"text\" inputmode=\"unknown\"></input>"
814 "<input id=\"test15\" type=\"text\" inputmode=\"verbatim\"></input>"
817 render_thread_->sink().ClearMessages();
819 struct InputModeTestCase {
820 const char* input_id;
821 ui::TextInputMode expected_mode;
823 static const InputModeTestCase kInputModeTestCases[] = {
824 {"test1", ui::TEXT_INPUT_MODE_DEFAULT},
825 {"test3", ui::TEXT_INPUT_MODE_VERBATIM},
826 {"test4", ui::TEXT_INPUT_MODE_LATIN},
827 {"test5", ui::TEXT_INPUT_MODE_LATIN_NAME},
828 {"test6", ui::TEXT_INPUT_MODE_LATIN_PROSE},
829 {"test7", ui::TEXT_INPUT_MODE_FULL_WIDTH_LATIN},
830 {"test8", ui::TEXT_INPUT_MODE_KANA},
831 {"test9", ui::TEXT_INPUT_MODE_KATAKANA},
832 {"test10", ui::TEXT_INPUT_MODE_NUMERIC},
833 {"test11", ui::TEXT_INPUT_MODE_TEL},
834 {"test12", ui::TEXT_INPUT_MODE_EMAIL},
835 {"test13", ui::TEXT_INPUT_MODE_URL},
836 {"test14", ui::TEXT_INPUT_MODE_DEFAULT},
837 {"test15", ui::TEXT_INPUT_MODE_VERBATIM},
840 const int kRepeatCount = 10;
841 for (int i = 0; i < kRepeatCount; i++) {
842 // Move the input focus to the first <input> element, where we should
844 ExecuteJavaScript("document.getElementById('test1').focus();");
845 ProcessPendingMessages();
846 render_thread_->sink().ClearMessages();
848 // Update the IME status and verify if our IME backend sends an IPC message
850 view()->UpdateTextInputType();
851 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
852 EXPECT_TRUE(msg != NULL);
853 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
854 ui::TextInputType type;
855 bool can_compose_inline = false;
856 ui::TextInputMode input_mode = ui::TEXT_INPUT_MODE_DEFAULT;
857 ViewHostMsg_TextInputTypeChanged::Read(msg,
860 &can_compose_inline);
861 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, type);
862 EXPECT_EQ(true, can_compose_inline);
864 // Move the input focus to the second <input> element, where we should
866 ExecuteJavaScript("document.getElementById('test2').focus();");
867 ProcessPendingMessages();
868 render_thread_->sink().ClearMessages();
870 // Update the IME status and verify if our IME backend sends an IPC message
871 // to de-activate IMEs.
872 view()->UpdateTextInputType();
873 msg = render_thread_->sink().GetMessageAt(0);
874 EXPECT_TRUE(msg != NULL);
875 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
876 ViewHostMsg_TextInputTypeChanged::Read(msg,
879 &can_compose_inline);
880 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, type);
882 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInputModeTestCases); i++) {
883 const InputModeTestCase* test_case = &kInputModeTestCases[i];
884 std::string javascript =
885 base::StringPrintf("document.getElementById('%s').focus();",
886 test_case->input_id);
887 // Move the input focus to the target <input> element, where we should
889 ExecuteJavaScriptAndReturnIntValue(ASCIIToUTF16(javascript), NULL);
890 ProcessPendingMessages();
891 render_thread_->sink().ClearMessages();
893 // Update the IME status and verify if our IME backend sends an IPC
894 // message to activate IMEs.
895 view()->UpdateTextInputType();
896 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
897 EXPECT_TRUE(msg != NULL);
898 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
899 ViewHostMsg_TextInputTypeChanged::Read(msg,
902 &can_compose_inline);
903 EXPECT_EQ(test_case->expected_mode, input_mode);
908 // Test that our IME backend can compose CJK words.
909 // Our IME front-end sends many platform-independent messages to the IME backend
910 // while it composes CJK words. This test sends the minimal messages captured
911 // on my local environment directly to the IME backend to verify if the backend
912 // can compose CJK words without any problems.
913 // This test uses an array of command sets because an IME composotion does not
914 // only depends on IME events, but also depends on window events, e.g. moving
915 // the window focus while composing a CJK text. To handle such complicated
916 // cases, this test should not only call IME-related functions in the
917 // RenderWidget class, but also call some RenderWidget members, e.g.
918 // ExecuteJavaScript(), RenderWidget::OnSetFocus(), etc.
919 TEST_F(RenderViewImplTest, ImeComposition) {
925 IME_CONFIRMCOMPOSITION,
926 IME_CANCELCOMPOSITION
933 const wchar_t* ime_string;
934 const wchar_t* result;
936 static const ImeMessage kImeMessages[] = {
937 // Scenario 1: input a Chinese word with Microsoft IME (on Vista).
938 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
939 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
940 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
941 {IME_SETCOMPOSITION, false, 1, 1, L"n", L"n"},
942 {IME_SETCOMPOSITION, false, 2, 2, L"ni", L"ni"},
943 {IME_SETCOMPOSITION, false, 3, 3, L"nih", L"nih"},
944 {IME_SETCOMPOSITION, false, 4, 4, L"niha", L"niha"},
945 {IME_SETCOMPOSITION, false, 5, 5, L"nihao", L"nihao"},
946 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"\x4F60\x597D", L"\x4F60\x597D"},
947 // Scenario 2: input a Japanese word with Microsoft IME (on Vista).
948 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
949 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
950 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
951 {IME_SETCOMPOSITION, false, 0, 1, L"\xFF4B", L"\xFF4B"},
952 {IME_SETCOMPOSITION, false, 0, 1, L"\x304B", L"\x304B"},
953 {IME_SETCOMPOSITION, false, 0, 2, L"\x304B\xFF4E", L"\x304B\xFF4E"},
954 {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\xFF4A",
955 L"\x304B\x3093\xFF4A"},
956 {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\x3058",
957 L"\x304B\x3093\x3058"},
958 {IME_SETCOMPOSITION, false, 0, 2, L"\x611F\x3058", L"\x611F\x3058"},
959 {IME_SETCOMPOSITION, false, 0, 2, L"\x6F22\x5B57", L"\x6F22\x5B57"},
960 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"},
961 {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"},
962 // Scenario 3: input a Korean word with Microsot IME (on Vista).
963 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
964 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
965 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
966 {IME_SETCOMPOSITION, false, 0, 1, L"\x3147", L"\x3147"},
967 {IME_SETCOMPOSITION, false, 0, 1, L"\xC544", L"\xC544"},
968 {IME_SETCOMPOSITION, false, 0, 1, L"\xC548", L"\xC548"},
969 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\xC548"},
970 {IME_SETCOMPOSITION, false, 0, 1, L"\x3134", L"\xC548\x3134"},
971 {IME_SETCOMPOSITION, false, 0, 1, L"\xB140", L"\xC548\xB140"},
972 {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"},
973 {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\xC548"},
974 {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"},
975 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\xC548\xB155"},
978 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kImeMessages); i++) {
979 const ImeMessage* ime_message = &kImeMessages[i];
980 switch (ime_message->command) {
982 // Load an HTML page consisting of a content-editable <div> element,
983 // and move the input focus to the <div> element, where we can use
985 view()->OnSetInputMethodActive(ime_message->enable);
986 view()->set_send_content_state_immediately(true);
991 "<div id=\"test1\" contenteditable=\"true\"></div>"
994 ExecuteJavaScript("document.getElementById('test1').focus();");
997 case IME_SETINPUTMODE:
998 // Activate (or deactivate) our IME back-end.
999 view()->OnSetInputMethodActive(ime_message->enable);
1003 // Update the window focus.
1004 view()->OnSetFocus(ime_message->enable);
1007 case IME_SETCOMPOSITION:
1008 view()->OnImeSetComposition(
1009 WideToUTF16Hack(ime_message->ime_string),
1010 std::vector<WebKit::WebCompositionUnderline>(),
1011 ime_message->selection_start,
1012 ime_message->selection_end);
1015 case IME_CONFIRMCOMPOSITION:
1016 view()->OnImeConfirmComposition(
1017 WideToUTF16Hack(ime_message->ime_string),
1018 gfx::Range::InvalidRange(),
1022 case IME_CANCELCOMPOSITION:
1023 view()->OnImeSetComposition(
1025 std::vector<WebKit::WebCompositionUnderline>(),
1030 // Update the status of our IME back-end.
1031 // TODO(hbono): we should verify messages to be sent from the back-end.
1032 view()->UpdateTextInputType();
1033 ProcessPendingMessages();
1034 render_thread_->sink().ClearMessages();
1036 if (ime_message->result) {
1037 // Retrieve the content of this page and compare it with the expected
1039 const int kMaxOutputCharacters = 128;
1040 std::wstring output = UTF16ToWideHack(
1041 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1042 EXPECT_EQ(output, ime_message->result);
1047 // Test that the RenderView::OnSetTextDirection() function can change the text
1048 // direction of the selected input element.
1049 TEST_F(RenderViewImplTest, OnSetTextDirection) {
1050 // Load an HTML page consisting of a <textarea> element and a <div> element.
1051 // This test changes the text direction of the <textarea> element, and
1052 // writes the values of its 'dir' attribute and its 'direction' property to
1053 // verify that the text direction is changed.
1054 view()->set_send_content_state_immediately(true);
1059 "<textarea id=\"test\"></textarea>"
1060 "<div id=\"result\" contenteditable=\"true\"></div>"
1063 render_thread_->sink().ClearMessages();
1065 static const struct {
1066 WebTextDirection direction;
1067 const wchar_t* expected_result;
1068 } kTextDirection[] = {
1069 { WebKit::WebTextDirectionRightToLeft, L"\x000A" L"rtl,rtl" },
1070 { WebKit::WebTextDirectionLeftToRight, L"\x000A" L"ltr,ltr" },
1072 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTextDirection); ++i) {
1073 // Set the text direction of the <textarea> element.
1074 ExecuteJavaScript("document.getElementById('test').focus();");
1075 view()->OnSetTextDirection(kTextDirection[i].direction);
1077 // Write the values of its DOM 'dir' attribute and its CSS 'direction'
1078 // property to the <div> element.
1079 ExecuteJavaScript("var result = document.getElementById('result');"
1080 "var node = document.getElementById('test');"
1081 "var style = getComputedStyle(node, null);"
1082 "result.innerText ="
1083 " node.getAttribute('dir') + ',' +"
1084 " style.getPropertyValue('direction');");
1086 // Copy the document content to std::wstring and compare with the
1088 const int kMaxOutputCharacters = 16;
1089 std::wstring output = UTF16ToWideHack(
1090 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1091 EXPECT_EQ(output, kTextDirection[i].expected_result);
1095 // see http://crbug.com/238750
1097 #define MAYBE_OnHandleKeyboardEvent DISABLED_OnHandleKeyboardEvent
1099 #define MAYBE_OnHandleKeyboardEvent OnHandleKeyboardEvent
1102 // Test that we can receive correct DOM events when we send input events
1103 // through the RenderWidget::OnHandleInputEvent() function.
1104 TEST_F(RenderViewImplTest, MAYBE_OnHandleKeyboardEvent) {
1105 #if !defined(OS_MACOSX)
1106 // Load an HTML page consisting of one <input> element and three
1107 // contentediable <div> elements.
1108 // The <input> element is used for sending keyboard events, and the <div>
1109 // elements are used for writing DOM events in the following format:
1110 // "<keyCode>,<shiftKey>,<controlKey>,<altKey>".
1111 // TODO(hbono): <http://crbug.com/2215> Our WebKit port set |ev.metaKey| to
1112 // true when pressing an alt key, i.e. the |ev.metaKey| value is not
1113 // trustworthy. We will check the |ev.metaKey| value when this issue is fixed.
1114 view()->set_send_content_state_immediately(true);
1118 "<script type='text/javascript' language='javascript'>"
1119 "function OnKeyEvent(ev) {"
1120 " var result = document.getElementById(ev.type);"
1121 " result.innerText ="
1122 " (ev.which || ev.keyCode) + ',' +"
1123 " ev.shiftKey + ',' +"
1124 " ev.ctrlKey + ',' +"
1131 "<input id='test' type='text'"
1132 " onkeydown='return OnKeyEvent(event);'"
1133 " onkeypress='return OnKeyEvent(event);'"
1134 " onkeyup='return OnKeyEvent(event);'>"
1136 "<div id='keydown' contenteditable='true'>"
1138 "<div id='keypress' contenteditable='true'>"
1140 "<div id='keyup' contenteditable='true'>"
1144 ExecuteJavaScript("document.getElementById('test').focus();");
1145 render_thread_->sink().ClearMessages();
1147 static const MockKeyboard::Layout kLayouts[] = {
1149 // Since we ignore the mock keyboard layout on Linux and instead just use
1150 // the screen's keyboard layout, these trivially pass. They are commented
1151 // out to avoid the illusion that they work.
1152 MockKeyboard::LAYOUT_ARABIC,
1153 MockKeyboard::LAYOUT_CANADIAN_FRENCH,
1154 MockKeyboard::LAYOUT_FRENCH,
1155 MockKeyboard::LAYOUT_HEBREW,
1156 MockKeyboard::LAYOUT_RUSSIAN,
1158 MockKeyboard::LAYOUT_UNITED_STATES,
1161 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) {
1162 // For each key code, we send three keyboard events.
1163 // * we press only the key;
1164 // * we press the key and a left-shift key, and;
1165 // * we press the key and a right-alt (AltGr) key.
1166 // For each modifiers, we need a string used for formatting its expected
1167 // result. (See the above comment for its format.)
1168 static const struct {
1169 MockKeyboard::Modifiers modifiers;
1170 const char* expected_result;
1171 } kModifierData[] = {
1172 {MockKeyboard::NONE, "false,false,false"},
1173 {MockKeyboard::LEFT_SHIFT, "true,false,false"},
1175 {MockKeyboard::RIGHT_ALT, "false,false,true"},
1179 MockKeyboard::Layout layout = kLayouts[i];
1180 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifierData); ++j) {
1181 // Virtual key codes used for this test.
1182 static const int kKeyCodes[] = {
1183 '0', '1', '2', '3', '4', '5', '6', '7',
1184 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1185 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1186 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
1192 ui::VKEY_OEM_PERIOD,
1200 // Not sure how to handle this key on Linux.
1205 MockKeyboard::Modifiers modifiers = kModifierData[j].modifiers;
1206 for (size_t k = 0; k < ARRAYSIZE_UNSAFE(kKeyCodes); ++k) {
1207 // Send a keyboard event to the RenderView object.
1208 // We should test a keyboard event only when the given keyboard-layout
1209 // driver is installed in a PC and the driver can assign a Unicode
1210 // charcter for the given tuple (key-code and modifiers).
1211 int key_code = kKeyCodes[k];
1213 if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0)
1216 // Create an expected result from the virtual-key code, the character
1217 // code, and the modifier-key status.
1218 // We format a string that emulates a DOM-event string produced hy
1219 // our JavaScript function. (See the above comment for the format.)
1220 static char expected_result[1024];
1221 expected_result[0] = 0;
1222 base::snprintf(&expected_result[0],
1223 sizeof(expected_result),
1224 "\n" // texts in the <input> element
1225 "%d,%s\n" // texts in the first <div> element
1226 "%d,%s\n" // texts in the second <div> element
1227 "%d,%s", // texts in the third <div> element
1228 key_code, kModifierData[j].expected_result,
1229 static_cast<int>(char_code[0]),
1230 kModifierData[j].expected_result,
1231 key_code, kModifierData[j].expected_result);
1233 // Retrieve the text in the test page and compare it with the expected
1234 // text created from a virtual-key code, a character code, and the
1235 // modifier-key status.
1236 const int kMaxOutputCharacters = 1024;
1237 std::string output = UTF16ToUTF8(
1238 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1239 EXPECT_EQ(expected_result, output);
1248 // Test that our EditorClientImpl class can insert characters when we send
1249 // keyboard events through the RenderWidget::OnHandleInputEvent() function.
1250 // This test is for preventing regressions caused only when we use non-US
1251 // keyboards, such as Issue 10846.
1252 // see http://crbug.com/244562
1254 #define MAYBE_InsertCharacters DISABLED_InsertCharacters
1256 #define MAYBE_InsertCharacters InsertCharacters
1258 TEST_F(RenderViewImplTest, MAYBE_InsertCharacters) {
1259 #if !defined(OS_MACOSX)
1260 static const struct {
1261 MockKeyboard::Layout layout;
1262 const wchar_t* expected_result;
1265 // Disabled these keyboard layouts because buildbots do not have their
1266 // keyboard-layout drivers installed.
1267 {MockKeyboard::LAYOUT_ARABIC,
1268 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1269 L"\x0038\x0039\x0634\x0624\x064a\x062b\x0628\x0644"
1270 L"\x0627\x0647\x062a\x0646\x0645\x0629\x0649\x062e"
1271 L"\x062d\x0636\x0642\x0633\x0641\x0639\x0631\x0635"
1272 L"\x0621\x063a\x0626\x0643\x003d\x0648\x002d\x0632"
1273 L"\x0638\x0630\x062c\x005c\x062f\x0637\x0028\x0021"
1274 L"\x0040\x0023\x0024\x0025\x005e\x0026\x002a\x0029"
1275 L"\x0650\x007d\x005d\x064f\x005b\x0623\x00f7\x0640"
1276 L"\x060c\x002f\x2019\x0622\x00d7\x061b\x064e\x064c"
1277 L"\x064d\x2018\x007b\x064b\x0652\x0625\x007e\x003a"
1278 L"\x002b\x002c\x005f\x002e\x061f\x0651\x003c\x007c"
1279 L"\x003e\x0022\x0030\x0031\x0032\x0033\x0034\x0035"
1280 L"\x0036\x0037\x0038\x0039\x0634\x0624\x064a\x062b"
1281 L"\x0628\x0644\x0627\x0647\x062a\x0646\x0645\x0629"
1282 L"\x0649\x062e\x062d\x0636\x0642\x0633\x0641\x0639"
1283 L"\x0631\x0635\x0621\x063a\x0626\x0643\x003d\x0648"
1284 L"\x002d\x0632\x0638\x0630\x062c\x005c\x062f\x0637"
1286 {MockKeyboard::LAYOUT_HEBREW,
1287 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1288 L"\x0038\x0039\x05e9\x05e0\x05d1\x05d2\x05e7\x05db"
1289 L"\x05e2\x05d9\x05df\x05d7\x05dc\x05da\x05e6\x05de"
1290 L"\x05dd\x05e4\x002f\x05e8\x05d3\x05d0\x05d5\x05d4"
1291 L"\x0027\x05e1\x05d8\x05d6\x05e3\x003d\x05ea\x002d"
1292 L"\x05e5\x002e\x003b\x005d\x005c\x005b\x002c\x0028"
1293 L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a"
1294 L"\x0029\x0041\x0042\x0043\x0044\x0045\x0046\x0047"
1295 L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f"
1296 L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057"
1297 L"\x0058\x0059\x005a\x003a\x002b\x003e\x005f\x003c"
1298 L"\x003f\x007e\x007d\x007c\x007b\x0022\x0030\x0031"
1299 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1300 L"\x05e9\x05e0\x05d1\x05d2\x05e7\x05db\x05e2\x05d9"
1301 L"\x05df\x05d7\x05dc\x05da\x05e6\x05de\x05dd\x05e4"
1302 L"\x002f\x05e8\x05d3\x05d0\x05d5\x05d4\x0027\x05e1"
1303 L"\x05d8\x05d6\x05e3\x003d\x05ea\x002d\x05e5\x002e"
1304 L"\x003b\x005d\x005c\x005b\x002c"
1308 // On Linux, the only way to test alternate keyboard layouts is to change
1309 // the keyboard layout of the whole screen. I'm worried about the side
1310 // effects this may have on the buildbots.
1311 {MockKeyboard::LAYOUT_CANADIAN_FRENCH,
1312 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1313 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1314 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1315 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1316 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1317 L"\x002e\x00e9\x003c\x0029\x0021\x0022\x002f\x0024"
1318 L"\x0025\x003f\x0026\x002a\x0028\x0041\x0042\x0043"
1319 L"\x0044\x0045\x0046\x0047\x0048\x0049\x004a\x004b"
1320 L"\x004c\x004d\x004e\x004f\x0050\x0051\x0052\x0053"
1321 L"\x0054\x0055\x0056\x0057\x0058\x0059\x005a\x003a"
1322 L"\x002b\x0027\x005f\x002e\x00c9\x003e\x0030\x0031"
1323 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1324 L"\x0061\x0062\x0063\x0064\x0065\x0066\x0067\x0068"
1325 L"\x0069\x006a\x006b\x006c\x006d\x006e\x006f\x0070"
1326 L"\x0071\x0072\x0073\x0074\x0075\x0076\x0077\x0078"
1327 L"\x0079\x007a\x003b\x003d\x002c\x002d\x002e\x00e9"
1330 {MockKeyboard::LAYOUT_FRENCH,
1331 L"\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d\x00e8"
1332 L"\x005f\x00e7\x0061\x0062\x0063\x0064\x0065\x0066"
1333 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1334 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1335 L"\x0077\x0078\x0079\x007a\x0024\x003d\x002c\x003b"
1336 L"\x003a\x00f9\x0029\x002a\x0021\x0030\x0031\x0032"
1337 L"\x0033\x0034\x0035\x0036\x0037\x0038\x0039\x0041"
1338 L"\x0042\x0043\x0044\x0045\x0046\x0047\x0048\x0049"
1339 L"\x004a\x004b\x004c\x004d\x004e\x004f\x0050\x0051"
1340 L"\x0052\x0053\x0054\x0055\x0056\x0057\x0058\x0059"
1341 L"\x005a\x00a3\x002b\x003f\x002e\x002f\x0025\x00b0"
1342 L"\x00b5\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d"
1343 L"\x00e8\x005f\x00e7\x0061\x0062\x0063\x0064\x0065"
1344 L"\x0066\x0067\x0068\x0069\x006a\x006b\x006c\x006d"
1345 L"\x006e\x006f\x0070\x0071\x0072\x0073\x0074\x0075"
1346 L"\x0076\x0077\x0078\x0079\x007a\x0024\x003d\x002c"
1347 L"\x003b\x003a\x00f9\x0029\x002a\x0021"
1349 {MockKeyboard::LAYOUT_RUSSIAN,
1350 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1351 L"\x0038\x0039\x0444\x0438\x0441\x0432\x0443\x0430"
1352 L"\x043f\x0440\x0448\x043e\x043b\x0434\x044c\x0442"
1353 L"\x0449\x0437\x0439\x043a\x044b\x0435\x0433\x043c"
1354 L"\x0446\x0447\x043d\x044f\x0436\x003d\x0431\x002d"
1355 L"\x044e\x002e\x0451\x0445\x005c\x044a\x044d\x0029"
1356 L"\x0021\x0022\x2116\x003b\x0025\x003a\x003f\x002a"
1357 L"\x0028\x0424\x0418\x0421\x0412\x0423\x0410\x041f"
1358 L"\x0420\x0428\x041e\x041b\x0414\x042c\x0422\x0429"
1359 L"\x0417\x0419\x041a\x042b\x0415\x0413\x041c\x0426"
1360 L"\x0427\x041d\x042f\x0416\x002b\x0411\x005f\x042e"
1361 L"\x002c\x0401\x0425\x002f\x042a\x042d\x0030\x0031"
1362 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1363 L"\x0444\x0438\x0441\x0432\x0443\x0430\x043f\x0440"
1364 L"\x0448\x043e\x043b\x0434\x044c\x0442\x0449\x0437"
1365 L"\x0439\x043a\x044b\x0435\x0433\x043c\x0446\x0447"
1366 L"\x043d\x044f\x0436\x003d\x0431\x002d\x044e\x002e"
1367 L"\x0451\x0445\x005c\x044a\x044d"
1369 #endif // defined(OS_WIN)
1370 {MockKeyboard::LAYOUT_UNITED_STATES,
1371 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1372 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1373 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1374 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1375 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1376 L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027\x0029"
1377 L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a"
1378 L"\x0028\x0041\x0042\x0043\x0044\x0045\x0046\x0047"
1379 L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f"
1380 L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057"
1381 L"\x0058\x0059\x005a\x003a\x002b\x003c\x005f\x003e"
1382 L"\x003f\x007e\x007b\x007c\x007d\x0022"
1384 // This is ifdefed out for Linux to correspond to the fact that we don't
1385 // test alt+keystroke for now.
1386 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1387 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1388 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1389 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1390 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1391 L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027"
1396 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) {
1397 // Load an HTML page consisting of one <div> element.
1398 // This <div> element is used by the EditorClientImpl class to insert
1399 // characters received through the RenderWidget::OnHandleInputEvent()
1401 view()->set_send_content_state_immediately(true);
1407 "<div id='test' contenteditable='true'>"
1411 ExecuteJavaScript("document.getElementById('test').focus();");
1412 render_thread_->sink().ClearMessages();
1414 // For each key code, we send three keyboard events.
1415 // * Pressing only the key;
1416 // * Pressing the key and a left-shift key, and;
1417 // * Pressing the key and a right-alt (AltGr) key.
1418 static const MockKeyboard::Modifiers kModifiers[] = {
1420 MockKeyboard::LEFT_SHIFT,
1422 MockKeyboard::RIGHT_ALT,
1426 MockKeyboard::Layout layout = kLayouts[i].layout;
1427 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifiers); ++j) {
1428 // Virtual key codes used for this test.
1429 static const int kKeyCodes[] = {
1430 '0', '1', '2', '3', '4', '5', '6', '7',
1431 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1432 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1433 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
1439 ui::VKEY_OEM_PERIOD,
1447 // Unclear how to handle this on Linux.
1452 MockKeyboard::Modifiers modifiers = kModifiers[j];
1453 for (size_t k = 0; k < ARRAYSIZE_UNSAFE(kKeyCodes); ++k) {
1454 // Send a keyboard event to the RenderView object.
1455 // We should test a keyboard event only when the given keyboard-layout
1456 // driver is installed in a PC and the driver can assign a Unicode
1457 // charcter for the given tuple (layout, key-code, and modifiers).
1458 int key_code = kKeyCodes[k];
1460 if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0)
1465 // Retrieve the text in the test page and compare it with the expected
1466 // text created from a virtual-key code, a character code, and the
1467 // modifier-key status.
1468 const int kMaxOutputCharacters = 4096;
1469 std::wstring output = UTF16ToWideHack(
1470 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1471 EXPECT_EQ(kLayouts[i].expected_result, output);
1478 // Crashy, http://crbug.com/53247.
1479 TEST_F(RenderViewImplTest, DISABLED_DidFailProvisionalLoadWithErrorForError) {
1480 GetMainFrame()->enableViewSourceMode(true);
1482 error.domain = WebString::fromUTF8(net::kErrorDomain);
1483 error.reason = net::ERR_FILE_NOT_FOUND;
1484 error.unreachableURL = GURL("http://foo");
1485 WebFrame* web_frame = GetMainFrame();
1487 // Start a load that will reach provisional state synchronously,
1488 // but won't complete synchronously.
1489 ViewMsg_Navigate_Params params;
1490 params.page_id = -1;
1491 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1492 params.url = GURL("data:text/html,test data");
1493 view()->OnNavigate(params);
1495 // An error occurred.
1496 view()->didFailProvisionalLoad(web_frame, error);
1497 // Frame should exit view-source mode.
1498 EXPECT_FALSE(web_frame->isViewSourceModeEnabled());
1501 TEST_F(RenderViewImplTest, DidFailProvisionalLoadWithErrorForCancellation) {
1502 GetMainFrame()->enableViewSourceMode(true);
1504 error.domain = WebString::fromUTF8(net::kErrorDomain);
1505 error.reason = net::ERR_ABORTED;
1506 error.unreachableURL = GURL("http://foo");
1507 WebFrame* web_frame = GetMainFrame();
1509 // Start a load that will reach provisional state synchronously,
1510 // but won't complete synchronously.
1511 ViewMsg_Navigate_Params params;
1512 params.page_id = -1;
1513 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1514 params.url = GURL("data:text/html,test data");
1515 view()->OnNavigate(params);
1517 // A cancellation occurred.
1518 view()->didFailProvisionalLoad(web_frame, error);
1519 // Frame should stay in view-source mode.
1520 EXPECT_TRUE(web_frame->isViewSourceModeEnabled());
1523 // Regression test for http://crbug.com/41562
1524 TEST_F(RenderViewImplTest, UpdateTargetURLWithInvalidURL) {
1525 const GURL invalid_gurl("http://");
1526 view()->setMouseOverURL(WebKit::WebURL(invalid_gurl));
1527 EXPECT_EQ(invalid_gurl, view()->target_url_);
1530 TEST_F(RenderViewImplTest, SetHistoryLengthAndPrune) {
1531 int expected_page_id = -1;
1533 // No history to merge and no committed pages.
1534 view()->OnSetHistoryLengthAndPrune(0, -1);
1535 EXPECT_EQ(0, view()->history_list_length_);
1536 EXPECT_EQ(-1, view()->history_list_offset_);
1538 // History to merge and no committed pages.
1539 view()->OnSetHistoryLengthAndPrune(2, -1);
1540 EXPECT_EQ(2, view()->history_list_length_);
1541 EXPECT_EQ(1, view()->history_list_offset_);
1542 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1543 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1546 // No history to merge and a committed page to be kept.
1547 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1548 expected_page_id = view()->page_id_;
1549 view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
1550 EXPECT_EQ(1, view()->history_list_length_);
1551 EXPECT_EQ(0, view()->history_list_offset_);
1552 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1555 // No history to merge and a committed page to be pruned.
1556 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1557 expected_page_id = view()->page_id_;
1558 view()->OnSetHistoryLengthAndPrune(0, expected_page_id + 1);
1559 EXPECT_EQ(0, view()->history_list_length_);
1560 EXPECT_EQ(-1, view()->history_list_offset_);
1563 // No history to merge and a committed page that the browser was unaware of.
1564 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1565 expected_page_id = view()->page_id_;
1566 view()->OnSetHistoryLengthAndPrune(0, -1);
1567 EXPECT_EQ(1, view()->history_list_length_);
1568 EXPECT_EQ(0, view()->history_list_offset_);
1569 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1572 // History to merge and a committed page to be kept.
1573 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1574 expected_page_id = view()->page_id_;
1575 view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
1576 EXPECT_EQ(3, view()->history_list_length_);
1577 EXPECT_EQ(2, view()->history_list_offset_);
1578 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1579 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1580 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1583 // History to merge and a committed page to be pruned.
1584 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1585 expected_page_id = view()->page_id_;
1586 view()->OnSetHistoryLengthAndPrune(2, expected_page_id + 1);
1587 EXPECT_EQ(2, view()->history_list_length_);
1588 EXPECT_EQ(1, view()->history_list_offset_);
1589 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1590 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1593 // History to merge and a committed page that the browser was unaware of.
1594 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1595 expected_page_id = view()->page_id_;
1596 view()->OnSetHistoryLengthAndPrune(2, -1);
1597 EXPECT_EQ(3, view()->history_list_length_);
1598 EXPECT_EQ(2, view()->history_list_offset_);
1599 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1600 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1601 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1604 int expected_page_id_2 = -1;
1606 // No history to merge and two committed pages, both to be kept.
1607 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1608 expected_page_id = view()->page_id_;
1609 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1610 expected_page_id_2 = view()->page_id_;
1611 EXPECT_GT(expected_page_id_2, expected_page_id);
1612 view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
1613 EXPECT_EQ(2, view()->history_list_length_);
1614 EXPECT_EQ(1, view()->history_list_offset_);
1615 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1616 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
1619 // No history to merge and two committed pages, and only the second is kept.
1620 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1621 expected_page_id = view()->page_id_;
1622 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1623 expected_page_id_2 = view()->page_id_;
1624 EXPECT_GT(expected_page_id_2, expected_page_id);
1625 view()->OnSetHistoryLengthAndPrune(0, expected_page_id_2);
1626 EXPECT_EQ(1, view()->history_list_length_);
1627 EXPECT_EQ(0, view()->history_list_offset_);
1628 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[0]);
1631 // No history to merge and two committed pages, both of which the browser was
1633 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1634 expected_page_id = view()->page_id_;
1635 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1636 expected_page_id_2 = view()->page_id_;
1637 EXPECT_GT(expected_page_id_2, expected_page_id);
1638 view()->OnSetHistoryLengthAndPrune(0, -1);
1639 EXPECT_EQ(2, view()->history_list_length_);
1640 EXPECT_EQ(1, view()->history_list_offset_);
1641 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1642 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
1645 // History to merge and two committed pages, both to be kept.
1646 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1647 expected_page_id = view()->page_id_;
1648 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1649 expected_page_id_2 = view()->page_id_;
1650 EXPECT_GT(expected_page_id_2, expected_page_id);
1651 view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
1652 EXPECT_EQ(4, view()->history_list_length_);
1653 EXPECT_EQ(3, view()->history_list_offset_);
1654 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1655 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1656 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1657 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
1660 // History to merge and two committed pages, and only the second is kept.
1661 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1662 expected_page_id = view()->page_id_;
1663 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1664 expected_page_id_2 = view()->page_id_;
1665 EXPECT_GT(expected_page_id_2, expected_page_id);
1666 view()->OnSetHistoryLengthAndPrune(2, expected_page_id_2);
1667 EXPECT_EQ(3, view()->history_list_length_);
1668 EXPECT_EQ(2, view()->history_list_offset_);
1669 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1670 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1671 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[2]);
1674 // History to merge and two committed pages, both of which the browser was
1676 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1677 expected_page_id = view()->page_id_;
1678 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1679 expected_page_id_2 = view()->page_id_;
1680 EXPECT_GT(expected_page_id_2, expected_page_id);
1681 view()->OnSetHistoryLengthAndPrune(2, -1);
1682 EXPECT_EQ(4, view()->history_list_length_);
1683 EXPECT_EQ(3, view()->history_list_offset_);
1684 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1685 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1686 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1687 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
1690 TEST_F(RenderViewImplTest, ContextMenu) {
1691 LoadHTML("<div>Page A</div>");
1693 // Create a right click in the center of the iframe. (I'm hoping this will
1694 // make this a bit more robust in case of some other formatting or other bug.)
1695 WebMouseEvent mouse_event;
1696 mouse_event.type = WebInputEvent::MouseDown;
1697 mouse_event.button = WebMouseEvent::ButtonRight;
1698 mouse_event.x = 250;
1699 mouse_event.y = 250;
1700 mouse_event.globalX = 250;
1701 mouse_event.globalY = 250;
1703 SendWebMouseEvent(mouse_event);
1705 // Now simulate the corresponding up event which should display the menu
1706 mouse_event.type = WebInputEvent::MouseUp;
1707 SendWebMouseEvent(mouse_event);
1709 EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
1710 ViewHostMsg_ContextMenu::ID));
1713 TEST_F(RenderViewImplTest, TestBackForward) {
1714 LoadHTML("<div id=pagename>Page A</div>");
1715 WebKit::WebHistoryItem page_a_item = GetMainFrame()->currentHistoryItem();
1716 int was_page_a = -1;
1717 string16 check_page_a =
1719 "Number(document.getElementById('pagename').innerHTML == 'Page A')");
1720 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
1721 EXPECT_EQ(1, was_page_a);
1723 LoadHTML("<div id=pagename>Page B</div>");
1724 int was_page_b = -1;
1725 string16 check_page_b =
1727 "Number(document.getElementById('pagename').innerHTML == 'Page B')");
1728 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1729 EXPECT_EQ(1, was_page_b);
1731 LoadHTML("<div id=pagename>Page C</div>");
1732 int was_page_c = -1;
1733 string16 check_page_c =
1735 "Number(document.getElementById('pagename').innerHTML == 'Page C')");
1736 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
1737 EXPECT_EQ(1, was_page_b);
1739 WebKit::WebHistoryItem forward_item = GetMainFrame()->currentHistoryItem();
1740 GoBack(GetMainFrame()->previousHistoryItem());
1741 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1742 EXPECT_EQ(1, was_page_b);
1744 GoForward(forward_item);
1745 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
1746 EXPECT_EQ(1, was_page_c);
1748 GoBack(GetMainFrame()->previousHistoryItem());
1749 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1750 EXPECT_EQ(1, was_page_b);
1752 forward_item = GetMainFrame()->currentHistoryItem();
1753 GoBack(page_a_item);
1754 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
1755 EXPECT_EQ(1, was_page_a);
1757 GoForward(forward_item);
1758 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1759 EXPECT_EQ(1, was_page_b);
1762 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
1763 TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) {
1766 // http://crbug.com/304193
1767 if (base::win::GetVersion() < base::win::VERSION_VISTA)
1771 LoadHTML("<textarea id=\"test\"></textarea>");
1772 ExecuteJavaScript("document.getElementById('test').focus();");
1774 const string16 empty_string = UTF8ToUTF16("");
1775 const std::vector<WebKit::WebCompositionUnderline> empty_underline;
1776 std::vector<gfx::Rect> bounds;
1777 view()->OnSetFocus(true);
1778 view()->OnSetInputMethodActive(true);
1780 // ASCII composition
1781 const string16 ascii_composition = UTF8ToUTF16("aiueo");
1782 view()->OnImeSetComposition(ascii_composition, empty_underline, 0, 0);
1783 view()->GetCompositionCharacterBounds(&bounds);
1784 ASSERT_EQ(ascii_composition.size(), bounds.size());
1785 for (size_t i = 0; i < bounds.size(); ++i)
1786 EXPECT_LT(0, bounds[i].width());
1787 view()->OnImeConfirmComposition(
1788 empty_string, gfx::Range::InvalidRange(), false);
1790 // Non surrogate pair unicode character.
1791 const string16 unicode_composition = UTF8ToUTF16(
1792 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
1793 view()->OnImeSetComposition(unicode_composition, empty_underline, 0, 0);
1794 view()->GetCompositionCharacterBounds(&bounds);
1795 ASSERT_EQ(unicode_composition.size(), bounds.size());
1796 for (size_t i = 0; i < bounds.size(); ++i)
1797 EXPECT_LT(0, bounds[i].width());
1798 view()->OnImeConfirmComposition(
1799 empty_string, gfx::Range::InvalidRange(), false);
1801 // Surrogate pair character.
1802 const string16 surrogate_pair_char = UTF8ToUTF16("\xF0\xA0\xAE\x9F");
1803 view()->OnImeSetComposition(surrogate_pair_char,
1807 view()->GetCompositionCharacterBounds(&bounds);
1808 ASSERT_EQ(surrogate_pair_char.size(), bounds.size());
1809 EXPECT_LT(0, bounds[0].width());
1810 EXPECT_EQ(0, bounds[1].width());
1811 view()->OnImeConfirmComposition(
1812 empty_string, gfx::Range::InvalidRange(), false);
1815 const string16 surrogate_pair_mixed_composition =
1816 surrogate_pair_char + UTF8ToUTF16("\xE3\x81\x82") + surrogate_pair_char +
1817 UTF8ToUTF16("b") + surrogate_pair_char;
1818 const size_t utf16_length = 8UL;
1819 const bool is_surrogate_pair_empty_rect[8] = {
1820 false, true, false, false, true, false, false, true };
1821 view()->OnImeSetComposition(surrogate_pair_mixed_composition,
1825 view()->GetCompositionCharacterBounds(&bounds);
1826 ASSERT_EQ(utf16_length, bounds.size());
1827 for (size_t i = 0; i < utf16_length; ++i) {
1828 if (is_surrogate_pair_empty_rect[i]) {
1829 EXPECT_EQ(0, bounds[i].width());
1831 EXPECT_LT(0, bounds[i].width());
1834 view()->OnImeConfirmComposition(
1835 empty_string, gfx::Range::InvalidRange(), false);
1839 TEST_F(RenderViewImplTest, ZoomLimit) {
1840 const double kMinZoomLevel = ZoomFactorToZoomLevel(kMinimumZoomFactor);
1841 const double kMaxZoomLevel = ZoomFactorToZoomLevel(kMaximumZoomFactor);
1843 ViewMsg_Navigate_Params params;
1844 params.page_id = -1;
1845 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1847 // Verifies navigation to a URL with preset zoom level indeed sets the level.
1848 // Regression test for http://crbug.com/139559, where the level was not
1849 // properly set when it is out of the default zoom limits of WebView.
1850 params.url = GURL("data:text/html,min_zoomlimit_test");
1851 view()->OnSetZoomLevelForLoadingURL(params.url, kMinZoomLevel);
1852 view()->OnNavigate(params);
1853 ProcessPendingMessages();
1854 EXPECT_DOUBLE_EQ(kMinZoomLevel, view()->GetWebView()->zoomLevel());
1856 // It should work even when the zoom limit is temporarily changed in the page.
1857 view()->GetWebView()->zoomLimitsChanged(ZoomFactorToZoomLevel(1.0),
1858 ZoomFactorToZoomLevel(1.0));
1859 params.url = GURL("data:text/html,max_zoomlimit_test");
1860 view()->OnSetZoomLevelForLoadingURL(params.url, kMaxZoomLevel);
1861 view()->OnNavigate(params);
1862 ProcessPendingMessages();
1863 EXPECT_DOUBLE_EQ(kMaxZoomLevel, view()->GetWebView()->zoomLevel());
1866 TEST_F(RenderViewImplTest, SetEditableSelectionAndComposition) {
1867 // Load an HTML page consisting of an input field.
1872 "<input id=\"test1\" value=\"some test text hello\"></input>"
1875 ExecuteJavaScript("document.getElementById('test1').focus();");
1876 view()->OnSetEditableSelectionOffsets(4, 8);
1877 const std::vector<WebKit::WebCompositionUnderline> empty_underline;
1878 view()->OnSetCompositionFromExistingText(7, 10, empty_underline);
1879 WebKit::WebTextInputInfo info = view()->webview()->textInputInfo();
1880 EXPECT_EQ(4, info.selectionStart);
1881 EXPECT_EQ(8, info.selectionEnd);
1882 EXPECT_EQ(7, info.compositionStart);
1883 EXPECT_EQ(10, info.compositionEnd);
1884 view()->OnUnselect();
1885 info = view()->webview()->textInputInfo();
1886 EXPECT_EQ(0, info.selectionStart);
1887 EXPECT_EQ(0, info.selectionEnd);
1891 TEST_F(RenderViewImplTest, OnExtendSelectionAndDelete) {
1892 // Load an HTML page consisting of an input field.
1897 "<input id=\"test1\" value=\"abcdefghijklmnopqrstuvwxyz\"></input>"
1900 ExecuteJavaScript("document.getElementById('test1').focus();");
1901 view()->OnSetEditableSelectionOffsets(10, 10);
1902 view()->OnExtendSelectionAndDelete(3, 4);
1903 WebKit::WebTextInputInfo info = view()->webview()->textInputInfo();
1904 EXPECT_EQ("abcdefgopqrstuvwxyz", info.value);
1905 EXPECT_EQ(7, info.selectionStart);
1906 EXPECT_EQ(7, info.selectionEnd);
1907 view()->OnSetEditableSelectionOffsets(4, 8);
1908 view()->OnExtendSelectionAndDelete(2, 5);
1909 info = view()->webview()->textInputInfo();
1910 EXPECT_EQ("abuvwxyz", info.value);
1911 EXPECT_EQ(2, info.selectionStart);
1912 EXPECT_EQ(2, info.selectionEnd);
1915 // Test that the navigating specific frames works correctly.
1916 TEST_F(RenderViewImplTest, NavigateFrame) {
1918 LoadHTML("hello <iframe srcdoc='fail' name='frame'></iframe>");
1920 // Navigate the frame only.
1921 ViewMsg_Navigate_Params nav_params;
1922 nav_params.url = GURL("data:text/html,world");
1923 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1924 nav_params.transition = PAGE_TRANSITION_TYPED;
1925 nav_params.current_history_list_length = 1;
1926 nav_params.current_history_list_offset = 0;
1927 nav_params.pending_history_list_offset = 1;
1928 nav_params.page_id = -1;
1929 nav_params.frame_to_navigate = "frame";
1930 view()->OnNavigate(nav_params);
1931 ProcessPendingMessages();
1933 // Copy the document content to std::wstring and compare with the
1935 const int kMaxOutputCharacters = 256;
1936 std::wstring output = UTF16ToWideHack(
1937 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1938 EXPECT_EQ(output, L"hello \n\nworld");
1941 // This test ensures that a RenderFrame object is created for the top level
1942 // frame in the RenderView.
1943 TEST_F(RenderViewImplTest, BasicRenderFrame) {
1944 EXPECT_TRUE(view()->main_render_frame_.get());
1947 TEST_F(RenderViewImplTest, GetSSLStatusOfFrame) {
1948 LoadHTML("<!DOCTYPE html><html><body></body></html>");
1950 WebFrame* frame = GetMainFrame();
1951 SSLStatus ssl_status = view()->GetSSLStatusOfFrame(frame);
1952 EXPECT_FALSE(net::IsCertStatusError(ssl_status.cert_status));
1954 const_cast<WebKit::WebURLResponse&>(frame->dataSource()->response()).
1956 SerializeSecurityInfo(0, net::CERT_STATUS_ALL_ERRORS, 0, 0));
1957 ssl_status = view()->GetSSLStatusOfFrame(frame);
1958 EXPECT_TRUE(net::IsCertStatusError(ssl_status.cert_status));
1961 class SuppressErrorPageTest : public RenderViewTest {
1963 virtual void SetUp() OVERRIDE {
1964 SetRendererClientForTesting(&client_);
1965 RenderViewTest::SetUp();
1968 RenderViewImpl* view() {
1969 return static_cast<RenderViewImpl*>(view_);
1973 class TestContentRendererClient : public ContentRendererClient {
1975 virtual bool ShouldSuppressErrorPage(const GURL& url) OVERRIDE {
1976 return url == GURL("http://example.com/suppress");
1979 virtual void GetNavigationErrorStrings(
1980 WebKit::WebFrame* frame,
1981 const WebKit::WebURLRequest& failed_request,
1982 const WebKit::WebURLError& error,
1983 const std::string& accept_languages,
1984 std::string* error_html,
1985 string16* error_description) OVERRIDE {
1987 *error_html = "A suffusion of yellow.";
1991 TestContentRendererClient client_;
1994 #if defined(OS_ANDROID)
1995 // Crashing on Android: http://crbug.com/311341
1996 #define MAYBE_Suppresses DISABLED_Suppresses
1998 #define MAYBE_Suppresses Suppresses
2001 TEST_F(SuppressErrorPageTest, MAYBE_Suppresses) {
2003 error.domain = WebString::fromUTF8(net::kErrorDomain);
2004 error.reason = net::ERR_FILE_NOT_FOUND;
2005 error.unreachableURL = GURL("http://example.com/suppress");
2006 WebFrame* web_frame = GetMainFrame();
2008 // Start a load that will reach provisional state synchronously,
2009 // but won't complete synchronously.
2010 ViewMsg_Navigate_Params params;
2011 params.page_id = -1;
2012 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
2013 params.url = GURL("data:text/html,test data");
2014 view()->OnNavigate(params);
2016 // An error occurred.
2017 view()->didFailProvisionalLoad(web_frame, error);
2018 const int kMaxOutputCharacters = 22;
2019 EXPECT_EQ("", UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
2022 #if defined(OS_ANDROID)
2023 // Crashing on Android: http://crbug.com/311341
2024 #define MAYBE_DoesNotSuppress DISABLED_DoesNotSuppress
2026 #define MAYBE_DoesNotSuppress DoesNotSuppress
2029 TEST_F(SuppressErrorPageTest, MAYBE_DoesNotSuppress) {
2031 error.domain = WebString::fromUTF8(net::kErrorDomain);
2032 error.reason = net::ERR_FILE_NOT_FOUND;
2033 error.unreachableURL = GURL("http://example.com/dont-suppress");
2034 WebFrame* web_frame = GetMainFrame();
2036 // Start a load that will reach provisional state synchronously,
2037 // but won't complete synchronously.
2038 ViewMsg_Navigate_Params params;
2039 params.page_id = -1;
2040 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
2041 params.url = GURL("data:text/html,test data");
2042 view()->OnNavigate(params);
2044 // An error occurred.
2045 view()->didFailProvisionalLoad(web_frame, error);
2046 ProcessPendingMessages();
2047 const int kMaxOutputCharacters = 22;
2048 EXPECT_EQ("A suffusion of yellow.",
2049 UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
2052 } // namespace content