blob: d8021d7921a4857068f234712237e89f3cfd2814 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <atk/atk.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include "base/environment.h"
#include "base/memory/singleton.h"
#include "base/no_destructor.h"
#include "ui/accessibility/platform/atk_util_auralinux.h"
#include "ui/accessibility/platform/ax_platform_node.h"
#include "ui/accessibility/platform/ax_platform_node_auralinux.h"
namespace {
const char* kAccessibilityEnabledVariables[] = {
"ACCESSIBILITY_ENABLED",
"GNOME_ACCESSIBILITY",
"QT_ACCESSIBILITY",
};
//
// AtkUtilAuraLinux definition and implementation.
//
struct AtkUtilAuraLinux {
AtkUtil parent;
};
struct AtkUtilAuraLinuxClass {
AtkUtilClass parent_class;
};
G_DEFINE_TYPE(AtkUtilAuraLinux, atk_util_auralinux, ATK_TYPE_UTIL)
static void atk_util_auralinux_init(AtkUtilAuraLinux* ax_util) {}
static AtkObject* AtkUtilAuraLinuxGetRoot() {
ui::AXPlatformNode* application = ui::AXPlatformNodeAuraLinux::application();
if (application) {
return application->GetNativeViewAccessible();
}
return nullptr;
}
using KeySnoopFuncMap = std::map<guint, std::pair<AtkKeySnoopFunc, gpointer>>;
static KeySnoopFuncMap& GetActiveKeySnoopFunctions() {
static base::NoDestructor<KeySnoopFuncMap> active_key_snoop_functions;
return *active_key_snoop_functions;
}
using AXPlatformNodeSet = std::set<ui::AXPlatformNodeAuraLinux*>;
static AXPlatformNodeSet& GetNodesWithPostponedEvents() {
static base::NoDestructor<AXPlatformNodeSet> nodes_with_postponed_events_list;
return *nodes_with_postponed_events_list;
}
static void RunPostponedEvents() {
for (ui::AXPlatformNodeAuraLinux* entry : GetNodesWithPostponedEvents()) {
entry->RunPostponedEvents();
}
GetNodesWithPostponedEvents().clear();
}
static guint AtkUtilAuraLinuxAddKeyEventListener(
AtkKeySnoopFunc key_snoop_function,
gpointer data) {
static guint current_key_event_listener_id = 0;
// AtkUtilAuraLinuxAddKeyEventListener is called by
// spi_atk_register_event_listeners in the at-spi2-atk library after it has
// initialized all its other listeners. Our internal knowledge of the
// internals of the AT-SPI/ATK bridge allows us to use this call as an
// indication of AT-SPI being ready, so we can finally run any events that had
// been waiting.
ui::AtkUtilAuraLinux::GetInstance()->SetAtSpiReady(true);
current_key_event_listener_id++;
GetActiveKeySnoopFunctions()[current_key_event_listener_id] =
std::make_pair(key_snoop_function, data);
return current_key_event_listener_id;
}
static void AtkUtilAuraLinuxRemoveKeyEventListener(guint listener_id) {
GetActiveKeySnoopFunctions().erase(listener_id);
}
static void atk_util_auralinux_class_init(AtkUtilAuraLinuxClass* klass) {
AtkUtilClass* atk_class = ATK_UTIL_CLASS(g_type_class_peek(ATK_TYPE_UTIL));
atk_class->get_root = AtkUtilAuraLinuxGetRoot;
atk_class->get_toolkit_name = []() { return "Chromium"; };
atk_class->get_toolkit_version = []() { return "1.0"; };
atk_class->add_key_event_listener = AtkUtilAuraLinuxAddKeyEventListener;
atk_class->remove_key_event_listener = AtkUtilAuraLinuxRemoveKeyEventListener;
}
} // namespace
namespace ui {
// static
AtkUtilAuraLinux* AtkUtilAuraLinux::GetInstance() {
return base::Singleton<AtkUtilAuraLinux>::get();
}
bool AtkUtilAuraLinux::ShouldEnableAccessibility() {
std::unique_ptr<base::Environment> env(base::Environment::Create());
for (const auto* variable : kAccessibilityEnabledVariables) {
std::string enable_accessibility;
env->GetVar(variable, &enable_accessibility);
if (enable_accessibility == "1")
return true;
}
return false;
}
void AtkUtilAuraLinux::InitializeAsync() {
static bool initialized = false;
if (initialized || !ShouldEnableAccessibility())
return;
initialized = true;
// Register our util class.
g_type_class_unref(g_type_class_ref(atk_util_auralinux_get_type()));
PlatformInitializeAsync();
}
void AtkUtilAuraLinux::InitializeForTesting() {
std::unique_ptr<base::Environment> env(base::Environment::Create());
env->SetVar(kAccessibilityEnabledVariables[0], "1");
InitializeAsync();
}
// static
// Disable CFI-icall since the key snooping function could be in another DSO.
__attribute__((no_sanitize("cfi-icall"))) DiscardAtkKeyEvent
AtkUtilAuraLinux::HandleAtkKeyEvent(AtkKeyEventStruct* key_event) {
DCHECK(key_event);
if (!ui::AXPlatformNode::GetAccessibilityMode().has_mode(
ui::AXMode::kNativeAPIs))
return DiscardAtkKeyEvent::Retain;
GetInstance()->InitializeAsync();
bool discard = false;
for (auto& entry : GetActiveKeySnoopFunctions()) {
AtkKeySnoopFunc key_snoop_function = entry.second.first;
gpointer data = entry.second.second;
// We want to ensure that all functions are called. We will discard this
// event if at least one function suggests that we do it, but we still
// need to call the functions that follow it in the map iterator.
if (key_snoop_function(key_event, data) != 0)
discard = true;
}
return discard ? DiscardAtkKeyEvent::Discard : DiscardAtkKeyEvent::Retain;
}
bool AtkUtilAuraLinux::IsAtSpiReady() {
return at_spi_ready_;
}
void AtkUtilAuraLinux::SetAtSpiReady(bool ready) {
at_spi_ready_ = ready;
if (ready)
RunPostponedEvents();
}
void AtkUtilAuraLinux::PostponeEventsFor(AXPlatformNodeAuraLinux* node) {
GetNodesWithPostponedEvents().insert(node);
}
void AtkUtilAuraLinux::CancelPostponedEventsFor(AXPlatformNodeAuraLinux* node) {
GetNodesWithPostponedEvents().erase(node);
}
} // namespace ui