1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/sync/glue/theme_util.h" 6 7 #include <string> 8 9 #include "base/logging.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "chrome/browser/extensions/extension_install_ui.h" 12 #include "chrome/browser/extensions/extension_service.h" 13 #include "chrome/browser/extensions/extension_updater.h" 14 #if defined(TOOLKIT_USES_GTK) 15 #include "chrome/browser/ui/gtk/gtk_theme_service.h" 16 #endif 17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/sync/protocol/theme_specifics.pb.h" 19 #include "chrome/browser/themes/theme_service.h" 20 #include "chrome/browser/themes/theme_service_factory.h" 21 #include "chrome/common/extensions/extension.h" 22 #include "chrome/common/extensions/extension_constants.h" 23 #include "googleurl/src/gurl.h" 24 25 namespace browser_sync { 26 27 const char kCurrentThemeClientTag[] = "current_theme"; 28 29 namespace { 30 31 bool IsSystemThemeDistinctFromDefaultTheme() { 32 #if defined(TOOLKIT_USES_GTK) 33 return true; 34 #else 35 return false; 36 #endif 37 } 38 39 bool UseSystemTheme(Profile* profile) { 40 #if defined(TOOLKIT_USES_GTK) 41 return GtkThemeService::GetFrom(profile)->UseGtkTheme(); 42 #else 43 return false; 44 #endif 45 } 46 47 } // namespace 48 49 bool AreThemeSpecificsEqual(const sync_pb::ThemeSpecifics& a, 50 const sync_pb::ThemeSpecifics& b) { 51 return AreThemeSpecificsEqualHelper( 52 a, b, IsSystemThemeDistinctFromDefaultTheme()); 53 } 54 55 bool AreThemeSpecificsEqualHelper( 56 const sync_pb::ThemeSpecifics& a, 57 const sync_pb::ThemeSpecifics& b, 58 bool is_system_theme_distinct_from_default_theme) { 59 if (a.use_custom_theme() != b.use_custom_theme()) { 60 return false; 61 } 62 63 if (a.use_custom_theme()) { 64 // We're using a custom theme, so simply compare IDs since those 65 // are guaranteed unique. 66 return a.custom_theme_id() == b.custom_theme_id(); 67 } else if (is_system_theme_distinct_from_default_theme) { 68 // We're not using a custom theme, but we care about system 69 // vs. default. 70 return a.use_system_theme_by_default() == b.use_system_theme_by_default(); 71 } else { 72 // We're not using a custom theme, and we don't care about system 73 // vs. default. 74 return true; 75 } 76 } 77 78 namespace { 79 80 bool IsTheme(const Extension& extension) { 81 return extension.is_theme(); 82 } 83 84 } // namespace 85 86 void SetCurrentThemeFromThemeSpecifics( 87 const sync_pb::ThemeSpecifics& theme_specifics, 88 Profile* profile) { 89 DCHECK(profile); 90 if (theme_specifics.use_custom_theme()) { 91 // TODO(akalin): Figure out what to do about third-party themes 92 // (i.e., those not on either Google gallery). 93 std::string id(theme_specifics.custom_theme_id()); 94 GURL update_url(theme_specifics.custom_theme_update_url()); 95 VLOG(1) << "Applying theme " << id << " with update_url " << update_url; 96 ExtensionServiceInterface* extensions_service = 97 profile->GetExtensionService(); 98 CHECK(extensions_service); 99 const Extension* extension = extensions_service->GetExtensionById(id, true); 100 if (extension) { 101 if (!extension->is_theme()) { 102 VLOG(1) << "Extension " << id << " is not a theme; aborting"; 103 return; 104 } 105 if (!extensions_service->IsExtensionEnabled(id)) { 106 VLOG(1) << "Theme " << id << " is not enabled; aborting"; 107 return; 108 } 109 // Get previous theme info before we set the new theme. 110 std::string previous_theme_id; 111 { 112 const Extension* current_theme = 113 ThemeServiceFactory::GetThemeForProfile(profile); 114 if (current_theme) { 115 DCHECK(current_theme->is_theme()); 116 previous_theme_id = current_theme->id(); 117 } 118 } 119 bool previous_use_system_theme = UseSystemTheme(profile); 120 // An enabled theme extension with the given id was found, so 121 // just set the current theme to it. 122 ThemeServiceFactory::GetForProfile(profile)->SetTheme(extension); 123 // Pretend the theme was just installed. 124 ExtensionInstallUI::ShowThemeInfoBar( 125 previous_theme_id, previous_use_system_theme, 126 extension, profile); 127 } else { 128 // No extension with this id exists -- we must install it; we do 129 // so by adding it as a pending extension and then triggering an 130 // auto-update cycle. 131 // Themes don't need to install silently as they just pop up an 132 // informational dialog after installation instead of a 133 // confirmation dialog. 134 const bool kInstallSilently = false; 135 const bool kEnableOnInstall = true; 136 const bool kEnableIncognitoOnInstall = false; 137 extensions_service->pending_extension_manager()->AddFromSync( 138 id, update_url, &IsTheme, 139 kInstallSilently, kEnableOnInstall, kEnableIncognitoOnInstall); 140 extensions_service->CheckForUpdatesSoon(); 141 } 142 } else if (theme_specifics.use_system_theme_by_default()) { 143 ThemeServiceFactory::GetForProfile(profile)->SetNativeTheme(); 144 } else { 145 ThemeServiceFactory::GetForProfile(profile)->UseDefaultTheme(); 146 } 147 } 148 149 bool UpdateThemeSpecificsOrSetCurrentThemeIfNecessary( 150 Profile* profile, sync_pb::ThemeSpecifics* theme_specifics) { 151 if (!theme_specifics->use_custom_theme() && 152 (ThemeServiceFactory::GetThemeForProfile(profile) || 153 (UseSystemTheme(profile) && 154 IsSystemThemeDistinctFromDefaultTheme()))) { 155 GetThemeSpecificsFromCurrentTheme(profile, theme_specifics); 156 return true; 157 } else { 158 SetCurrentThemeFromThemeSpecificsIfNecessary(*theme_specifics, profile); 159 return false; 160 } 161 } 162 163 void GetThemeSpecificsFromCurrentTheme( 164 Profile* profile, 165 sync_pb::ThemeSpecifics* theme_specifics) { 166 DCHECK(profile); 167 const Extension* current_theme = 168 ThemeServiceFactory::GetThemeForProfile(profile); 169 if (current_theme) { 170 DCHECK(current_theme->is_theme()); 171 } 172 GetThemeSpecificsFromCurrentThemeHelper( 173 current_theme, 174 IsSystemThemeDistinctFromDefaultTheme(), 175 UseSystemTheme(profile), 176 theme_specifics); 177 } 178 179 void GetThemeSpecificsFromCurrentThemeHelper( 180 const Extension* current_theme, 181 bool is_system_theme_distinct_from_default_theme, 182 bool use_system_theme_by_default, 183 sync_pb::ThemeSpecifics* theme_specifics) { 184 bool use_custom_theme = (current_theme != NULL); 185 theme_specifics->set_use_custom_theme(use_custom_theme); 186 if (is_system_theme_distinct_from_default_theme) { 187 theme_specifics->set_use_system_theme_by_default( 188 use_system_theme_by_default); 189 } else { 190 DCHECK(!use_system_theme_by_default); 191 } 192 if (use_custom_theme) { 193 DCHECK(current_theme); 194 DCHECK(current_theme->is_theme()); 195 theme_specifics->set_custom_theme_name(current_theme->name()); 196 theme_specifics->set_custom_theme_id(current_theme->id()); 197 theme_specifics->set_custom_theme_update_url( 198 current_theme->update_url().spec()); 199 } else { 200 DCHECK(!current_theme); 201 theme_specifics->clear_custom_theme_name(); 202 theme_specifics->clear_custom_theme_id(); 203 theme_specifics->clear_custom_theme_update_url(); 204 } 205 } 206 207 void SetCurrentThemeFromThemeSpecificsIfNecessary( 208 const sync_pb::ThemeSpecifics& theme_specifics, Profile* profile) { 209 DCHECK(profile); 210 sync_pb::ThemeSpecifics old_theme_specifics; 211 GetThemeSpecificsFromCurrentTheme(profile, &old_theme_specifics); 212 if (!AreThemeSpecificsEqual(old_theme_specifics, theme_specifics)) { 213 SetCurrentThemeFromThemeSpecifics(theme_specifics, profile); 214 } 215 } 216 217 } // namespace browser_sync 218