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