Index: chrome/browser/themes/theme_service.cc |
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc |
index e24346d5f292a15bce2f91abfbdb74d5abe8aec0..056b9dc194e010ed2f7f213b5f98d71151a53342 100644 |
--- a/chrome/browser/themes/theme_service.cc |
+++ b/chrome/browser/themes/theme_service.cc |
@@ -6,6 +6,7 @@ |
#include "base/bind.h" |
#include "base/memory/ref_counted_memory.h" |
+#include "base/message_loop/message_loop.h" |
#include "base/prefs/pref_service.h" |
#include "base/sequenced_task_runner.h" |
#include "base/strings/string_util.h" |
@@ -55,6 +56,12 @@ namespace { |
// unpacked on the filesystem.) |
const char* kDefaultThemeGalleryID = "hkacjpbfdknhflllbcmjibkdeoafencn"; |
+// Wait this many seconds after startup to garbage collect unused themes. |
+// Removing unused themes is done after a delay because there is no |
+// reason to do it at startup. |
+// ExtensionService::GarbageCollectExtensions() does something similar. |
+const int kRemoveUnusedThemesStartupDelay = 30; |
+ |
SkColor TintForUnderline(SkColor input) { |
return SkColorSetA(input, SkColorGetA(input) / 3); |
} |
@@ -79,6 +86,7 @@ ThemeService::ThemeService() |
: ready_(false), |
rb_(ResourceBundle::GetSharedInstance()), |
profile_(NULL), |
+ installed_pending_load_id_(kDefaultThemeID), |
number_of_infobars_(0), |
weak_ptr_factory_(this) { |
} |
@@ -96,11 +104,9 @@ void ThemeService::Init(Profile* profile) { |
LoadThemePrefs(); |
- if (!ready_) { |
- registrar_.Add(this, |
- chrome::NOTIFICATION_EXTENSIONS_READY, |
- content::Source<Profile>(profile_)); |
- } |
+ registrar_.Add(this, |
+ chrome::NOTIFICATION_EXTENSIONS_READY, |
+ content::Source<Profile>(profile_)); |
theme_syncable_service_.reset(new ThemeSyncableService(profile_, this)); |
} |
@@ -211,36 +217,83 @@ base::RefCountedMemory* ThemeService::GetRawData( |
void ThemeService::Observe(int type, |
const content::NotificationSource& source, |
const content::NotificationDetails& details) { |
- DCHECK(type == chrome::NOTIFICATION_EXTENSIONS_READY); |
- registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY, |
- content::Source<Profile>(profile_)); |
- |
- MigrateTheme(); |
- set_ready(); |
- |
- // Send notification in case anyone requested data and cached it when the |
- // theme service was not ready yet. |
- NotifyThemeChanged(); |
+ using content::Details; |
+ switch (type) { |
+ case chrome::NOTIFICATION_EXTENSIONS_READY: |
+ registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY, |
+ content::Source<Profile>(profile_)); |
+ OnExtensionServiceReady(); |
+ break; |
+ case chrome::NOTIFICATION_EXTENSION_INSTALLED: |
+ { |
+ // The theme may be initially disabled. Wait till it is loaded (if ever). |
+ Details<const extensions::InstalledExtensionInfo> installed_details( |
+ details); |
+ if (installed_details->extension->is_theme()) |
+ installed_pending_load_id_ = installed_details->extension->id(); |
+ break; |
+ } |
+ case chrome::NOTIFICATION_EXTENSION_LOADED: |
+ { |
+ const Extension* extension = Details<const Extension>(details).ptr(); |
+ if (extension->is_theme() && |
+ installed_pending_load_id_ != kDefaultThemeID && |
+ installed_pending_load_id_ == extension->id()) { |
+ SetTheme(extension); |
+ } |
+ installed_pending_load_id_ = kDefaultThemeID; |
+ break; |
+ } |
+ case chrome::NOTIFICATION_EXTENSION_ENABLED: |
+ { |
+ const Extension* extension = Details<const Extension>(details).ptr(); |
+ if (extension->is_theme()) |
+ SetTheme(extension); |
+ break; |
+ } |
+ case chrome::NOTIFICATION_EXTENSION_UNLOADED: |
+ { |
+ Details<const extensions::UnloadedExtensionInfo> unloaded_details( |
+ details); |
+ if (unloaded_details->reason != extension_misc::UNLOAD_REASON_UPDATE && |
+ unloaded_details->extension->is_theme() && |
+ unloaded_details->extension->id() == GetThemeID()) { |
+ UseDefaultTheme(); |
+ } |
+ break; |
+ } |
+ } |
} |
void ThemeService::SetTheme(const Extension* extension) { |
- // Clear our image cache. |
- FreePlatformCaches(); |
- |
- DCHECK(extension); |
DCHECK(extension->is_theme()); |
- if (DCHECK_IS_ON()) { |
- ExtensionService* service = |
- extensions::ExtensionSystem::Get(profile_)->extension_service(); |
- DCHECK(service); |
- DCHECK(service->GetExtensionById(extension->id(), false)); |
+ ExtensionService* service = |
+ extensions::ExtensionSystem::Get(profile_)->extension_service(); |
+ if (!service->IsExtensionEnabled(extension->id())) { |
+ // |extension| is disabled when reverting to the previous theme via an |
+ // infobar. |
+ service->EnableExtension(extension->id()); |
+ // Enabling the extension will call back to SetTheme(). |
+ return; |
} |
+ std::string previous_theme_id = GetThemeID(); |
+ |
+ // Clear our image cache. |
+ FreePlatformCaches(); |
+ |
BuildFromExtension(extension); |
SaveThemeID(extension->id()); |
NotifyThemeChanged(); |
content::RecordAction(UserMetricsAction("Themes_Installed")); |
+ |
+ if (previous_theme_id != kDefaultThemeID && |
+ previous_theme_id != extension->id()) { |
+ // Disable the old theme. |
+ service->DisableExtension(previous_theme_id, |
+ extensions::Extension::DISABLE_USER_ACTION); |
+ } |
} |
void ThemeService::SetCustomDefaultTheme( |
@@ -254,25 +307,41 @@ bool ThemeService::ShouldInitWithNativeTheme() const { |
return false; |
} |
-void ThemeService::RemoveUnusedThemes() { |
+void ThemeService::RemoveUnusedThemes(bool ignore_infobars) { |
// We do not want to garbage collect themes on startup (|ready_| is false). |
- // Themes will get garbage collected once |
- // ExtensionService::GarbageCollectExtensions() runs. |
+ // Themes will get garbage collected after |kRemoveUnusedThemesStartupDelay|. |
if (!profile_ || !ready_) |
return; |
+ if (!ignore_infobars && number_of_infobars_ != 0) |
+ return; |
ExtensionService* service = profile_->GetExtensionService(); |
if (!service) |
return; |
std::string current_theme = GetThemeID(); |
std::vector<std::string> remove_list; |
- const ExtensionSet* extensions = service->extensions(); |
+ scoped_ptr<const ExtensionSet> extensions( |
+ service->GenerateInstalledExtensionsSet()); |
+ extensions::ExtensionPrefs* prefs = service->extension_prefs(); |
for (ExtensionSet::const_iterator it = extensions->begin(); |
it != extensions->end(); ++it) { |
- if ((*it)->is_theme() && (*it)->id() != current_theme) { |
- remove_list.push_back((*it)->id()); |
+ const extensions::Extension* extension = *it; |
+ if (extension->is_theme() && |
+ extension->id() != current_theme) { |
+ // Only uninstall themes which are not disabled or are disabled with |
+ // reason DISABLE_USER_ACTION. We cannot blanket uninstall all disabled |
+ // themes because externally installed themes are initially disabled. |
+ int disable_reason = prefs->GetDisableReasons(extension->id()); |
+ if (!prefs->IsExtensionDisabled(extension->id()) || |
+ disable_reason == Extension::DISABLE_USER_ACTION) { |
+ remove_list.push_back((*it)->id()); |
+ } |
} |
} |
+ // TODO: Garbage collect all unused themes. This method misses themes which |
+ // are installed but not loaded because they are blacklisted by a management |
+ // policy provider. |
+ |
for (size_t i = 0; i < remove_list.size(); ++i) |
service->UninstallExtension(remove_list[i], false, NULL); |
} |
@@ -328,7 +397,14 @@ void ThemeService::ClearAllThemeData() { |
profile_->GetPrefs()->ClearPref(prefs::kCurrentThemePackFilename); |
SaveThemeID(kDefaultThemeID); |
- RemoveUnusedThemes(); |
+ // There should be no more infobars. This may not be the case because of |
+ // http://crbug.com/62154 |
+ // RemoveUnusedThemes is called on a task because ClearAllThemeData() may |
+ // be called as a result of NOTIFICATION_EXTENSION_UNLOADED. |
+ base::MessageLoop::current()->PostTask(FROM_HERE, |
+ base::Bind(&ThemeService::RemoveUnusedThemes, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ true)); |
} |
void ThemeService::LoadThemePrefs() { |
@@ -359,16 +435,9 @@ void ThemeService::LoadThemePrefs() { |
if (loaded_pack) { |
content::RecordAction(UserMetricsAction("Themes.Loaded")); |
set_ready(); |
- } else { |
- // TODO(erg): We need to pop up a dialog informing the user that their |
- // theme is being migrated. |
- ExtensionService* service = |
- extensions::ExtensionSystem::Get(profile_)->extension_service(); |
- if (service && service->is_ready()) { |
- MigrateTheme(); |
- set_ready(); |
- } |
} |
+ // Else: wait for the extension service to be ready so that the theme pack |
+ // can be recreated from the extension. |
} |
void ThemeService::NotifyThemeChanged() { |
@@ -398,16 +467,41 @@ void ThemeService::FreePlatformCaches() { |
} |
#endif |
-void ThemeService::SwapThemeSupplier( |
- scoped_refptr<CustomThemeSupplier> theme_supplier) { |
- if (theme_supplier_.get()) |
- theme_supplier_->StopUsingTheme(); |
- theme_supplier_ = theme_supplier; |
- if (theme_supplier_.get()) |
- theme_supplier_->StartUsingTheme(); |
+void ThemeService::OnExtensionServiceReady() { |
+ if (!ready_) { |
+ // If the ThemeService is not ready yet, the custom theme data pack needs to |
+ // be recreated from the extension. |
+ MigrateTheme(); |
+ set_ready(); |
+ |
+ // Send notification in case anyone requested data and cached it when the |
+ // theme service was not ready yet. |
+ NotifyThemeChanged(); |
+ } |
+ |
+ registrar_.Add(this, |
+ chrome::NOTIFICATION_EXTENSION_INSTALLED, |
+ content::Source<Profile>(profile_)); |
+ registrar_.Add(this, |
+ chrome::NOTIFICATION_EXTENSION_LOADED, |
+ content::Source<Profile>(profile_)); |
+ registrar_.Add(this, |
+ chrome::NOTIFICATION_EXTENSION_ENABLED, |
+ content::Source<Profile>(profile_)); |
+ registrar_.Add(this, |
+ chrome::NOTIFICATION_EXTENSION_UNLOADED, |
+ content::Source<Profile>(profile_)); |
+ |
+ base::MessageLoop::current()->PostDelayedTask(FROM_HERE, |
+ base::Bind(&ThemeService::RemoveUnusedThemes, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ false), |
+ base::TimeDelta::FromSeconds(kRemoveUnusedThemesStartupDelay)); |
} |
void ThemeService::MigrateTheme() { |
+ // TODO(erg): We need to pop up a dialog informing the user that their |
+ // theme is being migrated. |
ExtensionService* service = |
extensions::ExtensionSystem::Get(profile_)->extension_service(); |
const Extension* extension = service ? |
@@ -423,6 +517,15 @@ void ThemeService::MigrateTheme() { |
} |
} |
+void ThemeService::SwapThemeSupplier( |
+ scoped_refptr<CustomThemeSupplier> theme_supplier) { |
+ if (theme_supplier_.get()) |
+ theme_supplier_->StopUsingTheme(); |
+ theme_supplier_ = theme_supplier; |
+ if (theme_supplier_.get()) |
+ theme_supplier_->StartUsingTheme(); |
+} |
+ |
void ThemeService::SavePackName(const base::FilePath& pack_path) { |
profile_->GetPrefs()->SetFilePath( |
prefs::kCurrentThemePackFilename, pack_path); |
@@ -493,7 +596,7 @@ void ThemeService::OnInfobarDestroyed() { |
number_of_infobars_--; |
if (number_of_infobars_ == 0) |
- RemoveUnusedThemes(); |
+ RemoveUnusedThemes(false); |
} |
ThemeSyncableService* ThemeService::GetThemeSyncableService() const { |