summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-10 09:26:16 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-10 09:26:16 +0000
commit1eb175082bf16c5f7d92972f5d20277cb665d8e8 (patch)
tree3dedbb89b997b8a9fc37b001a911371468d848b8
parent31584b27d7956971710d50ffba70f00f5ea9433e (diff)
downloadchromium_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.cc28
-rw-r--r--chrome/browser/extensions/extension_browsertest.h7
-rw-r--r--chrome/browser/extensions/extension_crash_recovery_browsertest.cc129
-rw-r--r--chrome/browser/extensions/extensions_service.cc20
-rw-r--r--chrome/browser/extensions/extensions_service.h6
-rw-r--r--chrome/browser/task_manager_browsertest.cc6
-rwxr-xr-xchrome/chrome_tests.gypi1
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',