2 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
8 #import "mozActionElements.h"
11 #include "LocalAccessible-inl.h"
12 #include "DocAccessible.h"
13 #include "XULTabAccessible.h"
14 #include "HTMLFormControlAccessible.h"
16 #include "nsCocoaUtils.h"
17 #include "mozilla/FloatingPoint.h"
19 using namespace mozilla::a11y;
21 @implementation mozButtonAccessible
23 - (NSNumber*)moxHasPopup {
24 return @([self stateWithMask:states::HASPOPUP] != 0);
27 - (NSString*)moxPopupValue {
28 if ([self stateWithMask:states::HASPOPUP] != 0) {
29 return utils::GetAccAttr(self, nsGkAtoms::aria_haspopup);
37 @implementation mozPopupButtonAccessible
39 - (NSString*)moxTitle {
40 // Popup buttons don't have titles.
44 - (BOOL)moxBlockSelector:(SEL)selector {
45 if (selector == @selector(moxHasPopup)) {
49 return [super moxBlockSelector:selector];
52 - (NSArray*)moxChildren {
53 if ([self stateWithMask:states::EXPANDED] == 0) {
54 // If the popup button is collapsed don't return its children.
58 return [super moxChildren];
61 - (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled {
62 [super stateChanged:state isEnabled:enabled];
64 if (state == states::EXPANDED) {
65 // If the EXPANDED state is updated, fire AXMenu events on the
66 // popups child which is the actual menu.
67 if (mozAccessible* popup = (mozAccessible*)[self childAt:0]) {
68 [popup moxPostNotification:(enabled ? @"AXMenuOpened" : @"AXMenuClosed")];
75 @implementation mozRadioButtonAccessible
77 - (NSArray*)moxLinkedUIElements {
78 return [[self getRelationsByType:RelationType::MEMBER_OF]
79 arrayByAddingObjectsFromArray:[super moxLinkedUIElements]];
84 @implementation mozCheckboxAccessible
87 // check if we're checked or in a mixed state
89 [self stateWithMask:(states::CHECKED | states::PRESSED | states::MIXED)];
90 if (state & (states::CHECKED | states::PRESSED)) {
94 if (state & states::MIXED) {
102 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
104 return [NSNumber numberWithInt:[self isChecked]];
106 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
109 - (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled {
110 [super stateChanged:state isEnabled:enabled];
112 if (state & (states::CHECKED | states::PRESSED | states::MIXED)) {
113 [self moxPostNotification:NSAccessibilityValueChangedNotification];
119 @implementation mozPaneAccessible
121 - (NSArray*)moxChildren {
122 // By default, all tab panels are exposed in the a11y tree
123 // even if the tab they represent isn't the active tab. To
124 // prevent VoiceOver from navigating background tab content,
125 // only expose the tab panel that is currently on screen.
126 for (mozAccessible* child in [super moxChildren]) {
127 if (!([child state] & states::OFFSCREEN)) {
128 return [NSArray arrayWithObject:GetObjectOrRepresentedView(child)];
131 MOZ_ASSERT_UNREACHABLE("We have no on screen tab content?");
137 @implementation mozRangeAccessible
140 return [NSNumber numberWithDouble:mGeckoAccessible->CurValue()];
144 return [NSNumber numberWithDouble:mGeckoAccessible->MinValue()];
148 return [NSNumber numberWithDouble:mGeckoAccessible->MaxValue()];
151 - (NSString*)moxOrientation {
152 RefPtr<AccAttributes> attributes = mGeckoAccessible->Attributes();
155 attributes->GetAttribute(nsGkAtoms::aria_orientation, result);
156 if (result.Equals(u"horizontal"_ns)) {
157 return NSAccessibilityHorizontalOrientationValue;
158 } else if (result.Equals(u"vertical"_ns)) {
159 return NSAccessibilityVerticalOrientationValue;
163 return NSAccessibilityUnknownOrientationValue;
166 - (void)handleAccessibleEvent:(uint32_t)eventType {
168 case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE:
169 case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
170 [self moxPostNotification:NSAccessibilityValueChangedNotification];
173 [super handleAccessibleEvent:eventType];
180 @implementation mozMeterAccessible
182 - (NSString*)moxValueDescription {
183 nsAutoString valueDesc;
184 mGeckoAccessible->Value(valueDesc);
185 if (mGeckoAccessible->TagName() != nsGkAtoms::meter) {
186 // We're dealing with an aria meter, which shouldn't get
188 return nsCocoaUtils::ToNSString(valueDesc);
191 if (!valueDesc.IsEmpty()) {
192 // Append a comma to separate the existing value description
193 // from the value region.
194 valueDesc.Append(u", "_ns);
196 // We need to concat the given value description
197 // with a description of the value as either optimal,
198 // suboptimal, or critical.
200 if (mGeckoAccessible->IsRemote()) {
201 region = mGeckoAccessible->AsRemote()->ValueRegion();
203 HTMLMeterAccessible* localMeter =
204 static_cast<HTMLMeterAccessible*>(mGeckoAccessible->AsLocal());
205 region = localMeter->ValueRegion();
209 valueDesc.Append(u"Optimal value"_ns);
210 } else if (region == 0) {
211 valueDesc.Append(u"Suboptimal value"_ns);
213 MOZ_ASSERT(region == -1);
214 valueDesc.Append(u"Critical value"_ns);
217 return nsCocoaUtils::ToNSString(valueDesc);
222 @implementation mozIncrementableAccessible
224 - (NSString*)moxValueDescription {
225 nsAutoString valueDesc;
226 mGeckoAccessible->Value(valueDesc);
227 return nsCocoaUtils::ToNSString(valueDesc);
230 - (void)moxSetValue:(id)value {
231 [self setValue:([value doubleValue])];
234 - (void)moxPerformIncrement {
235 [self changeValueBySteps:1];
238 - (void)moxPerformDecrement {
239 [self changeValueBySteps:-1];
243 * Updates the accessible's current value by factor and step.
245 * factor: A signed integer representing the number of times to
246 * apply step to the current value. A positive value will increment,
247 * while a negative one will decrement.
248 * step: An unsigned integer specified by the webauthor and indicating the
249 * amount by which to increment/decrement the current value.
251 - (void)changeValueBySteps:(int)factor {
252 MOZ_ASSERT(mGeckoAccessible, "mGeckoAccessible is null");
255 mGeckoAccessible->CurValue() + (mGeckoAccessible->Step() * factor);
256 [self setValue:(newValue)];
260 * Updates the accessible's current value to the specified value
262 - (void)setValue:(double)value {
263 MOZ_ASSERT(mGeckoAccessible, "mGeckoAccessible is null");
264 mGeckoAccessible->SetCurValue(value);
269 @implementation mozDatePickerAccessible
271 - (NSString*)moxTitle {
272 return utils::LocalizedString(u"dateField"_ns);