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