diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index d728d9dd546b..ad5b7214ccd7 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -370,6 +370,10 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder( base::Bind(&Browser::ClearRecentDocuments, browser)) .SetMethod("setAppUserModelId", base::Bind(&Browser::SetAppUserModelID, browser)) + .SetMethod("setAsDefaultProtocolClient", + base::Bind(&Browser::SetAsDefaultProtocolClient, browser)) + .SetMethod("removeAsDefaultProtocolClient", + base::Bind(&Browser::RemoveAsDefaultProtocolClient, 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..634e14e60262 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -76,6 +76,12 @@ class Browser : public WindowListObserver { // Set the application user model ID. void SetAppUserModelID(const base::string16& name); + // Remove the default protocol handler registry key + bool RemoveAsDefaultProtocolClient(const std::string& protocol); + + // 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..6c7d4abaf645 100644 --- a/atom/browser/browser_linux.cc +++ b/atom/browser/browser_linux.cc @@ -34,6 +34,14 @@ void Browser::ClearRecentDocuments() { void Browser::SetAppUserModelID(const base::string16& name) { } +bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol) { + return false; +} + +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..0294894fcd61 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,25 @@ void Browser::ClearRecentDocuments() { [[NSDocumentController sharedDocumentController] clearRecentDocuments:nil]; } +bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol) { + return false; +} + +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..9531406f8cd4 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,105 @@ void Browser::SetUserTasks(const std::vector& tasks) { destinations->CommitList(); } +bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol) { + 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()); + + // Command Key + std::string cmdPathStr = keyPathStr + "\\shell\\open\\command"; + std::wstring cmdPath = std::wstring(cmdPathStr.begin(), cmdPathStr.end()); + + base::win::RegKey key; + base::win::RegKey commandKey; + if (FAILED(key.Open(root, keyPath.c_str(), KEY_ALL_ACCESS))) + // Key doesn't even exist, we can confirm that it is not set + return true; + + if (FAILED(commandKey.Open(root, cmdPath.c_str(), KEY_ALL_ACCESS))) + // Key doesn't even exist, we can confirm that it is not set + return true; + + std::wstring keyVal; + if (FAILED(commandKey.ReadValue(L"", &keyVal))) + // Default value not set, we can confirm that it is not set + return true; + + std::wstring exePath(path.value()); + std::wstring exe = L"\"" + exePath + L"\" \"%1\""; + if (keyVal == exe) { + // Let's kill the key + if (FAILED(key.DeleteKey(L"shell"))) + return false; + + return true; + } else { + return true; + } +} + +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; + + 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 1f139e08e608..903668c7f2bc 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -354,6 +354,35 @@ 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.removeAsDefaultProtocolClient(protocol)` _Windows_ + + * `protocol` String - The name of your protocol, without `://`. + +This method checks if the current executable as the default handler for a protocol +(aka URI scheme). If so, it will remove the app as the default handler. + +**Note:** On OS X, removing the app will automatically remove the app as the +default protocol handler. + ### `app.setUserTasks(tasks)` _Windows_ * `tasks` Array - Array of `Task` objects @@ -547,3 +576,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