Skip to content
This repository has been archived by the owner on Jan 4, 2019. It is now read-only.

Commit

Permalink
Extend default protocol client
Browse files Browse the repository at this point in the history
1. Set default protocol for win8 and win10
2. Get/Set default protocol for linux

Auditors: @bridiver, @bbondy
  • Loading branch information
darkdh committed Oct 17, 2016
1 parent 073f337 commit 016f564
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 4 deletions.
105 changes: 103 additions & 2 deletions atom/browser/browser_linux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,55 @@

#include "atom/browser/browser.h"

#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "atom/browser/native_window.h"
#include "atom/browser/window_list.h"
#include "atom/common/atom_version.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/process/launch.h"
#include "brightray/common/application_info.h"
#include "chrome/browser/ui/libgtk2ui/unity_service.h"

const char kXdgSettings[] = "xdg-settings";
const char kXdgSettingsDefaultBrowser[] = "default-web-browser";
const char kXdgSettingsDefaultSchemeHandler[] = "default-url-scheme-handler";

// Value returned by xdg-settings if it can't understand our request.
const int EXIT_XDG_SETTINGS_SYNTAX_ERROR = 1;

// Helper to launch xdg scripts. We don't want them to ask any questions on the
// terminal etc. The function returns true if the utility launches and exits
// cleanly, in which case |exit_code| returns the utility's exit code.
bool LaunchXdgUtility(const std::vector<std::string>& argv, int* exit_code) {
// xdg-settings internally runs xdg-mime, which uses mv to move newly-created
// files on top of originals after making changes to them. In the event that
// the original files are owned by another user (e.g. root, which can happen
// if they are updated within sudo), mv will prompt the user to confirm if
// standard input is a terminal (otherwise it just does it). So make sure it's
// not, to avoid locking everything up waiting for mv.
*exit_code = EXIT_FAILURE;
int devnull = open("/dev/null", O_RDONLY);
if (devnull < 0)
return false;
base::FileHandleMappingVector no_stdin;
no_stdin.push_back(std::make_pair(devnull, STDIN_FILENO));

base::LaunchOptions options;
options.fds_to_remap = &no_stdin;
base::Process process = base::LaunchProcess(argv, options);
close(devnull);
if (!process.IsValid())
return false;
return process.WaitForExit(exit_code);
}

namespace atom {

void Browser::Focus() {
Expand Down Expand Up @@ -40,14 +81,74 @@ bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol,
return false;
}

// We delegate the difficulty of setting the default browser and default url
// scheme handler in Linux desktop environments to an xdg utility, xdg-settings.

// When calling this script we first try to use the script on PATH.

// If |protocol| is empty this function sets Brave as the default browser,
// otherwise it sets Brave as the default handler application for |protocol|.
bool Browser::SetAsDefaultProtocolClient(const std::string& protocol,
mate::Arguments* args) {
return false;
std::unique_ptr<base::Environment> env(base::Environment::Create());

std::vector<std::string> argv;
argv.push_back(kXdgSettings);
argv.push_back("set");
if (protocol.empty()) {
argv.push_back(kXdgSettingsDefaultBrowser);
} else {
argv.push_back(kXdgSettingsDefaultSchemeHandler);
argv.push_back(protocol);
}
// TODO(Anthony): Make it configurable
argv.push_back("brave.desktop");

int exit_code;
bool ran_ok = LaunchXdgUtility(argv, &exit_code);
if (ran_ok && exit_code == EXIT_XDG_SETTINGS_SYNTAX_ERROR) {
return false;
}

return ran_ok && exit_code == EXIT_SUCCESS;
}

// If |protocol| is empty this function checks if Brave is the default browser,
// otherwise it checks if Brave is the default handler application for
// |protocol|.
bool Browser::IsDefaultProtocolClient(const std::string& protocol,
mate::Arguments* args) {
return false;
base::ThreadRestrictions::AssertIOAllowed();

std::unique_ptr<base::Environment> env(base::Environment::Create());

std::vector<std::string> argv;
argv.push_back(kXdgSettings);
argv.push_back("check");
if (protocol.empty()) {
argv.push_back(kXdgSettingsDefaultBrowser);
} else {
argv.push_back(kXdgSettingsDefaultSchemeHandler);
argv.push_back(protocol);
}
// TODO(Anthony): Make it configurable
argv.push_back("brave.desktop");

std::string reply;
int success_code;
bool ran_ok = base::GetAppOutputWithExitCode(base::CommandLine(argv), &reply,
&success_code);
if (ran_ok && success_code == EXIT_XDG_SETTINGS_SYNTAX_ERROR) {
return false;
}

if (!ran_ok || success_code != EXIT_SUCCESS) {
// xdg-settings failed: we can't determine or set the default browser.
return false;
}

// Allow any reply that starts with "yes".
return (reply.find("yes") == 0) ? true : false;
}

bool Browser::SetBadgeCount(int count) {
Expand Down
51 changes: 51 additions & 0 deletions atom/browser/browser_win.cc
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "base/win/scoped_comptr.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"

Expand Down Expand Up @@ -171,6 +172,52 @@ bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol,
}
}

