OLD | NEW |
| (Empty) |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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(ui::AX_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 | |
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(ui::AX_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 | |
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(ui::AX_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 | |
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 | |
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 | |
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 | |
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(ui::AX_ATTR_NAME).c_str(); | |
183 } | |
184 | |
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 ui::AX_ATTR_DESCRIPTION).c_str(); | |
193 } | |
194 | |
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->GetParent()) | |
200 return obj->GetParent()->ToBrowserAccessibilityGtk()->GetAtkObject(); | |
201 | |
202 BrowserAccessibilityManagerGtk* manager = | |
203 static_cast<BrowserAccessibilityManagerGtk*>(obj->manager()); | |
204 return gtk_widget_get_accessible(manager->parent_widget()); | |
205 } | |
206 | |
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 | |
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->InternalGetChild(index)->ToBrowserAccessibilityGtk()->GetAtkObject(); | |
226 g_object_ref(result); | |
227 return result; | |
228 } | |
229 | |
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->GetIndexInParent(); | |
235 } | |
236 | |
237 static AtkAttributeSet* browser_accessibility_get_attributes( | |
238 AtkObject* atk_object) { | |
239 return NULL; | |
240 } | |
241 | |
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 | |
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->GetState(); | |
257 | |
258 if (state & (1 << ui::AX_STATE_FOCUSABLE)) | |
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 << ui::AX_STATE_ENABLED)) | |
263 atk_state_set_add_state(state_set, ATK_STATE_ENABLED); | |
264 | |
265 return state_set; | |
266 } | |
267 | |
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 | |
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 | |
291 static void browser_accessibility_finalize(GObject* atk_object) { | |
292 G_OBJECT_CLASS(browser_accessibility_parent_class)->finalize(atk_object); | |
293 } | |
294 | |
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 | |
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 | |
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 | |
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->GetRole(); | |
367 if (role == ui::AX_ROLE_PROGRESS_INDICATOR || | |
368 role == ui::AX_ROLE_SCROLL_BAR || | |
369 role == ui::AX_ROLE_SLIDER) { | |
370 interface_mask |= 1 << ATK_VALUE_INTERFACE; | |
371 } | |
372 | |
373 return interface_mask; | |
374 } | |
375 | |
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 | |
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 | |
418 void browser_accessibility_detach(BrowserAccessibilityAtk* atk_object) { | |
419 atk_object->m_object = NULL; | |
420 } | |
421 | |
422 // static | |
423 BrowserAccessibility* BrowserAccessibility::Create() { | |
424 return new BrowserAccessibilityGtk(); | |
425 } | |
426 | |
427 BrowserAccessibilityGtk* BrowserAccessibility::ToBrowserAccessibilityGtk() { | |
428 return static_cast<BrowserAccessibilityGtk*>(this); | |
429 } | |
430 | |
431 BrowserAccessibilityGtk::BrowserAccessibilityGtk() | |
432 : atk_object_(NULL) { | |
433 } | |
434 | |
435 BrowserAccessibilityGtk::~BrowserAccessibilityGtk() { | |
436 browser_accessibility_detach(BROWSER_ACCESSIBILITY(atk_object_)); | |
437 if (atk_object_) | |
438 g_object_unref(atk_object_); | |
439 } | |
440 | |
441 AtkObject* BrowserAccessibilityGtk::GetAtkObject() const { | |
442 if (!G_IS_OBJECT(atk_object_)) | |
443 return NULL; | |
444 return atk_object_; | |
445 } | |
446 | |
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->GetParent()) { | |
466 atk_object_set_parent( | |
467 atk_object_, | |
468 this->GetParent()->ToBrowserAccessibilityGtk()->GetAtkObject()); | |
469 } | |
470 } | |
471 } | |
472 | |
473 bool BrowserAccessibilityGtk::IsNative() const { | |
474 return true; | |
475 } | |
476 | |
477 void BrowserAccessibilityGtk::InitRoleAndState() { | |
478 switch(GetRole()) { | |
479 case ui::AX_ROLE_DOCUMENT: | |
480 case ui::AX_ROLE_ROOT_WEB_AREA: | |
481 case ui::AX_ROLE_WEB_AREA: | |
482 atk_role_ = ATK_ROLE_DOCUMENT_WEB; | |
483 break; | |
484 case ui::AX_ROLE_GROUP: | |
485 case ui::AX_ROLE_DIV: | |
486 atk_role_ = ATK_ROLE_SECTION; | |
487 break; | |
488 case ui::AX_ROLE_BUTTON: | |
489 atk_role_ = ATK_ROLE_PUSH_BUTTON; | |
490 break; | |
491 case ui::AX_ROLE_CHECK_BOX: | |
492 atk_role_ = ATK_ROLE_CHECK_BOX; | |
493 break; | |
494 case ui::AX_ROLE_COMBO_BOX: | |
495 atk_role_ = ATK_ROLE_COMBO_BOX; | |
496 break; | |
497 case ui::AX_ROLE_LINK: | |
498 atk_role_ = ATK_ROLE_LINK; | |
499 break; | |
500 case ui::AX_ROLE_RADIO_BUTTON: | |
501 atk_role_ = ATK_ROLE_RADIO_BUTTON; | |
502 break; | |
503 case ui::AX_ROLE_STATIC_TEXT: | |
504 atk_role_ = ATK_ROLE_TEXT; | |
505 break; | |
506 case ui::AX_ROLE_TEXT_AREA: | |
507 atk_role_ = ATK_ROLE_ENTRY; | |
508 break; | |
509 case ui::AX_ROLE_TEXT_FIELD: | |
510 atk_role_ = ATK_ROLE_ENTRY; | |
511 break; | |
512 default: | |
513 atk_role_ = ATK_ROLE_UNKNOWN; | |
514 break; | |
515 } | |
516 } | |
517 | |
518 } // namespace content | |
OLD | NEW |