diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index d728d9dd546b..00edcf70b23d 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -370,6 +370,8 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder( base::Bind(&Browser::ClearRecentDocuments, browser)) .SetMethod("setAppUserModelId", base::Bind(&Browser::SetAppUserModelID, browser)) + .SetMethod("setAsDefaultProtocolClient", + base::Bind(&Browser::SetAsDefaultProtocolClient, browser)) #if defined(OS_MACOSX) .SetMethod("hide", base::Bind(&Browser::Hide, browser)) .SetMethod("show", base::Bind(&Browser::Show, browser)) diff --git a/atom/browser/browser.h b/atom/browser/browser.h index d976fae675cc..38899c11eaee 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -76,6 +76,9 @@ class Browser : public WindowListObserver { // Set the application user model ID. void SetAppUserModelID(const base::string16& name); + // Set as default handler for a protocol. + bool SetAsDefaultProtocolClient(const std::string& protocol); + #if defined(OS_MACOSX) // Hide the application. void Hide(); diff --git a/atom/browser/browser_linux.cc b/atom/browser/browser_linux.cc index 25cb9a0a2385..586ec717a03a 100644 --- a/atom/browser/browser_linux.cc +++ b/atom/browser/browser_linux.cc @@ -34,6 +34,10 @@ void Browser::ClearRecentDocuments() { void Browser::SetAppUserModelID(const base::string16& name) { } +bool Browser::SetAsDefaultProtocolClient(const std::string& protocol) { + return false; +} + std::string Browser::GetExecutableFileVersion() const { return brightray::GetApplicationVersion(); } diff --git a/atom/browser/browser_mac.mm b/atom/browser/browser_mac.mm index 5988e6620438..c61a9bddf922 100644 --- a/atom/browser/browser_mac.mm +++ b/atom/browser/browser_mac.mm @@ -8,6 +8,7 @@ #include "atom/browser/mac/atom_application_delegate.h" #include "atom/browser/native_window.h" #include "atom/browser/window_list.h" +#include "base/mac/bundle_locations.h" #include "base/mac/foundation_util.h" #include "base/strings/sys_string_conversions.h" #include "brightray/common/application_info.h" @@ -45,6 +46,21 @@ void Browser::ClearRecentDocuments() { [[NSDocumentController sharedDocumentController] clearRecentDocuments:nil]; } +bool Browser::SetAsDefaultProtocolClient(const std::string& protocol) { + if (protocol.empty()) + return false; + + NSString* identifier = [base::mac::MainBundle() bundleIdentifier]; + if (!identifier) + return false; + + NSString* protocol_ns = [NSString stringWithUTF8String:protocol.c_str()]; + OSStatus return_code = + LSSetDefaultHandlerForURLScheme(base::mac::NSToCFCast(protocol_ns), + base::mac::NSToCFCast(identifier)); + return return_code == noErr; +} + void Browser::SetAppUserModelID(const base::string16& name) { } diff --git a/atom/browser/browser_win.cc b/atom/browser/browser_win.cc index fdf4bd8c3bbe..0b9fe545d5db 100644 --- a/atom/browser/browser_win.cc +++ b/atom/browser/browser_win.cc @@ -19,6 +19,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/win/win_util.h" +#include "base/win/registry.h" #include "base/win/windows_version.h" #include "atom/common/atom_version.h" @@ -125,6 +126,59 @@ void Browser::SetUserTasks(const std::vector& tasks) { destinations->CommitList(); } +bool Browser::SetAsDefaultProtocolClient(const std::string& protocol) { + // HKEY_CLASSES_ROOT + // $PROTOCOL + // (Default) = "URL:$NAME" + // URL Protocol = "" + // shell + // open + // command + // (Default) = "$COMMAND" "%1" + // + // However, the "HKEY_CLASSES_ROOT" key can only be written by the + // Administrator user. So, we instead write to "HKEY_CURRENT_USER\ + // Software\Classes", which is inherited by "HKEY_CLASSES_ROOT" + // anyway, and can be written by unprivileged users. + + if (protocol.empty()) + return false; + + base::FilePath path; + if (!PathService::Get(base::FILE_EXE, &path)) { + LOG(ERROR) << "Error getting app exe path"; + return false; + } + + // Main Registry Key + HKEY root = HKEY_CURRENT_USER; + std::string keyPathStr = "Software\\Classes\\" + protocol; + std::wstring keyPath = std::wstring(keyPathStr.begin(), keyPathStr.end()); + std::string urlDeclStr = "URL:" + protocol; + std::wstring urlDecl = std::wstring(urlDeclStr.begin(), urlDeclStr.end()); + + // Command Key + std::string cmdPathStr = keyPathStr + "\\shell\\open\\command"; + std::wstring cmdPath = std::wstring(cmdPathStr.begin(), cmdPathStr.end()); + + // Executable Path + std::wstring exePath(path.value()); + std::wstring exe = L"\"" + exePath + L"\" \"%1\""; + + // Write information to registry + base::win::RegKey key(root, keyPath.c_str(), KEY_ALL_ACCESS); + if (FAILED(key.WriteValue(L"URL Protocol", L"")) || + FAILED(key.WriteValue(L"", urlDecl.c_str()))) + return false; + + base::win::RegKey commandKey(root, cmdPath.c_str(), KEY_ALL_ACCESS); + if (FAILED(commandKey.WriteValue(L"", exe.c_str()))) + return false; + + VLOG(1) << "Chrome registered as default handler for " << protocol << "."; + return true; +} + PCWSTR Browser::GetAppUserModelID() { if (app_user_model_id_.empty()) { SetAppUserModelID(base::ReplaceStringPlaceholders( diff --git a/docs/api/app.md b/docs/api/app.md index 8935ed894f09..e9bd1066a335 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -348,6 +348,25 @@ bar, and on OS X you can visit it from dock menu. Clears the recent documents list. +### `app.setAsDefaultProtocolClient(protocol)` _OS X_ _Windows_ + + * `protocol` String - The name of your protocol, without `://`. If you want your + app to handle `electron://` links, call this method with `electron` as the + parameter. + +This method sets the current executable as the default handler for a protocol +(aka URI scheme). It allows you to integrate your app deeper into the operating +system. Once registered, all links with `your-protocol://` will be openend with +the current executable. The whole link, including protocol, will be passed to +your application as a parameter. + +**Note:** On OS X, you can only register protocols that have been added to +your app's `info.plist`, which can not be modified at runtime. You can however +change the file with a simple text editor or script during build time. +Please refer to [Apple's documentation][CFBundleURLTypes] for details. + +The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally. + ### `app.setUserTasks(tasks)` _Windows_ * `tasks` Array - Array of `Task` objects @@ -541,3 +560,4 @@ Sets the `image` associated with this dock icon. [dock-menu]:https://developer.apple.com/library/mac/documentation/Carbon/Conceptual/customizing_docktile/concepts/dockconcepts.html#//apple_ref/doc/uid/TP30000986-CH2-TPXREF103 [tasks]:http://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx +[CFBundleURLTypes]: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115 \ No newline at end of file