tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

AccessibleWrap.mm (8714B)


      1 /* clang-format off */
      2 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      3 /* clang-format on */
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #include "DocAccessibleWrap.h"
      9 #include "nsObjCExceptions.h"
     10 #include "nsCocoaUtils.h"
     11 #include "nsUnicharUtils.h"
     12 
     13 #include "LocalAccessible-inl.h"
     14 #include "nsAccUtils.h"
     15 #include "mozilla/a11y/Role.h"
     16 #include "TextRange.h"
     17 #include "gfxPlatform.h"
     18 
     19 #import "MOXLandmarkAccessibles.h"
     20 #import "MOXMathAccessibles.h"
     21 #import "MOXOuterDoc.h"
     22 #import "MOXTextMarkerDelegate.h"
     23 #import "MOXWebAreaAccessible.h"
     24 #import "mozAccessible.h"
     25 #import "mozActionElements.h"
     26 #import "mozHTMLAccessible.h"
     27 #import "mozSelectableElements.h"
     28 #import "mozTableAccessible.h"
     29 #import "mozTextAccessible.h"
     30 
     31 using namespace mozilla;
     32 using namespace mozilla::a11y;
     33 
     34 AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc)
     35    : LocalAccessible(aContent, aDoc),
     36      mNativeObject(nil),
     37      mNativeInited(false) {
     38  if (aContent && aDoc && IsLiveRegion(aContent)) {
     39    // Check if this accessible is a live region and queue it
     40    // it for dispatching an event after it has been inserted.
     41    DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(aDoc);
     42    doc->QueueNewLiveRegion(this);
     43  }
     44 }
     45 
     46 AccessibleWrap::~AccessibleWrap() {}
     47 
     48 mozAccessible* AccessibleWrap::GetNativeObject() {
     49  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
     50 
     51  if (!mNativeInited && !mNativeObject) {
     52    // We don't creat OSX accessibles for xul tooltips, defunct accessibles,
     53    // <br> (whitespace) elements, or pruned children.
     54    //
     55    // To maintain a scripting environment where the XPCOM accessible hierarchy
     56    // look the same on all platforms, we still let the C++ objects be created
     57    // though.
     58    if (!IsXULTooltip() && !IsDefunct() && Role() != roles::WHITESPACE) {
     59      mNativeObject = [[GetNativeType() alloc] initWithAccessible:this];
     60    }
     61  }
     62 
     63  mNativeInited = true;
     64 
     65  return mNativeObject;
     66 
     67  NS_OBJC_END_TRY_BLOCK_RETURN(nil);
     68 }
     69 
     70 void AccessibleWrap::GetNativeInterface(void** aOutInterface) {
     71  *aOutInterface = static_cast<void*>(GetNativeObject());
     72 }
     73 
     74 // overridden in subclasses to create the right kind of object. by default we
     75 // create a generic 'mozAccessible' node.
     76 Class AccessibleWrap::GetNativeType() {
     77  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
     78 
     79  if (IsXULTabpanels()) {
     80    return [mozPaneAccessible class];
     81  }
     82 
     83  if (IsTable()) {
     84    return [mozTableAccessible class];
     85  }
     86 
     87  if (IsTableRow()) {
     88    return [mozTableRowAccessible class];
     89  }
     90 
     91  if (IsTableCell()) {
     92    return [mozTableCellAccessible class];
     93  }
     94 
     95  if (IsDoc()) {
     96    return [MOXWebAreaAccessible class];
     97  }
     98 
     99  if (IsOuterDoc()) {
    100    return [MOXOuterDoc class];
    101  }
    102 
    103  return GetTypeFromRole(Role());
    104 
    105  NS_OBJC_END_TRY_BLOCK_RETURN(nil);
    106 }
    107 
    108 // this method is very important. it is fired when an accessible object "dies".
    109 // after this point the object might still be around (because some 3rd party
    110 // still has a ref to it), but it is in fact 'dead'.
    111 void AccessibleWrap::Shutdown() {
    112  // this ensure we will not try to re-create the native object.
    113  mNativeInited = true;
    114 
    115  // we really intend to access the member directly.
    116  if (mNativeObject) {
    117    [mNativeObject expire];
    118    [mNativeObject release];
    119    mNativeObject = nil;
    120  }
    121 
    122  LocalAccessible::Shutdown();
    123 }
    124 
    125 nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
    126  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
    127 
    128  nsresult rv = LocalAccessible::HandleAccEvent(aEvent);
    129  NS_ENSURE_SUCCESS(rv, rv);
    130 
    131  if (IsDefunct()) {
    132    // The accessible can become defunct after their events are handled.
    133    return NS_OK;
    134  }
    135 
    136  uint32_t eventType = aEvent->GetEventType();
    137 
    138  if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
    139    DocAccessibleWrap* doc = static_cast<DocAccessibleWrap*>(Document());
    140    doc->ProcessNewLiveRegions();
    141  }
    142 
    143  if (((eventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED ||
    144        eventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED) &&
    145       !aEvent->FromUserInput()) ||
    146      eventType == nsIAccessibleEvent::EVENT_NAME_CHANGE) {
    147    for (LocalAccessible* container = aEvent->GetAccessible(); container;
    148         container = container->LocalParent()) {
    149      if (container->HasOwnContent() && IsLiveRegion(container->GetContent())) {
    150        // We rely on EventQueue::CoalesceEvents to remove duplicates
    151        Document()->FireDelayedEvent(
    152            nsIAccessibleEvent::EVENT_LIVE_REGION_CHANGED, container);
    153      }
    154    }
    155  }
    156 
    157  return NS_OK;
    158 
    159  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
    160 }
    161 
    162 bool AccessibleWrap::IsLiveRegion(nsIContent* aContent) {
    163  if (!aContent || !aContent->IsElement()) {
    164    return false;
    165  }
    166 
    167  static const dom::Element::AttrValuesArray sLiveRegionValues[] = {
    168      nsGkAtoms::OFF, nsGkAtoms::polite, nsGkAtoms::assertive, nullptr};
    169  int32_t attrValue = nsAccUtils::FindARIAAttrValueIn(
    170      aContent->AsElement(), nsGkAtoms::aria_live, sLiveRegionValues,
    171      eIgnoreCase);
    172  if (attrValue == 0) {
    173    // aria-live is "off", do nothing.
    174  } else if (attrValue > 0) {
    175    // aria-live attribute is polite or assertive. It's live!
    176    return true;
    177  } else if (const nsRoleMapEntry* roleMap =
    178                 aria::GetRoleMap(aContent->AsElement())) {
    179    // aria role defines it as a live region. It's live!
    180    if (roleMap->liveAttRule == ePoliteLiveAttr ||
    181        roleMap->liveAttRule == eAssertiveLiveAttr) {
    182      return true;
    183    }
    184  } else if (nsStaticAtom* value = GetAccService()->MarkupAttribute(
    185                 aContent, nsGkAtoms::aria_live)) {
    186    // HTML element defines it as a live region. It's live!
    187    if (value == nsGkAtoms::polite || value == nsGkAtoms::assertive) {
    188      return true;
    189    }
    190  }
    191 
    192  return false;
    193 }
    194 
    195 ////////////////////////////////////////////////////////////////////////////////
    196 // AccessibleWrap protected
    197 
    198 Class a11y::GetTypeFromRole(roles::Role aRole) {
    199  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
    200 
    201  switch (aRole) {
    202    case roles::COMBOBOX:
    203      return [mozPopupButtonAccessible class];
    204 
    205    case roles::PUSHBUTTON:
    206      return [mozButtonAccessible class];
    207 
    208    case roles::PAGETAB:
    209      return [mozTabAccessible class];
    210 
    211    case roles::DATE_EDITOR:
    212      return [mozDatePickerAccessible class];
    213 
    214    case roles::CHECKBUTTON:
    215    case roles::TOGGLE_BUTTON:
    216    case roles::SWITCH:
    217    case roles::CHECK_MENU_ITEM:
    218      return [mozCheckboxAccessible class];
    219 
    220    case roles::RADIOBUTTON:
    221    case roles::RADIO_MENU_ITEM:
    222      return [mozRadioButtonAccessible class];
    223 
    224    case roles::PROGRESSBAR:
    225      return [mozRangeAccessible class];
    226 
    227    case roles::METER:
    228      return [mozMeterAccessible class];
    229 
    230    case roles::SPINBUTTON:
    231    case roles::SLIDER:
    232      return [mozIncrementableAccessible class];
    233 
    234    case roles::HEADING:
    235      return [mozHeadingAccessible class];
    236 
    237    case roles::PAGETABLIST:
    238      return [mozTabGroupAccessible class];
    239 
    240    case roles::TEXT_LEAF:
    241    case roles::STATICTEXT:
    242      return [mozTextLeafAccessible class];
    243 
    244    case roles::LANDMARK:
    245      return [MOXLandmarkAccessible class];
    246 
    247    case roles::LINK:
    248      return [mozLinkAccessible class];
    249 
    250    case roles::LISTBOX:
    251      return [mozListboxAccessible class];
    252 
    253    case roles::LISTITEM:
    254      return [MOXListItemAccessible class];
    255 
    256    case roles::OPTION: {
    257      return [mozOptionAccessible class];
    258    }
    259 
    260    case roles::RICH_OPTION: {
    261      return [mozSelectableChildAccessible class];
    262    }
    263 
    264    case roles::COMBOBOX_LIST:
    265    case roles::MENUBAR:
    266    case roles::MENUPOPUP: {
    267      return [mozMenuAccessible class];
    268    }
    269 
    270    case roles::COMBOBOX_OPTION:
    271    case roles::PARENT_MENUITEM:
    272    case roles::MENUITEM: {
    273      return [mozMenuItemAccessible class];
    274    }
    275 
    276    case roles::MATHML_ROOT:
    277      return [MOXMathRootAccessible class];
    278 
    279    case roles::MATHML_SQUARE_ROOT:
    280      return [MOXMathSquareRootAccessible class];
    281 
    282    case roles::MATHML_FRACTION:
    283      return [MOXMathFractionAccessible class];
    284 
    285    case roles::MATHML_SUB:
    286    case roles::MATHML_SUP:
    287    case roles::MATHML_SUB_SUP:
    288      return [MOXMathSubSupAccessible class];
    289 
    290    case roles::MATHML_UNDER:
    291    case roles::MATHML_OVER:
    292    case roles::MATHML_UNDER_OVER:
    293      return [MOXMathUnderOverAccessible class];
    294 
    295    case roles::OUTLINE:
    296    case roles::TREE_TABLE:
    297      return [mozOutlineAccessible class];
    298 
    299    case roles::OUTLINEITEM:
    300      return [mozOutlineRowAccessible class];
    301 
    302    case roles::LABEL:
    303      return [MOXLabelAccessible class];
    304 
    305    default:
    306      return [mozAccessible class];
    307  }
    308 
    309  return nil;
    310 
    311  NS_OBJC_END_TRY_BLOCK_RETURN(nil);
    312 }