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(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 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 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 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 return obj->atk_acc_name().c_str(); 182 } 183 184 static const gchar* browser_accessibility_get_description( 185 AtkObject* atk_object) { 186 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object); 187 if (!obj) 188 return NULL; 189 return obj->atk_acc_description().c_str(); 190 } 191 192 static AtkObject* browser_accessibility_get_parent(AtkObject* atk_object) { 193 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object); 194 if (!obj) 195 return NULL; 196 if (obj->parent()) 197 return obj->parent()->ToBrowserAccessibilityGtk()->GetAtkObject(); 198 199 BrowserAccessibilityManagerGtk* manager = 200 static_cast<BrowserAccessibilityManagerGtk*>(obj->manager()); 201 return gtk_widget_get_accessible(manager->parent_widget()); 202 } 203 204 static gint browser_accessibility_get_n_children(AtkObject* atk_object) { 205 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object); 206 if (!obj) 207 return 0; 208 return obj->children().size(); 209 } 210 211 static AtkObject* browser_accessibility_ref_child( 212 AtkObject* atk_object, gint index) { 213 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object); 214 if (!obj) 215 return NULL; 216 AtkObject* result = 217 obj->children()[index]->ToBrowserAccessibilityGtk()->GetAtkObject(); 218 g_object_ref(result); 219 return result; 220 } 221 222 static gint browser_accessibility_get_index_in_parent(AtkObject* atk_object) { 223 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object); 224 if (!obj) 225 return 0; 226 return obj->index_in_parent(); 227 } 228 229 static AtkAttributeSet* browser_accessibility_get_attributes( 230 AtkObject* atk_object) { 231 return NULL; 232 } 233 234 static AtkRole browser_accessibility_get_role(AtkObject* atk_object) { 235 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object); 236 if (!obj) 237 return ATK_ROLE_INVALID; 238 return obj->atk_role(); 239 } 240 241 static AtkStateSet* browser_accessibility_ref_state_set(AtkObject* atk_object) { 242 BrowserAccessibilityGtk* obj = ToBrowserAccessibilityGtk(atk_object); 243 if (!obj) 244 return NULL; 245 AtkStateSet* state_set = 246 ATK_OBJECT_CLASS(browser_accessibility_parent_class)-> 247 ref_state_set(atk_object); 248 int32 state = obj->state(); 249 250 if (state & (1 << AccessibilityNodeData::STATE_FOCUSABLE)) 251 atk_state_set_add_state(state_set, ATK_STATE_FOCUSABLE); 252 if (obj->manager()->GetFocus(NULL) == obj) 253 atk_state_set_add_state(state_set, ATK_STATE_FOCUSED); 254 if (!(state & (1 << AccessibilityNodeData::STATE_UNAVAILABLE))) 255 atk_state_set_add_state(state_set, ATK_STATE_ENABLED); 256 257 return state_set; 258 } 259 260 static AtkRelationSet* browser_accessibility_ref_relation_set( 261 AtkObject* atk_object) { 262 AtkRelationSet* relation_set = 263 ATK_OBJECT_CLASS(browser_accessibility_parent_class) 264 ->ref_relation_set(atk_object); 265 return relation_set; 266 } 267 268 // 269 // The rest of the BrowserAccessibilityGtk code, not specific to one 270 // of the Atk* interfaces. 271 // 272 273 static void browser_accessibility_init(AtkObject* atk_object, gpointer data) { 274 if (ATK_OBJECT_CLASS(browser_accessibility_parent_class)->initialize) { 275 ATK_OBJECT_CLASS(browser_accessibility_parent_class)->initialize( 276 atk_object, data); 277 } 278 279 BROWSER_ACCESSIBILITY(atk_object)->m_object = 280 reinterpret_cast<BrowserAccessibilityGtk*>(data); 281 } 282 283 static void browser_accessibility_finalize(GObject* atk_object) { 284 G_OBJECT_CLASS(browser_accessibility_parent_class)->finalize(atk_object); 285 } 286 287 static void browser_accessibility_class_init(AtkObjectClass* klass) { 288 GObjectClass* gobject_class = G_OBJECT_CLASS(klass); 289 browser_accessibility_parent_class = g_type_class_peek_parent(klass); 290 291 gobject_class->finalize = browser_accessibility_finalize; 292 klass->initialize = browser_accessibility_init; 293 klass->get_name = browser_accessibility_get_name; 294 klass->get_description = browser_accessibility_get_description; 295 klass->get_parent = browser_accessibility_get_parent; 296 klass->get_n_children = browser_accessibility_get_n_children; 297 klass->ref_child = browser_accessibility_ref_child; 298 klass->get_role = browser_accessibility_get_role; 299 klass->ref_state_set = browser_accessibility_ref_state_set; 300 klass->get_index_in_parent = browser_accessibility_get_index_in_parent; 301 klass->get_attributes = browser_accessibility_get_attributes; 302 klass->ref_relation_set = browser_accessibility_ref_relation_set; 303 } 304 305 GType browser_accessibility_get_type() { 306 static volatile gsize type_volatile = 0; 307 308 if (g_once_init_enter(&type_volatile)) { 309 static const GTypeInfo tinfo = { 310 sizeof(BrowserAccessibilityAtkClass), 311 (GBaseInitFunc) 0, 312 (GBaseFinalizeFunc) 0, 313 (GClassInitFunc) browser_accessibility_class_init, 314 (GClassFinalizeFunc) 0, 315 0, /* class data */ 316 sizeof(BrowserAccessibilityAtk), /* instance size */ 317 0, /* nb preallocs */ 318 (GInstanceInitFunc) 0, 319 0 /* value table */ 320 }; 321 322 GType type = g_type_register_static( 323 ATK_TYPE_OBJECT, "BrowserAccessibility", &tinfo, GTypeFlags(0)); 324 g_once_init_leave(&type_volatile, type); 325 } 326 327 return type_volatile; 328 } 329 330 static const char* GetUniqueAccessibilityTypeName(int interface_mask) 331 { 332 // 20 characters is enough for "Chrome%x" with any integer value. 333 static char name[20]; 334 snprintf(name, sizeof(name), "Chrome%x", interface_mask); 335 return name; 336 } 337 338 enum AtkInterfaces { 339 ATK_ACTION_INTERFACE, 340 ATK_COMPONENT_INTERFACE, 341 ATK_DOCUMENT_INTERFACE, 342 ATK_EDITABLE_TEXT_INTERFACE, 343 ATK_HYPERLINK_INTERFACE, 344 ATK_HYPERTEXT_INTERFACE, 345 ATK_IMAGE_INTERFACE, 346 ATK_SELECTION_INTERFACE, 347 ATK_TABLE_INTERFACE, 348 ATK_TEXT_INTERFACE, 349 ATK_VALUE_INTERFACE, 350 }; 351 352 static int GetInterfaceMaskFromObject(BrowserAccessibilityGtk* obj) { 353 int interface_mask = 0; 354 355 // Component interface is always supported. 356 interface_mask |= 1 << ATK_COMPONENT_INTERFACE; 357 358 int role = obj->role(); 359 if (role == AccessibilityNodeData::ROLE_PROGRESS_INDICATOR || 360 role == AccessibilityNodeData::ROLE_SCROLLBAR || 361 role == AccessibilityNodeData::ROLE_SLIDER) { 362 interface_mask |= 1 << ATK_VALUE_INTERFACE; 363 } 364 365 return interface_mask; 366 } 367 368 static GType GetAccessibilityTypeFromObject(BrowserAccessibilityGtk* obj) { 369 static const GTypeInfo type_info = { 370 sizeof(BrowserAccessibilityAtkClass), 371 (GBaseInitFunc) 0, 372 (GBaseFinalizeFunc) 0, 373 (GClassInitFunc) 0, 374 (GClassFinalizeFunc) 0, 375 0, /* class data */ 376 sizeof(BrowserAccessibilityAtk), /* instance size */ 377 0, /* nb preallocs */ 378 (GInstanceInitFunc) 0, 379 0 /* value table */ 380 }; 381 382 int interface_mask = GetInterfaceMaskFromObject(obj); 383 const char* atk_type_name = GetUniqueAccessibilityTypeName(interface_mask); 384 GType type = g_type_from_name(atk_type_name); 385 if (type) 386 return type; 387 388 type = g_type_register_static(BROWSER_ACCESSIBILITY_TYPE, 389 atk_type_name, 390 &type_info, 391 GTypeFlags(0)); 392 if (interface_mask & (1 << ATK_COMPONENT_INTERFACE)) 393 g_type_add_interface_static(type, ATK_TYPE_COMPONENT, &ComponentInfo); 394 if (interface_mask & (1 << ATK_VALUE_INTERFACE)) 395 g_type_add_interface_static(type, ATK_TYPE_VALUE, &ValueInfo); 396 397 return type; 398 } 399 400 BrowserAccessibilityAtk* browser_accessibility_new( 401 BrowserAccessibilityGtk* obj) { 402 GType type = GetAccessibilityTypeFromObject(obj); 403 AtkObject* atk_object = static_cast<AtkObject*>(g_object_new(type, 0)); 404 405 atk_object_initialize(atk_object, obj); 406 407 return BROWSER_ACCESSIBILITY(atk_object); 408 } 409 410 void browser_accessibility_detach(BrowserAccessibilityAtk* atk_object) { 411 atk_object->m_object = NULL; 412 } 413 414 // static 415 BrowserAccessibility* BrowserAccessibility::Create() { 416 return new BrowserAccessibilityGtk(); 417 } 418 419 BrowserAccessibilityGtk* BrowserAccessibility::ToBrowserAccessibilityGtk() { 420 return static_cast<BrowserAccessibilityGtk*>(this); 421 } 422 423 BrowserAccessibilityGtk::BrowserAccessibilityGtk() 424 : atk_object_(NULL) { 425 } 426 427 BrowserAccessibilityGtk::~BrowserAccessibilityGtk() { 428 browser_accessibility_detach(BROWSER_ACCESSIBILITY(atk_object_)); 429 if (atk_object_) 430 g_object_unref(atk_object_); 431 } 432 433 AtkObject* BrowserAccessibilityGtk::GetAtkObject() const { 434 if (!G_IS_OBJECT(atk_object_)) 435 return NULL; 436 return atk_object_; 437 } 438 439 void BrowserAccessibilityGtk::PreInitialize() { 440 BrowserAccessibility::PreInitialize(); 441 InitRoleAndState(); 442 443 if (atk_object_) { 444 // If the object's role changes and that causes its 445 // interface mask to change, we need to create a new 446 // AtkObject for it. 447 int interface_mask = GetInterfaceMaskFromObject(this); 448 if (interface_mask != interface_mask_) { 449 g_object_unref(atk_object_); 450 atk_object_ = NULL; 451 } 452 } 453 454 if (!atk_object_) { 455 interface_mask_ = GetInterfaceMaskFromObject(this); 456 atk_object_ = ATK_OBJECT(browser_accessibility_new(this)); 457 if (this->parent()) { 458 atk_object_set_parent( 459 atk_object_, 460 this->parent()->ToBrowserAccessibilityGtk()->GetAtkObject()); 461 } 462 } 463 } 464 465 bool BrowserAccessibilityGtk::IsNative() const { 466 return true; 467 } 468 469 void BrowserAccessibilityGtk::InitRoleAndState() { 470 atk_acc_name_ = UTF16ToUTF8(name()); 471 472 string16 description; 473 GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, &description); 474 atk_acc_description_ = UTF16ToUTF8(description); 475 476 switch(role_) { 477 case AccessibilityNodeData::ROLE_DOCUMENT: 478 case AccessibilityNodeData::ROLE_ROOT_WEB_AREA: 479 case AccessibilityNodeData::ROLE_WEB_AREA: 480 atk_role_ = ATK_ROLE_DOCUMENT_WEB; 481 break; 482 case AccessibilityNodeData::ROLE_GROUP: 483 case AccessibilityNodeData::ROLE_DIV: 484 atk_role_ = ATK_ROLE_SECTION; 485 break; 486 case AccessibilityNodeData::ROLE_BUTTON: 487 atk_role_ = ATK_ROLE_PUSH_BUTTON; 488 break; 489 case AccessibilityNodeData::ROLE_CHECKBOX: 490 atk_role_ = ATK_ROLE_CHECK_BOX; 491 break; 492 case AccessibilityNodeData::ROLE_COMBO_BOX: 493 atk_role_ = ATK_ROLE_COMBO_BOX; 494 break; 495 case AccessibilityNodeData::ROLE_LINK: 496 atk_role_ = ATK_ROLE_LINK; 497 break; 498 case AccessibilityNodeData::ROLE_RADIO_BUTTON: 499 atk_role_ = ATK_ROLE_RADIO_BUTTON; 500 break; 501 case AccessibilityNodeData::ROLE_STATIC_TEXT: 502 atk_role_ = ATK_ROLE_TEXT; 503 break; 504 case AccessibilityNodeData::ROLE_TEXTAREA: 505 atk_role_ = ATK_ROLE_ENTRY; 506 break; 507 case AccessibilityNodeData::ROLE_TEXT_FIELD: 508 atk_role_ = ATK_ROLE_ENTRY; 509 break; 510 case AccessibilityNodeData::ROLE_WEBCORE_LINK: 511 atk_role_ = ATK_ROLE_LINK; 512 break; 513 default: 514 atk_role_ = ATK_ROLE_UNKNOWN; 515 break; 516 } 517 } 518 519 } // namespace content 520