// Returns the target used as a activate parameter when opening the settings
// pointing to the page that is the most relevant to a user trying to change the
// default handler for |protocol|.
base::string16 GetTargetForDefaultAppsSettings(const wchar_t* protocol) {
static const wchar_t kSystemSettingsDefaultAppsFormat[] =
L"SystemSettings_DefaultApps_%ls";

if (base::EqualsCaseInsensitiveASCII(protocol, L"http"))
return base::StringPrintf(kSystemSettingsDefaultAppsFormat, L"Browser");
if (base::EqualsCaseInsensitiveASCII(protocol, L"mailto"))
return base::StringPrintf(kSystemSettingsDefaultAppsFormat, L"Email");
return L"SettingsPageAppsDefaultsProtocolView";
}

// Launches the Windows 'settings' modern app with the 'default apps' view
// focused. This only works for Windows 8 and Windows 10. The appModelId
// looks arbitrary but it is the same in Win8 and Win10. There is no easy way to
// retrieve the appModelId from the registry.
bool LaunchDefaultAppsSettingsModernDialog(const wchar_t* protocol) {
DCHECK(protocol);
static const wchar_t kControlPanelAppModelId[] =
L"windows.immersivecontrolpanel_cw5n1h2txyewy"
L"!microsoft.windows.immersivecontrolpanel";

base::win::ScopedComPtr<IApplicationActivationManager> activator;
HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
if (SUCCEEDED(hr)) {
DWORD pid = 0;
CoAllowSetForegroundWindow(activator.get(), nullptr);
hr = activator->ActivateApplication(kControlPanelAppModelId,
L"page=SettingsPageAppsDefaults",
AO_NONE, &pid);
if (SUCCEEDED(hr)) {
hr = activator->ActivateApplication(
kControlPanelAppModelId,
base::StringPrintf(L"page=SettingsPageAppsDefaults&target=%ls",
GetTargetForDefaultAppsSettings(protocol).c_str())
.c_str(),
AO_NONE, &pid);
}
if (SUCCEEDED(hr))
return true;
}
return false;
}

bool Browser::SetAsDefaultProtocolClient(const std::string& protocol,
mate::Arguments* args) {
// HKEY_CLASSES_ROOT
Expand All @@ -190,6 +237,10 @@ bool Browser::SetAsDefaultProtocolClient(const std::string& protocol,
if (protocol.empty())
return false;

if (base::win::GetVersion() >= base::win::VERSION_WIN8)
return LaunchDefaultAppsSettingsModernDialog(
base::UTF8ToUTF16(protocol).c_str());

base::string16 exe;
if (!GetProtocolLaunchPath(args, &exe))
return false;
Expand Down
4 changes: 2 additions & 2 deletions docs/api/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ bar, and on macOS you can visit it from dock menu.

Clears the recent documents list.

### `app.setAsDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_
### `app.setAsDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_ _Brave on Linux_

* `protocol` String - The name of your protocol, without `://`. If you want your
app to handle `electron://` links, call this method with `electron` as the
Expand Down Expand Up @@ -496,7 +496,7 @@ protocol (aka URI scheme). If so, it will remove the app as the default handler.

Returns `true` when the call succeeded, otherwise returns `false`.

### `app.isDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_
### `app.isDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_ _Brave on Linux_

* `protocol` String - The name of your protocol, without `://`.
* `path` String (optional) _Windows_ - Defaults to `process.execPath`
Expand Down

0 comments on commit 016f564

Please sign in to comment.