fix: don't register some shortcuts without accessibility (#16125)

Fixed crash on macOS when using globalShortcut for media keys when accessibility access is not granted.
This commit is contained in:
Shelley Vohr 2019-01-03 20:40:17 -08:00 committed by GitHub
parent b57046e67d
commit 876064036d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 3 deletions

View file

@ -7,15 +7,44 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "atom/browser/api/atom_api_system_preferences.h"
#include "atom/common/native_mate_converters/accelerator_converter.h" #include "atom/common/native_mate_converters/accelerator_converter.h"
#include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/callback.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "native_mate/dictionary.h" #include "native_mate/dictionary.h"
#include "atom/common/node_includes.h" #include "atom/common/node_includes.h"
#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#endif
using extensions::GlobalShortcutListener; using extensions::GlobalShortcutListener;
namespace {
#if defined(OS_MACOSX)
bool RegisteringMediaKeyForUntrustedClient(const ui::Accelerator& accelerator) {
if (base::mac::IsAtLeastOS10_14()) {
constexpr ui::KeyboardCode mediaKeys[] = {
ui::VKEY_MEDIA_PLAY_PAUSE, ui::VKEY_MEDIA_NEXT_TRACK,
ui::VKEY_MEDIA_PREV_TRACK, ui::VKEY_MEDIA_STOP};
if (std::find(std::begin(mediaKeys), std::end(mediaKeys),
accelerator.key_code()) != std::end(mediaKeys)) {
bool trusted =
atom::api::SystemPreferences::IsTrustedAccessibilityClient(false);
if (!trusted)
return true;
}
}
return false;
}
#endif
} // namespace
namespace atom { namespace atom {
namespace api { namespace api {
@ -32,7 +61,7 @@ void GlobalShortcut::OnKeyPressed(const ui::Accelerator& accelerator) {
if (accelerator_callback_map_.find(accelerator) == if (accelerator_callback_map_.find(accelerator) ==
accelerator_callback_map_.end()) { accelerator_callback_map_.end()) {
// This should never occur, because if it does, GlobalGlobalShortcutListener // This should never occur, because if it does, GlobalGlobalShortcutListener
// notifes us with wrong accelerator. // notifies us with wrong accelerator.
NOTREACHED(); NOTREACHED();
return; return;
} }
@ -43,14 +72,19 @@ bool GlobalShortcut::RegisterAll(
const std::vector<ui::Accelerator>& accelerators, const std::vector<ui::Accelerator>& accelerators,
const base::Closure& callback) { const base::Closure& callback) {
std::vector<ui::Accelerator> registered; std::vector<ui::Accelerator> registered;
for (auto& accelerator : accelerators) { for (auto& accelerator : accelerators) {
#if defined(OS_MACOSX)
if (RegisteringMediaKeyForUntrustedClient(accelerator))
return false;
GlobalShortcutListener* listener = GlobalShortcutListener::GetInstance(); GlobalShortcutListener* listener = GlobalShortcutListener::GetInstance();
if (!listener->RegisterAccelerator(accelerator, this)) { if (!listener->RegisterAccelerator(accelerator, this)) {
// unregister all shortcuts if any failed // unregister all shortcuts if any failed
UnregisterSome(registered); UnregisterSome(registered);
return false; return false;
} }
#endif
registered.push_back(accelerator); registered.push_back(accelerator);
accelerator_callback_map_[accelerator] = callback; accelerator_callback_map_[accelerator] = callback;
} }
@ -59,6 +93,11 @@ bool GlobalShortcut::RegisterAll(
bool GlobalShortcut::Register(const ui::Accelerator& accelerator, bool GlobalShortcut::Register(const ui::Accelerator& accelerator,
const base::Closure& callback) { const base::Closure& callback) {
#if defined(OS_MACOSX)
if (RegisteringMediaKeyForUntrustedClient(accelerator))
return false;
#endif
if (!GlobalShortcutListener::GetInstance()->RegisterAccelerator(accelerator, if (!GlobalShortcutListener::GetInstance()->RegisterAccelerator(accelerator,
this)) { this)) {
return false; return false;

View file

@ -95,7 +95,7 @@ class SystemPreferences : public mate::EventEmitter<SystemPreferences>
std::string GetSystemColor(const std::string& color, mate::Arguments* args); std::string GetSystemColor(const std::string& color, mate::Arguments* args);
bool IsTrustedAccessibilityClient(bool prompt); static bool IsTrustedAccessibilityClient(bool prompt);
// TODO(codebytere): Write tests for these methods once we // TODO(codebytere): Write tests for these methods once we
// are running tests on a Mojave machine // are running tests on a Mojave machine

View file

@ -437,6 +437,7 @@ std::string SystemPreferences::GetSystemColor(const std::string& color,
} }
} }
// static
bool SystemPreferences::IsTrustedAccessibilityClient(bool prompt) { bool SystemPreferences::IsTrustedAccessibilityClient(bool prompt) {
NSDictionary* options = @{(id)kAXTrustedCheckOptionPrompt : @(prompt)}; NSDictionary* options = @{(id)kAXTrustedCheckOptionPrompt : @(prompt)};
return AXIsProcessTrustedWithOptions((CFDictionaryRef)options); return AXIsProcessTrustedWithOptions((CFDictionaryRef)options);

View file

@ -54,6 +54,14 @@ When the accelerator is already taken by other applications, this call will
silently fail. This behavior is intended by operating systems, since they don't silently fail. This behavior is intended by operating systems, since they don't
want applications to fight for global shortcuts. want applications to fight for global shortcuts.
The following accelerators will not be registered successfully on macOS 10.14 Mojave unless
the app has been authorized as a [trusted accessibility client](https://developer.apple.com/library/archive/documentation/Accessibility/Conceptual/AccessibilityMacOSX/OSXAXTestingApps.html):
* "Media Play/Pause"
* "Media Next Track"
* "Media Previous Track"
* "Media Stop"
### `globalShortcut.registerAll(accelerators, callback)` ### `globalShortcut.registerAll(accelerators, callback)`
* `accelerators` String[] - an array of [Accelerator](accelerator.md)s. * `accelerators` String[] - an array of [Accelerator](accelerator.md)s.
@ -65,6 +73,14 @@ When a given accelerator is already taken by other applications, this call will
silently fail. This behavior is intended by operating systems, since they don't silently fail. This behavior is intended by operating systems, since they don't
want applications to fight for global shortcuts. want applications to fight for global shortcuts.
The following accelerators will not be registered successfully on macOS 10.14 Mojave unless
the app has been authorized as a [trusted accessibility client](https://developer.apple.com/library/archive/documentation/Accessibility/Conceptual/AccessibilityMacOSX/OSXAXTestingApps.html):
* "Media Play/Pause"
* "Media Next Track"
* "Media Previous Track"
* "Media Stop"
### `globalShortcut.isRegistered(accelerator)` ### `globalShortcut.isRegistered(accelerator)`
* `accelerator` [Accelerator](accelerator.md) * `accelerator` [Accelerator](accelerator.md)