render_view_browsertest.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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. 4 5#include "base/basictypes.h" 6 7#include "base/memory/shared_memory.h" 8#include "base/strings/string_util.h" 9#include "base/strings/utf_string_conversions.h" 10#include "content/common/view_messages.h" 11#include "content/public/browser/native_web_keyboard_event.h" 12#include "content/public/browser/web_ui_controller_factory.h" 13#include "content/public/common/bindings_policy.h" 14#include "content/public/common/url_utils.h" 15#include "content/public/renderer/document_state.h" 16#include "content/public/renderer/history_item_serialization.h" 17#include "content/public/renderer/navigation_state.h" 18#include "content/public/test/render_view_test.h" 19#include "content/renderer/render_view_impl.h" 20#include "content/shell/common/shell_content_client.h" 21#include "content/shell/shell_content_browser_client.h" 22#include "content/test/mock_keyboard.h" 23#include "net/base/net_errors.h" 24#include "testing/gtest/include/gtest/gtest.h" 25#include "third_party/WebKit/public/platform/WebData.h" 26#include "third_party/WebKit/public/platform/WebHTTPBody.h" 27#include "third_party/WebKit/public/platform/WebString.h" 28#include "third_party/WebKit/public/platform/WebURLError.h" 29#include "third_party/WebKit/public/web/WebHistoryItem.h" 30#include "third_party/WebKit/public/web/WebView.h" 31#include "third_party/WebKit/public/web/WebWindowFeatures.h" 32#include "ui/base/keycodes/keyboard_codes.h" 33#include "ui/base/range/range.h" 34#include "ui/gfx/codec/jpeg_codec.h" 35 36#if defined(OS_LINUX) && !defined(USE_AURA) 37#include "ui/base/gtk/event_synthesis_gtk.h" 38#endif 39 40#if defined(USE_AURA) 41#include "ui/base/events/event.h" 42#endif 43 44#if defined(USE_AURA) && defined(USE_X11) 45#include <X11/Xlib.h> 46#include "ui/base/events/event_constants.h" 47#include "ui/base/keycodes/keyboard_code_conversion.h" 48#include "ui/base/x/x11_util.h" 49#endif 50 51using WebKit::WebFrame; 52using WebKit::WebInputEvent; 53using WebKit::WebMouseEvent; 54using WebKit::WebString; 55using WebKit::WebTextDirection; 56using WebKit::WebURLError; 57 58namespace content { 59 60namespace { 61 62#if defined(USE_AURA) && defined(USE_X11) 63// Converts MockKeyboard::Modifiers to ui::EventFlags. 64int ConvertMockKeyboardModifier(MockKeyboard::Modifiers modifiers) { 65 static struct ModifierMap { 66 MockKeyboard::Modifiers src; 67 int dst; 68 } kModifierMap[] = { 69 { MockKeyboard::LEFT_SHIFT, ui::EF_SHIFT_DOWN }, 70 { MockKeyboard::RIGHT_SHIFT, ui::EF_SHIFT_DOWN }, 71 { MockKeyboard::LEFT_CONTROL, ui::EF_CONTROL_DOWN }, 72 { MockKeyboard::RIGHT_CONTROL, ui::EF_CONTROL_DOWN }, 73 { MockKeyboard::LEFT_ALT, ui::EF_ALT_DOWN }, 74 { MockKeyboard::RIGHT_ALT, ui::EF_ALT_DOWN }, 75 }; 76 int flags = 0; 77 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kModifierMap); ++i) { 78 if (kModifierMap[i].src & modifiers) { 79 flags |= kModifierMap[i].dst; 80 } 81 } 82 return flags; 83} 84#endif 85 86class WebUITestWebUIControllerFactory : public WebUIControllerFactory { 87 public: 88 virtual WebUIController* CreateWebUIControllerForURL( 89 WebUI* web_ui, const GURL& url) const OVERRIDE { 90 return NULL; 91 } 92 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context, 93 const GURL& url) const OVERRIDE { 94 return WebUI::kNoWebUI; 95 } 96 virtual bool UseWebUIForURL(BrowserContext* browser_context, 97 const GURL& url) const OVERRIDE { 98 return HasWebUIScheme(url); 99 } 100 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context, 101 const GURL& url) const OVERRIDE { 102 return HasWebUIScheme(url); 103 } 104}; 105 106class MockRenderViewPepperHelper : public RenderViewPepperHelper { 107 public: 108 MockRenderViewPepperHelper() : text_input_type_(ui::TEXT_INPUT_TYPE_NONE) {} 109 virtual bool IsPluginFocused() const OVERRIDE { 110 return true; 111 } 112 virtual ui::TextInputType GetTextInputType() const OVERRIDE { 113 return text_input_type_; 114 } 115 void SetTextInputType(ui::TextInputType text_input_type) { 116 text_input_type_ = text_input_type; 117 } 118 private: 119 ui::TextInputType text_input_type_; 120}; 121 122} // namespace 123 124class RenderViewImplTest : public RenderViewTest { 125 public: 126 RenderViewImplTest() { 127 // Attach a pseudo keyboard device to this object. 128 mock_keyboard_.reset(new MockKeyboard()); 129 } 130 131 RenderViewImpl* view() { 132 return static_cast<RenderViewImpl*>(view_); 133 } 134 135 // Sends IPC messages that emulates a key-press event. 136 int SendKeyEvent(MockKeyboard::Layout layout, 137 int key_code, 138 MockKeyboard::Modifiers modifiers, 139 string16* output) { 140#if defined(OS_WIN) 141 // Retrieve the Unicode character for the given tuple (keyboard-layout, 142 // key-code, and modifiers). 143 // Exit when a keyboard-layout driver cannot assign a Unicode character to 144 // the tuple to prevent sending an invalid key code to the RenderView 145 // object. 146 CHECK(mock_keyboard_.get()); 147 CHECK(output); 148 int length = mock_keyboard_->GetCharacters(layout, key_code, modifiers, 149 output); 150 if (length != 1) 151 return -1; 152 153 // Create IPC messages from Windows messages and send them to our 154 // back-end. 155 // A keyboard event of Windows consists of three Windows messages: 156 // WM_KEYDOWN, WM_CHAR, and WM_KEYUP. 157 // WM_KEYDOWN and WM_KEYUP sends virtual-key codes. On the other hand, 158 // WM_CHAR sends a composed Unicode character. 159 MSG msg1 = { NULL, WM_KEYDOWN, key_code, 0 }; 160#if defined(USE_AURA) 161 ui::KeyEvent evt1(msg1, false); 162 NativeWebKeyboardEvent keydown_event(&evt1); 163#else 164 NativeWebKeyboardEvent keydown_event(msg1); 165#endif 166 SendNativeKeyEvent(keydown_event); 167 168 MSG msg2 = { NULL, WM_CHAR, (*output)[0], 0 }; 169#if defined(USE_AURA) 170 ui::KeyEvent evt2(msg2, true); 171 NativeWebKeyboardEvent char_event(&evt2); 172#else 173 NativeWebKeyboardEvent char_event(msg2); 174#endif 175 SendNativeKeyEvent(char_event); 176 177 MSG msg3 = { NULL, WM_KEYUP, key_code, 0 }; 178#if defined(USE_AURA) 179 ui::KeyEvent evt3(msg3, false); 180 NativeWebKeyboardEvent keyup_event(&evt3); 181#else 182 NativeWebKeyboardEvent keyup_event(msg3); 183#endif 184 SendNativeKeyEvent(keyup_event); 185 186 return length; 187#elif defined(USE_AURA) && defined(USE_X11) 188 // We ignore |layout|, which means we are only testing the layout of the 189 // current locale. TODO(mazda): fix this to respect |layout|. 190 CHECK(output); 191 const int flags = ConvertMockKeyboardModifier(modifiers); 192 193 XEvent xevent1; 194 InitXKeyEventForTesting(ui::ET_KEY_PRESSED, 195 static_cast<ui::KeyboardCode>(key_code), 196 flags, 197 &xevent1); 198 ui::KeyEvent event1(&xevent1, false); 199 NativeWebKeyboardEvent keydown_event(&event1); 200 SendNativeKeyEvent(keydown_event); 201 202 XEvent xevent2; 203 InitXKeyEventForTesting(ui::ET_KEY_PRESSED, 204 static_cast<ui::KeyboardCode>(key_code), 205 flags, 206 &xevent2); 207 ui::KeyEvent event2(&xevent2, true); 208 NativeWebKeyboardEvent char_event(&event2); 209 SendNativeKeyEvent(char_event); 210 211 XEvent xevent3; 212 InitXKeyEventForTesting(ui::ET_KEY_RELEASED, 213 static_cast<ui::KeyboardCode>(key_code), 214 flags, 215 &xevent3); 216 ui::KeyEvent event3(&xevent3, false); 217 NativeWebKeyboardEvent keyup_event(&event3); 218 SendNativeKeyEvent(keyup_event); 219 220 long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code), 221 flags); 222 output->assign(1, static_cast<char16>(c)); 223 return 1; 224#elif defined(OS_LINUX) 225 // We ignore |layout|, which means we are only testing the layout of the 226 // current locale. TODO(estade): fix this to respect |layout|. 227 std::vector<GdkEvent*> events; 228 ui::SynthesizeKeyPressEvents( 229 NULL, static_cast<ui::KeyboardCode>(key_code), 230 modifiers & (MockKeyboard::LEFT_CONTROL | MockKeyboard::RIGHT_CONTROL), 231 modifiers & (MockKeyboard::LEFT_SHIFT | MockKeyboard::RIGHT_SHIFT), 232 modifiers & (MockKeyboard::LEFT_ALT | MockKeyboard::RIGHT_ALT), 233 &events); 234 235 guint32 unicode_key = 0; 236 for (size_t i = 0; i < events.size(); ++i) { 237 // Only send the up/down events for key press itself (skip the up/down 238 // events for the modifier keys). 239 if ((i + 1) == (events.size() / 2) || i == (events.size() / 2)) { 240 unicode_key = gdk_keyval_to_unicode(events[i]->key.keyval); 241 NativeWebKeyboardEvent webkit_event(events[i]); 242 SendNativeKeyEvent(webkit_event); 243 244 // Need to add a char event after the key down. 245 if (webkit_event.type == WebKit::WebInputEvent::RawKeyDown) { 246 NativeWebKeyboardEvent char_event = webkit_event; 247 char_event.type = WebKit::WebInputEvent::Char; 248 char_event.skip_in_browser = true; 249 SendNativeKeyEvent(char_event); 250 } 251 } 252 gdk_event_free(events[i]); 253 } 254 255 output->assign(1, static_cast<char16>(unicode_key)); 256 return 1; 257#else 258 NOTIMPLEMENTED(); 259 return L'\0'; 260#endif 261 } 262 263 private: 264 scoped_ptr<MockKeyboard> mock_keyboard_; 265}; 266 267// Test that we get form state change notifications when input fields change. 268TEST_F(RenderViewImplTest, DISABLED_OnNavStateChanged) { 269 // Don't want any delay for form state sync changes. This will still post a 270 // message so updates will get coalesced, but as soon as we spin the message 271 // loop, it will generate an update. 272 view()->set_send_content_state_immediately(true); 273 274 LoadHTML("<input type=\"text\" id=\"elt_text\"></input>"); 275 276 // We should NOT have gotten a form state change notification yet. 277 EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching( 278 ViewHostMsg_UpdateState::ID)); 279 render_thread_->sink().ClearMessages(); 280 281 // Change the value of the input. We should have gotten an update state 282 // notification. We need to spin the message loop to catch this update. 283 ExecuteJavaScript("document.getElementById('elt_text').value = 'foo';"); 284 ProcessPendingMessages(); 285 EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching( 286 ViewHostMsg_UpdateState::ID)); 287} 288 289TEST_F(RenderViewImplTest, OnNavigationHttpPost) { 290 ViewMsg_Navigate_Params nav_params; 291 292 // An http url will trigger a resource load so cannot be used here. 293 nav_params.url = GURL("data:text/html,<div>Page</div>"); 294 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL; 295 nav_params.transition = PAGE_TRANSITION_TYPED; 296 nav_params.page_id = -1; 297 nav_params.is_post = true; 298 299 // Set up post data. 300 const unsigned char* raw_data = reinterpret_cast<const unsigned char*>( 301 "post \0\ndata"); 302 const unsigned int length = 11; 303 const std::vector<unsigned char> post_data(raw_data, raw_data + length); 304 nav_params.browser_initiated_post_data = post_data; 305 306 view()->OnNavigate(nav_params); 307 ProcessPendingMessages(); 308 309 const IPC::Message* frame_navigate_msg = 310 render_thread_->sink().GetUniqueMessageMatching( 311 ViewHostMsg_FrameNavigate::ID); 312 EXPECT_TRUE(frame_navigate_msg); 313 314 ViewHostMsg_FrameNavigate::Param host_nav_params; 315 ViewHostMsg_FrameNavigate::Read(frame_navigate_msg, &host_nav_params); 316 EXPECT_TRUE(host_nav_params.a.is_post); 317 318 // Check post data sent to browser matches 319 EXPECT_TRUE(host_nav_params.a.page_state.IsValid()); 320 const WebKit::WebHistoryItem item = PageStateToHistoryItem( 321 host_nav_params.a.page_state); 322 WebKit::WebHTTPBody body = item.httpBody(); 323 WebKit::WebHTTPBody::Element element; 324 bool successful = body.elementAt(0, element); 325 EXPECT_TRUE(successful); 326 EXPECT_EQ(WebKit::WebHTTPBody::Element::TypeData, element.type); 327 EXPECT_EQ(length, element.data.size()); 328 EXPECT_EQ(0, memcmp(raw_data, element.data.data(), length)); 329} 330 331TEST_F(RenderViewImplTest, DecideNavigationPolicy) { 332 WebUITestWebUIControllerFactory factory; 333 WebUIControllerFactory::RegisterFactory(&factory); 334 335 DocumentState state; 336 state.set_navigation_state(NavigationState::CreateContentInitiated()); 337 338 // Navigations to normal HTTP URLs can be handled locally. 339 WebKit::WebURLRequest request(GURL("http://foo.com")); 340 WebKit::WebNavigationPolicy policy = view()->decidePolicyForNavigation( 341 GetMainFrame(), 342 &state, 343 request, 344 WebKit::WebNavigationTypeLinkClicked, 345 WebKit::WebNavigationPolicyCurrentTab, 346 false); 347 EXPECT_EQ(WebKit::WebNavigationPolicyCurrentTab, policy); 348 349 // Verify that form posts to WebUI URLs will be sent to the browser process. 350 WebKit::WebURLRequest form_request(GURL("chrome://foo")); 351 form_request.setHTTPMethod("POST"); 352 policy = view()->decidePolicyForNavigation( 353 GetMainFrame(), 354 &state, 355 form_request, 356 WebKit::WebNavigationTypeFormSubmitted, 357 WebKit::WebNavigationPolicyCurrentTab, 358 false); 359 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy); 360 361 // Verify that popup links to WebUI URLs also are sent to browser. 362 WebKit::WebURLRequest popup_request(GURL("chrome://foo")); 363 policy = view()->decidePolicyForNavigation( 364 GetMainFrame(), 365 &state, 366 popup_request, 367 WebKit::WebNavigationTypeLinkClicked, 368 WebKit::WebNavigationPolicyNewForegroundTab, 369 false); 370 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy); 371} 372 373TEST_F(RenderViewImplTest, DecideNavigationPolicyForWebUI) { 374 // Enable bindings to simulate a WebUI view. 375 view()->OnAllowBindings(BINDINGS_POLICY_WEB_UI); 376 377 DocumentState state; 378 state.set_navigation_state(NavigationState::CreateContentInitiated()); 379 380 // Navigations to normal HTTP URLs will be sent to browser process. 381 WebKit::WebURLRequest request(GURL("http://foo.com")); 382 WebKit::WebNavigationPolicy policy = view()->decidePolicyForNavigation( 383 GetMainFrame(), 384 &state, 385 request, 386 WebKit::WebNavigationTypeLinkClicked, 387 WebKit::WebNavigationPolicyCurrentTab, 388 false); 389 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy); 390 391 // Navigations to WebUI URLs will also be sent to browser process. 392 WebKit::WebURLRequest webui_request(GURL("chrome://foo")); 393 policy = view()->decidePolicyForNavigation( 394 GetMainFrame(), 395 &state, 396 webui_request, 397 WebKit::WebNavigationTypeLinkClicked, 398 WebKit::WebNavigationPolicyCurrentTab, 399 false); 400 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy); 401 402 // Verify that form posts to data URLs will be sent to the browser process. 403 WebKit::WebURLRequest data_request(GURL("data:text/html,foo")); 404 data_request.setHTTPMethod("POST"); 405 policy = view()->decidePolicyForNavigation( 406 GetMainFrame(), 407 &state, 408 data_request, 409 WebKit::WebNavigationTypeFormSubmitted, 410 WebKit::WebNavigationPolicyCurrentTab, 411 false); 412 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy); 413 414 // Verify that a popup that creates a view first and then navigates to a 415 // normal HTTP URL will be sent to the browser process, even though the 416 // new view does not have any enabled_bindings_. 417 WebKit::WebURLRequest popup_request(GURL("http://foo.com")); 418 WebKit::WebView* new_web_view = view()->createView( 419 GetMainFrame(), popup_request, WebKit::WebWindowFeatures(), "foo", 420 WebKit::WebNavigationPolicyNewForegroundTab); 421 RenderViewImpl* new_view = RenderViewImpl::FromWebView(new_web_view); 422 policy = new_view->decidePolicyForNavigation( 423 new_web_view->mainFrame(), 424 &state, 425 popup_request, 426 WebKit::WebNavigationTypeLinkClicked, 427 WebKit::WebNavigationPolicyNewForegroundTab, 428 false); 429 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore, policy); 430 431 // Clean up after the new view so we don't leak it. 432 new_view->Close(); 433 new_view->Release(); 434} 435 436// Ensure the RenderViewImpl sends an ACK to a SwapOut request, even if it is 437// already swapped out. http://crbug.com/93427. 438TEST_F(RenderViewImplTest, SendSwapOutACK) { 439 LoadHTML("<div>Page A</div>"); 440 int initial_page_id = view()->GetPageId(); 441 442 // Respond to a swap out request. 443 view()->OnSwapOut(); 444 445 // Ensure the swap out commits synchronously. 446 EXPECT_NE(initial_page_id, view()->GetPageId()); 447 448 // Check for a valid OnSwapOutACK. 449 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching( 450 ViewHostMsg_SwapOut_ACK::ID); 451 ASSERT_TRUE(msg); 452 453 // It is possible to get another swap out request. Ensure that we send 454 // an ACK, even if we don't have to do anything else. 455 render_thread_->sink().ClearMessages(); 456 view()->OnSwapOut(); 457 const IPC::Message* msg2 = render_thread_->sink().GetUniqueMessageMatching( 458 ViewHostMsg_SwapOut_ACK::ID); 459 ASSERT_TRUE(msg2); 460 461 // If we navigate back to this RenderView, ensure we don't send a state 462 // update for the swapped out URL. (http://crbug.com/72235) 463 ViewMsg_Navigate_Params nav_params; 464 nav_params.url = GURL("data:text/html,<div>Page B</div>"); 465 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL; 466 nav_params.transition = PAGE_TRANSITION_TYPED; 467 nav_params.current_history_list_length = 1; 468 nav_params.current_history_list_offset = 0; 469 nav_params.pending_history_list_offset = 1; 470 nav_params.page_id = -1; 471 view()->OnNavigate(nav_params); 472 ProcessPendingMessages(); 473 const IPC::Message* msg3 = render_thread_->sink().GetUniqueMessageMatching( 474 ViewHostMsg_UpdateState::ID); 475 EXPECT_FALSE(msg3); 476} 477 478// Ensure the RenderViewImpl reloads the previous page if a reload request 479// arrives while it is showing swappedout://. http://crbug.com/143155. 480TEST_F(RenderViewImplTest, ReloadWhileSwappedOut) { 481 // Load page A. 482 LoadHTML("<div>Page A</div>"); 483 484 // Load page B, which will trigger an UpdateState message for page A. 485 LoadHTML("<div>Page B</div>"); 486 487 // Check for a valid UpdateState message for page A. 488 ProcessPendingMessages(); 489 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching( 490 ViewHostMsg_UpdateState::ID); 491 ASSERT_TRUE(msg_A); 492 int page_id_A; 493 PageState state_A; 494 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A); 495 EXPECT_EQ(1, page_id_A); 496 render_thread_->sink().ClearMessages(); 497 498 // Back to page A (page_id 1) and commit. 499 ViewMsg_Navigate_Params params_A; 500 params_A.navigation_type = ViewMsg_Navigate_Type::NORMAL; 501 params_A.transition = PAGE_TRANSITION_FORWARD_BACK; 502 params_A.current_history_list_length = 2; 503 params_A.current_history_list_offset = 1; 504 params_A.pending_history_list_offset = 0; 505 params_A.page_id = 1; 506 params_A.page_state = state_A; 507 view()->OnNavigate(params_A); 508 ProcessPendingMessages(); 509 510 // Respond to a swap out request. 511 view()->OnSwapOut(); 512 513 // Check for a OnSwapOutACK. 514 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching( 515 ViewHostMsg_SwapOut_ACK::ID); 516 ASSERT_TRUE(msg); 517 render_thread_->sink().ClearMessages(); 518 519 // It is possible to get a reload request at this point, containing the 520 // params.page_state of the initial page (e.g., if the new page fails the 521 // provisional load in the renderer process, after we unload the old page). 522 // Ensure the old page gets reloaded, not swappedout://. 523 ViewMsg_Navigate_Params nav_params; 524 nav_params.url = GURL("data:text/html,<div>Page A</div>"); 525 nav_params.navigation_type = ViewMsg_Navigate_Type::RELOAD; 526 nav_params.transition = PAGE_TRANSITION_RELOAD; 527 nav_params.current_history_list_length = 2; 528 nav_params.current_history_list_offset = 0; 529 nav_params.pending_history_list_offset = 0; 530 nav_params.page_id = 1; 531 nav_params.page_state = state_A; 532 view()->OnNavigate(nav_params); 533 ProcessPendingMessages(); 534 535 // Verify page A committed, not swappedout://. 536 const IPC::Message* frame_navigate_msg = 537 render_thread_->sink().GetUniqueMessageMatching( 538 ViewHostMsg_FrameNavigate::ID); 539 EXPECT_TRUE(frame_navigate_msg); 540 541 // Read URL out of the parent trait of the params object. 542 ViewHostMsg_FrameNavigate::Param commit_params; 543 ViewHostMsg_FrameNavigate::Read(frame_navigate_msg, &commit_params); 544 EXPECT_NE(GURL("swappedout://"), commit_params.a.url); 545} 546 547 548// Test that we get the correct UpdateState message when we go back twice 549// quickly without committing. Regression test for http://crbug.com/58082. 550// Disabled: http://crbug.com/157357 . 551TEST_F(RenderViewImplTest, DISABLED_LastCommittedUpdateState) { 552 // Load page A. 553 LoadHTML("<div>Page A</div>"); 554 555 // Load page B, which will trigger an UpdateState message for page A. 556 LoadHTML("<div>Page B</div>"); 557 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); 562 ASSERT_TRUE(msg_A); 563 int page_id_A; 564 PageState state_A; 565 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A); 566 EXPECT_EQ(1, page_id_A); 567 render_thread_->sink().ClearMessages(); 568 569 // Load page C, which will trigger an UpdateState message for page B. 570 LoadHTML("<div>Page C</div>"); 571 572 // Check for a valid UpdateState for page B. 573 ProcessPendingMessages(); 574 const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching( 575 ViewHostMsg_UpdateState::ID); 576 ASSERT_TRUE(msg_B); 577 int page_id_B; 578 PageState state_B; 579 ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B); 580 EXPECT_EQ(2, page_id_B); 581 EXPECT_NE(state_A, state_B); 582 render_thread_->sink().ClearMessages(); 583 584 // Load page D, which will trigger an UpdateState message for page C. 585 LoadHTML("<div>Page D</div>"); 586 587 // Check for a valid UpdateState for page C. 588 ProcessPendingMessages(); 589 const IPC::Message* msg_C = render_thread_->sink().GetUniqueMessageMatching( 590 ViewHostMsg_UpdateState::ID); 591 ASSERT_TRUE(msg_C); 592 int page_id_C; 593 PageState state_C; 594 ViewHostMsg_UpdateState::Read(msg_C, &page_id_C, &state_C); 595 EXPECT_EQ(3, page_id_C); 596 EXPECT_NE(state_B, state_C); 597 render_thread_->sink().ClearMessages(); 598 599 // Go back to C and commit, preparing for our real test. 600 ViewMsg_Navigate_Params params_C; 601 params_C.navigation_type = ViewMsg_Navigate_Type::NORMAL; 602 params_C.transition = PAGE_TRANSITION_FORWARD_BACK; 603 params_C.current_history_list_length = 4; 604 params_C.current_history_list_offset = 3; 605 params_C.pending_history_list_offset = 2; 606 params_C.page_id = 3; 607 params_C.page_state = state_C; 608 view()->OnNavigate(params_C); 609 ProcessPendingMessages(); 610 render_thread_->sink().ClearMessages(); 611 612 // Go back twice quickly, such that page B does not have a chance to commit. 613 // This leads to two changes to the back/forward list but only one change to 614 // the RenderView's page ID. 615 616 // Back to page B (page_id 2), without committing. 617 ViewMsg_Navigate_Params params_B; 618 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL; 619 params_B.transition = PAGE_TRANSITION_FORWARD_BACK; 620 params_B.current_history_list_length = 4; 621 params_B.current_history_list_offset = 2; 622 params_B.pending_history_list_offset = 1; 623 params_B.page_id = 2; 624 params_B.page_state = state_B; 625 view()->OnNavigate(params_B); 626 627 // Back to page A (page_id 1) and commit. 628 ViewMsg_Navigate_Params params; 629 params.navigation_type = ViewMsg_Navigate_Type::NORMAL; 630 params.transition = PAGE_TRANSITION_FORWARD_BACK; 631 params_B.current_history_list_length = 4; 632 params_B.current_history_list_offset = 2; 633 params_B.pending_history_list_offset = 0; 634 params.page_id = 1; 635 params.page_state = state_A; 636 view()->OnNavigate(params); 637 ProcessPendingMessages(); 638 639 // Now ensure that the UpdateState message we receive is consistent 640 // and represents page C in both page_id and state. 641 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching( 642 ViewHostMsg_UpdateState::ID); 643 ASSERT_TRUE(msg); 644 int page_id; 645 PageState state; 646 ViewHostMsg_UpdateState::Read(msg, &page_id, &state); 647 EXPECT_EQ(page_id_C, page_id); 648 EXPECT_NE(state_A, state); 649 EXPECT_NE(state_B, state); 650 EXPECT_EQ(state_C, state); 651} 652 653// Test that the history_page_ids_ list can reveal when a stale back/forward 654// navigation arrives from the browser and can be ignored. See 655// http://crbug.com/86758. 656TEST_F(RenderViewImplTest, StaleNavigationsIgnored) { 657 // Load page A. 658 LoadHTML("<div>Page A</div>"); 659 EXPECT_EQ(1, view()->history_list_length_); 660 EXPECT_EQ(0, view()->history_list_offset_); 661 EXPECT_EQ(1, view()->history_page_ids_[0]); 662 663 // Load page B, which will trigger an UpdateState message for page A. 664 LoadHTML("<div>Page B</div>"); 665 EXPECT_EQ(2, view()->history_list_length_); 666 EXPECT_EQ(1, view()->history_list_offset_); 667 EXPECT_EQ(2, view()->history_page_ids_[1]); 668 669 // Check for a valid UpdateState message for page A. 670 ProcessPendingMessages(); 671 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching( 672 ViewHostMsg_UpdateState::ID); 673 ASSERT_TRUE(msg_A); 674 int page_id_A; 675 PageState state_A; 676 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A); 677 EXPECT_EQ(1, page_id_A); 678 render_thread_->sink().ClearMessages(); 679 680 // Back to page A (page_id 1) and commit. 681 ViewMsg_Navigate_Params params_A; 682 params_A.navigation_type = ViewMsg_Navigate_Type::NORMAL; 683 params_A.transition = PAGE_TRANSITION_FORWARD_BACK; 684 params_A.current_history_list_length = 2; 685 params_A.current_history_list_offset = 1; 686 params_A.pending_history_list_offset = 0; 687 params_A.page_id = 1; 688 params_A.page_state = state_A; 689 view()->OnNavigate(params_A); 690 ProcessPendingMessages(); 691 692 // A new navigation commits, clearing the forward history. 693 LoadHTML("<div>Page C</div>"); 694 EXPECT_EQ(2, view()->history_list_length_); 695 EXPECT_EQ(1, view()->history_list_offset_); 696 EXPECT_EQ(3, view()->history_page_ids_[1]); 697 698 // The browser then sends a stale navigation to B, which should be ignored. 699 ViewMsg_Navigate_Params params_B; 700 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL; 701 params_B.transition = PAGE_TRANSITION_FORWARD_BACK; 702 params_B.current_history_list_length = 2; 703 params_B.current_history_list_offset = 0; 704 params_B.pending_history_list_offset = 1; 705 params_B.page_id = 2; 706 params_B.page_state = state_A; // Doesn't matter, just has to be present. 707 view()->OnNavigate(params_B); 708 709 // State should be unchanged. 710 EXPECT_EQ(2, view()->history_list_length_); 711 EXPECT_EQ(1, view()->history_list_offset_); 712 EXPECT_EQ(3, view()->history_page_ids_[1]); 713} 714 715// Test that we do not ignore navigations after the entry limit is reached, 716// in which case the browser starts dropping entries from the front. In this 717// case, we'll see a page_id mismatch but the RenderView's id will be older, 718// not newer, than params.page_id. Use this as a cue that we should update the 719// state and not treat it like a navigation to a cropped forward history item. 720// See http://crbug.com/89798. 721TEST_F(RenderViewImplTest, DontIgnoreBackAfterNavEntryLimit) { 722 // Load page A. 723 LoadHTML("<div>Page A</div>"); 724 EXPECT_EQ(1, view()->history_list_length_); 725 EXPECT_EQ(0, view()->history_list_offset_); 726 EXPECT_EQ(1, view()->history_page_ids_[0]); 727 728 // Load page B, which will trigger an UpdateState message for page A. 729 LoadHTML("<div>Page B</div>"); 730 EXPECT_EQ(2, view()->history_list_length_); 731 EXPECT_EQ(1, view()->history_list_offset_); 732 EXPECT_EQ(2, view()->history_page_ids_[1]); 733 734 // Check for a valid UpdateState message for page A. 735 ProcessPendingMessages(); 736 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching( 737 ViewHostMsg_UpdateState::ID); 738 ASSERT_TRUE(msg_A); 739 int page_id_A; 740 PageState state_A; 741 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A); 742 EXPECT_EQ(1, page_id_A); 743 render_thread_->sink().ClearMessages(); 744 745 // Load page C, which will trigger an UpdateState message for page B. 746 LoadHTML("<div>Page C</div>"); 747 EXPECT_EQ(3, view()->history_list_length_); 748 EXPECT_EQ(2, view()->history_list_offset_); 749 EXPECT_EQ(3, view()->history_page_ids_[2]); 750 751 // Check for a valid UpdateState message for page B. 752 ProcessPendingMessages(); 753 const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching( 754 ViewHostMsg_UpdateState::ID); 755 ASSERT_TRUE(msg_B); 756 int page_id_B; 757 PageState state_B; 758 ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B); 759 EXPECT_EQ(2, page_id_B); 760 render_thread_->sink().ClearMessages(); 761 762 // Suppose the browser has limited the number of NavigationEntries to 2. 763 // It has now dropped the first entry, but the renderer isn't notified. 764 // Ensure that going back to page B (page_id 2) at offset 0 is successful. 765 ViewMsg_Navigate_Params params_B; 766 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL; 767 params_B.transition = PAGE_TRANSITION_FORWARD_BACK; 768 params_B.current_history_list_length = 2; 769 params_B.current_history_list_offset = 1; 770 params_B.pending_history_list_offset = 0; 771 params_B.page_id = 2; 772 params_B.page_state = state_B; 773 view()->OnNavigate(params_B); 774 ProcessPendingMessages(); 775 776 EXPECT_EQ(2, view()->history_list_length_); 777 EXPECT_EQ(0, view()->history_list_offset_); 778 EXPECT_EQ(2, view()->history_page_ids_[0]); 779} 780 781// Test that our IME backend sends a notification message when the input focus 782// changes. 783TEST_F(RenderViewImplTest, OnImeTypeChanged) { 784 // Enable our IME backend code. 785 view()->OnSetInputMethodActive(true); 786 787 // Load an HTML page consisting of two input fields. 788 view()->set_send_content_state_immediately(true); 789 LoadHTML("<html>" 790 "<head>" 791 "</head>" 792 "<body>" 793 "<input id=\"test1\" type=\"text\" value=\"some text\"></input>" 794 "<input id=\"test2\" type=\"password\"></input>" 795 "<input id=\"test3\" type=\"text\" inputmode=\"verbatim\"></input>" 796 "<input id=\"test4\" type=\"text\" inputmode=\"latin\"></input>" 797 "<input id=\"test5\" type=\"text\" inputmode=\"latin-name\"></input>" 798 "<input id=\"test6\" type=\"text\" inputmode=\"latin-prose\">" 799 "</input>" 800 "<input id=\"test7\" type=\"text\" inputmode=\"full-width-latin\">" 801 "</input>" 802 "<input id=\"test8\" type=\"text\" inputmode=\"kana\"></input>" 803 "<input id=\"test9\" type=\"text\" inputmode=\"katakana\"></input>" 804 "<input id=\"test10\" type=\"text\" inputmode=\"numeric\"></input>" 805 "<input id=\"test11\" type=\"text\" inputmode=\"tel\"></input>" 806 "<input id=\"test12\" type=\"text\" inputmode=\"email\"></input>" 807 "<input id=\"test13\" type=\"text\" inputmode=\"url\"></input>" 808 "<input id=\"test14\" type=\"text\" inputmode=\"unknown\"></input>" 809 "<input id=\"test15\" type=\"text\" inputmode=\"verbatim\"></input>" 810 "</body>" 811 "</html>"); 812 render_thread_->sink().ClearMessages(); 813 814 struct InputModeTestCase { 815 const char* input_id; 816 ui::TextInputMode expected_mode; 817 }; 818 static const InputModeTestCase kInputModeTestCases[] = { 819 {"test1", ui::TEXT_INPUT_MODE_DEFAULT}, 820 {"test3", ui::TEXT_INPUT_MODE_VERBATIM}, 821 {"test4", ui::TEXT_INPUT_MODE_LATIN}, 822 {"test5", ui::TEXT_INPUT_MODE_LATIN_NAME}, 823 {"test6", ui::TEXT_INPUT_MODE_LATIN_PROSE}, 824 {"test7", ui::TEXT_INPUT_MODE_FULL_WIDTH_LATIN}, 825 {"test8", ui::TEXT_INPUT_MODE_KANA}, 826 {"test9", ui::TEXT_INPUT_MODE_KATAKANA}, 827 {"test10", ui::TEXT_INPUT_MODE_NUMERIC}, 828 {"test11", ui::TEXT_INPUT_MODE_TEL}, 829 {"test12", ui::TEXT_INPUT_MODE_EMAIL}, 830 {"test13", ui::TEXT_INPUT_MODE_URL}, 831 {"test14", ui::TEXT_INPUT_MODE_DEFAULT}, 832 {"test15", ui::TEXT_INPUT_MODE_VERBATIM}, 833 }; 834 835 const int kRepeatCount = 10; 836 for (int i = 0; i < kRepeatCount; i++) { 837 // Move the input focus to the first <input> element, where we should 838 // activate IMEs. 839 ExecuteJavaScript("document.getElementById('test1').focus();"); 840 ProcessPendingMessages(); 841 render_thread_->sink().ClearMessages(); 842 843 // Update the IME status and verify if our IME backend sends an IPC message 844 // to activate IMEs. 845 view()->UpdateTextInputType(); 846 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0); 847 EXPECT_TRUE(msg != NULL); 848 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type()); 849 ui::TextInputType type; 850 bool can_compose_inline = false; 851 ui::TextInputMode input_mode = ui::TEXT_INPUT_MODE_DEFAULT; 852 ViewHostMsg_TextInputTypeChanged::Read(msg, 853 &type, 854 &can_compose_inline, 855 &input_mode); 856 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, type); 857 EXPECT_EQ(true, can_compose_inline); 858 859 // Move the input focus to the second <input> element, where we should 860 // de-activate IMEs. 861 ExecuteJavaScript("document.getElementById('test2').focus();"); 862 ProcessPendingMessages(); 863 render_thread_->sink().ClearMessages(); 864 865 // Update the IME status and verify if our IME backend sends an IPC message 866 // to de-activate IMEs. 867 view()->UpdateTextInputType(); 868 msg = render_thread_->sink().GetMessageAt(0); 869 EXPECT_TRUE(msg != NULL); 870 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type()); 871 ViewHostMsg_TextInputTypeChanged::Read(msg, 872 &type, 873 &can_compose_inline, 874 &input_mode); 875 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, type); 876 877 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInputModeTestCases); i++) { 878 const InputModeTestCase* test_case = &kInputModeTestCases[i]; 879 std::string javascript = 880 base::StringPrintf("document.getElementById('%s').focus();", 881 test_case->input_id); 882 // Move the input focus to the target <input> element, where we should 883 // activate IMEs. 884 ExecuteJavaScriptAndReturnIntValue(ASCIIToUTF16(javascript), NULL); 885 ProcessPendingMessages(); 886 render_thread_->sink().ClearMessages(); 887 888 // Update the IME status and verify if our IME backend sends an IPC 889 // message to activate IMEs. 890 view()->UpdateTextInputType(); 891 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0); 892 EXPECT_TRUE(msg != NULL); 893 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type()); 894 ViewHostMsg_TextInputTypeChanged::Read(msg, 895 &type, 896 &can_compose_inline, 897 &input_mode); 898 EXPECT_EQ(test_case->expected_mode, input_mode); 899 } 900 } 901} 902 903// Test that our IME backend can compose CJK words. 904// Our IME front-end sends many platform-independent messages to the IME backend 905// while it composes CJK words. This test sends the minimal messages captured 906// on my local environment directly to the IME backend to verify if the backend 907// can compose CJK words without any problems. 908// This test uses an array of command sets because an IME composotion does not 909// only depends on IME events, but also depends on window events, e.g. moving 910// the window focus while composing a CJK text. To handle such complicated 911// cases, this test should not only call IME-related functions in the 912// RenderWidget class, but also call some RenderWidget members, e.g. 913// ExecuteJavaScript(), RenderWidget::OnSetFocus(), etc. 914TEST_F(RenderViewImplTest, ImeComposition) { 915 enum ImeCommand { 916 IME_INITIALIZE, 917 IME_SETINPUTMODE, 918 IME_SETFOCUS, 919 IME_SETCOMPOSITION, 920 IME_CONFIRMCOMPOSITION, 921 IME_CANCELCOMPOSITION 922 }; 923 struct ImeMessage { 924 ImeCommand command; 925 bool enable; 926 int selection_start; 927 int selection_end; 928 const wchar_t* ime_string; 929 const wchar_t* result; 930 }; 931 static const ImeMessage kImeMessages[] = { 932 // Scenario 1: input a Chinese word with Microsoft IME (on Vista). 933 {IME_INITIALIZE, true, 0, 0, NULL, NULL}, 934 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL}, 935 {IME_SETFOCUS, true, 0, 0, NULL, NULL}, 936 {IME_SETCOMPOSITION, false, 1, 1, L"n", L"n"}, 937 {IME_SETCOMPOSITION, false, 2, 2, L"ni", L"ni"}, 938 {IME_SETCOMPOSITION, false, 3, 3, L"nih", L"nih"}, 939 {IME_SETCOMPOSITION, false, 4, 4, L"niha", L"niha"}, 940 {IME_SETCOMPOSITION, false, 5, 5, L"nihao", L"nihao"}, 941 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"\x4F60\x597D", L"\x4F60\x597D"}, 942 // Scenario 2: input a Japanese word with Microsoft IME (on Vista). 943 {IME_INITIALIZE, true, 0, 0, NULL, NULL}, 944 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL}, 945 {IME_SETFOCUS, true, 0, 0, NULL, NULL}, 946 {IME_SETCOMPOSITION, false, 0, 1, L"\xFF4B", L"\xFF4B"}, 947 {IME_SETCOMPOSITION, false, 0, 1, L"\x304B", L"\x304B"}, 948 {IME_SETCOMPOSITION, false, 0, 2, L"\x304B\xFF4E", L"\x304B\xFF4E"}, 949 {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\xFF4A", 950 L"\x304B\x3093\xFF4A"}, 951 {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\x3058", 952 L"\x304B\x3093\x3058"}, 953 {IME_SETCOMPOSITION, false, 0, 2, L"\x611F\x3058", L"\x611F\x3058"}, 954 {IME_SETCOMPOSITION, false, 0, 2, L"\x6F22\x5B57", L"\x6F22\x5B57"}, 955 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"}, 956 {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"}, 957 // Scenario 3: input a Korean word with Microsot IME (on Vista). 958 {IME_INITIALIZE, true, 0, 0, NULL, NULL}, 959 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL}, 960 {IME_SETFOCUS, true, 0, 0, NULL, NULL}, 961 {IME_SETCOMPOSITION, false, 0, 1, L"\x3147", L"\x3147"}, 962 {IME_SETCOMPOSITION, false, 0, 1, L"\xC544", L"\xC544"}, 963 {IME_SETCOMPOSITION, false, 0, 1, L"\xC548", L"\xC548"}, 964 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\xC548"}, 965 {IME_SETCOMPOSITION, false, 0, 1, L"\x3134", L"\xC548\x3134"}, 966 {IME_SETCOMPOSITION, false, 0, 1, L"\xB140", L"\xC548\xB140"}, 967 {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"}, 968 {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\xC548"}, 969 {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"}, 970 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\xC548\xB155"}, 971 }; 972 973 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kImeMessages); i++) { 974 const ImeMessage* ime_message = &kImeMessages[i]; 975 switch (ime_message->command) { 976 case IME_INITIALIZE: 977 // Load an HTML page consisting of a content-editable <div> element, 978 // and move the input focus to the <div> element, where we can use 979 // IMEs. 980 view()->OnSetInputMethodActive(ime_message->enable); 981 view()->set_send_content_state_immediately(true); 982 LoadHTML("<html>" 983 "<head>" 984 "</head>" 985 "<body>" 986 "<div id=\"test1\" contenteditable=\"true\"></div>" 987 "</body>" 988 "</html>"); 989 ExecuteJavaScript("document.getElementById('test1').focus();"); 990 break; 991 992 case IME_SETINPUTMODE: 993 // Activate (or deactivate) our IME back-end. 994 view()->OnSetInputMethodActive(ime_message->enable); 995 break; 996 997 case IME_SETFOCUS: 998 // Update the window focus. 999 view()->OnSetFocus(ime_message->enable); 1000 break; 1001 1002 case IME_SETCOMPOSITION: 1003 view()->OnImeSetComposition( 1004 WideToUTF16Hack(ime_message->ime_string), 1005 std::vector<WebKit::WebCompositionUnderline>(), 1006 ime_message->selection_start, 1007 ime_message->selection_end); 1008 break; 1009 1010 case IME_CONFIRMCOMPOSITION: 1011 view()->OnImeConfirmComposition( 1012 WideToUTF16Hack(ime_message->ime_string), 1013 ui::Range::InvalidRange(), 1014 false); 1015 break; 1016 1017 case IME_CANCELCOMPOSITION: 1018 view()->OnImeSetComposition( 1019 string16(), 1020 std::vector<WebKit::WebCompositionUnderline>(), 1021 0, 0); 1022 break; 1023 } 1024 1025 // Update the status of our IME back-end. 1026 // TODO(hbono): we should verify messages to be sent from the back-end. 1027 view()->UpdateTextInputType(); 1028 ProcessPendingMessages(); 1029 render_thread_->sink().ClearMessages(); 1030 1031 if (ime_message->result) { 1032 // Retrieve the content of this page and compare it with the expected 1033 // result. 1034 const int kMaxOutputCharacters = 128; 1035 std::wstring output = UTF16ToWideHack( 1036 GetMainFrame()->contentAsText(kMaxOutputCharacters)); 1037 EXPECT_EQ(output, ime_message->result); 1038 } 1039 } 1040} 1041 1042// Test that the RenderView::OnSetTextDirection() function can change the text 1043// direction of the selected input element. 1044TEST_F(RenderViewImplTest, OnSetTextDirection) { 1045 // Load an HTML page consisting of a <textarea> element and a <div> element. 1046 // This test changes the text direction of the <textarea> element, and 1047 // writes the values of its 'dir' attribute and its 'direction' property to 1048 // verify that the text direction is changed. 1049 view()->set_send_content_state_immediately(true); 1050 LoadHTML("<html>" 1051 "<head>" 1052 "</head>" 1053 "<body>" 1054 "<textarea id=\"test\"></textarea>" 1055 "<div id=\"result\" contenteditable=\"true\"></div>" 1056 "</body>" 1057 "</html>"); 1058 render_thread_->sink().ClearMessages(); 1059 1060 static const struct { 1061 WebTextDirection direction; 1062 const wchar_t* expected_result; 1063 } kTextDirection[] = { 1064 { WebKit::WebTextDirectionRightToLeft, L"\x000A" L"rtl,rtl" }, 1065 { WebKit::WebTextDirectionLeftToRight, L"\x000A" L"ltr,ltr" }, 1066 }; 1067 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTextDirection); ++i) { 1068 // Set the text direction of the <textarea> element. 1069 ExecuteJavaScript("document.getElementById('test').focus();"); 1070 view()->OnSetTextDirection(kTextDirection[i].direction); 1071 1072 // Write the values of its DOM 'dir' attribute and its CSS 'direction' 1073 // property to the <div> element. 1074 ExecuteJavaScript("var result = document.getElementById('result');" 1075 "var node = document.getElementById('test');" 1076 "var style = getComputedStyle(node, null);" 1077 "result.innerText =" 1078 " node.getAttribute('dir') + ',' +" 1079 " style.getPropertyValue('direction');"); 1080 1081 // Copy the document content to std::wstring and compare with the 1082 // expected result. 1083 const int kMaxOutputCharacters = 16; 1084 std::wstring output = UTF16ToWideHack( 1085 GetMainFrame()->contentAsText(kMaxOutputCharacters)); 1086 EXPECT_EQ(output, kTextDirection[i].expected_result); 1087 } 1088} 1089 1090// see http://crbug.com/238750 1091#if defined(OS_WIN) 1092#define MAYBE_OnHandleKeyboardEvent DISABLED_OnHandleKeyboardEvent 1093#else 1094#define MAYBE_OnHandleKeyboardEvent OnHandleKeyboardEvent 1095#endif 1096 1097// Test that we can receive correct DOM events when we send input events 1098// through the RenderWidget::OnHandleInputEvent() function. 1099TEST_F(RenderViewImplTest, MAYBE_OnHandleKeyboardEvent) { 1100#if !defined(OS_MACOSX) 1101 // Load an HTML page consisting of one <input> element and three 1102 // contentediable <div> elements. 1103 // The <input> element is used for sending keyboard events, and the <div> 1104 // elements are used for writing DOM events in the following format: 1105 // "<keyCode>,<shiftKey>,<controlKey>,<altKey>". 1106 // TODO(hbono): <http://crbug.com/2215> Our WebKit port set |ev.metaKey| to 1107 // true when pressing an alt key, i.e. the |ev.metaKey| value is not 1108 // trustworthy. We will check the |ev.metaKey| value when this issue is fixed. 1109 view()->set_send_content_state_immediately(true); 1110 LoadHTML("<html>" 1111 "<head>" 1112 "<title></title>" 1113 "<script type='text/javascript' language='javascript'>" 1114 "function OnKeyEvent(ev) {" 1115 " var result = document.getElementById(ev.type);" 1116 " result.innerText =" 1117 " (ev.which || ev.keyCode) + ',' +" 1118 " ev.shiftKey + ',' +" 1119 " ev.ctrlKey + ',' +" 1120 " ev.altKey;" 1121 " return true;" 1122 "}" 1123 "</script>" 1124 "</head>" 1125 "<body>" 1126 "<input id='test' type='text'" 1127 " onkeydown='return OnKeyEvent(event);'" 1128 " onkeypress='return OnKeyEvent(event);'" 1129 " onkeyup='return OnKeyEvent(event);'>" 1130 "</input>" 1131 "<div id='keydown' contenteditable='true'>" 1132 "</div>" 1133 "<div id='keypress' contenteditable='true'>" 1134 "</div>" 1135 "<div id='keyup' contenteditable='true'>" 1136 "</div>" 1137 "</body>" 1138 "</html>"); 1139 ExecuteJavaScript("document.getElementById('test').focus();"); 1140 render_thread_->sink().ClearMessages(); 1141 1142 static const MockKeyboard::Layout kLayouts[] = { 1143#if defined(OS_WIN) 1144 // Since we ignore the mock keyboard layout on Linux and instead just use 1145 // the screen's keyboard layout, these trivially pass. They are commented 1146 // out to avoid the illusion that they work. 1147 MockKeyboard::LAYOUT_ARABIC, 1148 MockKeyboard::LAYOUT_CANADIAN_FRENCH, 1149 MockKeyboard::LAYOUT_FRENCH, 1150 MockKeyboard::LAYOUT_HEBREW, 1151 MockKeyboard::LAYOUT_RUSSIAN, 1152#endif 1153 MockKeyboard::LAYOUT_UNITED_STATES, 1154 }; 1155 1156 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) { 1157 // For each key code, we send three keyboard events. 1158 // * we press only the key; 1159 // * we press the key and a left-shift key, and; 1160 // * we press the key and a right-alt (AltGr) key. 1161 // For each modifiers, we need a string used for formatting its expected 1162 // result. (See the above comment for its format.) 1163 static const struct { 1164 MockKeyboard::Modifiers modifiers; 1165 const char* expected_result; 1166 } kModifierData[] = { 1167 {MockKeyboard::NONE, "false,false,false"}, 1168 {MockKeyboard::LEFT_SHIFT, "true,false,false"}, 1169#if defined(OS_WIN) 1170 {MockKeyboard::RIGHT_ALT, "false,false,true"}, 1171#endif 1172 }; 1173 1174 MockKeyboard::Layout layout = kLayouts[i]; 1175 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifierData); ++j) { 1176 // Virtual key codes used for this test. 1177 static const int kKeyCodes[] = { 1178 '0', '1', '2', '3', '4', '5', '6', '7', 1179 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 1180 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 1181 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 1182 'W', 'X', 'Y', 'Z', 1183 ui::VKEY_OEM_1, 1184 ui::VKEY_OEM_PLUS, 1185 ui::VKEY_OEM_COMMA, 1186 ui::VKEY_OEM_MINUS, 1187 ui::VKEY_OEM_PERIOD, 1188 ui::VKEY_OEM_2, 1189 ui::VKEY_OEM_3, 1190 ui::VKEY_OEM_4, 1191 ui::VKEY_OEM_5, 1192 ui::VKEY_OEM_6, 1193 ui::VKEY_OEM_7, 1194#if defined(OS_WIN) 1195 // Not sure how to handle this key on Linux. 1196 ui::VKEY_OEM_8, 1197#endif 1198 }; 1199 1200 MockKeyboard::Modifiers modifiers = kModifierData[j].modifiers; 1201 for (size_t k = 0; k < ARRAYSIZE_UNSAFE(kKeyCodes); ++k) { 1202 // Send a keyboard event to the RenderView object. 1203 // We should test a keyboard event only when the given keyboard-layout 1204 // driver is installed in a PC and the driver can assign a Unicode 1205 // charcter for the given tuple (key-code and modifiers). 1206 int key_code = kKeyCodes[k]; 1207 string16 char_code; 1208 if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0) 1209 continue; 1210 1211 // Create an expected result from the virtual-key code, the character 1212 // code, and the modifier-key status. 1213 // We format a string that emulates a DOM-event string produced hy 1214 // our JavaScript function. (See the above comment for the format.) 1215 static char expected_result[1024]; 1216 expected_result[0] = 0; 1217 base::snprintf(&expected_result[0], 1218 sizeof(expected_result), 1219 "\n" // texts in the <input> element 1220 "%d,%s\n" // texts in the first <div> element 1221 "%d,%s\n" // texts in the second <div> element 1222 "%d,%s", // texts in the third <div> element 1223 key_code, kModifierData[j].expected_result, 1224 static_cast<int>(char_code[0]), 1225 kModifierData[j].expected_result, 1226 key_code, kModifierData[j].expected_result); 1227 1228 // Retrieve the text in the test page and compare it with the expected 1229 // text created from a virtual-key code, a character code, and the 1230 // modifier-key status. 1231 const int kMaxOutputCharacters = 1024; 1232 std::string output = UTF16ToUTF8( 1233 GetMainFrame()->contentAsText(kMaxOutputCharacters)); 1234 EXPECT_EQ(expected_result, output); 1235 } 1236 } 1237 } 1238#else 1239 NOTIMPLEMENTED(); 1240#endif 1241} 1242 1243// Test that our EditorClientImpl class can insert characters when we send 1244// keyboard events through the RenderWidget::OnHandleInputEvent() function. 1245// This test is for preventing regressions caused only when we use non-US 1246// keyboards, such as Issue 10846. 1247// see http://crbug.com/244562 1248#if defined(OS_WIN) 1249#define MAYBE_InsertCharacters DISABLED_InsertCharacters 1250#else 1251#define MAYBE_InsertCharacters InsertCharacters 1252#endif 1253TEST_F(RenderViewImplTest, MAYBE_InsertCharacters) { 1254#if !defined(OS_MACOSX) 1255 static const struct { 1256 MockKeyboard::Layout layout; 1257 const wchar_t* expected_result; 1258 } kLayouts[] = { 1259#if 0 1260 // Disabled these keyboard layouts because buildbots do not have their 1261 // keyboard-layout drivers installed. 1262 {MockKeyboard::LAYOUT_ARABIC, 1263 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" 1264 L"\x0038\x0039\x0634\x0624\x064a\x062b\x0628\x0644" 1265 L"\x0627\x0647\x062a\x0646\x0645\x0629\x0649\x062e" 1266 L"\x062d\x0636\x0642\x0633\x0641\x0639\x0631\x0635" 1267 L"\x0621\x063a\x0626\x0643\x003d\x0648\x002d\x0632" 1268 L"\x0638\x0630\x062c\x005c\x062f\x0637\x0028\x0021" 1269 L"\x0040\x0023\x0024\x0025\x005e\x0026\x002a\x0029" 1270 L"\x0650\x007d\x005d\x064f\x005b\x0623\x00f7\x0640" 1271 L"\x060c\x002f\x2019\x0622\x00d7\x061b\x064e\x064c" 1272 L"\x064d\x2018\x007b\x064b\x0652\x0625\x007e\x003a" 1273 L"\x002b\x002c\x005f\x002e\x061f\x0651\x003c\x007c" 1274 L"\x003e\x0022\x0030\x0031\x0032\x0033\x0034\x0035" 1275 L"\x0036\x0037\x0038\x0039\x0634\x0624\x064a\x062b" 1276 L"\x0628\x0644\x0627\x0647\x062a\x0646\x0645\x0629" 1277 L"\x0649\x062e\x062d\x0636\x0642\x0633\x0641\x0639" 1278 L"\x0631\x0635\x0621\x063a\x0626\x0643\x003d\x0648" 1279 L"\x002d\x0632\x0638\x0630\x062c\x005c\x062f\x0637" 1280 }, 1281 {MockKeyboard::LAYOUT_HEBREW, 1282 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" 1283 L"\x0038\x0039\x05e9\x05e0\x05d1\x05d2\x05e7\x05db" 1284 L"\x05e2\x05d9\x05df\x05d7\x05dc\x05da\x05e6\x05de" 1285 L"\x05dd\x05e4\x002f\x05e8\x05d3\x05d0\x05d5\x05d4" 1286 L"\x0027\x05e1\x05d8\x05d6\x05e3\x003d\x05ea\x002d" 1287 L"\x05e5\x002e\x003b\x005d\x005c\x005b\x002c\x0028" 1288 L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a" 1289 L"\x0029\x0041\x0042\x0043\x0044\x0045\x0046\x0047" 1290 L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f" 1291 L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057" 1292 L"\x0058\x0059\x005a\x003a\x002b\x003e\x005f\x003c" 1293 L"\x003f\x007e\x007d\x007c\x007b\x0022\x0030\x0031" 1294 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039" 1295 L"\x05e9\x05e0\x05d1\x05d2\x05e7\x05db\x05e2\x05d9" 1296 L"\x05df\x05d7\x05dc\x05da\x05e6\x05de\x05dd\x05e4" 1297 L"\x002f\x05e8\x05d3\x05d0\x05d5\x05d4\x0027\x05e1" 1298 L"\x05d8\x05d6\x05e3\x003d\x05ea\x002d\x05e5\x002e" 1299 L"\x003b\x005d\x005c\x005b\x002c" 1300 }, 1301#endif 1302#if defined(OS_WIN) 1303 // On Linux, the only way to test alternate keyboard layouts is to change 1304 // the keyboard layout of the whole screen. I'm worried about the side 1305 // effects this may have on the buildbots. 1306 {MockKeyboard::LAYOUT_CANADIAN_FRENCH, 1307 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" 1308 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066" 1309 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e" 1310 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076" 1311 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d" 1312 L"\x002e\x00e9\x003c\x0029\x0021\x0022\x002f\x0024" 1313 L"\x0025\x003f\x0026\x002a\x0028\x0041\x0042\x0043" 1314 L"\x0044\x0045\x0046\x0047\x0048\x0049\x004a\x004b" 1315 L"\x004c\x004d\x004e\x004f\x0050\x0051\x0052\x0053" 1316 L"\x0054\x0055\x0056\x0057\x0058\x0059\x005a\x003a" 1317 L"\x002b\x0027\x005f\x002e\x00c9\x003e\x0030\x0031" 1318 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039" 1319 L"\x0061\x0062\x0063\x0064\x0065\x0066\x0067\x0068" 1320 L"\x0069\x006a\x006b\x006c\x006d\x006e\x006f\x0070" 1321 L"\x0071\x0072\x0073\x0074\x0075\x0076\x0077\x0078" 1322 L"\x0079\x007a\x003b\x003d\x002c\x002d\x002e\x00e9" 1323 L"\x003c" 1324 }, 1325 {MockKeyboard::LAYOUT_FRENCH, 1326 L"\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d\x00e8" 1327 L"\x005f\x00e7\x0061\x0062\x0063\x0064\x0065\x0066" 1328 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e" 1329 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076" 1330 L"\x0077\x0078\x0079\x007a\x0024\x003d\x002c\x003b" 1331 L"\x003a\x00f9\x0029\x002a\x0021\x0030\x0031\x0032" 1332 L"\x0033\x0034\x0035\x0036\x0037\x0038\x0039\x0041" 1333 L"\x0042\x0043\x0044\x0045\x0046\x0047\x0048\x0049" 1334 L"\x004a\x004b\x004c\x004d\x004e\x004f\x0050\x0051" 1335 L"\x0052\x0053\x0054\x0055\x0056\x0057\x0058\x0059" 1336 L"\x005a\x00a3\x002b\x003f\x002e\x002f\x0025\x00b0" 1337 L"\x00b5\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d" 1338 L"\x00e8\x005f\x00e7\x0061\x0062\x0063\x0064\x0065" 1339 L"\x0066\x0067\x0068\x0069\x006a\x006b\x006c\x006d" 1340 L"\x006e\x006f\x0070\x0071\x0072\x0073\x0074\x0075" 1341 L"\x0076\x0077\x0078\x0079\x007a\x0024\x003d\x002c" 1342 L"\x003b\x003a\x00f9\x0029\x002a\x0021" 1343 }, 1344 {MockKeyboard::LAYOUT_RUSSIAN, 1345 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" 1346 L"\x0038\x0039\x0444\x0438\x0441\x0432\x0443\x0430" 1347 L"\x043f\x0440\x0448\x043e\x043b\x0434\x044c\x0442" 1348 L"\x0449\x0437\x0439\x043a\x044b\x0435\x0433\x043c" 1349 L"\x0446\x0447\x043d\x044f\x0436\x003d\x0431\x002d" 1350 L"\x044e\x002e\x0451\x0445\x005c\x044a\x044d\x0029" 1351 L"\x0021\x0022\x2116\x003b\x0025\x003a\x003f\x002a" 1352 L"\x0028\x0424\x0418\x0421\x0412\x0423\x0410\x041f" 1353 L"\x0420\x0428\x041e\x041b\x0414\x042c\x0422\x0429" 1354 L"\x0417\x0419\x041a\x042b\x0415\x0413\x041c\x0426" 1355 L"\x0427\x041d\x042f\x0416\x002b\x0411\x005f\x042e" 1356 L"\x002c\x0401\x0425\x002f\x042a\x042d\x0030\x0031" 1357 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039" 1358 L"\x0444\x0438\x0441\x0432\x0443\x0430\x043f\x0440" 1359 L"\x0448\x043e\x043b\x0434\x044c\x0442\x0449\x0437" 1360 L"\x0439\x043a\x044b\x0435\x0433\x043c\x0446\x0447" 1361 L"\x043d\x044f\x0436\x003d\x0431\x002d\x044e\x002e" 1362 L"\x0451\x0445\x005c\x044a\x044d" 1363 }, 1364#endif // defined(OS_WIN) 1365 {MockKeyboard::LAYOUT_UNITED_STATES, 1366 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" 1367 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066" 1368 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e" 1369 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076" 1370 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d" 1371 L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027\x0029" 1372 L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a" 1373 L"\x0028\x0041\x0042\x0043\x0044\x0045\x0046\x0047" 1374 L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f" 1375 L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057" 1376 L"\x0058\x0059\x005a\x003a\x002b\x003c\x005f\x003e" 1377 L"\x003f\x007e\x007b\x007c\x007d\x0022" 1378#if defined(OS_WIN) 1379 // This is ifdefed out for Linux to correspond to the fact that we don't 1380 // test alt+keystroke for now. 1381 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037" 1382 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066" 1383 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e" 1384 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076" 1385 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d" 1386 L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027" 1387#endif 1388 }, 1389 }; 1390 1391 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) { 1392 // Load an HTML page consisting of one <div> element. 1393 // This <div> element is used by the EditorClientImpl class to insert 1394 // characters received through the RenderWidget::OnHandleInputEvent() 1395 // function. 1396 view()->set_send_content_state_immediately(true); 1397 LoadHTML("<html>" 1398 "<head>" 1399 "<title></title>" 1400 "</head>" 1401 "<body>" 1402 "<div id='test' contenteditable='true'>" 1403 "</div>" 1404 "</body>" 1405 "</html>"); 1406 ExecuteJavaScript("document.getElementById('test').focus();"); 1407 render_thread_->sink().ClearMessages(); 1408 1409 // For each key code, we send three keyboard events. 1410 // * Pressing only the key; 1411 // * Pressing the key and a left-shift key, and; 1412 // * Pressing the key and a right-alt (AltGr) key. 1413 static const MockKeyboard::Modifiers kModifiers[] = { 1414 MockKeyboard::NONE, 1415 MockKeyboard::LEFT_SHIFT, 1416#if defined(OS_WIN) 1417 MockKeyboard::RIGHT_ALT, 1418#endif 1419 }; 1420 1421 MockKeyboard::Layout layout = kLayouts[i].layout; 1422 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifiers); ++j) { 1423 // Virtual key codes used for this test. 1424 static const int kKeyCodes[] = { 1425 '0', '1', '2', '3', '4', '5', '6', '7', 1426 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 1427 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 1428 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 1429 'W', 'X', 'Y', 'Z', 1430 ui::VKEY_OEM_1, 1431 ui::VKEY_OEM_PLUS, 1432 ui::VKEY_OEM_COMMA, 1433 ui::VKEY_OEM_MINUS, 1434 ui::VKEY_OEM_PERIOD, 1435 ui::VKEY_OEM_2, 1436 ui::VKEY_OEM_3, 1437 ui::VKEY_OEM_4, 1438 ui::VKEY_OEM_5, 1439 ui::VKEY_OEM_6, 1440 ui::VKEY_OEM_7, 1441#if defined(OS_WIN) 1442 // Unclear how to handle this on Linux. 1443 ui::VKEY_OEM_8, 1444#endif 1445 }; 1446 1447 MockKeyboard::Modifiers modifiers = kModifiers[j]; 1448 for (size_t k = 0; k < ARRAYSIZE_UNSAFE(kKeyCodes); ++k) { 1449 // Send a keyboard event to the RenderView object. 1450 // We should test a keyboard event only when the given keyboard-layout 1451 // driver is installed in a PC and the driver can assign a Unicode 1452 // charcter for the given tuple (layout, key-code, and modifiers). 1453 int key_code = kKeyCodes[k]; 1454 string16 char_code; 1455 if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0) 1456 continue; 1457 } 1458 } 1459 1460 // Retrieve the text in the test page and compare it with the expected 1461 // text created from a virtual-key code, a character code, and the 1462 // modifier-key status. 1463 const int kMaxOutputCharacters = 4096; 1464 std::wstring output = UTF16ToWideHack( 1465 GetMainFrame()->contentAsText(kMaxOutputCharacters)); 1466 EXPECT_EQ(kLayouts[i].expected_result, output); 1467 } 1468#else 1469 NOTIMPLEMENTED(); 1470#endif 1471} 1472 1473// Crashy, http://crbug.com/53247. 1474TEST_F(RenderViewImplTest, DISABLED_DidFailProvisionalLoadWithErrorForError) { 1475 GetMainFrame()->enableViewSourceMode(true); 1476 WebURLError error; 1477 error.domain = WebString::fromUTF8(net::kErrorDomain); 1478 error.reason = net::ERR_FILE_NOT_FOUND; 1479 error.unreachableURL = GURL("http://foo"); 1480 WebFrame* web_frame = GetMainFrame(); 1481 1482 // Start a load that will reach provisional state synchronously, 1483 // but won't complete synchronously. 1484 ViewMsg_Navigate_Params params; 1485 params.page_id = -1; 1486 params.navigation_type = ViewMsg_Navigate_Type::NORMAL; 1487 params.url = GURL("data:text/html,test data"); 1488 view()->OnNavigate(params); 1489 1490 // An error occurred. 1491 view()->didFailProvisionalLoad(web_frame, error); 1492 // Frame should exit view-source mode. 1493 EXPECT_FALSE(web_frame->isViewSourceModeEnabled()); 1494} 1495 1496TEST_F(RenderViewImplTest, DidFailProvisionalLoadWithErrorForCancellation) { 1497 GetMainFrame()->enableViewSourceMode(true); 1498 WebURLError error; 1499 error.domain = WebString::fromUTF8(net::kErrorDomain); 1500 error.reason = net::ERR_ABORTED; 1501 error.unreachableURL = GURL("http://foo"); 1502 WebFrame* web_frame = GetMainFrame(); 1503 1504 // Start a load that will reach provisional state synchronously, 1505 // but won't complete synchronously. 1506 ViewMsg_Navigate_Params params; 1507 params.page_id = -1; 1508 params.navigation_type = ViewMsg_Navigate_Type::NORMAL; 1509 params.url = GURL("data:text/html,test data"); 1510 view()->OnNavigate(params); 1511 1512 // A cancellation occurred. 1513 view()->didFailProvisionalLoad(web_frame, error); 1514 // Frame should stay in view-source mode. 1515 EXPECT_TRUE(web_frame->isViewSourceModeEnabled()); 1516} 1517 1518// Regression test for http://crbug.com/41562 1519TEST_F(RenderViewImplTest, UpdateTargetURLWithInvalidURL) { 1520 const GURL invalid_gurl("http://"); 1521 view()->setMouseOverURL(WebKit::WebURL(invalid_gurl)); 1522 EXPECT_EQ(invalid_gurl, view()->target_url_); 1523} 1524 1525TEST_F(RenderViewImplTest, SetHistoryLengthAndPrune) { 1526 int expected_page_id = -1; 1527 1528 // No history to merge and no committed pages. 1529 view()->OnSetHistoryLengthAndPrune(0, -1); 1530 EXPECT_EQ(0, view()->history_list_length_); 1531 EXPECT_EQ(-1, view()->history_list_offset_); 1532 1533 // History to merge and no committed pages. 1534 view()->OnSetHistoryLengthAndPrune(2, -1); 1535 EXPECT_EQ(2, view()->history_list_length_); 1536 EXPECT_EQ(1, view()->history_list_offset_); 1537 EXPECT_EQ(-1, view()->history_page_ids_[0]); 1538 EXPECT_EQ(-1, view()->history_page_ids_[1]); 1539 ClearHistory(); 1540 1541 // No history to merge and a committed page to be kept. 1542 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1543 expected_page_id = view()->page_id_; 1544 view()->OnSetHistoryLengthAndPrune(0, expected_page_id); 1545 EXPECT_EQ(1, view()->history_list_length_); 1546 EXPECT_EQ(0, view()->history_list_offset_); 1547 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]); 1548 ClearHistory(); 1549 1550 // No history to merge and a committed page to be pruned. 1551 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1552 expected_page_id = view()->page_id_; 1553 view()->OnSetHistoryLengthAndPrune(0, expected_page_id + 1); 1554 EXPECT_EQ(0, view()->history_list_length_); 1555 EXPECT_EQ(-1, view()->history_list_offset_); 1556 ClearHistory(); 1557 1558 // No history to merge and a committed page that the browser was unaware of. 1559 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1560 expected_page_id = view()->page_id_; 1561 view()->OnSetHistoryLengthAndPrune(0, -1); 1562 EXPECT_EQ(1, view()->history_list_length_); 1563 EXPECT_EQ(0, view()->history_list_offset_); 1564 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]); 1565 ClearHistory(); 1566 1567 // History to merge and a committed page to be kept. 1568 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1569 expected_page_id = view()->page_id_; 1570 view()->OnSetHistoryLengthAndPrune(2, expected_page_id); 1571 EXPECT_EQ(3, view()->history_list_length_); 1572 EXPECT_EQ(2, view()->history_list_offset_); 1573 EXPECT_EQ(-1, view()->history_page_ids_[0]); 1574 EXPECT_EQ(-1, view()->history_page_ids_[1]); 1575 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]); 1576 ClearHistory(); 1577 1578 // History to merge and a committed page to be pruned. 1579 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1580 expected_page_id = view()->page_id_; 1581 view()->OnSetHistoryLengthAndPrune(2, expected_page_id + 1); 1582 EXPECT_EQ(2, view()->history_list_length_); 1583 EXPECT_EQ(1, view()->history_list_offset_); 1584 EXPECT_EQ(-1, view()->history_page_ids_[0]); 1585 EXPECT_EQ(-1, view()->history_page_ids_[1]); 1586 ClearHistory(); 1587 1588 // History to merge and a committed page that the browser was unaware of. 1589 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1590 expected_page_id = view()->page_id_; 1591 view()->OnSetHistoryLengthAndPrune(2, -1); 1592 EXPECT_EQ(3, view()->history_list_length_); 1593 EXPECT_EQ(2, view()->history_list_offset_); 1594 EXPECT_EQ(-1, view()->history_page_ids_[0]); 1595 EXPECT_EQ(-1, view()->history_page_ids_[1]); 1596 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]); 1597 ClearHistory(); 1598 1599 int expected_page_id_2 = -1; 1600 1601 // No history to merge and two committed pages, both to be kept. 1602 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1603 expected_page_id = view()->page_id_; 1604 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1605 expected_page_id_2 = view()->page_id_; 1606 EXPECT_GT(expected_page_id_2, expected_page_id); 1607 view()->OnSetHistoryLengthAndPrune(0, expected_page_id); 1608 EXPECT_EQ(2, view()->history_list_length_); 1609 EXPECT_EQ(1, view()->history_list_offset_); 1610 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]); 1611 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]); 1612 ClearHistory(); 1613 1614 // No history to merge and two committed pages, and only the second is kept. 1615 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1616 expected_page_id = view()->page_id_; 1617 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1618 expected_page_id_2 = view()->page_id_; 1619 EXPECT_GT(expected_page_id_2, expected_page_id); 1620 view()->OnSetHistoryLengthAndPrune(0, expected_page_id_2); 1621 EXPECT_EQ(1, view()->history_list_length_); 1622 EXPECT_EQ(0, view()->history_list_offset_); 1623 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[0]); 1624 ClearHistory(); 1625 1626 // No history to merge and two committed pages, both of which the browser was 1627 // unaware of. 1628 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1629 expected_page_id = view()->page_id_; 1630 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1631 expected_page_id_2 = view()->page_id_; 1632 EXPECT_GT(expected_page_id_2, expected_page_id); 1633 view()->OnSetHistoryLengthAndPrune(0, -1); 1634 EXPECT_EQ(2, view()->history_list_length_); 1635 EXPECT_EQ(1, view()->history_list_offset_); 1636 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]); 1637 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]); 1638 ClearHistory(); 1639 1640 // History to merge and two committed pages, both to be kept. 1641 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1642 expected_page_id = view()->page_id_; 1643 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1644 expected_page_id_2 = view()->page_id_; 1645 EXPECT_GT(expected_page_id_2, expected_page_id); 1646 view()->OnSetHistoryLengthAndPrune(2, expected_page_id); 1647 EXPECT_EQ(4, view()->history_list_length_); 1648 EXPECT_EQ(3, view()->history_list_offset_); 1649 EXPECT_EQ(-1, view()->history_page_ids_[0]); 1650 EXPECT_EQ(-1, view()->history_page_ids_[1]); 1651 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]); 1652 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]); 1653 ClearHistory(); 1654 1655 // History to merge and two committed pages, and only the second is kept. 1656 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1657 expected_page_id = view()->page_id_; 1658 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1659 expected_page_id_2 = view()->page_id_; 1660 EXPECT_GT(expected_page_id_2, expected_page_id); 1661 view()->OnSetHistoryLengthAndPrune(2, expected_page_id_2); 1662 EXPECT_EQ(3, view()->history_list_length_); 1663 EXPECT_EQ(2, view()->history_list_offset_); 1664 EXPECT_EQ(-1, view()->history_page_ids_[0]); 1665 EXPECT_EQ(-1, view()->history_page_ids_[1]); 1666 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[2]); 1667 ClearHistory(); 1668 1669 // History to merge and two committed pages, both of which the browser was 1670 // unaware of. 1671 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1672 expected_page_id = view()->page_id_; 1673 view()->didCommitProvisionalLoad(GetMainFrame(), true); 1674 expected_page_id_2 = view()->page_id_; 1675 EXPECT_GT(expected_page_id_2, expected_page_id); 1676 view()->OnSetHistoryLengthAndPrune(2, -1); 1677 EXPECT_EQ(4, view()->history_list_length_); 1678 EXPECT_EQ(3, view()->history_list_offset_); 1679 EXPECT_EQ(-1, view()->history_page_ids_[0]); 1680 EXPECT_EQ(-1, view()->history_page_ids_[1]); 1681 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]); 1682 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]); 1683} 1684 1685TEST_F(RenderViewImplTest, ContextMenu) { 1686 LoadHTML("<div>Page A</div>"); 1687 1688 // Create a right click in the center of the iframe. (I'm hoping this will 1689 // make this a bit more robust in case of some other formatting or other bug.) 1690 WebMouseEvent mouse_event; 1691 mouse_event.type = WebInputEvent::MouseDown; 1692 mouse_event.button = WebMouseEvent::ButtonRight; 1693 mouse_event.x = 250; 1694 mouse_event.y = 250; 1695 mouse_event.globalX = 250; 1696 mouse_event.globalY = 250; 1697 1698 SendWebMouseEvent(mouse_event); 1699 1700 // Now simulate the corresponding up event which should display the menu 1701 mouse_event.type = WebInputEvent::MouseUp; 1702 SendWebMouseEvent(mouse_event); 1703 1704 EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching( 1705 ViewHostMsg_ContextMenu::ID)); 1706} 1707 1708TEST_F(RenderViewImplTest, TestBackForward) { 1709 LoadHTML("<div id=pagename>Page A</div>"); 1710 WebKit::WebHistoryItem page_a_item = GetMainFrame()->currentHistoryItem(); 1711 int was_page_a = -1; 1712 string16 check_page_a = 1713 ASCIIToUTF16( 1714 "Number(document.getElementById('pagename').innerHTML == 'Page A')"); 1715 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a)); 1716 EXPECT_EQ(1, was_page_a); 1717 1718 LoadHTML("<div id=pagename>Page B</div>"); 1719 int was_page_b = -1; 1720 string16 check_page_b = 1721 ASCIIToUTF16( 1722 "Number(document.getElementById('pagename').innerHTML == 'Page B')"); 1723 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b)); 1724 EXPECT_EQ(1, was_page_b); 1725 1726 LoadHTML("<div id=pagename>Page C</div>"); 1727 int was_page_c = -1; 1728 string16 check_page_c = 1729 ASCIIToUTF16( 1730 "Number(document.getElementById('pagename').innerHTML == 'Page C')"); 1731 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c)); 1732 EXPECT_EQ(1, was_page_b); 1733 1734 WebKit::WebHistoryItem forward_item = GetMainFrame()->currentHistoryItem(); 1735 GoBack(GetMainFrame()->previousHistoryItem()); 1736 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b)); 1737 EXPECT_EQ(1, was_page_b); 1738 1739 GoForward(forward_item); 1740 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c)); 1741 EXPECT_EQ(1, was_page_c); 1742 1743 GoBack(GetMainFrame()->previousHistoryItem()); 1744 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b)); 1745 EXPECT_EQ(1, was_page_b); 1746 1747 forward_item = GetMainFrame()->currentHistoryItem(); 1748 GoBack(page_a_item); 1749 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a)); 1750 EXPECT_EQ(1, was_page_a); 1751 1752 GoForward(forward_item); 1753 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b)); 1754 EXPECT_EQ(1, was_page_b); 1755} 1756 1757#if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA) 1758TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) { 1759 LoadHTML("<textarea id=\"test\"></textarea>"); 1760 ExecuteJavaScript("document.getElementById('test').focus();"); 1761 1762 const string16 empty_string = UTF8ToUTF16(""); 1763 const std::vector<WebKit::WebCompositionUnderline> empty_underline; 1764 std::vector<gfx::Rect> bounds; 1765 view()->OnSetFocus(true); 1766 view()->OnSetInputMethodActive(true); 1767 1768 // ASCII composition 1769 const string16 ascii_composition = UTF8ToUTF16("aiueo"); 1770 view()->OnImeSetComposition(ascii_composition, empty_underline, 0, 0); 1771 view()->GetCompositionCharacterBounds(&bounds); 1772 ASSERT_EQ(ascii_composition.size(), bounds.size()); 1773 for (size_t i = 0; i < bounds.size(); ++i) 1774 EXPECT_LT(0, bounds[i].width()); 1775 view()->OnImeConfirmComposition( 1776 empty_string, ui::Range::InvalidRange(), false); 1777 1778 // Non surrogate pair unicode character. 1779 const string16 unicode_composition = UTF8ToUTF16( 1780 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"); 1781 view()->OnImeSetComposition(unicode_composition, empty_underline, 0, 0); 1782 view()->GetCompositionCharacterBounds(&bounds); 1783 ASSERT_EQ(unicode_composition.size(), bounds.size()); 1784 for (size_t i = 0; i < bounds.size(); ++i) 1785 EXPECT_LT(0, bounds[i].width()); 1786 view()->OnImeConfirmComposition( 1787 empty_string, ui::Range::InvalidRange(), false); 1788 1789 // Surrogate pair character. 1790 const string16 surrogate_pair_char = UTF8ToUTF16("\xF0\xA0\xAE\x9F"); 1791 view()->OnImeSetComposition(surrogate_pair_char, 1792 empty_underline, 1793 0, 1794 0); 1795 view()->GetCompositionCharacterBounds(&bounds); 1796 ASSERT_EQ(surrogate_pair_char.size(), bounds.size()); 1797 EXPECT_LT(0, bounds[0].width()); 1798 EXPECT_EQ(0, bounds[1].width()); 1799 view()->OnImeConfirmComposition( 1800 empty_string, ui::Range::InvalidRange(), false); 1801 1802 // Mixed string. 1803 const string16 surrogate_pair_mixed_composition = 1804 surrogate_pair_char + UTF8ToUTF16("\xE3\x81\x82") + surrogate_pair_char + 1805 UTF8ToUTF16("b") + surrogate_pair_char; 1806 const size_t utf16_length = 8UL; 1807 const bool is_surrogate_pair_empty_rect[8] = { 1808 false, true, false, false, true, false, false, true }; 1809 view()->OnImeSetComposition(surrogate_pair_mixed_composition, 1810 empty_underline, 1811 0, 1812 0); 1813 view()->GetCompositionCharacterBounds(&bounds); 1814 ASSERT_EQ(utf16_length, bounds.size()); 1815 for (size_t i = 0; i < utf16_length; ++i) { 1816 if (is_surrogate_pair_empty_rect[i]) { 1817 EXPECT_EQ(0, bounds[i].width()); 1818 } else { 1819 EXPECT_LT(0, bounds[i].width()); 1820 } 1821 } 1822 view()->OnImeConfirmComposition( 1823 empty_string, ui::Range::InvalidRange(), false); 1824} 1825#endif 1826 1827TEST_F(RenderViewImplTest, ZoomLimit) { 1828 const double kMinZoomLevel = 1829 WebKit::WebView::zoomFactorToZoomLevel(kMinimumZoomFactor); 1830 const double kMaxZoomLevel = 1831 WebKit::WebView::zoomFactorToZoomLevel(kMaximumZoomFactor); 1832 1833 ViewMsg_Navigate_Params params; 1834 params.page_id = -1; 1835 params.navigation_type = ViewMsg_Navigate_Type::NORMAL; 1836 1837 // Verifies navigation to a URL with preset zoom level indeed sets the level. 1838 // Regression test for http://crbug.com/139559, where the level was not 1839 // properly set when it is out of the default zoom limits of WebView. 1840 params.url = GURL("data:text/html,min_zoomlimit_test"); 1841 view()->OnSetZoomLevelForLoadingURL(params.url, kMinZoomLevel); 1842 view()->OnNavigate(params); 1843 ProcessPendingMessages(); 1844 EXPECT_DOUBLE_EQ(kMinZoomLevel, view()->GetWebView()->zoomLevel()); 1845 1846 // It should work even when the zoom limit is temporarily changed in the page. 1847 view()->GetWebView()->zoomLimitsChanged( 1848 WebKit::WebView::zoomFactorToZoomLevel(1.0), 1849 WebKit::WebView::zoomFactorToZoomLevel(1.0)); 1850 params.url = GURL("data:text/html,max_zoomlimit_test"); 1851 view()->OnSetZoomLevelForLoadingURL(params.url, kMaxZoomLevel); 1852 view()->OnNavigate(params); 1853 ProcessPendingMessages(); 1854 EXPECT_DOUBLE_EQ(kMaxZoomLevel, view()->GetWebView()->zoomLevel()); 1855} 1856 1857TEST_F(RenderViewImplTest, SetEditableSelectionAndComposition) { 1858 // Load an HTML page consisting of an input field. 1859 LoadHTML("<html>" 1860 "<head>" 1861 "</head>" 1862 "<body>" 1863 "<input id=\"test1\" value=\"some test text hello\"></input>" 1864 "</body>" 1865 "</html>"); 1866 ExecuteJavaScript("document.getElementById('test1').focus();"); 1867 view()->OnSetEditableSelectionOffsets(4, 8); 1868 const std::vector<WebKit::WebCompositionUnderline> empty_underline; 1869 view()->OnSetCompositionFromExistingText(7, 10, empty_underline); 1870 WebKit::WebTextInputInfo info = view()->webview()->textInputInfo(); 1871 EXPECT_EQ(4, info.selectionStart); 1872 EXPECT_EQ(8, info.selectionEnd); 1873 EXPECT_EQ(7, info.compositionStart); 1874 EXPECT_EQ(10, info.compositionEnd); 1875 view()->OnUnselect(); 1876 info = view()->webview()->textInputInfo(); 1877 EXPECT_EQ(0, info.selectionStart); 1878 EXPECT_EQ(0, info.selectionEnd); 1879} 1880 1881 1882TEST_F(RenderViewImplTest, OnExtendSelectionAndDelete) { 1883 // Load an HTML page consisting of an input field. 1884 LoadHTML("<html>" 1885 "<head>" 1886 "</head>" 1887 "<body>" 1888 "<input id=\"test1\" value=\"abcdefghijklmnopqrstuvwxyz\"></input>" 1889 "</body>" 1890 "</html>"); 1891 ExecuteJavaScript("document.getElementById('test1').focus();"); 1892 view()->OnSetEditableSelectionOffsets(10, 10); 1893 view()->OnExtendSelectionAndDelete(3, 4); 1894 WebKit::WebTextInputInfo info = view()->webview()->textInputInfo(); 1895 EXPECT_EQ("abcdefgopqrstuvwxyz", info.value); 1896 EXPECT_EQ(7, info.selectionStart); 1897 EXPECT_EQ(7, info.selectionEnd); 1898 view()->OnSetEditableSelectionOffsets(4, 8); 1899 view()->OnExtendSelectionAndDelete(2, 5); 1900 info = view()->webview()->textInputInfo(); 1901 EXPECT_EQ("abuvwxyz", info.value); 1902 EXPECT_EQ(2, info.selectionStart); 1903 EXPECT_EQ(2, info.selectionEnd); 1904} 1905 1906// Test that the navigating specific frames works correctly. 1907TEST_F(RenderViewImplTest, NavigateFrame) { 1908 // Load page A. 1909 LoadHTML("hello <iframe srcdoc='fail' name='frame'></iframe>"); 1910 1911 // Navigate the frame only. 1912 ViewMsg_Navigate_Params nav_params; 1913 nav_params.url = GURL("data:text/html,world"); 1914 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL; 1915 nav_params.transition = PAGE_TRANSITION_TYPED; 1916 nav_params.current_history_list_length = 1; 1917 nav_params.current_history_list_offset = 0; 1918 nav_params.pending_history_list_offset = 1; 1919 nav_params.page_id = -1; 1920 nav_params.frame_to_navigate = "frame"; 1921 view()->OnNavigate(nav_params); 1922 ProcessPendingMessages(); 1923 1924 // Copy the document content to std::wstring and compare with the 1925 // expected result. 1926 const int kMaxOutputCharacters = 256; 1927 std::wstring output = UTF16ToWideHack( 1928 GetMainFrame()->contentAsText(kMaxOutputCharacters)); 1929 EXPECT_EQ(output, L"hello \n\nworld"); 1930} 1931 1932// This test ensures that a RenderFrame object is created for the top level 1933// frame in the RenderView. 1934TEST_F(RenderViewImplTest, BasicRenderFrame) { 1935 EXPECT_TRUE(view()->main_render_frame_.get()); 1936} 1937 1938TEST_F(RenderViewImplTest, TextInputTypeWithPepper) { 1939 MockRenderViewPepperHelper* pepper_helper = new MockRenderViewPepperHelper(); 1940 pepper_helper->SetTextInputType(ui::TEXT_INPUT_TYPE_EMAIL); 1941 view()->pepper_helper_ .reset(pepper_helper); 1942 1943 view()->OnSetInputMethodActive(true); 1944 view()->set_send_content_state_immediately(true); 1945 LoadHTML("<html>" 1946 "<head>" 1947 "</head>" 1948 "<body>" 1949 "<input id=\"test1\" type=\"text\" value=\"some text\"></input>" 1950 "</body>" 1951 "</html>"); 1952 render_thread_->sink().ClearMessages(); 1953 1954 // Move the input focus to the first <input> element, where we should 1955 // activate IMEs. 1956 ExecuteJavaScript("document.getElementById('test1').focus();"); 1957 ProcessPendingMessages(); 1958 render_thread_->sink().ClearMessages(); 1959 1960 // Update the IME status and verify if our IME backend sends an IPC message 1961 // using mock pepper status. 1962 view()->UpdateTextInputType(); 1963 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0); 1964 EXPECT_TRUE(msg != NULL); 1965 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type()); 1966 ui::TextInputType type; 1967 bool can_compose_inline; 1968 ui::TextInputMode input_mode; 1969 ViewHostMsg_TextInputTypeChanged::Read(msg, 1970 &type, 1971 &can_compose_inline, 1972 &input_mode); 1973 EXPECT_EQ(ui::TEXT_INPUT_TYPE_EMAIL, type); 1974} 1975 1976} // namespace content 1977