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