1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/accessibility/browser_accessibility_gtk.h"
6
7 #include <gtk/gtk.h>
8
9 #include "base/strings/utf_string_conversions.h"
10 #include "content/browser/accessibility/browser_accessibility_manager_gtk.h"
11 #include "content/common/accessibility_messages.h"
12
13 namespace content {
14
15 static gpointer browser_accessibility_parent_class = NULL;
16
ToBrowserAccessibilityGtk(BrowserAccessibilityAtk * atk_object)17 static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
18 BrowserAccessibilityAtk* atk_object) {
19 if (!atk_object)
20 return NULL;
21
22 return atk_object->m_object;
23 }
24
25 //
26 // AtkComponent interface.
27 //
28
ToBrowserAccessibilityGtk(AtkComponent * atk_object)29 static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
30 AtkComponent* atk_object) {
31 if (!IS_BROWSER_ACCESSIBILITY(atk_object))
32 return NULL;
33
34 return ToBrowserAccessibilityGtk(BROWSER_ACCESSIBILITY(atk_object));
35 }
36
browser_accessibility_accessible_at_point(AtkComponent * component,gint x,gint y,AtkCoordType coord_type)37 static AtkObject* browser_accessibility_accessible_at_point(
38 AtkComponent* component, gint x, gint y, AtkCoordType coord_type) {
39 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(component);
40 if (!obj)
41 return NULL;
42
43 gfx::Point point(x, y);
44 if (!obj->GetGlobalBoundsRect().Contains(point))
45 return NULL;
46
47 BrowserAccessibility* result = obj->BrowserAccessibilityForPoint(point);
48 if (!result)
49 return NULL;
50
51 AtkObject* atk_result = result->ToBrowserAccessibilityGtk()->GetAtkObject();
52 g_object_ref(atk_result);
53 return atk_result;
54 }
55
browser_accessibility_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)56 static void browser_accessibility_get_extents(
57 AtkComponent* component, gint* x, gint* y, gint* width, gint* height,
58 AtkCoordType coord_type) {
59 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(component);
60 if (!obj)
61 return;
62
63 gfx::Rect bounds = obj->GetGlobalBoundsRect();
64 *x = bounds.x();
65 *y = bounds.y();
66 *width = bounds.width();
67 *height = bounds.height();
68 }
69
browser_accessibility_grab_focus(AtkComponent * component)70 static gboolean browser_accessibility_grab_focus(AtkComponent* component) {
71 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(component);
72 if (!obj)
73 return false;
74
75 obj->manager()->SetFocus(obj, true);
76 return true;
77 }
78
ComponentInterfaceInit(AtkComponentIface * iface)79 static void ComponentInterfaceInit(AtkComponentIface* iface) {
80 iface->ref_accessible_at_point = browser_accessibility_accessible_at_point;
81 iface->get_extents = browser_accessibility_get_extents;
82 iface->grab_focus = browser_accessibility_grab_focus;
83 }
84
85 static const GInterfaceInfo ComponentInfo = {
86 reinterpret_cast<GInterfaceInitFunc>(ComponentInterfaceInit), 0, 0
87 };
88
89 //
90 // AtkValue interface.
91 //
92
ToBrowserAccessibilityGtk(AtkValue * atk_object)93 static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
94 AtkValue* atk_object) {
95 if (!IS_BROWSER_ACCESSIBILITY(atk_object))
96 return NULL;
97
98 return ToBrowserAccessibilityGtk(BROWSER_ACCESSIBILITY(atk_object));
99 }
100
browser_accessibility_get_current_value(AtkValue * atk_object,GValue * value)101 static void browser_accessibility_get_current_value(
102 AtkValue* atk_object, GValue* value) {
103 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
104 if (!obj)
105 return;
106
107 float float_val;
108 if (obj->GetFloatAttribute(AccessibilityNodeData::ATTR_VALUE_FOR_RANGE,
109 &float_val)) {
110 memset(value, 0, sizeof(*value));
111 g_value_init(value, G_TYPE_FLOAT);
112 g_value_set_float(value, float_val);
113 }
114 }
115
browser_accessibility_get_minimum_value(AtkValue * atk_object,GValue * value)116 static void browser_accessibility_get_minimum_value(
117 AtkValue* atk_object, GValue* value) {
118 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
119 if (!obj)
120 return;
121
122 float float_val;
123 if (obj->GetFloatAttribute(AccessibilityNodeData::ATTR_MIN_VALUE_FOR_RANGE,
124 &float_val)) {
125 memset(value, 0, sizeof(*value));
126 g_value_init(value, G_TYPE_FLOAT);
127 g_value_set_float(value, float_val);
128 }
129 }
130
browser_accessibility_get_maximum_value(AtkValue * atk_object,GValue * value)131 static void browser_accessibility_get_maximum_value(
132 AtkValue* atk_object, GValue* value) {
133 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
134 if (!obj)
135 return;
136
137 float float_val;
138 if (obj->GetFloatAttribute(AccessibilityNodeData::ATTR_MAX_VALUE_FOR_RANGE,
139 &float_val)) {
140 memset(value, 0, sizeof(*value));
141 g_value_init(value, G_TYPE_FLOAT);
142 g_value_set_float(value, float_val);
143 }
144 }
145
browser_accessibility_get_minimum_increment(AtkValue * atk_object,GValue * value)146 static void browser_accessibility_get_minimum_increment(
147 AtkValue* atk_object, GValue* value) {
148 // TODO(dmazzoni): get the correct value from an <input type=range>.
149 memset(value, 0, sizeof(*value));
150 g_value_init(value, G_TYPE_FLOAT);
151 g_value_set_float(value, 1.0);
152 }
153
ValueInterfaceInit(AtkValueIface * iface)154 static void ValueInterfaceInit(AtkValueIface* iface) {
155 iface->get_current_value = browser_accessibility_get_current_value;
156 iface->get_minimum_value = browser_accessibility_get_minimum_value;
157 iface->get_maximum_value = browser_accessibility_get_maximum_value;
158 iface->get_minimum_increment = browser_accessibility_get_minimum_increment;
159 }
160
161 static const GInterfaceInfo ValueInfo = {
162 reinterpret_cast<GInterfaceInitFunc>(ValueInterfaceInit), 0, 0
163 };
164
165 //
166 // AtkObject interface
167 //
168
ToBrowserAccessibilityGtk(AtkObject * atk_object)169 static BrowserAccessibilityGtk* ToBrowserAccessibilityGtk(
170 AtkObject* atk_object) {
171 if (!IS_BROWSER_ACCESSIBILITY(atk_object))
172 return NULL;
173
174 return ToBrowserAccessibilityGtk(BROWSER_ACCESSIBILITY(atk_object));
175 }
176
browser_accessibility_get_name(AtkObject * atk_object)177 static const gchar* browser_accessibility_get_name(AtkObject* atk_object) {
178 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
179 if (!obj)
180 return NULL;
181
182 return obj->GetStringAttribute(AccessibilityNodeData::ATTR_NAME).c_str();
183 }
184
browser_accessibility_get_description(AtkObject * atk_object)185 static const gchar* browser_accessibility_get_description(
186 AtkObject* atk_object) {
187 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
188 if (!obj)
189 return NULL;
190
191 return obj->GetStringAttribute(
192 AccessibilityNodeData::ATTR_DESCRIPTION).c_str();
193 }
194
browser_accessibility_get_parent(AtkObject * atk_object)195 static AtkObject* browser_accessibility_get_parent(AtkObject* atk_object) {
196 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
197 if (!obj)
198 return NULL;
199 if (obj->parent())
200 return obj->parent()->ToBrowserAccessibilityGtk()->GetAtkObject();
201
202 BrowserAccessibilityManagerGtk* manager =
203 static_cast<BrowserAccessibilityManagerGtk*>(obj->manager());
204 return gtk_widget_get_accessible(manager->parent_widget());
205 }
206
browser_accessibility_get_n_children(AtkObject * atk_object)207 static gint browser_accessibility_get_n_children(AtkObject* atk_object) {
208 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
209 if (!obj)
210 return 0;
211
212 return obj->PlatformChildCount();
213 }
214
browser_accessibility_ref_child(AtkObject * atk_object,gint index)215 static AtkObject* browser_accessibility_ref_child(
216 AtkObject* atk_object, gint index) {
217 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
218 if (!obj)
219 return NULL;
220
221 if (index < 0 || index >= static_cast<gint>(obj->PlatformChildCount()))
222 return NULL;
223
224 AtkObject* result =
225 obj->children()[index]->ToBrowserAccessibilityGtk()->GetAtkObject();
226 g_object_ref(result);
227 return result;
228 }
229
browser_accessibility_get_index_in_parent(AtkObject * atk_object)230 static gint browser_accessibility_get_index_in_parent(AtkObject* atk_object) {
231 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
232 if (!obj)
233 return 0;
234 return obj->index_in_parent();
235 }
236
browser_accessibility_get_attributes(AtkObject * atk_object)237 static AtkAttributeSet* browser_accessibility_get_attributes(
238 AtkObject* atk_object) {
239 return NULL;
240 }
241
browser_accessibility_get_role(AtkObject * atk_object)242 static AtkRole browser_accessibility_get_role(AtkObject* atk_object) {
243 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
244 if (!obj)
245 return ATK_ROLE_INVALID;
246 return obj->atk_role();
247 }
248
browser_accessibility_ref_state_set(AtkObject * atk_object)249 static AtkStateSet* browser_accessibility_ref_state_set(AtkObject* atk_object) {
250 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object);
251 if (!obj)
252 return NULL;
253 AtkStateSet* state_set =
254 ATK_OBJECT_CLASS(browser_accessibility_parent_class)->
255 ref_state_set(atk_object);
256 int32 state = obj->state();
257
258 if (state & (1 << blink::WebAXStateFocusable))
259 atk_state_set_add_state(state_set, ATK_STATE_FOCUSABLE);
260 if (obj->manager()->GetFocus(NULL) == obj)
261 atk_state_set_add_state(state_set, ATK_STATE_FOCUSED);
262 if (state & (1 << blink::WebAXStateEnabled))
263 atk_state_set_add_state(state_set, ATK_STATE_ENABLED);
264
265 return state_set;
266 }
267
browser_accessibility_ref_relation_set(AtkObject * atk_object)268 static AtkRelationSet* browser_accessibility_ref_relation_set(
269 AtkObject* atk_object) {
270 AtkRelationSet* relation_set =
271 ATK_OBJECT_CLASS(browser_accessibility_parent_class)
272 ->ref_relation_set(atk_object);
273 return relation_set;
274 }
275
276 //
277 // The rest of the BrowserAccessibilityGtk code, not specific to one
278 // of the Atk* interfaces.
279 //
280
browser_accessibility_init(AtkObject * atk_object,gpointer data)281 static void browser_accessibility_init(AtkObject* atk_object, gpointer data) {
282 if (ATK_OBJECT_CLASS(browser_accessibility_parent_class)->initialize) {
283 ATK_OBJECT_CLASS(browser_accessibility_parent_class)->initialize(
284 atk_object, data);
285 }
286
287 BROWSER_ACCESSIBILITY(atk_object)->m_object =
288 reinterpret_cast<BrowserAccessibilityGtk*>(data);
289 }
290
browser_accessibility_finalize(GObject * atk_object)291 static void browser_accessibility_finalize(GObject* atk_object) {
292 G_OBJECT_CLASS(browser_accessibility_parent_class)->finalize(atk_object);
293 }
294
browser_accessibility_class_init(AtkObjectClass * klass)295 static void browser_accessibility_class_init(AtkObjectClass* klass) {
296 GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
297 browser_accessibility_parent_class = g_type_class_peek_parent(klass);
298
299 gobject_class->finalize = browser_accessibility_finalize;
300 klass->initialize = browser_accessibility_init;
301 klass->get_name = browser_accessibility_get_name;
302 klass->get_description = browser_accessibility_get_description;
303 klass->get_parent = browser_accessibility_get_parent;
304 klass->get_n_children = browser_accessibility_get_n_children;
305 klass->ref_child = browser_accessibility_ref_child;
306 klass->get_role = browser_accessibility_get_role;
307 klass->ref_state_set = browser_accessibility_ref_state_set;
308 klass->get_index_in_parent = browser_accessibility_get_index_in_parent;
309 klass->get_attributes = browser_accessibility_get_attributes;
310 klass->ref_relation_set = browser_accessibility_ref_relation_set;
311 }
312
browser_accessibility_get_type()313 GType browser_accessibility_get_type() {
314 static volatile gsize type_volatile = 0;
315
316 if (g_once_init_enter(&type_volatile)) {
317 static const GTypeInfo tinfo = {
318 sizeof(BrowserAccessibilityAtkClass),
319 (GBaseInitFunc) 0,
320 (GBaseFinalizeFunc) 0,
321 (GClassInitFunc) browser_accessibility_class_init,
322 (GClassFinalizeFunc) 0,
323 0, /* class data */
324 sizeof(BrowserAccessibilityAtk), /* instance size */
325 0, /* nb preallocs */
326 (GInstanceInitFunc) 0,
327 0 /* value table */
328 };
329
330 GType type = g_type_register_static(
331 ATK_TYPE_OBJECT, "BrowserAccessibility", &tinfo, GTypeFlags(0));
332 g_once_init_leave(&type_volatile, type);
333 }
334
335 return type_volatile;
336 }
337
GetUniqueAccessibilityTypeName(int interface_mask)338 static const char* GetUniqueAccessibilityTypeName(int interface_mask)
339 {
340 // 20 characters is enough for "Chrome%x" with any integer value.
341 static char name[20];
342 snprintf(name, sizeof(name), "Chrome%x", interface_mask);
343 return name;
344 }
345
346 enum AtkInterfaces {
347 ATK_ACTION_INTERFACE,
348 ATK_COMPONENT_INTERFACE,
349 ATK_DOCUMENT_INTERFACE,
350 ATK_EDITABLE_TEXT_INTERFACE,
351 ATK_HYPERLINK_INTERFACE,
352 ATK_HYPERTEXT_INTERFACE,
353 ATK_IMAGE_INTERFACE,
354 ATK_SELECTION_INTERFACE,
355 ATK_TABLE_INTERFACE,
356 ATK_TEXT_INTERFACE,
357 ATK_VALUE_INTERFACE,
358 };
359
GetInterfaceMaskFromObject(BrowserAccessibilityGtk * obj)360 static int GetInterfaceMaskFromObject(BrowserAccessibilityGtk* obj) {
361 int interface_mask = 0;
362
363 // Component interface is always supported.
364 interface_mask |= 1 << ATK_COMPONENT_INTERFACE;
365
366 int role = obj->role();
367 if (role == blink::WebAXRoleProgressIndicator ||
368 role == blink::WebAXRoleScrollBar ||
369 role == blink::WebAXRoleSlider) {
370 interface_mask |= 1 << ATK_VALUE_INTERFACE;
371 }
372
373 return interface_mask;
374 }
375
GetAccessibilityTypeFromObject(BrowserAccessibilityGtk * obj)376 static GType GetAccessibilityTypeFromObject(BrowserAccessibilityGtk* obj) {
377 static const GTypeInfo type_info = {
378 sizeof(BrowserAccessibilityAtkClass),
379 (GBaseInitFunc) 0,
380 (GBaseFinalizeFunc) 0,
381 (GClassInitFunc) 0,
382 (GClassFinalizeFunc) 0,
383 0, /* class data */
384 sizeof(BrowserAccessibilityAtk), /* instance size */
385 0, /* nb preallocs */
386 (GInstanceInitFunc) 0,
387 0 /* value table */
388 };
389
390 int interface_mask = GetInterfaceMaskFromObject(obj);
391 const char* atk_type_name = GetUniqueAccessibilityTypeName(interface_mask);
392 GType type = g_type_from_name(atk_type_name);
393 if (type)
394 return type;
395
396 type = g_type_register_static(BROWSER_ACCESSIBILITY_TYPE,
397 atk_type_name,
398 &type_info,
399 GTypeFlags(0));
400 if (interface_mask & (1 << ATK_COMPONENT_INTERFACE))
401 g_type_add_interface_static(type, ATK_TYPE_COMPONENT, &ComponentInfo);
402 if (interface_mask & (1 << ATK_VALUE_INTERFACE))
403 g_type_add_interface_static(type, ATK_TYPE_VALUE, &ValueInfo);
404
405 return type;
406 }
407
browser_accessibility_new(BrowserAccessibilityGtk * obj)408 BrowserAccessibilityAtk* browser_accessibility_new(
409 BrowserAccessibilityGtk* obj) {
410 GType type = GetAccessibilityTypeFromObject(obj);
411 AtkObject* atk_object = static_cast<AtkObject*>(g_object_new(type, 0));
412
413 atk_object_initialize(atk_object, obj);
414
415 return BROWSER_ACCESSIBILITY(atk_object);
416 }
417
browser_accessibility_detach(BrowserAccessibilityAtk * atk_object)418 void browser_accessibility_detach(BrowserAccessibilityAtk* atk_object) {
419 atk_object->m_object = NULL;
420 }
421
422 // static
Create()423 BrowserAccessibility* BrowserAccessibility::Create() {
424 return new BrowserAccessibilityGtk();
425 }
426
ToBrowserAccessibilityGtk()427 BrowserAccessibilityGtk* BrowserAccessibility::ToBrowserAccessibilityGtk() {
428 return static_cast<BrowserAccessibilityGtk*>(this);
429 }
430
BrowserAccessibilityGtk()431 BrowserAccessibilityGtk::BrowserAccessibilityGtk()
432 : atk_object_(NULL) {
433 }
434
~BrowserAccessibilityGtk()435 BrowserAccessibilityGtk::~BrowserAccessibilityGtk() {
436 browser_accessibility_detach(BROWSER_ACCESSIBILITY(atk_object_));
437 if (atk_object_)
438 g_object_unref(atk_object_);
439 }
440
GetAtkObject() const441 AtkObject* BrowserAccessibilityGtk::GetAtkObject() const {
442 if (!G_IS_OBJECT(atk_object_))
443 return NULL;
444 return atk_object_;
445 }
446
PreInitialize()447 void BrowserAccessibilityGtk::PreInitialize() {
448 BrowserAccessibility::PreInitialize();
449 InitRoleAndState();
450
451 if (atk_object_) {
452 // If the object's role changes and that causes its
453 // interface mask to change, we need to create a new
454 // AtkObject for it.
455 int interface_mask = GetInterfaceMaskFromObject(this);
456 if (interface_mask != interface_mask_) {
457 g_object_unref(atk_object_);
458 atk_object_ = NULL;
459 }
460 }
461
462 if (!atk_object_) {
463 interface_mask_ = GetInterfaceMaskFromObject(this);
464 atk_object_ = ATK_OBJECT(browser_accessibility_new(this));
465 if (this->parent()) {
466 atk_object_set_parent(
467 atk_object_,
468 this->parent()->ToBrowserAccessibilityGtk()->GetAtkObject());
469 }
470 }
471 }
472
IsNative() const473 bool BrowserAccessibilityGtk::IsNative() const {
474 return true;
475 }
476
InitRoleAndState()477 void BrowserAccessibilityGtk::InitRoleAndState() {
478 switch(role()) {
479 case blink::WebAXRoleDocument:
480 case blink::WebAXRoleRootWebArea:
481 case blink::WebAXRoleWebArea:
482 atk_role_ = ATK_ROLE_DOCUMENT_WEB;
483 break;
484 case blink::WebAXRoleGroup:
485 case blink::WebAXRoleDiv:
486 atk_role_ = ATK_ROLE_SECTION;
487 break;
488 case blink::WebAXRoleButton:
489 atk_role_ = ATK_ROLE_PUSH_BUTTON;
490 break;
491 case blink::WebAXRoleCheckBox:
492 atk_role_ = ATK_ROLE_CHECK_BOX;
493 break;
494 case blink::WebAXRoleComboBox:
495 atk_role_ = ATK_ROLE_COMBO_BOX;
496 break;
497 case blink::WebAXRoleLink:
498 atk_role_ = ATK_ROLE_LINK;
499 break;
500 case blink::WebAXRoleRadioButton:
501 atk_role_ = ATK_ROLE_RADIO_BUTTON;
502 break;
503 case blink::WebAXRoleStaticText:
504 atk_role_ = ATK_ROLE_TEXT;
505 break;
506 case blink::WebAXRoleTextArea:
507 atk_role_ = ATK_ROLE_ENTRY;
508 break;
509 case blink::WebAXRoleTextField:
510 atk_role_ = ATK_ROLE_ENTRY;
511 break;
512 default:
513 atk_role_ = ATK_ROLE_UNKNOWN;
514 break;
515 }
516 }
517
518 } // namespace content
519