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 "chrome/browser/browser_process.h"
6#include "chrome/browser/extensions/extension_browsertest.h"
7#include "chrome/browser/extensions/extension_host.h"
8#include "chrome/browser/extensions/extension_process_manager.h"
9#include "chrome/browser/extensions/extension_service.h"
10#include "chrome/browser/extensions/extension_system.h"
11#include "chrome/browser/notifications/balloon.h"
12#include "chrome/browser/notifications/balloon_collection.h"
13#include "chrome/browser/notifications/balloon_host.h"
14#include "chrome/browser/notifications/balloon_notification_ui_manager.h"
15#include "chrome/browser/notifications/notification.h"
16#include "chrome/browser/notifications/notification_delegate.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/ui/browser.h"
19#include "chrome/browser/ui/browser_commands.h"
20#include "chrome/browser/ui/tabs/tab_strip_model.h"
21#include "chrome/test/base/ui_test_utils.h"
22#include "content/public/browser/navigation_controller.h"
23#include "content/public/browser/render_process_host.h"
24#include "content/public/browser/render_view_host.h"
25#include "content/public/browser/web_contents.h"
26#include "content/public/common/result_codes.h"
27#include "ui/message_center/message_center.h"
28#include "ui/message_center/message_center_switches.h"
29#include "ui/message_center/message_center_util.h"
30#include "ui/message_center/notification_list.h"
31
32using content::NavigationController;
33using content::WebContents;
34using extensions::Extension;
35
36// Tests are timing out waiting for extension to crash.
37// http://crbug.com/174705
38#if defined(OS_MACOSX) || defined(USE_AURA) || defined(OS_LINUX)
39#define MAYBE_ExtensionCrashRecoveryTest DISABLED_ExtensionCrashRecoveryTest
40#else
41#define MAYBE_ExtensionCrashRecoveryTest ExtensionCrashRecoveryTest
42#endif  // defined(OS_MACOSX) || defined(USE_AURA) || defined(OS_LINUX)
43
44class ExtensionCrashRecoveryTestBase : public ExtensionBrowserTest {
45 protected:
46  virtual void AcceptNotification(size_t index) = 0;
47  virtual void CancelNotification(size_t index) = 0;
48  virtual size_t CountBalloons() = 0;
49
50  ExtensionService* GetExtensionService() {
51    return browser()->profile()->GetExtensionService();
52  }
53
54  ExtensionProcessManager* GetExtensionProcessManager() {
55    return extensions::ExtensionSystem::Get(browser()->profile())->
56        process_manager();
57  }
58
59  void CrashExtension(std::string extension_id) {
60    const Extension* extension =
61        GetExtensionService()->GetExtensionById(extension_id, false);
62    ASSERT_TRUE(extension);
63    extensions::ExtensionHost* extension_host = GetExtensionProcessManager()->
64        GetBackgroundHostForExtension(extension_id);
65    ASSERT_TRUE(extension_host);
66
67    base::KillProcess(extension_host->render_process_host()->GetHandle(),
68                      content::RESULT_CODE_KILLED, false);
69    ASSERT_TRUE(WaitForExtensionCrash(extension_id));
70    ASSERT_FALSE(GetExtensionProcessManager()->
71                 GetBackgroundHostForExtension(extension_id));
72
73    // Wait for extension crash balloon to appear.
74    base::MessageLoop::current()->RunUntilIdle();
75  }
76
77  void CheckExtensionConsistency(std::string extension_id) {
78    const Extension* extension =
79        GetExtensionService()->extensions()->GetByID(extension_id);
80    ASSERT_TRUE(extension);
81    extensions::ExtensionHost* extension_host = GetExtensionProcessManager()->
82        GetBackgroundHostForExtension(extension_id);
83    ASSERT_TRUE(extension_host);
84    ExtensionProcessManager::ViewSet all_views =
85        GetExtensionProcessManager()->GetAllViews();
86    ExtensionProcessManager::ViewSet::const_iterator it =
87        all_views.find(extension_host->host_contents()->GetRenderViewHost());
88    ASSERT_FALSE(it == all_views.end());
89    ASSERT_TRUE(extension_host->IsRenderViewLive());
90    extensions::ProcessMap* process_map =
91        browser()->profile()->GetExtensionService()->process_map();
92    ASSERT_TRUE(process_map->Contains(
93        extension_id,
94        extension_host->render_view_host()->GetProcess()->GetID()));
95  }
96
97  void LoadTestExtension() {
98    ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
99    const Extension* extension = LoadExtension(
100        test_data_dir_.AppendASCII("common").AppendASCII("background_page"));
101    ASSERT_TRUE(extension);
102    first_extension_id_ = extension->id();
103    CheckExtensionConsistency(first_extension_id_);
104  }
105
106  void LoadSecondExtension() {
107    const Extension* extension = LoadExtension(
108        test_data_dir_.AppendASCII("install").AppendASCII("install"));
109    ASSERT_TRUE(extension);
110    second_extension_id_ = extension->id();
111    CheckExtensionConsistency(second_extension_id_);
112  }
113
114  std::string first_extension_id_;
115  std::string second_extension_id_;
116};
117
118class MAYBE_ExtensionCrashRecoveryTest
119    : public ExtensionCrashRecoveryTestBase {
120 protected:
121  virtual void AcceptNotification(size_t index) OVERRIDE {
122    if (message_center::IsRichNotificationEnabled()) {
123      message_center::MessageCenter* message_center =
124          message_center::MessageCenter::Get();
125      ASSERT_GT(message_center->NotificationCount(), index);
126      message_center::NotificationList::Notifications::reverse_iterator it =
127          message_center->GetNotifications().rbegin();
128      for (size_t i=0; i < index; ++i)
129        it++;
130      std::string id = (*it)->id();
131      message_center->ClickOnNotification(id);
132    } else {
133      Balloon* balloon = GetNotificationDelegate(index);
134      ASSERT_TRUE(balloon);
135      balloon->OnClick();
136    }
137    WaitForExtensionLoad();
138  }
139
140  virtual void CancelNotification(size_t index) OVERRIDE {
141    if (message_center::IsRichNotificationEnabled()) {
142      message_center::MessageCenter* message_center =
143          message_center::MessageCenter::Get();
144      ASSERT_GT(message_center->NotificationCount(), index);
145      message_center::NotificationList::Notifications::reverse_iterator it =
146          message_center->GetNotifications().rbegin();
147      for (size_t i=0; i < index; i++) { it++; }
148      ASSERT_TRUE(g_browser_process->notification_ui_manager()->
149          CancelById((*it)->id()));
150    } else {
151      Balloon* balloon = GetNotificationDelegate(index);
152      ASSERT_TRUE(balloon);
153      std::string id = balloon->notification().notification_id();
154      ASSERT_TRUE(g_browser_process->notification_ui_manager()->CancelById(id));
155    }
156  }
157
158  virtual size_t CountBalloons() OVERRIDE {
159    if (message_center::IsRichNotificationEnabled())
160      return message_center::MessageCenter::Get()->NotificationCount();
161
162    return BalloonNotificationUIManager::GetInstanceForTesting()->
163        balloon_collection()->GetActiveBalloons().size();
164  }
165
166private:
167     Balloon* GetNotificationDelegate(size_t index) {
168       BalloonNotificationUIManager* manager =
169           BalloonNotificationUIManager::GetInstanceForTesting();
170       BalloonCollection::Balloons balloons =
171           manager->balloon_collection()->GetActiveBalloons();
172       return index < balloons.size() ? balloons.at(index) : NULL;
173     }
174};
175
176// Flaky: http://crbug.com/242167.
177IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest, DISABLED_Basic) {
178  const size_t size_before = GetExtensionService()->extensions()->size();
179  const size_t crash_size_before =
180      GetExtensionService()->terminated_extensions()->size();
181  LoadTestExtension();
182  CrashExtension(first_extension_id_);
183  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
184  ASSERT_EQ(crash_size_before + 1,
185            GetExtensionService()->terminated_extensions()->size());
186  ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
187
188  SCOPED_TRACE("after clicking the balloon");
189  CheckExtensionConsistency(first_extension_id_);
190  ASSERT_EQ(crash_size_before,
191            GetExtensionService()->terminated_extensions()->size());
192}
193
194// Flaky, http://crbug.com/241191.
195IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
196                       DISABLED_CloseAndReload) {
197  const size_t size_before = GetExtensionService()->extensions()->size();
198  const size_t crash_size_before =
199      GetExtensionService()->terminated_extensions()->size();
200  LoadTestExtension();
201  CrashExtension(first_extension_id_);
202
203  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
204  ASSERT_EQ(crash_size_before + 1,
205            GetExtensionService()->terminated_extensions()->size());
206
207  ASSERT_NO_FATAL_FAILURE(CancelNotification(0));
208  ReloadExtension(first_extension_id_);
209
210  SCOPED_TRACE("after reloading");
211  CheckExtensionConsistency(first_extension_id_);
212  ASSERT_EQ(crash_size_before,
213            GetExtensionService()->terminated_extensions()->size());
214}
215
216// Test is timing out on Windows http://crbug.com/174705.
217#if defined(OS_WIN)
218#define MAYBE_ReloadIndependently DISABLED_ReloadIndependently
219#else
220#define MAYBE_ReloadIndependently ReloadIndependently
221#endif  // defined(OS_WIN)
222IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
223                       MAYBE_ReloadIndependently) {
224  const size_t size_before = GetExtensionService()->extensions()->size();
225  LoadTestExtension();
226  CrashExtension(first_extension_id_);
227  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
228
229  ReloadExtension(first_extension_id_);
230
231  SCOPED_TRACE("after reloading");
232  CheckExtensionConsistency(first_extension_id_);
233
234  WebContents* current_tab =
235      browser()->tab_strip_model()->GetActiveWebContents();
236  ASSERT_TRUE(current_tab);
237
238  // The balloon should automatically hide after the extension is successfully
239  // reloaded.
240  ASSERT_EQ(0U, CountBalloons());
241}
242
243// Test is timing out on Windows http://crbug.com/174705.
244#if defined(OS_WIN)
245#define MAYBE_ReloadIndependentlyChangeTabs DISABLED_ReloadIndependentlyChangeTabs
246#else
247#define MAYBE_ReloadIndependentlyChangeTabs ReloadIndependentlyChangeTabs
248#endif  // defined(OS_WIN)
249
250IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
251                       MAYBE_ReloadIndependentlyChangeTabs) {
252  const size_t size_before = GetExtensionService()->extensions()->size();
253  LoadTestExtension();
254  CrashExtension(first_extension_id_);
255  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
256
257  WebContents* original_tab =
258      browser()->tab_strip_model()->GetActiveWebContents();
259  ASSERT_TRUE(original_tab);
260  ASSERT_EQ(1U, CountBalloons());
261
262  // Open a new tab, but the balloon will still be there.
263  chrome::NewTab(browser());
264  WebContents* new_current_tab =
265      browser()->tab_strip_model()->GetActiveWebContents();
266  ASSERT_TRUE(new_current_tab);
267  ASSERT_NE(new_current_tab, original_tab);
268  ASSERT_EQ(1U, CountBalloons());
269
270  ReloadExtension(first_extension_id_);
271
272  SCOPED_TRACE("after reloading");
273  CheckExtensionConsistency(first_extension_id_);
274
275  // The balloon should automatically hide after the extension is successfully
276  // reloaded.
277  ASSERT_EQ(0U, CountBalloons());
278}
279
280IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
281                       DISABLED_ReloadIndependentlyNavigatePage) {
282  const size_t size_before = GetExtensionService()->extensions()->size();
283  LoadTestExtension();
284  CrashExtension(first_extension_id_);
285  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
286
287  WebContents* current_tab =
288      browser()->tab_strip_model()->GetActiveWebContents();
289  ASSERT_TRUE(current_tab);
290  ASSERT_EQ(1U, CountBalloons());
291
292  // Navigate to another page.
293  ui_test_utils::NavigateToURL(
294      browser(), ui_test_utils::GetTestUrl(
295                     base::FilePath(base::FilePath::kCurrentDirectory),
296                     base::FilePath(FILE_PATH_LITERAL("title1.html"))));
297  ASSERT_EQ(1U, CountBalloons());
298
299  ReloadExtension(first_extension_id_);
300
301  SCOPED_TRACE("after reloading");
302  CheckExtensionConsistency(first_extension_id_);
303
304  // The balloon should automatically hide after the extension is successfully
305  // reloaded.
306  ASSERT_EQ(0U, CountBalloons());
307}
308
309// Make sure that when we don't do anything about the crashed extension
310// and close the browser, it doesn't crash. The browser is closed implicitly
311// at the end of each browser test.
312//
313// http://crbug.com/84719
314#if defined(OS_LINUX)
315#define MAYBE_ShutdownWhileCrashed DISABLED_ShutdownWhileCrashed
316#else
317#define MAYBE_ShutdownWhileCrashed ShutdownWhileCrashed
318#endif  // defined(OS_LINUX)
319
320IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
321                       MAYBE_ShutdownWhileCrashed) {
322  const size_t size_before = GetExtensionService()->extensions()->size();
323  LoadTestExtension();
324  CrashExtension(first_extension_id_);
325  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
326}
327
328// Flaky, http://crbug.com/241245.
329IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
330                       DISABLED_TwoExtensionsCrashFirst) {
331  const size_t size_before = GetExtensionService()->extensions()->size();
332  LoadTestExtension();
333  LoadSecondExtension();
334  CrashExtension(first_extension_id_);
335  ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
336  ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
337
338  SCOPED_TRACE("after clicking the balloon");
339  CheckExtensionConsistency(first_extension_id_);
340  CheckExtensionConsistency(second_extension_id_);
341}
342
343// Flaky: http://crbug.com/242196
344IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
345                       DISABLED_TwoExtensionsCrashSecond) {
346  const size_t size_before = GetExtensionService()->extensions()->size();
347  LoadTestExtension();
348  LoadSecondExtension();
349  CrashExtension(second_extension_id_);
350  ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
351  ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
352
353  SCOPED_TRACE("after clicking the balloon");
354  CheckExtensionConsistency(first_extension_id_);
355  CheckExtensionConsistency(second_extension_id_);
356}
357
358IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
359                       TwoExtensionsCrashBothAtOnce) {
360  const size_t size_before = GetExtensionService()->extensions()->size();
361  const size_t crash_size_before =
362      GetExtensionService()->terminated_extensions()->size();
363  LoadTestExtension();
364  LoadSecondExtension();
365  CrashExtension(first_extension_id_);
366  ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
367  ASSERT_EQ(crash_size_before + 1,
368            GetExtensionService()->terminated_extensions()->size());
369  CrashExtension(second_extension_id_);
370  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
371  ASSERT_EQ(crash_size_before + 2,
372            GetExtensionService()->terminated_extensions()->size());
373
374  {
375    SCOPED_TRACE("first balloon");
376    ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
377    CheckExtensionConsistency(first_extension_id_);
378  }
379
380  {
381    SCOPED_TRACE("second balloon");
382    ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
383    CheckExtensionConsistency(first_extension_id_);
384    CheckExtensionConsistency(second_extension_id_);
385  }
386}
387
388IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
389                       TwoExtensionsOneByOne) {
390  const size_t size_before = GetExtensionService()->extensions()->size();
391  LoadTestExtension();
392  CrashExtension(first_extension_id_);
393  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
394  LoadSecondExtension();
395  CrashExtension(second_extension_id_);
396  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
397
398  {
399    SCOPED_TRACE("first balloon");
400    ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
401    CheckExtensionConsistency(first_extension_id_);
402  }
403
404  {
405    SCOPED_TRACE("second balloon");
406    ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
407    CheckExtensionConsistency(first_extension_id_);
408    CheckExtensionConsistency(second_extension_id_);
409  }
410}
411
412// http://crbug.com/84719
413#if defined(OS_LINUX)
414#define MAYBE_TwoExtensionsShutdownWhileCrashed \
415    DISABLED_TwoExtensionsShutdownWhileCrashed
416#else
417#define MAYBE_TwoExtensionsShutdownWhileCrashed \
418    TwoExtensionsShutdownWhileCrashed
419#endif  // defined(OS_LINUX)
420
421// Make sure that when we don't do anything about the crashed extensions
422// and close the browser, it doesn't crash. The browser is closed implicitly
423// at the end of each browser test.
424IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
425                       MAYBE_TwoExtensionsShutdownWhileCrashed) {
426  const size_t size_before = GetExtensionService()->extensions()->size();
427  LoadTestExtension();
428  CrashExtension(first_extension_id_);
429  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
430  LoadSecondExtension();
431  CrashExtension(second_extension_id_);
432  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
433}
434
435// Flaky, http://crbug.com/241573.
436IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
437                       DISABLED_TwoExtensionsIgnoreFirst) {
438  const size_t size_before = GetExtensionService()->extensions()->size();
439  LoadTestExtension();
440  LoadSecondExtension();
441  CrashExtension(first_extension_id_);
442  ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
443  CrashExtension(second_extension_id_);
444  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
445
446  // Accept notification 1 before canceling notification 0.
447  // Otherwise, on Linux and Windows, there is a race here, in which
448  // canceled notifications do not immediately go away.
449  ASSERT_NO_FATAL_FAILURE(AcceptNotification(1));
450  ASSERT_NO_FATAL_FAILURE(CancelNotification(0));
451
452  SCOPED_TRACE("balloons done");
453  ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
454  CheckExtensionConsistency(second_extension_id_);
455}
456
457// Flaky, http://crbug.com/241164.
458IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
459                       DISABLED_TwoExtensionsReloadIndependently) {
460  const size_t size_before = GetExtensionService()->extensions()->size();
461  LoadTestExtension();
462  LoadSecondExtension();
463  CrashExtension(first_extension_id_);
464  ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
465  CrashExtension(second_extension_id_);
466  ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
467
468  {
469    SCOPED_TRACE("first: reload");
470    WebContents* current_tab =
471        browser()->tab_strip_model()->GetActiveWebContents();
472    ASSERT_TRUE(current_tab);
473    // At the beginning we should have one balloon displayed for each extension.
474    ASSERT_EQ(2U, CountBalloons());
475    ReloadExtension(first_extension_id_);
476    // One of the balloons should hide after the extension is reloaded.
477    ASSERT_EQ(1U, CountBalloons());
478    CheckExtensionConsistency(first_extension_id_);
479  }
480
481  {
482    SCOPED_TRACE("second: balloon");
483    ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
484    CheckExtensionConsistency(first_extension_id_);
485    CheckExtensionConsistency(second_extension_id_);
486  }
487}
488
489// http://crbug.com/243648
490#if defined(OS_WIN)
491#define MAYBE_CrashAndUninstall DISABLED_CrashAndUninstall
492#else
493#define MAYBE_CrashAndUninstall CrashAndUninstall
494#endif
495IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
496                       MAYBE_CrashAndUninstall) {
497  const size_t size_before = GetExtensionService()->extensions()->size();
498  const size_t crash_size_before =
499      GetExtensionService()->terminated_extensions()->size();
500  LoadTestExtension();
501  LoadSecondExtension();
502  CrashExtension(first_extension_id_);
503  ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
504  ASSERT_EQ(crash_size_before + 1,
505            GetExtensionService()->terminated_extensions()->size());
506
507  ASSERT_EQ(1U, CountBalloons());
508  UninstallExtension(first_extension_id_);
509  base::MessageLoop::current()->RunUntilIdle();
510
511  SCOPED_TRACE("after uninstalling");
512  ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
513  ASSERT_EQ(crash_size_before,
514            GetExtensionService()->terminated_extensions()->size());
515  ASSERT_EQ(0U, CountBalloons());
516}
517
518// http://crbug.com/84719
519#if defined(OS_LINUX)
520#define MAYBE_CrashAndUnloadAll DISABLED_CrashAndUnloadAll
521#else
522#define MAYBE_CrashAndUnloadAll CrashAndUnloadAll
523#endif  // defined(OS_LINUX)
524
525IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
526                       MAYBE_CrashAndUnloadAll) {
527  const size_t size_before = GetExtensionService()->extensions()->size();
528  const size_t crash_size_before =
529      GetExtensionService()->terminated_extensions()->size();
530  LoadTestExtension();
531  LoadSecondExtension();
532  CrashExtension(first_extension_id_);
533  ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
534  ASSERT_EQ(crash_size_before + 1,
535            GetExtensionService()->terminated_extensions()->size());
536
537  GetExtensionService()->UnloadAllExtensions();
538  ASSERT_EQ(crash_size_before,
539            GetExtensionService()->terminated_extensions()->size());
540}
541
542// Fails a DCHECK on Aura and Linux: http://crbug.com/169622
543// Failing on Windows: http://crbug.com/232340
544#if defined(USE_AURA) || defined(OS_WIN) || defined(OS_LINUX)
545#define MAYBE_ReloadTabsWithBackgroundPage DISABLED_ReloadTabsWithBackgroundPage
546#else
547#define MAYBE_ReloadTabsWithBackgroundPage ReloadTabsWithBackgroundPage
548#endif
549
550// Test that when an extension with a background page that has a tab open
551// crashes, the tab stays open, and reloading it reloads the extension.
552// Regression test for issue 71629.
553IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
554                       MAYBE_ReloadTabsWithBackgroundPage) {
555  TabStripModel* tab_strip = browser()->tab_strip_model();
556  const size_t size_before = GetExtensionService()->extensions()->size();
557  const size_t crash_size_before =
558      GetExtensionService()->terminated_extensions()->size();
559  LoadTestExtension();
560
561  // Open a tab extension.
562  chrome::NewTab(browser());
563  ui_test_utils::NavigateToURL(
564      browser(),
565      GURL("chrome-extension://" + first_extension_id_ + "/background.html"));
566
567  const int tabs_before = tab_strip->count();
568  CrashExtension(first_extension_id_);
569
570  // Tab should still be open, and extension should be crashed.
571  EXPECT_EQ(tabs_before, tab_strip->count());
572  EXPECT_EQ(size_before, GetExtensionService()->extensions()->size());
573  EXPECT_EQ(crash_size_before + 1,
574            GetExtensionService()->terminated_extensions()->size());
575
576  {
577    content::WindowedNotificationObserver observer(
578        content::NOTIFICATION_LOAD_STOP,
579        content::Source<NavigationController>(
580            &browser()->tab_strip_model()->GetActiveWebContents()->
581                GetController()));
582    chrome::Reload(browser(), CURRENT_TAB);
583    observer.Wait();
584  }
585  // Extension should now be loaded.
586  SCOPED_TRACE("after reloading the tab");
587  CheckExtensionConsistency(first_extension_id_);
588  ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
589  ASSERT_EQ(0U, CountBalloons());
590}
591