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/net/url_fixer_upper.h"
17 #include "chrome/common/pref_names.h"
18 #include "components/user_prefs/pref_registry_syncable.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.
TypeToPrefValue(SessionStartupPref::Type type)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
SetNewURLList(PrefService * prefs)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
URLListToPref(const base::ListValue * url_list,SessionStartupPref * pref)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 = URLFixerUpper::FixupURL(url_text, std::string());
58 pref->urls.push_back(fixed_url);
59 }
60 }
61 }
62
63 } // namespace
64
65 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)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
GetDefaultStartupType()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
SetStartupPref(Profile * profile,const SessionStartupPref & pref)96 void SessionStartupPref::SetStartupPref(
97 Profile* profile,
98 const SessionStartupPref& pref) {
99 DCHECK(profile);
100 SetStartupPref(profile->GetPrefs(), pref);
101 }
102
103 // static
SetStartupPref(PrefService * prefs,const SessionStartupPref & pref)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 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 StringValue(pref.urls[i].spec()));
122 }
123 }
124 }
125
126 // static
GetStartupPref(Profile * profile)127 SessionStartupPref SessionStartupPref::GetStartupPref(Profile* profile) {
128 DCHECK(profile);
129 return GetStartupPref(profile->GetPrefs());
130 }
131
132 // static
GetStartupPref(PrefService * prefs)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 ListValue* url_list = prefs->GetList(prefs::kURLsToRestoreOnStartup);
145 URLListToPref(url_list, &pref);
146
147 return pref;
148 }
149
150 // static
MigrateIfNecessary(PrefService * prefs)151 void SessionStartupPref::MigrateIfNecessary(PrefService* prefs) {
152 DCHECK(prefs);
153
154 // Check if we need to migrate the old version of the startup URLs preference
155 // to the new name, and also send metrics about the migration.
156 StartupURLsMigrationMetrics metrics_result =
157 STARTUP_URLS_MIGRATION_METRICS_MAX;
158 const base::ListValue* old_startup_urls =
159 prefs->GetList(prefs::kURLsToRestoreOnStartupOld);
160 if (!prefs->GetUserPrefValue(prefs::kRestoreStartupURLsMigrationTime)) {
161 // Record the absence of the migration timestamp, this will get overwritten
162 // below if migration occurs now.
163 metrics_result = STARTUP_URLS_MIGRATION_METRICS_NOT_PRESENT;
164
165 // Seems like we never migrated, do it if necessary.
166 if (!prefs->GetUserPrefValue(prefs::kURLsToRestoreOnStartup)) {
167 if (old_startup_urls && !old_startup_urls->empty()) {
168 prefs->Set(prefs::kURLsToRestoreOnStartup, *old_startup_urls);
169 prefs->ClearPref(prefs::kURLsToRestoreOnStartupOld);
170 }
171 metrics_result = STARTUP_URLS_MIGRATION_METRICS_PERFORMED;
172 }
173
174 prefs->SetInt64(prefs::kRestoreStartupURLsMigrationTime,
175 base::Time::Now().ToInternalValue());
176 } else if (old_startup_urls && !old_startup_urls->empty()) {
177 // Migration needs to be reset.
178 prefs->ClearPref(prefs::kURLsToRestoreOnStartupOld);
179 base::Time last_migration_time = base::Time::FromInternalValue(
180 prefs->GetInt64(prefs::kRestoreStartupURLsMigrationTime));
181 base::Time now = base::Time::Now();
182 prefs->SetInt64(prefs::kRestoreStartupURLsMigrationTime,
183 now.ToInternalValue());
184 if (now < last_migration_time)
185 last_migration_time = now;
186 UMA_HISTOGRAM_CUSTOM_TIMES("Settings.StartupURLsResetTime",
187 now - last_migration_time,
188 base::TimeDelta::FromDays(0),
189 base::TimeDelta::FromDays(7),
190 50);
191 metrics_result = STARTUP_URLS_MIGRATION_METRICS_RESET;
192 }
193
194 // Record a metric migration event if something interesting happened.
195 if (metrics_result != STARTUP_URLS_MIGRATION_METRICS_MAX) {
196 UMA_HISTOGRAM_ENUMERATION(
197 "Settings.StartupURLsMigration",
198 metrics_result,
199 STARTUP_URLS_MIGRATION_METRICS_MAX);
200 }
201
202 if (!prefs->GetBoolean(prefs::kRestoreOnStartupMigrated)) {
203 // Read existing values.
204 const base::Value* homepage_is_new_tab_page_value =
205 prefs->GetUserPrefValue(prefs::kHomePageIsNewTabPage);
206 bool homepage_is_new_tab_page = true;
207 if (homepage_is_new_tab_page_value) {
208 if (!homepage_is_new_tab_page_value->GetAsBoolean(
209 &homepage_is_new_tab_page))
210 NOTREACHED();
211 }
212
213 const base::Value* restore_on_startup_value =
214 prefs->GetUserPrefValue(prefs::kRestoreOnStartup);
215 int restore_on_startup = -1;
216 if (restore_on_startup_value) {
217 if (!restore_on_startup_value->GetAsInteger(&restore_on_startup))
218 NOTREACHED();
219 }
220
221 // If restore_on_startup has the deprecated value kPrefValueHomePage,
222 // migrate it to open the homepage on startup. If 'homepage is NTP' is set,
223 // that means just opening the NTP. If not, it means opening a one-item URL
224 // list containing the homepage.
225 if (restore_on_startup == kPrefValueHomePage) {
226 if (homepage_is_new_tab_page) {
227 prefs->SetInteger(prefs::kRestoreOnStartup, kPrefValueNewTab);
228 } else {
229 prefs->SetInteger(prefs::kRestoreOnStartup, kPrefValueURLs);
230 SetNewURLList(prefs);
231 }
232 } else if (!restore_on_startup_value && !homepage_is_new_tab_page &&
233 GetDefaultStartupType() == DEFAULT) {
234 // kRestoreOnStartup was never set by the user, but the homepage was set.
235 // Migrate to the list of URLs. (If restore_on_startup was never set,
236 // and homepage_is_new_tab_page is true, no action is needed. The new
237 // default value is "open the new tab page" which is what we want.)
238 prefs->SetInteger(prefs::kRestoreOnStartup, kPrefValueURLs);
239 SetNewURLList(prefs);
240 }
241
242 prefs->SetBoolean(prefs::kRestoreOnStartupMigrated, true);
243 }
244 }
245
246 // static
MigrateMacDefaultPrefIfNecessary(PrefService * prefs)247 void SessionStartupPref::MigrateMacDefaultPrefIfNecessary(PrefService* prefs) {
248 #if defined(OS_MACOSX)
249 DCHECK(prefs);
250 if (!restore_utils::IsWindowRestoreEnabled())
251 return;
252 // The default startup pref used to be LAST, now it is DEFAULT. Don't change
253 // the setting for existing profiles (even if the user has never changed it),
254 // but make new profiles default to DEFAULT.
255 bool old_profile_version =
256 !prefs->FindPreference(
257 prefs::kProfileCreatedByVersion)->IsDefaultValue() &&
258 Version(prefs->GetString(prefs::kProfileCreatedByVersion)).IsOlderThan(
259 "21.0.1180.0");
260 if (old_profile_version && TypeIsDefault(prefs))
261 prefs->SetInteger(prefs::kRestoreOnStartup, kPrefValueLast);
262 #endif
263 }
264
265 // static
TypeIsManaged(PrefService * prefs)266 bool SessionStartupPref::TypeIsManaged(PrefService* prefs) {
267 DCHECK(prefs);
268 const PrefService::Preference* pref_restore =
269 prefs->FindPreference(prefs::kRestoreOnStartup);
270 DCHECK(pref_restore);
271 return pref_restore->IsManaged();
272 }
273
274 // static
URLsAreManaged(PrefService * prefs)275 bool SessionStartupPref::URLsAreManaged(PrefService* prefs) {
276 DCHECK(prefs);
277 const PrefService::Preference* pref_urls =
278 prefs->FindPreference(prefs::kURLsToRestoreOnStartup);
279 DCHECK(pref_urls);
280 return pref_urls->IsManaged();
281 }
282
283 // static
TypeIsDefault(PrefService * prefs)284 bool SessionStartupPref::TypeIsDefault(PrefService* prefs) {
285 DCHECK(prefs);
286 const PrefService::Preference* pref_restore =
287 prefs->FindPreference(prefs::kRestoreOnStartup);
288 DCHECK(pref_restore);
289 return pref_restore->IsDefaultValue();
290 }
291
292 // static
PrefValueToType(int pref_value)293 SessionStartupPref::Type SessionStartupPref::PrefValueToType(int pref_value) {
294 switch (pref_value) {
295 case kPrefValueLast: return SessionStartupPref::LAST;
296 case kPrefValueURLs: return SessionStartupPref::URLS;
297 case kPrefValueHomePage: return SessionStartupPref::HOMEPAGE;
298 default: return SessionStartupPref::DEFAULT;
299 }
300 }
301
SessionStartupPref(Type type)302 SessionStartupPref::SessionStartupPref(Type type) : type(type) {}
303
~SessionStartupPref()304 SessionStartupPref::~SessionStartupPref() {}
305