[Extensions] Make ExtensionService not be ref-counted
Use weak pointers instead, which makes it easier to reason about lifetimes.
Make CrxInstaller et al. use a weak pointer instead.
Add MakeCrxInstaller() function to ExtensionService.
Make WebUI handlers use a raw pointer (since they will always get destroyed
before the profile does).
Make tests use raw or scoped pointers, as appropriate.
Rename extensions_service to extension_service where encountered.
Add checks for BrowserThread::PostTask.
BUG=
TEST=
Review URL: http://codereview.chromium.org/6873065
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82257 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index f6b9583e..948d9b01 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -10,6 +10,7 @@
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/file_util.h"
+#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/stl_util-inl.h"
@@ -158,7 +159,9 @@
: public base::RefCountedThreadSafe<ExtensionServiceBackend> {
public:
// |install_directory| is a path where to look for extensions to load.
- explicit ExtensionServiceBackend(const FilePath& install_directory);
+ ExtensionServiceBackend(
+ base::WeakPtr<ExtensionService> frontend,
+ const FilePath& install_directory);
// Loads a single extension from |path| where |path| is the top directory of
// a specific extension where its manifest file lives.
@@ -166,8 +169,7 @@
// AddExtension() is called.
// TODO(erikkay): It might be useful to be able to load a packed extension
// (presumably into memory) without installing it.
- void LoadSingleExtension(const FilePath &path,
- scoped_refptr<ExtensionService> frontend);
+ void LoadSingleExtension(const FilePath &path);
private:
friend class base::RefCountedThreadSafe<ExtensionServiceBackend>;
@@ -190,38 +192,33 @@
void ReportExtensionLoadError(const FilePath& extension_path,
const std::string& error);
- // This is a naked pointer which is set by each entry point.
- // The entry point is responsible for ensuring lifetime.
- ExtensionService* frontend_;
+ // Notify the frontend that an extension was installed.
+ void OnExtensionInstalled(const scoped_refptr<const Extension>& extension);
+
+ base::WeakPtr<ExtensionService> frontend_;
// The top-level extensions directory being installed to.
FilePath install_directory_;
- // Whether errors result in noisy alerts.
- bool alert_on_error_;
-
DISALLOW_COPY_AND_ASSIGN(ExtensionServiceBackend);
};
ExtensionServiceBackend::ExtensionServiceBackend(
+ base::WeakPtr<ExtensionService> frontend,
const FilePath& install_directory)
- : frontend_(NULL),
- install_directory_(install_directory),
- alert_on_error_(false) {
+ : frontend_(frontend),
+ install_directory_(install_directory) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
ExtensionServiceBackend::~ExtensionServiceBackend() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+ BrowserThread::CurrentlyOn(BrowserThread::FILE));
}
-void ExtensionServiceBackend::LoadSingleExtension(
- const FilePath& path_in, scoped_refptr<ExtensionService> frontend) {
+void ExtensionServiceBackend::LoadSingleExtension(const FilePath& path_in) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- frontend_ = frontend;
-
- // Explicit UI loads are always noisy.
- alert_on_error_ = true;
-
FilePath extension_path = path_in;
file_util::AbsolutePath(&extension_path);
@@ -237,28 +234,41 @@
&error));
if (!extension) {
- ReportExtensionLoadError(extension_path, error);
+ if (!BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &ExtensionServiceBackend::ReportExtensionLoadError,
+ extension_path, error)))
+ NOTREACHED() << error;
return;
}
// Report this as an installed extension so that it gets remembered in the
// prefs.
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(frontend_,
- &ExtensionService::OnExtensionInstalled,
- extension));
+ if (!BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &ExtensionServiceBackend::OnExtensionInstalled,
+ extension)))
+ NOTREACHED();
}
void ExtensionServiceBackend::ReportExtensionLoadError(
const FilePath& extension_path, const std::string &error) {
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(
- frontend_,
- &ExtensionService::ReportExtensionLoadError, extension_path,
- error, NotificationType::EXTENSION_INSTALL_ERROR, alert_on_error_));
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (frontend_.get())
+ frontend_->ReportExtensionLoadError(
+ extension_path, error, NotificationType::EXTENSION_INSTALL_ERROR,
+ true /* alert_on_error */);
+}
+
+void ExtensionServiceBackend::OnExtensionInstalled(
+ const scoped_refptr<const Extension>& extension) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (frontend_.get())
+ frontend_->OnExtensionInstalled(extension);
}
void ExtensionService::CheckExternalUninstall(const std::string& id) {
@@ -415,14 +425,16 @@
ExtensionPrefs* extension_prefs,
bool autoupdate_enabled,
bool extensions_enabled)
- : profile_(profile),
+ : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ profile_(profile),
extension_prefs_(extension_prefs),
- ALLOW_THIS_IN_INITIALIZER_LIST(pending_extension_manager_(*this)),
+ pending_extension_manager_(*ALLOW_THIS_IN_INITIALIZER_LIST(this)),
install_directory_(install_directory),
extensions_enabled_(extensions_enabled),
show_extensions_prompts_(true),
ready_(false),
- ALLOW_THIS_IN_INITIALIZER_LIST(toolbar_model_(this)),
+ toolbar_model_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
apps_promo_(profile->GetPrefs()),
event_routers_initialized_(false) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -455,7 +467,9 @@
update_frequency));
}
- backend_ = new ExtensionServiceBackend(install_directory_);
+ backend_ =
+ new ExtensionServiceBackend(weak_ptr_factory_.GetWeakPtr(),
+ install_directory_);
if (extensions_enabled_) {
ExternalExtensionProviderImpl::CreateExternalProviders(
@@ -486,7 +500,7 @@
}
ExtensionService::~ExtensionService() {
- DCHECK(!profile_); // Profile should have told us it's going away.
+ CHECK(!profile_); // Profile should have told us it's going away.
UnloadAllExtensions();
ProviderCollection::const_iterator i;
@@ -566,10 +580,11 @@
<< " because it is not installed or pending";
// Delete extension_path since we're not creating a CrxInstaller
// that would do it for us.
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableFunction(
- extension_file_util::DeleteFile, extension_path, false));
+ if (!BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(
+ extension_file_util::DeleteFile, extension_path, false)))
+ NOTREACHED();
return;
}
@@ -579,9 +594,7 @@
(!is_pending_extension || pending_extension_info.install_silently()) ?
NULL : new ExtensionInstallUI(profile_);
- scoped_refptr<CrxInstaller> installer(
- new CrxInstaller(this, // frontend
- client));
+ scoped_refptr<CrxInstaller> installer(MakeCrxInstaller(client));
installer->set_expected_id(id);
if (is_pending_extension)
installer->set_install_source(pending_extension_info.install_source());
@@ -690,12 +703,13 @@
// Tell the backend to start deleting installed extensions on the file thread.
if (Extension::LOAD != location) {
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableFunction(
- &extension_file_util::UninstallExtension,
- install_directory_,
- extension_id_copy));
+ if (!BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(
+ &extension_file_util::UninstallExtension,
+ install_directory_,
+ extension_id_copy)))
+ NOTREACHED();
}
ClearExtensionData(extension_url);
@@ -810,12 +824,13 @@
}
void ExtensionService::LoadExtension(const FilePath& extension_path) {
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(
- backend_.get(),
- &ExtensionServiceBackend::LoadSingleExtension,
- extension_path, scoped_refptr<ExtensionService>(this)));
+ if (!BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ backend_.get(),
+ &ExtensionServiceBackend::LoadSingleExtension,
+ extension_path)))
+ NOTREACHED();
}
void ExtensionService::LoadComponentExtensions() {
@@ -1136,9 +1151,10 @@
bool plugins_changed = false;
for (size_t i = 0; i < extension->plugins().size(); ++i) {
const Extension::PluginInfo& plugin = extension->plugins()[i];
- BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
- NewRunnableFunction(&ForceShutdownPlugin,
- plugin.path));
+ if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ NewRunnableFunction(&ForceShutdownPlugin,
+ plugin.path)))
+ NOTREACHED();
webkit::npapi::PluginList::Singleton()->RefreshPlugins();
webkit::npapi::PluginList::Singleton()->RemoveExtraPluginPath(
plugin.path);
@@ -1201,6 +1217,8 @@
pref_change_registrar_.RemoveAll();
profile_ = NULL;
toolbar_model_.DestroyingProfile();
+ method_factory_.RevokeAll();
+ weak_ptr_factory_.InvalidateWeakPtrs();
}
ExtensionPrefs* ExtensionService::extension_prefs() {
@@ -1515,11 +1533,13 @@
for (size_t i = 0; i < info->size(); ++i)
extension_paths[info->at(i)->extension_id] = info->at(i)->extension_path;
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableFunction(
- &extension_file_util::GarbageCollectExtensions, install_directory_,
- extension_paths));
+ if (!BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(
+ &extension_file_util::GarbageCollectExtensions,
+ install_directory_,
+ extension_paths)))
+ NOTREACHED();
// Also garbage-collect themes. We check |profile_| to be
// defensive; in the future, we may call GarbageCollectExtensions()
@@ -1728,10 +1748,11 @@
// Delete the extension directory since we're not going to
// load it.
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableFunction(&extension_file_util::DeleteFile,
- extension->path(), true));
+ if (!BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(&extension_file_util::DeleteFile,
+ extension->path(), true)))
+ NOTREACHED();
return;
}
@@ -1909,9 +1930,8 @@
pending_extension_manager()->AddFromExternalFile(id, location);
- scoped_refptr<CrxInstaller> installer(
- new CrxInstaller(this, // frontend
- NULL)); // no client (silent install)
+ // no client (silent install)
+ scoped_refptr<CrxInstaller> installer(MakeCrxInstaller(NULL));
installer->set_install_source(location);
installer->set_expected_id(id);
installer->set_expected_version(*version),
@@ -1962,11 +1982,12 @@
// either fully working or not loaded at all, but never half-crashed.
// We do it in a PostTask so that other handlers of this notification will
// still have access to the Extension and ExtensionHost.
- MessageLoop::current()->PostTask(FROM_HERE,
- NewRunnableMethod(this,
- &ExtensionService::UnloadExtension,
- host->extension()->id(),
- UnloadedExtensionInfo::DISABLE));
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(
+ &ExtensionService::UnloadExtension,
+ host->extension()->id(),
+ UnloadedExtensionInfo::DISABLE));
break;
}
@@ -2001,6 +2022,11 @@
return result;
}
+scoped_refptr<CrxInstaller> ExtensionService::MakeCrxInstaller(
+ ExtensionInstallUI* client) {
+ return new CrxInstaller(weak_ptr_factory_.GetWeakPtr(), client);
+}
+
bool ExtensionService::IsBackgroundPageReady(const Extension* extension) {
return (extension->background_url().is_empty() ||
extension_runtime_data_[extension->id()].background_page_ready);