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