blob: a006f94f3e087febbbd195f7d000d9c866f2788d [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]2f1804c2012-01-19 14:59:0711#include "base/mac/bundle_locations.h"
tapted676995d2016-04-18 11:32:2912#import "base/mac/foundation_util.h"
[email protected]0378bf42011-01-01 18:20:1413#include "base/mac/mac_util.h"
[email protected]a8522032013-06-24 22:51:4614#include "base/mac/scoped_nsobject.h"
Avi Drissman3730e99a2020-05-12 19:20:1815#include "base/metrics/histogram_functions.h"
[email protected]e1cb0e92010-06-15 07:23:5916#include "base/path_service.h"
Avi Drissman3730e99a2020-05-12 19:20:1817#include "base/strings/sys_string_conversions.h"
Avi Drissman3730e99a2020-05-12 19:20:1818#include "build/branding_buildflags.h"
[email protected]3b6aa8b62009-09-15 21:36:1119#import "chrome/browser/app_controller_mac.h"
Christopher Cameron4d18d1a2019-08-27 18:01:5320#include "chrome/browser/apps/app_shim/app_shim_listener.h"
[email protected]e8b6ca02013-07-10 18:00:5121#include "chrome/browser/browser_process.h"
Hans Wennborg63344452019-10-15 10:15:2122#include "chrome/browser/browser_process_platform_part.h"
[email protected]aaa47ee2009-11-05 21:53:0123#import "chrome/browser/chrome_browser_application_mac.h"
Christopher Lam32c684522017-07-28 03:05:0224#include "chrome/browser/first_run/first_run.h"
[email protected]c4d501e2012-03-27 20:08:0225#include "chrome/browser/mac/install_from_dmg.h"
[email protected]5950f5712011-06-20 22:15:5226#import "chrome/browser/mac/keystone_glue.h"
[email protected]7d1aaa62014-07-18 02:21:3027#include "chrome/browser/mac/mac_startup_profiler.h"
Robert Sesek124f80f92018-07-24 20:31:2928#include "chrome/browser/ui/cocoa/main_menu_builder.h"
Avi Drissman3730e99a2020-05-12 19:20:1829#include "chrome/common/channel_info.h"
[email protected]e1cb0e92010-06-15 07:23:5930#include "chrome/common/chrome_paths.h"
[email protected]d7dbe28c2010-07-29 04:33:4731#include "chrome/common/chrome_switches.h"
Sidney San Martínaa4c7d62019-10-01 02:01:2132#include "chrome/common/pref_names.h"
Christopher Cameron199deeb2018-10-17 20:29:5333#include "chrome/grit/chromium_strings.h"
Rohit Rao92f84b6a2020-03-25 14:57:5034#include "components/crash/core/app/crashpad.h"
[email protected]d6147bd2014-06-11 01:58:1935#include "components/metrics/metrics_service.h"
Tonko Sabolčec43615a92018-09-12 12:43:4136#include "components/os_crypt/os_crypt.h"
Avi Drissman3730e99a2020-05-12 19:20:1837#include "components/version_info/channel.h"
[email protected]4573fbd2011-10-31 20:25:1838#include "content/public/common/main_function_params.h"
[email protected]b39ef1cb2011-10-25 04:46:5539#include "content/public/common/result_codes.h"
Christopher Cameron199deeb2018-10-17 20:29:5340#include "ui/base/l10n/l10n_util.h"
[email protected]42ce29d2011-01-20 23:19:4641#include "ui/base/resource/resource_bundle.h"
[email protected]63942f22012-05-01 06:11:5242#include "ui/base/resource/resource_handle.h"
Leonard Grey7f2c9922018-12-05 16:03:2243#include "ui/native_theme/native_theme_mac.h"
[email protected]5c9587c2008-12-09 21:20:1644
calamityc95d7822015-05-04 02:10:1645namespace {
46
Avi Drissman3730e99a2020-05-12 19:20:1847#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
48
49// These values are persisted to logs as OSXOtherChromeInstancesResult.
50// Entries should not be renumbered and numeric values should never be reused.
51enum class OtherInstancesResult {
52 kFailureDontKnowWhenOtherChromeUsed,
53 kFailureToReadPlist,
54 kNoOtherChrome,
55 kOneOtherChromeAndLastUsedWithinWeek,
56 kOneOtherChromeAndLastUsedWithinMonth,
57 kOneOtherChromeAndLastUsedMoreThanAMonthAgo,
58 kMoreThanOneOtherChromeAndLastUsedWithinWeek,
59 kMoreThanOneOtherChromeAndLastUsedWithinMonth,
60 kMoreThanOneOtherChromeAndLastUsedMoreThanAMonthAgo,
61 kMaxValue = kMoreThanOneOtherChromeAndLastUsedMoreThanAMonthAgo,
62};
63
64struct WhenLastUsed {
65 int within_last_week = 0;
66 int within_last_month = 0;
67 int before_last_month = 0;
68};
69
70OtherInstancesResult OtherInstancesResultForWhenLastUsed(
71 const WhenLastUsed& used) {
72 if (used.within_last_week + used.within_last_month + used.before_last_month ==
73 0) {
74 return OtherInstancesResult::kNoOtherChrome;
75 }
76
77 if (used.within_last_week + used.within_last_month + used.before_last_month ==
78 1) {
79 if (used.within_last_week)
80 return OtherInstancesResult::kOneOtherChromeAndLastUsedWithinWeek;
81
82 if (used.within_last_month)
83 return OtherInstancesResult::kOneOtherChromeAndLastUsedWithinMonth;
84
85 return OtherInstancesResult::kOneOtherChromeAndLastUsedMoreThanAMonthAgo;
86 }
87
88 if (used.within_last_week)
89 return OtherInstancesResult::kMoreThanOneOtherChromeAndLastUsedWithinWeek;
90
91 if (used.within_last_month)
92 return OtherInstancesResult::kMoreThanOneOtherChromeAndLastUsedWithinMonth;
93
94 return OtherInstancesResult::
95 kMoreThanOneOtherChromeAndLastUsedMoreThanAMonthAgo;
96}
97
98void RecordChromeQueryResults(NSMetadataQuery* query) {
99 __block bool other_chrome_last_used_unknown = false;
100 __block bool failed_to_read_plist = false;
101 __block WhenLastUsed same_channel;
102 __block WhenLastUsed different_channel;
103
104 NSURL* this_url = NSBundle.mainBundle.bundleURL;
105 std::string this_channel = chrome::GetChannelName();
106 NSDate* about_a_week_ago =
107 [[NSDate date] dateByAddingTimeInterval:-7 * 24 * 60 * 60];
108 NSDate* about_a_month_ago =
109 [[NSDate date] dateByAddingTimeInterval:-30 * 24 * 60 * 60];
110
111 [query enumerateResultsUsingBlock:^(id result, NSUInteger idx, BOOL* stop) {
112 // Skip this copy of Chrome. Note that NSMetadataItemURLKey is not used as
113 // it always returns nil while NSMetadataItemPathKey returns a legit path.
114 // Filed as FB7689234.
115 NSString* app_path = base::mac::ObjCCast<NSString>(
116 [result valueForAttribute:NSMetadataItemPathKey]);
Avi Drissman852b58e32020-05-27 17:16:32117 if (!app_path) {
118 // It seems implausible, but there are Macs in the field for which
119 // Spotlight will find results for the query of locating Chrome but cannot
120 // actually return a path to the result. https://crbug.com/1086555
121 failed_to_read_plist = true;
122 *stop = YES;
123 return;
124 }
125
Avi Drissman3730e99a2020-05-12 19:20:18126 NSURL* app_url = [NSURL fileURLWithPath:app_path isDirectory:YES];
127 if ([app_url isEqual:this_url])
128 return;
129
130 NSURL* plist_url = [[app_url URLByAppendingPathComponent:@"Contents"
131 isDirectory:YES]
132 URLByAppendingPathComponent:@"Info.plist"
133 isDirectory:NO];
134 NSDictionary* plist = [NSDictionary dictionaryWithContentsOfURL:plist_url];
135 if (!plist) {
136 failed_to_read_plist = true;
137 *stop = YES;
138 return;
139 }
140
141 // Skip any SxS-capable copies of Chrome.
142 if (plist[@"CrProductDirName"])
143 return;
144
145 WhenLastUsed* when_last_used = &different_channel;
146 if (this_channel == base::SysNSStringToUTF8(plist[@"KSChannelID"]))
147 when_last_used = &same_channel;
148
149 NSDate* last_used = base::mac::ObjCCast<NSDate>(
150 [result valueForAttribute:NSMetadataItemLastUsedDateKey]);
151 if (!last_used) {
152 other_chrome_last_used_unknown = true;
153 *stop = YES;
154 return;
155 }
156
157 if ([last_used compare:about_a_week_ago] == NSOrderedDescending)
158 ++when_last_used->within_last_week;
159 else if ([last_used compare:about_a_month_ago] == NSOrderedDescending)
160 ++when_last_used->within_last_month;
161 else
162 ++when_last_used->before_last_month;
163 }];
164
165 if (other_chrome_last_used_unknown) {
166 base::UmaHistogramEnumeration(
167 "OSX.Installation.OtherChromeInstances.SameChannel",
168 OtherInstancesResult::kFailureDontKnowWhenOtherChromeUsed);
169 base::UmaHistogramEnumeration(
170 "OSX.Installation.OtherChromeInstances.DifferentChannel",
171 OtherInstancesResult::kFailureDontKnowWhenOtherChromeUsed);
172 return;
173 }
174
175 if (failed_to_read_plist) {
176 base::UmaHistogramEnumeration(
177 "OSX.Installation.OtherChromeInstances.SameChannel",
178 OtherInstancesResult::kFailureToReadPlist);
179 base::UmaHistogramEnumeration(
180 "OSX.Installation.OtherChromeInstances.DifferentChannel",
181 OtherInstancesResult::kFailureToReadPlist);
182 return;
183 }
184
185 base::UmaHistogramEnumeration(
186 "OSX.Installation.OtherChromeInstances.SameChannel",
187 OtherInstancesResultForWhenLastUsed(same_channel));
188 base::UmaHistogramEnumeration(
189 "OSX.Installation.OtherChromeInstances.DifferentChannel",
190 OtherInstancesResultForWhenLastUsed(different_channel));
191}
192
193void ExecuteChromeQuery() {
194 __block NSMetadataQuery* query = [[NSMetadataQuery alloc] init];
195
196 __block id token = [[NSNotificationCenter defaultCenter]
197 addObserverForName:NSMetadataQueryDidFinishGatheringNotification
198 object:query
199 queue:[NSOperationQueue mainQueue]
200 usingBlock:^(NSNotification* note) {
201 [query stopQuery];
202 RecordChromeQueryResults(query);
203 [query release];
204 [[NSNotificationCenter defaultCenter] removeObserver:token];
205 }];
206
207 query.predicate =
208 [NSPredicate predicateWithFormat:
209 @"kMDItemContentType == 'com.apple.application-bundle'"
210 @"AND kMDItemCFBundleIdentifier == 'com.google.Chrome'"];
211
212 [query startQuery];
213}
214
215// Records statistics about this install of Chromium if it is a Google Chrome
216// Beta or Google Chrome Dev instance. This is to allow for decisions to be made
217// about the migration of user data directories.
218void RecordBetaAndDevStats() {
219 version_info::Channel channel = chrome::GetChannel();
220 if (channel != version_info::Channel::BETA &&
221 channel != version_info::Channel::DEV) {
222 return;
223 }
224
225 ExecuteChromeQuery();
226}
227
228#endif // GOOGLE_CHROME_BRANDING
229
calamityc95d7822015-05-04 02:10:16230} // namespace
231
[email protected]e6b5bc22011-09-08 22:01:56232// ChromeBrowserMainPartsMac ---------------------------------------------------
[email protected]1fec64352010-07-27 13:55:21233
[email protected]e6b5bc22011-09-08 22:01:56234ChromeBrowserMainPartsMac::ChromeBrowserMainPartsMac(
Ran Jia96d43d42018-05-02 17:14:53235 const content::MainFunctionParams& parameters,
Xi Han85079c22019-04-18 21:43:05236 StartupData* startup_data)
237 : ChromeBrowserMainPartsPosix(parameters, startup_data) {}
[email protected]1fec64352010-07-27 13:55:21238
[email protected]39fc5d322012-09-15 10:54:55239ChromeBrowserMainPartsMac::~ChromeBrowserMainPartsMac() {
240}
241
Scott Violet9068b4df2018-01-12 16:44:21242int ChromeBrowserMainPartsMac::PreEarlyInitialization() {
[email protected]769ddfe2014-06-13 23:13:20243 if (base::mac::WasLaunchedAsLoginItemRestoreState()) {
avi3ef9ec9e2014-12-22 22:50:17244 base::CommandLine* singleton_command_line =
245 base::CommandLine::ForCurrentProcess();
[email protected]769ddfe2014-06-13 23:13:20246 singleton_command_line->AppendSwitch(switches::kRestoreLastSession);
247 } else if (base::mac::WasLaunchedAsHiddenLoginItem()) {
avi3ef9ec9e2014-12-22 22:50:17248 base::CommandLine* singleton_command_line =
249 base::CommandLine::ForCurrentProcess();
[email protected]f967b722011-09-07 00:58:04250 singleton_command_line->AppendSwitch(switches::kNoStartupWindow);
251 }
Scott Violet9068b4df2018-01-12 16:44:21252
Scott Violet740c2632018-03-09 04:11:42253 return ChromeBrowserMainPartsPosix::PreEarlyInitialization();
Scott Violet875789e2018-02-02 07:46:48254}
[email protected]a88f6362014-03-18 04:25:35255
Scott Violet875789e2018-02-02 07:46:48256void ChromeBrowserMainPartsMac::PreMainMessageLoopStart() {
257 MacStartupProfiler::GetInstance()->Profile(
258 MacStartupProfiler::PRE_MAIN_MESSAGE_LOOP_START);
259 ChromeBrowserMainPartsPosix::PreMainMessageLoopStart();
260
261 // ChromeBrowserMainParts should have loaded the resource bundle by this
262 // point (needed to load the nib).
263 CHECK(ui::ResourceBundle::HasSharedInstance());
[email protected]a88f6362014-03-18 04:25:35264
[email protected]c4d501e2012-03-27 20:08:02265 // This is a no-op if the KeystoneRegistration framework is not present.
266 // The framework is only distributed with branded Google Chrome builds.
267 [[KeystoneGlue defaultKeystoneGlue] registerWithKeystone];
268
269 // Disk image installation is sort of a first-run task, so it shares the
[email protected]0a160a1b2013-08-08 22:20:00270 // no first run switches.
[email protected]c4d501e2012-03-27 20:08:02271 //
272 // This needs to be done after the resource bundle is initialized (for
273 // access to localizations in the UI) and after Keystone is initialized
274 // (because the installation may need to promote Keystone) but before the
275 // app controller is set up (and thus before MainMenu.nib is loaded, because
276 // the app controller assumes that a browser has been set up and will crash
277 // upon receipt of certain notifications if no browser exists), before
278 // anyone tries doing anything silly like firing off an import job, and
279 // before anything creating preferences like Local State in order for the
280 // relaunched installed application to still consider itself as first-run.
[email protected]0a160a1b2013-08-08 22:20:00281 if (!first_run::IsFirstRunSuppressed(parsed_command_line())) {
[email protected]c4d501e2012-03-27 20:08:02282 if (MaybeInstallFromDiskImage()) {
283 // The application was installed and the installed copy has been
284 // launched. This process is now obsolete. Exit.
285 exit(0);
286 }
287 }
288
Robert Sesek124f80f92018-07-24 20:31:29289 // Create the app delegate. This object is intentionally leaked as a global
290 // singleton. It is accessed through -[NSApp delegate].
291 AppController* app_controller = [[AppController alloc] init];
292 [NSApp setDelegate:app_controller];
293
Christopher Cameron199deeb2018-10-17 20:29:53294 chrome::BuildMainMenu(NSApp, app_controller,
295 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), false);
Robert Sesek124f80f92018-07-24 20:31:29296 [app_controller mainMenuCreated];
kerrnel26eeef02017-03-10 19:03:43297
Tonko Sabolčec43615a92018-09-12 12:43:41298 PrefService* local_state = g_browser_process->local_state();
299 DCHECK(local_state);
Sidney San Martínaa4c7d62019-10-01 02:01:21300
Sidney San Martínaa4c7d62019-10-01 02:01:21301 // AppKit only restores windows to their original spaces when relaunching
302 // apps after a restart, and puts them all on the current space when an app
303 // is manually quit and relaunched. If Chrome restarted itself, ask AppKit to
304 // treat this launch like a system restart and restore everything.
305 if (local_state->GetBoolean(prefs::kWasRestarted)) {
306 [NSUserDefaults.standardUserDefaults registerDefaults:@{
307 @"NSWindowRestoresWorkspaceAtLaunch" : @YES
308 }];
309 }
[email protected]f967b722011-09-07 00:58:04310}
[email protected]03d8d3e92011-09-20 06:07:11311
[email protected]7d1aaa62014-07-18 02:21:30312void ChromeBrowserMainPartsMac::PostMainMessageLoopStart() {
313 MacStartupProfiler::GetInstance()->Profile(
314 MacStartupProfiler::POST_MAIN_MESSAGE_LOOP_START);
[email protected]6ddc10972014-07-29 07:53:33315 ChromeBrowserMainPartsPosix::PostMainMessageLoopStart();
Avi Drissman3730e99a2020-05-12 19:20:18316
317#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
318 RecordBetaAndDevStats();
319#endif // GOOGLE_CHROME_BRANDING
[email protected]7d1aaa62014-07-18 02:21:30320}
321
[email protected]bdad6e62014-05-06 08:47:37322void ChromeBrowserMainPartsMac::PreProfileInit() {
[email protected]7d1aaa62014-07-18 02:21:30323 MacStartupProfiler::GetInstance()->Profile(
324 MacStartupProfiler::PRE_PROFILE_INIT);
[email protected]bdad6e62014-05-06 08:47:37325 ChromeBrowserMainPartsPosix::PreProfileInit();
mlermana312cec2015-01-28 21:13:33326
[email protected]bdad6e62014-05-06 08:47:37327 // This is called here so that the app shim socket is only created after
328 // taking the singleton lock.
Christopher Cameron4d18d1a2019-08-27 18:01:53329 g_browser_process->platform_part()->app_shim_listener()->Init();
[email protected]bdad6e62014-05-06 08:47:37330}
331
[email protected]e8b6ca02013-07-10 18:00:51332void ChromeBrowserMainPartsMac::PostProfileInit() {
[email protected]7d1aaa62014-07-18 02:21:30333 MacStartupProfiler::GetInstance()->Profile(
334 MacStartupProfiler::POST_PROFILE_INIT);
[email protected]e8b6ca02013-07-10 18:00:51335 ChromeBrowserMainPartsPosix::PostProfileInit();
markd413b2d2015-03-13 07:45:40336
[email protected]e8b6ca02013-07-10 18:00:51337 g_browser_process->metrics_service()->RecordBreakpadRegistration(
markd413b2d2015-03-13 07:45:40338 crash_reporter::GetUploadsEnabled());
calamityc95d7822015-05-04 02:10:16339
borisv957d52d2016-04-13 18:35:43340 // Activation of Keystone is not automatic but done in response to the
341 // counting and reporting of profiles.
342 KeystoneGlue* glue = [KeystoneGlue defaultKeystoneGlue];
343 if (glue && ![glue isRegisteredAndActive]) {
344 // If profile loading has failed, we still need to handle other tasks
345 // like marking of the product as active.
Joshua Pawlicki9fbc8412019-03-20 16:05:12346 [glue setRegistrationActive];
borisv957d52d2016-04-13 18:35:43347 }
[email protected]e8b6ca02013-07-10 18:00:51348}
349
[email protected]03d8d3e92011-09-20 06:07:11350void ChromeBrowserMainPartsMac::DidEndMainMessageLoop() {
tapted676995d2016-04-18 11:32:29351 AppController* appController =
352 base::mac::ObjCCastStrict<AppController>([NSApp delegate]);
[email protected]03d8d3e92011-09-20 06:07:11353 [appController didEndMainMessageLoop];
354}