diff options
author | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-10 09:26:16 +0000 |
---|---|---|
committer | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-10 09:26:16 +0000 |
commit | 1eb175082bf16c5f7d92972f5d20277cb665d8e8 (patch) | |
tree | 3dedbb89b997b8a9fc37b001a911371468d848b8 | |
parent | 31584b27d7956971710d50ffba70f00f5ea9433e (diff) | |
download | chromium_src-1eb175082bf16c5f7d92972f5d20277cb665d8e8.zip chromium_src-1eb175082bf16c5f7d92972f5d20277cb665d8e8.tar.gz chromium_src-1eb175082bf16c5f7d92972f5d20277cb665d8e8.tar.bz2 |
Add basic tests for extension crash recovery.
Also update the test infrastructure to support the required features.
TEST=browser_tests
BUG=30405
Review URL: http://codereview.chromium.org/572038
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@38604 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/extensions/extension_browsertest.cc | 28 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_browsertest.h | 7 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_crash_recovery_browsertest.cc | 129 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 20 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.h | 6 | ||||
-rw-r--r-- | chrome/browser/task_manager_browsertest.cc | 6 | ||||
-rwxr-xr-x | chrome/chrome_tests.gypi | 1 |
7 files changed, 187 insertions, 10 deletions
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index 252e2e6..4e25a8a 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc @@ -246,6 +246,29 @@ bool ExtensionBrowserTest::WaitForExtensionInstallError() { return extension_installs_observed_ == before; } +void ExtensionBrowserTest::WaitForExtensionLoad() { + NotificationRegistrar registrar; + registrar.Add(this, NotificationType::EXTENSION_LOADED, + NotificationService::AllSources()); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, new MessageLoop::QuitTask, kTimeoutMs); + ui_test_utils::RunMessageLoop(); + WaitForExtensionHostsToLoad(); +} + +bool ExtensionBrowserTest::WaitForExtensionCrash( + const std::string& extension_id) { + ExtensionsService* service = browser()->profile()->GetExtensionsService(); + + if (!service->GetExtensionById(extension_id, true)) { + // The extension is already unloaded, presumably due to a crash. + return true; + } + ui_test_utils::RegisterAndWait(NotificationType::EXTENSION_PROCESS_TERMINATED, + this, kTimeoutMs); + return (service->GetExtensionById(extension_id, true) == NULL); +} + void ExtensionBrowserTest::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { @@ -287,6 +310,11 @@ void ExtensionBrowserTest::Observe(NotificationType type, MessageLoopForUI::current()->Quit(); break; + case NotificationType::EXTENSION_PROCESS_TERMINATED: + std::cout << "Got EXTENSION_PROCESS_TERMINATED notification.\n"; + MessageLoopForUI::current()->Quit(); + break; + default: NOTREACHED(); break; diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h index 65fb889..c0534d1 100644 --- a/chrome/browser/extensions/extension_browsertest.h +++ b/chrome/browser/extensions/extension_browsertest.h @@ -63,6 +63,13 @@ class ExtensionBrowserTest // error was raised. bool WaitForExtensionInstallError(); + // Waits until an extension is loaded. + void WaitForExtensionLoad(); + + // Wait for the specified extension to crash. Returns true if it really + // crashed. + bool WaitForExtensionCrash(const std::string& extension_id); + // NotificationObserver virtual void Observe(NotificationType type, const NotificationSource& source, diff --git a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc new file mode 100644 index 0000000..51f4347 --- /dev/null +++ b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc @@ -0,0 +1,129 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process_util.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/extensions/crashed_extension_infobar.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/tab_contents/infobar_delegate.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/test/ui_test_utils.h" + +class ExtensionCrashRecoveryTest : public ExtensionBrowserTest { + protected: + ExtensionsService* GetExtensionsService() { + return browser()->profile()->GetExtensionsService(); + } + + ExtensionProcessManager* GetExtensionProcessManager() { + return browser()->profile()->GetExtensionProcessManager(); + } + + CrashedExtensionInfoBarDelegate* GetCrashedExtensionInfoBarDelegate() { + TabContents* current_tab = browser()->GetSelectedTabContents(); + EXPECT_EQ(1, current_tab->infobar_delegate_count()); + InfoBarDelegate* delegate = current_tab->GetInfoBarDelegateAt(0); + return delegate->AsCrashedExtensionInfoBarDelegate(); + } + + void AcceptCrashedExtensionInfobar() { + CrashedExtensionInfoBarDelegate* infobar = + GetCrashedExtensionInfoBarDelegate(); + ASSERT_TRUE(infobar); + infobar->Accept(); + if (GetExtensionsService()->extensions()->empty()) + WaitForExtensionLoad(); + } + + void CancelCrashedExtensionInfobar() { + CrashedExtensionInfoBarDelegate* infobar = + GetCrashedExtensionInfoBarDelegate(); + ASSERT_TRUE(infobar); + infobar->Cancel(); + } + + void CrashExtension() { + Extension* extension = GetExtensionsService()->extensions()->at(0); + ASSERT_TRUE(extension); + ExtensionHost* extension_host = + GetExtensionProcessManager()->GetBackgroundHostForExtension(extension); + ASSERT_TRUE(extension_host); + + RenderProcessHost* extension_rph = + extension_host->render_view_host()->process(); + base::KillProcess(extension_rph->GetHandle(), + base::PROCESS_END_KILLED_BY_USER, false); + ASSERT_TRUE(WaitForExtensionCrash(extension_id_)); + ASSERT_FALSE( + GetExtensionProcessManager()->GetBackgroundHostForExtension(extension)); + ASSERT_TRUE(GetExtensionsService()->extensions()->empty()); + } + + void CheckExtensionConsistency() { + ASSERT_EQ(1U, GetExtensionsService()->extensions()->size()); + Extension* extension = GetExtensionsService()->extensions()->at(0); + ASSERT_TRUE(extension); + ExtensionHost* extension_host = + GetExtensionProcessManager()->GetBackgroundHostForExtension(extension); + ASSERT_TRUE(extension_host); + ASSERT_TRUE(GetExtensionProcessManager()->HasExtensionHost(extension_host)); + ASSERT_TRUE(extension_host->IsRenderViewLive()); + ASSERT_EQ(extension_host->render_view_host()->process(), + GetExtensionProcessManager()->GetExtensionProcess(extension->id())); + } + + void LoadTestExtension() { + ExtensionBrowserTest::SetUpInProcessBrowserTestFixture(); + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("common").AppendASCII("background_page"))); + Extension* extension = GetExtensionsService()->extensions()->at(0); + ASSERT_TRUE(extension); + extension_id_ = extension->id(); + CheckExtensionConsistency(); + } + + std::string extension_id_; +}; + +IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, Basic) { + LoadTestExtension(); + CrashExtension(); + AcceptCrashedExtensionInfobar(); + + SCOPED_TRACE("after clicking the infobar"); + CheckExtensionConsistency(); +} + +IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, CloseAndReload) { + LoadTestExtension(); + CrashExtension(); + CancelCrashedExtensionInfobar(); + ReloadExtension(extension_id_); + + SCOPED_TRACE("after reloading"); + CheckExtensionConsistency(); +} + +IN_PROC_BROWSER_TEST_F(ExtensionCrashRecoveryTest, ReloadIndependently) { + LoadTestExtension(); + CrashExtension(); + + ReloadExtension(extension_id_); + + SCOPED_TRACE("after reloading"); + CheckExtensionConsistency(); + + TabContents* current_tab = browser()->GetSelectedTabContents(); + ASSERT_TRUE(current_tab); + + // The infobar should automatically hide after the extension is successfully + // reloaded. + ASSERT_EQ(0, current_tab->infobar_delegate_count()); +} diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 48dfc2f..7dddfa9 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -224,16 +224,15 @@ void ExtensionsService::ReloadExtension(const std::string& extension_id) { path = current_extension->path(); UnloadExtension(extension_id); + } else { + path = unloaded_extension_paths_[extension_id]; } - if (path.empty()) { - // At this point we have to reconstruct the path from prefs, because - // we have no information about this extension in memory. - path = extension_prefs_->GetExtensionPath(extension_id); - } + // We should always be able to remember the extension's path. If it's not in + // the map, someone failed to update |unloaded_extension_paths_|. + CHECK(!path.empty()); - if (!path.empty()) - LoadExtension(path); + LoadExtension(path); } void ExtensionsService::UninstallExtension(const std::string& extension_id, @@ -551,6 +550,10 @@ void ExtensionsService::UnloadExtension(const std::string& extension_id) { // Callers should not send us nonexistant extensions. CHECK(extension.get()); + // Keep information about the extension so that we can reload it later + // even if it's not permanently installed. + unloaded_extension_paths_[extension->id()] = extension->path(); + ExtensionDOMUI::UnregisterChromeURLOverrides(profile_, extension->GetChromeURLOverrides()); @@ -616,6 +619,9 @@ void ExtensionsService::OnExtensionLoaded(Extension* extension, // Ensure extension is deleted unless we transfer ownership. scoped_ptr<Extension> scoped_extension(extension); + // The extension is now loaded, remove its data from unloaded extension map. + unloaded_extension_paths_.erase(extension->id()); + if (extensions_enabled() || extension->IsTheme() || extension->location() == Extension::LOAD || diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index f71f044..5b4d37e 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -314,6 +314,12 @@ class ExtensionsService // The model that tracks extensions with BrowserAction buttons. ExtensionToolbarModel toolbar_model_; + // Map unloaded extensions' ids to their paths. When a temporarily loaded + // extension is unloaded, we lose the infomation about it and don't have + // any in the extension preferences file. + typedef std::map<std::string, FilePath> UnloadedExtensionPathMap; + UnloadedExtensionPathMap unloaded_extension_paths_; + // Map of inspector cookies that are detached, waiting for an extension to be // reloaded. typedef std::map<std::string, int> OrphanedDevTools; diff --git a/chrome/browser/task_manager_browsertest.cc b/chrome/browser/task_manager_browsertest.cc index 1fe54ec..4d2eab8 100644 --- a/chrome/browser/task_manager_browsertest.cc +++ b/chrome/browser/task_manager_browsertest.cc @@ -167,15 +167,15 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, ReloadExtension) { // Reload the extension a few times and make sure our resource count // doesn't increase. ReloadExtension(extension->id()); - EXPECT_EQ(3, model()->ResourceCount()); + WaitForResourceChange(3); extension = model()->GetResourceExtension(2); ReloadExtension(extension->id()); - EXPECT_EQ(3, model()->ResourceCount()); + WaitForResourceChange(3); extension = model()->GetResourceExtension(2); ReloadExtension(extension->id()); - EXPECT_EQ(3, model()->ResourceCount()); + WaitForResourceChange(3); } IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, PopulateWebCacheFields) { diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 955cd12..cf2a16b 100755 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1116,6 +1116,7 @@ 'browser/extensions/extension_browsertest.cc', 'browser/extensions/extension_browsertest.h', 'browser/extensions/extension_browsertests_misc.cc', + 'browser/extensions/extension_crash_recovery_browsertest.cc', 'browser/extensions/extension_history_apitest.cc', 'browser/extensions/extension_i18n_apitest.cc', 'browser/extensions/extension_javascript_url_apitest.cc', |