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.
5 #include "chrome/browser/shell_integration.h"
8 #include "base/command_line.h"
9 #include "base/files/file_util.h"
10 #include "base/i18n/file_util_icu.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/task/lazy_thread_pool_task_runner.h"
16 #include "base/task/single_thread_task_runner_thread_mode.h"
17 #include "base/task/task_traits.h"
18 #include "base/threading/scoped_blocking_call.h"
19 #include "build/build_config.h"
20 #include "chrome/browser/policy/policy_path_parser.h"
21 #include "chrome/common/chrome_paths.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "components/prefs/pref_service.h"
24 #include "components/version_info/version_info.h"
25 #include "content/public/browser/browser_task_traits.h"
26 #include "content/public/browser/browser_thread.h"
28 #if defined(OS_CHROMEOS)
29 #include "chromeos/constants/chromeos_switches.h"
33 #include "base/win/windows_version.h"
34 #include "chrome/browser/shell_integration_win.h"
35 #include "chrome/installer/util/shell_util.h"
39 #include "chrome/common/channel_info.h"
40 #include "chrome/grit/chromium_strings.h"
41 #include "ui/base/l10n/l10n_util.h"
44 using content::BrowserThread;
46 namespace shell_integration {
50 const struct AppModeInfo* gAppModeInfo = nullptr;
52 // TODO(crbug.com/773563): Remove |g_sequenced_task_runner| and use an instance
53 // field / singleton instead.
55 base::LazyThreadPoolCOMSTATaskRunner g_sequenced_task_runner =
56 LAZY_COM_STA_TASK_RUNNER_INITIALIZER(
57 base::TaskTraits(base::MayBlock()),
58 base::SingleThreadTaskRunnerThreadMode::SHARED);
60 base::LazyThreadPoolSequencedTaskRunner g_sequenced_task_runner =
61 LAZY_THREAD_POOL_SEQUENCED_TASK_RUNNER_INITIALIZER(
62 base::TaskTraits(base::MayBlock()));
67 bool CanSetAsDefaultBrowser() {
68 return GetDefaultWebClientSetPermission() != SET_DEFAULT_NOT_ALLOWED;
72 bool IsElevationNeededForSettingDefaultProtocolClient() {
75 #endif // !defined(OS_WIN)
77 void SetAppModeInfo(const struct AppModeInfo* info) {
81 const struct AppModeInfo* AppModeInfo() {
85 bool IsRunningInAppMode() {
86 return gAppModeInfo != NULL;
89 base::CommandLine CommandLineArgsForLauncher(
91 const std::string& extension_app_id,
92 const base::FilePath& profile_path) {
93 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
94 base::BlockingType::MAY_BLOCK);
95 base::CommandLine new_cmd_line(base::CommandLine::NO_PROGRAM);
98 extension_app_id.empty() ? base::FilePath() : profile_path,
101 // If |extension_app_id| is present, we use the kAppId switch rather than
102 // the kApp switch (the launch url will be read from the extension app
104 if (!extension_app_id.empty()) {
105 new_cmd_line.AppendSwitchASCII(switches::kAppId, extension_app_id);
107 // Use '--app=url' instead of just 'url' to launch the browser with minimal
109 // Note: Do not change this flag! Old Gears shortcuts will break if you do!
110 new_cmd_line.AppendSwitchASCII(switches::kApp, url.spec());
115 void AppendProfileArgs(const base::FilePath& profile_path,
116 base::CommandLine* command_line) {
117 DCHECK(command_line);
118 const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
120 // Use the same UserDataDir for new launches that we currently have set.
121 base::FilePath user_data_dir =
122 cmd_line.GetSwitchValuePath(switches::kUserDataDir);
123 #if defined(OS_MACOSX) || defined(OS_WIN)
124 policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
126 if (!user_data_dir.empty()) {
127 // Make sure user_data_dir is an absolute path.
128 user_data_dir = base::MakeAbsoluteFilePath(user_data_dir);
129 if (!user_data_dir.empty() && base::PathExists(user_data_dir))
130 command_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir);
133 #if defined(OS_CHROMEOS)
134 base::FilePath profile = cmd_line.GetSwitchValuePath(
135 chromeos::switches::kLoginProfile);
136 if (!profile.empty())
137 command_line->AppendSwitchPath(chromeos::switches::kLoginProfile, profile);
139 if (!profile_path.empty())
140 command_line->AppendSwitchPath(switches::kProfileDirectory,
141 profile_path.BaseName());
146 base::string16 GetAppShortcutsSubdirName() {
147 if (chrome::GetChannel() == version_info::Channel::CANARY)
148 return l10n_util::GetStringUTF16(IDS_APP_SHORTCUTS_SUBDIR_NAME_CANARY);
149 return l10n_util::GetStringUTF16(IDS_APP_SHORTCUTS_SUBDIR_NAME);
151 #endif // !defined(OS_WIN)
153 ///////////////////////////////////////////////////////////////////////////////
154 // DefaultWebClientWorker
157 void DefaultWebClientWorker::StartCheckIsDefault() {
158 g_sequenced_task_runner.Get()->PostTask(
160 base::BindOnce(&DefaultWebClientWorker::CheckIsDefault, this, false));
163 void DefaultWebClientWorker::StartSetAsDefault() {
164 g_sequenced_task_runner.Get()->PostTask(
165 FROM_HERE, base::BindOnce(&DefaultWebClientWorker::SetAsDefault, this));
168 ///////////////////////////////////////////////////////////////////////////////
169 // DefaultWebClientWorker, protected:
171 DefaultWebClientWorker::DefaultWebClientWorker(
172 const DefaultWebClientWorkerCallback& callback,
173 const char* worker_name)
174 : callback_(callback), worker_name_(worker_name) {}
176 DefaultWebClientWorker::~DefaultWebClientWorker() = default;
178 void DefaultWebClientWorker::OnCheckIsDefaultComplete(
179 DefaultWebClientState state,
180 bool is_following_set_as_default) {
181 DCHECK_CURRENTLY_ON(BrowserThread::UI);
184 if (is_following_set_as_default)
185 ReportSetDefaultResult(state);
188 ///////////////////////////////////////////////////////////////////////////////
189 // DefaultWebClientWorker, private:
191 void DefaultWebClientWorker::CheckIsDefault(bool is_following_set_as_default) {
192 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
193 base::BlockingType::MAY_BLOCK);
195 DefaultWebClientState state = CheckIsDefaultImpl();
196 content::GetUIThreadTaskRunner({})->PostTask(
197 FROM_HERE, base::BindOnce(&DefaultBrowserWorker::OnCheckIsDefaultComplete,
198 this, state, is_following_set_as_default));
201 void DefaultWebClientWorker::SetAsDefault() {
202 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
203 base::BlockingType::MAY_BLOCK);
205 // SetAsDefaultImpl will make sure the callback is executed exactly once.
207 base::Bind(&DefaultWebClientWorker::CheckIsDefault, this, true));
210 void DefaultWebClientWorker::ReportSetDefaultResult(
211 DefaultWebClientState state) {
212 base::LinearHistogram::FactoryGet(
213 base::StringPrintf("%s.SetDefaultResult2", worker_name_), 1,
214 DefaultWebClientState::NUM_DEFAULT_STATES,
215 DefaultWebClientState::NUM_DEFAULT_STATES + 1,
216 base::HistogramBase::kUmaTargetedHistogramFlag)
220 void DefaultWebClientWorker::UpdateUI(DefaultWebClientState state) {
221 if (!callback_.is_null()) {
225 case UNKNOWN_DEFAULT:
226 case OTHER_MODE_IS_DEFAULT:
227 callback_.Run(state);
229 case NUM_DEFAULT_STATES:
236 ///////////////////////////////////////////////////////////////////////////////
237 // DefaultBrowserWorker
240 DefaultBrowserWorker::DefaultBrowserWorker(
241 const DefaultWebClientWorkerCallback& callback)
242 : DefaultWebClientWorker(callback, "DefaultBrowser") {}
244 ///////////////////////////////////////////////////////////////////////////////
245 // DefaultBrowserWorker, private:
247 DefaultBrowserWorker::~DefaultBrowserWorker() = default;
249 DefaultWebClientState DefaultBrowserWorker::CheckIsDefaultImpl() {
250 return GetDefaultBrowser();
253 void DefaultBrowserWorker::SetAsDefaultImpl(
254 const base::Closure& on_finished_callback) {
255 switch (GetDefaultWebClientSetPermission()) {
256 case SET_DEFAULT_NOT_ALLOWED:
259 case SET_DEFAULT_UNATTENDED:
260 SetAsDefaultBrowser();
262 case SET_DEFAULT_INTERACTIVE:
264 if (interactive_permitted_) {
265 switch (ShellUtil::GetInteractiveSetDefaultMode()) {
266 case ShellUtil::INTENT_PICKER:
267 win::SetAsDefaultBrowserUsingIntentPicker();
269 case ShellUtil::SYSTEM_SETTINGS:
270 win::SetAsDefaultBrowserUsingSystemSettings(on_finished_callback);
271 // Early return because the function above takes care of calling
272 // |on_finished_callback|.
276 #endif // defined(OS_WIN)
279 on_finished_callback.Run();
282 ///////////////////////////////////////////////////////////////////////////////
283 // DefaultProtocolClientWorker
286 DefaultProtocolClientWorker::DefaultProtocolClientWorker(
287 const DefaultWebClientWorkerCallback& callback,
288 const std::string& protocol)
289 : DefaultWebClientWorker(callback, "DefaultProtocolClient"),
290 protocol_(protocol) {}
292 ///////////////////////////////////////////////////////////////////////////////
293 // DefaultProtocolClientWorker, protected:
295 DefaultProtocolClientWorker::~DefaultProtocolClientWorker() = default;
297 ///////////////////////////////////////////////////////////////////////////////
298 // DefaultProtocolClientWorker, private:
300 DefaultWebClientState DefaultProtocolClientWorker::CheckIsDefaultImpl() {
301 return IsDefaultProtocolClient(protocol_);
304 void DefaultProtocolClientWorker::SetAsDefaultImpl(
305 const base::Closure& on_finished_callback) {
306 switch (GetDefaultWebClientSetPermission()) {
307 case SET_DEFAULT_NOT_ALLOWED:
308 // Not allowed, do nothing.
310 case SET_DEFAULT_UNATTENDED:
311 SetAsDefaultProtocolClient(protocol_);
313 case SET_DEFAULT_INTERACTIVE:
315 if (interactive_permitted_) {
316 switch (ShellUtil::GetInteractiveSetDefaultMode()) {
317 case ShellUtil::INTENT_PICKER:
318 win::SetAsDefaultProtocolClientUsingIntentPicker(protocol_);
320 case ShellUtil::SYSTEM_SETTINGS:
321 win::SetAsDefaultProtocolClientUsingSystemSettings(
322 protocol_, on_finished_callback);
323 // Early return because the function above takes care of calling
324 // |on_finished_callback|.
328 #endif // defined(OS_WIN)
331 on_finished_callback.Run();
334 } // namespace shell_integration