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 13namespace content { 14 15static gpointer browser_accessibility_parent_class = NULL; 16 17static 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 29static 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 37static 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 56static 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 70static 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 79static 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 85static const GInterfaceInfo ComponentInfo = { 86 reinterpret_cast<GInterfaceInitFunc>(ComponentInterfaceInit), 0, 0 87}; 88 89// 90// AtkValue interface. 91// 92 93static 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 101static 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 116static 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 131static 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 146static 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 154static 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 161static const GInterfaceInfo ValueInfo = { 162 reinterpret_cast<GInterfaceInitFunc>(ValueInterfaceInit), 0, 0 163}; 164 165// 166// AtkObject interface 167// 168 169static 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 177static 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 185static 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 195static 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 207static 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 215static 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 230static 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 237static AtkAttributeSet* browser_accessibility_get_attributes( 238 AtkObject* atk_object) { 239 return NULL; 240} 241 242static 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 249static 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 268static 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 281static 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 291static void browser_accessibility_finalize(GObject* atk_object) { 292 G_OBJECT_CLASS(browser_accessibility_parent_class)->finalize(atk_object); 293} 294 295static 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 313GType 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 338static 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 346enum 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 360static 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 376static 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 408BrowserAccessibilityAtk* 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 418void browser_accessibility_detach(BrowserAccessibilityAtk* atk_object) { 419 atk_object->m_object = NULL; 420} 421 422// static 423BrowserAccessibility* BrowserAccessibility::Create() { 424 return new BrowserAccessibilityGtk(); 425} 426 427BrowserAccessibilityGtk* BrowserAccessibility::ToBrowserAccessibilityGtk() { 428 return static_cast<BrowserAccessibilityGtk*>(this); 429} 430 431BrowserAccessibilityGtk::BrowserAccessibilityGtk() 432 : atk_object_(NULL) { 433} 434 435BrowserAccessibilityGtk::~BrowserAccessibilityGtk() { 436 browser_accessibility_detach(BROWSER_ACCESSIBILITY(atk_object_)); 437 if (atk_object_) 438 g_object_unref(atk_object_); 439} 440 441AtkObject* BrowserAccessibilityGtk::GetAtkObject() const { 442 if (!G_IS_OBJECT(atk_object_)) 443 return NULL; 444 return atk_object_; 445} 446 447void 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 473bool BrowserAccessibilityGtk::IsNative() const { 474 return true; 475} 476 477void 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