Home | History | Annotate | Download | only in accessibility
      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