1 // Copyright 2012 The Chromium Authors
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"
9 #include "base/command_line.h"
10 #include "base/files/file_util.h"
11 #include "base/functional/bind.h"
12 #include "base/i18n/file_util_icu.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/task/lazy_thread_pool_task_runner.h"
18 #include "base/task/single_thread_task_runner_thread_mode.h"
19 #include "base/task/task_traits.h"
20 #include "base/threading/scoped_blocking_call.h"
21 #include "build/branding_buildflags.h"
22 #include "build/build_config.h"
23 #include "build/chromeos_buildflags.h"
24 #include "chrome/browser/policy/policy_path_parser.h"
25 #include "chrome/common/chrome_paths.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "components/prefs/pref_service.h"
28 #include "components/version_info/version_info.h"
29 #include "content/public/browser/browser_task_traits.h"
30 #include "content/public/browser/browser_thread.h"
32 #if BUILDFLAG(IS_CHROMEOS_ASH)
33 #include "ash/constants/ash_switches.h"
37 #include "base/win/windows_version.h"
38 #include "chrome/browser/shell_integration_win.h"
39 #include "chrome/installer/util/shell_util.h"
42 #if !BUILDFLAG(IS_WIN)
43 #include "chrome/common/channel_info.h"
44 #include "chrome/grit/branded_strings.h"
45 #include "ui/base/l10n/l10n_util.h"
48 using content::BrowserThread;
50 namespace shell_integration {
54 // TODO(crbug.com/773563): Remove |g_sequenced_task_runner| and use an instance
55 // field / singleton instead.
57 base::LazyThreadPoolCOMSTATaskRunner g_sequenced_task_runner =
58 LAZY_COM_STA_TASK_RUNNER_INITIALIZER(
59 base::TaskTraits(base::MayBlock()),
60 base::SingleThreadTaskRunnerThreadMode::SHARED);
62 base::LazyThreadPoolSequencedTaskRunner g_sequenced_task_runner =
63 LAZY_THREAD_POOL_SEQUENCED_TASK_RUNNER_INITIALIZER(
64 base::TaskTraits(base::MayBlock()));
67 bool IsValidDefaultWebClientState(DefaultWebClientState state) {
72 case OTHER_MODE_IS_DEFAULT:
74 case NUM_DEFAULT_STATES:
81 void RunCallback(DefaultWebClientWorkerCallback callback,
82 DefaultWebClientState state) {
83 if (!callback.is_null() && IsValidDefaultWebClientState(state)) {
84 std::move(callback).Run(state);
89 DefaultWebClientSetPermission GetDefaultWebClientSetPermission(
90 internal::WebClientSetMethod method) {
91 #if BUILDFLAG(CHROME_FOR_TESTING)
92 return SET_DEFAULT_NOT_ALLOWED;
94 return internal::GetPlatformSpecificDefaultWebClientSetPermission(method);
100 DefaultWebClientSetPermission GetDefaultBrowserSetPermission() {
101 return GetDefaultWebClientSetPermission(
102 internal::WebClientSetMethod::kDefaultBrowser);
105 DefaultWebClientSetPermission GetDefaultSchemeClientSetPermission() {
106 return GetDefaultWebClientSetPermission(
107 internal::WebClientSetMethod::kDefaultSchemeHandler);
110 bool CanSetAsDefaultBrowser() {
111 return GetDefaultBrowserSetPermission() != SET_DEFAULT_NOT_ALLOWED;
114 base::CommandLine CommandLineArgsForLauncher(
116 const std::string& extension_app_id,
117 const base::FilePath& profile_path,
118 const std::string& run_on_os_login_mode) {
119 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
120 base::BlockingType::MAY_BLOCK);
121 base::CommandLine new_cmd_line(base::CommandLine::NO_PROGRAM);
124 extension_app_id.empty() ? base::FilePath() : profile_path,
127 // If |extension_app_id| is present, we use the kAppId switch rather than
128 // the kApp switch (the launch url will be read from the extension app
130 if (!extension_app_id.empty()) {
131 new_cmd_line.AppendSwitchASCII(switches::kAppId, extension_app_id);
133 // Use '--app=url' instead of just 'url' to launch the browser with minimal
135 // Note: Do not change this flag! Old Gears shortcuts will break if you do!
136 new_cmd_line.AppendSwitchASCII(switches::kApp, url.spec());
139 if (!run_on_os_login_mode.empty()) {
140 new_cmd_line.AppendSwitchASCII(switches::kAppRunOnOsLoginMode,
141 run_on_os_login_mode);
147 void AppendProfileArgs(const base::FilePath& profile_path,
148 base::CommandLine* command_line) {
149 DCHECK(command_line);
150 const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
152 // Use the same UserDataDir for new launches that we currently have set.
153 base::FilePath user_data_dir =
154 cmd_line.GetSwitchValuePath(switches::kUserDataDir);
155 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
156 policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
158 if (!user_data_dir.empty()) {
159 // Make sure user_data_dir is an absolute path.
160 user_data_dir = base::MakeAbsoluteFilePath(user_data_dir);
161 if (!user_data_dir.empty() && base::PathExists(user_data_dir))
162 command_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir);
165 #if BUILDFLAG(IS_CHROMEOS_ASH)
166 base::FilePath profile =
167 cmd_line.GetSwitchValuePath(ash::switches::kLoginProfile);
168 if (!profile.empty())
169 command_line->AppendSwitchPath(ash::switches::kLoginProfile, profile);
171 if (!profile_path.empty())
172 command_line->AppendSwitchPath(switches::kProfileDirectory,
173 profile_path.BaseName());
177 #if !BUILDFLAG(IS_WIN)
178 std::u16string GetAppShortcutsSubdirName() {
179 if (chrome::GetChannel() == version_info::Channel::CANARY)
180 return l10n_util::GetStringUTF16(IDS_APP_SHORTCUTS_SUBDIR_NAME_CANARY);
181 return l10n_util::GetStringUTF16(IDS_APP_SHORTCUTS_SUBDIR_NAME);
183 #endif // !BUILDFLAG(IS_WIN)
185 ///////////////////////////////////////////////////////////////////////////////
186 // DefaultWebClientWorker
189 void DefaultWebClientWorker::StartCheckIsDefault(
190 DefaultWebClientWorkerCallback callback) {
191 g_sequenced_task_runner.Get()->PostTask(
192 FROM_HERE, base::BindOnce(&DefaultWebClientWorker::CheckIsDefault, this,
193 false, std::move(callback)));
196 void DefaultWebClientWorker::StartSetAsDefault(
197 DefaultWebClientWorkerCallback callback) {
198 g_sequenced_task_runner.Get()->PostTask(
199 FROM_HERE, base::BindOnce(&DefaultWebClientWorker::SetAsDefault, this,
200 std::move(callback)));
203 ///////////////////////////////////////////////////////////////////////////////
204 // DefaultWebClientWorker, protected:
206 DefaultWebClientWorker::DefaultWebClientWorker(const char* worker_name)
207 : worker_name_(worker_name) {}
209 DefaultWebClientWorker::~DefaultWebClientWorker() = default;
211 void DefaultWebClientWorker::OnCheckIsDefaultComplete(
212 DefaultWebClientState state,
213 bool is_following_set_as_default,
214 DefaultWebClientWorkerCallback callback) {
215 DCHECK_CURRENTLY_ON(BrowserThread::UI);
216 RunCallback(std::move(callback), state);
218 if (is_following_set_as_default)
219 ReportSetDefaultResult(state);
222 ///////////////////////////////////////////////////////////////////////////////
223 // DefaultWebClientWorker, private:
225 void DefaultWebClientWorker::CheckIsDefault(
226 bool is_following_set_as_default,
227 DefaultWebClientWorkerCallback callback) {
228 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
229 base::BlockingType::MAY_BLOCK);
231 DefaultWebClientState state = CheckIsDefaultImpl();
232 content::GetUIThreadTaskRunner({})->PostTask(
234 base::BindOnce(&DefaultBrowserWorker::OnCheckIsDefaultComplete, this,
235 state, is_following_set_as_default, std::move(callback)));
238 void DefaultWebClientWorker::SetAsDefault(
239 DefaultWebClientWorkerCallback callback) {
240 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
241 base::BlockingType::MAY_BLOCK);
243 // SetAsDefaultImpl will make sure the callback is executed exactly once.
244 SetAsDefaultImpl(base::BindOnce(&DefaultWebClientWorker::CheckIsDefault, this,
245 true, std::move(callback)));
248 void DefaultWebClientWorker::ReportSetDefaultResult(
249 DefaultWebClientState state) {
250 base::LinearHistogram::FactoryGet(
251 base::StringPrintf("%s.SetDefaultResult2", worker_name_), 1,
252 DefaultWebClientState::NUM_DEFAULT_STATES,
253 DefaultWebClientState::NUM_DEFAULT_STATES + 1,
254 base::HistogramBase::kUmaTargetedHistogramFlag)
258 ///////////////////////////////////////////////////////////////////////////////
259 // DefaultBrowserWorker
262 DefaultBrowserWorker::DefaultBrowserWorker()
263 : DefaultWebClientWorker("DefaultBrowser") {}
265 ///////////////////////////////////////////////////////////////////////////////
266 // DefaultBrowserWorker, private:
268 DefaultBrowserWorker::~DefaultBrowserWorker() = default;
270 DefaultWebClientState DefaultBrowserWorker::CheckIsDefaultImpl() {
271 return GetDefaultBrowser();
274 void DefaultBrowserWorker::SetAsDefaultImpl(
275 base::OnceClosure on_finished_callback) {
276 switch (GetDefaultBrowserSetPermission()) {
277 case SET_DEFAULT_NOT_ALLOWED:
278 // This is a no-op on channels where set-default is not allowed, but not
281 case SET_DEFAULT_UNATTENDED:
282 SetAsDefaultBrowser();
284 case SET_DEFAULT_INTERACTIVE:
285 #if BUILDFLAG(IS_WIN)
286 if (interactive_permitted_) {
287 win::SetAsDefaultBrowserUsingSystemSettings(
288 std::move(on_finished_callback));
289 // Early return because the function above takes care of calling
290 // `on_finished_callback`.
293 #endif // BUILDFLAG(IS_WIN)
296 std::move(on_finished_callback).Run();
299 ///////////////////////////////////////////////////////////////////////////////
300 // DefaultSchemeClientWorker
303 DefaultSchemeClientWorker::DefaultSchemeClientWorker(const std::string& scheme)
304 : DefaultWebClientWorker("DefaultSchemeClient"), scheme_(scheme) {}
306 DefaultSchemeClientWorker::DefaultSchemeClientWorker(const GURL& url)
307 : DefaultWebClientWorker("DefaultSchemeClient"),
308 scheme_(url.scheme()),
311 void DefaultSchemeClientWorker::StartCheckIsDefaultAndGetDefaultClientName(
312 DefaultSchemeHandlerWorkerCallback callback) {
313 g_sequenced_task_runner.Get()->PostTask(
316 &DefaultSchemeClientWorker::CheckIsDefaultAndGetDefaultClientName,
317 this, std::move(callback)));
320 ///////////////////////////////////////////////////////////////////////////////
321 // DefaultSchemeClientWorker, protected:
323 DefaultSchemeClientWorker::~DefaultSchemeClientWorker() = default;
325 void DefaultSchemeClientWorker::OnCheckIsDefaultAndGetDefaultClientNameComplete(
326 DefaultWebClientState state,
327 std::u16string program_name,
328 DefaultSchemeHandlerWorkerCallback callback) {
329 DCHECK_CURRENTLY_ON(BrowserThread::UI);
331 if (!callback.is_null() && IsValidDefaultWebClientState(state)) {
332 std::move(callback).Run(state, program_name);
336 ///////////////////////////////////////////////////////////////////////////////
337 // DefaultSchemeClientWorker, private:
339 void DefaultSchemeClientWorker::CheckIsDefaultAndGetDefaultClientName(
340 DefaultSchemeHandlerWorkerCallback callback) {
341 DCHECK(!url_.is_empty());
342 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
343 base::BlockingType::MAY_BLOCK);
345 DefaultWebClientState state = CheckIsDefaultImpl();
346 std::u16string program_name = GetDefaultClientNameImpl();
347 content::GetUIThreadTaskRunner({})->PostTask(
349 base::BindOnce(&DefaultSchemeClientWorker::
350 OnCheckIsDefaultAndGetDefaultClientNameComplete,
351 this, state, program_name, std::move(callback)));
354 DefaultWebClientState DefaultSchemeClientWorker::CheckIsDefaultImpl() {
355 return IsDefaultClientForScheme(scheme_);
358 std::u16string DefaultSchemeClientWorker::GetDefaultClientNameImpl() {
359 return GetApplicationNameForScheme(url_);
362 void DefaultSchemeClientWorker::SetAsDefaultImpl(
363 base::OnceClosure on_finished_callback) {
364 switch (GetDefaultSchemeClientSetPermission()) {
365 case SET_DEFAULT_NOT_ALLOWED:
366 // Not allowed, do nothing.
368 case SET_DEFAULT_UNATTENDED:
369 SetAsDefaultClientForScheme(scheme_);
371 case SET_DEFAULT_INTERACTIVE:
372 #if BUILDFLAG(IS_WIN)
373 if (interactive_permitted_) {
374 win::SetAsDefaultClientForSchemeUsingSystemSettings(
375 scheme_, std::move(on_finished_callback));
376 // Early return because the function above takes care of calling
377 // `on_finished_callback`.
380 #endif // BUILDFLAG(IS_WIN)
383 std::move(on_finished_callback).Run();
386 } // namespace shell_integration