1*6777b538SAndroid Build Coastguard Worker// Copyright 2012 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker// Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker// found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Worker 5*6777b538SAndroid Build Coastguard Worker#include "base/mac/mac_util.h" 6*6777b538SAndroid Build Coastguard Worker 7*6777b538SAndroid Build Coastguard Worker#import <Cocoa/Cocoa.h> 8*6777b538SAndroid Build Coastguard Worker#include <CoreServices/CoreServices.h> 9*6777b538SAndroid Build Coastguard Worker#import <IOKit/IOKitLib.h> 10*6777b538SAndroid Build Coastguard Worker#include <errno.h> 11*6777b538SAndroid Build Coastguard Worker#include <stddef.h> 12*6777b538SAndroid Build Coastguard Worker#include <string.h> 13*6777b538SAndroid Build Coastguard Worker#include <sys/sysctl.h> 14*6777b538SAndroid Build Coastguard Worker#include <sys/types.h> 15*6777b538SAndroid Build Coastguard Worker#include <sys/utsname.h> 16*6777b538SAndroid Build Coastguard Worker#include <sys/xattr.h> 17*6777b538SAndroid Build Coastguard Worker 18*6777b538SAndroid Build Coastguard Worker#include <string> 19*6777b538SAndroid Build Coastguard Worker#include <string_view> 20*6777b538SAndroid Build Coastguard Worker#include <vector> 21*6777b538SAndroid Build Coastguard Worker 22*6777b538SAndroid Build Coastguard Worker#include "base/apple/bridging.h" 23*6777b538SAndroid Build Coastguard Worker#include "base/apple/bundle_locations.h" 24*6777b538SAndroid Build Coastguard Worker#include "base/apple/foundation_util.h" 25*6777b538SAndroid Build Coastguard Worker#include "base/apple/osstatus_logging.h" 26*6777b538SAndroid Build Coastguard Worker#include "base/apple/scoped_cftyperef.h" 27*6777b538SAndroid Build Coastguard Worker#include "base/check.h" 28*6777b538SAndroid Build Coastguard Worker#include "base/files/file_path.h" 29*6777b538SAndroid Build Coastguard Worker#include "base/logging.h" 30*6777b538SAndroid Build Coastguard Worker#include "base/mac/scoped_aedesc.h" 31*6777b538SAndroid Build Coastguard Worker#include "base/mac/scoped_ioobject.h" 32*6777b538SAndroid Build Coastguard Worker#include "base/posix/sysctl.h" 33*6777b538SAndroid Build Coastguard Worker#include "base/strings/string_number_conversions.h" 34*6777b538SAndroid Build Coastguard Worker 35*6777b538SAndroid Build Coastguard Worker#include "base/strings/string_split.h" 36*6777b538SAndroid Build Coastguard Worker#include "base/strings/string_util.h" 37*6777b538SAndroid Build Coastguard Worker#include "base/strings/sys_string_conversions.h" 38*6777b538SAndroid Build Coastguard Worker#include "base/threading/scoped_blocking_call.h" 39*6777b538SAndroid Build Coastguard Worker#include "build/build_config.h" 40*6777b538SAndroid Build Coastguard Worker 41*6777b538SAndroid Build Coastguard Workernamespace base::mac { 42*6777b538SAndroid Build Coastguard Worker 43*6777b538SAndroid Build Coastguard Workernamespace { 44*6777b538SAndroid Build Coastguard Worker 45*6777b538SAndroid Build Coastguard Workerclass LoginItemsFileList { 46*6777b538SAndroid Build Coastguard Worker public: 47*6777b538SAndroid Build Coastguard Worker LoginItemsFileList() = default; 48*6777b538SAndroid Build Coastguard Worker LoginItemsFileList(const LoginItemsFileList&) = delete; 49*6777b538SAndroid Build Coastguard Worker LoginItemsFileList& operator=(const LoginItemsFileList&) = delete; 50*6777b538SAndroid Build Coastguard Worker ~LoginItemsFileList() = default; 51*6777b538SAndroid Build Coastguard Worker 52*6777b538SAndroid Build Coastguard Worker [[nodiscard]] bool Initialize() { 53*6777b538SAndroid Build Coastguard Worker DCHECK(!login_items_) << __func__ << " called more than once."; 54*6777b538SAndroid Build Coastguard Worker // The LSSharedFileList suite of functions has been deprecated. Instead, 55*6777b538SAndroid Build Coastguard Worker // a LoginItems helper should be registered with SMLoginItemSetEnabled() 56*6777b538SAndroid Build Coastguard Worker // https://crbug.com/1154377. 57*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic push 58*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic ignored "-Wdeprecated-declarations" 59*6777b538SAndroid Build Coastguard Worker login_items_.reset(LSSharedFileListCreate( 60*6777b538SAndroid Build Coastguard Worker nullptr, kLSSharedFileListSessionLoginItems, nullptr)); 61*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic pop 62*6777b538SAndroid Build Coastguard Worker DLOG_IF(ERROR, !login_items_.get()) << "Couldn't get a Login Items list."; 63*6777b538SAndroid Build Coastguard Worker return login_items_.get(); 64*6777b538SAndroid Build Coastguard Worker } 65*6777b538SAndroid Build Coastguard Worker 66*6777b538SAndroid Build Coastguard Worker LSSharedFileListRef GetLoginFileList() { 67*6777b538SAndroid Build Coastguard Worker DCHECK(login_items_) << "Initialize() failed or not called."; 68*6777b538SAndroid Build Coastguard Worker return login_items_.get(); 69*6777b538SAndroid Build Coastguard Worker } 70*6777b538SAndroid Build Coastguard Worker 71*6777b538SAndroid Build Coastguard Worker // Looks into Shared File Lists corresponding to Login Items for the item 72*6777b538SAndroid Build Coastguard Worker // representing the specified bundle. If such an item is found, returns a 73*6777b538SAndroid Build Coastguard Worker // retained reference to it. Caller is responsible for releasing the 74*6777b538SAndroid Build Coastguard Worker // reference. 75*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<LSSharedFileListItemRef> GetLoginItemForApp( 76*6777b538SAndroid Build Coastguard Worker NSURL* url) { 77*6777b538SAndroid Build Coastguard Worker DCHECK(login_items_) << "Initialize() failed or not called."; 78*6777b538SAndroid Build Coastguard Worker 79*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic push // https://crbug.com/1154377 80*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic ignored "-Wdeprecated-declarations" 81*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<CFArrayRef> login_items_array( 82*6777b538SAndroid Build Coastguard Worker LSSharedFileListCopySnapshot(login_items_.get(), /*inList=*/nullptr)); 83*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic pop 84*6777b538SAndroid Build Coastguard Worker 85*6777b538SAndroid Build Coastguard Worker for (CFIndex i = 0; i < CFArrayGetCount(login_items_array.get()); ++i) { 86*6777b538SAndroid Build Coastguard Worker LSSharedFileListItemRef item = 87*6777b538SAndroid Build Coastguard Worker (LSSharedFileListItemRef)CFArrayGetValueAtIndex( 88*6777b538SAndroid Build Coastguard Worker login_items_array.get(), i); 89*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic push // https://crbug.com/1154377 90*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic ignored "-Wdeprecated-declarations" 91*6777b538SAndroid Build Coastguard Worker // kLSSharedFileListDoNotMountVolumes is used so that we don't trigger 92*6777b538SAndroid Build Coastguard Worker // mounting when it's not expected by a user. Just listing the login 93*6777b538SAndroid Build Coastguard Worker // items should not cause any side-effects. 94*6777b538SAndroid Build Coastguard Worker NSURL* item_url = 95*6777b538SAndroid Build Coastguard Worker apple::CFToNSOwnershipCast(LSSharedFileListItemCopyResolvedURL( 96*6777b538SAndroid Build Coastguard Worker item, kLSSharedFileListDoNotMountVolumes, /*outError=*/nullptr)); 97*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic pop 98*6777b538SAndroid Build Coastguard Worker 99*6777b538SAndroid Build Coastguard Worker if (item_url && [item_url isEqual:url]) { 100*6777b538SAndroid Build Coastguard Worker return apple::ScopedCFTypeRef<LSSharedFileListItemRef>( 101*6777b538SAndroid Build Coastguard Worker item, base::scoped_policy::RETAIN); 102*6777b538SAndroid Build Coastguard Worker } 103*6777b538SAndroid Build Coastguard Worker } 104*6777b538SAndroid Build Coastguard Worker 105*6777b538SAndroid Build Coastguard Worker return apple::ScopedCFTypeRef<LSSharedFileListItemRef>(); 106*6777b538SAndroid Build Coastguard Worker } 107*6777b538SAndroid Build Coastguard Worker 108*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<LSSharedFileListItemRef> GetLoginItemForMainApp() { 109*6777b538SAndroid Build Coastguard Worker NSURL* url = [NSURL fileURLWithPath:base::apple::MainBundle().bundlePath]; 110*6777b538SAndroid Build Coastguard Worker return GetLoginItemForApp(url); 111*6777b538SAndroid Build Coastguard Worker } 112*6777b538SAndroid Build Coastguard Worker 113*6777b538SAndroid Build Coastguard Worker private: 114*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<LSSharedFileListRef> login_items_; 115*6777b538SAndroid Build Coastguard Worker}; 116*6777b538SAndroid Build Coastguard Worker 117*6777b538SAndroid Build Coastguard Workerbool IsHiddenLoginItem(LSSharedFileListItemRef item) { 118*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic push // https://crbug.com/1154377 119*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic ignored "-Wdeprecated-declarations" 120*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<CFBooleanRef> hidden( 121*6777b538SAndroid Build Coastguard Worker reinterpret_cast<CFBooleanRef>(LSSharedFileListItemCopyProperty( 122*6777b538SAndroid Build Coastguard Worker item, kLSSharedFileListLoginItemHidden))); 123*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic pop 124*6777b538SAndroid Build Coastguard Worker 125*6777b538SAndroid Build Coastguard Worker return hidden && hidden.get() == kCFBooleanTrue; 126*6777b538SAndroid Build Coastguard Worker} 127*6777b538SAndroid Build Coastguard Worker 128*6777b538SAndroid Build Coastguard Worker} // namespace 129*6777b538SAndroid Build Coastguard Worker 130*6777b538SAndroid Build Coastguard WorkerCGColorSpaceRef GetSRGBColorSpace() { 131*6777b538SAndroid Build Coastguard Worker // Leaked. That's OK, it's scoped to the lifetime of the application. 132*6777b538SAndroid Build Coastguard Worker static CGColorSpaceRef g_color_space_sRGB = 133*6777b538SAndroid Build Coastguard Worker CGColorSpaceCreateWithName(kCGColorSpaceSRGB); 134*6777b538SAndroid Build Coastguard Worker DLOG_IF(ERROR, !g_color_space_sRGB) << "Couldn't get the sRGB color space"; 135*6777b538SAndroid Build Coastguard Worker return g_color_space_sRGB; 136*6777b538SAndroid Build Coastguard Worker} 137*6777b538SAndroid Build Coastguard Worker 138*6777b538SAndroid Build Coastguard Workervoid AddToLoginItems(const FilePath& app_bundle_file_path, 139*6777b538SAndroid Build Coastguard Worker bool hide_on_startup) { 140*6777b538SAndroid Build Coastguard Worker LoginItemsFileList login_items; 141*6777b538SAndroid Build Coastguard Worker if (!login_items.Initialize()) { 142*6777b538SAndroid Build Coastguard Worker return; 143*6777b538SAndroid Build Coastguard Worker } 144*6777b538SAndroid Build Coastguard Worker 145*6777b538SAndroid Build Coastguard Worker NSURL* app_bundle_url = base::apple::FilePathToNSURL(app_bundle_file_path); 146*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<LSSharedFileListItemRef> item = 147*6777b538SAndroid Build Coastguard Worker login_items.GetLoginItemForApp(app_bundle_url); 148*6777b538SAndroid Build Coastguard Worker 149*6777b538SAndroid Build Coastguard Worker if (item.get() && (IsHiddenLoginItem(item.get()) == hide_on_startup)) { 150*6777b538SAndroid Build Coastguard Worker return; // There already is a login item with required hide flag. 151*6777b538SAndroid Build Coastguard Worker } 152*6777b538SAndroid Build Coastguard Worker 153*6777b538SAndroid Build Coastguard Worker // Remove the old item, it has wrong hide flag, we'll create a new one. 154*6777b538SAndroid Build Coastguard Worker if (item.get()) { 155*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic push // https://crbug.com/1154377 156*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic ignored "-Wdeprecated-declarations" 157*6777b538SAndroid Build Coastguard Worker LSSharedFileListItemRemove(login_items.GetLoginFileList(), item.get()); 158*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic pop 159*6777b538SAndroid Build Coastguard Worker } 160*6777b538SAndroid Build Coastguard Worker 161*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic push // https://crbug.com/1154377 162*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic ignored "-Wdeprecated-declarations" 163*6777b538SAndroid Build Coastguard Worker BOOL hide = hide_on_startup ? YES : NO; 164*6777b538SAndroid Build Coastguard Worker NSDictionary* properties = 165*6777b538SAndroid Build Coastguard Worker @{apple::CFToNSPtrCast(kLSSharedFileListLoginItemHidden) : @(hide)}; 166*6777b538SAndroid Build Coastguard Worker 167*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<LSSharedFileListItemRef> new_item( 168*6777b538SAndroid Build Coastguard Worker LSSharedFileListInsertItemURL( 169*6777b538SAndroid Build Coastguard Worker login_items.GetLoginFileList(), kLSSharedFileListItemLast, 170*6777b538SAndroid Build Coastguard Worker /*inDisplayName=*/nullptr, 171*6777b538SAndroid Build Coastguard Worker /*inIconRef=*/nullptr, apple::NSToCFPtrCast(app_bundle_url), 172*6777b538SAndroid Build Coastguard Worker apple::NSToCFPtrCast(properties), /*inPropertiesToClear=*/nullptr)); 173*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic pop 174*6777b538SAndroid Build Coastguard Worker 175*6777b538SAndroid Build Coastguard Worker if (!new_item.get()) { 176*6777b538SAndroid Build Coastguard Worker DLOG(ERROR) << "Couldn't insert current app into Login Items list."; 177*6777b538SAndroid Build Coastguard Worker } 178*6777b538SAndroid Build Coastguard Worker} 179*6777b538SAndroid Build Coastguard Worker 180*6777b538SAndroid Build Coastguard Workervoid RemoveFromLoginItems(const FilePath& app_bundle_file_path) { 181*6777b538SAndroid Build Coastguard Worker LoginItemsFileList login_items; 182*6777b538SAndroid Build Coastguard Worker if (!login_items.Initialize()) { 183*6777b538SAndroid Build Coastguard Worker return; 184*6777b538SAndroid Build Coastguard Worker } 185*6777b538SAndroid Build Coastguard Worker 186*6777b538SAndroid Build Coastguard Worker NSURL* app_bundle_url = base::apple::FilePathToNSURL(app_bundle_file_path); 187*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<LSSharedFileListItemRef> item = 188*6777b538SAndroid Build Coastguard Worker login_items.GetLoginItemForApp(app_bundle_url); 189*6777b538SAndroid Build Coastguard Worker if (!item.get()) { 190*6777b538SAndroid Build Coastguard Worker return; 191*6777b538SAndroid Build Coastguard Worker } 192*6777b538SAndroid Build Coastguard Worker 193*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic push // https://crbug.com/1154377 194*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic ignored "-Wdeprecated-declarations" 195*6777b538SAndroid Build Coastguard Worker LSSharedFileListItemRemove(login_items.GetLoginFileList(), item.get()); 196*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic pop 197*6777b538SAndroid Build Coastguard Worker} 198*6777b538SAndroid Build Coastguard Worker 199*6777b538SAndroid Build Coastguard Workerbool WasLaunchedAsLoginOrResumeItem() { 200*6777b538SAndroid Build Coastguard Worker ProcessSerialNumber psn = {0, kCurrentProcess}; 201*6777b538SAndroid Build Coastguard Worker ProcessInfoRec info = {}; 202*6777b538SAndroid Build Coastguard Worker info.processInfoLength = sizeof(info); 203*6777b538SAndroid Build Coastguard Worker 204*6777b538SAndroid Build Coastguard Worker// GetProcessInformation has been deprecated since macOS 10.9, but there is no 205*6777b538SAndroid Build Coastguard Worker// replacement that provides the information we need. See 206*6777b538SAndroid Build Coastguard Worker// https://crbug.com/650854. 207*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic push 208*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic ignored "-Wdeprecated-declarations" 209*6777b538SAndroid Build Coastguard Worker if (GetProcessInformation(&psn, &info) == noErr) { 210*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic pop 211*6777b538SAndroid Build Coastguard Worker ProcessInfoRec parent_info = {}; 212*6777b538SAndroid Build Coastguard Worker parent_info.processInfoLength = sizeof(parent_info); 213*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic push 214*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic ignored "-Wdeprecated-declarations" 215*6777b538SAndroid Build Coastguard Worker if (GetProcessInformation(&info.processLauncher, &parent_info) == noErr) { 216*6777b538SAndroid Build Coastguard Worker#pragma clang diagnostic pop 217*6777b538SAndroid Build Coastguard Worker return parent_info.processSignature == 'lgnw'; 218*6777b538SAndroid Build Coastguard Worker } 219*6777b538SAndroid Build Coastguard Worker } 220*6777b538SAndroid Build Coastguard Worker return false; 221*6777b538SAndroid Build Coastguard Worker} 222*6777b538SAndroid Build Coastguard Worker 223*6777b538SAndroid Build Coastguard Workerbool WasLaunchedAsLoginItemRestoreState() { 224*6777b538SAndroid Build Coastguard Worker // "Reopen windows..." option was added for 10.7. Prior OS versions should 225*6777b538SAndroid Build Coastguard Worker // not have this behavior. 226*6777b538SAndroid Build Coastguard Worker if (!WasLaunchedAsLoginOrResumeItem()) { 227*6777b538SAndroid Build Coastguard Worker return false; 228*6777b538SAndroid Build Coastguard Worker } 229*6777b538SAndroid Build Coastguard Worker 230*6777b538SAndroid Build Coastguard Worker CFStringRef app = CFSTR("com.apple.loginwindow"); 231*6777b538SAndroid Build Coastguard Worker CFStringRef save_state = CFSTR("TALLogoutSavesState"); 232*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<CFPropertyListRef> plist( 233*6777b538SAndroid Build Coastguard Worker CFPreferencesCopyAppValue(save_state, app)); 234*6777b538SAndroid Build Coastguard Worker // According to documentation, com.apple.loginwindow.plist does not exist on a 235*6777b538SAndroid Build Coastguard Worker // fresh installation until the user changes a login window setting. The 236*6777b538SAndroid Build Coastguard Worker // "reopen windows" option is checked by default, so the plist would exist had 237*6777b538SAndroid Build Coastguard Worker // the user unchecked it. 238*6777b538SAndroid Build Coastguard Worker // https://developer.apple.com/library/mac/documentation/macosx/conceptual/bpsystemstartup/chapters/CustomLogin.html 239*6777b538SAndroid Build Coastguard Worker if (!plist) { 240*6777b538SAndroid Build Coastguard Worker return true; 241*6777b538SAndroid Build Coastguard Worker } 242*6777b538SAndroid Build Coastguard Worker 243*6777b538SAndroid Build Coastguard Worker if (CFBooleanRef restore_state = 244*6777b538SAndroid Build Coastguard Worker base::apple::CFCast<CFBooleanRef>(plist.get())) { 245*6777b538SAndroid Build Coastguard Worker return CFBooleanGetValue(restore_state); 246*6777b538SAndroid Build Coastguard Worker } 247*6777b538SAndroid Build Coastguard Worker 248*6777b538SAndroid Build Coastguard Worker return false; 249*6777b538SAndroid Build Coastguard Worker} 250*6777b538SAndroid Build Coastguard Worker 251*6777b538SAndroid Build Coastguard Workerbool WasLaunchedAsHiddenLoginItem() { 252*6777b538SAndroid Build Coastguard Worker if (!WasLaunchedAsLoginOrResumeItem()) { 253*6777b538SAndroid Build Coastguard Worker return false; 254*6777b538SAndroid Build Coastguard Worker } 255*6777b538SAndroid Build Coastguard Worker 256*6777b538SAndroid Build Coastguard Worker LoginItemsFileList login_items; 257*6777b538SAndroid Build Coastguard Worker if (!login_items.Initialize()) { 258*6777b538SAndroid Build Coastguard Worker return false; 259*6777b538SAndroid Build Coastguard Worker } 260*6777b538SAndroid Build Coastguard Worker 261*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<LSSharedFileListItemRef> item( 262*6777b538SAndroid Build Coastguard Worker login_items.GetLoginItemForMainApp()); 263*6777b538SAndroid Build Coastguard Worker if (!item.get()) { 264*6777b538SAndroid Build Coastguard Worker // The OS itself can launch items, usually for the resume feature. 265*6777b538SAndroid Build Coastguard Worker return false; 266*6777b538SAndroid Build Coastguard Worker } 267*6777b538SAndroid Build Coastguard Worker return IsHiddenLoginItem(item.get()); 268*6777b538SAndroid Build Coastguard Worker} 269*6777b538SAndroid Build Coastguard Worker 270*6777b538SAndroid Build Coastguard Workerbool RemoveQuarantineAttribute(const FilePath& file_path) { 271*6777b538SAndroid Build Coastguard Worker const char kQuarantineAttrName[] = "com.apple.quarantine"; 272*6777b538SAndroid Build Coastguard Worker int status = removexattr(file_path.value().c_str(), kQuarantineAttrName, 0); 273*6777b538SAndroid Build Coastguard Worker return status == 0 || errno == ENOATTR; 274*6777b538SAndroid Build Coastguard Worker} 275*6777b538SAndroid Build Coastguard Worker 276*6777b538SAndroid Build Coastguard Workervoid SetFileTags(const FilePath& file_path, 277*6777b538SAndroid Build Coastguard Worker const std::vector<std::string>& file_tags) { 278*6777b538SAndroid Build Coastguard Worker if (file_tags.empty()) { 279*6777b538SAndroid Build Coastguard Worker return; 280*6777b538SAndroid Build Coastguard Worker } 281*6777b538SAndroid Build Coastguard Worker 282*6777b538SAndroid Build Coastguard Worker NSMutableArray* tag_array = [NSMutableArray array]; 283*6777b538SAndroid Build Coastguard Worker for (const auto& tag : file_tags) { 284*6777b538SAndroid Build Coastguard Worker [tag_array addObject:SysUTF8ToNSString(tag)]; 285*6777b538SAndroid Build Coastguard Worker } 286*6777b538SAndroid Build Coastguard Worker 287*6777b538SAndroid Build Coastguard Worker NSURL* file_url = apple::FilePathToNSURL(file_path); 288*6777b538SAndroid Build Coastguard Worker [file_url setResourceValue:tag_array forKey:NSURLTagNamesKey error:nil]; 289*6777b538SAndroid Build Coastguard Worker} 290*6777b538SAndroid Build Coastguard Worker 291*6777b538SAndroid Build Coastguard Workernamespace { 292*6777b538SAndroid Build Coastguard Worker 293*6777b538SAndroid Build Coastguard Workerint ParseOSProductVersion(const std::string_view& version) { 294*6777b538SAndroid Build Coastguard Worker int macos_version = 0; 295*6777b538SAndroid Build Coastguard Worker 296*6777b538SAndroid Build Coastguard Worker // The number of parts that need to be a part of the return value 297*6777b538SAndroid Build Coastguard Worker // (major/minor/bugfix). 298*6777b538SAndroid Build Coastguard Worker int parts = 3; 299*6777b538SAndroid Build Coastguard Worker 300*6777b538SAndroid Build Coastguard Worker // When a Rapid Security Response is applied to a system, the UI will display 301*6777b538SAndroid Build Coastguard Worker // an additional letter (e.g. "13.4.1 (a)"). That extra letter should not be 302*6777b538SAndroid Build Coastguard Worker // present in `version_string`; in fact, the version string should not contain 303*6777b538SAndroid Build Coastguard Worker // any spaces. However, take the first space-delimited "word" for parsing. 304*6777b538SAndroid Build Coastguard Worker std::vector<std::string_view> words = base::SplitStringPiece( 305*6777b538SAndroid Build Coastguard Worker version, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); 306*6777b538SAndroid Build Coastguard Worker CHECK_GE(words.size(), 1u); 307*6777b538SAndroid Build Coastguard Worker 308*6777b538SAndroid Build Coastguard Worker // There are expected to be either two or three numbers separated by a dot. 309*6777b538SAndroid Build Coastguard Worker // Walk through them, and add them to the version string. 310*6777b538SAndroid Build Coastguard Worker for (const auto& value_str : base::SplitStringPiece( 311*6777b538SAndroid Build Coastguard Worker words[0], ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) { 312*6777b538SAndroid Build Coastguard Worker int value; 313*6777b538SAndroid Build Coastguard Worker bool success = base::StringToInt(value_str, &value); 314*6777b538SAndroid Build Coastguard Worker CHECK(success); 315*6777b538SAndroid Build Coastguard Worker macos_version *= 100; 316*6777b538SAndroid Build Coastguard Worker macos_version += value; 317*6777b538SAndroid Build Coastguard Worker if (--parts == 0) { 318*6777b538SAndroid Build Coastguard Worker break; 319*6777b538SAndroid Build Coastguard Worker } 320*6777b538SAndroid Build Coastguard Worker } 321*6777b538SAndroid Build Coastguard Worker 322*6777b538SAndroid Build Coastguard Worker // While historically the string has comprised exactly two or three numbers 323*6777b538SAndroid Build Coastguard Worker // separated by a dot, it's not inconceivable that it might one day be only 324*6777b538SAndroid Build Coastguard Worker // one number. Therefore, only check to see that at least one number was found 325*6777b538SAndroid Build Coastguard Worker // and processed. 326*6777b538SAndroid Build Coastguard Worker CHECK_LE(parts, 2); 327*6777b538SAndroid Build Coastguard Worker 328*6777b538SAndroid Build Coastguard Worker // Tack on as many '00 digits as needed to be sure that exactly three version 329*6777b538SAndroid Build Coastguard Worker // numbers are returned. 330*6777b538SAndroid Build Coastguard Worker for (int i = 0; i < parts; ++i) { 331*6777b538SAndroid Build Coastguard Worker macos_version *= 100; 332*6777b538SAndroid Build Coastguard Worker } 333*6777b538SAndroid Build Coastguard Worker 334*6777b538SAndroid Build Coastguard Worker // Checks that the value is within expected bounds corresponding to released 335*6777b538SAndroid Build Coastguard Worker // OS version numbers. The most important bit is making sure that the "10.16" 336*6777b538SAndroid Build Coastguard Worker // compatibility mode isn't engaged. 337*6777b538SAndroid Build Coastguard Worker CHECK(macos_version >= 10'00'00); 338*6777b538SAndroid Build Coastguard Worker CHECK(macos_version < 10'16'00 || macos_version >= 11'00'00); 339*6777b538SAndroid Build Coastguard Worker 340*6777b538SAndroid Build Coastguard Worker return macos_version; 341*6777b538SAndroid Build Coastguard Worker} 342*6777b538SAndroid Build Coastguard Worker 343*6777b538SAndroid Build Coastguard Worker} // namespace 344*6777b538SAndroid Build Coastguard Worker 345*6777b538SAndroid Build Coastguard Workerint ParseOSProductVersionForTesting(const std::string_view& version) { 346*6777b538SAndroid Build Coastguard Worker return ParseOSProductVersion(version); 347*6777b538SAndroid Build Coastguard Worker} 348*6777b538SAndroid Build Coastguard Worker 349*6777b538SAndroid Build Coastguard Workerint MacOSVersion() { 350*6777b538SAndroid Build Coastguard Worker static int macos_version = ParseOSProductVersion( 351*6777b538SAndroid Build Coastguard Worker StringSysctlByName("kern.osproductversion").value()); 352*6777b538SAndroid Build Coastguard Worker 353*6777b538SAndroid Build Coastguard Worker return macos_version; 354*6777b538SAndroid Build Coastguard Worker} 355*6777b538SAndroid Build Coastguard Worker 356*6777b538SAndroid Build Coastguard Workernamespace { 357*6777b538SAndroid Build Coastguard Worker 358*6777b538SAndroid Build Coastguard Worker#if defined(ARCH_CPU_X86_64) 359*6777b538SAndroid Build Coastguard Worker// https://developer.apple.com/documentation/apple_silicon/about_the_rosetta_translation_environment#3616845 360*6777b538SAndroid Build Coastguard Workerbool ProcessIsTranslated() { 361*6777b538SAndroid Build Coastguard Worker int ret = 0; 362*6777b538SAndroid Build Coastguard Worker size_t size = sizeof(ret); 363*6777b538SAndroid Build Coastguard Worker if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) == -1) { 364*6777b538SAndroid Build Coastguard Worker return false; 365*6777b538SAndroid Build Coastguard Worker } 366*6777b538SAndroid Build Coastguard Worker return ret; 367*6777b538SAndroid Build Coastguard Worker} 368*6777b538SAndroid Build Coastguard Worker#endif // ARCH_CPU_X86_64 369*6777b538SAndroid Build Coastguard Worker 370*6777b538SAndroid Build Coastguard Worker} // namespace 371*6777b538SAndroid Build Coastguard Worker 372*6777b538SAndroid Build Coastguard WorkerCPUType GetCPUType() { 373*6777b538SAndroid Build Coastguard Worker#if defined(ARCH_CPU_ARM64) 374*6777b538SAndroid Build Coastguard Worker return CPUType::kArm; 375*6777b538SAndroid Build Coastguard Worker#elif defined(ARCH_CPU_X86_64) 376*6777b538SAndroid Build Coastguard Worker return ProcessIsTranslated() ? CPUType::kTranslatedIntel : CPUType::kIntel; 377*6777b538SAndroid Build Coastguard Worker#else 378*6777b538SAndroid Build Coastguard Worker#error Time for another chip transition? 379*6777b538SAndroid Build Coastguard Worker#endif // ARCH_CPU_* 380*6777b538SAndroid Build Coastguard Worker} 381*6777b538SAndroid Build Coastguard Worker 382*6777b538SAndroid Build Coastguard Workerstd::string GetOSDisplayName() { 383*6777b538SAndroid Build Coastguard Worker std::string version_string = base::SysNSStringToUTF8( 384*6777b538SAndroid Build Coastguard Worker NSProcessInfo.processInfo.operatingSystemVersionString); 385*6777b538SAndroid Build Coastguard Worker return "macOS " + version_string; 386*6777b538SAndroid Build Coastguard Worker} 387*6777b538SAndroid Build Coastguard Worker 388*6777b538SAndroid Build Coastguard Workerstd::string GetPlatformSerialNumber() { 389*6777b538SAndroid Build Coastguard Worker base::mac::ScopedIOObject<io_service_t> expert_device( 390*6777b538SAndroid Build Coastguard Worker IOServiceGetMatchingService(kIOMasterPortDefault, 391*6777b538SAndroid Build Coastguard Worker IOServiceMatching("IOPlatformExpertDevice"))); 392*6777b538SAndroid Build Coastguard Worker if (!expert_device) { 393*6777b538SAndroid Build Coastguard Worker DLOG(ERROR) << "Error retrieving the machine serial number."; 394*6777b538SAndroid Build Coastguard Worker return std::string(); 395*6777b538SAndroid Build Coastguard Worker } 396*6777b538SAndroid Build Coastguard Worker 397*6777b538SAndroid Build Coastguard Worker apple::ScopedCFTypeRef<CFTypeRef> serial_number( 398*6777b538SAndroid Build Coastguard Worker IORegistryEntryCreateCFProperty(expert_device.get(), 399*6777b538SAndroid Build Coastguard Worker CFSTR(kIOPlatformSerialNumberKey), 400*6777b538SAndroid Build Coastguard Worker kCFAllocatorDefault, 0)); 401*6777b538SAndroid Build Coastguard Worker CFStringRef serial_number_cfstring = 402*6777b538SAndroid Build Coastguard Worker base::apple::CFCast<CFStringRef>(serial_number.get()); 403*6777b538SAndroid Build Coastguard Worker if (!serial_number_cfstring) { 404*6777b538SAndroid Build Coastguard Worker DLOG(ERROR) << "Error retrieving the machine serial number."; 405*6777b538SAndroid Build Coastguard Worker return std::string(); 406*6777b538SAndroid Build Coastguard Worker } 407*6777b538SAndroid Build Coastguard Worker 408*6777b538SAndroid Build Coastguard Worker return base::SysCFStringRefToUTF8(serial_number_cfstring); 409*6777b538SAndroid Build Coastguard Worker} 410*6777b538SAndroid Build Coastguard Worker 411*6777b538SAndroid Build Coastguard Workervoid OpenSystemSettingsPane(SystemSettingsPane pane, 412*6777b538SAndroid Build Coastguard Worker const std::string& id_param) { 413*6777b538SAndroid Build Coastguard Worker NSString* url = nil; 414*6777b538SAndroid Build Coastguard Worker NSString* pane_file = nil; 415*6777b538SAndroid Build Coastguard Worker NSData* subpane_data = nil; 416*6777b538SAndroid Build Coastguard Worker // On macOS 13 and later, System Settings are implemented with app extensions 417*6777b538SAndroid Build Coastguard Worker // found at /System/Library/ExtensionKit/Extensions/. URLs to open them are 418*6777b538SAndroid Build Coastguard Worker // constructed with a scheme of "x-apple.systempreferences" and a body of the 419*6777b538SAndroid Build Coastguard Worker // the bundle ID of the app extension. (In the Info.plist there is an 420*6777b538SAndroid Build Coastguard Worker // EXAppExtensionAttributes dictionary with legacy identifiers, but given that 421*6777b538SAndroid Build Coastguard Worker // those are explicitly named "legacy", this code prefers to use the bundle 422*6777b538SAndroid Build Coastguard Worker // IDs for the URLs it uses.) It is not yet known how to definitively identify 423*6777b538SAndroid Build Coastguard Worker // the query string used to open sub-panes; the ones used below were 424*6777b538SAndroid Build Coastguard Worker // determined from historical usage, disassembly of related code, and 425*6777b538SAndroid Build Coastguard Worker // guessing. Clarity was requested from Apple in FB11753405. The current best 426*6777b538SAndroid Build Coastguard Worker // guess is to analyze the method named -revealElementForKey:, but because 427*6777b538SAndroid Build Coastguard Worker // the extensions are all written in Swift it's hard to confirm this is 428*6777b538SAndroid Build Coastguard Worker // correct or to use this knowledge. 429*6777b538SAndroid Build Coastguard Worker // 430*6777b538SAndroid Build Coastguard Worker // For macOS 12 and earlier, to determine the `subpane_data`, find a method 431*6777b538SAndroid Build Coastguard Worker // named -handleOpenParameter: which takes an AEDesc as a parameter. 432*6777b538SAndroid Build Coastguard Worker switch (pane) { 433*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kAccessibility_Captions: 434*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 435*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.Accessibility-Settings." 436*6777b538SAndroid Build Coastguard Worker @"extension?Captioning"; 437*6777b538SAndroid Build Coastguard Worker } else { 438*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.preference.universalaccess?" 439*6777b538SAndroid Build Coastguard Worker @"Captioning"; 440*6777b538SAndroid Build Coastguard Worker } 441*6777b538SAndroid Build Coastguard Worker break; 442*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kDateTime: 443*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 444*6777b538SAndroid Build Coastguard Worker url = 445*6777b538SAndroid Build Coastguard Worker @"x-apple.systempreferences:com.apple.Date-Time-Settings.extension"; 446*6777b538SAndroid Build Coastguard Worker } else { 447*6777b538SAndroid Build Coastguard Worker pane_file = @"/System/Library/PreferencePanes/DateAndTime.prefPane"; 448*6777b538SAndroid Build Coastguard Worker } 449*6777b538SAndroid Build Coastguard Worker break; 450*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kNetwork_Proxies: 451*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 452*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.Network-Settings.extension?" 453*6777b538SAndroid Build Coastguard Worker @"Proxies"; 454*6777b538SAndroid Build Coastguard Worker } else { 455*6777b538SAndroid Build Coastguard Worker pane_file = @"/System/Library/PreferencePanes/Network.prefPane"; 456*6777b538SAndroid Build Coastguard Worker subpane_data = [@"Proxies" dataUsingEncoding:NSASCIIStringEncoding]; 457*6777b538SAndroid Build Coastguard Worker } 458*6777b538SAndroid Build Coastguard Worker break; 459*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kNotifications: 460*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 461*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.Notifications-Settings." 462*6777b538SAndroid Build Coastguard Worker @"extension"; 463*6777b538SAndroid Build Coastguard Worker if (!id_param.empty()) { 464*6777b538SAndroid Build Coastguard Worker url = [url stringByAppendingFormat:@"?id=%s", id_param.c_str()]; 465*6777b538SAndroid Build Coastguard Worker } 466*6777b538SAndroid Build Coastguard Worker } else { 467*6777b538SAndroid Build Coastguard Worker pane_file = @"/System/Library/PreferencePanes/Notifications.prefPane"; 468*6777b538SAndroid Build Coastguard Worker NSDictionary* subpane_dict = @{ 469*6777b538SAndroid Build Coastguard Worker @"command" : @"show", 470*6777b538SAndroid Build Coastguard Worker @"identifier" : SysUTF8ToNSString(id_param) 471*6777b538SAndroid Build Coastguard Worker }; 472*6777b538SAndroid Build Coastguard Worker subpane_data = [NSPropertyListSerialization 473*6777b538SAndroid Build Coastguard Worker dataWithPropertyList:subpane_dict 474*6777b538SAndroid Build Coastguard Worker format:NSPropertyListXMLFormat_v1_0 475*6777b538SAndroid Build Coastguard Worker options:0 476*6777b538SAndroid Build Coastguard Worker error:nil]; 477*6777b538SAndroid Build Coastguard Worker } 478*6777b538SAndroid Build Coastguard Worker break; 479*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kPrintersScanners: 480*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 481*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.Print-Scan-Settings." 482*6777b538SAndroid Build Coastguard Worker @"extension"; 483*6777b538SAndroid Build Coastguard Worker } else { 484*6777b538SAndroid Build Coastguard Worker pane_file = @"/System/Library/PreferencePanes/PrintAndFax.prefPane"; 485*6777b538SAndroid Build Coastguard Worker } 486*6777b538SAndroid Build Coastguard Worker break; 487*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kPrivacySecurity_Accessibility: 488*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 489*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.settings.PrivacySecurity." 490*6777b538SAndroid Build Coastguard Worker @"extension?Privacy_Accessibility"; 491*6777b538SAndroid Build Coastguard Worker } else { 492*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.preference.security?" 493*6777b538SAndroid Build Coastguard Worker @"Privacy_Accessibility"; 494*6777b538SAndroid Build Coastguard Worker } 495*6777b538SAndroid Build Coastguard Worker break; 496*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kPrivacySecurity_Bluetooth: 497*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 498*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.settings.PrivacySecurity." 499*6777b538SAndroid Build Coastguard Worker @"extension?Privacy_Bluetooth"; 500*6777b538SAndroid Build Coastguard Worker } else { 501*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.preference.security?" 502*6777b538SAndroid Build Coastguard Worker @"Privacy_Bluetooth"; 503*6777b538SAndroid Build Coastguard Worker } 504*6777b538SAndroid Build Coastguard Worker break; 505*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kPrivacySecurity_Camera: 506*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 507*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.settings.PrivacySecurity." 508*6777b538SAndroid Build Coastguard Worker @"extension?Privacy_Camera"; 509*6777b538SAndroid Build Coastguard Worker } else { 510*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.preference.security?" 511*6777b538SAndroid Build Coastguard Worker @"Privacy_Camera"; 512*6777b538SAndroid Build Coastguard Worker } 513*6777b538SAndroid Build Coastguard Worker break; 514*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kPrivacySecurity_Extensions_Sharing: 515*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 516*6777b538SAndroid Build Coastguard Worker // See ShareKit, -[SHKSharingServicePicker openAppExtensionsPrefpane]. 517*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.ExtensionsPreferences?" 518*6777b538SAndroid Build Coastguard Worker @"Sharing"; 519*6777b538SAndroid Build Coastguard Worker } else { 520*6777b538SAndroid Build Coastguard Worker // This is equivalent to the implementation of AppKit's 521*6777b538SAndroid Build Coastguard Worker // +[NSSharingServicePicker openAppExtensionsPrefPane]. 522*6777b538SAndroid Build Coastguard Worker pane_file = @"/System/Library/PreferencePanes/Extensions.prefPane"; 523*6777b538SAndroid Build Coastguard Worker NSDictionary* subpane_dict = @{ 524*6777b538SAndroid Build Coastguard Worker @"action" : @"revealExtensionPoint", 525*6777b538SAndroid Build Coastguard Worker @"protocol" : @"com.apple.share-services" 526*6777b538SAndroid Build Coastguard Worker }; 527*6777b538SAndroid Build Coastguard Worker subpane_data = [NSPropertyListSerialization 528*6777b538SAndroid Build Coastguard Worker dataWithPropertyList:subpane_dict 529*6777b538SAndroid Build Coastguard Worker format:NSPropertyListXMLFormat_v1_0 530*6777b538SAndroid Build Coastguard Worker options:0 531*6777b538SAndroid Build Coastguard Worker error:nil]; 532*6777b538SAndroid Build Coastguard Worker } 533*6777b538SAndroid Build Coastguard Worker break; 534*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kPrivacySecurity_LocationServices: 535*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 536*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.settings.PrivacySecurity." 537*6777b538SAndroid Build Coastguard Worker @"extension?Privacy_LocationServices"; 538*6777b538SAndroid Build Coastguard Worker } else { 539*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.preference.security?" 540*6777b538SAndroid Build Coastguard Worker @"Privacy_LocationServices"; 541*6777b538SAndroid Build Coastguard Worker } 542*6777b538SAndroid Build Coastguard Worker break; 543*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kPrivacySecurity_Microphone: 544*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 545*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.settings.PrivacySecurity." 546*6777b538SAndroid Build Coastguard Worker @"extension?Privacy_Microphone"; 547*6777b538SAndroid Build Coastguard Worker } else { 548*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.preference.security?" 549*6777b538SAndroid Build Coastguard Worker @"Privacy_Microphone"; 550*6777b538SAndroid Build Coastguard Worker } 551*6777b538SAndroid Build Coastguard Worker break; 552*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kPrivacySecurity_ScreenRecording: 553*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 554*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.settings.PrivacySecurity." 555*6777b538SAndroid Build Coastguard Worker @"extension?Privacy_ScreenCapture"; 556*6777b538SAndroid Build Coastguard Worker } else { 557*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.preference.security?" 558*6777b538SAndroid Build Coastguard Worker @"Privacy_ScreenCapture"; 559*6777b538SAndroid Build Coastguard Worker } 560*6777b538SAndroid Build Coastguard Worker break; 561*6777b538SAndroid Build Coastguard Worker case SystemSettingsPane::kTrackpad: 562*6777b538SAndroid Build Coastguard Worker if (MacOSMajorVersion() >= 13) { 563*6777b538SAndroid Build Coastguard Worker url = @"x-apple.systempreferences:com.apple.Trackpad-Settings." 564*6777b538SAndroid Build Coastguard Worker @"extension"; 565*6777b538SAndroid Build Coastguard Worker } else { 566*6777b538SAndroid Build Coastguard Worker pane_file = @"/System/Library/PreferencePanes/Trackpad.prefPane"; 567*6777b538SAndroid Build Coastguard Worker } 568*6777b538SAndroid Build Coastguard Worker break; 569*6777b538SAndroid Build Coastguard Worker } 570*6777b538SAndroid Build Coastguard Worker 571*6777b538SAndroid Build Coastguard Worker DCHECK(url != nil ^ pane_file != nil); 572*6777b538SAndroid Build Coastguard Worker 573*6777b538SAndroid Build Coastguard Worker if (url) { 574*6777b538SAndroid Build Coastguard Worker [NSWorkspace.sharedWorkspace openURL:[NSURL URLWithString:url]]; 575*6777b538SAndroid Build Coastguard Worker return; 576*6777b538SAndroid Build Coastguard Worker } 577*6777b538SAndroid Build Coastguard Worker 578*6777b538SAndroid Build Coastguard Worker NSAppleEventDescriptor* subpane_descriptor; 579*6777b538SAndroid Build Coastguard Worker NSArray* pane_file_urls = @[ [NSURL fileURLWithPath:pane_file] ]; 580*6777b538SAndroid Build Coastguard Worker 581*6777b538SAndroid Build Coastguard Worker LSLaunchURLSpec launchSpec = {0}; 582*6777b538SAndroid Build Coastguard Worker launchSpec.itemURLs = apple::NSToCFPtrCast(pane_file_urls); 583*6777b538SAndroid Build Coastguard Worker if (subpane_data) { 584*6777b538SAndroid Build Coastguard Worker subpane_descriptor = 585*6777b538SAndroid Build Coastguard Worker [[NSAppleEventDescriptor alloc] initWithDescriptorType:'ptru' 586*6777b538SAndroid Build Coastguard Worker data:subpane_data]; 587*6777b538SAndroid Build Coastguard Worker launchSpec.passThruParams = subpane_descriptor.aeDesc; 588*6777b538SAndroid Build Coastguard Worker } 589*6777b538SAndroid Build Coastguard Worker launchSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents; 590*6777b538SAndroid Build Coastguard Worker 591*6777b538SAndroid Build Coastguard Worker LSOpenFromURLSpec(&launchSpec, nullptr); 592*6777b538SAndroid Build Coastguard Worker} 593*6777b538SAndroid Build Coastguard Worker 594*6777b538SAndroid Build Coastguard Worker} // namespace base::mac 595