File: atk_util_auralinux.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (245 lines) | stat: -rw-r--r-- 7,497 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
// Copyright 2015 The Chromium Authors
// 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 <array>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>

#if defined(USE_GIO)
#include <gio/gio.h>
#endif

#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.h"
#include "ui/accessibility/platform/ax_platform_node.h"
#include "ui/accessibility/platform/ax_platform_node_auralinux.h"
#include "ui/base/glib/gsettings.h"

namespace {

constexpr auto kAccessibilityEnabledVariables =
    std::to_array<base::cstring_view>({
        "ACCESSIBILITY_ENABLED",
        "GNOME_ACCESSIBILITY",
        "QT_ACCESSIBILITY",
    });

//
// AtkUtilAuraLinux definition and implementation.
//
struct AtkUtilAuraLinux {
  AtkUtil parent;
};

struct AtkUtilAuraLinuxClass {
  AtkUtilClass parent_class;
};

// SAFETY: Usage of third-party library macro is outside our control.
UNSAFE_BUFFERS(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() {
  // Check enabled/disabled accessibility based on env variable
  std::unique_ptr<base::Environment> env(base::Environment::Create());
  for (base::cstring_view variable : kAccessibilityEnabledVariables) {
    std::string enable_accessibility =
        env->GetVar(variable).value_or(std::string());
    if (enable_accessibility == "1")
      return true;
    if (enable_accessibility == "0") {
      return false;
    }
  }

#if defined(USE_GIO)
  // Do not run additional checks when Chrome runs in headless mode, which means
  // we are in a test environment
  std::string chrome_headless =
      env->GetVar("CHROME_HEADLESS").value_or(std::string());
  if (chrome_headless == "1") {
    return false;
  }

  // Check enabled accessibility based on a11y DBus interface
  GDBusConnection* connection =
      g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);

  if (connection) {
    GVariant* result = g_dbus_connection_call_sync(
        connection, "org.a11y.Bus", "/org/a11y/bus",
        "org.freedesktop.DBus.Properties", "Get",
        g_variant_new("(ss)", "org.a11y.Status", "IsEnabled"), nullptr,
        G_DBUS_CALL_FLAGS_NONE, -1, nullptr, nullptr);
    g_object_unref(connection);

    if (result) {
      GVariant* property;
      g_variant_get(result, "(v)", &property);
      const bool accessibilityEnabled = g_variant_get_boolean(property);
      g_variant_unref(result);
      g_variant_unref(property);
      return accessibilityEnabled;
    }
  }

  // Check enabled accessibility based on GSettings
  auto settings = ui::GSettingsNew("org.gnome.desktop.interface");
  if (settings) {
    return g_settings_get_boolean(settings, "toolkit-accessibility");
  }
#endif

  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 (!AXPlatform::GetInstance().GetMode().has_mode(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