This source file includes following definitions.
- IncreaseLightness
- WritePackToDiskCallback
- weak_ptr_factory_
- Init
- GetImageNamed
- UsingNativeTheme
- GetImageSkiaNamed
- GetColor
- GetDisplayProperty
- ShouldUseNativeFrame
- HasCustomImage
- GetRawData
- Observe
- SetTheme
- SetCustomDefaultTheme
- ShouldInitWithNativeTheme
- RemoveUnusedThemes
- UseDefaultTheme
- SetNativeTheme
- UsingDefaultTheme
- GetThemeID
- GetTint
- ClearAllThemeData
- LoadThemePrefs
- NotifyThemeChanged
- FreePlatformCaches
- OnExtensionServiceReady
- MigrateTheme
- SwapThemeSupplier
- SavePackName
- SaveThemeID
- BuildFromExtension
- IsManagedUser
- SetManagedUserTheme
- OnInfobarDisplayed
- OnInfobarDestroyed
- GetThemeSyncableService
#include "chrome/browser/themes/theme_service.h"
#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"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/managed_mode/managed_user_theme.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/browser_theme_pack.h"
#include "chrome/browser/themes/custom_theme_supplier.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/themes/theme_syncable_service.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/user_metrics.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "grit/theme_resources.h"
#include "grit/ui_resources.h"
#include "ui/base/layout.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image_skia.h"
#if defined(OS_WIN)
#include "ui/base/win/shell.h"
#endif
using base::UserMetricsAction;
using content::BrowserThread;
using extensions::Extension;
using extensions::UnloadedExtensionInfo;
using ui::ResourceBundle;
typedef ThemeProperties Properties;
const char* ThemeService::kDefaultThemeID = "";
namespace {
const char* kDefaultThemeGalleryID = "hkacjpbfdknhflllbcmjibkdeoafencn";
const int kRemoveUnusedThemesStartupDelay = 30;
SkColor IncreaseLightness(SkColor color, double percent) {
color_utils::HSL result;
color_utils::SkColorToHSL(color, &result);
result.l += (1 - result.l) * percent;
return color_utils::HSLToSkColor(result, SkColorGetA(color));
}
void WritePackToDiskCallback(BrowserThemePack* pack,
const base::FilePath& path) {
if (!pack->WriteToDisk(path))
NOTREACHED() << "Could not write theme pack to disk";
}
}
ThemeService::ThemeService()
: ready_(false),
rb_(ResourceBundle::GetSharedInstance()),
profile_(NULL),
installed_pending_load_id_(kDefaultThemeID),
number_of_infobars_(0),
weak_ptr_factory_(this) {
}
ThemeService::~ThemeService() {
FreePlatformCaches();
}
void ThemeService::Init(Profile* profile) {
DCHECK(CalledOnValidThread());
profile_ = profile;
LoadThemePrefs();
registrar_.Add(this,
chrome::NOTIFICATION_EXTENSIONS_READY,
content::Source<Profile>(profile_));
theme_syncable_service_.reset(new ThemeSyncableService(profile_, this));
}
gfx::Image ThemeService::GetImageNamed(int id) const {
DCHECK(CalledOnValidThread());
gfx::Image image;
if (theme_supplier_.get())
image = theme_supplier_->GetImageNamed(id);
if (image.IsEmpty())
image = rb_.GetNativeImageNamed(id);
return image;
}
bool ThemeService::UsingNativeTheme() const {
return UsingDefaultTheme();
}
gfx::ImageSkia* ThemeService::GetImageSkiaNamed(int id) const {
gfx::Image image = GetImageNamed(id);
if (image.IsEmpty())
return NULL;
return const_cast<gfx::ImageSkia*>(image.ToImageSkia());
}
SkColor ThemeService::GetColor(int id) const {
DCHECK(CalledOnValidThread());
SkColor color;
if (theme_supplier_.get() && theme_supplier_->GetColor(id, &color))
return color;
switch (id) {
case Properties::COLOR_NTP_SECTION_HEADER_TEXT:
return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.30);
case Properties::COLOR_NTP_SECTION_HEADER_TEXT_HOVER:
return GetColor(Properties::COLOR_NTP_TEXT);
case Properties::COLOR_NTP_SECTION_HEADER_RULE:
return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.70);
case Properties::COLOR_NTP_SECTION_HEADER_RULE_LIGHT:
return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.86);
case Properties::COLOR_NTP_TEXT_LIGHT:
return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.40);
case Properties::COLOR_MANAGED_USER_LABEL:
return color_utils::GetReadableColor(
SK_ColorWHITE,
GetColor(Properties::COLOR_MANAGED_USER_LABEL_BACKGROUND));
case Properties::COLOR_MANAGED_USER_LABEL_BACKGROUND:
return color_utils::BlendTowardOppositeLuminance(
GetColor(Properties::COLOR_FRAME), 0x80);
case Properties::COLOR_MANAGED_USER_LABEL_BORDER:
return color_utils::AlphaBlend(
GetColor(Properties::COLOR_MANAGED_USER_LABEL_BACKGROUND),
SK_ColorBLACK,
230);
case Properties::COLOR_STATUS_BAR_TEXT: {
SkColor toolbar_color = GetColor(Properties::COLOR_TOOLBAR);
SkColor text_color = GetColor(Properties::COLOR_TAB_TEXT);
return SkColorSetARGB(
SkColorGetA(text_color),
(SkColorGetR(text_color) + SkColorGetR(toolbar_color)) / 2,
(SkColorGetG(text_color) + SkColorGetR(toolbar_color)) / 2,
(SkColorGetB(text_color) + SkColorGetR(toolbar_color)) / 2);
}
}
return Properties::GetDefaultColor(id);
}
int ThemeService::GetDisplayProperty(int id) const {
int result = 0;
if (theme_supplier_.get() &&
theme_supplier_->GetDisplayProperty(id, &result)) {
return result;
}
if (id == Properties::NTP_LOGO_ALTERNATE &&
!UsingDefaultTheme() &&
!UsingNativeTheme()) {
return 1;
}
return Properties::GetDefaultDisplayProperty(id);
}
bool ThemeService::ShouldUseNativeFrame() const {
if (HasCustomImage(IDR_THEME_FRAME))
return false;
#if defined(OS_WIN)
return ui::win::IsAeroGlassEnabled();
#else
return false;
#endif
}
bool ThemeService::HasCustomImage(int id) const {
if (!Properties::IsThemeableImage(id))
return false;
if (theme_supplier_.get())
return theme_supplier_->HasCustomImage(id);
return false;
}
base::RefCountedMemory* ThemeService::GetRawData(
int id,
ui::ScaleFactor scale_factor) const {
int ntp_alternate = GetDisplayProperty(Properties::NTP_LOGO_ALTERNATE);
if (id == IDR_PRODUCT_LOGO && ntp_alternate != 0)
id = IDR_PRODUCT_LOGO_WHITE;
base::RefCountedMemory* data = NULL;
if (theme_supplier_.get())
data = theme_supplier_->GetRawData(id, scale_factor);
if (!data)
data = rb_.LoadDataResourceBytesForScale(id, ui::SCALE_FACTOR_100P);
return data;
}
void ThemeService::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
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:
{
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_DEPRECATED:
{
Details<const UnloadedExtensionInfo> unloaded_details(details);
if (unloaded_details->reason != UnloadedExtensionInfo::REASON_UPDATE &&
unloaded_details->extension->is_theme() &&
unloaded_details->extension->id() == GetThemeID()) {
UseDefaultTheme();
}
break;
}
}
}
void ThemeService::SetTheme(const Extension* extension) {
DCHECK(extension->is_theme());
ExtensionService* service =
extensions::ExtensionSystem::Get(profile_)->extension_service();
if (!service->IsExtensionEnabled(extension->id())) {
service->EnableExtension(extension->id());
return;
}
std::string previous_theme_id = GetThemeID();
FreePlatformCaches();
BuildFromExtension(extension);
SaveThemeID(extension->id());
NotifyThemeChanged();
content::RecordAction(UserMetricsAction("Themes_Installed"));
if (previous_theme_id != kDefaultThemeID &&
previous_theme_id != extension->id()) {
service->DisableExtension(previous_theme_id,
extensions::Extension::DISABLE_USER_ACTION);
}
}
void ThemeService::SetCustomDefaultTheme(
scoped_refptr<CustomThemeSupplier> theme_supplier) {
ClearAllThemeData();
SwapThemeSupplier(theme_supplier);
NotifyThemeChanged();
}
bool ThemeService::ShouldInitWithNativeTheme() const {
return false;
}
void ThemeService::RemoveUnusedThemes(bool ignore_infobars) {
if (!profile_ || !ready_)
return;
if (!ignore_infobars && number_of_infobars_ != 0)
return;
ExtensionService* service =
extensions::ExtensionSystem::Get(profile_)->extension_service();
if (!service)
return;
std::string current_theme = GetThemeID();
std::vector<std::string> remove_list;
scoped_ptr<const extensions::ExtensionSet> extensions(
extensions::ExtensionRegistry::Get(profile_)
->GenerateInstalledExtensionsSet());
extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
for (extensions::ExtensionSet::const_iterator it = extensions->begin();
it != extensions->end(); ++it) {
const extensions::Extension* extension = *it;
if (extension->is_theme() &&
extension->id() != current_theme) {
int disable_reason = prefs->GetDisableReasons(extension->id());
if (!prefs->IsExtensionDisabled(extension->id()) ||
disable_reason == Extension::DISABLE_USER_ACTION) {
remove_list.push_back((*it)->id());
}
}
}
for (size_t i = 0; i < remove_list.size(); ++i)
service->UninstallExtension(remove_list[i], false, NULL);
}
void ThemeService::UseDefaultTheme() {
if (ready_)
content::RecordAction(UserMetricsAction("Themes_Reset"));
if (IsManagedUser()) {
SetManagedUserTheme();
return;
}
ClearAllThemeData();
NotifyThemeChanged();
}
void ThemeService::SetNativeTheme() {
UseDefaultTheme();
}
bool ThemeService::UsingDefaultTheme() const {
std::string id = GetThemeID();
return id == ThemeService::kDefaultThemeID ||
id == kDefaultThemeGalleryID;
}
std::string ThemeService::GetThemeID() const {
return profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
}
color_utils::HSL ThemeService::GetTint(int id) const {
DCHECK(CalledOnValidThread());
color_utils::HSL hsl;
if (theme_supplier_.get() && theme_supplier_->GetTint(id, &hsl))
return hsl;
return ThemeProperties::GetDefaultTint(id);
}
void ThemeService::ClearAllThemeData() {
if (!ready_)
return;
SwapThemeSupplier(NULL);
FreePlatformCaches();
profile_->GetPrefs()->ClearPref(prefs::kCurrentThemePackFilename);
SaveThemeID(kDefaultThemeID);
base::MessageLoop::current()->PostTask(FROM_HERE,
base::Bind(&ThemeService::RemoveUnusedThemes,
weak_ptr_factory_.GetWeakPtr(),
true));
}
void ThemeService::LoadThemePrefs() {
PrefService* prefs = profile_->GetPrefs();
std::string current_id = GetThemeID();
if (current_id == kDefaultThemeID) {
if (IsManagedUser())
SetManagedUserTheme();
else if (ShouldInitWithNativeTheme())
SetNativeTheme();
else
UseDefaultTheme();
set_ready();
return;
}
bool loaded_pack = false;
base::FilePath path = prefs->GetFilePath(prefs::kCurrentThemePackFilename);
if (path != base::FilePath()) {
SwapThemeSupplier(BrowserThemePack::BuildFromDataPack(path, current_id));
loaded_pack = theme_supplier_.get() != NULL;
}
if (loaded_pack) {
content::RecordAction(UserMetricsAction("Themes.Loaded"));
set_ready();
}
}
void ThemeService::NotifyThemeChanged() {
if (!ready_)
return;
DVLOG(1) << "Sending BROWSER_THEME_CHANGED";
content::NotificationService* service =
content::NotificationService::current();
service->Notify(chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
content::Source<ThemeService>(this),
content::NotificationService::NoDetails());
#if defined(OS_MACOSX)
NotifyPlatformThemeChanged();
#endif
if (theme_syncable_service_.get()) {
theme_syncable_service_->OnThemeChange();
}
}
#if defined(OS_WIN) || defined(USE_AURA)
void ThemeService::FreePlatformCaches() {
}
#endif
void ThemeService::OnExtensionServiceReady() {
if (!ready_) {
MigrateTheme();
set_ready();
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_DEPRECATED,
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() {
ExtensionService* service =
extensions::ExtensionSystem::Get(profile_)->extension_service();
const Extension* extension = service ?
service->GetExtensionById(GetThemeID(), false) : NULL;
if (extension) {
DLOG(ERROR) << "Migrating theme";
BuildFromExtension(extension);
content::RecordAction(UserMetricsAction("Themes.Migrated"));
} else {
DLOG(ERROR) << "Theme is mysteriously gone.";
ClearAllThemeData();
content::RecordAction(UserMetricsAction("Themes.Gone"));
}
}
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);
}
void ThemeService::SaveThemeID(const std::string& id) {
profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, id);
}
void ThemeService::BuildFromExtension(const Extension* extension) {
scoped_refptr<BrowserThemePack> pack(
BrowserThemePack::BuildFromExtension(extension));
if (!pack.get()) {
LOG(ERROR) << "Could not load theme.";
return;
}
ExtensionService* service =
extensions::ExtensionSystem::Get(profile_)->extension_service();
if (!service)
return;
base::FilePath pack_path =
extension->path().Append(chrome::kThemePackFilename);
service->GetFileTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&WritePackToDiskCallback, pack, pack_path));
SavePackName(pack_path);
SwapThemeSupplier(pack);
}
bool ThemeService::IsManagedUser() const {
return profile_->IsManaged();
}
void ThemeService::SetManagedUserTheme() {
SetCustomDefaultTheme(new ManagedUserTheme);
}
void ThemeService::OnInfobarDisplayed() {
number_of_infobars_++;
}
void ThemeService::OnInfobarDestroyed() {
number_of_infobars_--;
if (number_of_infobars_ == 0)
RemoveUnusedThemes(false);
}
ThemeSyncableService* ThemeService::GetThemeSyncableService() const {
return theme_syncable_service_.get();
}