Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / shell_integration_mac.mm
blob9507d3df6cbb20646e6d712f206da8128f371708
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/shell_integration.h"
7 #include "base/mac/bundle_locations.h"
8 #include "base/mac/foundation_util.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "chrome/common/channel_info.h"
11 #include "components/version_info/version_info.h"
12 #import "third_party/mozilla/NSWorkspace+Utils.h"
14 ShellIntegration::DefaultWebClientSetPermission
15     ShellIntegration::CanSetAsDefaultBrowser() {
16   if (chrome::GetChannel() != version_info::Channel::CANARY) {
17     return SET_DEFAULT_UNATTENDED;
18   }
20   return SET_DEFAULT_NOT_ALLOWED;
23 // Sets Chromium as default browser to be used by the operating system. This
24 // applies only for the current user. Returns false if this cannot be done, or
25 // if the operation fails.
26 bool ShellIntegration::SetAsDefaultBrowser() {
27   if (CanSetAsDefaultBrowser() != SET_DEFAULT_UNATTENDED)
28     return false;
30   // We really do want the outer bundle here, not the main bundle since setting
31   // a shortcut to Chrome as the default browser doesn't make sense.
32   NSString* identifier = [base::mac::OuterBundle() bundleIdentifier];
33   if (!identifier)
34     return false;
36   [[NSWorkspace sharedWorkspace] setDefaultBrowserWithIdentifier:identifier];
37   return true;
40 // Sets Chromium as the default application to be used by the operating system
41 // for the given protocol. This applies only for the current user. Returns false
42 // if this cannot be done, or if the operation fails.
43 bool ShellIntegration::SetAsDefaultProtocolClient(const std::string& protocol) {
44   if (protocol.empty())
45     return false;
47   if (CanSetAsDefaultProtocolClient() != SET_DEFAULT_UNATTENDED)
48     return false;
50   // We really do want the main bundle here since it makes sense to set an
51   // app shortcut as a default protocol handler.
52   NSString* identifier = [base::mac::MainBundle() bundleIdentifier];
53   if (!identifier)
54     return false;
56   NSString* protocol_ns = [NSString stringWithUTF8String:protocol.c_str()];
57   OSStatus return_code =
58       LSSetDefaultHandlerForURLScheme(base::mac::NSToCFCast(protocol_ns),
59                                       base::mac::NSToCFCast(identifier));
60   return return_code == noErr;
63 namespace {
65 // Returns true if |identifier| is the bundle id of the default browser.
66 bool IsIdentifierDefaultBrowser(NSString* identifier) {
67   NSString* default_browser =
68       [[NSWorkspace sharedWorkspace] defaultBrowserIdentifier];
69   if (!default_browser)
70     return false;
72   // We need to ensure we do the comparison case-insensitive as LS doesn't
73   // persist the case of our bundle id.
74   NSComparisonResult result =
75       [default_browser caseInsensitiveCompare:identifier];
76   return result == NSOrderedSame;
79 // Returns true if |identifier| is the bundle id of the default client
80 // application for the given protocol.
81 bool IsIdentifierDefaultProtocolClient(NSString* identifier,
82                                        NSString* protocol) {
83   CFStringRef default_client_cf =
84       LSCopyDefaultHandlerForURLScheme(base::mac::NSToCFCast(protocol));
85   NSString* default_client = static_cast<NSString*>(
86       base::mac::CFTypeRefToNSObjectAutorelease(default_client_cf));
87   if (!default_client)
88     return false;
90   // We need to ensure we do the comparison case-insensitive as LS doesn't
91   // persist the case of our bundle id.
92   NSComparisonResult result =
93       [default_client caseInsensitiveCompare:identifier];
94   return result == NSOrderedSame;
97 }  // namespace
99 // static
100 base::string16 ShellIntegration::GetApplicationNameForProtocol(
101     const GURL& url) {
102   NSURL* ns_url = [NSURL URLWithString:
103       base::SysUTF8ToNSString(url.possibly_invalid_spec())];
104   CFURLRef openingApp = NULL;
105   OSStatus status = LSGetApplicationForURL((CFURLRef)ns_url,
106                                            kLSRolesAll,
107                                            NULL,
108                                            &openingApp);
109   if (status != noErr) {
110     // likely kLSApplicationNotFoundErr
111     return base::string16();
112   }
113   NSString* appPath = [(NSURL*)openingApp path];
114   CFRelease(openingApp);  // NOT A BUG; LSGetApplicationForURL retains for us
115   NSString* appDisplayName =
116       [[NSFileManager defaultManager] displayNameAtPath:appPath];
117   return base::SysNSStringToUTF16(appDisplayName);
120 // Attempt to determine if this instance of Chrome is the default browser and
121 // return the appropriate state. (Defined as being the handler for HTTP/HTTPS
122 // protocols; we don't want to report "no" here if the user has simply chosen
123 // to open HTML files in a text editor and FTP links with an FTP client.)
124 ShellIntegration::DefaultWebClientState ShellIntegration::GetDefaultBrowser() {
125   // We really do want the outer bundle here, since this we want to know the
126   // status of the main Chrome bundle and not a shortcut.
127   NSString* my_identifier = [base::mac::OuterBundle() bundleIdentifier];
128   if (!my_identifier)
129     return UNKNOWN_DEFAULT;
131   return IsIdentifierDefaultBrowser(my_identifier) ? IS_DEFAULT : NOT_DEFAULT;
134 // Returns true if Firefox is the default browser for the current user.
135 bool ShellIntegration::IsFirefoxDefaultBrowser() {
136   return IsIdentifierDefaultBrowser(@"org.mozilla.firefox");
139 // Attempt to determine if this instance of Chrome is the default client
140 // application for the given protocol and return the appropriate state.
141 ShellIntegration::DefaultWebClientState
142     ShellIntegration::IsDefaultProtocolClient(const std::string& protocol) {
143   if (protocol.empty())
144     return UNKNOWN_DEFAULT;
146   // We really do want the main bundle here since it makes sense to set an
147   // app shortcut as a default protocol handler.
148   NSString* my_identifier = [base::mac::MainBundle() bundleIdentifier];
149   if (!my_identifier)
150     return UNKNOWN_DEFAULT;
152   NSString* protocol_ns = [NSString stringWithUTF8String:protocol.c_str()];
153   return IsIdentifierDefaultProtocolClient(my_identifier, protocol_ns) ?
154       IS_DEFAULT : NOT_DEFAULT;