platform_util_mac.mm 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // Copyright (c) 2013 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/common/platform_util.h"
  5. #import <Carbon/Carbon.h>
  6. #import <Cocoa/Cocoa.h>
  7. #import <ServiceManagement/ServiceManagement.h>
  8. #include "base/callback.h"
  9. #include "base/files/file_path.h"
  10. #include "base/files/file_util.h"
  11. #include "base/logging.h"
  12. #include "base/mac/foundation_util.h"
  13. #include "base/mac/mac_logging.h"
  14. #include "base/mac/scoped_aedesc.h"
  15. #include "base/strings/stringprintf.h"
  16. #include "base/strings/sys_string_conversions.h"
  17. #include "net/base/mac/url_conversions.h"
  18. #include "url/gurl.h"
  19. namespace {
  20. // This may be called from a global dispatch queue, the methods used here are
  21. // thread safe, including LSGetApplicationForURL (> 10.2) and
  22. // NSWorkspace#openURLs.
  23. std::string OpenURL(NSURL* ns_url, bool activate) {
  24. CFURLRef ref = nil;
  25. if (@available(macOS 10.10, *)) {
  26. ref = LSCopyDefaultApplicationURLForURL(base::mac::NSToCFCast(ns_url),
  27. kLSRolesAll, nullptr);
  28. }
  29. // If no application could be found, NULL is returned and outError
  30. // (if not NULL) is populated with kLSApplicationNotFoundErr.
  31. if (ref == NULL)
  32. return "No application in the Launch Services database matches the input "
  33. "criteria.";
  34. NSUInteger launchOptions = NSWorkspaceLaunchDefault;
  35. if (!activate)
  36. launchOptions |= NSWorkspaceLaunchWithoutActivation;
  37. bool opened = [[NSWorkspace sharedWorkspace] openURLs:@[ ns_url ]
  38. withAppBundleIdentifier:nil
  39. options:launchOptions
  40. additionalEventParamDescriptor:nil
  41. launchIdentifiers:nil];
  42. if (!opened)
  43. return "Failed to open URL";
  44. return "";
  45. }
  46. NSString* GetLoginHelperBundleIdentifier() {
  47. return [[[NSBundle mainBundle] bundleIdentifier]
  48. stringByAppendingString:@".loginhelper"];
  49. }
  50. } // namespace
  51. namespace platform_util {
  52. bool ShowItemInFolder(const base::FilePath& path) {
  53. // The API only takes absolute path.
  54. base::FilePath full_path =
  55. path.IsAbsolute() ? path : base::MakeAbsoluteFilePath(path);
  56. DCHECK([NSThread isMainThread]);
  57. NSString* path_string = base::SysUTF8ToNSString(full_path.value());
  58. if (!path_string || ![[NSWorkspace sharedWorkspace] selectFile:path_string
  59. inFileViewerRootedAtPath:@""]) {
  60. LOG(WARNING) << "NSWorkspace failed to select file " << full_path.value();
  61. return false;
  62. }
  63. return true;
  64. }
  65. bool OpenItem(const base::FilePath& full_path) {
  66. DCHECK([NSThread isMainThread]);
  67. NSString* path_string = base::SysUTF8ToNSString(full_path.value());
  68. if (!path_string)
  69. return false;
  70. NSURL* url = [NSURL fileURLWithPath:path_string];
  71. if (!url)
  72. return false;
  73. const NSWorkspaceLaunchOptions launch_options =
  74. NSWorkspaceLaunchAsync | NSWorkspaceLaunchWithErrorPresentation;
  75. return [[NSWorkspace sharedWorkspace] openURLs:@[ url ]
  76. withAppBundleIdentifier:nil
  77. options:launch_options
  78. additionalEventParamDescriptor:nil
  79. launchIdentifiers:NULL];
  80. }
  81. bool OpenExternal(const GURL& url, const OpenExternalOptions& options) {
  82. DCHECK([NSThread isMainThread]);
  83. NSURL* ns_url = net::NSURLWithGURL(url);
  84. if (ns_url)
  85. return OpenURL(ns_url, options.activate).empty();
  86. return false;
  87. }
  88. void OpenExternal(const GURL& url,
  89. const OpenExternalOptions& options,
  90. const OpenExternalCallback& callback) {
  91. NSURL* ns_url = net::NSURLWithGURL(url);
  92. if (!ns_url) {
  93. callback.Run("Invalid URL");
  94. return;
  95. }
  96. __block OpenExternalCallback c = callback;
  97. dispatch_async(
  98. dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  99. __block std::string error = OpenURL(ns_url, options.activate);
  100. dispatch_async(dispatch_get_main_queue(), ^{
  101. c.Run(error);
  102. });
  103. });
  104. }
  105. bool MoveItemToTrash(const base::FilePath& full_path) {
  106. NSString* path_string = base::SysUTF8ToNSString(full_path.value());
  107. BOOL status = [[NSFileManager defaultManager]
  108. trashItemAtURL:[NSURL fileURLWithPath:path_string]
  109. resultingItemURL:nil
  110. error:nil];
  111. if (!path_string || !status)
  112. LOG(WARNING) << "NSWorkspace failed to move file " << full_path.value()
  113. << " to trash";
  114. return status;
  115. }
  116. void Beep() {
  117. NSBeep();
  118. }
  119. bool GetLoginItemEnabled() {
  120. BOOL enabled = NO;
  121. // SMJobCopyDictionary does not work in sandbox (see rdar://13626319)
  122. CFArrayRef jobs = SMCopyAllJobDictionaries(kSMDomainUserLaunchd);
  123. NSArray* jobs_ = CFBridgingRelease(jobs);
  124. NSString* identifier = GetLoginHelperBundleIdentifier();
  125. if (jobs_ && [jobs_ count] > 0) {
  126. for (NSDictionary* job in jobs_) {
  127. if ([identifier isEqualToString:[job objectForKey:@"Label"]]) {
  128. enabled = [[job objectForKey:@"OnDemand"] boolValue];
  129. break;
  130. }
  131. }
  132. }
  133. return enabled;
  134. }
  135. void SetLoginItemEnabled(bool enabled) {
  136. NSString* identifier = GetLoginHelperBundleIdentifier();
  137. SMLoginItemSetEnabled((__bridge CFStringRef)identifier, enabled);
  138. }
  139. } // namespace platform_util