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/test/events_test_utils_x11.h"
60 #if defined(USE_OZONE)
61 #include "ui/events/keycodes/keyboard_code_conversion.h"
64 using blink::WebFrame;
65 using blink::WebInputEvent;
66 using blink::WebMouseEvent;
67 using blink::WebRuntimeFeatures;
68 using blink::WebString;
69 using blink::WebTextDirection;
70 using blink::WebURLError;
76 #if (defined(USE_AURA) && defined(USE_X11)) || defined(USE_OZONE)
77 // Converts MockKeyboard::Modifiers to ui::EventFlags.
78 int ConvertMockKeyboardModifier(MockKeyboard::Modifiers modifiers) {
79 static struct ModifierMap {
80 MockKeyboard::Modifiers src;
83 { MockKeyboard::LEFT_SHIFT, ui::EF_SHIFT_DOWN },
84 { MockKeyboard::RIGHT_SHIFT, ui::EF_SHIFT_DOWN },
85 { MockKeyboard::LEFT_CONTROL, ui::EF_CONTROL_DOWN },
86 { MockKeyboard::RIGHT_CONTROL, ui::EF_CONTROL_DOWN },
87 { MockKeyboard::LEFT_ALT, ui::EF_ALT_DOWN },
88 { MockKeyboard::RIGHT_ALT, ui::EF_ALT_DOWN },
91 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kModifierMap); ++i) {
92 if (kModifierMap[i].src & modifiers) {
93 flags |= kModifierMap[i].dst;
100 class WebUITestWebUIControllerFactory : public WebUIControllerFactory {
102 virtual WebUIController* CreateWebUIControllerForURL(
103 WebUI* web_ui, const GURL& url) const OVERRIDE {
106 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
107 const GURL& url) const OVERRIDE {
108 return WebUI::kNoWebUI;
110 virtual bool UseWebUIForURL(BrowserContext* browser_context,
111 const GURL& url) const OVERRIDE {
112 return HasWebUIScheme(url);
114 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
115 const GURL& url) const OVERRIDE {
116 return HasWebUIScheme(url);
122 class RenderViewImplTest : public RenderViewTest {
124 RenderViewImplTest() {
125 // Attach a pseudo keyboard device to this object.
126 mock_keyboard_.reset(new MockKeyboard());
129 virtual ~RenderViewImplTest() {}
131 virtual void SetUp() OVERRIDE {
132 RenderViewTest::SetUp();
133 // Enable Blink's experimental and test only features so that test code
134 // does not have to bother enabling each feature.
135 WebRuntimeFeatures::enableExperimentalFeatures(true);
136 WebRuntimeFeatures::enableTestOnlyFeatures(true);
139 RenderViewImpl* view() {
140 return static_cast<RenderViewImpl*>(view_);
143 RenderFrameImpl* frame() {
144 return static_cast<RenderFrameImpl*>(view()->GetMainRenderFrame());
147 // Sends IPC messages that emulates a key-press event.
148 int SendKeyEvent(MockKeyboard::Layout layout,
150 MockKeyboard::Modifiers modifiers,
151 base::string16* output) {
153 // Retrieve the Unicode character for the given tuple (keyboard-layout,
154 // key-code, and modifiers).
155 // Exit when a keyboard-layout driver cannot assign a Unicode character to
156 // the tuple to prevent sending an invalid key code to the RenderView
158 CHECK(mock_keyboard_.get());
160 int length = mock_keyboard_->GetCharacters(layout, key_code, modifiers,
165 // Create IPC messages from Windows messages and send them to our
167 // A keyboard event of Windows consists of three Windows messages:
168 // WM_KEYDOWN, WM_CHAR, and WM_KEYUP.
169 // WM_KEYDOWN and WM_KEYUP sends virtual-key codes. On the other hand,
170 // WM_CHAR sends a composed Unicode character.
171 MSG msg1 = { NULL, WM_KEYDOWN, key_code, 0 };
172 #if defined(USE_AURA)
173 ui::KeyEvent evt1(msg1, false);
174 NativeWebKeyboardEvent keydown_event(&evt1);
176 NativeWebKeyboardEvent keydown_event(msg1);
178 SendNativeKeyEvent(keydown_event);
180 MSG msg2 = { NULL, WM_CHAR, (*output)[0], 0 };
181 #if defined(USE_AURA)
182 ui::KeyEvent evt2(msg2, true);
183 NativeWebKeyboardEvent char_event(&evt2);
185 NativeWebKeyboardEvent char_event(msg2);
187 SendNativeKeyEvent(char_event);
189 MSG msg3 = { NULL, WM_KEYUP, key_code, 0 };
190 #if defined(USE_AURA)
191 ui::KeyEvent evt3(msg3, false);
192 NativeWebKeyboardEvent keyup_event(&evt3);
194 NativeWebKeyboardEvent keyup_event(msg3);
196 SendNativeKeyEvent(keyup_event);
199 #elif defined(USE_AURA) && defined(USE_X11)
200 // We ignore |layout|, which means we are only testing the layout of the
201 // current locale. TODO(mazda): fix this to respect |layout|.
203 const int flags = ConvertMockKeyboardModifier(modifiers);
205 ui::ScopedXI2Event xevent;
206 xevent.InitKeyEvent(ui::ET_KEY_PRESSED,
207 static_cast<ui::KeyboardCode>(key_code),
209 ui::KeyEvent event1(xevent, false);
210 NativeWebKeyboardEvent keydown_event(&event1);
211 SendNativeKeyEvent(keydown_event);
213 xevent.InitKeyEvent(ui::ET_KEY_PRESSED,
214 static_cast<ui::KeyboardCode>(key_code),
216 ui::KeyEvent event2(xevent, true);
217 NativeWebKeyboardEvent char_event(&event2);
218 SendNativeKeyEvent(char_event);
220 xevent.InitKeyEvent(ui::ET_KEY_RELEASED,
221 static_cast<ui::KeyboardCode>(key_code),
223 ui::KeyEvent event3(xevent, false);
224 NativeWebKeyboardEvent keyup_event(&event3);
225 SendNativeKeyEvent(keyup_event);
227 long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
229 output->assign(1, static_cast<base::char16>(c));
231 #elif defined(USE_OZONE)
232 const int flags = ConvertMockKeyboardModifier(modifiers);
234 // Ozone's native events are ui::Events. So first create the "native" event,
235 // then create the actual ui::KeyEvent with the native event.
236 ui::KeyEvent keydown_native_event(ui::ET_KEY_PRESSED,
237 static_cast<ui::KeyboardCode>(key_code),
240 ui::KeyEvent keydown_event(&keydown_native_event, false);
241 NativeWebKeyboardEvent keydown_web_event(&keydown_event);
242 SendNativeKeyEvent(keydown_web_event);
244 ui::KeyEvent char_native_event(ui::ET_KEY_PRESSED,
245 static_cast<ui::KeyboardCode>(key_code),
248 ui::KeyEvent char_event(&char_native_event, true);
249 NativeWebKeyboardEvent char_web_event(&char_event);
250 SendNativeKeyEvent(char_web_event);
252 ui::KeyEvent keyup_native_event(ui::ET_KEY_RELEASED,
253 static_cast<ui::KeyboardCode>(key_code),
256 ui::KeyEvent keyup_event(&keyup_native_event, false);
257 NativeWebKeyboardEvent keyup_web_event(&keyup_event);
258 SendNativeKeyEvent(keyup_web_event);
260 long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
262 output->assign(1, static_cast<base::char16>(c));
264 #elif defined(TOOLKIT_GTK)
265 // We ignore |layout|, which means we are only testing the layout of the
266 // current locale. TODO(estade): fix this to respect |layout|.
267 std::vector<GdkEvent*> events;
268 ui::SynthesizeKeyPressEvents(
269 NULL, static_cast<ui::KeyboardCode>(key_code),
270 modifiers & (MockKeyboard::LEFT_CONTROL | MockKeyboard::RIGHT_CONTROL),
271 modifiers & (MockKeyboard::LEFT_SHIFT | MockKeyboard::RIGHT_SHIFT),
272 modifiers & (MockKeyboard::LEFT_ALT | MockKeyboard::RIGHT_ALT),
275 guint32 unicode_key = 0;
276 for (size_t i = 0; i < events.size(); ++i) {
277 // Only send the up/down events for key press itself (skip the up/down
278 // events for the modifier keys).
279 if ((i + 1) == (events.size() / 2) || i == (events.size() / 2)) {
280 unicode_key = gdk_keyval_to_unicode(events[i]->key.keyval);
281 NativeWebKeyboardEvent webkit_event(events[i]);
282 SendNativeKeyEvent(webkit_event);
284 // Need to add a char event after the key down.
285 if (webkit_event.type == blink::WebInputEvent::RawKeyDown) {
286 NativeWebKeyboardEvent char_event = webkit_event;
287 char_event.type = blink::WebInputEvent::Char;
288 char_event.skip_in_browser = true;
289 SendNativeKeyEvent(char_event);
292 gdk_event_free(events[i]);
295 output->assign(1, static_cast<base::char16>(unicode_key));
304 scoped_ptr<MockKeyboard> mock_keyboard_;
307 // Test that we get form state change notifications when input fields change.
308 TEST_F(RenderViewImplTest, DISABLED_OnNavStateChanged) {
309 // Don't want any delay for form state sync changes. This will still post a
310 // message so updates will get coalesced, but as soon as we spin the message
311 // loop, it will generate an update.
312 view()->set_send_content_state_immediately(true);
314 LoadHTML("<input type=\"text\" id=\"elt_text\"></input>");
316 // We should NOT have gotten a form state change notification yet.
317 EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
318 ViewHostMsg_UpdateState::ID));
319 render_thread_->sink().ClearMessages();
321 // Change the value of the input. We should have gotten an update state
322 // notification. We need to spin the message loop to catch this update.
323 ExecuteJavaScript("document.getElementById('elt_text').value = 'foo';");
324 ProcessPendingMessages();
325 EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
326 ViewHostMsg_UpdateState::ID));
329 TEST_F(RenderViewImplTest, OnNavigationHttpPost) {
330 ViewMsg_Navigate_Params nav_params;
332 // An http url will trigger a resource load so cannot be used here.
333 nav_params.url = GURL("data:text/html,<div>Page</div>");
334 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
335 nav_params.transition = PAGE_TRANSITION_TYPED;
336 nav_params.page_id = -1;
337 nav_params.is_post = true;
340 const unsigned char* raw_data = reinterpret_cast<const unsigned char*>(
342 const unsigned int length = 11;
343 const std::vector<unsigned char> post_data(raw_data, raw_data + length);
344 nav_params.browser_initiated_post_data = post_data;
346 view()->OnNavigate(nav_params);
347 ProcessPendingMessages();
349 const IPC::Message* frame_navigate_msg =
350 render_thread_->sink().GetUniqueMessageMatching(
351 ViewHostMsg_FrameNavigate::ID);
352 EXPECT_TRUE(frame_navigate_msg);
354 ViewHostMsg_FrameNavigate::Param host_nav_params;
355 ViewHostMsg_FrameNavigate::Read(frame_navigate_msg, &host_nav_params);
356 EXPECT_TRUE(host_nav_params.a.is_post);
358 // Check post data sent to browser matches
359 EXPECT_TRUE(host_nav_params.a.page_state.IsValid());
360 const blink::WebHistoryItem item = PageStateToHistoryItem(
361 host_nav_params.a.page_state);
362 blink::WebHTTPBody body = item.httpBody();
363 blink::WebHTTPBody::Element element;
364 bool successful = body.elementAt(0, element);
365 EXPECT_TRUE(successful);
366 EXPECT_EQ(blink::WebHTTPBody::Element::TypeData, element.type);
367 EXPECT_EQ(length, element.data.size());
368 EXPECT_EQ(0, memcmp(raw_data, element.data.data(), length));
371 TEST_F(RenderViewImplTest, DecideNavigationPolicy) {
372 WebUITestWebUIControllerFactory factory;
373 WebUIControllerFactory::RegisterFactory(&factory);
376 state.set_navigation_state(NavigationState::CreateContentInitiated());
378 // Navigations to normal HTTP URLs can be handled locally.
379 blink::WebURLRequest request(GURL("http://foo.com"));
380 blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
384 blink::WebNavigationTypeLinkClicked,
385 blink::WebNavigationPolicyCurrentTab,
387 EXPECT_EQ(blink::WebNavigationPolicyCurrentTab, policy);
389 // Verify that form posts to WebUI URLs will be sent to the browser process.
390 blink::WebURLRequest form_request(GURL("chrome://foo"));
391 form_request.setHTTPMethod("POST");
392 policy = frame()->decidePolicyForNavigation(
396 blink::WebNavigationTypeFormSubmitted,
397 blink::WebNavigationPolicyCurrentTab,
399 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
401 // Verify that popup links to WebUI URLs also are sent to browser.
402 blink::WebURLRequest popup_request(GURL("chrome://foo"));
403 policy = frame()->decidePolicyForNavigation(
407 blink::WebNavigationTypeLinkClicked,
408 blink::WebNavigationPolicyNewForegroundTab,
410 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
413 TEST_F(RenderViewImplTest, DecideNavigationPolicyHandlesAllTopLevel) {
415 state.set_navigation_state(NavigationState::CreateContentInitiated());
417 RendererPreferences prefs = view()->renderer_preferences();
418 prefs.browser_handles_all_top_level_requests = true;
419 view()->OnSetRendererPrefs(prefs);
421 const blink::WebNavigationType kNavTypes[] = {
422 blink::WebNavigationTypeLinkClicked,
423 blink::WebNavigationTypeFormSubmitted,
424 blink::WebNavigationTypeBackForward,
425 blink::WebNavigationTypeReload,
426 blink::WebNavigationTypeFormResubmitted,
427 blink::WebNavigationTypeOther,
430 blink::WebURLRequest request(GURL("http://foo.com"));
431 for (size_t i = 0; i < arraysize(kNavTypes); ++i) {
432 blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
437 blink::WebNavigationPolicyCurrentTab,
439 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
443 TEST_F(RenderViewImplTest, DecideNavigationPolicyForWebUI) {
444 // Enable bindings to simulate a WebUI view.
445 view()->OnAllowBindings(BINDINGS_POLICY_WEB_UI);
448 state.set_navigation_state(NavigationState::CreateContentInitiated());
450 // Navigations to normal HTTP URLs will be sent to browser process.
451 blink::WebURLRequest request(GURL("http://foo.com"));
452 blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
456 blink::WebNavigationTypeLinkClicked,
457 blink::WebNavigationPolicyCurrentTab,
459 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
461 // Navigations to WebUI URLs will also be sent to browser process.
462 blink::WebURLRequest webui_request(GURL("chrome://foo"));
463 policy = frame()->decidePolicyForNavigation(
467 blink::WebNavigationTypeLinkClicked,
468 blink::WebNavigationPolicyCurrentTab,
470 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
472 // Verify that form posts to data URLs will be sent to the browser process.
473 blink::WebURLRequest data_request(GURL("data:text/html,foo"));
474 data_request.setHTTPMethod("POST");
475 policy = frame()->decidePolicyForNavigation(
479 blink::WebNavigationTypeFormSubmitted,
480 blink::WebNavigationPolicyCurrentTab,
482 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
484 // Verify that a popup that creates a view first and then navigates to a
485 // normal HTTP URL will be sent to the browser process, even though the
486 // new view does not have any enabled_bindings_.
487 blink::WebURLRequest popup_request(GURL("http://foo.com"));
488 blink::WebView* new_web_view = view()->createView(
489 GetMainFrame(), popup_request, blink::WebWindowFeatures(), "foo",
490 blink::WebNavigationPolicyNewForegroundTab, false);
491 RenderViewImpl* new_view = RenderViewImpl::FromWebView(new_web_view);
492 policy = static_cast<RenderFrameImpl*>(new_view->GetMainRenderFrame())->
493 decidePolicyForNavigation(
494 new_web_view->mainFrame(),
497 blink::WebNavigationTypeLinkClicked,
498 blink::WebNavigationPolicyNewForegroundTab,
500 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
502 // Clean up after the new view so we don't leak it.
507 // Ensure the RenderViewImpl sends an ACK to a SwapOut request, even if it is
508 // already swapped out. http://crbug.com/93427.
509 TEST_F(RenderViewImplTest, SendSwapOutACK) {
510 LoadHTML("<div>Page A</div>");
511 int initial_page_id = view()->GetPageId();
513 // Respond to a swap out request.
516 // Ensure the swap out commits synchronously.
517 EXPECT_NE(initial_page_id, view()->GetPageId());
519 // Check for a valid OnSwapOutACK.
520 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
521 ViewHostMsg_SwapOut_ACK::ID);
524 // It is possible to get another swap out request. Ensure that we send
525 // an ACK, even if we don't have to do anything else.
526 render_thread_->sink().ClearMessages();
528 const IPC::Message* msg2 = render_thread_->sink().GetUniqueMessageMatching(
529 ViewHostMsg_SwapOut_ACK::ID);
532 // If we navigate back to this RenderView, ensure we don't send a state
533 // update for the swapped out URL. (http://crbug.com/72235)
534 ViewMsg_Navigate_Params nav_params;
535 nav_params.url = GURL("data:text/html,<div>Page B</div>");
536 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
537 nav_params.transition = PAGE_TRANSITION_TYPED;
538 nav_params.current_history_list_length = 1;
539 nav_params.current_history_list_offset = 0;
540 nav_params.pending_history_list_offset = 1;
541 nav_params.page_id = -1;
542 view()->OnNavigate(nav_params);
543 ProcessPendingMessages();
544 const IPC::Message* msg3 = render_thread_->sink().GetUniqueMessageMatching(
545 ViewHostMsg_UpdateState::ID);
549 // Ensure the RenderViewImpl reloads the previous page if a reload request
550 // arrives while it is showing swappedout://. http://crbug.com/143155.
551 TEST_F(RenderViewImplTest, ReloadWhileSwappedOut) {
553 LoadHTML("<div>Page A</div>");
555 // Load page B, which will trigger an UpdateState message for page A.
556 LoadHTML("<div>Page B</div>");
558 // Check for a valid UpdateState message for page A.
559 ProcessPendingMessages();
560 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
561 ViewHostMsg_UpdateState::ID);
565 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
566 EXPECT_EQ(1, page_id_A);
567 render_thread_->sink().ClearMessages();
569 // Back to page A (page_id 1) and commit.
570 ViewMsg_Navigate_Params params_A;
571 params_A.navigation_type = ViewMsg_Navigate_Type::NORMAL;
572 params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
573 params_A.current_history_list_length = 2;
574 params_A.current_history_list_offset = 1;
575 params_A.pending_history_list_offset = 0;
576 params_A.page_id = 1;
577 params_A.page_state = state_A;
578 view()->OnNavigate(params_A);
579 ProcessPendingMessages();
581 // Respond to a swap out request.
584 // Check for a OnSwapOutACK.
585 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
586 ViewHostMsg_SwapOut_ACK::ID);
588 render_thread_->sink().ClearMessages();
590 // It is possible to get a reload request at this point, containing the
591 // params.page_state of the initial page (e.g., if the new page fails the
592 // provisional load in the renderer process, after we unload the old page).
593 // Ensure the old page gets reloaded, not swappedout://.
594 ViewMsg_Navigate_Params nav_params;
595 nav_params.url = GURL("data:text/html,<div>Page A</div>");
596 nav_params.navigation_type = ViewMsg_Navigate_Type::RELOAD;
597 nav_params.transition = PAGE_TRANSITION_RELOAD;
598 nav_params.current_history_list_length = 2;
599 nav_params.current_history_list_offset = 0;
600 nav_params.pending_history_list_offset = 0;
601 nav_params.page_id = 1;
602 nav_params.page_state = state_A;
603 view()->OnNavigate(nav_params);
604 ProcessPendingMessages();
606 // Verify page A committed, not swappedout://.
607 const IPC::Message* frame_navigate_msg =
608 render_thread_->sink().GetUniqueMessageMatching(
609 ViewHostMsg_FrameNavigate::ID);
610 EXPECT_TRUE(frame_navigate_msg);
612 // Read URL out of the parent trait of the params object.
613 ViewHostMsg_FrameNavigate::Param commit_params;
614 ViewHostMsg_FrameNavigate::Read(frame_navigate_msg, &commit_params);
615 EXPECT_NE(GURL("swappedout://"), commit_params.a.url);
619 // Test that we get the correct UpdateState message when we go back twice
620 // quickly without committing. Regression test for http://crbug.com/58082.
621 // Disabled: http://crbug.com/157357 .
622 TEST_F(RenderViewImplTest, DISABLED_LastCommittedUpdateState) {
624 LoadHTML("<div>Page A</div>");
626 // Load page B, which will trigger an UpdateState message for page A.
627 LoadHTML("<div>Page B</div>");
629 // Check for a valid UpdateState message for page A.
630 ProcessPendingMessages();
631 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
632 ViewHostMsg_UpdateState::ID);
636 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
637 EXPECT_EQ(1, page_id_A);
638 render_thread_->sink().ClearMessages();
640 // Load page C, which will trigger an UpdateState message for page B.
641 LoadHTML("<div>Page C</div>");
643 // Check for a valid UpdateState for page B.
644 ProcessPendingMessages();
645 const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
646 ViewHostMsg_UpdateState::ID);
650 ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
651 EXPECT_EQ(2, page_id_B);
652 EXPECT_NE(state_A, state_B);
653 render_thread_->sink().ClearMessages();
655 // Load page D, which will trigger an UpdateState message for page C.
656 LoadHTML("<div>Page D</div>");
658 // Check for a valid UpdateState for page C.
659 ProcessPendingMessages();
660 const IPC::Message* msg_C = render_thread_->sink().GetUniqueMessageMatching(
661 ViewHostMsg_UpdateState::ID);
665 ViewHostMsg_UpdateState::Read(msg_C, &page_id_C, &state_C);
666 EXPECT_EQ(3, page_id_C);
667 EXPECT_NE(state_B, state_C);
668 render_thread_->sink().ClearMessages();
670 // Go back to C and commit, preparing for our real test.
671 ViewMsg_Navigate_Params params_C;
672 params_C.navigation_type = ViewMsg_Navigate_Type::NORMAL;
673 params_C.transition = PAGE_TRANSITION_FORWARD_BACK;
674 params_C.current_history_list_length = 4;
675 params_C.current_history_list_offset = 3;
676 params_C.pending_history_list_offset = 2;
677 params_C.page_id = 3;
678 params_C.page_state = state_C;
679 view()->OnNavigate(params_C);
680 ProcessPendingMessages();
681 render_thread_->sink().ClearMessages();
683 // Go back twice quickly, such that page B does not have a chance to commit.
684 // This leads to two changes to the back/forward list but only one change to
685 // the RenderView's page ID.
687 // Back to page B (page_id 2), without committing.
688 ViewMsg_Navigate_Params params_B;
689 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL;
690 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
691 params_B.current_history_list_length = 4;
692 params_B.current_history_list_offset = 2;
693 params_B.pending_history_list_offset = 1;
694 params_B.page_id = 2;
695 params_B.page_state = state_B;
696 view()->OnNavigate(params_B);
698 // Back to page A (page_id 1) and commit.
699 ViewMsg_Navigate_Params params;
700 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
701 params.transition = PAGE_TRANSITION_FORWARD_BACK;
702 params_B.current_history_list_length = 4;
703 params_B.current_history_list_offset = 2;
704 params_B.pending_history_list_offset = 0;
706 params.page_state = state_A;
707 view()->OnNavigate(params);
708 ProcessPendingMessages();
710 // Now ensure that the UpdateState message we receive is consistent
711 // and represents page C in both page_id and state.
712 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
713 ViewHostMsg_UpdateState::ID);
717 ViewHostMsg_UpdateState::Read(msg, &page_id, &state);
718 EXPECT_EQ(page_id_C, page_id);
719 EXPECT_NE(state_A, state);
720 EXPECT_NE(state_B, state);
721 EXPECT_EQ(state_C, state);
724 // Test that the history_page_ids_ list can reveal when a stale back/forward
725 // navigation arrives from the browser and can be ignored. See
726 // http://crbug.com/86758.
727 TEST_F(RenderViewImplTest, StaleNavigationsIgnored) {
729 LoadHTML("<div>Page A</div>");
730 EXPECT_EQ(1, view()->history_list_length_);
731 EXPECT_EQ(0, view()->history_list_offset_);
732 EXPECT_EQ(1, view()->history_page_ids_[0]);
734 // Load page B, which will trigger an UpdateState message for page A.
735 LoadHTML("<div>Page B</div>");
736 EXPECT_EQ(2, view()->history_list_length_);
737 EXPECT_EQ(1, view()->history_list_offset_);
738 EXPECT_EQ(2, view()->history_page_ids_[1]);
740 // Check for a valid UpdateState message for page A.
741 ProcessPendingMessages();
742 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
743 ViewHostMsg_UpdateState::ID);
747 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
748 EXPECT_EQ(1, page_id_A);
749 render_thread_->sink().ClearMessages();
751 // Back to page A (page_id 1) and commit.
752 ViewMsg_Navigate_Params params_A;
753 params_A.navigation_type = ViewMsg_Navigate_Type::NORMAL;
754 params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
755 params_A.current_history_list_length = 2;
756 params_A.current_history_list_offset = 1;
757 params_A.pending_history_list_offset = 0;
758 params_A.page_id = 1;
759 params_A.page_state = state_A;
760 view()->OnNavigate(params_A);
761 ProcessPendingMessages();
763 // A new navigation commits, clearing the forward history.
764 LoadHTML("<div>Page C</div>");
765 EXPECT_EQ(2, view()->history_list_length_);
766 EXPECT_EQ(1, view()->history_list_offset_);
767 EXPECT_EQ(3, view()->history_page_ids_[1]);
769 // The browser then sends a stale navigation to B, which should be ignored.
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 = 0;
775 params_B.pending_history_list_offset = 1;
776 params_B.page_id = 2;
777 params_B.page_state = state_A; // Doesn't matter, just has to be present.
778 view()->OnNavigate(params_B);
780 // State should be unchanged.
781 EXPECT_EQ(2, view()->history_list_length_);
782 EXPECT_EQ(1, view()->history_list_offset_);
783 EXPECT_EQ(3, view()->history_page_ids_[1]);
786 // Test that we do not ignore navigations after the entry limit is reached,
787 // in which case the browser starts dropping entries from the front. In this
788 // case, we'll see a page_id mismatch but the RenderView's id will be older,
789 // not newer, than params.page_id. Use this as a cue that we should update the
790 // state and not treat it like a navigation to a cropped forward history item.
791 // See http://crbug.com/89798.
792 TEST_F(RenderViewImplTest, DontIgnoreBackAfterNavEntryLimit) {
794 LoadHTML("<div>Page A</div>");
795 EXPECT_EQ(1, view()->history_list_length_);
796 EXPECT_EQ(0, view()->history_list_offset_);
797 EXPECT_EQ(1, view()->history_page_ids_[0]);
799 // Load page B, which will trigger an UpdateState message for page A.
800 LoadHTML("<div>Page B</div>");
801 EXPECT_EQ(2, view()->history_list_length_);
802 EXPECT_EQ(1, view()->history_list_offset_);
803 EXPECT_EQ(2, view()->history_page_ids_[1]);
805 // Check for a valid UpdateState message for page A.
806 ProcessPendingMessages();
807 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
808 ViewHostMsg_UpdateState::ID);
812 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
813 EXPECT_EQ(1, page_id_A);
814 render_thread_->sink().ClearMessages();
816 // Load page C, which will trigger an UpdateState message for page B.
817 LoadHTML("<div>Page C</div>");
818 EXPECT_EQ(3, view()->history_list_length_);
819 EXPECT_EQ(2, view()->history_list_offset_);
820 EXPECT_EQ(3, view()->history_page_ids_[2]);
822 // Check for a valid UpdateState message for page B.
823 ProcessPendingMessages();
824 const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
825 ViewHostMsg_UpdateState::ID);
829 ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
830 EXPECT_EQ(2, page_id_B);
831 render_thread_->sink().ClearMessages();
833 // Suppose the browser has limited the number of NavigationEntries to 2.
834 // It has now dropped the first entry, but the renderer isn't notified.
835 // Ensure that going back to page B (page_id 2) at offset 0 is successful.
836 ViewMsg_Navigate_Params params_B;
837 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL;
838 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
839 params_B.current_history_list_length = 2;
840 params_B.current_history_list_offset = 1;
841 params_B.pending_history_list_offset = 0;
842 params_B.page_id = 2;
843 params_B.page_state = state_B;
844 view()->OnNavigate(params_B);
845 ProcessPendingMessages();
847 EXPECT_EQ(2, view()->history_list_length_);
848 EXPECT_EQ(0, view()->history_list_offset_);
849 EXPECT_EQ(2, view()->history_page_ids_[0]);
852 // Test that our IME backend sends a notification message when the input focus
854 TEST_F(RenderViewImplTest, OnImeTypeChanged) {
855 // Enable our IME backend code.
856 view()->OnSetInputMethodActive(true);
858 // Load an HTML page consisting of two input fields.
859 view()->set_send_content_state_immediately(true);
864 "<input id=\"test1\" type=\"text\" value=\"some text\"></input>"
865 "<input id=\"test2\" type=\"password\"></input>"
866 "<input id=\"test3\" type=\"text\" inputmode=\"verbatim\"></input>"
867 "<input id=\"test4\" type=\"text\" inputmode=\"latin\"></input>"
868 "<input id=\"test5\" type=\"text\" inputmode=\"latin-name\"></input>"
869 "<input id=\"test6\" type=\"text\" inputmode=\"latin-prose\">"
871 "<input id=\"test7\" type=\"text\" inputmode=\"full-width-latin\">"
873 "<input id=\"test8\" type=\"text\" inputmode=\"kana\"></input>"
874 "<input id=\"test9\" type=\"text\" inputmode=\"katakana\"></input>"
875 "<input id=\"test10\" type=\"text\" inputmode=\"numeric\"></input>"
876 "<input id=\"test11\" type=\"text\" inputmode=\"tel\"></input>"
877 "<input id=\"test12\" type=\"text\" inputmode=\"email\"></input>"
878 "<input id=\"test13\" type=\"text\" inputmode=\"url\"></input>"
879 "<input id=\"test14\" type=\"text\" inputmode=\"unknown\"></input>"
880 "<input id=\"test15\" type=\"text\" inputmode=\"verbatim\"></input>"
883 render_thread_->sink().ClearMessages();
885 struct InputModeTestCase {
886 const char* input_id;
887 ui::TextInputMode expected_mode;
889 static const InputModeTestCase kInputModeTestCases[] = {
890 {"test1", ui::TEXT_INPUT_MODE_DEFAULT},
891 {"test3", ui::TEXT_INPUT_MODE_VERBATIM},
892 {"test4", ui::TEXT_INPUT_MODE_LATIN},
893 {"test5", ui::TEXT_INPUT_MODE_LATIN_NAME},
894 {"test6", ui::TEXT_INPUT_MODE_LATIN_PROSE},
895 {"test7", ui::TEXT_INPUT_MODE_FULL_WIDTH_LATIN},
896 {"test8", ui::TEXT_INPUT_MODE_KANA},
897 {"test9", ui::TEXT_INPUT_MODE_KATAKANA},
898 {"test10", ui::TEXT_INPUT_MODE_NUMERIC},
899 {"test11", ui::TEXT_INPUT_MODE_TEL},
900 {"test12", ui::TEXT_INPUT_MODE_EMAIL},
901 {"test13", ui::TEXT_INPUT_MODE_URL},
902 {"test14", ui::TEXT_INPUT_MODE_DEFAULT},
903 {"test15", ui::TEXT_INPUT_MODE_VERBATIM},
906 const int kRepeatCount = 10;
907 for (int i = 0; i < kRepeatCount; i++) {
908 // Move the input focus to the first <input> element, where we should
910 ExecuteJavaScript("document.getElementById('test1').focus();");
911 ProcessPendingMessages();
912 render_thread_->sink().ClearMessages();
914 // Update the IME status and verify if our IME backend sends an IPC message
916 view()->UpdateTextInputType();
917 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
918 EXPECT_TRUE(msg != NULL);
919 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
920 ui::TextInputType type;
921 bool can_compose_inline = false;
922 ui::TextInputMode input_mode = ui::TEXT_INPUT_MODE_DEFAULT;
923 ViewHostMsg_TextInputTypeChanged::Read(msg,
926 &can_compose_inline);
927 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, type);
928 EXPECT_EQ(true, can_compose_inline);
930 // Move the input focus to the second <input> element, where we should
932 ExecuteJavaScript("document.getElementById('test2').focus();");
933 ProcessPendingMessages();
934 render_thread_->sink().ClearMessages();
936 // Update the IME status and verify if our IME backend sends an IPC message
937 // to de-activate IMEs.
938 view()->UpdateTextInputType();
939 msg = render_thread_->sink().GetMessageAt(0);
940 EXPECT_TRUE(msg != NULL);
941 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
942 ViewHostMsg_TextInputTypeChanged::Read(msg,
945 &can_compose_inline);
946 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, type);
948 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInputModeTestCases); i++) {
949 const InputModeTestCase* test_case = &kInputModeTestCases[i];
950 std::string javascript =
951 base::StringPrintf("document.getElementById('%s').focus();",
952 test_case->input_id);
953 // Move the input focus to the target <input> element, where we should
955 ExecuteJavaScriptAndReturnIntValue(base::ASCIIToUTF16(javascript), NULL);
956 ProcessPendingMessages();
957 render_thread_->sink().ClearMessages();
959 // Update the IME status and verify if our IME backend sends an IPC
960 // message to activate IMEs.
961 view()->UpdateTextInputType();
962 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
963 EXPECT_TRUE(msg != NULL);
964 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
965 ViewHostMsg_TextInputTypeChanged::Read(msg,
968 &can_compose_inline);
969 EXPECT_EQ(test_case->expected_mode, input_mode);
974 // Test that our IME backend can compose CJK words.
975 // Our IME front-end sends many platform-independent messages to the IME backend
976 // while it composes CJK words. This test sends the minimal messages captured
977 // on my local environment directly to the IME backend to verify if the backend
978 // can compose CJK words without any problems.
979 // This test uses an array of command sets because an IME composotion does not
980 // only depends on IME events, but also depends on window events, e.g. moving
981 // the window focus while composing a CJK text. To handle such complicated
982 // cases, this test should not only call IME-related functions in the
983 // RenderWidget class, but also call some RenderWidget members, e.g.
984 // ExecuteJavaScript(), RenderWidget::OnSetFocus(), etc.
985 TEST_F(RenderViewImplTest, ImeComposition) {
991 IME_CONFIRMCOMPOSITION,
992 IME_CANCELCOMPOSITION
999 const wchar_t* ime_string;
1000 const wchar_t* result;
1002 static const ImeMessage kImeMessages[] = {
1003 // Scenario 1: input a Chinese word with Microsoft IME (on Vista).
1004 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
1005 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
1006 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
1007 {IME_SETCOMPOSITION, false, 1, 1, L"n", L"n"},
1008 {IME_SETCOMPOSITION, false, 2, 2, L"ni", L"ni"},
1009 {IME_SETCOMPOSITION, false, 3, 3, L"nih", L"nih"},
1010 {IME_SETCOMPOSITION, false, 4, 4, L"niha", L"niha"},
1011 {IME_SETCOMPOSITION, false, 5, 5, L"nihao", L"nihao"},
1012 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"\x4F60\x597D", L"\x4F60\x597D"},
1013 // Scenario 2: input a Japanese word with Microsoft IME (on Vista).
1014 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
1015 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
1016 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
1017 {IME_SETCOMPOSITION, false, 0, 1, L"\xFF4B", L"\xFF4B"},
1018 {IME_SETCOMPOSITION, false, 0, 1, L"\x304B", L"\x304B"},
1019 {IME_SETCOMPOSITION, false, 0, 2, L"\x304B\xFF4E", L"\x304B\xFF4E"},
1020 {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\xFF4A",
1021 L"\x304B\x3093\xFF4A"},
1022 {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\x3058",
1023 L"\x304B\x3093\x3058"},
1024 {IME_SETCOMPOSITION, false, 0, 2, L"\x611F\x3058", L"\x611F\x3058"},
1025 {IME_SETCOMPOSITION, false, 0, 2, L"\x6F22\x5B57", L"\x6F22\x5B57"},
1026 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"},
1027 {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"},
1028 // Scenario 3: input a Korean word with Microsot IME (on Vista).
1029 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
1030 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
1031 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
1032 {IME_SETCOMPOSITION, false, 0, 1, L"\x3147", L"\x3147"},
1033 {IME_SETCOMPOSITION, false, 0, 1, L"\xC544", L"\xC544"},
1034 {IME_SETCOMPOSITION, false, 0, 1, L"\xC548", L"\xC548"},
1035 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\xC548"},
1036 {IME_SETCOMPOSITION, false, 0, 1, L"\x3134", L"\xC548\x3134"},
1037 {IME_SETCOMPOSITION, false, 0, 1, L"\xB140", L"\xC548\xB140"},
1038 {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"},
1039 {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\xC548"},
1040 {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"},
1041 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\xC548\xB155"},
1044 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kImeMessages); i++) {
1045 const ImeMessage* ime_message = &kImeMessages[i];
1046 switch (ime_message->command) {
1047 case IME_INITIALIZE:
1048 // Load an HTML page consisting of a content-editable <div> element,
1049 // and move the input focus to the <div> element, where we can use
1051 view()->OnSetInputMethodActive(ime_message->enable);
1052 view()->set_send_content_state_immediately(true);
1057 "<div id=\"test1\" contenteditable=\"true\"></div>"
1060 ExecuteJavaScript("document.getElementById('test1').focus();");
1063 case IME_SETINPUTMODE:
1064 // Activate (or deactivate) our IME back-end.
1065 view()->OnSetInputMethodActive(ime_message->enable);
1069 // Update the window focus.
1070 view()->OnSetFocus(ime_message->enable);
1073 case IME_SETCOMPOSITION:
1074 view()->OnImeSetComposition(
1075 base::WideToUTF16Hack(ime_message->ime_string),
1076 std::vector<blink::WebCompositionUnderline>(),
1077 ime_message->selection_start,
1078 ime_message->selection_end);
1081 case IME_CONFIRMCOMPOSITION:
1082 view()->OnImeConfirmComposition(
1083 base::WideToUTF16Hack(ime_message->ime_string),
1084 gfx::Range::InvalidRange(),
1088 case IME_CANCELCOMPOSITION:
1089 view()->OnImeSetComposition(
1091 std::vector<blink::WebCompositionUnderline>(),
1096 // Update the status of our IME back-end.
1097 // TODO(hbono): we should verify messages to be sent from the back-end.
1098 view()->UpdateTextInputType();
1099 ProcessPendingMessages();
1100 render_thread_->sink().ClearMessages();
1102 if (ime_message->result) {
1103 // Retrieve the content of this page and compare it with the expected
1105 const int kMaxOutputCharacters = 128;
1106 std::wstring output = base::UTF16ToWideHack(
1107 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1108 EXPECT_EQ(output, ime_message->result);
1113 // Test that the RenderView::OnSetTextDirection() function can change the text
1114 // direction of the selected input element.
1115 TEST_F(RenderViewImplTest, OnSetTextDirection) {
1116 // Load an HTML page consisting of a <textarea> element and a <div> element.
1117 // This test changes the text direction of the <textarea> element, and
1118 // writes the values of its 'dir' attribute and its 'direction' property to
1119 // verify that the text direction is changed.
1120 view()->set_send_content_state_immediately(true);
1125 "<textarea id=\"test\"></textarea>"
1126 "<div id=\"result\" contenteditable=\"true\"></div>"
1129 render_thread_->sink().ClearMessages();
1131 static const struct {
1132 WebTextDirection direction;
1133 const wchar_t* expected_result;
1134 } kTextDirection[] = {
1135 { blink::WebTextDirectionRightToLeft, L"\x000A" L"rtl,rtl" },
1136 { blink::WebTextDirectionLeftToRight, L"\x000A" L"ltr,ltr" },
1138 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTextDirection); ++i) {
1139 // Set the text direction of the <textarea> element.
1140 ExecuteJavaScript("document.getElementById('test').focus();");
1141 view()->OnSetTextDirection(kTextDirection[i].direction);
1143 // Write the values of its DOM 'dir' attribute and its CSS 'direction'
1144 // property to the <div> element.
1145 ExecuteJavaScript("var result = document.getElementById('result');"
1146 "var node = document.getElementById('test');"
1147 "var style = getComputedStyle(node, null);"
1148 "result.innerText ="
1149 " node.getAttribute('dir') + ',' +"
1150 " style.getPropertyValue('direction');");
1152 // Copy the document content to std::wstring and compare with the
1154 const int kMaxOutputCharacters = 16;
1155 std::wstring output = base::UTF16ToWideHack(
1156 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1157 EXPECT_EQ(output, kTextDirection[i].expected_result);
1161 // see http://crbug.com/238750
1163 #define MAYBE_OnHandleKeyboardEvent DISABLED_OnHandleKeyboardEvent
1165 #define MAYBE_OnHandleKeyboardEvent OnHandleKeyboardEvent
1168 // Test that we can receive correct DOM events when we send input events
1169 // through the RenderWidget::OnHandleInputEvent() function.
1170 TEST_F(RenderViewImplTest, MAYBE_OnHandleKeyboardEvent) {
1171 #if !defined(OS_MACOSX)
1172 // Load an HTML page consisting of one <input> element and three
1173 // contentediable <div> elements.
1174 // The <input> element is used for sending keyboard events, and the <div>
1175 // elements are used for writing DOM events in the following format:
1176 // "<keyCode>,<shiftKey>,<controlKey>,<altKey>".
1177 // TODO(hbono): <http://crbug.com/2215> Our WebKit port set |ev.metaKey| to
1178 // true when pressing an alt key, i.e. the |ev.metaKey| value is not
1179 // trustworthy. We will check the |ev.metaKey| value when this issue is fixed.
1180 view()->set_send_content_state_immediately(true);
1184 "<script type='text/javascript' language='javascript'>"
1185 "function OnKeyEvent(ev) {"
1186 " var result = document.getElementById(ev.type);"
1187 " result.innerText ="
1188 " (ev.which || ev.keyCode) + ',' +"
1189 " ev.shiftKey + ',' +"
1190 " ev.ctrlKey + ',' +"
1197 "<input id='test' type='text'"
1198 " onkeydown='return OnKeyEvent(event);'"
1199 " onkeypress='return OnKeyEvent(event);'"
1200 " onkeyup='return OnKeyEvent(event);'>"
1202 "<div id='keydown' contenteditable='true'>"
1204 "<div id='keypress' contenteditable='true'>"
1206 "<div id='keyup' contenteditable='true'>"
1210 ExecuteJavaScript("document.getElementById('test').focus();");
1211 render_thread_->sink().ClearMessages();
1213 static const MockKeyboard::Layout kLayouts[] = {
1215 // Since we ignore the mock keyboard layout on Linux and instead just use
1216 // the screen's keyboard layout, these trivially pass. They are commented
1217 // out to avoid the illusion that they work.
1218 MockKeyboard::LAYOUT_ARABIC,
1219 MockKeyboard::LAYOUT_CANADIAN_FRENCH,
1220 MockKeyboard::LAYOUT_FRENCH,
1221 MockKeyboard::LAYOUT_HEBREW,
1222 MockKeyboard::LAYOUT_RUSSIAN,
1224 MockKeyboard::LAYOUT_UNITED_STATES,
1227 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) {
1228 // For each key code, we send three keyboard events.
1229 // * we press only the key;
1230 // * we press the key and a left-shift key, and;
1231 // * we press the key and a right-alt (AltGr) key.
1232 // For each modifiers, we need a string used for formatting its expected
1233 // result. (See the above comment for its format.)
1234 static const struct {
1235 MockKeyboard::Modifiers modifiers;
1236 const char* expected_result;
1237 } kModifierData[] = {
1238 {MockKeyboard::NONE, "false,false,false"},
1239 {MockKeyboard::LEFT_SHIFT, "true,false,false"},
1241 {MockKeyboard::RIGHT_ALT, "false,false,true"},
1245 MockKeyboard::Layout layout = kLayouts[i];
1246 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifierData); ++j) {
1247 // Virtual key codes used for this test.
1248 static const int kKeyCodes[] = {
1249 '0', '1', '2', '3', '4', '5', '6', '7',
1250 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1251 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1252 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
1258 ui::VKEY_OEM_PERIOD,
1266 // Not sure how to handle this key on Linux.
1271 MockKeyboard::Modifiers modifiers = kModifierData[j].modifiers;
1272 for (size_t k = 0; k < ARRAYSIZE_UNSAFE(kKeyCodes); ++k) {
1273 // Send a keyboard event to the RenderView object.
1274 // We should test a keyboard event only when the given keyboard-layout
1275 // driver is installed in a PC and the driver can assign a Unicode
1276 // charcter for the given tuple (key-code and modifiers).
1277 int key_code = kKeyCodes[k];
1278 base::string16 char_code;
1279 if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0)
1282 // Create an expected result from the virtual-key code, the character
1283 // code, and the modifier-key status.
1284 // We format a string that emulates a DOM-event string produced hy
1285 // our JavaScript function. (See the above comment for the format.)
1286 static char expected_result[1024];
1287 expected_result[0] = 0;
1288 base::snprintf(&expected_result[0],
1289 sizeof(expected_result),
1290 "\n" // texts in the <input> element
1291 "%d,%s\n" // texts in the first <div> element
1292 "%d,%s\n" // texts in the second <div> element
1293 "%d,%s", // texts in the third <div> element
1294 key_code, kModifierData[j].expected_result,
1295 static_cast<int>(char_code[0]),
1296 kModifierData[j].expected_result,
1297 key_code, kModifierData[j].expected_result);
1299 // Retrieve the text in the test page and compare it with the expected
1300 // text created from a virtual-key code, a character code, and the
1301 // modifier-key status.
1302 const int kMaxOutputCharacters = 1024;
1303 std::string output = base::UTF16ToUTF8(
1304 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1305 EXPECT_EQ(expected_result, output);
1314 // Test that our EditorClientImpl class can insert characters when we send
1315 // keyboard events through the RenderWidget::OnHandleInputEvent() function.
1316 // This test is for preventing regressions caused only when we use non-US
1317 // keyboards, such as Issue 10846.
1318 // see http://crbug.com/244562
1320 #define MAYBE_InsertCharacters DISABLED_InsertCharacters
1322 #define MAYBE_InsertCharacters InsertCharacters
1324 TEST_F(RenderViewImplTest, MAYBE_InsertCharacters) {
1325 #if !defined(OS_MACOSX)
1326 static const struct {
1327 MockKeyboard::Layout layout;
1328 const wchar_t* expected_result;
1331 // Disabled these keyboard layouts because buildbots do not have their
1332 // keyboard-layout drivers installed.
1333 {MockKeyboard::LAYOUT_ARABIC,
1334 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1335 L"\x0038\x0039\x0634\x0624\x064a\x062b\x0628\x0644"
1336 L"\x0627\x0647\x062a\x0646\x0645\x0629\x0649\x062e"
1337 L"\x062d\x0636\x0642\x0633\x0641\x0639\x0631\x0635"
1338 L"\x0621\x063a\x0626\x0643\x003d\x0648\x002d\x0632"
1339 L"\x0638\x0630\x062c\x005c\x062f\x0637\x0028\x0021"
1340 L"\x0040\x0023\x0024\x0025\x005e\x0026\x002a\x0029"
1341 L"\x0650\x007d\x005d\x064f\x005b\x0623\x00f7\x0640"
1342 L"\x060c\x002f\x2019\x0622\x00d7\x061b\x064e\x064c"
1343 L"\x064d\x2018\x007b\x064b\x0652\x0625\x007e\x003a"
1344 L"\x002b\x002c\x005f\x002e\x061f\x0651\x003c\x007c"
1345 L"\x003e\x0022\x0030\x0031\x0032\x0033\x0034\x0035"
1346 L"\x0036\x0037\x0038\x0039\x0634\x0624\x064a\x062b"
1347 L"\x0628\x0644\x0627\x0647\x062a\x0646\x0645\x0629"
1348 L"\x0649\x062e\x062d\x0636\x0642\x0633\x0641\x0639"
1349 L"\x0631\x0635\x0621\x063a\x0626\x0643\x003d\x0648"
1350 L"\x002d\x0632\x0638\x0630\x062c\x005c\x062f\x0637"
1352 {MockKeyboard::LAYOUT_HEBREW,
1353 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1354 L"\x0038\x0039\x05e9\x05e0\x05d1\x05d2\x05e7\x05db"
1355 L"\x05e2\x05d9\x05df\x05d7\x05dc\x05da\x05e6\x05de"
1356 L"\x05dd\x05e4\x002f\x05e8\x05d3\x05d0\x05d5\x05d4"
1357 L"\x0027\x05e1\x05d8\x05d6\x05e3\x003d\x05ea\x002d"
1358 L"\x05e5\x002e\x003b\x005d\x005c\x005b\x002c\x0028"
1359 L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a"
1360 L"\x0029\x0041\x0042\x0043\x0044\x0045\x0046\x0047"
1361 L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f"
1362 L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057"
1363 L"\x0058\x0059\x005a\x003a\x002b\x003e\x005f\x003c"
1364 L"\x003f\x007e\x007d\x007c\x007b\x0022\x0030\x0031"
1365 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1366 L"\x05e9\x05e0\x05d1\x05d2\x05e7\x05db\x05e2\x05d9"
1367 L"\x05df\x05d7\x05dc\x05da\x05e6\x05de\x05dd\x05e4"
1368 L"\x002f\x05e8\x05d3\x05d0\x05d5\x05d4\x0027\x05e1"
1369 L"\x05d8\x05d6\x05e3\x003d\x05ea\x002d\x05e5\x002e"
1370 L"\x003b\x005d\x005c\x005b\x002c"
1374 // On Linux, the only way to test alternate keyboard layouts is to change
1375 // the keyboard layout of the whole screen. I'm worried about the side
1376 // effects this may have on the buildbots.
1377 {MockKeyboard::LAYOUT_CANADIAN_FRENCH,
1378 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1379 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1380 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1381 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1382 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1383 L"\x002e\x00e9\x003c\x0029\x0021\x0022\x002f\x0024"
1384 L"\x0025\x003f\x0026\x002a\x0028\x0041\x0042\x0043"
1385 L"\x0044\x0045\x0046\x0047\x0048\x0049\x004a\x004b"
1386 L"\x004c\x004d\x004e\x004f\x0050\x0051\x0052\x0053"
1387 L"\x0054\x0055\x0056\x0057\x0058\x0059\x005a\x003a"
1388 L"\x002b\x0027\x005f\x002e\x00c9\x003e\x0030\x0031"
1389 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1390 L"\x0061\x0062\x0063\x0064\x0065\x0066\x0067\x0068"
1391 L"\x0069\x006a\x006b\x006c\x006d\x006e\x006f\x0070"
1392 L"\x0071\x0072\x0073\x0074\x0075\x0076\x0077\x0078"
1393 L"\x0079\x007a\x003b\x003d\x002c\x002d\x002e\x00e9"
1396 {MockKeyboard::LAYOUT_FRENCH,
1397 L"\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d\x00e8"
1398 L"\x005f\x00e7\x0061\x0062\x0063\x0064\x0065\x0066"
1399 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1400 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1401 L"\x0077\x0078\x0079\x007a\x0024\x003d\x002c\x003b"
1402 L"\x003a\x00f9\x0029\x002a\x0021\x0030\x0031\x0032"
1403 L"\x0033\x0034\x0035\x0036\x0037\x0038\x0039\x0041"
1404 L"\x0042\x0043\x0044\x0045\x0046\x0047\x0048\x0049"
1405 L"\x004a\x004b\x004c\x004d\x004e\x004f\x0050\x0051"
1406 L"\x0052\x0053\x0054\x0055\x0056\x0057\x0058\x0059"
1407 L"\x005a\x00a3\x002b\x003f\x002e\x002f\x0025\x00b0"
1408 L"\x00b5\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d"
1409 L"\x00e8\x005f\x00e7\x0061\x0062\x0063\x0064\x0065"
1410 L"\x0066\x0067\x0068\x0069\x006a\x006b\x006c\x006d"
1411 L"\x006e\x006f\x0070\x0071\x0072\x0073\x0074\x0075"
1412 L"\x0076\x0077\x0078\x0079\x007a\x0024\x003d\x002c"
1413 L"\x003b\x003a\x00f9\x0029\x002a\x0021"
1415 {MockKeyboard::LAYOUT_RUSSIAN,
1416 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1417 L"\x0038\x0039\x0444\x0438\x0441\x0432\x0443\x0430"
1418 L"\x043f\x0440\x0448\x043e\x043b\x0434\x044c\x0442"
1419 L"\x0449\x0437\x0439\x043a\x044b\x0435\x0433\x043c"
1420 L"\x0446\x0447\x043d\x044f\x0436\x003d\x0431\x002d"
1421 L"\x044e\x002e\x0451\x0445\x005c\x044a\x044d\x0029"
1422 L"\x0021\x0022\x2116\x003b\x0025\x003a\x003f\x002a"
1423 L"\x0028\x0424\x0418\x0421\x0412\x0423\x0410\x041f"
1424 L"\x0420\x0428\x041e\x041b\x0414\x042c\x0422\x0429"
1425 L"\x0417\x0419\x041a\x042b\x0415\x0413\x041c\x0426"
1426 L"\x0427\x041d\x042f\x0416\x002b\x0411\x005f\x042e"
1427 L"\x002c\x0401\x0425\x002f\x042a\x042d\x0030\x0031"
1428 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1429 L"\x0444\x0438\x0441\x0432\x0443\x0430\x043f\x0440"
1430 L"\x0448\x043e\x043b\x0434\x044c\x0442\x0449\x0437"
1431 L"\x0439\x043a\x044b\x0435\x0433\x043c\x0446\x0447"
1432 L"\x043d\x044f\x0436\x003d\x0431\x002d\x044e\x002e"
1433 L"\x0451\x0445\x005c\x044a\x044d"
1435 #endif // defined(OS_WIN)
1436 {MockKeyboard::LAYOUT_UNITED_STATES,
1437 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1438 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1439 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1440 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1441 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1442 L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027\x0029"
1443 L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a"
1444 L"\x0028\x0041\x0042\x0043\x0044\x0045\x0046\x0047"
1445 L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f"
1446 L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057"
1447 L"\x0058\x0059\x005a\x003a\x002b\x003c\x005f\x003e"
1448 L"\x003f\x007e\x007b\x007c\x007d\x0022"
1450 // This is ifdefed out for Linux to correspond to the fact that we don't
1451 // test alt+keystroke for now.
1452 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1453 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1454 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1455 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1456 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1457 L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027"
1462 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) {
1463 // Load an HTML page consisting of one <div> element.
1464 // This <div> element is used by the EditorClientImpl class to insert
1465 // characters received through the RenderWidget::OnHandleInputEvent()
1467 view()->set_send_content_state_immediately(true);
1473 "<div id='test' contenteditable='true'>"
1477 ExecuteJavaScript("document.getElementById('test').focus();");
1478 render_thread_->sink().ClearMessages();
1480 // For each key code, we send three keyboard events.
1481 // * Pressing only the key;
1482 // * Pressing the key and a left-shift key, and;
1483 // * Pressing the key and a right-alt (AltGr) key.
1484 static const MockKeyboard::Modifiers kModifiers[] = {
1486 MockKeyboard::LEFT_SHIFT,
1488 MockKeyboard::RIGHT_ALT,
1492 MockKeyboard::Layout layout = kLayouts[i].layout;
1493 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifiers); ++j) {
1494 // Virtual key codes used for this test.
1495 static const int kKeyCodes[] = {
1496 '0', '1', '2', '3', '4', '5', '6', '7',
1497 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1498 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1499 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
1505 ui::VKEY_OEM_PERIOD,
1513 // Unclear how to handle this on Linux.
1518 MockKeyboard::Modifiers modifiers = kModifiers[j];
1519 for (size_t k = 0; k < ARRAYSIZE_UNSAFE(kKeyCodes); ++k) {
1520 // Send a keyboard event to the RenderView object.
1521 // We should test a keyboard event only when the given keyboard-layout
1522 // driver is installed in a PC and the driver can assign a Unicode
1523 // charcter for the given tuple (layout, key-code, and modifiers).
1524 int key_code = kKeyCodes[k];
1525 base::string16 char_code;
1526 if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0)
1531 // Retrieve the text in the test page and compare it with the expected
1532 // text created from a virtual-key code, a character code, and the
1533 // modifier-key status.
1534 const int kMaxOutputCharacters = 4096;
1535 std::wstring output = base::UTF16ToWideHack(
1536 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1537 EXPECT_EQ(kLayouts[i].expected_result, output);
1544 // Crashy, http://crbug.com/53247.
1545 TEST_F(RenderViewImplTest, DISABLED_DidFailProvisionalLoadWithErrorForError) {
1546 GetMainFrame()->enableViewSourceMode(true);
1548 error.domain = WebString::fromUTF8(net::kErrorDomain);
1549 error.reason = net::ERR_FILE_NOT_FOUND;
1550 error.unreachableURL = GURL("http://foo");
1551 WebFrame* web_frame = GetMainFrame();
1553 // Start a load that will reach provisional state synchronously,
1554 // but won't complete synchronously.
1555 ViewMsg_Navigate_Params params;
1556 params.page_id = -1;
1557 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1558 params.url = GURL("data:text/html,test data");
1559 view()->OnNavigate(params);
1561 // An error occurred.
1562 view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
1563 // Frame should exit view-source mode.
1564 EXPECT_FALSE(web_frame->isViewSourceModeEnabled());
1567 TEST_F(RenderViewImplTest, DidFailProvisionalLoadWithErrorForCancellation) {
1568 GetMainFrame()->enableViewSourceMode(true);
1570 error.domain = WebString::fromUTF8(net::kErrorDomain);
1571 error.reason = net::ERR_ABORTED;
1572 error.unreachableURL = GURL("http://foo");
1573 WebFrame* web_frame = GetMainFrame();
1575 // Start a load that will reach provisional state synchronously,
1576 // but won't complete synchronously.
1577 ViewMsg_Navigate_Params params;
1578 params.page_id = -1;
1579 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1580 params.url = GURL("data:text/html,test data");
1581 view()->OnNavigate(params);
1583 // A cancellation occurred.
1584 view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
1585 // Frame should stay in view-source mode.
1586 EXPECT_TRUE(web_frame->isViewSourceModeEnabled());
1589 // Regression test for http://crbug.com/41562
1590 TEST_F(RenderViewImplTest, UpdateTargetURLWithInvalidURL) {
1591 const GURL invalid_gurl("http://");
1592 view()->setMouseOverURL(blink::WebURL(invalid_gurl));
1593 EXPECT_EQ(invalid_gurl, view()->target_url_);
1596 TEST_F(RenderViewImplTest, SetHistoryLengthAndPrune) {
1597 int expected_page_id = -1;
1599 // No history to merge and no committed pages.
1600 view()->OnSetHistoryLengthAndPrune(0, -1);
1601 EXPECT_EQ(0, view()->history_list_length_);
1602 EXPECT_EQ(-1, view()->history_list_offset_);
1604 // History to merge and no committed pages.
1605 view()->OnSetHistoryLengthAndPrune(2, -1);
1606 EXPECT_EQ(2, view()->history_list_length_);
1607 EXPECT_EQ(1, view()->history_list_offset_);
1608 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1609 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1612 // No history to merge and a committed page to be kept.
1613 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1614 expected_page_id = view()->page_id_;
1615 view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
1616 EXPECT_EQ(1, view()->history_list_length_);
1617 EXPECT_EQ(0, view()->history_list_offset_);
1618 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1621 // No history to merge and a committed page to be pruned.
1622 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1623 expected_page_id = view()->page_id_;
1624 view()->OnSetHistoryLengthAndPrune(0, expected_page_id + 1);
1625 EXPECT_EQ(0, view()->history_list_length_);
1626 EXPECT_EQ(-1, view()->history_list_offset_);
1629 // No history to merge and a committed page that the browser was unaware of.
1630 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1631 expected_page_id = view()->page_id_;
1632 view()->OnSetHistoryLengthAndPrune(0, -1);
1633 EXPECT_EQ(1, view()->history_list_length_);
1634 EXPECT_EQ(0, view()->history_list_offset_);
1635 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1638 // History to merge and a committed page to be kept.
1639 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1640 expected_page_id = view()->page_id_;
1641 view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
1642 EXPECT_EQ(3, view()->history_list_length_);
1643 EXPECT_EQ(2, view()->history_list_offset_);
1644 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1645 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1646 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1649 // History to merge and a committed page to be pruned.
1650 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1651 expected_page_id = view()->page_id_;
1652 view()->OnSetHistoryLengthAndPrune(2, expected_page_id + 1);
1653 EXPECT_EQ(2, view()->history_list_length_);
1654 EXPECT_EQ(1, view()->history_list_offset_);
1655 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1656 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1659 // History to merge and a committed page that the browser was unaware of.
1660 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1661 expected_page_id = view()->page_id_;
1662 view()->OnSetHistoryLengthAndPrune(2, -1);
1663 EXPECT_EQ(3, view()->history_list_length_);
1664 EXPECT_EQ(2, view()->history_list_offset_);
1665 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1666 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1667 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1670 int expected_page_id_2 = -1;
1672 // No history to merge and two committed pages, both to be kept.
1673 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1674 expected_page_id = view()->page_id_;
1675 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1676 expected_page_id_2 = view()->page_id_;
1677 EXPECT_GT(expected_page_id_2, expected_page_id);
1678 view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
1679 EXPECT_EQ(2, view()->history_list_length_);
1680 EXPECT_EQ(1, view()->history_list_offset_);
1681 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1682 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
1685 // No history to merge and two committed pages, and only the second is kept.
1686 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1687 expected_page_id = view()->page_id_;
1688 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1689 expected_page_id_2 = view()->page_id_;
1690 EXPECT_GT(expected_page_id_2, expected_page_id);
1691 view()->OnSetHistoryLengthAndPrune(0, expected_page_id_2);
1692 EXPECT_EQ(1, view()->history_list_length_);
1693 EXPECT_EQ(0, view()->history_list_offset_);
1694 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[0]);
1697 // No history to merge and two committed pages, both of which the browser was
1699 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1700 expected_page_id = view()->page_id_;
1701 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1702 expected_page_id_2 = view()->page_id_;
1703 EXPECT_GT(expected_page_id_2, expected_page_id);
1704 view()->OnSetHistoryLengthAndPrune(0, -1);
1705 EXPECT_EQ(2, view()->history_list_length_);
1706 EXPECT_EQ(1, view()->history_list_offset_);
1707 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1708 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
1711 // History to merge and two committed pages, both to be kept.
1712 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1713 expected_page_id = view()->page_id_;
1714 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1715 expected_page_id_2 = view()->page_id_;
1716 EXPECT_GT(expected_page_id_2, expected_page_id);
1717 view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
1718 EXPECT_EQ(4, view()->history_list_length_);
1719 EXPECT_EQ(3, view()->history_list_offset_);
1720 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1721 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1722 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1723 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
1726 // History to merge and two committed pages, and only the second is kept.
1727 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1728 expected_page_id = view()->page_id_;
1729 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1730 expected_page_id_2 = view()->page_id_;
1731 EXPECT_GT(expected_page_id_2, expected_page_id);
1732 view()->OnSetHistoryLengthAndPrune(2, expected_page_id_2);
1733 EXPECT_EQ(3, view()->history_list_length_);
1734 EXPECT_EQ(2, view()->history_list_offset_);
1735 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1736 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1737 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[2]);
1740 // History to merge and two committed pages, both of which the browser was
1742 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1743 expected_page_id = view()->page_id_;
1744 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1745 expected_page_id_2 = view()->page_id_;
1746 EXPECT_GT(expected_page_id_2, expected_page_id);
1747 view()->OnSetHistoryLengthAndPrune(2, -1);
1748 EXPECT_EQ(4, view()->history_list_length_);
1749 EXPECT_EQ(3, view()->history_list_offset_);
1750 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1751 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1752 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1753 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
1756 TEST_F(RenderViewImplTest, ContextMenu) {
1757 LoadHTML("<div>Page A</div>");
1759 // Create a right click in the center of the iframe. (I'm hoping this will
1760 // make this a bit more robust in case of some other formatting or other bug.)
1761 WebMouseEvent mouse_event;
1762 mouse_event.type = WebInputEvent::MouseDown;
1763 mouse_event.button = WebMouseEvent::ButtonRight;
1764 mouse_event.x = 250;
1765 mouse_event.y = 250;
1766 mouse_event.globalX = 250;
1767 mouse_event.globalY = 250;
1769 SendWebMouseEvent(mouse_event);
1771 // Now simulate the corresponding up event which should display the menu
1772 mouse_event.type = WebInputEvent::MouseUp;
1773 SendWebMouseEvent(mouse_event);
1775 EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
1776 ViewHostMsg_ContextMenu::ID));
1779 TEST_F(RenderViewImplTest, TestBackForward) {
1780 LoadHTML("<div id=pagename>Page A</div>");
1781 blink::WebHistoryItem page_a_item = GetMainFrame()->currentHistoryItem();
1782 int was_page_a = -1;
1783 base::string16 check_page_a =
1785 "Number(document.getElementById('pagename').innerHTML == 'Page A')");
1786 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
1787 EXPECT_EQ(1, was_page_a);
1789 LoadHTML("<div id=pagename>Page B</div>");
1790 int was_page_b = -1;
1791 base::string16 check_page_b =
1793 "Number(document.getElementById('pagename').innerHTML == 'Page B')");
1794 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1795 EXPECT_EQ(1, was_page_b);
1797 LoadHTML("<div id=pagename>Page C</div>");
1798 int was_page_c = -1;
1799 base::string16 check_page_c =
1801 "Number(document.getElementById('pagename').innerHTML == 'Page C')");
1802 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
1803 EXPECT_EQ(1, was_page_b);
1805 blink::WebHistoryItem forward_item = GetMainFrame()->currentHistoryItem();
1806 GoBack(GetMainFrame()->previousHistoryItem());
1807 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1808 EXPECT_EQ(1, was_page_b);
1810 GoForward(forward_item);
1811 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
1812 EXPECT_EQ(1, was_page_c);
1814 GoBack(GetMainFrame()->previousHistoryItem());
1815 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1816 EXPECT_EQ(1, was_page_b);
1818 forward_item = GetMainFrame()->currentHistoryItem();
1819 GoBack(page_a_item);
1820 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
1821 EXPECT_EQ(1, was_page_a);
1823 GoForward(forward_item);
1824 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1825 EXPECT_EQ(1, was_page_b);
1828 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
1829 TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) {
1832 // http://crbug.com/304193
1833 if (base::win::GetVersion() < base::win::VERSION_VISTA)
1837 LoadHTML("<textarea id=\"test\"></textarea>");
1838 ExecuteJavaScript("document.getElementById('test').focus();");
1840 const base::string16 empty_string;
1841 const std::vector<blink::WebCompositionUnderline> empty_underline;
1842 std::vector<gfx::Rect> bounds;
1843 view()->OnSetFocus(true);
1844 view()->OnSetInputMethodActive(true);
1846 // ASCII composition
1847 const base::string16 ascii_composition = base::UTF8ToUTF16("aiueo");
1848 view()->OnImeSetComposition(ascii_composition, empty_underline, 0, 0);
1849 view()->GetCompositionCharacterBounds(&bounds);
1850 ASSERT_EQ(ascii_composition.size(), bounds.size());
1851 for (size_t i = 0; i < bounds.size(); ++i)
1852 EXPECT_LT(0, bounds[i].width());
1853 view()->OnImeConfirmComposition(
1854 empty_string, gfx::Range::InvalidRange(), false);
1856 // Non surrogate pair unicode character.
1857 const base::string16 unicode_composition = base::UTF8ToUTF16(
1858 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
1859 view()->OnImeSetComposition(unicode_composition, empty_underline, 0, 0);
1860 view()->GetCompositionCharacterBounds(&bounds);
1861 ASSERT_EQ(unicode_composition.size(), bounds.size());
1862 for (size_t i = 0; i < bounds.size(); ++i)
1863 EXPECT_LT(0, bounds[i].width());
1864 view()->OnImeConfirmComposition(
1865 empty_string, gfx::Range::InvalidRange(), false);
1867 // Surrogate pair character.
1868 const base::string16 surrogate_pair_char =
1869 base::UTF8ToUTF16("\xF0\xA0\xAE\x9F");
1870 view()->OnImeSetComposition(surrogate_pair_char,
1874 view()->GetCompositionCharacterBounds(&bounds);
1875 ASSERT_EQ(surrogate_pair_char.size(), bounds.size());
1876 EXPECT_LT(0, bounds[0].width());
1877 EXPECT_EQ(0, bounds[1].width());
1878 view()->OnImeConfirmComposition(
1879 empty_string, gfx::Range::InvalidRange(), false);
1882 const base::string16 surrogate_pair_mixed_composition =
1883 surrogate_pair_char + base::UTF8ToUTF16("\xE3\x81\x82") +
1884 surrogate_pair_char + base::UTF8ToUTF16("b") + surrogate_pair_char;
1885 const size_t utf16_length = 8UL;
1886 const bool is_surrogate_pair_empty_rect[8] = {
1887 false, true, false, false, true, false, false, true };
1888 view()->OnImeSetComposition(surrogate_pair_mixed_composition,
1892 view()->GetCompositionCharacterBounds(&bounds);
1893 ASSERT_EQ(utf16_length, bounds.size());
1894 for (size_t i = 0; i < utf16_length; ++i) {
1895 if (is_surrogate_pair_empty_rect[i]) {
1896 EXPECT_EQ(0, bounds[i].width());
1898 EXPECT_LT(0, bounds[i].width());
1901 view()->OnImeConfirmComposition(
1902 empty_string, gfx::Range::InvalidRange(), false);
1906 TEST_F(RenderViewImplTest, ZoomLimit) {
1907 const double kMinZoomLevel = ZoomFactorToZoomLevel(kMinimumZoomFactor);
1908 const double kMaxZoomLevel = ZoomFactorToZoomLevel(kMaximumZoomFactor);
1910 ViewMsg_Navigate_Params params;
1911 params.page_id = -1;
1912 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1914 // Verifies navigation to a URL with preset zoom level indeed sets the level.
1915 // Regression test for http://crbug.com/139559, where the level was not
1916 // properly set when it is out of the default zoom limits of WebView.
1917 params.url = GURL("data:text/html,min_zoomlimit_test");
1918 view()->OnSetZoomLevelForLoadingURL(params.url, kMinZoomLevel);
1919 view()->OnNavigate(params);
1920 ProcessPendingMessages();
1921 EXPECT_DOUBLE_EQ(kMinZoomLevel, view()->GetWebView()->zoomLevel());
1923 // It should work even when the zoom limit is temporarily changed in the page.
1924 view()->GetWebView()->zoomLimitsChanged(ZoomFactorToZoomLevel(1.0),
1925 ZoomFactorToZoomLevel(1.0));
1926 params.url = GURL("data:text/html,max_zoomlimit_test");
1927 view()->OnSetZoomLevelForLoadingURL(params.url, kMaxZoomLevel);
1928 view()->OnNavigate(params);
1929 ProcessPendingMessages();
1930 EXPECT_DOUBLE_EQ(kMaxZoomLevel, view()->GetWebView()->zoomLevel());
1933 TEST_F(RenderViewImplTest, SetEditableSelectionAndComposition) {
1934 // Load an HTML page consisting of an input field.
1939 "<input id=\"test1\" value=\"some test text hello\"></input>"
1942 ExecuteJavaScript("document.getElementById('test1').focus();");
1943 view()->OnSetEditableSelectionOffsets(4, 8);
1944 const std::vector<blink::WebCompositionUnderline> empty_underline;
1945 view()->OnSetCompositionFromExistingText(7, 10, empty_underline);
1946 blink::WebTextInputInfo info = view()->webview()->textInputInfo();
1947 EXPECT_EQ(4, info.selectionStart);
1948 EXPECT_EQ(8, info.selectionEnd);
1949 EXPECT_EQ(7, info.compositionStart);
1950 EXPECT_EQ(10, info.compositionEnd);
1951 view()->OnUnselect();
1952 info = view()->webview()->textInputInfo();
1953 EXPECT_EQ(0, info.selectionStart);
1954 EXPECT_EQ(0, info.selectionEnd);
1958 TEST_F(RenderViewImplTest, OnExtendSelectionAndDelete) {
1959 // Load an HTML page consisting of an input field.
1964 "<input id=\"test1\" value=\"abcdefghijklmnopqrstuvwxyz\"></input>"
1967 ExecuteJavaScript("document.getElementById('test1').focus();");
1968 view()->OnSetEditableSelectionOffsets(10, 10);
1969 view()->OnExtendSelectionAndDelete(3, 4);
1970 blink::WebTextInputInfo info = view()->webview()->textInputInfo();
1971 EXPECT_EQ("abcdefgopqrstuvwxyz", info.value);
1972 EXPECT_EQ(7, info.selectionStart);
1973 EXPECT_EQ(7, info.selectionEnd);
1974 view()->OnSetEditableSelectionOffsets(4, 8);
1975 view()->OnExtendSelectionAndDelete(2, 5);
1976 info = view()->webview()->textInputInfo();
1977 EXPECT_EQ("abuvwxyz", info.value);
1978 EXPECT_EQ(2, info.selectionStart);
1979 EXPECT_EQ(2, info.selectionEnd);
1982 // Test that the navigating specific frames works correctly.
1983 TEST_F(RenderViewImplTest, NavigateFrame) {
1985 LoadHTML("hello <iframe srcdoc='fail' name='frame'></iframe>");
1987 // Navigate the frame only.
1988 ViewMsg_Navigate_Params nav_params;
1989 nav_params.url = GURL("data:text/html,world");
1990 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1991 nav_params.transition = PAGE_TRANSITION_TYPED;
1992 nav_params.current_history_list_length = 1;
1993 nav_params.current_history_list_offset = 0;
1994 nav_params.pending_history_list_offset = 1;
1995 nav_params.page_id = -1;
1996 nav_params.frame_to_navigate = "frame";
1997 view()->OnNavigate(nav_params);
1998 ProcessPendingMessages();
2000 // Copy the document content to std::wstring and compare with the
2002 const int kMaxOutputCharacters = 256;
2003 std::wstring output = base::UTF16ToWideHack(
2004 GetMainFrame()->contentAsText(kMaxOutputCharacters));
2005 EXPECT_EQ(output, L"hello \n\nworld");
2008 // This test ensures that a RenderFrame object is created for the top level
2009 // frame in the RenderView.
2010 TEST_F(RenderViewImplTest, BasicRenderFrame) {
2011 EXPECT_TRUE(view()->main_render_frame_.get());
2014 TEST_F(RenderViewImplTest, GetSSLStatusOfFrame) {
2015 LoadHTML("<!DOCTYPE html><html><body></body></html>");
2017 WebFrame* frame = GetMainFrame();
2018 SSLStatus ssl_status = view()->GetSSLStatusOfFrame(frame);
2019 EXPECT_FALSE(net::IsCertStatusError(ssl_status.cert_status));
2021 const_cast<blink::WebURLResponse&>(frame->dataSource()->response()).
2023 SerializeSecurityInfo(0, net::CERT_STATUS_ALL_ERRORS, 0, 0,
2024 SignedCertificateTimestampIDStatusList()));
2025 ssl_status = view()->GetSSLStatusOfFrame(frame);
2026 EXPECT_TRUE(net::IsCertStatusError(ssl_status.cert_status));
2029 TEST_F(RenderViewImplTest, MessageOrderInDidChangeSelection) {
2030 view()->OnSetInputMethodActive(true);
2031 view()->set_send_content_state_immediately(true);
2032 LoadHTML("<textarea id=\"test\"></textarea>");
2034 view()->handling_input_event_ = true;
2035 ExecuteJavaScript("document.getElementById('test').focus();");
2037 bool is_input_type_called = false;
2038 bool is_selection_called = false;
2039 size_t last_input_type = 0;
2040 size_t last_selection = 0;
2042 for (size_t i = 0; i < render_thread_->sink().message_count(); ++i) {
2043 const uint32 type = render_thread_->sink().GetMessageAt(i)->type();
2044 if (type == ViewHostMsg_TextInputTypeChanged::ID) {
2045 is_input_type_called = true;
2046 last_input_type = i;
2047 } else if (type == ViewHostMsg_SelectionChanged::ID) {
2048 is_selection_called = true;
2053 EXPECT_TRUE(is_input_type_called);
2054 EXPECT_TRUE(is_selection_called);
2056 // InputTypeChange shold be called earlier than SelectionChanged.
2057 EXPECT_LT(last_input_type, last_selection);
2060 class SuppressErrorPageTest : public RenderViewTest {
2062 virtual void SetUp() OVERRIDE {
2063 SetRendererClientForTesting(&client_);
2064 RenderViewTest::SetUp();
2067 RenderViewImpl* view() {
2068 return static_cast<RenderViewImpl*>(view_);
2072 class TestContentRendererClient : public ContentRendererClient {
2074 virtual bool ShouldSuppressErrorPage(RenderFrame* render_frame,
2075 const GURL& url) OVERRIDE {
2076 return url == GURL("http://example.com/suppress");
2079 virtual void GetNavigationErrorStrings(
2080 content::RenderView* render_view,
2081 blink::WebFrame* frame,
2082 const blink::WebURLRequest& failed_request,
2083 const blink::WebURLError& error,
2084 std::string* error_html,
2085 base::string16* error_description) OVERRIDE {
2087 *error_html = "A suffusion of yellow.";
2091 TestContentRendererClient client_;
2094 #if defined(OS_ANDROID)
2095 // Crashing on Android: http://crbug.com/311341
2096 #define MAYBE_Suppresses DISABLED_Suppresses
2098 #define MAYBE_Suppresses Suppresses
2101 TEST_F(SuppressErrorPageTest, MAYBE_Suppresses) {
2103 error.domain = WebString::fromUTF8(net::kErrorDomain);
2104 error.reason = net::ERR_FILE_NOT_FOUND;
2105 error.unreachableURL = GURL("http://example.com/suppress");
2106 WebFrame* web_frame = GetMainFrame();
2108 // Start a load that will reach provisional state synchronously,
2109 // but won't complete synchronously.
2110 ViewMsg_Navigate_Params params;
2111 params.page_id = -1;
2112 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
2113 params.url = GURL("data:text/html,test data");
2114 view()->OnNavigate(params);
2116 // An error occurred.
2117 view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
2118 const int kMaxOutputCharacters = 22;
2119 EXPECT_EQ("", UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
2122 #if defined(OS_ANDROID)
2123 // Crashing on Android: http://crbug.com/311341
2124 #define MAYBE_DoesNotSuppress DISABLED_DoesNotSuppress
2126 #define MAYBE_DoesNotSuppress DoesNotSuppress
2129 TEST_F(SuppressErrorPageTest, MAYBE_DoesNotSuppress) {
2131 error.domain = WebString::fromUTF8(net::kErrorDomain);
2132 error.reason = net::ERR_FILE_NOT_FOUND;
2133 error.unreachableURL = GURL("http://example.com/dont-suppress");
2134 WebFrame* web_frame = GetMainFrame();
2136 // Start a load that will reach provisional state synchronously,
2137 // but won't complete synchronously.
2138 ViewMsg_Navigate_Params params;
2139 params.page_id = -1;
2140 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
2141 params.url = GURL("data:text/html,test data");
2142 view()->OnNavigate(params);
2144 // An error occurred.
2145 view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
2146 ProcessPendingMessages();
2147 const int kMaxOutputCharacters = 22;
2148 EXPECT_EQ("A suffusion of yellow.",
2149 UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
2152 // Tests if IME API's candidatewindow* events sent from browser are handled
2154 TEST_F(RenderViewImplTest, SendCandidateWindowEvents) {
2155 // Sends an HTML with an <input> element and scripts to the renderer.
2156 // The script handles all 3 of candidatewindow* events for an
2157 // InputMethodContext object and once it received 'show', 'update', 'hide'
2158 // should appear in the result div.
2159 LoadHTML("<input id='test'>"
2160 "<div id='result'>Result: </div>"
2162 "window.onload = function() {"
2163 " var result = document.getElementById('result');"
2164 " var test = document.getElementById('test');"
2166 " var context = test.inputMethodContext;"
2168 " context.oncandidatewindowshow = function() {"
2169 " result.innerText += 'show'; };"
2170 " context.oncandidatewindowupdate = function(){"
2171 " result.innerText += 'update'; };"
2172 " context.oncandidatewindowhide = function(){"
2173 " result.innerText += 'hide'; };"
2178 // Fire candidatewindow events.
2179 view()->OnCandidateWindowShown();
2180 view()->OnCandidateWindowUpdated();
2181 view()->OnCandidateWindowHidden();
2183 // Retrieve the content and check if it is expected.
2184 const int kMaxOutputCharacters = 50;
2185 std::string output = base::UTF16ToUTF8(
2186 GetMainFrame()->contentAsText(kMaxOutputCharacters));
2187 EXPECT_EQ(output, "\nResult:showupdatehide");
2190 // Ensure the render view sends favicon url update events correctly.
2191 TEST_F(RenderViewImplTest, SendFaviconURLUpdateEvent) {
2192 // An event should be sent when a favicon url exists.
2195 "<link rel='icon' href='http://www.google.com/favicon.ico'>"
2198 EXPECT_TRUE(render_thread_->sink().GetFirstMessageMatching(
2199 ViewHostMsg_UpdateFaviconURL::ID));
2200 render_thread_->sink().ClearMessages();
2202 // An event should not be sent if no favicon url exists. This is an assumption
2203 // made by some of Chrome's favicon handling.
2208 EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
2209 ViewHostMsg_UpdateFaviconURL::ID));
2212 TEST_F(RenderViewImplTest, FocusElementCallsFocusedNodeChanged) {
2213 LoadHTML("<input id='test1' value='hello1'></input>"
2214 "<input id='test2' value='hello2'></input>");
2216 ExecuteJavaScript("document.getElementById('test1').focus();");
2217 const IPC::Message* msg1 = render_thread_->sink().GetFirstMessageMatching(
2218 ViewHostMsg_FocusedNodeChanged::ID);
2221 ViewHostMsg_FocusedNodeChanged::Param params;
2222 ViewHostMsg_FocusedNodeChanged::Read(msg1, ¶ms);
2223 EXPECT_TRUE(params.a);
2224 render_thread_->sink().ClearMessages();
2226 ExecuteJavaScript("document.getElementById('test2').focus();");
2227 const IPC::Message* msg2 = render_thread_->sink().GetFirstMessageMatching(
2228 ViewHostMsg_FocusedNodeChanged::ID);
2230 ViewHostMsg_FocusedNodeChanged::Read(msg2, ¶ms);
2231 EXPECT_TRUE(params.a);
2232 render_thread_->sink().ClearMessages();
2234 view()->webview()->clearFocusedNode();
2235 const IPC::Message* msg3 = render_thread_->sink().GetFirstMessageMatching(
2236 ViewHostMsg_FocusedNodeChanged::ID);
2238 ViewHostMsg_FocusedNodeChanged::Read(msg3, ¶ms);
2239 EXPECT_FALSE(params.a);
2240 render_thread_->sink().ClearMessages();
2243 } // namespace content