NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / password_manager / password_manager_browsertest.cc
blob535c2ed2476c69f0102e41214db007b9a88ec60e
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.
5 #include <string>
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"
37 // NavigationObserver ---------------------------------------------------------
39 namespace {
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_));
61 virtual ~NavigationObserver() {}
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;
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();
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;
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();
107 bool infobar_shown() const { return infobar_shown_; }
108 bool infobar_removed() const { return infobar_removed_; }
110 void disable_should_automatically_accept_infobar() {
111 should_automatically_accept_infobar_ = false;
114 void Wait() {
115 message_loop_runner_->Run();
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_;
129 DISALLOW_COPY_AND_ASSIGN(NavigationObserver);
132 } // namespace
135 // PasswordManagerBrowserTest -------------------------------------------------
137 class PasswordManagerBrowserTest : public InProcessBrowserTest {
138 public:
139 PasswordManagerBrowserTest() {}
140 virtual ~PasswordManagerBrowserTest() {}
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);
153 protected:
154 content::WebContents* WebContents() {
155 return browser()->tab_strip_model()->GetActiveWebContents();
158 content::RenderViewHost* RenderViewHost() {
159 return WebContents()->GetRenderViewHost();
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());
171 NavigationObserver observer(WebContents());
172 GURL url = embedded_test_server()->GetURL(path);
173 ui_test_utils::NavigateToURL(browser(), url);
174 observer.Wait();
177 private:
178 DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest);
181 // Actual tests ---------------------------------------------------------------
182 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
183 PromptForNormalSubmit) {
184 NavigateToFile("/password/password_form.html");
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());
198 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
199 PromptForSubmitWithInPageNavigation) {
200 NavigateToFile("/password/password_navigate_before_submit.html");
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());
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");
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());
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");
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());
247 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, Redirects) {
248 NavigateToFile("/password/password_form.html");
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());
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());
270 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
271 PromptForSubmitUsingJavaScript) {
272 NavigateToFile("/password/password_form.html");
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());
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");
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());
307 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForNavigation) {
308 NavigateToFile("/password/password_form.html");
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());
318 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
319 NoPromptForSubFrameNavigation) {
320 NavigateToFile("/password/multi_frames.html");
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';";
335 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill));
336 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
337 observer.Wait();
338 EXPECT_FALSE(observer.infobar_shown());
341 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
342 PromptAfterSubmitWithSubFrameNavigation) {
343 NavigateToFile("/password/multi_frames.html");
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();";
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());
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");
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());
388 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
389 PromptForXHRWithoutOnSubmit) {
390 NavigateToFile("/password/password_xhr_submit.html");
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());
404 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
405 NoPromptIfLinkClicked) {
406 NavigateToFile("/password/password_form.html");
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());
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());
428 // Visit a signup form.
429 NavigateToFile("/password/signup_form.html");
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));
440 first_observer.Wait();
441 ASSERT_TRUE(first_observer.infobar_shown());
443 // Now navigate to a login form that has similar HTML markup.
444 NavigateToFile("/password/password_form.html");
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));
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);
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());
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 */));
483 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForSubmitFromIframe) {
484 NavigateToFile("/password/password_submit_from_iframe.html");
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()";
497 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
498 observer.Wait();
499 EXPECT_TRUE(observer.infobar_shown());
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");
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());
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");
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());
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");
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());
553 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DeleteFrameBeforeSubmit) {
554 NavigateToFile("/password/multi_frames.html");
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';";
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.