• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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