Load extensions service entirly on UI thread.
BUG=37548
TEST=SxtensionsServiceTest.LoadAllExtensionsFromDirectorySuccess,ExtensionsStartupTest.Test,manual testing of example extension from crbug/47286
Review URL: http://codereview.chromium.org/4752005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@66267 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 17ea2e6..05c6768 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -15,6 +15,7 @@
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
+#include "base/thread_restrictions.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
@@ -55,6 +56,7 @@
#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/extensions/extension_file_util.h"
#include "chrome/common/extensions/extension_l10n_util.h"
+#include "chrome/common/extensions/extension_resource.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/json_value_serializer.h"
@@ -86,14 +88,27 @@
static const int kOmniboxIconPaddingRight = 0;
#endif
-bool ShouldReloadExtensionManifest(const ExtensionInfo& info) {
- // Always reload LOAD extension manifests, because they can change on disk
- // independent of the manifest in our prefs.
- if (info.extension_location == Extension::LOAD)
- return true;
+// The following enumeration is used in histograms matching
+// Extensions.ManifestReload* . Values may be added, as long
+// as existing values are not changed.
+enum ManifestReloadReason {
+ NOT_NEEDED = 0, // Reload not needed.
+ UNPACKED_DIR, // Unpacked directory
+ NEEDS_RELOCALIZATION, // The local has changed since we read this extension.
+ NUM_MANIFEST_RELOAD_REASONS
+};
- // Otherwise, reload the manifest it needs to be relocalized.
- return extension_l10n_util::ShouldRelocalizeManifest(info);
+ManifestReloadReason ShouldReloadExtensionManifest(const ExtensionInfo& info) {
+ // Always reload manifests of unpacked extensions, because they can change
+ // on disk independent of the manifest in our prefs.
+ if (info.extension_location == Extension::LOAD)
+ return UNPACKED_DIR;
+
+ // Reload the manifest if it needs to be relocalized.
+ if (extension_l10n_util::ShouldRelocalizeManifest(info))
+ return NEEDS_RELOCALIZATION;
+
+ return NOT_NEEDED;
}
void GetExplicitOriginsInExtent(const Extension* extension,
@@ -216,13 +231,6 @@
const GURL& update_url,
Extension::Location location);
- // Reloads the given extensions from their manifests on disk (instead of what
- // we have cached in the prefs).
- void ReloadExtensionManifests(
- ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
- base::TimeTicks start_time,
- scoped_refptr<ExtensionsService> frontend);
-
private:
friend class base::RefCountedThreadSafe<ExtensionsServiceBackend>;
@@ -445,41 +453,6 @@
external_extension_added_ |= true;
}
-void ExtensionsServiceBackend::ReloadExtensionManifests(
- ExtensionPrefs::ExtensionsInfo* extensions_to_reload,
- base::TimeTicks start_time,
- scoped_refptr<ExtensionsService> frontend) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-
- frontend_ = frontend;
-
- for (size_t i = 0; i < extensions_to_reload->size(); ++i) {
- ExtensionInfo* info = extensions_to_reload->at(i).get();
- if (!ShouldReloadExtensionManifest(*info))
- continue;
-
- // We need to reload original manifest in order to localize properly.
- std::string error;
- scoped_refptr<const Extension> extension(extension_file_util::LoadExtension(
- info->extension_path, info->extension_location, false, &error));
-
- if (extension.get())
- extensions_to_reload->at(i)->extension_manifest.reset(
- static_cast<DictionaryValue*>(
- extension->manifest_value()->DeepCopy()));
- }
-
- // Finish installing on UI thread.
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(
- frontend_,
- &ExtensionsService::ContinueLoadAllExtensions,
- extensions_to_reload,
- start_time,
- true));
-}
-
bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url,
const GURL& referrer_url) {
// Special-case the themes mini-gallery.
@@ -783,7 +756,7 @@
// type to be external. An external extension should not be
// rejected if it fails the safty checks for a syncable extension.
// TODO(skerner): Work out other potential overlapping conditions.
- // (crbug/61000)
+ // (crbug.com/61000)
PendingExtensionMap::iterator it = pending_extensions_.find(id);
if (it != pending_extensions_.end()) {
VLOG(1) << "Extension id " << id
@@ -989,47 +962,66 @@
}
void ExtensionsService::LoadAllExtensions() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
base::TimeTicks start_time = base::TimeTicks::Now();
// Load any component extensions.
LoadComponentExtensions();
// Load the previously installed extensions.
- scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(
+ scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
extension_prefs_->GetInstalledExtensionsInfo());
- // If any extensions need localization, we bounce them all to the file thread
- // for re-reading and localization.
- for (size_t i = 0; i < info->size(); ++i) {
- if (ShouldReloadExtensionManifest(*info->at(i))) {
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE, NewRunnableMethod(
- backend_.get(),
- &ExtensionsServiceBackend::ReloadExtensionManifests,
- info.release(), // Callee takes ownership of the memory.
- start_time,
- scoped_refptr<ExtensionsService>(this)));
- return;
+ std::vector<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS, 0);
+ bool should_write_prefs = false;
+
+ for (size_t i = 0; i < extensions_info->size(); ++i) {
+ ExtensionInfo* info = extensions_info->at(i).get();
+
+ ManifestReloadReason reload_reason = ShouldReloadExtensionManifest(*info);
+ ++reload_reason_counts[reload_reason];
+ UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestReloadEnumValue",
+ reload_reason, 100);
+
+ if (reload_reason != NOT_NEEDED) {
+ // Reloading and extension reads files from disk. We do this on the
+ // UI thread because reloads should be very rare, and the complexity
+ // added by delaying the time when the extensions service knows about
+ // all extensions is significant. See crbug.com/37548 for details.
+ // |allow_io| disables tests that file operations run on the file
+ // thread.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ std::string error;
+ scoped_refptr<const Extension> extension(
+ extension_file_util::LoadExtension(
+ info->extension_path, info->extension_location, false, &error));
+
+ if (extension.get()) {
+ extensions_info->at(i)->extension_manifest.reset(
+ static_cast<DictionaryValue*>(
+ extension->manifest_value()->DeepCopy()));
+ should_write_prefs = true;
+ }
}
}
- // Don't update prefs.
- // Callee takes ownership of the memory.
- ContinueLoadAllExtensions(info.release(), start_time, false);
-}
-
-void ExtensionsService::ContinueLoadAllExtensions(
- ExtensionPrefs::ExtensionsInfo* extensions_info,
- base::TimeTicks start_time,
- bool write_to_prefs) {
- scoped_ptr<ExtensionPrefs::ExtensionsInfo> info(extensions_info);
-
- for (size_t i = 0; i < info->size(); ++i) {
- LoadInstalledExtension(*info->at(i), write_to_prefs);
+ for (size_t i = 0; i < extensions_info->size(); ++i) {
+ LoadInstalledExtension(*extensions_info->at(i), should_write_prefs);
}
OnLoadedInstalledExtensions();
+ // The histograms Extensions.ManifestReload* allow us to validate
+ // the assumption that reloading manifest is a rare event.
+ UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNotNeeded",
+ reload_reason_counts[NOT_NEEDED]);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadUnpackedDir",
+ reload_reason_counts[UNPACKED_DIR]);
+ UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNeedsRelocalization",
+ reload_reason_counts[NEEDS_RELOCALIZATION]);
+
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll", extensions_.size());
UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled", disabled_extensions_.size());
@@ -1073,30 +1065,30 @@
UMA_HISTOGRAM_ENUMERATION("Extensions.LoadType", type, 100);
switch (type) {
case Extension::TYPE_THEME:
- theme_count++;
+ ++theme_count;
break;
case Extension::TYPE_USER_SCRIPT:
- user_script_count++;
+ ++user_script_count;
break;
case Extension::TYPE_HOSTED_APP:
- app_count++;
- hosted_app_count++;
+ ++app_count;
+ ++hosted_app_count;
break;
case Extension::TYPE_PACKAGED_APP:
- app_count++;
- packaged_app_count++;
+ ++app_count;
+ ++packaged_app_count;
break;
case Extension::TYPE_EXTENSION:
default:
- extension_count++;
+ ++extension_count;
break;
}
if (Extension::IsExternalLocation(location))
- external_count++;
+ ++external_count;
if ((*ex)->page_action() != NULL)
- page_action_count++;
+ ++page_action_count;
if ((*ex)->browser_action() != NULL)
- browser_action_count++;
+ ++browser_action_count;
}
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadApp", app_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadHostedApp", hosted_app_count);
@@ -1110,6 +1102,7 @@
browser_action_count);
}
+
void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
bool write_to_prefs) {
std::string error;
@@ -1887,7 +1880,7 @@
// This is only here to help track down crbug.com/49114.
ExtensionHost::HostPointerList::iterator iter =
ExtensionHost::recently_deleted()->begin();
- for (; iter != ExtensionHost::recently_deleted()->end(); iter++) {
+ for (; iter != ExtensionHost::recently_deleted()->end(); ++iter) {
if (*iter == host) {
CHECK(host->GetURL().spec().size() + 2 != 0);
break;