diff options
Diffstat (limited to 'chrome/browser/extensions')
5 files changed, 183 insertions, 7 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; |