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/callback.h"
8 #include "base/memory/shared_memory.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/win/windows_version.h"
12 #include "content/common/frame_messages.h"
13 #include "content/common/ssl_status_serialization.h"
14 #include "content/common/view_messages.h"
15 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/native_web_keyboard_event.h"
17 #include "content/public/browser/web_ui_controller_factory.h"
18 #include "content/public/common/bindings_policy.h"
19 #include "content/public/common/page_zoom.h"
20 #include "content/public/common/url_constants.h"
21 #include "content/public/common/url_utils.h"
22 #include "content/public/renderer/document_state.h"
23 #include "content/public/renderer/history_item_serialization.h"
24 #include "content/public/renderer/navigation_state.h"
25 #include "content/public/test/browser_test_utils.h"
26 #include "content/public/test/render_view_test.h"
27 #include "content/public/test/test_utils.h"
28 #include "content/renderer/render_view_impl.h"
29 #include "content/shell/browser/shell.h"
30 #include "content/shell/browser/shell_browser_context.h"
31 #include "content/test/mock_keyboard.h"
32 #include "net/base/net_errors.h"
33 #include "net/cert/cert_status_flags.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "third_party/WebKit/public/platform/WebData.h"
36 #include "third_party/WebKit/public/platform/WebHTTPBody.h"
37 #include "third_party/WebKit/public/platform/WebString.h"
38 #include "third_party/WebKit/public/platform/WebURLResponse.h"
39 #include "third_party/WebKit/public/web/WebDataSource.h"
40 #include "third_party/WebKit/public/web/WebFrame.h"
41 #include "third_party/WebKit/public/web/WebHistoryItem.h"
42 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
43 #include "third_party/WebKit/public/web/WebView.h"
44 #include "third_party/WebKit/public/web/WebWindowFeatures.h"
45 #include "ui/events/keycodes/keyboard_codes.h"
46 #include "ui/gfx/codec/jpeg_codec.h"
47 #include "ui/gfx/range/range.h"
49 #if defined(OS_LINUX) && !defined(USE_AURA)
50 #include "ui/base/gtk/event_synthesis_gtk.h"
54 #include "ui/events/event.h"
57 #if defined(USE_AURA) && defined(USE_X11)
59 #include "ui/events/event_constants.h"
60 #include "ui/events/keycodes/keyboard_code_conversion.h"
61 #include "ui/events/test/events_test_utils_x11.h"
64 #if defined(USE_OZONE)
65 #include "ui/events/keycodes/keyboard_code_conversion.h"
68 using blink::WebFrame;
69 using blink::WebInputEvent;
70 using blink::WebMouseEvent;
71 using blink::WebRuntimeFeatures;
72 using blink::WebString;
73 using blink::WebTextDirection;
74 using blink::WebURLError;
80 #if (defined(USE_AURA) && defined(USE_X11)) || defined(USE_OZONE)
81 // Converts MockKeyboard::Modifiers to ui::EventFlags.
82 int ConvertMockKeyboardModifier(MockKeyboard::Modifiers modifiers) {
83 static struct ModifierMap {
84 MockKeyboard::Modifiers src;
87 { MockKeyboard::LEFT_SHIFT, ui::EF_SHIFT_DOWN },
88 { MockKeyboard::RIGHT_SHIFT, ui::EF_SHIFT_DOWN },
89 { MockKeyboard::LEFT_CONTROL, ui::EF_CONTROL_DOWN },
90 { MockKeyboard::RIGHT_CONTROL, ui::EF_CONTROL_DOWN },
91 { MockKeyboard::LEFT_ALT, ui::EF_ALT_DOWN },
92 { MockKeyboard::RIGHT_ALT, ui::EF_ALT_DOWN },
95 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kModifierMap); ++i) {
96 if (kModifierMap[i].src & modifiers) {
97 flags |= kModifierMap[i].dst;
104 class WebUITestWebUIControllerFactory : public WebUIControllerFactory {
106 virtual WebUIController* CreateWebUIControllerForURL(
107 WebUI* web_ui, const GURL& url) const OVERRIDE {
110 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
111 const GURL& url) const OVERRIDE {
112 return WebUI::kNoWebUI;
114 virtual bool UseWebUIForURL(BrowserContext* browser_context,
115 const GURL& url) const OVERRIDE {
116 return HasWebUIScheme(url);
118 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
119 const GURL& url) const OVERRIDE {
120 return HasWebUIScheme(url);
124 class RenderViewImplTest : public RenderViewTest {
126 RenderViewImplTest() {
127 // Attach a pseudo keyboard device to this object.
128 mock_keyboard_.reset(new MockKeyboard());
131 virtual ~RenderViewImplTest() {}
133 virtual void SetUp() OVERRIDE {
134 RenderViewTest::SetUp();
135 // Enable Blink's experimental and test only features so that test code
136 // does not have to bother enabling each feature.
137 WebRuntimeFeatures::enableExperimentalFeatures(true);
138 WebRuntimeFeatures::enableTestOnlyFeatures(true);
141 RenderViewImpl* view() {
142 return static_cast<RenderViewImpl*>(view_);
145 RenderFrameImpl* frame() {
146 return static_cast<RenderFrameImpl*>(view()->GetMainRenderFrame());
149 // Sends IPC messages that emulates a key-press event.
150 int SendKeyEvent(MockKeyboard::Layout layout,
152 MockKeyboard::Modifiers modifiers,
153 base::string16* output) {
155 // Retrieve the Unicode character for the given tuple (keyboard-layout,
156 // key-code, and modifiers).
157 // Exit when a keyboard-layout driver cannot assign a Unicode character to
158 // the tuple to prevent sending an invalid key code to the RenderView
160 CHECK(mock_keyboard_.get());
162 int length = mock_keyboard_->GetCharacters(layout, key_code, modifiers,
167 // Create IPC messages from Windows messages and send them to our
169 // A keyboard event of Windows consists of three Windows messages:
170 // WM_KEYDOWN, WM_CHAR, and WM_KEYUP.
171 // WM_KEYDOWN and WM_KEYUP sends virtual-key codes. On the other hand,
172 // WM_CHAR sends a composed Unicode character.
173 MSG msg1 = { NULL, WM_KEYDOWN, key_code, 0 };
174 #if defined(USE_AURA)
175 ui::KeyEvent evt1(msg1, false);
176 NativeWebKeyboardEvent keydown_event(&evt1);
178 NativeWebKeyboardEvent keydown_event(msg1);
180 SendNativeKeyEvent(keydown_event);
182 MSG msg2 = { NULL, WM_CHAR, (*output)[0], 0 };
183 #if defined(USE_AURA)
184 ui::KeyEvent evt2(msg2, true);
185 NativeWebKeyboardEvent char_event(&evt2);
187 NativeWebKeyboardEvent char_event(msg2);
189 SendNativeKeyEvent(char_event);
191 MSG msg3 = { NULL, WM_KEYUP, key_code, 0 };
192 #if defined(USE_AURA)
193 ui::KeyEvent evt3(msg3, false);
194 NativeWebKeyboardEvent keyup_event(&evt3);
196 NativeWebKeyboardEvent keyup_event(msg3);
198 SendNativeKeyEvent(keyup_event);
201 #elif defined(USE_AURA) && defined(USE_X11)
202 // We ignore |layout|, which means we are only testing the layout of the
203 // current locale. TODO(mazda): fix this to respect |layout|.
205 const int flags = ConvertMockKeyboardModifier(modifiers);
207 ui::ScopedXI2Event xevent;
208 xevent.InitKeyEvent(ui::ET_KEY_PRESSED,
209 static_cast<ui::KeyboardCode>(key_code),
211 ui::KeyEvent event1(xevent, false);
212 NativeWebKeyboardEvent keydown_event(&event1);
213 SendNativeKeyEvent(keydown_event);
215 xevent.InitKeyEvent(ui::ET_KEY_PRESSED,
216 static_cast<ui::KeyboardCode>(key_code),
218 ui::KeyEvent event2(xevent, true);
219 NativeWebKeyboardEvent char_event(&event2);
220 SendNativeKeyEvent(char_event);
222 xevent.InitKeyEvent(ui::ET_KEY_RELEASED,
223 static_cast<ui::KeyboardCode>(key_code),
225 ui::KeyEvent event3(xevent, false);
226 NativeWebKeyboardEvent keyup_event(&event3);
227 SendNativeKeyEvent(keyup_event);
229 long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
231 output->assign(1, static_cast<base::char16>(c));
233 #elif defined(USE_OZONE)
234 const int flags = ConvertMockKeyboardModifier(modifiers);
236 // Ozone's native events are ui::Events. So first create the "native" event,
237 // then create the actual ui::KeyEvent with the native event.
238 ui::KeyEvent keydown_native_event(ui::ET_KEY_PRESSED,
239 static_cast<ui::KeyboardCode>(key_code),
242 ui::KeyEvent keydown_event(&keydown_native_event, false);
243 NativeWebKeyboardEvent keydown_web_event(&keydown_event);
244 SendNativeKeyEvent(keydown_web_event);
246 ui::KeyEvent char_native_event(ui::ET_KEY_PRESSED,
247 static_cast<ui::KeyboardCode>(key_code),
250 ui::KeyEvent char_event(&char_native_event, true);
251 NativeWebKeyboardEvent char_web_event(&char_event);
252 SendNativeKeyEvent(char_web_event);
254 ui::KeyEvent keyup_native_event(ui::ET_KEY_RELEASED,
255 static_cast<ui::KeyboardCode>(key_code),
258 ui::KeyEvent keyup_event(&keyup_native_event, false);
259 NativeWebKeyboardEvent keyup_web_event(&keyup_event);
260 SendNativeKeyEvent(keyup_web_event);
262 long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
264 output->assign(1, static_cast<base::char16>(c));
266 #elif defined(TOOLKIT_GTK)
267 // We ignore |layout|, which means we are only testing the layout of the
268 // current locale. TODO(estade): fix this to respect |layout|.
269 std::vector<GdkEvent*> events;
270 ui::SynthesizeKeyPressEvents(
271 NULL, static_cast<ui::KeyboardCode>(key_code),
272 modifiers & (MockKeyboard::LEFT_CONTROL | MockKeyboard::RIGHT_CONTROL),
273 modifiers & (MockKeyboard::LEFT_SHIFT | MockKeyboard::RIGHT_SHIFT),
274 modifiers & (MockKeyboard::LEFT_ALT | MockKeyboard::RIGHT_ALT),
277 guint32 unicode_key = 0;
278 for (size_t i = 0; i < events.size(); ++i) {
279 // Only send the up/down events for key press itself (skip the up/down
280 // events for the modifier keys).
281 if ((i + 1) == (events.size() / 2) || i == (events.size() / 2)) {
282 unicode_key = gdk_keyval_to_unicode(events[i]->key.keyval);
283 NativeWebKeyboardEvent webkit_event(events[i]);
284 SendNativeKeyEvent(webkit_event);
286 // Need to add a char event after the key down.
287 if (webkit_event.type == blink::WebInputEvent::RawKeyDown) {
288 NativeWebKeyboardEvent char_event = webkit_event;
289 char_event.type = blink::WebInputEvent::Char;
290 char_event.skip_in_browser = true;
291 SendNativeKeyEvent(char_event);
294 gdk_event_free(events[i]);
297 output->assign(1, static_cast<base::char16>(unicode_key));
306 scoped_ptr<MockKeyboard> mock_keyboard_;
311 // Test that we get form state change notifications when input fields change.
312 TEST_F(RenderViewImplTest, DISABLED_OnNavStateChanged) {
313 // Don't want any delay for form state sync changes. This will still post a
314 // message so updates will get coalesced, but as soon as we spin the message
315 // loop, it will generate an update.
316 view()->set_send_content_state_immediately(true);
318 LoadHTML("<input type=\"text\" id=\"elt_text\"></input>");
320 // We should NOT have gotten a form state change notification yet.
321 EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
322 ViewHostMsg_UpdateState::ID));
323 render_thread_->sink().ClearMessages();
325 // Change the value of the input. We should have gotten an update state
326 // notification. We need to spin the message loop to catch this update.
327 ExecuteJavaScript("document.getElementById('elt_text').value = 'foo';");
328 ProcessPendingMessages();
329 EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
330 ViewHostMsg_UpdateState::ID));
333 TEST_F(RenderViewImplTest, OnNavigationHttpPost) {
334 FrameMsg_Navigate_Params nav_params;
336 // An http url will trigger a resource load so cannot be used here.
337 nav_params.url = GURL("data:text/html,<div>Page</div>");
338 nav_params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
339 nav_params.transition = PAGE_TRANSITION_TYPED;
340 nav_params.page_id = -1;
341 nav_params.is_post = true;
344 const unsigned char* raw_data = reinterpret_cast<const unsigned char*>(
346 const unsigned int length = 11;
347 const std::vector<unsigned char> post_data(raw_data, raw_data + length);
348 nav_params.browser_initiated_post_data = post_data;
350 frame()->OnNavigate(nav_params);
351 ProcessPendingMessages();
353 const IPC::Message* frame_navigate_msg =
354 render_thread_->sink().GetUniqueMessageMatching(
355 FrameHostMsg_DidCommitProvisionalLoad::ID);
356 EXPECT_TRUE(frame_navigate_msg);
358 FrameHostMsg_DidCommitProvisionalLoad::Param host_nav_params;
359 FrameHostMsg_DidCommitProvisionalLoad::Read(frame_navigate_msg,
361 EXPECT_TRUE(host_nav_params.a.is_post);
363 // Check post data sent to browser matches
364 EXPECT_TRUE(host_nav_params.a.page_state.IsValid());
365 const blink::WebHistoryItem item = PageStateToHistoryItem(
366 host_nav_params.a.page_state);
367 blink::WebHTTPBody body = item.httpBody();
368 blink::WebHTTPBody::Element element;
369 bool successful = body.elementAt(0, element);
370 EXPECT_TRUE(successful);
371 EXPECT_EQ(blink::WebHTTPBody::Element::TypeData, element.type);
372 EXPECT_EQ(length, element.data.size());
373 EXPECT_EQ(0, memcmp(raw_data, element.data.data(), length));
376 TEST_F(RenderViewImplTest, DecideNavigationPolicy) {
377 WebUITestWebUIControllerFactory factory;
378 WebUIControllerFactory::RegisterFactory(&factory);
381 state.set_navigation_state(NavigationState::CreateContentInitiated());
383 // Navigations to normal HTTP URLs can be handled locally.
384 blink::WebURLRequest request(GURL("http://foo.com"));
385 blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
389 blink::WebNavigationTypeLinkClicked,
390 blink::WebNavigationPolicyCurrentTab,
392 EXPECT_EQ(blink::WebNavigationPolicyCurrentTab, policy);
394 // Verify that form posts to WebUI URLs will be sent to the browser process.
395 blink::WebURLRequest form_request(GURL("chrome://foo"));
396 form_request.setHTTPMethod("POST");
397 policy = frame()->decidePolicyForNavigation(
401 blink::WebNavigationTypeFormSubmitted,
402 blink::WebNavigationPolicyCurrentTab,
404 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
406 // Verify that popup links to WebUI URLs also are sent to browser.
407 blink::WebURLRequest popup_request(GURL("chrome://foo"));
408 policy = frame()->decidePolicyForNavigation(
412 blink::WebNavigationTypeLinkClicked,
413 blink::WebNavigationPolicyNewForegroundTab,
415 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
418 TEST_F(RenderViewImplTest, DecideNavigationPolicyHandlesAllTopLevel) {
420 state.set_navigation_state(NavigationState::CreateContentInitiated());
422 RendererPreferences prefs = view()->renderer_preferences();
423 prefs.browser_handles_all_top_level_requests = true;
424 view()->OnSetRendererPrefs(prefs);
426 const blink::WebNavigationType kNavTypes[] = {
427 blink::WebNavigationTypeLinkClicked,
428 blink::WebNavigationTypeFormSubmitted,
429 blink::WebNavigationTypeBackForward,
430 blink::WebNavigationTypeReload,
431 blink::WebNavigationTypeFormResubmitted,
432 blink::WebNavigationTypeOther,
435 blink::WebURLRequest request(GURL("http://foo.com"));
436 for (size_t i = 0; i < arraysize(kNavTypes); ++i) {
437 blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
442 blink::WebNavigationPolicyCurrentTab,
444 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
448 TEST_F(RenderViewImplTest, DecideNavigationPolicyForWebUI) {
449 // Enable bindings to simulate a WebUI view.
450 view()->OnAllowBindings(BINDINGS_POLICY_WEB_UI);
453 state.set_navigation_state(NavigationState::CreateContentInitiated());
455 // Navigations to normal HTTP URLs will be sent to browser process.
456 blink::WebURLRequest request(GURL("http://foo.com"));
457 blink::WebNavigationPolicy policy = frame()->decidePolicyForNavigation(
461 blink::WebNavigationTypeLinkClicked,
462 blink::WebNavigationPolicyCurrentTab,
464 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
466 // Navigations to WebUI URLs will also be sent to browser process.
467 blink::WebURLRequest webui_request(GURL("chrome://foo"));
468 policy = frame()->decidePolicyForNavigation(
472 blink::WebNavigationTypeLinkClicked,
473 blink::WebNavigationPolicyCurrentTab,
475 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
477 // Verify that form posts to data URLs will be sent to the browser process.
478 blink::WebURLRequest data_request(GURL("data:text/html,foo"));
479 data_request.setHTTPMethod("POST");
480 policy = frame()->decidePolicyForNavigation(
484 blink::WebNavigationTypeFormSubmitted,
485 blink::WebNavigationPolicyCurrentTab,
487 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
489 // Verify that a popup that creates a view first and then navigates to a
490 // normal HTTP URL will be sent to the browser process, even though the
491 // new view does not have any enabled_bindings_.
492 blink::WebURLRequest popup_request(GURL("http://foo.com"));
493 blink::WebView* new_web_view = view()->createView(
494 GetMainFrame(), popup_request, blink::WebWindowFeatures(), "foo",
495 blink::WebNavigationPolicyNewForegroundTab, false);
496 RenderViewImpl* new_view = RenderViewImpl::FromWebView(new_web_view);
497 policy = static_cast<RenderFrameImpl*>(new_view->GetMainRenderFrame())->
498 decidePolicyForNavigation(
499 new_web_view->mainFrame(),
502 blink::WebNavigationTypeLinkClicked,
503 blink::WebNavigationPolicyNewForegroundTab,
505 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
507 // Clean up after the new view so we don't leak it.
512 // Ensure the RenderViewImpl sends an ACK to a SwapOut request, even if it is
513 // already swapped out. http://crbug.com/93427.
514 TEST_F(RenderViewImplTest, SendSwapOutACK) {
515 LoadHTML("<div>Page A</div>");
516 int initial_page_id = view()->GetPageId();
518 // Respond to a swap out request.
521 // Ensure the swap out commits synchronously.
522 EXPECT_NE(initial_page_id, view()->GetPageId());
524 // Check for a valid OnSwapOutACK.
525 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
526 ViewHostMsg_SwapOut_ACK::ID);
529 // It is possible to get another swap out request. Ensure that we send
530 // an ACK, even if we don't have to do anything else.
531 render_thread_->sink().ClearMessages();
533 const IPC::Message* msg2 = render_thread_->sink().GetUniqueMessageMatching(
534 ViewHostMsg_SwapOut_ACK::ID);
537 // If we navigate back to this RenderView, ensure we don't send a state
538 // update for the swapped out URL. (http://crbug.com/72235)
539 FrameMsg_Navigate_Params nav_params;
540 nav_params.url = GURL("data:text/html,<div>Page B</div>");
541 nav_params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
542 nav_params.transition = PAGE_TRANSITION_TYPED;
543 nav_params.current_history_list_length = 1;
544 nav_params.current_history_list_offset = 0;
545 nav_params.pending_history_list_offset = 1;
546 nav_params.page_id = -1;
547 frame()->OnNavigate(nav_params);
548 ProcessPendingMessages();
549 const IPC::Message* msg3 = render_thread_->sink().GetUniqueMessageMatching(
550 ViewHostMsg_UpdateState::ID);
554 // Ensure the RenderViewImpl reloads the previous page if a reload request
555 // arrives while it is showing swappedout://. http://crbug.com/143155.
556 TEST_F(RenderViewImplTest, ReloadWhileSwappedOut) {
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 // Back to page A (page_id 1) and commit.
575 FrameMsg_Navigate_Params params_A;
576 params_A.navigation_type = FrameMsg_Navigate_Type::NORMAL;
577 params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
578 params_A.current_history_list_length = 2;
579 params_A.current_history_list_offset = 1;
580 params_A.pending_history_list_offset = 0;
581 params_A.page_id = 1;
582 params_A.page_state = state_A;
583 frame()->OnNavigate(params_A);
584 ProcessPendingMessages();
586 // Respond to a swap out request.
589 // Check for a OnSwapOutACK.
590 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
591 ViewHostMsg_SwapOut_ACK::ID);
593 render_thread_->sink().ClearMessages();
595 // It is possible to get a reload request at this point, containing the
596 // params.page_state of the initial page (e.g., if the new page fails the
597 // provisional load in the renderer process, after we unload the old page).
598 // Ensure the old page gets reloaded, not swappedout://.
599 FrameMsg_Navigate_Params nav_params;
600 nav_params.url = GURL("data:text/html,<div>Page A</div>");
601 nav_params.navigation_type = FrameMsg_Navigate_Type::RELOAD;
602 nav_params.transition = PAGE_TRANSITION_RELOAD;
603 nav_params.current_history_list_length = 2;
604 nav_params.current_history_list_offset = 0;
605 nav_params.pending_history_list_offset = 0;
606 nav_params.page_id = 1;
607 nav_params.page_state = state_A;
608 frame()->OnNavigate(nav_params);
609 ProcessPendingMessages();
611 // Verify page A committed, not swappedout://.
612 const IPC::Message* frame_navigate_msg =
613 render_thread_->sink().GetUniqueMessageMatching(
614 FrameHostMsg_DidCommitProvisionalLoad::ID);
615 EXPECT_TRUE(frame_navigate_msg);
617 // Read URL out of the parent trait of the params object.
618 FrameHostMsg_DidCommitProvisionalLoad::Param commit_params;
619 FrameHostMsg_DidCommitProvisionalLoad::Read(frame_navigate_msg,
621 EXPECT_NE(GURL("swappedout://"), commit_params.a.url);
625 // Test that we get the correct UpdateState message when we go back twice
626 // quickly without committing. Regression test for http://crbug.com/58082.
627 // Disabled: http://crbug.com/157357 .
628 TEST_F(RenderViewImplTest, DISABLED_LastCommittedUpdateState) {
630 LoadHTML("<div>Page A</div>");
632 // Load page B, which will trigger an UpdateState message for page A.
633 LoadHTML("<div>Page B</div>");
635 // Check for a valid UpdateState message for page A.
636 ProcessPendingMessages();
637 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
638 ViewHostMsg_UpdateState::ID);
642 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
643 EXPECT_EQ(1, page_id_A);
644 render_thread_->sink().ClearMessages();
646 // Load page C, which will trigger an UpdateState message for page B.
647 LoadHTML("<div>Page C</div>");
649 // Check for a valid UpdateState for page B.
650 ProcessPendingMessages();
651 const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
652 ViewHostMsg_UpdateState::ID);
656 ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
657 EXPECT_EQ(2, page_id_B);
658 EXPECT_NE(state_A, state_B);
659 render_thread_->sink().ClearMessages();
661 // Load page D, which will trigger an UpdateState message for page C.
662 LoadHTML("<div>Page D</div>");
664 // Check for a valid UpdateState for page C.
665 ProcessPendingMessages();
666 const IPC::Message* msg_C = render_thread_->sink().GetUniqueMessageMatching(
667 ViewHostMsg_UpdateState::ID);
671 ViewHostMsg_UpdateState::Read(msg_C, &page_id_C, &state_C);
672 EXPECT_EQ(3, page_id_C);
673 EXPECT_NE(state_B, state_C);
674 render_thread_->sink().ClearMessages();
676 // Go back to C and commit, preparing for our real test.
677 FrameMsg_Navigate_Params params_C;
678 params_C.navigation_type = FrameMsg_Navigate_Type::NORMAL;
679 params_C.transition = PAGE_TRANSITION_FORWARD_BACK;
680 params_C.current_history_list_length = 4;
681 params_C.current_history_list_offset = 3;
682 params_C.pending_history_list_offset = 2;
683 params_C.page_id = 3;
684 params_C.page_state = state_C;
685 frame()->OnNavigate(params_C);
686 ProcessPendingMessages();
687 render_thread_->sink().ClearMessages();
689 // Go back twice quickly, such that page B does not have a chance to commit.
690 // This leads to two changes to the back/forward list but only one change to
691 // the RenderView's page ID.
693 // Back to page B (page_id 2), without committing.
694 FrameMsg_Navigate_Params params_B;
695 params_B.navigation_type = FrameMsg_Navigate_Type::NORMAL;
696 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
697 params_B.current_history_list_length = 4;
698 params_B.current_history_list_offset = 2;
699 params_B.pending_history_list_offset = 1;
700 params_B.page_id = 2;
701 params_B.page_state = state_B;
702 frame()->OnNavigate(params_B);
704 // Back to page A (page_id 1) and commit.
705 FrameMsg_Navigate_Params params;
706 params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
707 params.transition = PAGE_TRANSITION_FORWARD_BACK;
708 params_B.current_history_list_length = 4;
709 params_B.current_history_list_offset = 2;
710 params_B.pending_history_list_offset = 0;
712 params.page_state = state_A;
713 frame()->OnNavigate(params);
714 ProcessPendingMessages();
716 // Now ensure that the UpdateState message we receive is consistent
717 // and represents page C in both page_id and state.
718 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
719 ViewHostMsg_UpdateState::ID);
723 ViewHostMsg_UpdateState::Read(msg, &page_id, &state);
724 EXPECT_EQ(page_id_C, page_id);
725 EXPECT_NE(state_A, state);
726 EXPECT_NE(state_B, state);
727 EXPECT_EQ(state_C, state);
730 // Test that the history_page_ids_ list can reveal when a stale back/forward
731 // navigation arrives from the browser and can be ignored. See
732 // http://crbug.com/86758.
733 TEST_F(RenderViewImplTest, StaleNavigationsIgnored) {
735 LoadHTML("<div>Page A</div>");
736 EXPECT_EQ(1, view()->history_list_length_);
737 EXPECT_EQ(0, view()->history_list_offset_);
738 EXPECT_EQ(1, view()->history_page_ids_[0]);
740 // Load page B, which will trigger an UpdateState message for page A.
741 LoadHTML("<div>Page B</div>");
742 EXPECT_EQ(2, view()->history_list_length_);
743 EXPECT_EQ(1, view()->history_list_offset_);
744 EXPECT_EQ(2, view()->history_page_ids_[1]);
746 // Check for a valid UpdateState message for page A.
747 ProcessPendingMessages();
748 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
749 ViewHostMsg_UpdateState::ID);
753 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
754 EXPECT_EQ(1, page_id_A);
755 render_thread_->sink().ClearMessages();
757 // Back to page A (page_id 1) and commit.
758 FrameMsg_Navigate_Params params_A;
759 params_A.navigation_type = FrameMsg_Navigate_Type::NORMAL;
760 params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
761 params_A.current_history_list_length = 2;
762 params_A.current_history_list_offset = 1;
763 params_A.pending_history_list_offset = 0;
764 params_A.page_id = 1;
765 params_A.page_state = state_A;
766 frame()->OnNavigate(params_A);
767 ProcessPendingMessages();
769 // A new navigation commits, clearing the forward history.
770 LoadHTML("<div>Page C</div>");
771 EXPECT_EQ(2, view()->history_list_length_);
772 EXPECT_EQ(1, view()->history_list_offset_);
773 EXPECT_EQ(3, view()->history_page_ids_[1]);
775 // The browser then sends a stale navigation to B, which should be ignored.
776 FrameMsg_Navigate_Params params_B;
777 params_B.navigation_type = FrameMsg_Navigate_Type::NORMAL;
778 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
779 params_B.current_history_list_length = 2;
780 params_B.current_history_list_offset = 0;
781 params_B.pending_history_list_offset = 1;
782 params_B.page_id = 2;
783 params_B.page_state = state_A; // Doesn't matter, just has to be present.
784 frame()->OnNavigate(params_B);
786 // State should be unchanged.
787 EXPECT_EQ(2, view()->history_list_length_);
788 EXPECT_EQ(1, view()->history_list_offset_);
789 EXPECT_EQ(3, view()->history_page_ids_[1]);
792 // Test that we do not ignore navigations after the entry limit is reached,
793 // in which case the browser starts dropping entries from the front. In this
794 // case, we'll see a page_id mismatch but the RenderView's id will be older,
795 // not newer, than params.page_id. Use this as a cue that we should update the
796 // state and not treat it like a navigation to a cropped forward history item.
797 // See http://crbug.com/89798.
798 TEST_F(RenderViewImplTest, DontIgnoreBackAfterNavEntryLimit) {
800 LoadHTML("<div>Page A</div>");
801 EXPECT_EQ(1, view()->history_list_length_);
802 EXPECT_EQ(0, view()->history_list_offset_);
803 EXPECT_EQ(1, view()->history_page_ids_[0]);
805 // Load page B, which will trigger an UpdateState message for page A.
806 LoadHTML("<div>Page B</div>");
807 EXPECT_EQ(2, view()->history_list_length_);
808 EXPECT_EQ(1, view()->history_list_offset_);
809 EXPECT_EQ(2, view()->history_page_ids_[1]);
811 // Check for a valid UpdateState message for page A.
812 ProcessPendingMessages();
813 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
814 ViewHostMsg_UpdateState::ID);
818 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
819 EXPECT_EQ(1, page_id_A);
820 render_thread_->sink().ClearMessages();
822 // Load page C, which will trigger an UpdateState message for page B.
823 LoadHTML("<div>Page C</div>");
824 EXPECT_EQ(3, view()->history_list_length_);
825 EXPECT_EQ(2, view()->history_list_offset_);
826 EXPECT_EQ(3, view()->history_page_ids_[2]);
828 // Check for a valid UpdateState message for page B.
829 ProcessPendingMessages();
830 const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
831 ViewHostMsg_UpdateState::ID);
835 ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
836 EXPECT_EQ(2, page_id_B);
837 render_thread_->sink().ClearMessages();
839 // Suppose the browser has limited the number of NavigationEntries to 2.
840 // It has now dropped the first entry, but the renderer isn't notified.
841 // Ensure that going back to page B (page_id 2) at offset 0 is successful.
842 FrameMsg_Navigate_Params params_B;
843 params_B.navigation_type = FrameMsg_Navigate_Type::NORMAL;
844 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
845 params_B.current_history_list_length = 2;
846 params_B.current_history_list_offset = 1;
847 params_B.pending_history_list_offset = 0;
848 params_B.page_id = 2;
849 params_B.page_state = state_B;
850 frame()->OnNavigate(params_B);
851 ProcessPendingMessages();
853 EXPECT_EQ(2, view()->history_list_length_);
854 EXPECT_EQ(0, view()->history_list_offset_);
855 EXPECT_EQ(2, view()->history_page_ids_[0]);
858 // Test that our IME backend sends a notification message when the input focus
860 TEST_F(RenderViewImplTest, OnImeTypeChanged) {
861 // Enable our IME backend code.
862 view()->OnSetInputMethodActive(true);
864 // Load an HTML page consisting of two input fields.
865 view()->set_send_content_state_immediately(true);
870 "<input id=\"test1\" type=\"text\" value=\"some text\"></input>"
871 "<input id=\"test2\" type=\"password\"></input>"
872 "<input id=\"test3\" type=\"text\" inputmode=\"verbatim\"></input>"
873 "<input id=\"test4\" type=\"text\" inputmode=\"latin\"></input>"
874 "<input id=\"test5\" type=\"text\" inputmode=\"latin-name\"></input>"
875 "<input id=\"test6\" type=\"text\" inputmode=\"latin-prose\">"
877 "<input id=\"test7\" type=\"text\" inputmode=\"full-width-latin\">"
879 "<input id=\"test8\" type=\"text\" inputmode=\"kana\"></input>"
880 "<input id=\"test9\" type=\"text\" inputmode=\"katakana\"></input>"
881 "<input id=\"test10\" type=\"text\" inputmode=\"numeric\"></input>"
882 "<input id=\"test11\" type=\"text\" inputmode=\"tel\"></input>"
883 "<input id=\"test12\" type=\"text\" inputmode=\"email\"></input>"
884 "<input id=\"test13\" type=\"text\" inputmode=\"url\"></input>"
885 "<input id=\"test14\" type=\"text\" inputmode=\"unknown\"></input>"
886 "<input id=\"test15\" type=\"text\" inputmode=\"verbatim\"></input>"
889 render_thread_->sink().ClearMessages();
891 struct InputModeTestCase {
892 const char* input_id;
893 ui::TextInputMode expected_mode;
895 static const InputModeTestCase kInputModeTestCases[] = {
896 {"test1", ui::TEXT_INPUT_MODE_DEFAULT},
897 {"test3", ui::TEXT_INPUT_MODE_VERBATIM},
898 {"test4", ui::TEXT_INPUT_MODE_LATIN},
899 {"test5", ui::TEXT_INPUT_MODE_LATIN_NAME},
900 {"test6", ui::TEXT_INPUT_MODE_LATIN_PROSE},
901 {"test7", ui::TEXT_INPUT_MODE_FULL_WIDTH_LATIN},
902 {"test8", ui::TEXT_INPUT_MODE_KANA},
903 {"test9", ui::TEXT_INPUT_MODE_KATAKANA},
904 {"test10", ui::TEXT_INPUT_MODE_NUMERIC},
905 {"test11", ui::TEXT_INPUT_MODE_TEL},
906 {"test12", ui::TEXT_INPUT_MODE_EMAIL},
907 {"test13", ui::TEXT_INPUT_MODE_URL},
908 {"test14", ui::TEXT_INPUT_MODE_DEFAULT},
909 {"test15", ui::TEXT_INPUT_MODE_VERBATIM},
912 const int kRepeatCount = 10;
913 for (int i = 0; i < kRepeatCount; i++) {
914 // Move the input focus to the first <input> element, where we should
916 ExecuteJavaScript("document.getElementById('test1').focus();");
917 ProcessPendingMessages();
918 render_thread_->sink().ClearMessages();
920 // Update the IME status and verify if our IME backend sends an IPC message
922 view()->UpdateTextInputType();
923 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
924 EXPECT_TRUE(msg != NULL);
925 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
926 ui::TextInputType type;
927 bool can_compose_inline = false;
928 ui::TextInputMode input_mode = ui::TEXT_INPUT_MODE_DEFAULT;
929 ViewHostMsg_TextInputTypeChanged::Read(msg,
932 &can_compose_inline);
933 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, type);
934 EXPECT_EQ(true, can_compose_inline);
936 // Move the input focus to the second <input> element, where we should
938 ExecuteJavaScript("document.getElementById('test2').focus();");
939 ProcessPendingMessages();
940 render_thread_->sink().ClearMessages();
942 // Update the IME status and verify if our IME backend sends an IPC message
943 // to de-activate IMEs.
944 view()->UpdateTextInputType();
945 msg = render_thread_->sink().GetMessageAt(0);
946 EXPECT_TRUE(msg != NULL);
947 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
948 ViewHostMsg_TextInputTypeChanged::Read(msg,
951 &can_compose_inline);
952 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, type);
954 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInputModeTestCases); i++) {
955 const InputModeTestCase* test_case = &kInputModeTestCases[i];
956 std::string javascript =
957 base::StringPrintf("document.getElementById('%s').focus();",
958 test_case->input_id);
959 // Move the input focus to the target <input> element, where we should
961 ExecuteJavaScriptAndReturnIntValue(base::ASCIIToUTF16(javascript), NULL);
962 ProcessPendingMessages();
963 render_thread_->sink().ClearMessages();
965 // Update the IME status and verify if our IME backend sends an IPC
966 // message to activate IMEs.
967 view()->UpdateTextInputType();
968 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
969 EXPECT_TRUE(msg != NULL);
970 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
971 ViewHostMsg_TextInputTypeChanged::Read(msg,
974 &can_compose_inline);
975 EXPECT_EQ(test_case->expected_mode, input_mode);
980 // Test that our IME backend can compose CJK words.
981 // Our IME front-end sends many platform-independent messages to the IME backend
982 // while it composes CJK words. This test sends the minimal messages captured
983 // on my local environment directly to the IME backend to verify if the backend
984 // can compose CJK words without any problems.
985 // This test uses an array of command sets because an IME composotion does not
986 // only depends on IME events, but also depends on window events, e.g. moving
987 // the window focus while composing a CJK text. To handle such complicated
988 // cases, this test should not only call IME-related functions in the
989 // RenderWidget class, but also call some RenderWidget members, e.g.
990 // ExecuteJavaScript(), RenderWidget::OnSetFocus(), etc.
991 TEST_F(RenderViewImplTest, ImeComposition) {
997 IME_CONFIRMCOMPOSITION,
998 IME_CANCELCOMPOSITION
1003 int selection_start;
1005 const wchar_t* ime_string;
1006 const wchar_t* result;
1008 static const ImeMessage kImeMessages[] = {
1009 // Scenario 1: input a Chinese word with Microsoft IME (on Vista).
1010 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
1011 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
1012 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
1013 {IME_SETCOMPOSITION, false, 1, 1, L"n", L"n"},
1014 {IME_SETCOMPOSITION, false, 2, 2, L"ni", L"ni"},
1015 {IME_SETCOMPOSITION, false, 3, 3, L"nih", L"nih"},
1016 {IME_SETCOMPOSITION, false, 4, 4, L"niha", L"niha"},
1017 {IME_SETCOMPOSITION, false, 5, 5, L"nihao", L"nihao"},
1018 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"\x4F60\x597D", L"\x4F60\x597D"},
1019 // Scenario 2: input a Japanese word with Microsoft IME (on Vista).
1020 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
1021 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
1022 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
1023 {IME_SETCOMPOSITION, false, 0, 1, L"\xFF4B", L"\xFF4B"},
1024 {IME_SETCOMPOSITION, false, 0, 1, L"\x304B", L"\x304B"},
1025 {IME_SETCOMPOSITION, false, 0, 2, L"\x304B\xFF4E", L"\x304B\xFF4E"},
1026 {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\xFF4A",
1027 L"\x304B\x3093\xFF4A"},
1028 {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\x3058",
1029 L"\x304B\x3093\x3058"},
1030 {IME_SETCOMPOSITION, false, 0, 2, L"\x611F\x3058", L"\x611F\x3058"},
1031 {IME_SETCOMPOSITION, false, 0, 2, L"\x6F22\x5B57", L"\x6F22\x5B57"},
1032 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"},
1033 {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"},
1034 // Scenario 3: input a Korean word with Microsot IME (on Vista).
1035 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
1036 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
1037 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
1038 {IME_SETCOMPOSITION, false, 0, 1, L"\x3147", L"\x3147"},
1039 {IME_SETCOMPOSITION, false, 0, 1, L"\xC544", L"\xC544"},
1040 {IME_SETCOMPOSITION, false, 0, 1, L"\xC548", L"\xC548"},
1041 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\xC548"},
1042 {IME_SETCOMPOSITION, false, 0, 1, L"\x3134", L"\xC548\x3134"},
1043 {IME_SETCOMPOSITION, false, 0, 1, L"\xB140", L"\xC548\xB140"},
1044 {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"},
1045 {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\xC548"},
1046 {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"},
1047 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\xC548\xB155"},
1050 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kImeMessages); i++) {
1051 const ImeMessage* ime_message = &kImeMessages[i];
1052 switch (ime_message->command) {
1053 case IME_INITIALIZE:
1054 // Load an HTML page consisting of a content-editable <div> element,
1055 // and move the input focus to the <div> element, where we can use
1057 view()->OnSetInputMethodActive(ime_message->enable);
1058 view()->set_send_content_state_immediately(true);
1063 "<div id=\"test1\" contenteditable=\"true\"></div>"
1066 ExecuteJavaScript("document.getElementById('test1').focus();");
1069 case IME_SETINPUTMODE:
1070 // Activate (or deactivate) our IME back-end.
1071 view()->OnSetInputMethodActive(ime_message->enable);
1075 // Update the window focus.
1076 view()->OnSetFocus(ime_message->enable);
1079 case IME_SETCOMPOSITION:
1080 view()->OnImeSetComposition(
1081 base::WideToUTF16Hack(ime_message->ime_string),
1082 std::vector<blink::WebCompositionUnderline>(),
1083 ime_message->selection_start,
1084 ime_message->selection_end);
1087 case IME_CONFIRMCOMPOSITION:
1088 view()->OnImeConfirmComposition(
1089 base::WideToUTF16Hack(ime_message->ime_string),
1090 gfx::Range::InvalidRange(),
1094 case IME_CANCELCOMPOSITION:
1095 view()->OnImeSetComposition(
1097 std::vector<blink::WebCompositionUnderline>(),
1102 // Update the status of our IME back-end.
1103 // TODO(hbono): we should verify messages to be sent from the back-end.
1104 view()->UpdateTextInputType();
1105 ProcessPendingMessages();
1106 render_thread_->sink().ClearMessages();
1108 if (ime_message->result) {
1109 // Retrieve the content of this page and compare it with the expected
1111 const int kMaxOutputCharacters = 128;
1112 std::wstring output = base::UTF16ToWideHack(
1113 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1114 EXPECT_EQ(output, ime_message->result);
1119 // Test that the RenderView::OnSetTextDirection() function can change the text
1120 // direction of the selected input element.
1121 TEST_F(RenderViewImplTest, OnSetTextDirection) {
1122 // Load an HTML page consisting of a <textarea> element and a <div> element.
1123 // This test changes the text direction of the <textarea> element, and
1124 // writes the values of its 'dir' attribute and its 'direction' property to
1125 // verify that the text direction is changed.
1126 view()->set_send_content_state_immediately(true);
1131 "<textarea id=\"test\"></textarea>"
1132 "<div id=\"result\" contenteditable=\"true\"></div>"
1135 render_thread_->sink().ClearMessages();
1137 static const struct {
1138 WebTextDirection direction;
1139 const wchar_t* expected_result;
1140 } kTextDirection[] = {
1141 { blink::WebTextDirectionRightToLeft, L"\x000A" L"rtl,rtl" },
1142 { blink::WebTextDirectionLeftToRight, L"\x000A" L"ltr,ltr" },
1144 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTextDirection); ++i) {
1145 // Set the text direction of the <textarea> element.
1146 ExecuteJavaScript("document.getElementById('test').focus();");
1147 view()->OnSetTextDirection(kTextDirection[i].direction);
1149 // Write the values of its DOM 'dir' attribute and its CSS 'direction'
1150 // property to the <div> element.
1151 ExecuteJavaScript("var result = document.getElementById('result');"
1152 "var node = document.getElementById('test');"
1153 "var style = getComputedStyle(node, null);"
1154 "result.innerText ="
1155 " node.getAttribute('dir') + ',' +"
1156 " style.getPropertyValue('direction');");
1158 // Copy the document content to std::wstring and compare with the
1160 const int kMaxOutputCharacters = 16;
1161 std::wstring output = base::UTF16ToWideHack(
1162 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1163 EXPECT_EQ(output, kTextDirection[i].expected_result);
1167 // see http://crbug.com/238750
1169 #define MAYBE_OnHandleKeyboardEvent DISABLED_OnHandleKeyboardEvent
1171 #define MAYBE_OnHandleKeyboardEvent OnHandleKeyboardEvent
1174 // Test that we can receive correct DOM events when we send input events
1175 // through the RenderWidget::OnHandleInputEvent() function.
1176 TEST_F(RenderViewImplTest, MAYBE_OnHandleKeyboardEvent) {
1177 #if !defined(OS_MACOSX)
1178 // Load an HTML page consisting of one <input> element and three
1179 // contentediable <div> elements.
1180 // The <input> element is used for sending keyboard events, and the <div>
1181 // elements are used for writing DOM events in the following format:
1182 // "<keyCode>,<shiftKey>,<controlKey>,<altKey>".
1183 // TODO(hbono): <http://crbug.com/2215> Our WebKit port set |ev.metaKey| to
1184 // true when pressing an alt key, i.e. the |ev.metaKey| value is not
1185 // trustworthy. We will check the |ev.metaKey| value when this issue is fixed.
1186 view()->set_send_content_state_immediately(true);
1190 "<script type='text/javascript' language='javascript'>"
1191 "function OnKeyEvent(ev) {"
1192 " var result = document.getElementById(ev.type);"
1193 " result.innerText ="
1194 " (ev.which || ev.keyCode) + ',' +"
1195 " ev.shiftKey + ',' +"
1196 " ev.ctrlKey + ',' +"
1203 "<input id='test' type='text'"
1204 " onkeydown='return OnKeyEvent(event);'"
1205 " onkeypress='return OnKeyEvent(event);'"
1206 " onkeyup='return OnKeyEvent(event);'>"
1208 "<div id='keydown' contenteditable='true'>"
1210 "<div id='keypress' contenteditable='true'>"
1212 "<div id='keyup' contenteditable='true'>"
1216 ExecuteJavaScript("document.getElementById('test').focus();");
1217 render_thread_->sink().ClearMessages();
1219 static const MockKeyboard::Layout kLayouts[] = {
1221 // Since we ignore the mock keyboard layout on Linux and instead just use
1222 // the screen's keyboard layout, these trivially pass. They are commented
1223 // out to avoid the illusion that they work.
1224 MockKeyboard::LAYOUT_ARABIC,
1225 MockKeyboard::LAYOUT_CANADIAN_FRENCH,
1226 MockKeyboard::LAYOUT_FRENCH,
1227 MockKeyboard::LAYOUT_HEBREW,
1228 MockKeyboard::LAYOUT_RUSSIAN,
1230 MockKeyboard::LAYOUT_UNITED_STATES,
1233 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) {
1234 // For each key code, we send three keyboard events.
1235 // * we press only the key;
1236 // * we press the key and a left-shift key, and;
1237 // * we press the key and a right-alt (AltGr) key.
1238 // For each modifiers, we need a string used for formatting its expected
1239 // result. (See the above comment for its format.)
1240 static const struct {
1241 MockKeyboard::Modifiers modifiers;
1242 const char* expected_result;
1243 } kModifierData[] = {
1244 {MockKeyboard::NONE, "false,false,false"},
1245 {MockKeyboard::LEFT_SHIFT, "true,false,false"},
1247 {MockKeyboard::RIGHT_ALT, "false,false,true"},
1251 MockKeyboard::Layout layout = kLayouts[i];
1252 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifierData); ++j) {
1253 // Virtual key codes used for this test.
1254 static const int kKeyCodes[] = {
1255 '0', '1', '2', '3', '4', '5', '6', '7',
1256 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1257 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1258 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
1264 ui::VKEY_OEM_PERIOD,
1272 // Not sure how to handle this key on Linux.
1277 MockKeyboard::Modifiers modifiers = kModifierData[j].modifiers;
1278 for (size_t k = 0; k < ARRAYSIZE_UNSAFE(kKeyCodes); ++k) {
1279 // Send a keyboard event to the RenderView object.
1280 // We should test a keyboard event only when the given keyboard-layout
1281 // driver is installed in a PC and the driver can assign a Unicode
1282 // charcter for the given tuple (key-code and modifiers).
1283 int key_code = kKeyCodes[k];
1284 base::string16 char_code;
1285 if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0)
1288 // Create an expected result from the virtual-key code, the character
1289 // code, and the modifier-key status.
1290 // We format a string that emulates a DOM-event string produced hy
1291 // our JavaScript function. (See the above comment for the format.)
1292 static char expected_result[1024];
1293 expected_result[0] = 0;
1294 base::snprintf(&expected_result[0],
1295 sizeof(expected_result),
1296 "\n" // texts in the <input> element
1297 "%d,%s\n" // texts in the first <div> element
1298 "%d,%s\n" // texts in the second <div> element
1299 "%d,%s", // texts in the third <div> element
1300 key_code, kModifierData[j].expected_result,
1301 static_cast<int>(char_code[0]),
1302 kModifierData[j].expected_result,
1303 key_code, kModifierData[j].expected_result);
1305 // Retrieve the text in the test page and compare it with the expected
1306 // text created from a virtual-key code, a character code, and the
1307 // modifier-key status.
1308 const int kMaxOutputCharacters = 1024;
1309 std::string output = base::UTF16ToUTF8(
1310 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1311 EXPECT_EQ(expected_result, output);
1320 // Test that our EditorClientImpl class can insert characters when we send
1321 // keyboard events through the RenderWidget::OnHandleInputEvent() function.
1322 // This test is for preventing regressions caused only when we use non-US
1323 // keyboards, such as Issue 10846.
1324 // see http://crbug.com/244562
1326 #define MAYBE_InsertCharacters DISABLED_InsertCharacters
1328 #define MAYBE_InsertCharacters InsertCharacters
1330 TEST_F(RenderViewImplTest, MAYBE_InsertCharacters) {
1331 #if !defined(OS_MACOSX)
1332 static const struct {
1333 MockKeyboard::Layout layout;
1334 const wchar_t* expected_result;
1337 // Disabled these keyboard layouts because buildbots do not have their
1338 // keyboard-layout drivers installed.
1339 {MockKeyboard::LAYOUT_ARABIC,
1340 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1341 L"\x0038\x0039\x0634\x0624\x064a\x062b\x0628\x0644"
1342 L"\x0627\x0647\x062a\x0646\x0645\x0629\x0649\x062e"
1343 L"\x062d\x0636\x0642\x0633\x0641\x0639\x0631\x0635"
1344 L"\x0621\x063a\x0626\x0643\x003d\x0648\x002d\x0632"
1345 L"\x0638\x0630\x062c\x005c\x062f\x0637\x0028\x0021"
1346 L"\x0040\x0023\x0024\x0025\x005e\x0026\x002a\x0029"
1347 L"\x0650\x007d\x005d\x064f\x005b\x0623\x00f7\x0640"
1348 L"\x060c\x002f\x2019\x0622\x00d7\x061b\x064e\x064c"
1349 L"\x064d\x2018\x007b\x064b\x0652\x0625\x007e\x003a"
1350 L"\x002b\x002c\x005f\x002e\x061f\x0651\x003c\x007c"
1351 L"\x003e\x0022\x0030\x0031\x0032\x0033\x0034\x0035"
1352 L"\x0036\x0037\x0038\x0039\x0634\x0624\x064a\x062b"
1353 L"\x0628\x0644\x0627\x0647\x062a\x0646\x0645\x0629"
1354 L"\x0649\x062e\x062d\x0636\x0642\x0633\x0641\x0639"
1355 L"\x0631\x0635\x0621\x063a\x0626\x0643\x003d\x0648"
1356 L"\x002d\x0632\x0638\x0630\x062c\x005c\x062f\x0637"
1358 {MockKeyboard::LAYOUT_HEBREW,
1359 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1360 L"\x0038\x0039\x05e9\x05e0\x05d1\x05d2\x05e7\x05db"
1361 L"\x05e2\x05d9\x05df\x05d7\x05dc\x05da\x05e6\x05de"
1362 L"\x05dd\x05e4\x002f\x05e8\x05d3\x05d0\x05d5\x05d4"
1363 L"\x0027\x05e1\x05d8\x05d6\x05e3\x003d\x05ea\x002d"
1364 L"\x05e5\x002e\x003b\x005d\x005c\x005b\x002c\x0028"
1365 L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a"
1366 L"\x0029\x0041\x0042\x0043\x0044\x0045\x0046\x0047"
1367 L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f"
1368 L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057"
1369 L"\x0058\x0059\x005a\x003a\x002b\x003e\x005f\x003c"
1370 L"\x003f\x007e\x007d\x007c\x007b\x0022\x0030\x0031"
1371 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1372 L"\x05e9\x05e0\x05d1\x05d2\x05e7\x05db\x05e2\x05d9"
1373 L"\x05df\x05d7\x05dc\x05da\x05e6\x05de\x05dd\x05e4"
1374 L"\x002f\x05e8\x05d3\x05d0\x05d5\x05d4\x0027\x05e1"
1375 L"\x05d8\x05d6\x05e3\x003d\x05ea\x002d\x05e5\x002e"
1376 L"\x003b\x005d\x005c\x005b\x002c"
1380 // On Linux, the only way to test alternate keyboard layouts is to change
1381 // the keyboard layout of the whole screen. I'm worried about the side
1382 // effects this may have on the buildbots.
1383 {MockKeyboard::LAYOUT_CANADIAN_FRENCH,
1384 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1385 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1386 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1387 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1388 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1389 L"\x002e\x00e9\x003c\x0029\x0021\x0022\x002f\x0024"
1390 L"\x0025\x003f\x0026\x002a\x0028\x0041\x0042\x0043"
1391 L"\x0044\x0045\x0046\x0047\x0048\x0049\x004a\x004b"
1392 L"\x004c\x004d\x004e\x004f\x0050\x0051\x0052\x0053"
1393 L"\x0054\x0055\x0056\x0057\x0058\x0059\x005a\x003a"
1394 L"\x002b\x0027\x005f\x002e\x00c9\x003e\x0030\x0031"
1395 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1396 L"\x0061\x0062\x0063\x0064\x0065\x0066\x0067\x0068"
1397 L"\x0069\x006a\x006b\x006c\x006d\x006e\x006f\x0070"
1398 L"\x0071\x0072\x0073\x0074\x0075\x0076\x0077\x0078"
1399 L"\x0079\x007a\x003b\x003d\x002c\x002d\x002e\x00e9"
1402 {MockKeyboard::LAYOUT_FRENCH,
1403 L"\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d\x00e8"
1404 L"\x005f\x00e7\x0061\x0062\x0063\x0064\x0065\x0066"
1405 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1406 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1407 L"\x0077\x0078\x0079\x007a\x0024\x003d\x002c\x003b"
1408 L"\x003a\x00f9\x0029\x002a\x0021\x0030\x0031\x0032"
1409 L"\x0033\x0034\x0035\x0036\x0037\x0038\x0039\x0041"
1410 L"\x0042\x0043\x0044\x0045\x0046\x0047\x0048\x0049"
1411 L"\x004a\x004b\x004c\x004d\x004e\x004f\x0050\x0051"
1412 L"\x0052\x0053\x0054\x0055\x0056\x0057\x0058\x0059"
1413 L"\x005a\x00a3\x002b\x003f\x002e\x002f\x0025\x00b0"
1414 L"\x00b5\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d"
1415 L"\x00e8\x005f\x00e7\x0061\x0062\x0063\x0064\x0065"
1416 L"\x0066\x0067\x0068\x0069\x006a\x006b\x006c\x006d"
1417 L"\x006e\x006f\x0070\x0071\x0072\x0073\x0074\x0075"
1418 L"\x0076\x0077\x0078\x0079\x007a\x0024\x003d\x002c"
1419 L"\x003b\x003a\x00f9\x0029\x002a\x0021"
1421 {MockKeyboard::LAYOUT_RUSSIAN,
1422 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1423 L"\x0038\x0039\x0444\x0438\x0441\x0432\x0443\x0430"
1424 L"\x043f\x0440\x0448\x043e\x043b\x0434\x044c\x0442"
1425 L"\x0449\x0437\x0439\x043a\x044b\x0435\x0433\x043c"
1426 L"\x0446\x0447\x043d\x044f\x0436\x003d\x0431\x002d"
1427 L"\x044e\x002e\x0451\x0445\x005c\x044a\x044d\x0029"
1428 L"\x0021\x0022\x2116\x003b\x0025\x003a\x003f\x002a"
1429 L"\x0028\x0424\x0418\x0421\x0412\x0423\x0410\x041f"
1430 L"\x0420\x0428\x041e\x041b\x0414\x042c\x0422\x0429"
1431 L"\x0417\x0419\x041a\x042b\x0415\x0413\x041c\x0426"
1432 L"\x0427\x041d\x042f\x0416\x002b\x0411\x005f\x042e"
1433 L"\x002c\x0401\x0425\x002f\x042a\x042d\x0030\x0031"
1434 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1435 L"\x0444\x0438\x0441\x0432\x0443\x0430\x043f\x0440"
1436 L"\x0448\x043e\x043b\x0434\x044c\x0442\x0449\x0437"
1437 L"\x0439\x043a\x044b\x0435\x0433\x043c\x0446\x0447"
1438 L"\x043d\x044f\x0436\x003d\x0431\x002d\x044e\x002e"
1439 L"\x0451\x0445\x005c\x044a\x044d"
1441 #endif // defined(OS_WIN)
1442 {MockKeyboard::LAYOUT_UNITED_STATES,
1443 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1444 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1445 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1446 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1447 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1448 L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027\x0029"
1449 L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a"
1450 L"\x0028\x0041\x0042\x0043\x0044\x0045\x0046\x0047"
1451 L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f"
1452 L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057"
1453 L"\x0058\x0059\x005a\x003a\x002b\x003c\x005f\x003e"
1454 L"\x003f\x007e\x007b\x007c\x007d\x0022"
1456 // This is ifdefed out for Linux to correspond to the fact that we don't
1457 // test alt+keystroke for now.
1458 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1459 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1460 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1461 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1462 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1463 L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027"
1468 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) {
1469 // Load an HTML page consisting of one <div> element.
1470 // This <div> element is used by the EditorClientImpl class to insert
1471 // characters received through the RenderWidget::OnHandleInputEvent()
1473 view()->set_send_content_state_immediately(true);
1479 "<div id='test' contenteditable='true'>"
1483 ExecuteJavaScript("document.getElementById('test').focus();");
1484 render_thread_->sink().ClearMessages();
1486 // For each key code, we send three keyboard events.
1487 // * Pressing only the key;
1488 // * Pressing the key and a left-shift key, and;
1489 // * Pressing the key and a right-alt (AltGr) key.
1490 static const MockKeyboard::Modifiers kModifiers[] = {
1492 MockKeyboard::LEFT_SHIFT,
1494 MockKeyboard::RIGHT_ALT,
1498 MockKeyboard::Layout layout = kLayouts[i].layout;
1499 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifiers); ++j) {
1500 // Virtual key codes used for this test.
1501 static const int kKeyCodes[] = {
1502 '0', '1', '2', '3', '4', '5', '6', '7',
1503 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1504 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1505 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
1511 ui::VKEY_OEM_PERIOD,
1519 // Unclear how to handle this on Linux.
1524 MockKeyboard::Modifiers modifiers = kModifiers[j];
1525 for (size_t k = 0; k < ARRAYSIZE_UNSAFE(kKeyCodes); ++k) {
1526 // Send a keyboard event to the RenderView object.
1527 // We should test a keyboard event only when the given keyboard-layout
1528 // driver is installed in a PC and the driver can assign a Unicode
1529 // charcter for the given tuple (layout, key-code, and modifiers).
1530 int key_code = kKeyCodes[k];
1531 base::string16 char_code;
1532 if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0)
1537 // Retrieve the text in the test page and compare it with the expected
1538 // text created from a virtual-key code, a character code, and the
1539 // modifier-key status.
1540 const int kMaxOutputCharacters = 4096;
1541 std::wstring output = base::UTF16ToWideHack(
1542 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1543 EXPECT_EQ(kLayouts[i].expected_result, output);
1550 // Crashy, http://crbug.com/53247.
1551 TEST_F(RenderViewImplTest, DISABLED_DidFailProvisionalLoadWithErrorForError) {
1552 GetMainFrame()->enableViewSourceMode(true);
1554 error.domain = WebString::fromUTF8(net::kErrorDomain);
1555 error.reason = net::ERR_FILE_NOT_FOUND;
1556 error.unreachableURL = GURL("http://foo");
1557 WebFrame* web_frame = GetMainFrame();
1559 // Start a load that will reach provisional state synchronously,
1560 // but won't complete synchronously.
1561 FrameMsg_Navigate_Params params;
1562 params.page_id = -1;
1563 params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
1564 params.url = GURL("data:text/html,test data");
1565 frame()->OnNavigate(params);
1567 // An error occurred.
1568 view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
1569 // Frame should exit view-source mode.
1570 EXPECT_FALSE(web_frame->isViewSourceModeEnabled());
1573 TEST_F(RenderViewImplTest, DidFailProvisionalLoadWithErrorForCancellation) {
1574 GetMainFrame()->enableViewSourceMode(true);
1576 error.domain = WebString::fromUTF8(net::kErrorDomain);
1577 error.reason = net::ERR_ABORTED;
1578 error.unreachableURL = GURL("http://foo");
1579 WebFrame* web_frame = GetMainFrame();
1581 // Start a load that will reach provisional state synchronously,
1582 // but won't complete synchronously.
1583 FrameMsg_Navigate_Params params;
1584 params.page_id = -1;
1585 params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
1586 params.url = GURL("data:text/html,test data");
1587 frame()->OnNavigate(params);
1589 // A cancellation occurred.
1590 view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
1591 // Frame should stay in view-source mode.
1592 EXPECT_TRUE(web_frame->isViewSourceModeEnabled());
1595 // Regression test for http://crbug.com/41562
1596 TEST_F(RenderViewImplTest, UpdateTargetURLWithInvalidURL) {
1597 const GURL invalid_gurl("http://");
1598 view()->setMouseOverURL(blink::WebURL(invalid_gurl));
1599 EXPECT_EQ(invalid_gurl, view()->target_url_);
1602 TEST_F(RenderViewImplTest, SetHistoryLengthAndPrune) {
1603 int expected_page_id = -1;
1605 // No history to merge and no committed pages.
1606 view()->OnSetHistoryLengthAndPrune(0, -1);
1607 EXPECT_EQ(0, view()->history_list_length_);
1608 EXPECT_EQ(-1, view()->history_list_offset_);
1610 // History to merge and no committed pages.
1611 view()->OnSetHistoryLengthAndPrune(2, -1);
1612 EXPECT_EQ(2, view()->history_list_length_);
1613 EXPECT_EQ(1, view()->history_list_offset_);
1614 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1615 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1618 // No history to merge and a committed page to be kept.
1619 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1620 expected_page_id = view()->page_id_;
1621 view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
1622 EXPECT_EQ(1, view()->history_list_length_);
1623 EXPECT_EQ(0, view()->history_list_offset_);
1624 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1627 // No history to merge and a committed page to be pruned.
1628 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1629 expected_page_id = view()->page_id_;
1630 view()->OnSetHistoryLengthAndPrune(0, expected_page_id + 1);
1631 EXPECT_EQ(0, view()->history_list_length_);
1632 EXPECT_EQ(-1, view()->history_list_offset_);
1635 // No history to merge and a committed page that the browser was unaware of.
1636 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1637 expected_page_id = view()->page_id_;
1638 view()->OnSetHistoryLengthAndPrune(0, -1);
1639 EXPECT_EQ(1, view()->history_list_length_);
1640 EXPECT_EQ(0, view()->history_list_offset_);
1641 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1644 // History to merge and a committed page to be kept.
1645 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1646 expected_page_id = view()->page_id_;
1647 view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
1648 EXPECT_EQ(3, view()->history_list_length_);
1649 EXPECT_EQ(2, view()->history_list_offset_);
1650 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1651 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1652 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1655 // History to merge and a committed page to be pruned.
1656 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1657 expected_page_id = view()->page_id_;
1658 view()->OnSetHistoryLengthAndPrune(2, expected_page_id + 1);
1659 EXPECT_EQ(2, view()->history_list_length_);
1660 EXPECT_EQ(1, view()->history_list_offset_);
1661 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1662 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1665 // History to merge and a committed page that the browser was unaware of.
1666 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1667 expected_page_id = view()->page_id_;
1668 view()->OnSetHistoryLengthAndPrune(2, -1);
1669 EXPECT_EQ(3, view()->history_list_length_);
1670 EXPECT_EQ(2, view()->history_list_offset_);
1671 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1672 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1673 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1676 int expected_page_id_2 = -1;
1678 // No history to merge and two committed pages, both to be kept.
1679 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1680 expected_page_id = view()->page_id_;
1681 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1682 expected_page_id_2 = view()->page_id_;
1683 EXPECT_GT(expected_page_id_2, expected_page_id);
1684 view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
1685 EXPECT_EQ(2, view()->history_list_length_);
1686 EXPECT_EQ(1, view()->history_list_offset_);
1687 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1688 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
1691 // No history to merge and two committed pages, and only the second is kept.
1692 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1693 expected_page_id = view()->page_id_;
1694 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1695 expected_page_id_2 = view()->page_id_;
1696 EXPECT_GT(expected_page_id_2, expected_page_id);
1697 view()->OnSetHistoryLengthAndPrune(0, expected_page_id_2);
1698 EXPECT_EQ(1, view()->history_list_length_);
1699 EXPECT_EQ(0, view()->history_list_offset_);
1700 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[0]);
1703 // No history to merge and two committed pages, both of which the browser was
1705 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1706 expected_page_id = view()->page_id_;
1707 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1708 expected_page_id_2 = view()->page_id_;
1709 EXPECT_GT(expected_page_id_2, expected_page_id);
1710 view()->OnSetHistoryLengthAndPrune(0, -1);
1711 EXPECT_EQ(2, view()->history_list_length_);
1712 EXPECT_EQ(1, view()->history_list_offset_);
1713 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1714 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
1717 // History to merge and two committed pages, both to be kept.
1718 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1719 expected_page_id = view()->page_id_;
1720 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1721 expected_page_id_2 = view()->page_id_;
1722 EXPECT_GT(expected_page_id_2, expected_page_id);
1723 view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
1724 EXPECT_EQ(4, view()->history_list_length_);
1725 EXPECT_EQ(3, view()->history_list_offset_);
1726 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1727 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1728 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1729 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
1732 // History to merge and two committed pages, and only the second is kept.
1733 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1734 expected_page_id = view()->page_id_;
1735 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1736 expected_page_id_2 = view()->page_id_;
1737 EXPECT_GT(expected_page_id_2, expected_page_id);
1738 view()->OnSetHistoryLengthAndPrune(2, expected_page_id_2);
1739 EXPECT_EQ(3, view()->history_list_length_);
1740 EXPECT_EQ(2, view()->history_list_offset_);
1741 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1742 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1743 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[2]);
1746 // History to merge and two committed pages, both of which the browser was
1748 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1749 expected_page_id = view()->page_id_;
1750 frame()->didCommitProvisionalLoad(GetMainFrame(), true);
1751 expected_page_id_2 = view()->page_id_;
1752 EXPECT_GT(expected_page_id_2, expected_page_id);
1753 view()->OnSetHistoryLengthAndPrune(2, -1);
1754 EXPECT_EQ(4, view()->history_list_length_);
1755 EXPECT_EQ(3, view()->history_list_offset_);
1756 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1757 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1758 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1759 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
1762 TEST_F(RenderViewImplTest, ContextMenu) {
1763 LoadHTML("<div>Page A</div>");
1765 // Create a right click in the center of the iframe. (I'm hoping this will
1766 // make this a bit more robust in case of some other formatting or other bug.)
1767 WebMouseEvent mouse_event;
1768 mouse_event.type = WebInputEvent::MouseDown;
1769 mouse_event.button = WebMouseEvent::ButtonRight;
1770 mouse_event.x = 250;
1771 mouse_event.y = 250;
1772 mouse_event.globalX = 250;
1773 mouse_event.globalY = 250;
1775 SendWebMouseEvent(mouse_event);
1777 // Now simulate the corresponding up event which should display the menu
1778 mouse_event.type = WebInputEvent::MouseUp;
1779 SendWebMouseEvent(mouse_event);
1781 EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
1782 FrameHostMsg_ContextMenu::ID));
1785 TEST_F(RenderViewImplTest, TestBackForward) {
1786 LoadHTML("<div id=pagename>Page A</div>");
1787 blink::WebHistoryItem page_a_item = GetMainFrame()->currentHistoryItem();
1788 int was_page_a = -1;
1789 base::string16 check_page_a =
1791 "Number(document.getElementById('pagename').innerHTML == 'Page A')");
1792 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
1793 EXPECT_EQ(1, was_page_a);
1795 LoadHTML("<div id=pagename>Page B</div>");
1796 int was_page_b = -1;
1797 base::string16 check_page_b =
1799 "Number(document.getElementById('pagename').innerHTML == 'Page B')");
1800 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1801 EXPECT_EQ(1, was_page_b);
1803 LoadHTML("<div id=pagename>Page C</div>");
1804 int was_page_c = -1;
1805 base::string16 check_page_c =
1807 "Number(document.getElementById('pagename').innerHTML == 'Page C')");
1808 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
1809 EXPECT_EQ(1, was_page_b);
1811 blink::WebHistoryItem forward_item = GetMainFrame()->currentHistoryItem();
1812 GoBack(GetMainFrame()->previousHistoryItem());
1813 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1814 EXPECT_EQ(1, was_page_b);
1816 GoForward(forward_item);
1817 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
1818 EXPECT_EQ(1, was_page_c);
1820 GoBack(GetMainFrame()->previousHistoryItem());
1821 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1822 EXPECT_EQ(1, was_page_b);
1824 forward_item = GetMainFrame()->currentHistoryItem();
1825 GoBack(page_a_item);
1826 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
1827 EXPECT_EQ(1, was_page_a);
1829 GoForward(forward_item);
1830 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1831 EXPECT_EQ(1, was_page_b);
1834 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
1835 TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) {
1838 // http://crbug.com/304193
1839 if (base::win::GetVersion() < base::win::VERSION_VISTA)
1843 LoadHTML("<textarea id=\"test\"></textarea>");
1844 ExecuteJavaScript("document.getElementById('test').focus();");
1846 const base::string16 empty_string;
1847 const std::vector<blink::WebCompositionUnderline> empty_underline;
1848 std::vector<gfx::Rect> bounds;
1849 view()->OnSetFocus(true);
1850 view()->OnSetInputMethodActive(true);
1852 // ASCII composition
1853 const base::string16 ascii_composition = base::UTF8ToUTF16("aiueo");
1854 view()->OnImeSetComposition(ascii_composition, empty_underline, 0, 0);
1855 view()->GetCompositionCharacterBounds(&bounds);
1856 ASSERT_EQ(ascii_composition.size(), bounds.size());
1857 for (size_t i = 0; i < bounds.size(); ++i)
1858 EXPECT_LT(0, bounds[i].width());
1859 view()->OnImeConfirmComposition(
1860 empty_string, gfx::Range::InvalidRange(), false);
1862 // Non surrogate pair unicode character.
1863 const base::string16 unicode_composition = base::UTF8ToUTF16(
1864 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
1865 view()->OnImeSetComposition(unicode_composition, empty_underline, 0, 0);
1866 view()->GetCompositionCharacterBounds(&bounds);
1867 ASSERT_EQ(unicode_composition.size(), bounds.size());
1868 for (size_t i = 0; i < bounds.size(); ++i)
1869 EXPECT_LT(0, bounds[i].width());
1870 view()->OnImeConfirmComposition(
1871 empty_string, gfx::Range::InvalidRange(), false);
1873 // Surrogate pair character.
1874 const base::string16 surrogate_pair_char =
1875 base::UTF8ToUTF16("\xF0\xA0\xAE\x9F");
1876 view()->OnImeSetComposition(surrogate_pair_char,
1880 view()->GetCompositionCharacterBounds(&bounds);
1881 ASSERT_EQ(surrogate_pair_char.size(), bounds.size());
1882 EXPECT_LT(0, bounds[0].width());
1883 EXPECT_EQ(0, bounds[1].width());
1884 view()->OnImeConfirmComposition(
1885 empty_string, gfx::Range::InvalidRange(), false);
1888 const base::string16 surrogate_pair_mixed_composition =
1889 surrogate_pair_char + base::UTF8ToUTF16("\xE3\x81\x82") +
1890 surrogate_pair_char + base::UTF8ToUTF16("b") + surrogate_pair_char;
1891 const size_t utf16_length = 8UL;
1892 const bool is_surrogate_pair_empty_rect[8] = {
1893 false, true, false, false, true, false, false, true };
1894 view()->OnImeSetComposition(surrogate_pair_mixed_composition,
1898 view()->GetCompositionCharacterBounds(&bounds);
1899 ASSERT_EQ(utf16_length, bounds.size());
1900 for (size_t i = 0; i < utf16_length; ++i) {
1901 if (is_surrogate_pair_empty_rect[i]) {
1902 EXPECT_EQ(0, bounds[i].width());
1904 EXPECT_LT(0, bounds[i].width());
1907 view()->OnImeConfirmComposition(
1908 empty_string, gfx::Range::InvalidRange(), false);
1912 TEST_F(RenderViewImplTest, ZoomLimit) {
1913 const double kMinZoomLevel = ZoomFactorToZoomLevel(kMinimumZoomFactor);
1914 const double kMaxZoomLevel = ZoomFactorToZoomLevel(kMaximumZoomFactor);
1916 FrameMsg_Navigate_Params params;
1917 params.page_id = -1;
1918 params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
1920 // Verifies navigation to a URL with preset zoom level indeed sets the level.
1921 // Regression test for http://crbug.com/139559, where the level was not
1922 // properly set when it is out of the default zoom limits of WebView.
1923 params.url = GURL("data:text/html,min_zoomlimit_test");
1924 view()->OnSetZoomLevelForLoadingURL(params.url, kMinZoomLevel);
1925 frame()->OnNavigate(params);
1926 ProcessPendingMessages();
1927 EXPECT_DOUBLE_EQ(kMinZoomLevel, view()->GetWebView()->zoomLevel());
1929 // It should work even when the zoom limit is temporarily changed in the page.
1930 view()->GetWebView()->zoomLimitsChanged(ZoomFactorToZoomLevel(1.0),
1931 ZoomFactorToZoomLevel(1.0));
1932 params.url = GURL("data:text/html,max_zoomlimit_test");
1933 view()->OnSetZoomLevelForLoadingURL(params.url, kMaxZoomLevel);
1934 frame()->OnNavigate(params);
1935 ProcessPendingMessages();
1936 EXPECT_DOUBLE_EQ(kMaxZoomLevel, view()->GetWebView()->zoomLevel());
1939 TEST_F(RenderViewImplTest, SetEditableSelectionAndComposition) {
1940 // Load an HTML page consisting of an input field.
1945 "<input id=\"test1\" value=\"some test text hello\"></input>"
1948 ExecuteJavaScript("document.getElementById('test1').focus();");
1949 view()->OnSetEditableSelectionOffsets(4, 8);
1950 const std::vector<blink::WebCompositionUnderline> empty_underline;
1951 view()->OnSetCompositionFromExistingText(7, 10, empty_underline);
1952 blink::WebTextInputInfo info = view()->webview()->textInputInfo();
1953 EXPECT_EQ(4, info.selectionStart);
1954 EXPECT_EQ(8, info.selectionEnd);
1955 EXPECT_EQ(7, info.compositionStart);
1956 EXPECT_EQ(10, info.compositionEnd);
1957 view()->OnUnselect();
1958 info = view()->webview()->textInputInfo();
1959 EXPECT_EQ(0, info.selectionStart);
1960 EXPECT_EQ(0, info.selectionEnd);
1964 TEST_F(RenderViewImplTest, OnExtendSelectionAndDelete) {
1965 // Load an HTML page consisting of an input field.
1970 "<input id=\"test1\" value=\"abcdefghijklmnopqrstuvwxyz\"></input>"
1973 ExecuteJavaScript("document.getElementById('test1').focus();");
1974 view()->OnSetEditableSelectionOffsets(10, 10);
1975 view()->OnExtendSelectionAndDelete(3, 4);
1976 blink::WebTextInputInfo info = view()->webview()->textInputInfo();
1977 EXPECT_EQ("abcdefgopqrstuvwxyz", info.value);
1978 EXPECT_EQ(7, info.selectionStart);
1979 EXPECT_EQ(7, info.selectionEnd);
1980 view()->OnSetEditableSelectionOffsets(4, 8);
1981 view()->OnExtendSelectionAndDelete(2, 5);
1982 info = view()->webview()->textInputInfo();
1983 EXPECT_EQ("abuvwxyz", info.value);
1984 EXPECT_EQ(2, info.selectionStart);
1985 EXPECT_EQ(2, info.selectionEnd);
1988 // Test that the navigating specific frames works correctly.
1989 TEST_F(RenderViewImplTest, NavigateFrame) {
1991 LoadHTML("hello <iframe srcdoc='fail' name='frame'></iframe>");
1993 // Navigate the frame only.
1994 FrameMsg_Navigate_Params nav_params;
1995 nav_params.url = GURL("data:text/html,world");
1996 nav_params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
1997 nav_params.transition = PAGE_TRANSITION_TYPED;
1998 nav_params.current_history_list_length = 1;
1999 nav_params.current_history_list_offset = 0;
2000 nav_params.pending_history_list_offset = 1;
2001 nav_params.page_id = -1;
2002 nav_params.frame_to_navigate = "frame";
2003 frame()->OnNavigate(nav_params);
2004 ProcessPendingMessages();
2006 // Copy the document content to std::wstring and compare with the
2008 const int kMaxOutputCharacters = 256;
2009 std::wstring output = base::UTF16ToWideHack(
2010 GetMainFrame()->contentAsText(kMaxOutputCharacters));
2011 EXPECT_EQ(output, L"hello \n\nworld");
2014 // This test ensures that a RenderFrame object is created for the top level
2015 // frame in the RenderView.
2016 TEST_F(RenderViewImplTest, BasicRenderFrame) {
2017 EXPECT_TRUE(view()->main_render_frame_.get());
2020 TEST_F(RenderViewImplTest, GetSSLStatusOfFrame) {
2021 LoadHTML("<!DOCTYPE html><html><body></body></html>");
2023 WebFrame* frame = GetMainFrame();
2024 SSLStatus ssl_status = view()->GetSSLStatusOfFrame(frame);
2025 EXPECT_FALSE(net::IsCertStatusError(ssl_status.cert_status));
2027 const_cast<blink::WebURLResponse&>(frame->dataSource()->response()).
2029 SerializeSecurityInfo(0, net::CERT_STATUS_ALL_ERRORS, 0, 0,
2030 SignedCertificateTimestampIDStatusList()));
2031 ssl_status = view()->GetSSLStatusOfFrame(frame);
2032 EXPECT_TRUE(net::IsCertStatusError(ssl_status.cert_status));
2035 TEST_F(RenderViewImplTest, MessageOrderInDidChangeSelection) {
2036 view()->OnSetInputMethodActive(true);
2037 view()->set_send_content_state_immediately(true);
2038 LoadHTML("<textarea id=\"test\"></textarea>");
2040 view()->handling_input_event_ = true;
2041 ExecuteJavaScript("document.getElementById('test').focus();");
2043 bool is_input_type_called = false;
2044 bool is_selection_called = false;
2045 size_t last_input_type = 0;
2046 size_t last_selection = 0;
2048 for (size_t i = 0; i < render_thread_->sink().message_count(); ++i) {
2049 const uint32 type = render_thread_->sink().GetMessageAt(i)->type();
2050 if (type == ViewHostMsg_TextInputTypeChanged::ID) {
2051 is_input_type_called = true;
2052 last_input_type = i;
2053 } else if (type == ViewHostMsg_SelectionChanged::ID) {
2054 is_selection_called = true;
2059 EXPECT_TRUE(is_input_type_called);
2060 EXPECT_TRUE(is_selection_called);
2062 // InputTypeChange shold be called earlier than SelectionChanged.
2063 EXPECT_LT(last_input_type, last_selection);
2066 class SuppressErrorPageTest : public RenderViewTest {
2068 virtual void SetUp() OVERRIDE {
2069 SetRendererClientForTesting(&client_);
2070 RenderViewTest::SetUp();
2073 RenderViewImpl* view() {
2074 return static_cast<RenderViewImpl*>(view_);
2077 RenderFrameImpl* frame() {
2078 return static_cast<RenderFrameImpl*>(view()->GetMainRenderFrame());
2082 class TestContentRendererClient : public ContentRendererClient {
2084 virtual bool ShouldSuppressErrorPage(RenderFrame* render_frame,
2085 const GURL& url) OVERRIDE {
2086 return url == GURL("http://example.com/suppress");
2089 virtual void GetNavigationErrorStrings(
2090 content::RenderView* render_view,
2091 blink::WebFrame* frame,
2092 const blink::WebURLRequest& failed_request,
2093 const blink::WebURLError& error,
2094 std::string* error_html,
2095 base::string16* error_description) OVERRIDE {
2097 *error_html = "A suffusion of yellow.";
2101 TestContentRendererClient client_;
2104 #if defined(OS_ANDROID)
2105 // Crashing on Android: http://crbug.com/311341
2106 #define MAYBE_Suppresses DISABLED_Suppresses
2108 #define MAYBE_Suppresses Suppresses
2111 TEST_F(SuppressErrorPageTest, MAYBE_Suppresses) {
2113 error.domain = WebString::fromUTF8(net::kErrorDomain);
2114 error.reason = net::ERR_FILE_NOT_FOUND;
2115 error.unreachableURL = GURL("http://example.com/suppress");
2116 WebFrame* web_frame = GetMainFrame();
2118 // Start a load that will reach provisional state synchronously,
2119 // but won't complete synchronously.
2120 FrameMsg_Navigate_Params params;
2121 params.page_id = -1;
2122 params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
2123 params.url = GURL("data:text/html,test data");
2124 frame()->OnNavigate(params);
2126 // An error occurred.
2127 view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
2128 const int kMaxOutputCharacters = 22;
2129 EXPECT_EQ("", UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
2132 #if defined(OS_ANDROID)
2133 // Crashing on Android: http://crbug.com/311341
2134 #define MAYBE_DoesNotSuppress DISABLED_DoesNotSuppress
2136 #define MAYBE_DoesNotSuppress DoesNotSuppress
2139 TEST_F(SuppressErrorPageTest, MAYBE_DoesNotSuppress) {
2141 error.domain = WebString::fromUTF8(net::kErrorDomain);
2142 error.reason = net::ERR_FILE_NOT_FOUND;
2143 error.unreachableURL = GURL("http://example.com/dont-suppress");
2144 WebFrame* web_frame = GetMainFrame();
2146 // Start a load that will reach provisional state synchronously,
2147 // but won't complete synchronously.
2148 FrameMsg_Navigate_Params params;
2149 params.page_id = -1;
2150 params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
2151 params.url = GURL("data:text/html,test data");
2152 frame()->OnNavigate(params);
2154 // An error occurred.
2155 view()->main_render_frame()->didFailProvisionalLoad(web_frame, error);
2156 ProcessPendingMessages();
2157 const int kMaxOutputCharacters = 22;
2158 EXPECT_EQ("A suffusion of yellow.",
2159 UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
2162 // Tests if IME API's candidatewindow* events sent from browser are handled
2164 TEST_F(RenderViewImplTest, SendCandidateWindowEvents) {
2165 // Sends an HTML with an <input> element and scripts to the renderer.
2166 // The script handles all 3 of candidatewindow* events for an
2167 // InputMethodContext object and once it received 'show', 'update', 'hide'
2168 // should appear in the result div.
2169 LoadHTML("<input id='test'>"
2170 "<div id='result'>Result: </div>"
2172 "window.onload = function() {"
2173 " var result = document.getElementById('result');"
2174 " var test = document.getElementById('test');"
2176 " var context = test.inputMethodContext;"
2178 " context.oncandidatewindowshow = function() {"
2179 " result.innerText += 'show'; };"
2180 " context.oncandidatewindowupdate = function(){"
2181 " result.innerText += 'update'; };"
2182 " context.oncandidatewindowhide = function(){"
2183 " result.innerText += 'hide'; };"
2188 // Fire candidatewindow events.
2189 view()->OnCandidateWindowShown();
2190 view()->OnCandidateWindowUpdated();
2191 view()->OnCandidateWindowHidden();
2193 // Retrieve the content and check if it is expected.
2194 const int kMaxOutputCharacters = 50;
2195 std::string output = base::UTF16ToUTF8(
2196 GetMainFrame()->contentAsText(kMaxOutputCharacters));
2197 EXPECT_EQ(output, "\nResult:showupdatehide");
2200 // Ensure the render view sends favicon url update events correctly.
2201 TEST_F(RenderViewImplTest, SendFaviconURLUpdateEvent) {
2202 // An event should be sent when a favicon url exists.
2205 "<link rel='icon' href='http://www.google.com/favicon.ico'>"
2208 EXPECT_TRUE(render_thread_->sink().GetFirstMessageMatching(
2209 ViewHostMsg_UpdateFaviconURL::ID));
2210 render_thread_->sink().ClearMessages();
2212 // An event should not be sent if no favicon url exists. This is an assumption
2213 // made by some of Chrome's favicon handling.
2218 EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
2219 ViewHostMsg_UpdateFaviconURL::ID));
2222 TEST_F(RenderViewImplTest, FocusElementCallsFocusedNodeChanged) {
2223 LoadHTML("<input id='test1' value='hello1'></input>"
2224 "<input id='test2' value='hello2'></input>");
2226 ExecuteJavaScript("document.getElementById('test1').focus();");
2227 const IPC::Message* msg1 = render_thread_->sink().GetFirstMessageMatching(
2228 ViewHostMsg_FocusedNodeChanged::ID);
2231 ViewHostMsg_FocusedNodeChanged::Param params;
2232 ViewHostMsg_FocusedNodeChanged::Read(msg1, ¶ms);
2233 EXPECT_TRUE(params.a);
2234 render_thread_->sink().ClearMessages();
2236 ExecuteJavaScript("document.getElementById('test2').focus();");
2237 const IPC::Message* msg2 = render_thread_->sink().GetFirstMessageMatching(
2238 ViewHostMsg_FocusedNodeChanged::ID);
2240 ViewHostMsg_FocusedNodeChanged::Read(msg2, ¶ms);
2241 EXPECT_TRUE(params.a);
2242 render_thread_->sink().ClearMessages();
2244 view()->webview()->clearFocusedNode();
2245 const IPC::Message* msg3 = render_thread_->sink().GetFirstMessageMatching(
2246 ViewHostMsg_FocusedNodeChanged::ID);
2248 ViewHostMsg_FocusedNodeChanged::Read(msg3, ¶ms);
2249 EXPECT_FALSE(params.a);
2250 render_thread_->sink().ClearMessages();
2253 } // namespace content