Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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/notifications/balloon_host.h"
     12 #include "chrome/browser/notifications/notification.h"
     13 #include "chrome/browser/notifications/notification_delegate.h"
     14 #include "chrome/browser/notifications/notification_ui_manager.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/tabs/tab_strip_model.h"
     17 #include "chrome/browser/ui/browser.h"
     18 #include "chrome/test/ui_test_utils.h"
     19 #include "content/browser/renderer_host/render_process_host.h"
     20 #include "content/browser/renderer_host/render_view_host.h"
     21 #include "content/browser/tab_contents/tab_contents.h"
     22 #include "content/common/result_codes.h"
     23 
     24 class ExtensionCrashRecoveryTest : public ExtensionBrowserTest {
     25  protected:
     26   ExtensionService* GetExtensionService() {
     27     return browser()->profile()->GetExtensionService();
     28   }
     29 
     30   ExtensionProcessManager* GetExtensionProcessManager() {
     31     return browser()->profile()->GetExtensionProcessManager();
     32   }
     33 
     34   Balloon* GetNotificationDelegate(size_t index) {
     35     NotificationUIManager* manager =
     36         g_browser_process->notification_ui_manager();
     37     BalloonCollection::Balloons balloons =
     38         manager->balloon_collection()->GetActiveBalloons();
     39     return balloons.at(index);
     40   }
     41 
     42   void AcceptNotification(size_t index) {
     43     Balloon* balloon = GetNotificationDelegate(index);
     44     ASSERT_TRUE(balloon);
     45     balloon->OnClick();
     46     WaitForExtensionLoad();
     47   }
     48 
     49   void CancelNotification(size_t index) {
     50     Balloon* balloon = GetNotificationDelegate(index);
     51     NotificationUIManager* manager =
     52         g_browser_process->notification_ui_manager();
     53     manager->CancelById(balloon->notification().notification_id());
     54   }
     55 
     56   size_t CountBalloons() {
     57     NotificationUIManager* manager =
     58         g_browser_process->notification_ui_manager();
     59     BalloonCollection::Balloons balloons =
     60         manager->balloon_collection()->GetActiveBalloons();
     61     return balloons.size();
     62   }
     63 
     64   void CrashExtension(size_t index) {
     65     ASSERT_LT(index, GetExtensionService()->extensions()->size());
     66     const Extension* extension =
     67         GetExtensionService()->extensions()->at(index);
     68     ASSERT_TRUE(extension);
     69     std::string extension_id(extension->id());
     70     ExtensionHost* extension_host =
     71         GetExtensionProcessManager()->GetBackgroundHostForExtension(extension);
     72     ASSERT_TRUE(extension_host);
     73 
     74     RenderProcessHost* extension_rph =
     75         extension_host->render_view_host()->process();
     76     base::KillProcess(extension_rph->GetHandle(), ResultCodes::KILLED, false);
     77     ASSERT_TRUE(WaitForExtensionCrash(extension_id));
     78     ASSERT_FALSE(
     79         GetExtensionProcessManager()->GetBackgroundHostForExtension(extension));
     80   }
     81 
     82   void CheckExtensionConsistency(size_t index) {
     83     ASSERT_LT(index, GetExtensionService()->extensions()->size());
     84     const Extension* extension =
     85         GetExtensionService()->extensions()->at(index);
     86     ASSERT_TRUE(extension);
     87     ExtensionHost* extension_host =
     88         GetExtensionProcessManager()->GetBackgroundHostForExtension(extension);
     89     ASSERT_TRUE(extension_host);
     90     ASSERT_TRUE(GetExtensionProcessManager()->HasExtensionHost(extension_host));
     91     ASSERT_TRUE(extension_host->IsRenderViewLive());
     92     ASSERT_EQ(extension_host->render_view_host()->process(),
     93         GetExtensionProcessManager()->GetExtensionProcess(extension->id()));
     94   }
     95 
     96   void LoadTestExtension() {
     97     ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
     98     const size_t size_before = GetExtensionService()->extensions()->size();
     99     ASSERT_TRUE(LoadExtension(
    100         test_data_dir_.AppendASCII("common").AppendASCII("background_page")));
    101     const Extension* extension = GetExtensionService()->extensions()->back();
    102     ASSERT_TRUE(extension);
    103     first_extension_id_ = extension->id();
    104     CheckExtensionConsistency(size_before);
    105   }
    106 
    107   void LoadSecondExtension() {
    108     int offset = GetExtensionService()->extensions()->size();
    109     ASSERT_TRUE(LoadExtension(
    110         test_data_dir_.AppendASCII("install").AppendASCII("install")));
    111     const Extension* extension =
    112         GetExtensionService()->extensions()->at(offset);
    113     ASSERT_TRUE(extension);
    114     second_extension_id_ = extension->id();
    115     CheckExtensionConsistency(offset);
    116   }
    117 
    118   std::string first_extension_id_;
    119   std::string second_extension_id_;
    120 };
    121 
    122 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, Basic) {
    123   const size_t size_before = GetExtensionService()->extensions()->size();
    124   const size_t crash_size_before =
    125       GetExtensionService()->terminated_extensions()->size();
    126   LoadTestExtension();
    127   CrashExtension(size_before);
    128   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    129   ASSERT_EQ(crash_size_before + 1,
    130             GetExtensionService()->terminated_extensions()->size());
    131   AcceptNotification(0);
    132 
    133   SCOPED_TRACE("after clicking the balloon");
    134   CheckExtensionConsistency(size_before);
    135   ASSERT_EQ(crash_size_before,
    136             GetExtensionService()->terminated_extensions()->size());
    137 }
    138 
    139 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, CloseAndReload) {
    140   const size_t size_before = GetExtensionService()->extensions()->size();
    141   const size_t crash_size_before =
    142       GetExtensionService()->terminated_extensions()->size();
    143   LoadTestExtension();
    144   CrashExtension(size_before);
    145 
    146   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    147   ASSERT_EQ(crash_size_before + 1,
    148             GetExtensionService()->terminated_extensions()->size());
    149 
    150   CancelNotification(0);
    151   ReloadExtension(first_extension_id_);
    152 
    153   SCOPED_TRACE("after reloading");
    154   CheckExtensionConsistency(size_before);
    155   ASSERT_EQ(crash_size_before,
    156             GetExtensionService()->terminated_extensions()->size());
    157 }
    158 
    159 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, ReloadIndependently) {
    160   const size_t size_before = GetExtensionService()->extensions()->size();
    161   LoadTestExtension();
    162   CrashExtension(size_before);
    163   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    164 
    165   ReloadExtension(first_extension_id_);
    166 
    167   SCOPED_TRACE("after reloading");
    168   CheckExtensionConsistency(size_before);
    169 
    170   TabContents* current_tab = browser()->GetSelectedTabContents();
    171   ASSERT_TRUE(current_tab);
    172 
    173   // The balloon should automatically hide after the extension is successfully
    174   // reloaded.
    175   ASSERT_EQ(0U, CountBalloons());
    176 }
    177 
    178 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
    179                        ReloadIndependentlyChangeTabs) {
    180   const size_t size_before = GetExtensionService()->extensions()->size();
    181   LoadTestExtension();
    182   CrashExtension(size_before);
    183   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    184 
    185   TabContents* original_tab = browser()->GetSelectedTabContents();
    186   ASSERT_TRUE(original_tab);
    187   ASSERT_EQ(1U, CountBalloons());
    188 
    189   // Open a new tab, but the balloon will still be there.
    190   browser()->NewTab();
    191   TabContents* new_current_tab = browser()->GetSelectedTabContents();
    192   ASSERT_TRUE(new_current_tab);
    193   ASSERT_NE(new_current_tab, original_tab);
    194   ASSERT_EQ(1U, CountBalloons());
    195 
    196   ReloadExtension(first_extension_id_);
    197 
    198   SCOPED_TRACE("after reloading");
    199   CheckExtensionConsistency(size_before);
    200 
    201   // The balloon should automatically hide after the extension is successfully
    202   // reloaded.
    203   ASSERT_EQ(0U, CountBalloons());
    204 }
    205 
    206 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
    207                        ReloadIndependentlyNavigatePage) {
    208   const size_t size_before = GetExtensionService()->extensions()->size();
    209   LoadTestExtension();
    210   CrashExtension(size_before);
    211   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    212 
    213   TabContents* current_tab = browser()->GetSelectedTabContents();
    214   ASSERT_TRUE(current_tab);
    215   ASSERT_EQ(1U, CountBalloons());
    216 
    217   // Navigate to another page.
    218   ui_test_utils::NavigateToURL(browser(),
    219       ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
    220                                 FilePath(FILE_PATH_LITERAL("title1.html"))));
    221   ASSERT_EQ(1U, CountBalloons());
    222 
    223   ReloadExtension(first_extension_id_);
    224 
    225   SCOPED_TRACE("after reloading");
    226   CheckExtensionConsistency(size_before);
    227 
    228   // The infobar should automatically hide after the extension is successfully
    229   // reloaded.
    230   ASSERT_EQ(0U, CountBalloons());
    231 }
    232 
    233 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
    234                        ReloadIndependentlyTwoInfoBars) {
    235   const size_t size_before = GetExtensionService()->extensions()->size();
    236   LoadTestExtension();
    237 
    238   // Open a new window so that there will be an info bar in each.
    239   Browser* browser2 = CreateBrowser(browser()->profile());
    240 
    241   CrashExtension(size_before);
    242   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    243 
    244   TabContents* current_tab = browser()->GetSelectedTabContents();
    245   ASSERT_TRUE(current_tab);
    246   ASSERT_EQ(1U, CountBalloons());
    247 
    248   TabContents* current_tab2 = browser2->GetSelectedTabContents();
    249   ASSERT_TRUE(current_tab2);
    250   ASSERT_EQ(1U, CountBalloons());
    251 
    252   ReloadExtension(first_extension_id_);
    253 
    254   SCOPED_TRACE("after reloading");
    255   CheckExtensionConsistency(size_before);
    256 
    257   // Both infobars should automatically hide after the extension is successfully
    258   // reloaded.
    259   ASSERT_EQ(0U, CountBalloons());
    260   ASSERT_EQ(0U, CountBalloons());
    261 }
    262 
    263 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
    264                        ReloadIndependentlyTwoInfoBarsSameBrowser) {
    265   const size_t size_before = GetExtensionService()->extensions()->size();
    266   LoadTestExtension();
    267 
    268   // Open a new window so that there will be an info bar in each.
    269   Browser* browser2 = CreateBrowser(browser()->profile());
    270 
    271   CrashExtension(size_before);
    272   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    273 
    274   TabContents* current_tab = browser()->GetSelectedTabContents();
    275   ASSERT_TRUE(current_tab);
    276   ASSERT_EQ(1U, CountBalloons());
    277 
    278   TabContents* current_tab2 = browser2->GetSelectedTabContents();
    279   ASSERT_TRUE(current_tab2);
    280   ASSERT_EQ(1U, CountBalloons());
    281 
    282   // Move second window into first browser so there will be multiple tabs
    283   // with the info bar for the same extension in one browser.
    284   TabContentsWrapper* contents =
    285       browser2->tabstrip_model()->DetachTabContentsAt(0);
    286   browser()->tabstrip_model()->AppendTabContents(contents, true);
    287   current_tab2 = browser()->GetSelectedTabContents();
    288   ASSERT_EQ(1U, CountBalloons());
    289   ASSERT_NE(current_tab2, current_tab);
    290 
    291   ReloadExtension(first_extension_id_);
    292 
    293   SCOPED_TRACE("after reloading");
    294   CheckExtensionConsistency(size_before);
    295 
    296   // Both infobars should automatically hide after the extension is successfully
    297   // reloaded.
    298   ASSERT_EQ(0U, CountBalloons());
    299   browser()->SelectPreviousTab();
    300   ASSERT_EQ(current_tab, browser()->GetSelectedTabContents());
    301   ASSERT_EQ(0U, CountBalloons());
    302 }
    303 
    304 // Make sure that when we don't do anything about the crashed extension
    305 // and close the browser, it doesn't crash. The browser is closed implicitly
    306 // at the end of each browser test.
    307 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, ShutdownWhileCrashed) {
    308   const size_t size_before = GetExtensionService()->extensions()->size();
    309   LoadTestExtension();
    310   CrashExtension(size_before);
    311   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    312 }
    313 
    314 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsCrashFirst) {
    315   const size_t size_before = GetExtensionService()->extensions()->size();
    316   LoadTestExtension();
    317   LoadSecondExtension();
    318   CrashExtension(size_before);
    319   ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
    320   AcceptNotification(0);
    321 
    322   SCOPED_TRACE("after clicking the balloon");
    323   CheckExtensionConsistency(size_before);
    324   CheckExtensionConsistency(size_before + 1);
    325 }
    326 
    327 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsCrashSecond) {
    328   const size_t size_before = GetExtensionService()->extensions()->size();
    329   LoadTestExtension();
    330   LoadSecondExtension();
    331   CrashExtension(size_before + 1);
    332   ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
    333   AcceptNotification(0);
    334 
    335   SCOPED_TRACE("after clicking the balloon");
    336   CheckExtensionConsistency(size_before);
    337   CheckExtensionConsistency(size_before + 1);
    338 }
    339 
    340 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
    341                        TwoExtensionsCrashBothAtOnce) {
    342   const size_t size_before = GetExtensionService()->extensions()->size();
    343   const size_t crash_size_before =
    344       GetExtensionService()->terminated_extensions()->size();
    345   LoadTestExtension();
    346   LoadSecondExtension();
    347   CrashExtension(size_before);
    348   ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
    349   ASSERT_EQ(crash_size_before + 1,
    350             GetExtensionService()->terminated_extensions()->size());
    351   CrashExtension(size_before);
    352   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    353   ASSERT_EQ(crash_size_before + 2,
    354             GetExtensionService()->terminated_extensions()->size());
    355 
    356   {
    357     SCOPED_TRACE("first balloon");
    358     AcceptNotification(0);
    359     CheckExtensionConsistency(size_before);
    360   }
    361 
    362   {
    363     SCOPED_TRACE("second balloon");
    364     AcceptNotification(0);
    365     CheckExtensionConsistency(size_before);
    366     CheckExtensionConsistency(size_before + 1);
    367   }
    368 }
    369 
    370 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsOneByOne) {
    371   const size_t size_before = GetExtensionService()->extensions()->size();
    372   LoadTestExtension();
    373   CrashExtension(size_before);
    374   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    375   LoadSecondExtension();
    376   CrashExtension(size_before);
    377   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    378 
    379   {
    380     SCOPED_TRACE("first balloon");
    381     AcceptNotification(0);
    382     CheckExtensionConsistency(size_before);
    383   }
    384 
    385   {
    386     SCOPED_TRACE("second balloon");
    387     AcceptNotification(0);
    388     CheckExtensionConsistency(size_before);
    389     CheckExtensionConsistency(size_before + 1);
    390   }
    391 }
    392 
    393 // Make sure that when we don't do anything about the crashed extensions
    394 // and close the browser, it doesn't crash. The browser is closed implicitly
    395 // at the end of each browser test.
    396 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
    397                        TwoExtensionsShutdownWhileCrashed) {
    398   const size_t size_before = GetExtensionService()->extensions()->size();
    399   LoadTestExtension();
    400   CrashExtension(size_before);
    401   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    402   LoadSecondExtension();
    403   CrashExtension(size_before);
    404   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    405 }
    406 
    407 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, TwoExtensionsIgnoreFirst) {
    408   const size_t size_before = GetExtensionService()->extensions()->size();
    409   LoadTestExtension();
    410   LoadSecondExtension();
    411   CrashExtension(size_before);
    412   ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
    413   CrashExtension(size_before);
    414   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    415 
    416   CancelNotification(0);
    417   // Cancelling the balloon at 0 will close the balloon, and the balloon in
    418   // index 1 will move into index 0.
    419   AcceptNotification(0);
    420 
    421   SCOPED_TRACE("balloons done");
    422   ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
    423   CheckExtensionConsistency(size_before);
    424 }
    425 
    426 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest,
    427                        TwoExtensionsReloadIndependently) {
    428   const size_t size_before = GetExtensionService()->extensions()->size();
    429   LoadTestExtension();
    430   LoadSecondExtension();
    431   CrashExtension(size_before);
    432   ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
    433   CrashExtension(size_before);
    434   ASSERT_EQ(size_before, GetExtensionService()->extensions()->size());
    435 
    436   {
    437     SCOPED_TRACE("first: reload");
    438     TabContents* current_tab = browser()->GetSelectedTabContents();
    439     ASSERT_TRUE(current_tab);
    440     // At the beginning we should have one infobar displayed for each extension.
    441     ASSERT_EQ(2U, CountBalloons());
    442     ReloadExtension(first_extension_id_);
    443     // One of the infobars should hide after the extension is reloaded.
    444     ASSERT_EQ(1U, CountBalloons());
    445     CheckExtensionConsistency(size_before);
    446   }
    447 
    448   {
    449     SCOPED_TRACE("second: balloon");
    450     AcceptNotification(0);
    451     CheckExtensionConsistency(size_before);
    452     CheckExtensionConsistency(size_before + 1);
    453   }
    454 }
    455 
    456 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, CrashAndUninstall) {
    457   const size_t size_before = GetExtensionService()->extensions()->size();
    458   const size_t crash_size_before =
    459       GetExtensionService()->terminated_extensions()->size();
    460   LoadTestExtension();
    461   LoadSecondExtension();
    462   CrashExtension(size_before);
    463   ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
    464   ASSERT_EQ(crash_size_before + 1,
    465             GetExtensionService()->terminated_extensions()->size());
    466 
    467   ASSERT_EQ(1U, CountBalloons());
    468   UninstallExtension(first_extension_id_);
    469   MessageLoop::current()->RunAllPending();
    470 
    471   SCOPED_TRACE("after uninstalling");
    472   ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
    473   ASSERT_EQ(crash_size_before,
    474             GetExtensionService()->terminated_extensions()->size());
    475   ASSERT_EQ(0U, CountBalloons());
    476 }
    477 
    478 IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, CrashAndUnloadAll) {
    479   const size_t size_before = GetExtensionService()->extensions()->size();
    480   const size_t crash_size_before =
    481       GetExtensionService()->terminated_extensions()->size();
    482   LoadTestExtension();
    483   LoadSecondExtension();
    484   CrashExtension(size_before);
    485   ASSERT_EQ(size_before + 1, GetExtensionService()->extensions()->size());
    486   ASSERT_EQ(crash_size_before + 1,
    487             GetExtensionService()->terminated_extensions()->size());
    488 
    489   GetExtensionService()->UnloadAllExtensions();
    490   ASSERT_EQ(crash_size_before,
    491             GetExtensionService()->terminated_extensions()->size());
    492 }
    493