Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / password_manager / password_manager_browsertest.cc
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
39 namespace {
40
41 // Observer that waits for navigation to complete and for the password infobar
42 // to be shown.
43 class 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
137 class 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 ---------------------------------------------------------------
182 IN_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
198 IN_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
214 IN_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
231 IN_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
247 IN_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
270 IN_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.
289 IN_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
307 IN_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
318 IN_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
341 IN_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
365 IN_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
388 IN_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
404 IN_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
420 IN_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
483 IN_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
502 IN_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
518 IN_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
534 IN_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
553 IN_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 }