DocAccessibleWrap.mm (4473B)
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 "ARIAMap.h" 10 #include "DocAccessible-inl.h" 11 #include "nsAccUtils.h" 12 13 #import "mozAccessible.h" 14 #import "MOXTextMarkerDelegate.h" 15 16 using namespace mozilla; 17 using namespace mozilla::a11y; 18 19 DocAccessibleWrap::DocAccessibleWrap(dom::Document* aDocument, 20 PresShell* aPresShell) 21 : DocAccessible(aDocument, aPresShell) {} 22 23 void DocAccessibleWrap::Shutdown() { 24 [MOXTextMarkerDelegate destroyForDoc:this]; 25 DocAccessible::Shutdown(); 26 } 27 28 DocAccessibleWrap::~DocAccessibleWrap() {} 29 30 void DocAccessibleWrap::AttributeChanged(dom::Element* aElement, 31 int32_t aNameSpaceID, 32 nsAtom* aAttribute, 33 AttrModType aModType, 34 const nsAttrValue* aOldValue) { 35 DocAccessible::AttributeChanged(aElement, aNameSpaceID, aAttribute, aModType, 36 aOldValue); 37 if (aAttribute == nsGkAtoms::aria_errormessage) { 38 LocalAccessible* accessible = 39 mContent != aElement ? GetAccessible(aElement) : this; 40 if (!accessible) { 41 return; 42 } 43 FireDelayedEvent(nsIAccessibleEvent::EVENT_ERRORMESSAGE_CHANGED, 44 accessible); 45 } 46 47 if (aAttribute == nsGkAtoms::aria_live) { 48 LocalAccessible* accessible = 49 mContent != aElement ? GetAccessible(aElement) : this; 50 if (!accessible) { 51 return; 52 } 53 54 static const dom::Element::AttrValuesArray sLiveRegionValues[] = { 55 nsGkAtoms::OFF, nsGkAtoms::polite, nsGkAtoms::assertive, nullptr}; 56 int32_t attrValue = nsAccUtils::FindARIAAttrValueIn( 57 aElement, nsGkAtoms::aria_live, sLiveRegionValues, eIgnoreCase); 58 if (attrValue > 0) { 59 if (!aOldValue || aOldValue->IsEmptyString() || 60 aOldValue->Equals(nsGkAtoms::OFF, eIgnoreCase)) { 61 // This element just got an active aria-live attribute value 62 FireDelayedEvent(nsIAccessibleEvent::EVENT_LIVE_REGION_ADDED, 63 accessible); 64 } 65 } else { 66 if (aOldValue && (aOldValue->Equals(nsGkAtoms::polite, eIgnoreCase) || 67 aOldValue->Equals(nsGkAtoms::assertive, eIgnoreCase))) { 68 // This element lost an active live region 69 FireDelayedEvent(nsIAccessibleEvent::EVENT_LIVE_REGION_REMOVED, 70 accessible); 71 } else if (attrValue == 0) { 72 // aria-live="off", check if its a role-based live region that 73 // needs to be removed. 74 if (const nsRoleMapEntry* roleMap = accessible->ARIARoleMap()) { 75 // aria role defines it as a live region. It's live! 76 if (roleMap->liveAttRule == ePoliteLiveAttr || 77 roleMap->liveAttRule == eAssertiveLiveAttr) { 78 FireDelayedEvent(nsIAccessibleEvent::EVENT_LIVE_REGION_REMOVED, 79 accessible); 80 } 81 } else if (nsStaticAtom* value = GetAccService()->MarkupAttribute( 82 aElement, nsGkAtoms::aria_live)) { 83 // HTML element defines it as a live region. It's live! 84 if (value == nsGkAtoms::polite || value == nsGkAtoms::assertive) { 85 FireDelayedEvent(nsIAccessibleEvent::EVENT_LIVE_REGION_REMOVED, 86 accessible); 87 } 88 } 89 } 90 } 91 } 92 } 93 94 void DocAccessibleWrap::QueueNewLiveRegion(LocalAccessible* aAccessible) { 95 if (!aAccessible) { 96 return; 97 } 98 99 mNewLiveRegions.Insert(aAccessible->UniqueID()); 100 } 101 102 void DocAccessibleWrap::ProcessNewLiveRegions() { 103 for (const auto& uniqueID : mNewLiveRegions) { 104 if (LocalAccessible* liveRegion = 105 GetAccessibleByUniqueID(const_cast<void*>(uniqueID))) { 106 FireDelayedEvent(nsIAccessibleEvent::EVENT_LIVE_REGION_ADDED, liveRegion); 107 } 108 } 109 110 mNewLiveRegions.Clear(); 111 } 112 113 void DocAccessibleWrap::DoInitialUpdate() { 114 DocAccessible::DoInitialUpdate(); 115 if (IsLiveRegion(mDocumentNode->GetBodyElement())) { 116 // Check if this doc's body element is a live region 117 QueueNewLiveRegion(this); 118 } 119 120 ProcessNewLiveRegions(); 121 }