// Copyright (c) 2011 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 "chrome/browser/sync/glue/theme_util.h"
#include <string>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_updater.h"
#if defined(TOOLKIT_USES_GTK)
#include "chrome/browser/ui/gtk/gtk_theme_service.h"
#endif
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/protocol/theme_specifics.pb.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "googleurl/src/gurl.h"
namespace browser_sync {
const char kCurrentThemeClientTag[] = "current_theme";
namespace {
bool IsSystemThemeDistinctFromDefaultTheme() {
#if defined(TOOLKIT_USES_GTK)
return true;
#else
return false;
#endif
}
bool UseSystemTheme(Profile* profile) {
#if defined(TOOLKIT_USES_GTK)
return GtkThemeService::GetFrom(profile)->UseGtkTheme();
#else
return false;
#endif
}
} // namespace
bool AreThemeSpecificsEqual(const sync_pb::ThemeSpecifics& a,
const sync_pb::ThemeSpecifics& b) {
return AreThemeSpecificsEqualHelper(
a, b, IsSystemThemeDistinctFromDefaultTheme());
}
bool AreThemeSpecificsEqualHelper(
const sync_pb::ThemeSpecifics& a,
const sync_pb::ThemeSpecifics& b,
bool is_system_theme_distinct_from_default_theme) {
if (a.use_custom_theme() != b.use_custom_theme()) {
return false;
}
if (a.use_custom_theme()) {
// We're using a custom theme, so simply compare IDs since those
// are guaranteed unique.
return a.custom_theme_id() == b.custom_theme_id();
} else if (is_system_theme_distinct_from_default_theme) {
// We're not using a custom theme, but we care about system
// vs. default.
return a.use_system_theme_by_default() == b.use_system_theme_by_default();
} else {
// We're not using a custom theme, and we don't care about system
// vs. default.
return true;
}
}
namespace {
bool IsTheme(const Extension& extension) {
return extension.is_theme();
}
} // namespace
void SetCurrentThemeFromThemeSpecifics(
const sync_pb::ThemeSpecifics& theme_specifics,
Profile* profile) {
DCHECK(profile);
if (theme_specifics.use_custom_theme()) {
// TODO(akalin): Figure out what to do about third-party themes
// (i.e., those not on either Google gallery).
std::string id(theme_specifics.custom_theme_id());
GURL update_url(theme_specifics.custom_theme_update_url());
VLOG(1) << "Applying theme " << id << " with update_url " << update_url;
ExtensionServiceInterface* extensions_service =
profile->GetExtensionService();
CHECK(extensions_service);
const Extension* extension = extensions_service->GetExtensionById(id, true);
if (extension) {
if (!extension->is_theme()) {
VLOG(1) << "Extension " << id << " is not a theme; aborting";
return;
}
if (!extensions_service->IsExtensionEnabled(id)) {
VLOG(1) << "Theme " << id << " is not enabled; aborting";
return;
}
// Get previous theme info before we set the new theme.
std::string previous_theme_id;
{
const Extension* current_theme =
ThemeServiceFactory::GetThemeForProfile(profile);
if (current_theme) {
DCHECK(current_theme->is_theme());
previous_theme_id = current_theme->id();
}
}
bool previous_use_system_theme = UseSystemTheme(profile);
// An enabled theme extension with the given id was found, so
// just set the current theme to it.
ThemeServiceFactory::GetForProfile(profile)->SetTheme(extension);
// Pretend the theme was just installed.
ExtensionInstallUI::ShowThemeInfoBar(
previous_theme_id, previous_use_system_theme,
extension, profile);
} else {
// No extension with this id exists -- we must install it; we do
// so by adding it as a pending extension and then triggering an
// auto-update cycle.
// Themes don't need to install silently as they just pop up an
// informational dialog after installation instead of a
// confirmation dialog.
const bool kInstallSilently = false;
const bool kEnableOnInstall = true;
const bool kEnableIncognitoOnInstall = false;
extensions_service->pending_extension_manager()->AddFromSync(
id, update_url, &IsTheme,
kInstallSilently, kEnableOnInstall, kEnableIncognitoOnInstall);
extensions_service->CheckForUpdatesSoon();
}
} else if (theme_specifics.use_system_theme_by_default()) {
ThemeServiceFactory::GetForProfile(profile)->SetNativeTheme();
} else {
ThemeServiceFactory::GetForProfile(profile)->UseDefaultTheme();
}
}
bool UpdateThemeSpecificsOrSetCurrentThemeIfNecessary(
Profile* profile, sync_pb::ThemeSpecifics* theme_specifics) {
if (!theme_specifics->use_custom_theme() &&
(ThemeServiceFactory::GetThemeForProfile(profile) ||
(UseSystemTheme(profile) &&
IsSystemThemeDistinctFromDefaultTheme()))) {
GetThemeSpecificsFromCurrentTheme(profile, theme_specifics);
return true;
} else {
SetCurrentThemeFromThemeSpecificsIfNecessary(*theme_specifics, profile);
return false;
}
}
void GetThemeSpecificsFromCurrentTheme(
Profile* profile,
sync_pb::ThemeSpecifics* theme_specifics) {
DCHECK(profile);
const Extension* current_theme =
ThemeServiceFactory::GetThemeForProfile(profile);
if (current_theme) {
DCHECK(current_theme->is_theme());
}
GetThemeSpecificsFromCurrentThemeHelper(
current_theme,
IsSystemThemeDistinctFromDefaultTheme(),
UseSystemTheme(profile),
theme_specifics);
}
void GetThemeSpecificsFromCurrentThemeHelper(
const Extension* current_theme,
bool is_system_theme_distinct_from_default_theme,
bool use_system_theme_by_default,
sync_pb::ThemeSpecifics* theme_specifics) {
bool use_custom_theme = (current_theme != NULL);
theme_specifics->set_use_custom_theme(use_custom_theme);
if (is_system_theme_distinct_from_default_theme) {
theme_specifics->set_use_system_theme_by_default(
use_system_theme_by_default);
} else {
DCHECK(!use_system_theme_by_default);
}
if (use_custom_theme) {
DCHECK(current_theme);
DCHECK(current_theme->is_theme());
theme_specifics->set_custom_theme_name(current_theme->name());
theme_specifics->set_custom_theme_id(current_theme->id());
theme_specifics->set_custom_theme_update_url(
current_theme->update_url().spec());
} else {
DCHECK(!current_theme);
theme_specifics->clear_custom_theme_name();
theme_specifics->clear_custom_theme_id();
theme_specifics->clear_custom_theme_update_url();
}
}
void SetCurrentThemeFromThemeSpecificsIfNecessary(
const sync_pb::ThemeSpecifics& theme_specifics, Profile* profile) {
DCHECK(profile);
sync_pb::ThemeSpecifics old_theme_specifics;
GetThemeSpecificsFromCurrentTheme(profile, &old_theme_specifics);
if (!AreThemeSpecificsEqual(old_theme_specifics, theme_specifics)) {
SetCurrentThemeFromThemeSpecifics(theme_specifics, profile);
}
}
} // namespace browser_sync