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 "base/bind.h"
6#include "base/command_line.h"
7#include "base/mac/mac_util.h"
8#include "base/prefs/pref_service.h"
9#include "chrome/browser/background/background_mode_manager.h"
10#include "chrome/browser/browser_process.h"
11#include "chrome/common/chrome_switches.h"
12#include "chrome/common/pref_names.h"
13#include "chrome/grit/generated_resources.h"
14#include "content/public/browser/browser_thread.h"
15#include "ui/base/l10n/l10n_util.h"
16
17using content::BrowserThread;
18
19namespace {
20void SetUserRemovedLoginItemPrefOnUIThread() {
21  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
22  PrefService* service = g_browser_process->local_state();
23  service->SetBoolean(prefs::kUserRemovedLoginItem, true);
24}
25
26void SetCreatedLoginItemPrefOnUIThread() {
27  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
28  PrefService* service = g_browser_process->local_state();
29  service->SetBoolean(prefs::kChromeCreatedLoginItem, true);
30}
31
32void DisableLaunchOnStartupOnFileThread() {
33  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
34  // If the LoginItem is not hidden, it means it's user created, so don't
35  // delete it.
36  bool is_hidden = false;
37  if (base::mac::CheckLoginItemStatus(&is_hidden) && is_hidden)
38    base::mac::RemoveFromLoginItems();
39}
40
41void CheckForUserRemovedLoginItemOnFileThread() {
42  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
43  if (!base::mac::CheckLoginItemStatus(NULL)) {
44    // There's no LoginItem, so set the kUserRemovedLoginItem pref.
45    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
46                            base::Bind(SetUserRemovedLoginItemPrefOnUIThread));
47  }
48}
49
50void EnableLaunchOnStartupOnFileThread(bool need_migration) {
51  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
52  if (need_migration) {
53    // This is the first time running Chrome since the kChromeCreatedLoginItem
54    // pref was added. Initialize the status of this pref based on whether
55    // there is already a hidden login item.
56    bool is_hidden = false;
57    if (base::mac::CheckLoginItemStatus(&is_hidden)) {
58      if (is_hidden) {
59      // We already have a hidden login item, so set the kChromeCreatedLoginItem
60      // flag.
61        BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
62                                base::Bind(SetCreatedLoginItemPrefOnUIThread));
63      }
64      // LoginItem already exists - just exit.
65      return;
66    }
67  }
68
69  // Check if Chrome is already a Login Item - if not, create one.
70  if (!base::mac::CheckLoginItemStatus(NULL)) {
71    // Call back to the UI thread to set our preference so we know that Chrome
72    // created the login item (which means we are allowed to delete it later).
73    // There's a race condition here if the user disables launch on startup
74    // before our callback is run, but the user can manually disable
75    // "Open At Login" via the dock if this happens.
76    base::mac::AddToLoginItems(true);  // Hide on startup.
77    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
78                            base::Bind(SetCreatedLoginItemPrefOnUIThread));
79  }
80}
81
82}  // namespace
83
84void BackgroundModeManager::EnableLaunchOnStartup(bool should_launch) {
85  // LoginItems are associated with an executable, not with a specific
86  // user-data-dir, so only mess with the LoginItem when running with the
87  // default user-data-dir. So if a user is running multiple instances of
88  // Chrome with different user-data-dirs, they won't conflict in their
89  // use of LoginItems.
90  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUserDataDir))
91    return;
92
93  // There are a few cases we need to handle:
94  //
95  // 1) Chrome is transitioning to "launch on startup" state, and there's no
96  // login item currently. We create a new item if the kUserRemovedLoginItem
97  // and kChromeCreatedLoginItem flags are already false, and set the
98  // kChromeCreatedLoginItem flag to true. If kChromeCreatedLoginItem is
99  // already set (meaning that we created a login item that has since been
100  // deleted) then we will set the kUserRemovedLoginItem so we do not create
101  // login items in the future.
102  //
103  // 2) Chrome is transitioning to the "do not launch on startup" state. If
104  // the kChromeCreatedLoginItem flag is false, we do nothing. Otherwise, we
105  // will delete the login item if it's present, and not we will set
106  // kUserRemovedLoginItem to true to prevent future login items from being
107  // created.
108  if (should_launch) {
109    PrefService* service = g_browser_process->local_state();
110    // If the user removed the login item, don't ever create another one.
111    if (service->GetBoolean(prefs::kUserRemovedLoginItem))
112      return;
113
114    if (service->GetBoolean(prefs::kChromeCreatedLoginItem)) {
115      DCHECK(service->GetBoolean(prefs::kMigratedLoginItemPref));
116      // If we previously created a login item, we don't need to create
117      // a new one - just check to see if the user removed it so we don't
118      // ever create another one.
119      BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
120                              base::Bind(
121                                  CheckForUserRemovedLoginItemOnFileThread));
122    } else {
123      bool need_migration = !service->GetBoolean(
124          prefs::kMigratedLoginItemPref);
125      service->SetBoolean(prefs::kMigratedLoginItemPref, true);
126      BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
127                              base::Bind(EnableLaunchOnStartupOnFileThread,
128                                         need_migration));
129    }
130  } else {
131    PrefService* service = g_browser_process->local_state();
132    // If Chrome didn't create any login items, just exit.
133    if (!service->GetBoolean(prefs::kChromeCreatedLoginItem))
134      return;
135
136    // Clear the pref now that we're removing the login item.
137    service->ClearPref(prefs::kChromeCreatedLoginItem);
138
139    // If the user removed our login item, note this so we don't ever create
140    // another one.
141    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
142                            base::Bind(
143                                CheckForUserRemovedLoginItemOnFileThread));
144
145    // Call to the File thread to remove the login item since it requires
146    // accessing the disk.
147    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
148                            base::Bind(DisableLaunchOnStartupOnFileThread));
149  }
150}
151
152void BackgroundModeManager::DisplayAppInstalledNotification(
153    const extensions::Extension* extension) {
154  // TODO(atwilson): Display a platform-appropriate notification here.
155  // http://crbug.com/74970
156}
157
158base::string16 BackgroundModeManager::GetPreferencesMenuLabel() {
159  return l10n_util::GetStringUTF16(IDS_OPTIONS);
160}
161