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_manager_mac.h"
      6 
      7 #import "base/logging.h"
      8 #import "content/browser/accessibility/browser_accessibility_cocoa.h"
      9 #import "content/browser/accessibility/browser_accessibility_mac.h"
     10 #include "content/common/accessibility_messages.h"
     11 
     12 namespace content {
     13 
     14 // static
     15 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
     16     const ui::AXTreeUpdate& initial_tree,
     17     BrowserAccessibilityDelegate* delegate,
     18     BrowserAccessibilityFactory* factory) {
     19   return new BrowserAccessibilityManagerMac(
     20       NULL, initial_tree, delegate, factory);
     21 }
     22 
     23 BrowserAccessibilityManagerMac::BrowserAccessibilityManagerMac(
     24     NSView* parent_view,
     25     const ui::AXTreeUpdate& initial_tree,
     26     BrowserAccessibilityDelegate* delegate,
     27     BrowserAccessibilityFactory* factory)
     28     : BrowserAccessibilityManager(initial_tree, delegate, factory),
     29       parent_view_(parent_view),
     30       created_live_region_(false) {
     31 }
     32 
     33 // static
     34 ui::AXTreeUpdate BrowserAccessibilityManagerMac::GetEmptyDocument() {
     35   ui::AXNodeData empty_document;
     36   empty_document.id = 0;
     37   empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
     38   empty_document.state =
     39       1 << ui::AX_STATE_READ_ONLY;
     40   ui::AXTreeUpdate update;
     41   update.nodes.push_back(empty_document);
     42   return update;
     43 }
     44 
     45 BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus(
     46     BrowserAccessibility* root) {
     47   BrowserAccessibility* node = GetActiveDescendantFocus(root);
     48   return node;
     49 }
     50 
     51 void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent(
     52     ui::AXEvent event_type,
     53     BrowserAccessibility* node) {
     54   if (!node->IsNative())
     55     return;
     56 
     57   // Refer to AXObjectCache.mm (webkit).
     58   NSString* event_id = @"";
     59   switch (event_type) {
     60     case ui::AX_EVENT_ACTIVEDESCENDANTCHANGED:
     61       if (node->GetRole() == ui::AX_ROLE_TREE) {
     62         event_id = NSAccessibilitySelectedRowsChangedNotification;
     63       } else {
     64         event_id = NSAccessibilityFocusedUIElementChangedNotification;
     65         BrowserAccessibility* active_descendant_focus =
     66             GetActiveDescendantFocus(GetRoot());
     67         if (active_descendant_focus)
     68           node = active_descendant_focus;
     69       }
     70 
     71       break;
     72     case ui::AX_EVENT_ALERT:
     73       // Not used on Mac.
     74       return;
     75     case ui::AX_EVENT_BLUR:
     76       // A no-op on Mac.
     77       return;
     78     case ui::AX_EVENT_CHECKED_STATE_CHANGED:
     79       // Not used on Mac.
     80       return;
     81     case ui::AX_EVENT_CHILDREN_CHANGED:
     82       // TODO(dtseng): no clear equivalent on Mac.
     83       return;
     84     case ui::AX_EVENT_FOCUS:
     85       event_id = NSAccessibilityFocusedUIElementChangedNotification;
     86       break;
     87     case ui::AX_EVENT_LAYOUT_COMPLETE:
     88       event_id = @"AXLayoutComplete";
     89       break;
     90     case ui::AX_EVENT_LIVE_REGION_CHANGED:
     91       event_id = @"AXLiveRegionChanged";
     92       break;
     93     case ui::AX_EVENT_LOAD_COMPLETE:
     94       event_id = @"AXLoadComplete";
     95       break;
     96     case ui::AX_EVENT_MENU_LIST_VALUE_CHANGED:
     97       // Not used on Mac.
     98       return;
     99     case ui::AX_EVENT_SHOW:
    100       // Not used on Mac.
    101       return;
    102     case ui::AX_EVENT_HIDE:
    103       // Not used on Mac.
    104       return;
    105     case ui::AX_EVENT_ROW_COUNT_CHANGED:
    106       event_id = NSAccessibilityRowCountChangedNotification;
    107       break;
    108     case ui::AX_EVENT_ROW_COLLAPSED:
    109       event_id = @"AXRowCollapsed";
    110       break;
    111     case ui::AX_EVENT_ROW_EXPANDED:
    112       event_id = @"AXRowExpanded";
    113       break;
    114     case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
    115       // Not used on Mac.
    116       return;
    117     case ui::AX_EVENT_SELECTED_CHILDREN_CHANGED:
    118       event_id = NSAccessibilitySelectedChildrenChangedNotification;
    119       break;
    120     case ui::AX_EVENT_SELECTED_TEXT_CHANGED:
    121       event_id = NSAccessibilitySelectedTextChangedNotification;
    122       break;
    123     case ui::AX_EVENT_TEXT_INSERTED:
    124       // Not used on Mac.
    125       return;
    126     case ui::AX_EVENT_TEXT_REMOVED:
    127       // Not used on Mac.
    128       return;
    129     case ui::AX_EVENT_VALUE_CHANGED:
    130       event_id = NSAccessibilityValueChangedNotification;
    131       break;
    132     case ui::AX_EVENT_ARIA_ATTRIBUTE_CHANGED:
    133       // Not used on Mac.
    134       return;
    135     case ui::AX_EVENT_AUTOCORRECTION_OCCURED:
    136       // Not used on Mac.
    137       return;
    138     case ui::AX_EVENT_INVALID_STATUS_CHANGED:
    139       // Not used on Mac.
    140       return;
    141     case ui::AX_EVENT_LOCATION_CHANGED:
    142       // Not used on Mac.
    143       return;
    144     case ui::AX_EVENT_MENU_LIST_ITEM_SELECTED:
    145       // Not used on Mac.
    146       return;
    147     case ui::AX_EVENT_TEXT_CHANGED:
    148       // Not used on Mac.
    149       return;
    150     default:
    151       LOG(WARNING) << "Unknown accessibility event: " << event_type;
    152       return;
    153   }
    154   BrowserAccessibilityCocoa* native_node = node->ToBrowserAccessibilityCocoa();
    155   DCHECK(native_node);
    156   NSAccessibilityPostNotification(native_node, event_id);
    157 }
    158 
    159 void BrowserAccessibilityManagerMac::OnNodeCreationFinished(ui::AXNode* node) {
    160   BrowserAccessibility* obj = GetFromAXNode(node);
    161   if (obj && obj->HasStringAttribute(ui::AX_ATTR_LIVE_STATUS))
    162     created_live_region_ = true;
    163 }
    164 
    165 void BrowserAccessibilityManagerMac::OnTreeUpdateFinished() {
    166   if (!created_live_region_)
    167     return;
    168 
    169   // This code is to work around a bug in VoiceOver, where a new live
    170   // region that gets added is ignored. VoiceOver seems to only scan the
    171   // page for live regions once. By recreating the NSAccessibility
    172   // object for the root of the tree, we force VoiceOver to clear out its
    173   // internal state and find newly-added live regions this time.
    174   BrowserAccessibilityMac* root =
    175       static_cast<BrowserAccessibilityMac*>(GetRoot());
    176   root->RecreateNativeObject();
    177   NotifyAccessibilityEvent(ui::AX_EVENT_CHILDREN_CHANGED, root);
    178 
    179   created_live_region_ = false;
    180 }
    181 
    182 }  // namespace content
    183