blob: 45618b85358e2ef8db8ab56cacfc57a8cef77d35 [file] [log] [blame]
[email protected]71c0eb92012-01-03 17:57:301// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]5c9587c2008-12-09 21:20:162// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]e6b5bc22011-09-08 22:01:565#include "chrome/browser/chrome_browser_main_mac.h"
[email protected]1152b7e2009-09-14 03:26:036
[email protected]ce8c16ad2009-08-12 19:00:427#import <Cocoa/Cocoa.h>
[email protected]1152b7e2009-09-14 03:26:038
Sebastien Marchandf1349f52019-01-25 03:16:419#include "base/bind.h"
[email protected]5c9587c2008-12-09 21:20:1610#include "base/command_line.h"
[email protected]57999812013-02-24 05:40:5211#include "base/files/file_path.h"
calamityc95d7822015-05-04 02:10:1612#include "base/files/file_util.h"
[email protected]2f1804c2012-01-19 14:59:0713#include "base/mac/bundle_locations.h"
tapted676995d2016-04-18 11:32:2914#import "base/mac/foundation_util.h"
[email protected]0378bf42011-01-01 18:20:1415#include "base/mac/mac_util.h"
[email protected]a8522032013-06-24 22:51:4616#include "base/mac/scoped_nsobject.h"
Avi Drissman3730e99a2020-05-12 19:20:1817#include "base/metrics/histogram_functions.h"
[email protected]e1cb0e92010-06-15 07:23:5918#include "base/path_service.h"
Avi Drissman3730e99a2020-05-12 19:20:1819#include "base/strings/sys_string_conversions.h"
Gabriel Charette44db1422018-08-06 11:19:3320#include "base/task/post_task.h"
21#include "base/task/task_traits.h"
Gabriel Charette055039132020-02-26 23:02:0622#include "base/task/thread_pool.h"
gabb15e19072016-05-11 20:45:4123#include "base/threading/thread_task_runner_handle.h"
Avi Drissman3730e99a2020-05-12 19:20:1824#include "build/branding_buildflags.h"
[email protected]3b6aa8b62009-09-15 21:36:1125#import "chrome/browser/app_controller_mac.h"
Christopher Cameron4d18d1a2019-08-27 18:01:5326#include "chrome/browser/apps/app_shim/app_shim_listener.h"
[email protected]e8b6ca02013-07-10 18:00:5127#include "chrome/browser/browser_process.h"
Hans Wennborg63344452019-10-15 10:15:2128#include "chrome/browser/browser_process_platform_part.h"
[email protected]aaa47ee2009-11-05 21:53:0129#import "chrome/browser/chrome_browser_application_mac.h"
Christopher Lam32c684522017-07-28 03:05:0230#include "chrome/browser/first_run/first_run.h"
[email protected]c4d501e2012-03-27 20:08:0231#include "chrome/browser/mac/install_from_dmg.h"
[email protected]5950f5712011-06-20 22:15:5232#import "chrome/browser/mac/keystone_glue.h"
[email protected]7d1aaa62014-07-18 02:21:3033#include "chrome/browser/mac/mac_startup_profiler.h"
Robert Sesek124f80f92018-07-24 20:31:2934#include "chrome/browser/ui/cocoa/main_menu_builder.h"
Avi Drissman3730e99a2020-05-12 19:20:1835#include "chrome/common/channel_info.h"
[email protected]e1cb0e92010-06-15 07:23:5936#include "chrome/common/chrome_paths.h"
[email protected]d7dbe28c2010-07-29 04:33:4737#include "chrome/common/chrome_switches.h"
Sidney San Martínaa4c7d62019-10-01 02:01:2138#include "chrome/common/pref_names.h"
Christopher Cameron199deeb2018-10-17 20:29:5339#include "chrome/grit/chromium_strings.h"
Rohit Rao92f84b6a2020-03-25 14:57:5040#include "components/crash/core/app/crashpad.h"
[email protected]d6147bd2014-06-11 01:58:1941#include "components/metrics/metrics_service.h"
Tonko Sabolčec43615a92018-09-12 12:43:4142#include "components/os_crypt/os_crypt.h"
Avi Drissman3730e99a2020-05-12 19:20:1843#include "components/version_info/channel.h"
[email protected]4573fbd2011-10-31 20:25:1844#include "content/public/common/main_function_params.h"
[email protected]b39ef1cb2011-10-25 04:46:5545#include "content/public/common/result_codes.h"
Christopher Cameron199deeb2018-10-17 20:29:5346#include "ui/base/l10n/l10n_util.h"
[email protected]42ce29d2011-01-20 23:19:4647#include "ui/base/resource/resource_bundle.h"
[email protected]63942f22012-05-01 06:11:5248#include "ui/base/resource/resource_handle.h"
Leonard Grey7f2c9922018-12-05 16:03:2249#include "ui/native_theme/native_theme_mac.h"
[email protected]5c9587c2008-12-09 21:20:1650
calamityc95d7822015-05-04 02:10:1651namespace {
52
53// Writes an undocumented sentinel file that prevents Spotlight from indexing
54// below a particular path in order to reap some power savings.
55void EnsureMetadataNeverIndexFileOnFileThread(
56 const base::FilePath& user_data_dir) {
57 const char kMetadataNeverIndexFilename[] = ".metadata_never_index";
58 base::FilePath metadata_file_path =
59 user_data_dir.Append(kMetadataNeverIndexFilename);
60 if (base::PathExists(metadata_file_path))
61 return;
62
63 if (base::WriteFile(metadata_file_path, nullptr, 0) == -1)
64 DLOG(FATAL) << "Could not write .metadata_never_index file.";
65}
66
67void EnsureMetadataNeverIndexFile(const base::FilePath& user_data_dir) {
Gabriel Charette055039132020-02-26 23:02:0668 base::ThreadPool::PostTask(
Christopher Lam32c684522017-07-28 03:05:0269 FROM_HERE,
Gabriel Charette055039132020-02-26 23:02:0670 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
Christopher Lam32c684522017-07-28 03:05:0271 base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
kylechar99ef9042019-02-25 18:09:4372 base::BindOnce(&EnsureMetadataNeverIndexFileOnFileThread, user_data_dir));
calamityc95d7822015-05-04 02:10:1673}
74
Avi Drissman3730e99a2020-05-12 19:20:1875#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
76
77// These values are persisted to logs as OSXOtherChromeInstancesResult.
78// Entries should not be renumbered and numeric values should never be reused.
79enum class OtherInstancesResult {
80 kFailureDontKnowWhenOtherChromeUsed,
81 kFailureToReadPlist,
82 kNoOtherChrome,
83 kOneOtherChromeAndLastUsedWithinWeek,
84 kOneOtherChromeAndLastUsedWithinMonth,
85 kOneOtherChromeAndLastUsedMoreThanAMonthAgo,
86 kMoreThanOneOtherChromeAndLastUsedWithinWeek,
87 kMoreThanOneOtherChromeAndLastUsedWithinMonth,
88 kMoreThanOneOtherChromeAndLastUsedMoreThanAMonthAgo,
89 kMaxValue = kMoreThanOneOtherChromeAndLastUsedMoreThanAMonthAgo,
90};
91
92struct WhenLastUsed {
93 int within_last_week = 0;
94 int within_last_month = 0;
95 int before_last_month = 0;
96};
97
98OtherInstancesResult OtherInstancesResultForWhenLastUsed(
99 const WhenLastUsed& used) {
100 if (used.within_last_week + used.within_last_month + used.before_last_month ==
101 0) {
102 return OtherInstancesResult::kNoOtherChrome;
103 }
104
105 if (used.within_last_week + used.within_last_month + used.before_last_month ==
106 1) {
107 if (used.within_last_week)
108 return OtherInstancesResult::kOneOtherChromeAndLastUsedWithinWeek;
109
110 if (used.within_last_month)
111 return OtherInstancesResult::kOneOtherChromeAndLastUsedWithinMonth;
112
113 return OtherInstancesResult::kOneOtherChromeAndLastUsedMoreThanAMonthAgo;
114 }
115
116 if (used.within_last_week)
117 return OtherInstancesResult::kMoreThanOneOtherChromeAndLastUsedWithinWeek;
118
119 if (used.within_last_month)
120 return OtherInstancesResult::kMoreThanOneOtherChromeAndLastUsedWithinMonth;
121
122 return OtherInstancesResult::
123 kMoreThanOneOtherChromeAndLastUsedMoreThanAMonthAgo;
124}
125
126void RecordChromeQueryResults(NSMetadataQuery* query) {
127 __block bool other_chrome_last_used_unknown = false;
128 __block bool failed_to_read_plist = false;
129 __block WhenLastUsed same_channel;
130 __block WhenLastUsed different_channel;
131
132 NSURL* this_url = NSBundle.mainBundle.bundleURL;
133 std::string this_channel = chrome::GetChannelName();
134 NSDate* about_a_week_ago =
135 [[NSDate date] dateByAddingTimeInterval:-7 * 24 * 60 * 60];
136 NSDate* about_a_month_ago =
137 [[NSDate date] dateByAddingTimeInterval:-30 * 24 * 60 * 60];
138
139 [query enumerateResultsUsingBlock:^(id result, NSUInteger idx, BOOL* stop) {
140 // Skip this copy of Chrome. Note that NSMetadataItemURLKey is not used as
141 // it always returns nil while NSMetadataItemPathKey returns a legit path.
142 // Filed as FB7689234.
143 NSString* app_path = base::mac::ObjCCast<NSString>(
144 [result valueForAttribute:NSMetadataItemPathKey]);
145 NSURL* app_url = [NSURL fileURLWithPath:app_path isDirectory:YES];
146 if ([app_url isEqual:this_url])
147 return;
148
149 NSURL* plist_url = [[app_url URLByAppendingPathComponent:@"Contents"
150 isDirectory:YES]
151 URLByAppendingPathComponent:@"Info.plist"
152 isDirectory:NO];
153 NSDictionary* plist = [NSDictionary dictionaryWithContentsOfURL:plist_url];
154 if (!plist) {
155 failed_to_read_plist = true;
156 *stop = YES;
157 return;
158 }
159
160 // Skip any SxS-capable copies of Chrome.
161 if (plist[@"CrProductDirName"])
162 return;
163
164 WhenLastUsed* when_last_used = &different_channel;
165 if (this_channel == base::SysNSStringToUTF8(plist[@"KSChannelID"]))
166 when_last_used = &same_channel;
167
168 NSDate* last_used = base::mac::ObjCCast<NSDate>(
169 [result valueForAttribute:NSMetadataItemLastUsedDateKey]);
170 if (!last_used) {
171 other_chrome_last_used_unknown = true;
172 *stop = YES;
173 return;
174 }
175
176 if ([last_used compare:about_a_week_ago] == NSOrderedDescending)
177 ++when_last_used->within_last_week;
178 else if ([last_used compare:about_a_month_ago] == NSOrderedDescending)
179 ++when_last_used->within_last_month;
180 else
181 ++when_last_used->before_last_month;
182 }];
183
184 if (other_chrome_last_used_unknown) {
185 base::UmaHistogramEnumeration(
186 "OSX.Installation.OtherChromeInstances.SameChannel",
187 OtherInstancesResult::kFailureDontKnowWhenOtherChromeUsed);
188 base::UmaHistogramEnumeration(
189 "OSX.Installation.OtherChromeInstances.DifferentChannel",
190 OtherInstancesResult::kFailureDontKnowWhenOtherChromeUsed);
191 return;
192 }
193
194 if (failed_to_read_plist) {
195 base::UmaHistogramEnumeration(
196 "OSX.Installation.OtherChromeInstances.SameChannel",
197 OtherInstancesResult::kFailureToReadPlist);
198 base::UmaHistogramEnumeration(
199 "OSX.Installation.OtherChromeInstances.DifferentChannel",
200 OtherInstancesResult::kFailureToReadPlist);
201 return;
202 }
203
204 base::UmaHistogramEnumeration(
205 "OSX.Installation.OtherChromeInstances.SameChannel",
206 OtherInstancesResultForWhenLastUsed(same_channel));
207 base::UmaHistogramEnumeration(
208 "OSX.Installation.OtherChromeInstances.DifferentChannel",
209 OtherInstancesResultForWhenLastUsed(different_channel));
210}
211
212void ExecuteChromeQuery() {
213 __block NSMetadataQuery* query = [[NSMetadataQuery alloc] init];
214
215 __block id token = [[NSNotificationCenter defaultCenter]
216 addObserverForName:NSMetadataQueryDidFinishGatheringNotification
217 object:query
218 queue:[NSOperationQueue mainQueue]
219 usingBlock:^(NSNotification* note) {
220 [query stopQuery];
221 RecordChromeQueryResults(query);
222 [query release];
223 [[NSNotificationCenter defaultCenter] removeObserver:token];
224 }];
225
226 query.predicate =
227 [NSPredicate predicateWithFormat:
228 @"kMDItemContentType == 'com.apple.application-bundle'"
229 @"AND kMDItemCFBundleIdentifier == 'com.google.Chrome'"];
230
231 [query startQuery];
232}
233
234// Records statistics about this install of Chromium if it is a Google Chrome
235// Beta or Google Chrome Dev instance. This is to allow for decisions to be made
236// about the migration of user data directories.
237void RecordBetaAndDevStats() {
238 version_info::Channel channel = chrome::GetChannel();
239 if (channel != version_info::Channel::BETA &&
240 channel != version_info::Channel::DEV) {
241 return;
242 }
243
244 ExecuteChromeQuery();
245}
246
247#endif // GOOGLE_CHROME_BRANDING
248
calamityc95d7822015-05-04 02:10:16249} // namespace
250
[email protected]e6b5bc22011-09-08 22:01:56251// ChromeBrowserMainPartsMac ---------------------------------------------------
[email protected]1fec64352010-07-27 13:55:21252
[email protected]e6b5bc22011-09-08 22:01:56253ChromeBrowserMainPartsMac::ChromeBrowserMainPartsMac(
Ran Jia96d43d42018-05-02 17:14:53254 const content::MainFunctionParams& parameters,
Xi Han85079c22019-04-18 21:43:05255 StartupData* startup_data)
256 : ChromeBrowserMainPartsPosix(parameters, startup_data) {}
[email protected]1fec64352010-07-27 13:55:21257
[email protected]39fc5d322012-09-15 10:54:55258ChromeBrowserMainPartsMac::~ChromeBrowserMainPartsMac() {
259}
260
Scott Violet9068b4df2018-01-12 16:44:21261int ChromeBrowserMainPartsMac::PreEarlyInitialization() {
[email protected]769ddfe2014-06-13 23:13:20262 if (base::mac::WasLaunchedAsLoginItemRestoreState()) {
avi3ef9ec9e2014-12-22 22:50:17263 base::CommandLine* singleton_command_line =
264 base::CommandLine::ForCurrentProcess();
[email protected]769ddfe2014-06-13 23:13:20265 singleton_command_line->AppendSwitch(switches::kRestoreLastSession);
266 } else if (base::mac::WasLaunchedAsHiddenLoginItem()) {
avi3ef9ec9e2014-12-22 22:50:17267 base::CommandLine* singleton_command_line =
268 base::CommandLine::ForCurrentProcess();
[email protected]f967b722011-09-07 00:58:04269 singleton_command_line->AppendSwitch(switches::kNoStartupWindow);
270 }
Scott Violet9068b4df2018-01-12 16:44:21271
Scott Violet740c2632018-03-09 04:11:42272 return ChromeBrowserMainPartsPosix::PreEarlyInitialization();
Scott Violet875789e2018-02-02 07:46:48273}
[email protected]a88f6362014-03-18 04:25:35274
Scott Violet875789e2018-02-02 07:46:48275void ChromeBrowserMainPartsMac::PreMainMessageLoopStart() {
276 MacStartupProfiler::GetInstance()->Profile(
277 MacStartupProfiler::PRE_MAIN_MESSAGE_LOOP_START);
278 ChromeBrowserMainPartsPosix::PreMainMessageLoopStart();
279
280 // ChromeBrowserMainParts should have loaded the resource bundle by this
281 // point (needed to load the nib).
282 CHECK(ui::ResourceBundle::HasSharedInstance());
[email protected]a88f6362014-03-18 04:25:35283
[email protected]c4d501e2012-03-27 20:08:02284 // This is a no-op if the KeystoneRegistration framework is not present.
285 // The framework is only distributed with branded Google Chrome builds.
286 [[KeystoneGlue defaultKeystoneGlue] registerWithKeystone];
287
288 // Disk image installation is sort of a first-run task, so it shares the
[email protected]0a160a1b2013-08-08 22:20:00289 // no first run switches.
[email protected]c4d501e2012-03-27 20:08:02290 //
291 // This needs to be done after the resource bundle is initialized (for
292 // access to localizations in the UI) and after Keystone is initialized
293 // (because the installation may need to promote Keystone) but before the
294 // app controller is set up (and thus before MainMenu.nib is loaded, because
295 // the app controller assumes that a browser has been set up and will crash
296 // upon receipt of certain notifications if no browser exists), before
297 // anyone tries doing anything silly like firing off an import job, and
298 // before anything creating preferences like Local State in order for the
299 // relaunched installed application to still consider itself as first-run.
[email protected]0a160a1b2013-08-08 22:20:00300 if (!first_run::IsFirstRunSuppressed(parsed_command_line())) {
[email protected]c4d501e2012-03-27 20:08:02301 if (MaybeInstallFromDiskImage()) {
302 // The application was installed and the installed copy has been
303 // launched. This process is now obsolete. Exit.
304 exit(0);
305 }
306 }
307
Robert Sesek124f80f92018-07-24 20:31:29308 // Create the app delegate. This object is intentionally leaked as a global
309 // singleton. It is accessed through -[NSApp delegate].
310 AppController* app_controller = [[AppController alloc] init];
311 [NSApp setDelegate:app_controller];
312
Christopher Cameron199deeb2018-10-17 20:29:53313 chrome::BuildMainMenu(NSApp, app_controller,
314 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), false);
Robert Sesek124f80f92018-07-24 20:31:29315 [app_controller mainMenuCreated];
kerrnel26eeef02017-03-10 19:03:43316
Tonko Sabolčec43615a92018-09-12 12:43:41317 PrefService* local_state = g_browser_process->local_state();
318 DCHECK(local_state);
Sidney San Martínaa4c7d62019-10-01 02:01:21319
Sidney San Martínaa4c7d62019-10-01 02:01:21320 // AppKit only restores windows to their original spaces when relaunching
321 // apps after a restart, and puts them all on the current space when an app
322 // is manually quit and relaunched. If Chrome restarted itself, ask AppKit to
323 // treat this launch like a system restart and restore everything.
324 if (local_state->GetBoolean(prefs::kWasRestarted)) {
325 [NSUserDefaults.standardUserDefaults registerDefaults:@{
326 @"NSWindowRestoresWorkspaceAtLaunch" : @YES
327 }];
328 }
[email protected]f967b722011-09-07 00:58:04329}
[email protected]03d8d3e92011-09-20 06:07:11330
[email protected]7d1aaa62014-07-18 02:21:30331void ChromeBrowserMainPartsMac::PostMainMessageLoopStart() {
332 MacStartupProfiler::GetInstance()->Profile(
333 MacStartupProfiler::POST_MAIN_MESSAGE_LOOP_START);
[email protected]6ddc10972014-07-29 07:53:33334 ChromeBrowserMainPartsPosix::PostMainMessageLoopStart();
Avi Drissman3730e99a2020-05-12 19:20:18335
336#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
337 RecordBetaAndDevStats();
338#endif // GOOGLE_CHROME_BRANDING
[email protected]7d1aaa62014-07-18 02:21:30339}
340
[email protected]bdad6e62014-05-06 08:47:37341void ChromeBrowserMainPartsMac::PreProfileInit() {
[email protected]7d1aaa62014-07-18 02:21:30342 MacStartupProfiler::GetInstance()->Profile(
343 MacStartupProfiler::PRE_PROFILE_INIT);
[email protected]bdad6e62014-05-06 08:47:37344 ChromeBrowserMainPartsPosix::PreProfileInit();
mlermana312cec2015-01-28 21:13:33345
[email protected]bdad6e62014-05-06 08:47:37346 // This is called here so that the app shim socket is only created after
347 // taking the singleton lock.
Christopher Cameron4d18d1a2019-08-27 18:01:53348 g_browser_process->platform_part()->app_shim_listener()->Init();
[email protected]bdad6e62014-05-06 08:47:37349}
350
[email protected]e8b6ca02013-07-10 18:00:51351void ChromeBrowserMainPartsMac::PostProfileInit() {
[email protected]7d1aaa62014-07-18 02:21:30352 MacStartupProfiler::GetInstance()->Profile(
353 MacStartupProfiler::POST_PROFILE_INIT);
[email protected]e8b6ca02013-07-10 18:00:51354 ChromeBrowserMainPartsPosix::PostProfileInit();
markd413b2d2015-03-13 07:45:40355
[email protected]e8b6ca02013-07-10 18:00:51356 g_browser_process->metrics_service()->RecordBreakpadRegistration(
markd413b2d2015-03-13 07:45:40357 crash_reporter::GetUploadsEnabled());
calamityc95d7822015-05-04 02:10:16358
Christopher Lam32c684522017-07-28 03:05:02359 if (first_run::IsChromeFirstRun())
360 EnsureMetadataNeverIndexFile(user_data_dir());
bcwhitee9301292015-06-22 15:31:58361
borisv957d52d2016-04-13 18:35:43362 // Activation of Keystone is not automatic but done in response to the
363 // counting and reporting of profiles.
364 KeystoneGlue* glue = [KeystoneGlue defaultKeystoneGlue];
365 if (glue && ![glue isRegisteredAndActive]) {
366 // If profile loading has failed, we still need to handle other tasks
367 // like marking of the product as active.
Joshua Pawlicki9fbc8412019-03-20 16:05:12368 [glue setRegistrationActive];
borisv957d52d2016-04-13 18:35:43369 }
[email protected]e8b6ca02013-07-10 18:00:51370}
371
[email protected]03d8d3e92011-09-20 06:07:11372void ChromeBrowserMainPartsMac::DidEndMainMessageLoop() {
tapted676995d2016-04-18 11:32:29373 AppController* appController =
374 base::mac::ObjCCastStrict<AppController>([NSApp delegate]);
[email protected]03d8d3e92011-09-20 06:07:11375 [appController didEndMainMessageLoop];
376}