password_manager_browsertest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright 2013 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 <string> 6 7#include "base/command_line.h" 8#include "base/metrics/histogram_samples.h" 9#include "base/metrics/statistics_recorder.h" 10#include "chrome/browser/chrome_notification_types.h" 11#include "chrome/browser/infobars/confirm_infobar_delegate.h" 12#include "chrome/browser/infobars/infobar.h" 13#include "chrome/browser/infobars/infobar_service.h" 14#include "chrome/browser/password_manager/password_store_factory.h" 15#include "chrome/browser/password_manager/test_password_store_service.h" 16#include "chrome/browser/ui/browser.h" 17#include "chrome/browser/ui/tabs/tab_strip_model.h" 18#include "chrome/test/base/in_process_browser_test.h" 19#include "chrome/test/base/test_switches.h" 20#include "chrome/test/base/ui_test_utils.h" 21#include "components/autofill/core/browser/autofill_test_utils.h" 22#include "components/password_manager/core/browser/test_password_store.h" 23#include "content/public/browser/notification_observer.h" 24#include "content/public/browser/notification_registrar.h" 25#include "content/public/browser/notification_service.h" 26#include "content/public/browser/render_view_host.h" 27#include "content/public/browser/web_contents.h" 28#include "content/public/browser/web_contents_observer.h" 29#include "content/public/test/browser_test_utils.h" 30#include "content/public/test/test_utils.h" 31#include "net/test/embedded_test_server/embedded_test_server.h" 32#include "net/url_request/test_url_fetcher_factory.h" 33#include "testing/gmock/include/gmock/gmock.h" 34#include "ui/events/keycodes/keyboard_codes.h" 35 36 37// NavigationObserver --------------------------------------------------------- 38 39namespace { 40 41// Observer that waits for navigation to complete and for the password infobar 42// to be shown. 43class NavigationObserver : public content::NotificationObserver, 44 public content::WebContentsObserver { 45 public: 46 explicit NavigationObserver(content::WebContents* web_contents) 47 : content::WebContentsObserver(web_contents), 48 message_loop_runner_(new content::MessageLoopRunner), 49 infobar_shown_(false), 50 infobar_removed_(false), 51 should_automatically_accept_infobar_(true), 52 infobar_service_(InfoBarService::FromWebContents(web_contents)) { 53 registrar_.Add(this, 54 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, 55 content::Source<InfoBarService>(infobar_service_)); 56 registrar_.Add(this, 57 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, 58 content::Source<InfoBarService>(infobar_service_)); 59 } 60 61 virtual ~NavigationObserver() {} 62 63 // Normally Wait() will not return until a main frame navigation occurs. 64 // If a path is set, Wait() will return after this path has been seen, 65 // regardless of the frame that navigated. Useful for multi-frame pages. 66 void SetPathToWaitFor(const std::string& path) { 67 wait_for_path_ = path; 68 } 69 70 // content::NotificationObserver: 71 virtual void Observe(int type, 72 const content::NotificationSource& source, 73 const content::NotificationDetails& details) OVERRIDE { 74 switch (type) { 75 case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED: 76 if (should_automatically_accept_infobar_) { 77 infobar_service_->infobar_at(0) 78 ->delegate() 79 ->AsConfirmInfoBarDelegate() 80 ->Accept(); 81 } 82 infobar_shown_ = true; 83 return; 84 case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED: 85 infobar_removed_ = true; 86 return; 87 default: 88 NOTREACHED(); 89 return; 90 } 91 } 92 93 // content::WebContentsObserver: 94 virtual void DidFinishLoad( 95 int64 frame_id, 96 const GURL& validated_url, 97 bool is_main_frame, 98 content::RenderViewHost* render_view_host) OVERRIDE { 99 if (!wait_for_path_.empty()) { 100 if (validated_url.path() == wait_for_path_) 101 message_loop_runner_->Quit(); 102 } else if (is_main_frame) { 103 message_loop_runner_->Quit(); 104 } 105 } 106 107 bool infobar_shown() const { return infobar_shown_; } 108 bool infobar_removed() const { return infobar_removed_; } 109 110 void disable_should_automatically_accept_infobar() { 111 should_automatically_accept_infobar_ = false; 112 } 113 114 void Wait() { 115 message_loop_runner_->Run(); 116 } 117 118 private: 119 std::string wait_for_path_; 120 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 121 bool infobar_shown_; 122 bool infobar_removed_; 123 // If |should_automatically_accept_infobar_| is true, then whenever the test 124 // sees an infobar added, it will click its accepting button. Default = true. 125 bool should_automatically_accept_infobar_; 126 content::NotificationRegistrar registrar_; 127 InfoBarService* infobar_service_; 128 129 DISALLOW_COPY_AND_ASSIGN(NavigationObserver); 130}; 131 132} // namespace 133 134 135// PasswordManagerBrowserTest ------------------------------------------------- 136 137class PasswordManagerBrowserTest : public InProcessBrowserTest { 138 public: 139 PasswordManagerBrowserTest() {} 140 virtual ~PasswordManagerBrowserTest() {} 141 142 // InProcessBrowserTest: 143 virtual void SetUpOnMainThread() OVERRIDE { 144 // Use TestPasswordStore to remove a possible race. Normally the 145 // PasswordStore does its database manipulation on the DB thread, which 146 // creates a possible race during navigation. Specifically the 147 // PasswordManager will ignore any forms in a page if the load from the 148 // PasswordStore has not completed. 149 PasswordStoreFactory::GetInstance()->SetTestingFactory( 150 browser()->profile(), TestPasswordStoreService::Build); 151 } 152 153 protected: 154 content::WebContents* WebContents() { 155 return browser()->tab_strip_model()->GetActiveWebContents(); 156 } 157 158 content::RenderViewHost* RenderViewHost() { 159 return WebContents()->GetRenderViewHost(); 160 } 161 162 // Wrapper around ui_test_utils::NavigateToURL that waits until 163 // DidFinishLoad() fires. Normally this function returns after 164 // DidStopLoading(), which caused flakiness as the NavigationObserver 165 // would sometimes see the DidFinishLoad event from a previous navigation and 166 // return immediately. 167 void NavigateToFile(const std::string& path) { 168 if (!embedded_test_server()->Started()) 169 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 170 171 NavigationObserver observer(WebContents()); 172 GURL url = embedded_test_server()->GetURL(path); 173 ui_test_utils::NavigateToURL(browser(), url); 174 observer.Wait(); 175 } 176 177 private: 178 DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest); 179}; 180 181// Actual tests --------------------------------------------------------------- 182IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 183 PromptForNormalSubmit) { 184 NavigateToFile("/password/password_form.html"); 185 186 // Fill a form and submit through a <input type="submit"> button. Nothing 187 // special. 188 NavigationObserver observer(WebContents()); 189 std::string fill_and_submit = 190 "document.getElementById('username_field').value = 'temp';" 191 "document.getElementById('password_field').value = 'random';" 192 "document.getElementById('input_submit_button').click()"; 193 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 194 observer.Wait(); 195 EXPECT_TRUE(observer.infobar_shown()); 196} 197 198IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 199 PromptForSubmitWithInPageNavigation) { 200 NavigateToFile("/password/password_navigate_before_submit.html"); 201 202 // Fill a form and submit through a <input type="submit"> button. Nothing 203 // special. The form does an in-page navigation before submitting. 204 NavigationObserver observer(WebContents()); 205 std::string fill_and_submit = 206 "document.getElementById('username_field').value = 'temp';" 207 "document.getElementById('password_field').value = 'random';" 208 "document.getElementById('input_submit_button').click()"; 209 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 210 observer.Wait(); 211 EXPECT_TRUE(observer.infobar_shown()); 212} 213 214IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 215 LoginSuccessWithUnrelatedForm) { 216 // Log in, see a form on the landing page. That form is not related to the 217 // login form (=has a different action), so we should offer saving the 218 // password. 219 NavigateToFile("/password/password_form.html"); 220 221 NavigationObserver observer(WebContents()); 222 std::string fill_and_submit = 223 "document.getElementById('username_unrelated').value = 'temp';" 224 "document.getElementById('password_unrelated').value = 'random';" 225 "document.getElementById('submit_unrelated').click()"; 226 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 227 observer.Wait(); 228 EXPECT_TRUE(observer.infobar_shown()); 229} 230 231IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, LoginFailed) { 232 // Log in, see a form on the landing page. That form is not related to the 233 // login form (=has a different action), so we should offer saving the 234 // password. 235 NavigateToFile("/password/password_form.html"); 236 237 NavigationObserver observer(WebContents()); 238 std::string fill_and_submit = 239 "document.getElementById('username_failed').value = 'temp';" 240 "document.getElementById('password_failed').value = 'random';" 241 "document.getElementById('submit_failed').click()"; 242 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 243 observer.Wait(); 244 EXPECT_FALSE(observer.infobar_shown()); 245} 246 247IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, Redirects) { 248 NavigateToFile("/password/password_form.html"); 249 250 // Fill a form and submit through a <input type="submit"> button. The form 251 // points to a redirection page. 252 NavigationObserver observer(WebContents()); 253 std::string fill_and_submit = 254 "document.getElementById('username_redirect').value = 'temp';" 255 "document.getElementById('password_redirect').value = 'random';" 256 "document.getElementById('submit_redirect').click()"; 257 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 258 observer.disable_should_automatically_accept_infobar(); 259 observer.Wait(); 260 EXPECT_TRUE(observer.infobar_shown()); 261 262 // The redirection page now redirects via Javascript. We check that the 263 // infobar stays. 264 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), 265 "window.location.href = 'done.html';")); 266 observer.Wait(); 267 EXPECT_FALSE(observer.infobar_removed()); 268} 269 270IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 271 PromptForSubmitUsingJavaScript) { 272 NavigateToFile("/password/password_form.html"); 273 274 // Fill a form and submit using <button> that calls submit() on the form. 275 // This should work regardless of the type of element, as long as submit() is 276 // called. 277 NavigationObserver observer(WebContents()); 278 std::string fill_and_submit = 279 "document.getElementById('username_field').value = 'temp';" 280 "document.getElementById('password_field').value = 'random';" 281 "document.getElementById('submit_button').click()"; 282 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 283 observer.Wait(); 284 EXPECT_TRUE(observer.infobar_shown()); 285} 286 287// Flaky: crbug.com/301547, observed on win and mac. Probably happens on all 288// platforms. 289IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 290 DISABLED_PromptForDynamicForm) { 291 NavigateToFile("/password/dynamic_password_form.html"); 292 293 // Fill the dynamic password form and submit. 294 NavigationObserver observer(WebContents()); 295 std::string fill_and_submit = 296 "document.getElementById('create_form_button').click();" 297 "window.setTimeout(function() {" 298 " document.dynamic_form.username.value = 'tempro';" 299 " document.dynamic_form.password.value = 'random';" 300 " document.dynamic_form.submit();" 301 "}, 0)"; 302 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 303 observer.Wait(); 304 EXPECT_TRUE(observer.infobar_shown()); 305} 306 307IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForNavigation) { 308 NavigateToFile("/password/password_form.html"); 309 310 // Don't fill the password form, just navigate away. Shouldn't prompt. 311 NavigationObserver observer(WebContents()); 312 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), 313 "window.location.href = 'done.html';")); 314 observer.Wait(); 315 EXPECT_FALSE(observer.infobar_shown()); 316} 317 318IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 319 NoPromptForSubFrameNavigation) { 320 NavigateToFile("/password/multi_frames.html"); 321 322 // If you are filling out a password form in one frame and a different frame 323 // navigates, this should not trigger the infobar. 324 NavigationObserver observer(WebContents()); 325 observer.SetPathToWaitFor("/password/done.html"); 326 std::string fill = 327 "var first_frame = document.getElementById('first_frame');" 328 "var frame_doc = first_frame.contentDocument;" 329 "frame_doc.getElementById('username_field').value = 'temp';" 330 "frame_doc.getElementById('password_field').value = 'random';"; 331 std::string navigate_frame = 332 "var second_iframe = document.getElementById('second_frame');" 333 "second_iframe.contentWindow.location.href = 'done.html';"; 334 335 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill)); 336 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); 337 observer.Wait(); 338 EXPECT_FALSE(observer.infobar_shown()); 339} 340 341IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 342 PromptAfterSubmitWithSubFrameNavigation) { 343 NavigateToFile("/password/multi_frames.html"); 344 345 // Make sure that we prompt to save password even if a sub-frame navigation 346 // happens first. 347 NavigationObserver observer(WebContents()); 348 observer.SetPathToWaitFor("/password/done.html"); 349 std::string navigate_frame = 350 "var second_iframe = document.getElementById('second_frame');" 351 "second_iframe.contentWindow.location.href = 'other.html';"; 352 std::string fill_and_submit = 353 "var first_frame = document.getElementById('first_frame');" 354 "var frame_doc = first_frame.contentDocument;" 355 "frame_doc.getElementById('username_field').value = 'temp';" 356 "frame_doc.getElementById('password_field').value = 'random';" 357 "frame_doc.getElementById('input_submit_button').click();"; 358 359 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); 360 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 361 observer.Wait(); 362 EXPECT_TRUE(observer.infobar_shown()); 363} 364 365IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 366 PromptForXHRSubmit) { 367#if defined(OS_WIN) && defined(USE_ASH) 368 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 369 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 370 return; 371#endif 372 NavigateToFile("/password/password_xhr_submit.html"); 373 374 // Verify that we show the save password prompt if a form returns false 375 // in its onsubmit handler but instead logs in/navigates via XHR. 376 // Note that calling 'submit()' on a form with javascript doesn't call 377 // the onsubmit handler, so we click the submit button instead. 378 NavigationObserver observer(WebContents()); 379 std::string fill_and_submit = 380 "document.getElementById('username_field').value = 'temp';" 381 "document.getElementById('password_field').value = 'random';" 382 "document.getElementById('submit_button').click()"; 383 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 384 observer.Wait(); 385 EXPECT_TRUE(observer.infobar_shown()); 386} 387 388IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 389 PromptForXHRWithoutOnSubmit) { 390 NavigateToFile("/password/password_xhr_submit.html"); 391 392 // Verify that if XHR navigation occurs and the form is properly filled out, 393 // we try and save the password even though onsubmit hasn't been called. 394 NavigationObserver observer(WebContents()); 395 std::string fill_and_navigate = 396 "document.getElementById('username_field').value = 'temp';" 397 "document.getElementById('password_field').value = 'random';" 398 "send_xhr()"; 399 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate)); 400 observer.Wait(); 401 EXPECT_TRUE(observer.infobar_shown()); 402} 403 404IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 405 NoPromptIfLinkClicked) { 406 NavigateToFile("/password/password_form.html"); 407 408 // Verify that if the user takes a direct action to leave the page, we don't 409 // prompt to save the password even if the form is already filled out. 410 NavigationObserver observer(WebContents()); 411 std::string fill_and_click_link = 412 "document.getElementById('username_field').value = 'temp';" 413 "document.getElementById('password_field').value = 'random';" 414 "document.getElementById('link').click();"; 415 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_click_link)); 416 observer.Wait(); 417 EXPECT_FALSE(observer.infobar_shown()); 418} 419 420IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 421 VerifyPasswordGenerationUpload) { 422 // Prevent Autofill requests from actually going over the wire. 423 net::TestURLFetcherFactory factory; 424 // Disable Autofill requesting access to AddressBook data. This causes 425 // the test to hang on Mac. 426 autofill::test::DisableSystemServices(browser()->profile()); 427 428 // Visit a signup form. 429 NavigateToFile("/password/signup_form.html"); 430 431 // Enter a password and save it. 432 NavigationObserver first_observer(WebContents()); 433 std::string fill_and_submit = 434 "document.getElementById('other_info').value = 'stuff';" 435 "document.getElementById('username_field').value = 'my_username';" 436 "document.getElementById('password_field').value = 'password';" 437 "document.getElementById('input_submit_button').click()"; 438 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 439 440 first_observer.Wait(); 441 ASSERT_TRUE(first_observer.infobar_shown()); 442 443 // Now navigate to a login form that has similar HTML markup. 444 NavigateToFile("/password/password_form.html"); 445 446 // Simulate a user click to force an autofill of the form's DOM value, not 447 // just the suggested value. 448 std::string click = "document.getElementById('testform_no_name').click()"; 449 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), click)); 450 451 // The form should be filled with the previously submitted username. 452 std::string get_username = 453 "window.domAutomationController.send(" 454 "document.getElementById('username_field').value);"; 455 std::string actual_username; 456 ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(), 457 get_username, 458 &actual_username)); 459 ASSERT_EQ("my_username", actual_username); 460 461 // Submit the form and verify that there is no infobar (as the password 462 // has already been saved). 463 NavigationObserver second_observer(WebContents()); 464 std::string submit_form = 465 "document.getElementById('input_submit_button').click()"; 466 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form)); 467 second_observer.Wait(); 468 EXPECT_FALSE(second_observer.infobar_shown()); 469 470 // Verify that we sent a ping to Autofill saying that the original form 471 // was likely an account creation form since it has more than 2 text input 472 // fields and was used for the first time on a different form. 473 base::HistogramBase* upload_histogram = 474 base::StatisticsRecorder::FindHistogram( 475 "PasswordGeneration.UploadStarted"); 476 ASSERT_TRUE(upload_histogram); 477 scoped_ptr<base::HistogramSamples> snapshot = 478 upload_histogram->SnapshotSamples(); 479 EXPECT_EQ(0, snapshot->GetCount(0 /* failure */)); 480 EXPECT_EQ(1, snapshot->GetCount(1 /* success */)); 481} 482 483IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForSubmitFromIframe) { 484 NavigateToFile("/password/password_submit_from_iframe.html"); 485 486 // Submit a form in an iframe, then cause the whole page to navigate without a 487 // user gesture. We expect the save password prompt to be shown here, because 488 // some pages use such iframes for login forms. 489 NavigationObserver observer(WebContents()); 490 std::string fill_and_submit = 491 "var iframe = document.getElementById('test_iframe');" 492 "var iframe_doc = iframe.contentDocument;" 493 "iframe_doc.getElementById('username_field').value = 'temp';" 494 "iframe_doc.getElementById('password_field').value = 'random';" 495 "iframe_doc.getElementById('submit_button').click()"; 496 497 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 498 observer.Wait(); 499 EXPECT_TRUE(observer.infobar_shown()); 500} 501 502IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 503 PromptForInputElementWithoutName) { 504 // Check that the prompt is shown for forms where input elements lack the 505 // "name" attribute but the "id" is present. 506 NavigateToFile("/password/password_form.html"); 507 508 NavigationObserver observer(WebContents()); 509 std::string fill_and_submit = 510 "document.getElementById('username_field_no_name').value = 'temp';" 511 "document.getElementById('password_field_no_name').value = 'random';" 512 "document.getElementById('input_submit_button_no_name').click()"; 513 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 514 observer.Wait(); 515 EXPECT_TRUE(observer.infobar_shown()); 516} 517 518IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 519 PromptForInputElementWithoutId) { 520 // Check that the prompt is shown for forms where input elements lack the 521 // "id" attribute but the "name" attribute is present. 522 NavigateToFile("/password/password_form.html"); 523 524 NavigationObserver observer(WebContents()); 525 std::string fill_and_submit = 526 "document.getElementsByName('username_field_no_id')[0].value = 'temp';" 527 "document.getElementsByName('password_field_no_id')[0].value = 'random';" 528 "document.getElementsByName('input_submit_button_no_id')[0].click()"; 529 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 530 observer.Wait(); 531 EXPECT_TRUE(observer.infobar_shown()); 532} 533 534IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 535 NoPromptForInputElementWithoutIdAndName) { 536 // Check that no prompt is shown for forms where the input fields lack both 537 // the "id" and the "name" attributes. 538 NavigateToFile("/password/password_form.html"); 539 540 NavigationObserver observer(WebContents()); 541 std::string fill_and_submit = 542 "var form = document.getElementById('testform_elements_no_id_no_name');" 543 "var username = form.children[0];" 544 "username.value = 'temp';" 545 "var password = form.children[1];" 546 "password.value = 'random';" 547 "form.children[2].click()"; // form.children[2] is the submit button. 548 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 549 observer.Wait(); 550 EXPECT_FALSE(observer.infobar_shown()); 551} 552 553IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DeleteFrameBeforeSubmit) { 554 NavigateToFile("/password/multi_frames.html"); 555 556 NavigationObserver observer(WebContents()); 557 // Make sure we save some password info from an iframe and then destroy it. 558 std::string save_and_remove = 559 "var first_frame = document.getElementById('first_frame');" 560 "var frame_doc = first_frame.contentDocument;" 561 "frame_doc.getElementById('username_field').value = 'temp';" 562 "frame_doc.getElementById('password_field').value = 'random';" 563 "frame_doc.getElementById('input_submit_button').click();" 564 "first_frame.parentNode.removeChild(first_frame);"; 565 // Submit from the main frame, but without navigating through the onsubmit 566 // handler. 567 std::string navigate_frame = 568 "document.getElementById('username_field').value = 'temp';" 569 "document.getElementById('password_field').value = 'random';" 570 "document.getElementById('input_submit_button').click();" 571 "window.location.href = 'done.html';"; 572 573 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), save_and_remove)); 574 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); 575 observer.Wait(); 576 // The only thing we check here is that there is no use-after-free reported. 577} 578