| // 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 |