1 // Copyright (c) 2012 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/prefs/session_startup_pref.h" 6 7 #include <string> 8 9 #include "base/metrics/histogram.h" 10 #include "base/prefs/pref_service.h" 11 #include "base/prefs/scoped_user_pref_update.h" 12 #include "base/time/time.h" 13 #include "base/values.h" 14 #include "base/version.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/common/pref_names.h" 17 #include "components/pref_registry/pref_registry_syncable.h" 18 #include "components/url_fixer/url_fixer.h" 19 20 #if defined(OS_MACOSX) 21 #include "chrome/browser/ui/cocoa/window_restore_utils.h" 22 #endif 23 24 namespace { 25 26 enum StartupURLsMigrationMetrics { 27 STARTUP_URLS_MIGRATION_METRICS_PERFORMED, 28 STARTUP_URLS_MIGRATION_METRICS_NOT_PRESENT, 29 STARTUP_URLS_MIGRATION_METRICS_RESET, 30 STARTUP_URLS_MIGRATION_METRICS_MAX, 31 }; 32 33 // Converts a SessionStartupPref::Type to an integer written to prefs. 34 int TypeToPrefValue(SessionStartupPref::Type type) { 35 switch (type) { 36 case SessionStartupPref::LAST: return SessionStartupPref::kPrefValueLast; 37 case SessionStartupPref::URLS: return SessionStartupPref::kPrefValueURLs; 38 default: return SessionStartupPref::kPrefValueNewTab; 39 } 40 } 41 42 void SetNewURLList(PrefService* prefs) { 43 if (prefs->IsUserModifiablePreference(prefs::kURLsToRestoreOnStartup)) { 44 base::ListValue new_url_pref_list; 45 base::StringValue* home_page = 46 new base::StringValue(prefs->GetString(prefs::kHomePage)); 47 new_url_pref_list.Append(home_page); 48 prefs->Set(prefs::kURLsToRestoreOnStartup, new_url_pref_list); 49 } 50 } 51 52 void URLListToPref(const base::ListValue* url_list, SessionStartupPref* pref) { 53 pref->urls.clear(); 54 for (size_t i = 0; i < url_list->GetSize(); ++i) { 55 std::string url_text; 56 if (url_list->GetString(i, &url_text)) { 57 GURL fixed_url = url_fixer::FixupURL(url_text, std::string()); 58 pref->urls.push_back(fixed_url); 59 } 60 } 61 } 62 63 } // namespace 64 65 // static 66 void SessionStartupPref::RegisterProfilePrefs( 67 user_prefs::PrefRegistrySyncable* registry) { 68 registry->RegisterIntegerPref( 69 prefs::kRestoreOnStartup, 70 TypeToPrefValue(GetDefaultStartupType()), 71 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); 72 registry->RegisterListPref(prefs::kURLsToRestoreOnStartup, 73 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); 74 registry->RegisterListPref(prefs::kURLsToRestoreOnStartupOld, 75 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 76 registry->RegisterBooleanPref( 77 prefs::kRestoreOnStartupMigrated, 78 false, 79 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 80 registry->RegisterInt64Pref( 81 prefs::kRestoreStartupURLsMigrationTime, 82 false, 83 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 84 } 85 86 // static 87 SessionStartupPref::Type SessionStartupPref::GetDefaultStartupType() { 88 #if defined(OS_CHROMEOS) 89 return SessionStartupPref::LAST; 90 #else 91 return SessionStartupPref::DEFAULT; 92 #endif 93 } 94 95 // static 96 void SessionStartupPref::SetStartupPref( 97 Profile* profile, 98 const SessionStartupPref& pref) { 99 DCHECK(profile); 100 SetStartupPref(profile->GetPrefs(), pref); 101 } 102 103 // static 104 void SessionStartupPref::SetStartupPref(PrefService* prefs, 105 const SessionStartupPref& pref) { 106 DCHECK(prefs); 107 108 if (!SessionStartupPref::TypeIsManaged(prefs)) 109 prefs->SetInteger(prefs::kRestoreOnStartup, TypeToPrefValue(pref.type)); 110 111 if (!SessionStartupPref::URLsAreManaged(prefs)) { 112 // Always save the URLs, that way the UI can remain consistent even if the 113 // user changes the startup type pref. 114 // Ownership of the ListValue retains with the pref service. 115 ListPrefUpdate update(prefs, prefs::kURLsToRestoreOnStartup); 116 base::ListValue* url_pref_list = update.Get(); 117 DCHECK(url_pref_list); 118 url_pref_list->Clear(); 119 for (size_t i = 0; i < pref.urls.size(); ++i) { 120 url_pref_list->Set(static_cast<int>(i), 121 new base::StringValue(pref.urls[i].spec())); 122 } 123 } 124 } 125 126 // static 127 SessionStartupPref SessionStartupPref::GetStartupPref(Profile* profile) { 128 DCHECK(profile); 129 return GetStartupPref(profile->GetPrefs()); 130 } 131 132 // static 133 SessionStartupPref SessionStartupPref::GetStartupPref(PrefService* prefs) { 134 DCHECK(prefs); 135 136 MigrateIfNecessary(prefs); 137 MigrateMacDefaultPrefIfNecessary(prefs); 138 139 SessionStartupPref pref( 140 PrefValueToType(prefs->GetInteger(prefs::kRestoreOnStartup))); 141 142 // Always load the urls, even if the pref type isn't URLS. This way the 143 // preferences panels can show the user their last choice. 144 const base::ListValue* url_list = 145 prefs->GetList(prefs::kURLsToRestoreOnStartup); 146 URLListToPref(url_list, &pref); 147 148 return pref; 149 } 150 151 // static 152 void SessionStartupPref::MigrateIfNecessary(PrefService* prefs) { 153 DCHECK(prefs); 154 155 // Check if we need to migrate the old version of the startup URLs preference 156 // to the new name, and also send metrics about the migration. 157 StartupURLsMigrationMetrics metrics_result = 158 STARTUP_URLS_MIGRATION_METRICS_MAX; 159 const base::ListValue* old_startup_urls = 160 prefs->GetList(prefs::kURLsToRestoreOnStartupOld); 161 if (!prefs->GetUserPrefValue(prefs::kRestoreStartupURLsMigrationTime)) { 162 // Record the absence of the migration timestamp, this will get overwritten 163 // below if migration occurs now. 164 metrics_result = STARTUP_URLS_MIGRATION_METRICS_NOT_PRESENT; 165 166 // Seems like we never migrated, do it if necessary. 167 if (!prefs->GetUserPrefValue(prefs::kURLsToRestoreOnStartup)) { 168 if (old_startup_urls && !old_startup_urls->empty()) { 169 prefs->Set(prefs::kURLsToRestoreOnStartup, *old_startup_urls); 170 prefs->ClearPref(prefs::kURLsToRestoreOnStartupOld); 171 } 172 metrics_result = STARTUP_URLS_MIGRATION_METRICS_PERFORMED; 173 } 174 175 prefs->SetInt64(prefs::kRestoreStartupURLsMigrationTime, 176 base::Time::Now().ToInternalValue()); 177 } else if (old_startup_urls && !old_startup_urls->empty()) { 178 // Migration needs to be reset. 179 prefs->ClearPref(prefs::kURLsToRestoreOnStartupOld); 180 base::Time last_migration_time = base::Time::FromInternalValue( 181 prefs->GetInt64(prefs::kRestoreStartupURLsMigrationTime)); 182 base::Time now = base::Time::Now(); 183 prefs->SetInt64(prefs::kRestoreStartupURLsMigrationTime, 184 now.ToInternalValue()); 185 if (now < last_migration_time) 186 last_migration_time = now; 187 UMA_HISTOGRAM_CUSTOM_TIMES("Settings.StartupURLsResetTime", 188 now - last_migration_time, 189 base::TimeDelta::FromDays(0), 190 base::TimeDelta::FromDays(7), 191 50); 192 metrics_result = STARTUP_URLS_MIGRATION_METRICS_RESET; 193 } 194 195 // Record a metric migration event if something interesting happened. 196 if (metrics_result != STARTUP_URLS_MIGRATION_METRICS_MAX) { 197 UMA_HISTOGRAM_ENUMERATION( 198 "Settings.StartupURLsMigration", 199 metrics_result, 200 STARTUP_URLS_MIGRATION_METRICS_MAX); 201 } 202 203 if (!prefs->GetBoolean(prefs::kRestoreOnStartupMigrated)) { 204 // Read existing values. 205 const base::Value* homepage_is_new_tab_page_value = 206 prefs->GetUserPrefValue(prefs::kHomePageIsNewTabPage); 207 bool homepage_is_new_tab_page = true; 208 if (homepage_is_new_tab_page_value) { 209 if (!homepage_is_new_tab_page_value->GetAsBoolean( 210 &homepage_is_new_tab_page)) 211 NOTREACHED(); 212 } 213 214 const base::Value* restore_on_startup_value = 215 prefs->GetUserPrefValue(prefs::kRestoreOnStartup); 216 int restore_on_startup = -1; 217 if (restore_on_startup_value) { 218 if (!restore_on_startup_value->GetAsInteger(&restore_on_startup)) 219 NOTREACHED(); 220 } 221 222 // If restore_on_startup has the deprecated value kPrefValueHomePage, 223 // migrate it to open the homepage on startup. If 'homepage is NTP' is set, 224 // that means just opening the NTP. If not, it means opening a one-item URL 225 // list containing the homepage. 226 if (restore_on_startup == kPrefValueHomePage) { 227 if (homepage_is_new_tab_page) { 228 prefs->SetInteger(prefs::kRestoreOnStartup, kPrefValueNewTab); 229 } else { 230 prefs->SetInteger(prefs::kRestoreOnStartup, kPrefValueURLs); 231 SetNewURLList(prefs); 232 } 233 } else if (!restore_on_startup_value && !homepage_is_new_tab_page && 234 GetDefaultStartupType() == DEFAULT) { 235 // kRestoreOnStartup was never set by the user, but the homepage was set. 236 // Migrate to the list of URLs. (If restore_on_startup was never set, 237 // and homepage_is_new_tab_page is true, no action is needed. The new 238 // default value is "open the new tab page" which is what we want.) 239 prefs->SetInteger(prefs::kRestoreOnStartup, kPrefValueURLs); 240 SetNewURLList(prefs); 241 } 242 243 prefs->SetBoolean(prefs::kRestoreOnStartupMigrated, true); 244 } 245 } 246 247 // static 248 void SessionStartupPref::MigrateMacDefaultPrefIfNecessary(PrefService* prefs) { 249 #if defined(OS_MACOSX) 250 DCHECK(prefs); 251 if (!restore_utils::IsWindowRestoreEnabled()) 252 return; 253 // The default startup pref used to be LAST, now it is DEFAULT. Don't change 254 // the setting for existing profiles (even if the user has never changed it), 255 // but make new profiles default to DEFAULT. 256 bool old_profile_version = 257 !prefs->FindPreference( 258 prefs::kProfileCreatedByVersion)->IsDefaultValue() && 259 Version(prefs->GetString(prefs::kProfileCreatedByVersion)).IsOlderThan( 260 "21.0.1180.0"); 261 if (old_profile_version && TypeIsDefault(prefs)) 262 prefs->SetInteger(prefs::kRestoreOnStartup, kPrefValueLast); 263 #endif 264 } 265 266 // static 267 bool SessionStartupPref::TypeIsManaged(PrefService* prefs) { 268 DCHECK(prefs); 269 const PrefService::Preference* pref_restore = 270 prefs->FindPreference(prefs::kRestoreOnStartup); 271 DCHECK(pref_restore); 272 return pref_restore->IsManaged(); 273 } 274 275 // static 276 bool SessionStartupPref::URLsAreManaged(PrefService* prefs) { 277 DCHECK(prefs); 278 const PrefService::Preference* pref_urls = 279 prefs->FindPreference(prefs::kURLsToRestoreOnStartup); 280 DCHECK(pref_urls); 281 return pref_urls->IsManaged(); 282 } 283 284 // static 285 bool SessionStartupPref::TypeIsDefault(PrefService* prefs) { 286 DCHECK(prefs); 287 const PrefService::Preference* pref_restore = 288 prefs->FindPreference(prefs::kRestoreOnStartup); 289 DCHECK(pref_restore); 290 return pref_restore->IsDefaultValue(); 291 } 292 293 // static 294 SessionStartupPref::Type SessionStartupPref::PrefValueToType(int pref_value) { 295 switch (pref_value) { 296 case kPrefValueLast: return SessionStartupPref::LAST; 297 case kPrefValueURLs: return SessionStartupPref::URLS; 298 case kPrefValueHomePage: return SessionStartupPref::HOMEPAGE; 299 default: return SessionStartupPref::DEFAULT; 300 } 301 } 302 303 SessionStartupPref::SessionStartupPref(Type type) : type(type) {} 304 305 SessionStartupPref::~SessionStartupPref() {} 306