summaryrefslogtreecommitdiffstats
path: root/chrome/browser/gtk/accessibility_event_router_gtk.h
blob: 2cc2cf9334038c0243e036e1945552e287bb45f5 (plain)
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
// Copyright (c) 2010 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.

#ifndef CHROME_BROWSER_GTK_ACCESSIBILITY_EVENT_ROUTER_GTK_H_
#define CHROME_BROWSER_GTK_ACCESSIBILITY_EVENT_ROUTER_GTK_H_
#pragma once

#include <gtk/gtk.h>

#include <string>
#include <vector>

#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
#include "base/hash_tables.h"
#include "base/singleton.h"
#include "base/task.h"
#include "chrome/browser/accessibility_events.h"

class Profile;
#if defined (TOOLKIT_VIEWS)
namespace views {
class NativeTextfieldGtk;
}
#endif

// Allows us to use (GtkWidget*) in a hash_map with gcc.
namespace __gnu_cxx {
template<>
struct hash<GtkWidget*> {
  size_t operator()(GtkWidget* widget) const {
    return reinterpret_cast<size_t>(widget);
  }
};
}  // namespace __gnu_cxx

// Struct to keep track of event listener hook ids to remove them later.
struct InstalledHook {
  InstalledHook(guint _signal_id, gulong _hook_id)
      : signal_id(_signal_id), hook_id(_hook_id) { }
  guint signal_id;
  gulong hook_id;
};

// NOTE: This class is part of the Accessibility Extension API, which lets
// extensions receive accessibility events. It's distinct from code that
// implements platform accessibility APIs like MSAA or ATK.
//
// Singleton class that adds a signal emission hook to many gtk events, and
// then sends an accessibility notification whenever a relevant event is
// sent to an accessible control.
//
// Gtk widgets are not accessible by default. When you register a root widget,
// that widget and all of its descendants will start sending accessibility
// event notifications. You can then override the default behavior for
// specific descendants using other methods.
//
// You can use Profile::PauseAccessibilityEvents to prevent a flurry
// of accessibility events when a window is being created or initialized.
class AccessibilityEventRouterGtk {
 public:
  // Internal information about a particular widget to override the
  // information we get directly from gtk.
  struct WidgetInfo {
    WidgetInfo() : refcount(0) { }

    // The number of times that AddWidgetNameOverride has been called on this
    // widget. When RemoveWidget has been called an equal number of
    // times and the refcount reaches zero, this entry will be deleted.
    int refcount;

    // If nonempty, will use this name instead of the widget's label.
    std::string name;
  };

  // Internal information about a root widget
  struct RootWidgetInfo {
    RootWidgetInfo() : refcount(0), profile(NULL) { }

    // The number of times that AddRootWidget has been called on this
    // widget. When RemoveRootWidget has been called an equal number of
    // times and the refcount reaches zero, this entry will be deleted.
    int refcount;

    // The profile associated with this root widget; accessibility
    // notifications for any descendant of this root widget will get routed
    // to this profile.
    Profile* profile;
  };

  // Get the single instance of this class.
  static AccessibilityEventRouterGtk* GetInstance();

  // Start sending accessibility events for this widget and all of its
  // descendants.  Notifications will go to the specified profile.
  // Uses reference counting, so it's safe to call this twice on the
  // same widget, as long as each call is paired with a call to
  // RemoveRootWidget.
  void AddRootWidget(GtkWidget* root_widget, Profile* profile);

  // Stop sending accessibility events for this widget and all of its
  // descendants.
  void RemoveRootWidget(GtkWidget* root_widget);

  // Use the following string as the name of this widget, instead of the
  // gtk label associated with the widget. Must be paired with a call to
  // RemoveWidgetNameOverride.
  void AddWidgetNameOverride(GtkWidget* widget, std::string name);

  // Forget widget name override. Must be paired with a call to
  // AddWidgetNameOverride (uses reference counting).
  void RemoveWidgetNameOverride(GtkWidget* widget);

  //
  // The following methods are only for use by gtk signal handlers.
  //

  // Called by the signal handler.  Checks the type of the widget and
  // calls one of the more specific Send*Notification methods, below.
  void DispatchAccessibilityNotification(
      GtkWidget* widget, NotificationType type);

  // Post a task to call DispatchAccessibilityNotification the next time
  // through the event loop.
  void PostDispatchAccessibilityNotification(
      GtkWidget* widget, NotificationType type);

 private:
  AccessibilityEventRouterGtk();
  virtual ~AccessibilityEventRouterGtk();

  // Given a widget, determine if it's the descendant of a root widget
  // that's mapped to a profile and if so, if it's marked as accessible.
  void FindWidget(GtkWidget* widget, Profile** profile, bool* is_accessible);

  // Return the name of a widget.
  std::string GetWidgetName(GtkWidget* widget);

  // Each of these methods constructs an AccessibilityControlInfo object
  // and sends a notification of a specific accessibility event.
  void SendButtonNotification(
      GtkWidget* widget, NotificationType type, Profile* profile);
  void SendCheckboxNotification(
      GtkWidget* widget, NotificationType type, Profile* profile);
  void SendComboBoxNotification(
      GtkWidget* widget, NotificationType type, Profile* profile);
  void SendListBoxNotification(
      GtkWidget* widget, NotificationType type, Profile* profile);
  void SendMenuItemNotification(
      GtkWidget* widget, NotificationType type, Profile* profile);
  void SendRadioButtonNotification(
      GtkWidget* widget, NotificationType type, Profile* profile);
  void SendTabNotification(
      GtkWidget* widget, NotificationType type, Profile* profile);
  void SendEntryNotification(
      GtkWidget* widget, NotificationType type, Profile* profile);
  void SendTextViewNotification(
      GtkWidget* widget, NotificationType type, Profile* profile);

  bool IsPassword(GtkWidget* widget);
  void InstallEventListeners();
  void RemoveEventListeners();

  // Start and stop listening to signals.
  void StartListening();
  void StopListening();

  // Add a signal emission hook for one particular signal name and
  // widget type, and save the hook_id in installed_hooks so we can
  // remove it later.
  void InstallEventListener(
      const char *signal_name,
      GType widget_type,
      GSignalEmissionHook hook_func);

  friend struct DefaultSingletonTraits<AccessibilityEventRouterGtk>;

  // The set of all root widgets; only descendants of these will generate
  // accessibility notifications.
  base::hash_map<GtkWidget*, RootWidgetInfo> root_widget_info_map_;

  // Extra information about specific widgets.
  base::hash_map<GtkWidget*, WidgetInfo> widget_info_map_;

  // Installed event listener hook ids so we can remove them later.
  std::vector<InstalledHook> installed_hooks_;

  // True if we are currently listening to signals.
  bool listening_;

  // The profile associated with the most recent window event  - used to
  // figure out where to route a few events that can't be directly traced
  // to a window with a profile (like menu events).
  Profile* most_recent_profile_;

  // The most recent focused widget.
  GtkWidget* most_recent_widget_;

  // Used to schedule invocations of StartListening() and to defer handling
  // of some events until the next time through the event loop.
  ScopedRunnableMethodFactory<AccessibilityEventRouterGtk> method_factory_;

  friend class AccessibilityEventRouterGtkTest;
  FRIEND_TEST_ALL_PREFIXES(AccessibilityEventRouterGtkTest, AddRootWidgetTwice);
};

#endif  // CHROME_BROWSER_GTK_ACCESSIBILITY_EVENT_ROUTER_GTK_H_