browser_mac.mm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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/browser/browser.h"
  5. #include "atom/browser/mac/atom_application.h"
  6. #include "atom/browser/mac/atom_application_delegate.h"
  7. #include "atom/browser/mac/dict_util.h"
  8. #include "atom/browser/native_window.h"
  9. #include "atom/browser/window_list.h"
  10. #include "atom/common/application_info.h"
  11. #include "atom/common/platform_util.h"
  12. #include "atom/common/promise_util.h"
  13. #include "base/mac/bundle_locations.h"
  14. #include "base/mac/foundation_util.h"
  15. #include "base/mac/mac_util.h"
  16. #include "base/mac/scoped_cftyperef.h"
  17. #include "base/strings/string_number_conversions.h"
  18. #include "base/strings/sys_string_conversions.h"
  19. #include "net/base/mac/url_conversions.h"
  20. #include "ui/gfx/image/image.h"
  21. #include "url/gurl.h"
  22. namespace atom {
  23. void Browser::SetShutdownHandler(base::Callback<bool()> handler) {
  24. [[AtomApplication sharedApplication] setShutdownHandler:std::move(handler)];
  25. }
  26. void Browser::Focus() {
  27. [[AtomApplication sharedApplication] activateIgnoringOtherApps:NO];
  28. }
  29. void Browser::Hide() {
  30. [[AtomApplication sharedApplication] hide:nil];
  31. }
  32. void Browser::Show() {
  33. [[AtomApplication sharedApplication] unhide:nil];
  34. }
  35. void Browser::AddRecentDocument(const base::FilePath& path) {
  36. NSString* path_string = base::mac::FilePathToNSString(path);
  37. if (!path_string)
  38. return;
  39. NSURL* u = [NSURL fileURLWithPath:path_string];
  40. if (!u)
  41. return;
  42. [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:u];
  43. }
  44. void Browser::ClearRecentDocuments() {
  45. [[NSDocumentController sharedDocumentController] clearRecentDocuments:nil];
  46. }
  47. bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol,
  48. mate::Arguments* args) {
  49. NSString* identifier = [base::mac::MainBundle() bundleIdentifier];
  50. if (!identifier)
  51. return false;
  52. if (!Browser::IsDefaultProtocolClient(protocol, args))
  53. return false;
  54. NSString* protocol_ns = [NSString stringWithUTF8String:protocol.c_str()];
  55. CFStringRef protocol_cf = base::mac::NSToCFCast(protocol_ns);
  56. CFArrayRef bundleList = LSCopyAllHandlersForURLScheme(protocol_cf);
  57. if (!bundleList) {
  58. return false;
  59. }
  60. // On macOS, we can't query the default, but the handlers list seems to put
  61. // Apple's defaults first, so we'll use the first option that isn't our bundle
  62. CFStringRef other = nil;
  63. for (CFIndex i = 0; i < CFArrayGetCount(bundleList); ++i) {
  64. other =
  65. base::mac::CFCast<CFStringRef>(CFArrayGetValueAtIndex(bundleList, i));
  66. if (![identifier isEqualToString:(__bridge NSString*)other]) {
  67. break;
  68. }
  69. }
  70. // No other app was found set it to none instead of setting it back to itself.
  71. if ([identifier isEqualToString:(__bridge NSString*)other]) {
  72. other = base::mac::NSToCFCast(@"None");
  73. }
  74. OSStatus return_code = LSSetDefaultHandlerForURLScheme(protocol_cf, other);
  75. return return_code == noErr;
  76. }
  77. bool Browser::SetAsDefaultProtocolClient(const std::string& protocol,
  78. mate::Arguments* args) {
  79. if (protocol.empty())
  80. return false;
  81. NSString* identifier = [base::mac::MainBundle() bundleIdentifier];
  82. if (!identifier)
  83. return false;
  84. NSString* protocol_ns = [NSString stringWithUTF8String:protocol.c_str()];
  85. OSStatus return_code = LSSetDefaultHandlerForURLScheme(
  86. base::mac::NSToCFCast(protocol_ns), base::mac::NSToCFCast(identifier));
  87. return return_code == noErr;
  88. }
  89. bool Browser::IsDefaultProtocolClient(const std::string& protocol,
  90. mate::Arguments* args) {
  91. if (protocol.empty())
  92. return false;
  93. NSString* identifier = [base::mac::MainBundle() bundleIdentifier];
  94. if (!identifier)
  95. return false;
  96. NSString* protocol_ns = [NSString stringWithUTF8String:protocol.c_str()];
  97. base::ScopedCFTypeRef<CFStringRef> bundleId(
  98. LSCopyDefaultHandlerForURLScheme(base::mac::NSToCFCast(protocol_ns)));
  99. if (!bundleId)
  100. return false;
  101. // Ensure the comparison is case-insensitive
  102. // as LS does not persist the case of the bundle id.
  103. NSComparisonResult result =
  104. [base::mac::CFToNSCast(bundleId) caseInsensitiveCompare:identifier];
  105. return result == NSOrderedSame;
  106. }
  107. void Browser::SetAppUserModelID(const base::string16& name) {}
  108. bool Browser::SetBadgeCount(int count) {
  109. DockSetBadgeText(count != 0 ? base::NumberToString(count) : "");
  110. badge_count_ = count;
  111. return true;
  112. }
  113. void Browser::SetUserActivity(const std::string& type,
  114. const base::DictionaryValue& user_info,
  115. mate::Arguments* args) {
  116. std::string url_string;
  117. args->GetNext(&url_string);
  118. [[AtomApplication sharedApplication]
  119. setCurrentActivity:base::SysUTF8ToNSString(type)
  120. withUserInfo:DictionaryValueToNSDictionary(user_info)
  121. withWebpageURL:net::NSURLWithGURL(GURL(url_string))];
  122. }
  123. std::string Browser::GetCurrentActivityType() {
  124. NSUserActivity* userActivity =
  125. [[AtomApplication sharedApplication] getCurrentActivity];
  126. return base::SysNSStringToUTF8(userActivity.activityType);
  127. }
  128. void Browser::InvalidateCurrentActivity() {
  129. [[AtomApplication sharedApplication] invalidateCurrentActivity];
  130. }
  131. void Browser::UpdateCurrentActivity(const std::string& type,
  132. const base::DictionaryValue& user_info) {
  133. [[AtomApplication sharedApplication]
  134. updateCurrentActivity:base::SysUTF8ToNSString(type)
  135. withUserInfo:DictionaryValueToNSDictionary(user_info)];
  136. }
  137. bool Browser::WillContinueUserActivity(const std::string& type) {
  138. bool prevent_default = false;
  139. for (BrowserObserver& observer : observers_)
  140. observer.OnWillContinueUserActivity(&prevent_default, type);
  141. return prevent_default;
  142. }
  143. void Browser::DidFailToContinueUserActivity(const std::string& type,
  144. const std::string& error) {
  145. for (BrowserObserver& observer : observers_)
  146. observer.OnDidFailToContinueUserActivity(type, error);
  147. }
  148. bool Browser::ContinueUserActivity(const std::string& type,
  149. const base::DictionaryValue& user_info) {
  150. bool prevent_default = false;
  151. for (BrowserObserver& observer : observers_)
  152. observer.OnContinueUserActivity(&prevent_default, type, user_info);
  153. return prevent_default;
  154. }
  155. void Browser::UserActivityWasContinued(const std::string& type,
  156. const base::DictionaryValue& user_info) {
  157. for (BrowserObserver& observer : observers_)
  158. observer.OnUserActivityWasContinued(type, user_info);
  159. }
  160. bool Browser::UpdateUserActivityState(const std::string& type,
  161. const base::DictionaryValue& user_info) {
  162. bool prevent_default = false;
  163. for (BrowserObserver& observer : observers_)
  164. observer.OnUpdateUserActivityState(&prevent_default, type, user_info);
  165. return prevent_default;
  166. }
  167. Browser::LoginItemSettings Browser::GetLoginItemSettings(
  168. const LoginItemSettings& options) {
  169. LoginItemSettings settings;
  170. #if defined(MAS_BUILD)
  171. settings.open_at_login = platform_util::GetLoginItemEnabled();
  172. #else
  173. settings.open_at_login =
  174. base::mac::CheckLoginItemStatus(&settings.open_as_hidden);
  175. settings.restore_state = base::mac::WasLaunchedAsLoginItemRestoreState();
  176. settings.opened_at_login = base::mac::WasLaunchedAsLoginOrResumeItem();
  177. settings.opened_as_hidden = base::mac::WasLaunchedAsHiddenLoginItem();
  178. #endif
  179. return settings;
  180. }
  181. // copied from GetLoginItemForApp in base/mac/mac_util.mm
  182. LSSharedFileListItemRef GetLoginItemForApp() {
  183. base::ScopedCFTypeRef<LSSharedFileListRef> login_items(
  184. LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL));
  185. if (!login_items.get()) {
  186. LOG(ERROR) << "Couldn't get a Login Items list.";
  187. return NULL;
  188. }
  189. base::scoped_nsobject<NSArray> login_items_array(
  190. base::mac::CFToNSCast(LSSharedFileListCopySnapshot(login_items, NULL)));
  191. NSURL* url = [NSURL fileURLWithPath:[base::mac::MainBundle() bundlePath]];
  192. for (NSUInteger i = 0; i < [login_items_array count]; ++i) {
  193. LSSharedFileListItemRef item =
  194. reinterpret_cast<LSSharedFileListItemRef>(login_items_array[i]);
  195. base::ScopedCFTypeRef<CFErrorRef> error;
  196. CFURLRef item_url_ref =
  197. LSSharedFileListItemCopyResolvedURL(item, 0, error.InitializeInto());
  198. if (!error && item_url_ref) {
  199. base::ScopedCFTypeRef<CFURLRef> item_url(item_url_ref);
  200. if (CFEqual(item_url, url)) {
  201. CFRetain(item);
  202. return item;
  203. }
  204. }
  205. }
  206. return NULL;
  207. }
  208. void RemoveFromLoginItems() {
  209. base::ScopedCFTypeRef<LSSharedFileListRef> list(
  210. LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL));
  211. if (!list) {
  212. LOG(ERROR) << "Unable to access shared file list";
  213. return;
  214. }
  215. if (GetLoginItemForApp() != NULL) {
  216. base::scoped_nsobject<NSArray> login_items_array(
  217. base::mac::CFToNSCast(LSSharedFileListCopySnapshot(list, NULL)));
  218. if (!login_items_array) {
  219. LOG(ERROR) << "No items in list of auto-loaded apps";
  220. return;
  221. }
  222. for (NSUInteger i = 0; i < [login_items_array count]; ++i) {
  223. LSSharedFileListItemRef item =
  224. reinterpret_cast<LSSharedFileListItemRef>(login_items_array[i]);
  225. base::ScopedCFTypeRef<CFErrorRef> error;
  226. CFURLRef url_ref =
  227. LSSharedFileListItemCopyResolvedURL(item, 0, error.InitializeInto());
  228. if (!error && url_ref) {
  229. base::ScopedCFTypeRef<CFURLRef> url(url_ref);
  230. if ([[base::mac::CFToNSCast(url.get()) path]
  231. hasPrefix:[[NSBundle mainBundle] bundlePath]])
  232. LSSharedFileListItemRemove(list, item);
  233. }
  234. }
  235. }
  236. }
  237. void Browser::SetLoginItemSettings(LoginItemSettings settings) {
  238. #if defined(MAS_BUILD)
  239. if (!platform_util::SetLoginItemEnabled(settings.open_at_login)) {
  240. LOG(ERROR) << "Unable to set login item enabled on sandboxed app.";
  241. }
  242. #else
  243. if (settings.open_at_login)
  244. base::mac::AddToLoginItems(settings.open_as_hidden);
  245. else {
  246. RemoveFromLoginItems();
  247. }
  248. #endif
  249. }
  250. std::string Browser::GetExecutableFileVersion() const {
  251. return GetApplicationVersion();
  252. }
  253. std::string Browser::GetExecutableFileProductName() const {
  254. return GetApplicationName();
  255. }
  256. int Browser::DockBounce(BounceType type) {
  257. return [[AtomApplication sharedApplication]
  258. requestUserAttention:static_cast<NSRequestUserAttentionType>(type)];
  259. }
  260. void Browser::DockCancelBounce(int request_id) {
  261. [[AtomApplication sharedApplication] cancelUserAttentionRequest:request_id];
  262. }
  263. void Browser::DockSetBadgeText(const std::string& label) {
  264. NSDockTile* tile = [[AtomApplication sharedApplication] dockTile];
  265. [tile setBadgeLabel:base::SysUTF8ToNSString(label)];
  266. }
  267. void Browser::DockDownloadFinished(const std::string& filePath) {
  268. [[NSDistributedNotificationCenter defaultCenter]
  269. postNotificationName:@"com.apple.DownloadFileFinished"
  270. object:base::SysUTF8ToNSString(filePath)];
  271. }
  272. std::string Browser::DockGetBadgeText() {
  273. NSDockTile* tile = [[AtomApplication sharedApplication] dockTile];
  274. return base::SysNSStringToUTF8([tile badgeLabel]);
  275. }
  276. void Browser::DockHide() {
  277. for (auto* const& window : WindowList::GetWindows())
  278. [window->GetNativeWindow().GetNativeNSWindow() setCanHide:NO];
  279. ProcessSerialNumber psn = {0, kCurrentProcess};
  280. TransformProcessType(&psn, kProcessTransformToUIElementApplication);
  281. }
  282. bool Browser::DockIsVisible() {
  283. // Because DockShow has a slight delay this may not be true immediately
  284. // after that call.
  285. return ([[NSRunningApplication currentApplication] activationPolicy] ==
  286. NSApplicationActivationPolicyRegular);
  287. }
  288. v8::Local<v8::Promise> Browser::DockShow(v8::Isolate* isolate) {
  289. util::Promise promise(isolate);
  290. v8::Local<v8::Promise> handle = promise.GetHandle();
  291. BOOL active = [[NSRunningApplication currentApplication] isActive];
  292. ProcessSerialNumber psn = {0, kCurrentProcess};
  293. if (active) {
  294. // Workaround buggy behavior of TransformProcessType.
  295. // http://stackoverflow.com/questions/7596643/
  296. NSArray* runningApps = [NSRunningApplication
  297. runningApplicationsWithBundleIdentifier:@"com.apple.dock"];
  298. for (NSRunningApplication* app in runningApps) {
  299. [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
  300. break;
  301. }
  302. __block util::Promise p = std::move(promise);
  303. dispatch_time_t one_ms = dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC);
  304. dispatch_after(one_ms, dispatch_get_main_queue(), ^{
  305. TransformProcessType(&psn, kProcessTransformToForegroundApplication);
  306. dispatch_time_t one_ms = dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC);
  307. dispatch_after(one_ms, dispatch_get_main_queue(), ^{
  308. [[NSRunningApplication currentApplication]
  309. activateWithOptions:NSApplicationActivateIgnoringOtherApps];
  310. p.Resolve();
  311. });
  312. });
  313. } else {
  314. TransformProcessType(&psn, kProcessTransformToForegroundApplication);
  315. promise.Resolve();
  316. }
  317. return handle;
  318. }
  319. void Browser::DockSetMenu(AtomMenuModel* model) {
  320. AtomApplicationDelegate* delegate =
  321. (AtomApplicationDelegate*)[NSApp delegate];
  322. [delegate setApplicationDockMenu:model];
  323. }
  324. void Browser::DockSetIcon(const gfx::Image& image) {
  325. [[AtomApplication sharedApplication]
  326. setApplicationIconImage:image.AsNSImage()];
  327. }
  328. void Browser::ShowAboutPanel() {
  329. NSDictionary* options = DictionaryValueToNSDictionary(about_panel_options_);
  330. // Credits must be a NSAttributedString instead of NSString
  331. NSString* credits = (NSString*)options[@"Credits"];
  332. if (credits != nil) {
  333. base::scoped_nsobject<NSMutableDictionary> mutable_options(
  334. [options mutableCopy]);
  335. base::scoped_nsobject<NSAttributedString> creditString(
  336. [[NSAttributedString alloc]
  337. initWithString:credits
  338. attributes:@{
  339. NSForegroundColorAttributeName : [NSColor textColor]
  340. }]);
  341. [mutable_options setValue:creditString forKey:@"Credits"];
  342. options = [NSDictionary dictionaryWithDictionary:mutable_options];
  343. }
  344. [[AtomApplication sharedApplication]
  345. orderFrontStandardAboutPanelWithOptions:options];
  346. }
  347. void Browser::SetAboutPanelOptions(const base::DictionaryValue& options) {
  348. about_panel_options_.Clear();
  349. for (const auto& pair : options) {
  350. std::string key = pair.first;
  351. const auto& val = pair.second;
  352. if (!key.empty() && val->is_string()) {
  353. key[0] = base::ToUpperASCII(key[0]);
  354. about_panel_options_.SetString(key, val->GetString());
  355. }
  356. }
  357. }
  358. void Browser::ShowEmojiPanel() {
  359. [[AtomApplication sharedApplication] orderFrontCharacterPalette:nil];
  360. }
  361. bool Browser::IsEmojiPanelSupported() {
  362. return true;
  363. }
  364. } // namespace atom