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