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
24namespace {
25
26enum 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.
34int 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
42void 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
52void 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
66void 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
87SessionStartupPref::Type SessionStartupPref::GetDefaultStartupType() {
88#if defined(OS_CHROMEOS)
89  return SessionStartupPref::LAST;
90#else
91  return SessionStartupPref::DEFAULT;
92#endif
93}
94
95// static
96void SessionStartupPref::SetStartupPref(
97    Profile* profile,
98    const SessionStartupPref& pref) {
99  DCHECK(profile);
100  SetStartupPref(profile->GetPrefs(), pref);
101}
102
103// static
104void 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
127SessionStartupPref SessionStartupPref::GetStartupPref(Profile* profile) {
128  DCHECK(profile);
129  return GetStartupPref(profile->GetPrefs());
130}
131
132// static
133SessionStartupPref 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
152void 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
248void 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
267bool 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
276bool 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
285bool 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
294SessionStartupPref::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
303SessionStartupPref::SessionStartupPref(Type type) : type(type) {}
304
305SessionStartupPref::~SessionStartupPref() {}
306