| File: | Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm |
| Warning: | line 391, column 19 Local variable 'object' is uncounted and unsafe |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* |
| 2 | * Copyright (C) 2008-2025 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
| 14 | * its contributors may be used to endorse or promote products derived |
| 15 | * from this software without specific prior written permission. |
| 16 | * |
| 17 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| 18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 20 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| 21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | */ |
| 28 | |
| 29 | #import "config.h" |
| 30 | #import "WebAccessibilityObjectWrapperMac.h" |
| 31 | |
| 32 | #import "AXIsolatedTree.h" |
| 33 | #import "AXObjectCacheInlines.h" |
| 34 | |
| 35 | #if PLATFORM(MAC)(defined 1 && 1) |
| 36 | |
| 37 | #import "AXCrossProcessSearch.h" |
| 38 | #import "AXIsolatedObject.h" |
| 39 | #import "AXLogger.h" |
| 40 | #import "AXRemoteFrame.h" |
| 41 | #import "AXSearchManager.h" |
| 42 | #import "AXTextMarker.h" |
| 43 | #import "AXTreeStore.h" |
| 44 | #import "AXTreeStoreInlines.h" |
| 45 | #import "AccessibilityObjectInlines.h" |
| 46 | #import "AccessibilityProgressIndicator.h" |
| 47 | #import "AccessibilityRenderObject.h" |
| 48 | #import "AccessibilityScrollView.h" |
| 49 | #import "AccessibilitySpinButton.h" |
| 50 | #import "AccessibilityTableColumn.h" |
| 51 | #import "CGUtilities.h" |
| 52 | #import "Chrome.h" |
| 53 | #import "ChromeClient.h" |
| 54 | #import "ContextMenuController.h" |
| 55 | #import "DateComponents.h" |
| 56 | #import "DocumentView.h" |
| 57 | #import "ElementInlines.h" |
| 58 | #import "Font.h" |
| 59 | #import "FontCascade.h" |
| 60 | #import "FrameSelection.h" |
| 61 | #import "HTMLAnchorElement.h" |
| 62 | #import "HTMLAreaElement.h" |
| 63 | #import "HTMLFrameOwnerElement.h" |
| 64 | #import "HTMLImageElement.h" |
| 65 | #import "HTMLInputElement.h" |
| 66 | #import "HTMLNames.h" |
| 67 | #import "IntRect.h" |
| 68 | #import "LocalFrame.h" |
| 69 | #import "LocalFrameLoaderClient.h" |
| 70 | #import "LocalizedStrings.h" |
| 71 | #import "Page.h" |
| 72 | #import "PluginDocument.h" |
| 73 | #import "PluginViewBase.h" |
| 74 | #import "Range.h" |
| 75 | #import "RenderInline.h" |
| 76 | #import "RenderObjectInlines.h" |
| 77 | #import "RenderTextControl.h" |
| 78 | #import "RenderView.h" |
| 79 | #import "RenderWidget.h" |
| 80 | #import "ScrollView.h" |
| 81 | #import "Settings.h" |
| 82 | #import "TextIterator.h" |
| 83 | #import "VisibleUnits.h" |
| 84 | #import "WebCoreFrameView.h" |
| 85 | #import <AppKit/NSAccessibilityConstants.h> |
| 86 | #import <pal/spi/cocoa/NSAccessibilitySPI.h> |
| 87 | #import <wtf/ObjCRuntimeExtras.h> |
| 88 | #import <wtf/RuntimeApplicationChecks.h> |
| 89 | #import <wtf/cocoa/TypeCastsCocoa.h> |
| 90 | #import <wtf/cocoa/VectorCocoa.h> |
| 91 | #import <wtf/text/MakeString.h> |
| 92 | #import <wtf/text/WTFString.h> |
| 93 | |
| 94 | #if ENABLE(MODEL_ELEMENT_ACCESSIBILITY)(defined 1 && 1) |
| 95 | #import "ModelPlayerAccessibilityChildren.h" |
| 96 | #endif |
| 97 | |
| 98 | using namespace WebCore; |
| 99 | |
| 100 | static id attributeValueForTesting(const RefPtr<AXCoreObject>&, NSString *); |
| 101 | |
| 102 | // Static C helper functions. |
| 103 | |
| 104 | // The CFAttributedStringType representation of the text associated with this accessibility |
| 105 | // object that is specified by the given range. |
| 106 | static NSAttributedString *attributedStringForNSRange(const AXCoreObject& backingObject, NSRange range) |
| 107 | { |
| 108 | if (!range.length) |
| 109 | return nilnullptr; |
| 110 | |
| 111 | auto markerRange = backingObject.textMarkerRangeForNSRange(range); |
| 112 | if (!markerRange) |
| 113 | return nilnullptr; |
| 114 | |
| 115 | auto attributedString = backingObject.attributedStringForTextMarkerRange(WTF::move(markerRange), AXCoreObject::SpellCheck::Yes); |
| 116 | return [attributedString length] ? attributedString.autorelease() : nilnullptr; |
| 117 | } |
| 118 | |
| 119 | // The RTF representation of the text associated with this accessibility object that is |
| 120 | // specified by the given range. |
| 121 | static NSData *rtfForNSRange(const AXCoreObject& backingObject, NSRange range) |
| 122 | { |
| 123 | RetainPtr<NSAttributedString> attributedString = attributedStringForNSRange(backingObject, range); |
| 124 | return [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:@{ }]; |
| 125 | } |
| 126 | |
| 127 | // Date time helpers. |
| 128 | |
| 129 | // VO requests a bit-wise combination of these constants via the API |
| 130 | // AXDateTimeComponents to determine which fields of a datetime value are presented to the user. |
| 131 | typedef NS_OPTIONS(NSUInteger, AXFDateTimeComponent)__attribute__((availability(swift,unavailable))) NSUInteger AXFDateTimeComponent ; enum __attribute__((flag_enum,enum_extensibility(open))) : AXFDateTimeComponent { |
| 132 | AXFDateTimeComponentSeconds = 0x0002, |
| 133 | AXFDateTimeComponentMinutes = 0x0004, |
| 134 | AXFDateTimeComponentHours = 0x0008, |
| 135 | AXFDateTimeComponentDays = 0x0020, |
| 136 | AXFDateTimeComponentMonths = 0x0040, |
| 137 | AXFDateTimeComponentYears = 0x0080, |
| 138 | AXFDateTimeComponentEras = 0x0100 |
| 139 | }; |
| 140 | |
| 141 | static inline unsigned convertToAXFDateTimeComponents(DateComponentsType type) |
| 142 | { |
| 143 | switch (type) { |
| 144 | case DateComponentsType::Invalid: |
| 145 | return 0; |
| 146 | case DateComponentsType::Date: |
| 147 | return AXFDateTimeComponentDays | AXFDateTimeComponentMonths | AXFDateTimeComponentYears; |
| 148 | case DateComponentsType::DateTimeLocal: |
| 149 | return AXFDateTimeComponentSeconds | AXFDateTimeComponentMinutes | AXFDateTimeComponentHours |
| 150 | | AXFDateTimeComponentDays | AXFDateTimeComponentMonths | AXFDateTimeComponentYears; |
| 151 | case DateComponentsType::Month: |
| 152 | return AXFDateTimeComponentMonths | AXFDateTimeComponentYears; |
| 153 | case DateComponentsType::Time: |
| 154 | return AXFDateTimeComponentSeconds | AXFDateTimeComponentMinutes | AXFDateTimeComponentHours; |
| 155 | case DateComponentsType::Week: |
| 156 | return 0; |
| 157 | }; |
| 158 | } |
| 159 | |
| 160 | // VoiceOver expects the datetime value in the local time zone. Since we store it in GMT, we need to convert it to local before returning it to VoiceOver. |
| 161 | // This helper funtion computes the offset to go from local to GMT and returns its opposite. |
| 162 | static inline NSInteger gmtToLocalTimeOffset(DateComponentsType type) |
| 163 | { |
| 164 | NSTimeZone *timeZone = [NSTimeZone localTimeZone]; |
| 165 | NSDate *now = [NSDate date]; |
| 166 | NSInteger offset = -1 * [timeZone secondsFromGMTForDate:now]; |
| 167 | if (type != DateComponentsType::DateTimeLocal && [timeZone isDaylightSavingTimeForDate:now]) |
| 168 | return offset + 3600; // + number of seconds in an hour. |
| 169 | return offset; |
| 170 | } |
| 171 | |
| 172 | @implementation WebAccessibilityObjectWrapper |
| 173 | |
| 174 | - (void)detach |
| 175 | { |
| 176 | AX_ASSERT(isMainThread())((void)0); |
| 177 | |
| 178 | // If the IsolatedObject is initialized, do not UnregisterUniqueIdForUIElement here because the wrapper may be in the middle of serving a request on the AX thread. |
| 179 | // The IsolatedObject is capable to tend to some requests after the live object is gone. |
| 180 | // In regular mode, UnregisterUniqueIdForUIElement immediately. |
| 181 | #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)(defined 1 && 1) |
| 182 | if (!m_isolatedObjectInitialized) |
| 183 | #endif |
| 184 | NSAccessibilityUnregisterUniqueIdForUIElement(self); |
| 185 | |
| 186 | [super detach]; |
| 187 | } |
| 188 | |
| 189 | #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)(defined 1 && 1) |
| 190 | - (void)detachIsolatedObject:(AccessibilityDetachmentType)detachmentType |
| 191 | { |
| 192 | // Only unregister this wrapper if the underlying object or cache is being destroyed. Unregistering it in other cases (like `ElementChanged`) |
| 193 | // would cause AX clients to get a notification that this wrapper was destroyed, which wouldn't be true. |
| 194 | if (detachmentType == AccessibilityDetachmentType::ElementDestroyed || detachmentType == AccessibilityDetachmentType::CacheDestroyed) |
| 195 | NSAccessibilityUnregisterUniqueIdForUIElement(self); |
| 196 | [super detachIsolatedObject:detachmentType]; |
| 197 | } |
| 198 | #endif |
| 199 | |
| 200 | - (id)attachmentView |
| 201 | { |
| 202 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 203 | RefPtr<AXCoreObject> backingObject = protectedSelf.get().axBackingObject; |
| 204 | if (!backingObject) |
| 205 | return nilnullptr; |
| 206 | |
| 207 | RefPtr widget = backingObject->widgetForAttachmentView(); |
| 208 | return widget ? NSAccessibilityUnignoredDescendant(widget->platformWidget()) : nilnullptr; |
| 209 | }); |
| 210 | } |
| 211 | |
| 212 | #pragma mark SystemInterface wrappers |
| 213 | |
| 214 | static inline BOOL AXObjectIsTextMarker(id object) |
| 215 | { |
| 216 | return object && CFGetTypeID((__bridge CFTypeRef)object) == AXTextMarkerGetTypeID(); |
| 217 | } |
| 218 | |
| 219 | static inline BOOL AXObjectIsTextMarkerRange(id object) |
| 220 | { |
| 221 | return object && CFGetTypeID((__bridge CFTypeRef)object) == AXTextMarkerRangeGetTypeID(); |
| 222 | } |
| 223 | |
| 224 | #pragma mark Other helpers |
| 225 | |
| 226 | static IntRect screenToContents(AXCoreObject& axObject, IntRect&& rect) |
| 227 | { |
| 228 | AX_ASSERT(isMainThread())((void)0); |
| 229 | |
| 230 | RefPtr document = axObject.document(); |
| 231 | RefPtr frameView = document ? document->view() : nullptr; |
| 232 | if (!frameView) |
| 233 | return { }; |
| 234 | |
| 235 | IntPoint startPoint = frameView->screenToContents(rect.minXMaxYCorner()); |
| 236 | IntPoint endPoint = frameView->screenToContents(rect.maxXMinYCorner()); |
| 237 | return IntRect(startPoint.x(), startPoint.y(), endPoint.x() - startPoint.x(), endPoint.y() - startPoint.y()); |
| 238 | } |
| 239 | |
| 240 | #pragma mark Select text helpers |
| 241 | |
| 242 | // To be deprecated. |
| 243 | static std::pair<AccessibilitySearchTextCriteria, AccessibilityTextOperation> accessibilityTextCriteriaForParameterizedAttribute(const NSDictionary *parameterizedAttribute) |
| 244 | { |
| 245 | AccessibilitySearchTextCriteria criteria; |
| 246 | AccessibilityTextOperation operation; |
| 247 | |
| 248 | RetainPtr<NSString> activityParameter = [parameterizedAttribute objectForKey:NSAccessibilitySelectTextActivity@"AXSelectTextActivity"]; |
| 249 | RetainPtr<NSString> ambiguityResolutionParameter = [parameterizedAttribute objectForKey:NSAccessibilitySelectTextAmbiguityResolution@"AXSelectTextAmbiguityResolution"]; |
| 250 | RetainPtr<NSString> replacementStringParameter = [parameterizedAttribute objectForKey:NSAccessibilitySelectTextReplacementString@"AXSelectTextReplacementString"]; |
| 251 | RetainPtr<NSArray> searchStringsParameter = [parameterizedAttribute objectForKey:NSAccessibilitySelectTextSearchStrings@"AXSelectTextSearchStrings"]; |
| 252 | |
| 253 | if ([activityParameter isKindOfClass:[NSString class]]) { |
| 254 | if ([activityParameter isEqualToString:NSAccessibilitySelectTextActivityFindAndReplace@"AXSelectTextActivityFindAndReplace"]) |
| 255 | operation.type = AccessibilityTextOperationType::Replace; |
| 256 | else if ([activityParameter isEqualToString:NSAccessibilitySelectTextActivityFindAndCapitalize@"AXSelectTextActivityFindAndCapitalize"]) |
| 257 | operation.type = AccessibilityTextOperationType::Capitalize; |
| 258 | else if ([activityParameter isEqualToString:NSAccessibilitySelectTextActivityFindAndLowercase@"AXSelectTextActivityFindAndLowercase"]) |
| 259 | operation.type = AccessibilityTextOperationType::Lowercase; |
| 260 | else if ([activityParameter isEqualToString:NSAccessibilitySelectTextActivityFindAndUppercase@"AXSelectTextActivityFindAndUppercase"]) |
| 261 | operation.type = AccessibilityTextOperationType::Uppercase; |
| 262 | } |
| 263 | |
| 264 | criteria.direction = AccessibilitySearchTextDirection::Closest; |
| 265 | if ([ambiguityResolutionParameter isKindOfClass:[NSString class]]) { |
| 266 | if ([ambiguityResolutionParameter isEqualToString:NSAccessibilitySelectTextAmbiguityResolutionClosestAfterSelection@"AXSelectTextAmbiguityResolutionClosestAfterSelection"]) |
| 267 | criteria.direction = AccessibilitySearchTextDirection::Forward; |
| 268 | else if ([ambiguityResolutionParameter isEqualToString:NSAccessibilitySelectTextAmbiguityResolutionClosestBeforeSelection@"AXSelectTextAmbiguityResolutionClosestBeforeSelection"]) |
| 269 | criteria.direction = AccessibilitySearchTextDirection::Backward; |
| 270 | } |
| 271 | |
| 272 | if ([replacementStringParameter isKindOfClass:[NSString class]]) |
| 273 | operation.replacementStrings = { String(replacementStringParameter.get()) }; |
| 274 | |
| 275 | if ([searchStringsParameter isKindOfClass:[NSArray class]]) |
| 276 | criteria.searchStrings = makeVector<String>(searchStringsParameter.get()); |
| 277 | |
| 278 | return std::make_pair(criteria, operation); |
| 279 | } |
| 280 | |
| 281 | static AccessibilitySearchTextCriteria accessibilitySearchTextCriteriaForParameterizedAttribute(const NSDictionary *params) |
| 282 | { |
| 283 | AccessibilitySearchTextCriteria criteria; |
| 284 | |
| 285 | RetainPtr<NSArray> searchStrings = [params objectForKey:NSAccessibilitySearchTextSearchStrings@"AXSearchTextSearchStrings"]; |
| 286 | RetainPtr<NSString> start = [params objectForKey:NSAccessibilitySearchTextStartFrom@"AXSearchTextStartFrom"]; |
| 287 | RetainPtr<NSString> direction = [params objectForKey:NSAccessibilitySearchTextDirection@"AXSearchTextDirection"]; |
| 288 | |
| 289 | if ([searchStrings isKindOfClass:[NSArray class]]) |
| 290 | criteria.searchStrings = makeVector<String>(searchStrings.get()); |
| 291 | |
| 292 | if ([start isKindOfClass:[NSString class]]) { |
| 293 | if ([start isEqualToString:NSAccessibilitySearchTextStartFromBegin@"AXSearchTextStartFromBegin"]) |
| 294 | criteria.start = AccessibilitySearchTextStartFrom::Begin; |
| 295 | else if ([start isEqualToString:NSAccessibilitySearchTextStartFromEnd@"AXSearchTextStartFromEnd"]) |
| 296 | criteria.start = AccessibilitySearchTextStartFrom::End; |
| 297 | } |
| 298 | |
| 299 | if ([direction isKindOfClass:[NSString class]]) { |
| 300 | if ([direction isEqualToString:NSAccessibilitySearchTextDirectionBackward@"AXSearchTextDirectionBackward"]) |
| 301 | criteria.direction = AccessibilitySearchTextDirection::Backward; |
| 302 | else if ([direction isEqualToString:NSAccessibilitySearchTextDirectionClosest@"AXSearchTextDirectionClosest"]) |
| 303 | criteria.direction = AccessibilitySearchTextDirection::Closest; |
| 304 | else if ([direction isEqualToString:NSAccessibilitySearchTextDirectionAll@"AXSearchTextDirectionAll"]) |
| 305 | criteria.direction = AccessibilitySearchTextDirection::All; |
| 306 | } |
| 307 | |
| 308 | return criteria; |
| 309 | } |
| 310 | |
| 311 | static AccessibilityTextOperation accessibilityTextOperationForParameterizedAttribute(AXObjectCache* axObjectCache, const NSDictionary *parameterizedAttribute) |
| 312 | { |
| 313 | AccessibilityTextOperation operation; |
| 314 | |
| 315 | RetainPtr<NSArray> markerRanges = [parameterizedAttribute objectForKey:NSAccessibilityTextOperationMarkerRanges@"AXTextOperationMarkerRanges"]; |
| 316 | RetainPtr<NSString> operationType = [parameterizedAttribute objectForKey:NSAccessibilityTextOperationType@"AXTextOperationType"]; |
| 317 | RetainPtr<NSArray> individualReplacementStrings = [parameterizedAttribute objectForKey:NSAccessibilityTextOperationIndividualReplacementStrings@"AXTextOperationIndividualReplacementStrings"]; |
| 318 | RetainPtr<NSString> replacementString = [parameterizedAttribute objectForKey:NSAccessibilityTextOperationReplacementString@"AXTextOperationReplacementString"]; |
| 319 | RetainPtr<NSNumber> smartReplace = [parameterizedAttribute objectForKey:NSAccessibilityTextOperationSmartReplace@"AXTextOperationSmartReplace"]; |
| 320 | |
| 321 | if ([markerRanges isKindOfClass:[NSArray class]]) { |
| 322 | operation.textRanges = makeVector(markerRanges.get(), [&axObjectCache] (id markerRange) { |
| 323 | AX_ASSERT(AXObjectIsTextMarkerRange(markerRange))((void)0); |
| 324 | return rangeForTextMarkerRange(axObjectCache, (AXTextMarkerRangeRef)markerRange); |
| 325 | }); |
| 326 | } |
| 327 | |
| 328 | if ([operationType isKindOfClass:[NSString class]]) { |
| 329 | if ([operationType isEqualToString:NSAccessibilityTextOperationReplace@"TextOperationReplace"]) |
| 330 | operation.type = AccessibilityTextOperationType::Replace; |
| 331 | else if ([operationType isEqualToString:NSAccessibilityTextOperationReplacePreserveCase@"TextOperationReplacePreserveCase"]) |
| 332 | operation.type = AccessibilityTextOperationType::ReplacePreserveCase; |
| 333 | else if ([operationType isEqualToString:NSAccessibilityTextOperationCapitalize@"Capitalize"]) |
| 334 | operation.type = AccessibilityTextOperationType::Capitalize; |
| 335 | else if ([operationType isEqualToString:NSAccessibilityTextOperationLowercase@"Lowercase"]) |
| 336 | operation.type = AccessibilityTextOperationType::Lowercase; |
| 337 | else if ([operationType isEqualToString:NSAccessibilityTextOperationUppercase@"Uppercase"]) |
| 338 | operation.type = AccessibilityTextOperationType::Uppercase; |
| 339 | } |
| 340 | |
| 341 | if ([individualReplacementStrings isKindOfClass:[NSArray class]]) { |
| 342 | operation.replacementStrings = makeVector<String>(individualReplacementStrings.get()); |
| 343 | } else if ([replacementString isKindOfClass:[NSString class]]) |
| 344 | operation.replacementStrings = { String(replacementString.get()) }; |
| 345 | |
| 346 | if ([smartReplace isKindOfClass:[NSNumber class]]) |
| 347 | operation.smartReplace = [smartReplace boolValue] ? AccessibilityTextOperationSmartReplace::Yes : AccessibilityTextOperationSmartReplace::No; |
| 348 | |
| 349 | return operation; |
| 350 | } |
| 351 | |
| 352 | static std::pair<AXTextMarkerRange, AccessibilitySearchDirection> misspellingSearchCriteriaForParameterizedAttribute(const NSDictionary *params) |
| 353 | { |
| 354 | RetainPtr<id> markerRangeRef = [params objectForKey:NSAccessibilityStartTextMarkerRangeParam@"AXStartTextMarkerRange"]; |
| 355 | if (!AXObjectIsTextMarkerRange(markerRangeRef.get())) |
| 356 | return { }; |
| 357 | |
| 358 | std::pair<AXTextMarkerRange, AccessibilitySearchDirection> criteria; |
| 359 | criteria.first = AXTextMarkerRange { (AXTextMarkerRangeRef)markerRangeRef.get() }; |
| 360 | |
| 361 | RetainPtr<NSNumber> forward = [params objectForKey:NSAccessibilitySearchTextDirection@"AXSearchTextDirection"]; |
| 362 | if ([forward isKindOfClass:[NSNumber class]]) |
| 363 | criteria.second = [forward boolValue] ? AccessibilitySearchDirection::Next : AccessibilitySearchDirection::Previous; |
| 364 | else |
| 365 | criteria.second = AccessibilitySearchDirection::Next; |
| 366 | |
| 367 | return criteria; |
| 368 | } |
| 369 | |
| 370 | #pragma mark Cross-process search helpers |
| 371 | |
| 372 | // Creates an NSAccessibilityRemoteUIElement from an AccessibilityRemoteToken. |
| 373 | // Returns nil if the token is empty or element creation fails. |
| 374 | static RetainPtr<id> remoteElementFromToken(const AccessibilityRemoteToken& token) |
| 375 | { |
| 376 | if (token.bytes.isEmpty()) |
| 377 | return nilnullptr; |
| 378 | RetainPtr<NSData> tokenData = adoptNS([[NSData alloc] initWithBytes:token.bytes.span().data() length:token.bytes.size()]); |
| 379 | return adoptNS([[NSAccessibilityRemoteUIElement alloc] initWithRemoteToken:tokenData.get()]); |
| 380 | } |
| 381 | |
| 382 | // Converts AccessibilitySearchResults to NSArray, creating platform elements for both |
| 383 | // local objects and remote tokens. |
| 384 | static RetainPtr<NSArray> searchResultsToNSArray(const AccessibilitySearchResults& searchResults, unsigned limit) |
| 385 | { |
| 386 | RetainPtr<NSMutableArray> result = adoptNS([[NSMutableArray alloc] init]); |
| 387 | for (const auto& searchResult : searchResults) { |
| 388 | if ([result count] >= limit) |
| 389 | break; |
| 390 | |
| 391 | if (auto* object = searchResult.objectIfLocalResult()) { |
Local variable 'object' is uncounted and unsafe | |
| 392 | if (RetainPtr wrapper = object->wrapper()) |
| 393 | [result addObject:wrapper.get()]; |
| 394 | } else if (searchResult.isRemote()) { |
| 395 | if (RetainPtr remoteElement = remoteElementFromToken(*searchResult.remoteToken())) |
| 396 | [result addObject:remoteElement.get()]; |
| 397 | } |
| 398 | } |
| 399 | return result; |
| 400 | } |
| 401 | |
| 402 | // Performs a search that may span multiple web content processes. |
| 403 | // For remote frames encountered during the search, sends IPC to query them and merges results. |
| 404 | // If we're in a child frame, eagerly dispatches parent search in parallel with local search. |
| 405 | static RetainPtr<NSArray> performSearchWithRemoteFrames(AXCoreObject& backingObject, AccessibilitySearchCriteria&& criteria) |
| 406 | { |
| 407 | unsigned originalLimit = criteria.resultsLimit; |
| 408 | auto searchResults = performSearchWithParentCoordination(backingObject, WTF::move(criteria)); |
| 409 | return searchResultsToNSArray(searchResults, originalLimit); |
| 410 | } |
| 411 | |
| 412 | #pragma mark Text Marker helpers |
| 413 | |
| 414 | static RetainPtr<AXTextMarkerRef> nextTextMarker(AXObjectCache* cache, const AXTextMarker& marker) |
| 415 | { |
| 416 | if (!cache) |
| 417 | return nilnullptr; |
| 418 | |
| 419 | auto nextMarker = cache->nextTextMarker(marker); |
| 420 | return nextMarker ? nextMarker.platformData() : nilnullptr; |
| 421 | } |
| 422 | |
| 423 | static RetainPtr<AXTextMarkerRef> previousTextMarker(AXObjectCache* cache, const AXTextMarker& marker) |
| 424 | { |
| 425 | if (!cache) |
| 426 | return nilnullptr; |
| 427 | |
| 428 | auto previousMarker = cache->previousTextMarker(marker); |
| 429 | return previousMarker ? previousMarker.platformData() : nilnullptr; |
| 430 | } |
| 431 | |
| 432 | static NSAttributedString *attributedStringForTextMarkerRange(const AXCoreObject& object, AXTextMarkerRangeRef textMarkerRangeRef, AXCoreObject::SpellCheck spellCheck) |
| 433 | { |
| 434 | if (!textMarkerRangeRef) |
| 435 | return nilnullptr; |
| 436 | |
| 437 | return object.attributedStringForTextMarkerRange({ textMarkerRangeRef }, spellCheck).autorelease(); |
| 438 | } |
| 439 | |
| 440 | ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-implementations" |
| 441 | - (NSArray*)accessibilityActionNames |
| 442 | { |
| 443 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 444 | if (!backingObject) |
| 445 | return nilnullptr; |
| 446 | |
| 447 | // All elements should get ShowMenu and ScrollToVisible. |
| 448 | // But certain earlier VoiceOver versions do not support scroll to visible, and it confuses them to see it in the list. |
| 449 | static NeverDestroyed<RetainPtr<NSArray>> defaultElementActions = @[NSAccessibilityShowMenuAction, NSAccessibilityScrollToVisibleAction@"AXScrollToVisible"]; |
| 450 | |
| 451 | // Action elements allow Press. |
| 452 | // The order is important to VoiceOver, which expects the 'default' action to be the first action. In this case the default action should be press. |
| 453 | static NeverDestroyed<RetainPtr<NSArray>> actionElementActions = @[NSAccessibilityPressAction, NSAccessibilityShowMenuAction, NSAccessibilityScrollToVisibleAction@"AXScrollToVisible"]; |
| 454 | |
| 455 | // Menu elements allow Press and Cancel. |
| 456 | static NeverDestroyed<RetainPtr<NSArray>> menuElementActions = [actionElementActions.get() arrayByAddingObject:NSAccessibilityCancelAction]; |
| 457 | |
| 458 | static NeverDestroyed<RetainPtr<NSArray>> incrementorActions = [defaultElementActions.get() arrayByAddingObjectsFromArray:@[NSAccessibilityIncrementAction, NSAccessibilityDecrementAction]]; |
| 459 | |
| 460 | if (backingObject->isSlider() || (backingObject->isSpinButton() && backingObject->spinButtonType() == SpinButtonType::Standalone)) { |
| 461 | // Non-standalone spinbuttons should not advertise the increment and decrement actions because they have separate increment and decrement controls. |
| 462 | return incrementorActions.get().get(); |
| 463 | } |
| 464 | |
| 465 | if (backingObject->isMenuRelated()) |
| 466 | return menuElementActions.get().get(); |
| 467 | if (backingObject->isAttachment()) |
| 468 | return [[self attachmentView] accessibilityActionNames]; |
| 469 | if (backingObject->supportsPressAction()) |
| 470 | return actionElementActions.get().get(); |
| 471 | |
| 472 | return defaultElementActions.get().get(); |
| 473 | } |
| 474 | ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop |
| 475 | |
| 476 | - (NSArray *)_additionalAccessibilityAttributeNames:(const RefPtr<AXCoreObject>&)backingObject |
| 477 | { |
| 478 | NSMutableArray *additional = [NSMutableArray array]; |
| 479 | if (backingObject->supportsActiveDescendant()) |
| 480 | [additional addObject:NSAccessibilityActiveElementAttribute@"AXActiveElement"]; |
| 481 | |
| 482 | if (backingObject->supportsARIAOwns()) |
| 483 | [additional addObject:NSAccessibilityOwnsAttribute@"AXOwns"]; |
| 484 | |
| 485 | if (backingObject->supportsExpanded() || backingObject->isSummary()) |
| 486 | [additional addObject:NSAccessibilityExpandedAttribute]; |
| 487 | |
| 488 | if (backingObject->isScrollbar() |
| 489 | || backingObject->isRadioGroup() |
| 490 | || backingObject->isSplitter() |
| 491 | || backingObject->isToolbar() |
| 492 | || backingObject->role() == AccessibilityRole::HorizontalRule) |
| 493 | [additional addObject:NSAccessibilityOrientationAttribute]; |
| 494 | |
| 495 | if (backingObject->supportsDragging()) |
| 496 | [additional addObject:NSAccessibilityGrabbedAttribute@"AXGrabbed"]; |
| 497 | |
| 498 | if (backingObject->supportsDropping()) |
| 499 | [additional addObject:NSAccessibilityDropEffectsAttribute@"AXDropEffects"]; |
| 500 | |
| 501 | if (backingObject->isExposableTable() && backingObject->supportsSelectedRows()) |
| 502 | [additional addObject:NSAccessibilitySelectedRowsAttribute]; |
| 503 | |
| 504 | if (backingObject->isTreeGrid() && backingObject->isExposableTable()) |
| 505 | [additional addObject:NSAccessibilityOrientationAttribute]; |
| 506 | |
| 507 | if (backingObject->supportsSetSize()) |
| 508 | [additional addObject:NSAccessibilityARIASetSizeAttribute@"AXARIASetSize"]; |
| 509 | |
| 510 | if (backingObject->supportsPosInSet()) |
| 511 | [additional addObject:NSAccessibilityARIAPosInSetAttribute@"AXARIAPosInSet"]; |
| 512 | |
| 513 | if (backingObject->supportsKeyShortcuts()) |
| 514 | [additional addObject:NSAccessibilityKeyShortcutsAttribute@"AXKeyShortcutsValue"]; |
| 515 | |
| 516 | if (backingObject->supportsLiveRegion()) { |
| 517 | [additional addObject:NSAccessibilityARIALiveAttribute@"AXARIALive"]; |
| 518 | [additional addObject:NSAccessibilityARIARelevantAttribute@"AXARIARelevant"]; |
| 519 | } |
| 520 | |
| 521 | bool addedHasPopup = false; |
| 522 | if (backingObject->hasPopup()) { |
| 523 | addedHasPopup = true; |
| 524 | [additional addObject:NSAccessibilityHasPopupAttribute@"AXHasPopup"]; |
| 525 | } |
| 526 | bool addedAriaAtomic = false; |
| 527 | bool addedSortDirection = false; |
| 528 | for (RefPtr ancestor = backingObject; ancestor; ancestor = ancestor->parentObject()) { |
| 529 | if (!addedHasPopup && ancestor->isLink() && ancestor->hasPopup()) { |
| 530 | // Require ancestor to be a link, matching AXCoreObject::selfOrAncestorLinkHasPopup. |
| 531 | addedHasPopup = true; |
| 532 | [additional addObject:NSAccessibilityHasPopupAttribute@"AXHasPopup"]; |
| 533 | } |
| 534 | |
| 535 | if (!addedAriaAtomic && ancestor->supportsLiveRegion()) { |
| 536 | addedAriaAtomic = true; |
| 537 | [additional addObject:NSAccessibilityARIAAtomicAttribute@"AXARIAAtomic"]; |
| 538 | } |
| 539 | |
| 540 | if (!addedSortDirection) { |
| 541 | auto sortDirection = ancestor->sortDirection(); |
| 542 | if (sortDirection != AccessibilitySortDirection::None && sortDirection != AccessibilitySortDirection::Invalid) { |
| 543 | addedSortDirection = true; |
| 544 | [additional addObject:NSAccessibilitySortDirectionAttribute]; |
| 545 | } |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | if (backingObject->supportsDatetimeAttribute()) |
| 550 | [additional addObject:NSAccessibilityDatetimeValueAttribute@"AXDateTimeValue"]; |
| 551 | |
| 552 | if (backingObject->supportsRequiredAttribute()) |
| 553 | [additional addObject:NSAccessibilityRequiredAttribute]; |
| 554 | |
| 555 | if (backingObject->isMathRoot()) { |
| 556 | // The index of a square root is always known, so there's no object associated with it. |
| 557 | if (!backingObject->isMathSquareRoot()) |
| 558 | [additional addObject:NSAccessibilityMathRootIndexAttribute@"AXMathRootIndex"]; |
| 559 | [additional addObject:NSAccessibilityMathRootRadicandAttribute@"AXMathRootRadicand"]; |
| 560 | } else if (backingObject->isMathFraction()) { |
| 561 | [additional addObject:NSAccessibilityMathFractionNumeratorAttribute@"AXMathFractionNumerator"]; |
| 562 | [additional addObject:NSAccessibilityMathFractionDenominatorAttribute@"AXMathFractionDenominator"]; |
| 563 | [additional addObject:NSAccessibilityMathLineThicknessAttribute@"AXMathLineThickness"]; |
| 564 | } else if (backingObject->isMathSubscriptSuperscript()) { |
| 565 | [additional addObject:NSAccessibilityMathBaseAttribute@"AXMathBase"]; |
| 566 | [additional addObject:NSAccessibilityMathSubscriptAttribute@"AXMathSubscript"]; |
| 567 | [additional addObject:NSAccessibilityMathSuperscriptAttribute@"AXMathSuperscript"]; |
| 568 | } else if (backingObject->isMathUnderOver()) { |
| 569 | [additional addObject:NSAccessibilityMathBaseAttribute@"AXMathBase"]; |
| 570 | [additional addObject:NSAccessibilityMathUnderAttribute@"AXMathUnder"]; |
| 571 | [additional addObject:NSAccessibilityMathOverAttribute@"AXMathOver"]; |
| 572 | } else if (backingObject->isMathFenced()) { |
| 573 | [additional addObject:NSAccessibilityMathFencedOpenAttribute@"AXMathFencedOpen"]; |
| 574 | [additional addObject:NSAccessibilityMathFencedCloseAttribute@"AXMathFencedClose"]; |
| 575 | } else if (backingObject->isMathMultiscript()) { |
| 576 | [additional addObject:NSAccessibilityMathBaseAttribute@"AXMathBase"]; |
| 577 | [additional addObject:NSAccessibilityMathPrescriptsAttribute@"AXMathPrescripts"]; |
| 578 | [additional addObject:NSAccessibilityMathPostscriptsAttribute@"AXMathPostscripts"]; |
| 579 | } |
| 580 | |
| 581 | // isStaticText() objects already note their support for path in `accessibilityAttributeNames`. |
| 582 | if (!backingObject->isStaticText() && backingObject->supportsPath()) |
| 583 | [additional addObject:NSAccessibilityPathAttribute@"AXPath"]; |
| 584 | |
| 585 | if (backingObject->supportsExpandedTextValue()) |
| 586 | [additional addObject:NSAccessibilityExpandedTextValueAttribute@"AXExpandedTextValue"]; |
| 587 | |
| 588 | if (!backingObject->brailleLabel().isEmpty()) |
| 589 | [additional addObject:NSAccessibilityBrailleLabelAttribute@"AXBrailleLabel"]; |
| 590 | |
| 591 | if (!backingObject->brailleRoleDescription().isEmpty()) |
| 592 | [additional addObject:NSAccessibilityBrailleRoleDescriptionAttribute@"AXBrailleRoleDescription"]; |
| 593 | |
| 594 | if (backingObject->detailedByObjects().size()) |
| 595 | [additional addObject:NSAccessibilityDetailsElementsAttribute@"AXDetailsElements"]; |
| 596 | |
| 597 | if (backingObject->errorMessageObjects().size()) |
| 598 | [additional addObject:NSAccessibilityErrorMessageElementsAttribute@"AXErrorMessageElements"]; |
| 599 | |
| 600 | if (!backingObject->keyShortcuts().isEmpty()) |
| 601 | [additional addObject:NSAccessibilityKeyShortcutsAttribute@"AXKeyShortcutsValue"]; |
| 602 | |
| 603 | if (backingObject->titleUIElement()) |
| 604 | [additional addObject:NSAccessibilityTitleUIElementAttribute]; |
| 605 | |
| 606 | if (backingObject->isColumnHeader() || backingObject->isRowHeader()) |
| 607 | [additional addObject:NSAccessibilitySortDirectionAttribute]; |
| 608 | |
| 609 | // Only expose AXARIACurrent attribute when the element is set to be current item. |
| 610 | if (backingObject->currentState() != AccessibilityCurrentState::False) |
| 611 | [additional addObject:NSAccessibilityARIACurrentAttribute@"AXARIACurrent"]; |
| 612 | |
| 613 | if (backingObject->isTreeItem() && backingObject->supportsCheckedState()) { |
| 614 | // Tree items normally do not support value, but should if they are checkable. |
| 615 | [additional addObject:NSAccessibilityValueAttribute]; |
| 616 | } |
| 617 | |
| 618 | return additional; |
| 619 | } |
| 620 | |
| 621 | ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-implementations" |
| 622 | - (NSArray *)accessibilityAttributeNames |
| 623 | { |
| 624 | AXTRACE("WebAccessibilityObjectWrapper accessibilityAttributeNames"_s)(void)0; |
| 625 | |
| 626 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 627 | if (!backingObject) |
| 628 | return nilnullptr; |
| 629 | |
| 630 | // TODO: we definitely don't want to return accessibilityAttributeNames for AccessibilityScrollViews, |
| 631 | // only for external attachments like PDFs. Figure out some more specific logic to do this only when needed. |
| 632 | #if !ENABLE_ACCESSIBILITY_LOCAL_FRAME0 |
| 633 | if (backingObject->isAttachment()) |
| 634 | return [[self attachmentView] accessibilityAttributeNames]; |
| 635 | #endif // !ENABLE_ACCESSIBILITY_LOCAL_FRAME |
| 636 | |
| 637 | static NeverDestroyed<RetainPtr<NSArray>> attributes = @[ |
| 638 | NSAccessibilityRoleAttribute, |
| 639 | NSAccessibilitySubroleAttribute, |
| 640 | NSAccessibilityRoleDescriptionAttribute, |
| 641 | NSAccessibilityChildrenAttribute, |
| 642 | NSAccessibilityChildrenInNavigationOrderAttribute@"AXChildrenInNavigationOrder", |
| 643 | NSAccessibilityHelpAttribute, |
| 644 | NSAccessibilityParentAttribute, |
| 645 | NSAccessibilityPositionAttribute, |
| 646 | NSAccessibilitySizeAttribute, |
| 647 | NSAccessibilityTitleAttribute, |
| 648 | NSAccessibilityDescriptionAttribute, |
| 649 | NSAccessibilityValueAttribute, |
| 650 | NSAccessibilityFocusedAttribute, |
| 651 | NSAccessibilityEnabledAttribute, |
| 652 | NSAccessibilityWindowAttribute, |
| 653 | NSAccessibilitySelectedTextMarkerRangeAttribute@"AXSelectedTextMarkerRange", |
| 654 | NSAccessibilityStartTextMarkerAttribute@"AXStartTextMarker", |
| 655 | NSAccessibilityEndTextMarkerAttribute@"AXEndTextMarker", |
| 656 | NSAccessibilityVisitedAttribute@"AXVisited", |
| 657 | NSAccessibilityLinkedUIElementsAttribute, |
| 658 | NSAccessibilitySelectedAttribute, |
| 659 | NSAccessibilityBlockQuoteLevelAttribute@"AXBlockQuoteLevel", |
| 660 | NSAccessibilityTopLevelUIElementAttribute, |
| 661 | NSAccessibilityLanguageAttribute@"AXLanguage", |
| 662 | NSAccessibilityDOMIdentifierAttribute@"AXDOMIdentifier", |
| 663 | NSAccessibilityDOMClassListAttribute@"AXDOMClassList", |
| 664 | NSAccessibilityFocusableAncestorAttribute@"AXFocusableAncestor", |
| 665 | NSAccessibilityEditableAncestorAttribute@"AXEditableAncestor", |
| 666 | NSAccessibilityHighestEditableAncestorAttribute@"AXHighestEditableAncestor", |
| 667 | NSAccessibilityTextInputMarkedRangeAttribute@"AXTextInputMarkedRange", |
| 668 | NSAccessibilityTextInputMarkedTextMarkerRangeAttribute@"AXTextInputMarkedTextMarkerRange", |
| 669 | NSAccessibilityVisibleCharacterRangeAttribute, |
| 670 | NSAccessibilityRelativeFrameAttribute@"AXRelativeFrame", |
| 671 | // AppKit needs to know the screen height in order to do the coordinate conversion. |
| 672 | NSAccessibilityPrimaryScreenHeightAttribute@"_AXPrimaryScreenHeight", |
| 673 | // All objects should expose the ARIA busy attribute (ARIA 1.1 with ISSUE-538). |
| 674 | NSAccessibilityElementBusyAttribute@"AXElementBusy", |
| 675 | // This exists to tell assistive technologies that WebKit stitches adjacent text |
| 676 | // elements together in the accessibility tree — thus, assistive technologies know |
| 677 | // they don't need to. |
| 678 | @"AXPerformsOwnTextStitching", |
| 679 | // This indicates to assistive technologies whether WebKit will handle posting |
| 680 | // announcements for live region updates, or whether the AT is responsible. |
| 681 | @"AXPostsOwnLiveRegionAnnouncements" |
| 682 | ]; |
| 683 | static NeverDestroyed spinButtonCommonAttributes = [] { |
| 684 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 685 | [tempArray addObject:NSAccessibilityValueDescriptionAttribute]; |
| 686 | [tempArray addObject:NSAccessibilityMinValueAttribute]; |
| 687 | [tempArray addObject:NSAccessibilityMaxValueAttribute]; |
| 688 | return tempArray; |
| 689 | }(); |
| 690 | static NeverDestroyed compositeSpinButtonAttributes = [] { |
| 691 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:spinButtonCommonAttributes.get().get()]); |
| 692 | [tempArray addObject:NSAccessibilityIncrementButtonAttribute]; |
| 693 | [tempArray addObject:NSAccessibilityDecrementButtonAttribute]; |
| 694 | return tempArray; |
| 695 | }(); |
| 696 | static NeverDestroyed anchorAttrs = [] { |
| 697 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 698 | [tempArray addObject:NSAccessibilityURLAttribute]; |
| 699 | [tempArray addObject:NSAccessibilityAccessKeyAttribute@"AXAccessKey"]; |
| 700 | [tempArray addObject:NSAccessibilityLinkRelationshipTypeAttribute@"AXLinkRelationshipType"]; |
| 701 | return tempArray; |
| 702 | }(); |
| 703 | static NeverDestroyed webAreaAttrs = [] { |
| 704 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 705 | // WebAreas should not expose AXSubrole. |
| 706 | [tempArray removeObject:NSAccessibilitySubroleAttribute]; |
| 707 | // WebAreas should not expose ancestor attributes |
| 708 | [tempArray removeObject:NSAccessibilityFocusableAncestorAttribute@"AXFocusableAncestor"]; |
| 709 | [tempArray removeObject:NSAccessibilityEditableAncestorAttribute@"AXEditableAncestor"]; |
| 710 | [tempArray removeObject:NSAccessibilityHighestEditableAncestorAttribute@"AXHighestEditableAncestor"]; |
| 711 | [tempArray addObject:NSAccessibilityLinkUIElementsAttribute@"AXLinkUIElements"]; |
| 712 | [tempArray addObject:NSAccessibilityLoadedAttribute@"AXLoaded"]; |
| 713 | [tempArray addObject:NSAccessibilityLayoutCountAttribute@"AXLayoutCount"]; |
| 714 | [tempArray addObject:NSAccessibilityLoadingProgressAttribute@"AXLoadingProgress"]; |
| 715 | [tempArray addObject:NSAccessibilityURLAttribute]; |
| 716 | [tempArray addObject:NSAccessibilityCaretBrowsingEnabledAttribute@"AXCaretBrowsingEnabled"]; |
| 717 | [tempArray addObject:NSAccessibilityPreventKeyboardDOMEventDispatchAttribute@"AXPreventKeyboardDOMEventDispatch"]; |
| 718 | return tempArray; |
| 719 | }(); |
| 720 | static NeverDestroyed textAttrs = [] { |
| 721 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 722 | [tempArray addObject:NSAccessibilityNumberOfCharactersAttribute]; |
| 723 | [tempArray addObject:NSAccessibilitySelectedTextAttribute]; |
| 724 | [tempArray addObject:NSAccessibilitySelectedTextRangeAttribute]; |
| 725 | [tempArray addObject:NSAccessibilityInsertionPointLineNumberAttribute]; |
| 726 | [tempArray addObject:NSAccessibilityAccessKeyAttribute@"AXAccessKey"]; |
| 727 | [tempArray addObject:NSAccessibilityRequiredAttribute]; |
| 728 | [tempArray addObject:NSAccessibilityInvalidAttribute@"AXInvalid"]; |
| 729 | [tempArray addObject:NSAccessibilityPlaceholderValueAttribute]; |
| 730 | [tempArray addObject:NSAccessibilityValueAutofillAvailableAttribute@"AXValueAutofillAvailable"]; |
| 731 | return tempArray; |
| 732 | }(); |
| 733 | static NeverDestroyed listAttrs = [] { |
| 734 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 735 | [tempArray addObject:NSAccessibilityActiveElementAttribute@"AXActiveElement"]; |
| 736 | [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; |
| 737 | [tempArray addObject:NSAccessibilityVisibleChildrenAttribute]; |
| 738 | [tempArray addObject:NSAccessibilityOrientationAttribute]; |
| 739 | return tempArray; |
| 740 | }(); |
| 741 | static NeverDestroyed listBoxAttrs = [] { |
| 742 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:listAttrs.get().get()]); |
| 743 | [tempArray addObject:NSAccessibilityAccessKeyAttribute@"AXAccessKey"]; |
| 744 | [tempArray addObject:NSAccessibilityRequiredAttribute]; |
| 745 | [tempArray addObject:NSAccessibilityInvalidAttribute@"AXInvalid"]; |
| 746 | [tempArray addObject:NSAccessibilityOrientationAttribute]; |
| 747 | return tempArray; |
| 748 | }(); |
| 749 | static NeverDestroyed rangeAttrs = [] { |
| 750 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 751 | [tempArray addObject:NSAccessibilityMinValueAttribute]; |
| 752 | [tempArray addObject:NSAccessibilityMaxValueAttribute]; |
| 753 | [tempArray addObject:NSAccessibilityOrientationAttribute]; |
| 754 | [tempArray addObject:NSAccessibilityValueDescriptionAttribute]; |
| 755 | return tempArray; |
| 756 | }(); |
| 757 | static NeverDestroyed menuBarAttrs = [] { |
| 758 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 759 | [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; |
| 760 | [tempArray addObject:NSAccessibilityOrientationAttribute]; |
| 761 | return tempArray; |
| 762 | }(); |
| 763 | static NeverDestroyed menuAttrs = [] { |
| 764 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 765 | [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; |
| 766 | [tempArray addObject:NSAccessibilityOrientationAttribute]; |
| 767 | return tempArray; |
| 768 | }(); |
| 769 | static NeverDestroyed menuItemAttrs = [] { |
| 770 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 771 | [tempArray addObject:NSAccessibilityTitleAttribute]; |
| 772 | [tempArray addObject:NSAccessibilityDescriptionAttribute]; |
| 773 | [tempArray addObject:NSAccessibilityHelpAttribute]; |
| 774 | [tempArray addObject:NSAccessibilitySelectedAttribute]; |
| 775 | [tempArray addObject:(NSString*)kAXMenuItemCmdCharAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXMenuItemCmdChar" ""))]; |
| 776 | [tempArray addObject:(NSString*)kAXMenuItemCmdVirtualKeyAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXMenuItemCmdVirtualKey" ""))]; |
| 777 | [tempArray addObject:(NSString*)kAXMenuItemCmdGlyphAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXMenuItemCmdGlyph" ""))]; |
| 778 | [tempArray addObject:(NSString*)kAXMenuItemCmdModifiersAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXMenuItemCmdModifiers" ""))]; |
| 779 | [tempArray addObject:(NSString*)kAXMenuItemMarkCharAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXMenuItemMarkChar" ""))]; |
| 780 | [tempArray addObject:(NSString*)kAXMenuItemPrimaryUIElementAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXMenuItemPrimaryUIElement" ""))]; |
| 781 | [tempArray addObject:NSAccessibilityServesAsTitleForUIElementsAttribute]; |
| 782 | [tempArray addObject:NSAccessibilityFocusedAttribute]; |
| 783 | return tempArray; |
| 784 | }(); |
| 785 | static NeverDestroyed<RetainPtr<NSArray>> sharedControlAttrs = @[ |
| 786 | NSAccessibilityAccessKeyAttribute@"AXAccessKey", |
| 787 | NSAccessibilityRequiredAttribute, |
| 788 | NSAccessibilityInvalidAttribute@"AXInvalid", |
| 789 | ]; |
| 790 | static NeverDestroyed<RetainPtr<NSArray>> sharedComboBoxAttrs = @[ |
| 791 | NSAccessibilitySelectedChildrenAttribute, |
| 792 | NSAccessibilityExpandedAttribute, |
| 793 | NSAccessibilityOrientationAttribute, |
| 794 | ]; |
| 795 | static NeverDestroyed controlAttrs = [] { |
| 796 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 797 | [tempArray addObjectsFromArray:sharedControlAttrs.get().get()]; |
| 798 | return tempArray; |
| 799 | }(); |
| 800 | static NeverDestroyed buttonAttrs = [] { |
| 801 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 802 | // Buttons should not expose AXValue. |
| 803 | [tempArray removeObject:NSAccessibilityValueAttribute]; |
| 804 | [tempArray addObject:NSAccessibilityAccessKeyAttribute@"AXAccessKey"]; |
| 805 | [tempArray addObject:NSAccessibilityInvalidAttribute@"AXInvalid"]; |
| 806 | return tempArray; |
| 807 | }(); |
| 808 | static NeverDestroyed popupOrToggleButtonAttrs = [] { |
| 809 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:buttonAttrs.get().get()]); |
| 810 | // Popup and toggle buttons do expose AXValue. |
| 811 | [tempArray addObject:NSAccessibilityValueAttribute]; |
| 812 | return tempArray; |
| 813 | }(); |
| 814 | static NeverDestroyed comboBoxAttrs = [] { |
| 815 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:controlAttrs.get().get()]); |
| 816 | [tempArray addObjectsFromArray:sharedComboBoxAttrs.get().get()]; |
| 817 | return tempArray; |
| 818 | }(); |
| 819 | static NeverDestroyed textComboBoxAttrs = [] { |
| 820 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:textAttrs.get().get()]); |
| 821 | [tempArray addObjectsFromArray:sharedControlAttrs.get().get()]; |
| 822 | [tempArray addObjectsFromArray:sharedComboBoxAttrs.get().get()]; |
| 823 | return tempArray; |
| 824 | }(); |
| 825 | static NeverDestroyed tableAttrs = [] { |
| 826 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 827 | [tempArray addObject:NSAccessibilityRowsAttribute]; |
| 828 | [tempArray addObject:NSAccessibilityVisibleRowsAttribute]; |
| 829 | [tempArray addObject:NSAccessibilityColumnsAttribute]; |
| 830 | [tempArray addObject:NSAccessibilityVisibleColumnsAttribute]; |
| 831 | [tempArray addObject:NSAccessibilityVisibleCellsAttribute]; |
| 832 | [tempArray addObject:NSAccessibilityColumnHeaderUIElementsAttribute]; |
| 833 | [tempArray addObject:NSAccessibilityRowHeaderUIElementsAttribute]; |
| 834 | [tempArray addObject:NSAccessibilityHeaderAttribute]; |
| 835 | [tempArray addObject:NSAccessibilityColumnCountAttribute]; |
| 836 | [tempArray addObject:NSAccessibilityRowCountAttribute]; |
| 837 | [tempArray addObject:NSAccessibilityARIAColumnCountAttribute@"AXARIAColumnCount"]; |
| 838 | [tempArray addObject:NSAccessibilityARIARowCountAttribute@"AXARIARowCount"]; |
| 839 | [tempArray addObject:NSAccessibilitySelectedCellsAttribute]; |
| 840 | [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; |
| 841 | [tempArray addObject:NSAccessibilityTableLevelAttribute@"AXTableLevel"]; |
| 842 | return tempArray; |
| 843 | }(); |
| 844 | static NeverDestroyed tableRowAttrs = [] { |
| 845 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 846 | [tempArray addObject:NSAccessibilityIndexAttribute]; |
| 847 | // FIXME: Consider exposing NSAccessibilityARIARowIndexAttribute and NSAccessibilityRowIndexDescriptionAttribute, |
| 848 | // i.e. aria-rowindex and aria-rowindextext, just like already done for cells |
| 849 | // https://bugs.webkit.org/show_bug.cgi?id=296857 |
| 850 | return tempArray; |
| 851 | }(); |
| 852 | static NeverDestroyed tableColAttrs = [] { |
| 853 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 854 | [tempArray addObject:NSAccessibilityIndexAttribute]; |
| 855 | [tempArray addObject:NSAccessibilityHeaderAttribute]; |
| 856 | [tempArray addObject:NSAccessibilityRowsAttribute]; |
| 857 | [tempArray addObject:NSAccessibilityVisibleRowsAttribute]; |
| 858 | return tempArray; |
| 859 | }(); |
| 860 | static NeverDestroyed tableCellAttrs = [] { |
| 861 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 862 | [tempArray addObject:NSAccessibilityRowIndexRangeAttribute]; |
| 863 | [tempArray addObject:NSAccessibilityColumnIndexRangeAttribute]; |
| 864 | [tempArray addObject:NSAccessibilityColumnHeaderUIElementsAttribute]; |
| 865 | [tempArray addObject:NSAccessibilityRowHeaderUIElementsAttribute]; |
| 866 | [tempArray addObject:NSAccessibilityARIAColumnIndexAttribute@"AXARIAColumnIndex"]; |
| 867 | [tempArray addObject:NSAccessibilityARIARowIndexAttribute@"AXARIARowIndex"]; |
| 868 | [tempArray addObject:NSAccessibilityColumnIndexDescriptionAttribute@"AXColumnIndexDescription"]; |
| 869 | [tempArray addObject:NSAccessibilityRowIndexDescriptionAttribute@"AXRowIndexDescription"]; |
| 870 | return tempArray; |
| 871 | }(); |
| 872 | static NeverDestroyed groupAttrs = [] { |
| 873 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 874 | [tempArray addObject:NSAccessibilityInlineTextAttribute@"AXInlineText"]; |
| 875 | return tempArray; |
| 876 | }(); |
| 877 | static NeverDestroyed inputImageAttrs = [] { |
| 878 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:buttonAttrs.get().get()]); |
| 879 | [tempArray addObject:NSAccessibilityURLAttribute]; |
| 880 | return tempArray; |
| 881 | }(); |
| 882 | static NeverDestroyed secureFieldAttributes = [] { |
| 883 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 884 | [tempArray addObject:NSAccessibilityRequiredAttribute]; |
| 885 | [tempArray addObject:NSAccessibilityInvalidAttribute@"AXInvalid"]; |
| 886 | [tempArray addObject:NSAccessibilityPlaceholderValueAttribute]; |
| 887 | [tempArray addObject:NSAccessibilitySelectedTextRangeAttribute]; |
| 888 | return tempArray; |
| 889 | }(); |
| 890 | static NeverDestroyed tabListAttrs = [] { |
| 891 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 892 | [tempArray addObject:NSAccessibilityTabsAttribute]; |
| 893 | [tempArray addObject:NSAccessibilityContentsAttribute]; |
| 894 | [tempArray addObject:NSAccessibilityOrientationAttribute]; |
| 895 | [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; |
| 896 | return tempArray; |
| 897 | }(); |
| 898 | static NeverDestroyed outlineAttrs = [] { |
| 899 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 900 | [tempArray addObject:NSAccessibilityRequiredAttribute]; |
| 901 | [tempArray addObject:NSAccessibilitySelectedRowsAttribute]; |
| 902 | [tempArray addObject:NSAccessibilityRowsAttribute]; |
| 903 | [tempArray addObject:NSAccessibilityColumnsAttribute]; |
| 904 | [tempArray addObject:NSAccessibilityOrientationAttribute]; |
| 905 | [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; |
| 906 | return tempArray; |
| 907 | }(); |
| 908 | static NeverDestroyed outlineRowAttrs = [] { |
| 909 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:tableRowAttrs.get().get()]); |
| 910 | [tempArray addObject:NSAccessibilityDisclosingAttribute]; |
| 911 | [tempArray addObject:NSAccessibilityDisclosedByRowAttribute]; |
| 912 | [tempArray addObject:NSAccessibilityDisclosureLevelAttribute]; |
| 913 | [tempArray addObject:NSAccessibilityDisclosedRowsAttribute]; |
| 914 | [tempArray removeObject:NSAccessibilityValueAttribute]; |
| 915 | return tempArray; |
| 916 | }(); |
| 917 | static NeverDestroyed scrollViewAttrs = [] { |
| 918 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 919 | [tempArray addObject:NSAccessibilityContentsAttribute]; |
| 920 | [tempArray addObject:NSAccessibilityHorizontalScrollBarAttribute]; |
| 921 | [tempArray addObject:NSAccessibilityVerticalScrollBarAttribute]; |
| 922 | return tempArray; |
| 923 | }(); |
| 924 | static NeverDestroyed imageAttrs = [] { |
| 925 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 926 | [tempArray addObject:NSAccessibilityImageOverlayElementsAttribute@"AXImageOverlayElements"]; |
| 927 | [tempArray addObject:NSAccessibilityEmbeddedImageDescriptionAttribute@"AXEmbeddedImageDescription"]; |
| 928 | [tempArray addObject:NSAccessibilityURLAttribute]; |
| 929 | return tempArray; |
| 930 | }(); |
| 931 | static NeverDestroyed videoAttrs = [] { |
| 932 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 933 | // This should represent the URL of the video content, not the poster. |
| 934 | [tempArray addObject:NSAccessibilityURLAttribute]; |
| 935 | return tempArray; |
| 936 | }(); |
| 937 | static NeverDestroyed staticTextAttrs = [] { |
| 938 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:attributes.get().get()]); |
| 939 | [tempArray addObject:NSAccessibilityIntersectionWithSelectionRangeAttribute@"AXIntersectionWithSelectionRange"]; |
| 940 | [tempArray addObject:NSAccessibilityPathAttribute@"AXPath"]; |
| 941 | return tempArray; |
| 942 | }(); |
| 943 | |
| 944 | RetainPtr<NSArray> objectAttributes = nilnullptr; |
| 945 | if (backingObject->isSecureField()) |
| 946 | objectAttributes = secureFieldAttributes.get().get(); |
| 947 | else if (backingObject->isWebArea()) |
| 948 | objectAttributes = webAreaAttrs.get().get(); |
| 949 | else if (backingObject->isStaticText()) |
| 950 | objectAttributes = staticTextAttrs.get().get(); |
| 951 | else if (backingObject->isComboBox() && backingObject->isTextControl()) |
| 952 | objectAttributes = textComboBoxAttrs.get().get(); |
| 953 | else if (backingObject->isComboBox()) |
| 954 | objectAttributes = comboBoxAttrs.get().get(); |
| 955 | else if (backingObject->isTextControl()) |
| 956 | objectAttributes = textAttrs.get().get(); |
| 957 | else if (backingObject->isLink()) |
| 958 | objectAttributes = anchorAttrs.get().get(); |
| 959 | else if (backingObject->isImage()) |
| 960 | objectAttributes = imageAttrs.get().get(); |
| 961 | else if (backingObject->isTree()) |
| 962 | objectAttributes = outlineAttrs.get().get(); |
| 963 | else if (backingObject->isExposableTable()) |
| 964 | objectAttributes = tableAttrs.get().get(); |
| 965 | else if (backingObject->isTableColumn()) |
| 966 | objectAttributes = tableColAttrs.get().get(); |
| 967 | else if (backingObject->isExposedTableCell()) |
| 968 | objectAttributes = tableCellAttrs.get().get(); |
| 969 | else if (backingObject->isExposedTableRow()) { |
| 970 | // An ARIA table row can be collapsed and expanded, so it needs the extra attributes. |
| 971 | if (backingObject->isARIATreeGridRow()) |
| 972 | objectAttributes = outlineRowAttrs.get().get(); |
| 973 | else |
| 974 | objectAttributes = tableRowAttrs.get().get(); |
| 975 | } else if (backingObject->isTreeItem()) |
| 976 | objectAttributes = outlineRowAttrs.get().get(); |
| 977 | else if (backingObject->isListBox()) |
| 978 | objectAttributes = listBoxAttrs.get().get(); |
| 979 | else if (backingObject->isList()) |
| 980 | objectAttributes = listAttrs.get().get(); |
| 981 | else if (backingObject->isProgressIndicator() || backingObject->isSlider() || backingObject->isSplitter()) |
| 982 | objectAttributes = rangeAttrs.get().get(); |
| 983 | // These are processed in order because an input image is a button, and a button is a control. |
| 984 | else if (backingObject->isInputImage()) |
| 985 | objectAttributes = inputImageAttrs.get().get(); |
| 986 | else if (backingObject->isPopUpButton() || backingObject->isToggleButton()) |
| 987 | objectAttributes = popupOrToggleButtonAttrs.get().get(); |
| 988 | else if (backingObject->isButton()) |
| 989 | objectAttributes = buttonAttrs.get().get(); |
| 990 | else if (backingObject->isControl()) |
| 991 | objectAttributes = controlAttrs.get().get(); |
| 992 | |
| 993 | else if (backingObject->isGroup() || backingObject->isListItem() || backingObject->role() == AccessibilityRole::Figure) |
| 994 | objectAttributes = groupAttrs.get().get(); |
| 995 | else if (backingObject->isTabList()) |
| 996 | objectAttributes = tabListAttrs.get().get(); |
| 997 | else if (backingObject->isScrollArea()) |
| 998 | objectAttributes = scrollViewAttrs.get().get(); |
| 999 | else if (backingObject->isSpinButton()) { |
| 1000 | if (backingObject->spinButtonType() == SpinButtonType::Composite) |
| 1001 | objectAttributes = compositeSpinButtonAttributes.get().get(); |
| 1002 | else |
| 1003 | objectAttributes = spinButtonCommonAttributes.get().get(); |
| 1004 | } else if (backingObject->isMenu()) |
| 1005 | objectAttributes = menuAttrs.get().get(); |
| 1006 | else if (backingObject->isMenuBar()) |
| 1007 | objectAttributes = menuBarAttrs.get().get(); |
| 1008 | else if (backingObject->isMenuItem()) |
| 1009 | objectAttributes = menuItemAttrs.get().get(); |
| 1010 | else if (backingObject->isVideo()) |
| 1011 | objectAttributes = videoAttrs.get().get(); |
| 1012 | |
| 1013 | if (!objectAttributes) |
| 1014 | objectAttributes = attributes.get().get(); |
| 1015 | |
| 1016 | // Adding these additional attributes is separated so that we only call arrayByAddingObjectsFromArray |
| 1017 | // (which does a copy) if we have any uncommon attributes to add. |
| 1018 | RetainPtr<NSArray> additionalAttributes = [self _additionalAccessibilityAttributeNames:backingObject]; |
| 1019 | if ([additionalAttributes count]) |
| 1020 | objectAttributes = [objectAttributes arrayByAddingObjectsFromArray:additionalAttributes.get()]; |
| 1021 | |
| 1022 | return objectAttributes.unsafeGet(); |
| 1023 | } |
| 1024 | ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop |
| 1025 | |
| 1026 | - (id)remoteAccessibilityParentObject |
| 1027 | { |
| 1028 | RefPtr<AXCoreObject> backingObject = self.axBackingObject; |
| 1029 | return backingObject ? backingObject->remoteParent().unsafeGet() : nilnullptr; |
| 1030 | } |
| 1031 | |
| 1032 | static void convertToVector(NSArray* array, AccessibilityObject::AccessibilityChildrenVector& vector) |
| 1033 | { |
| 1034 | unsigned length = [array count]; |
| 1035 | vector.reserveInitialCapacity(length); |
| 1036 | for (unsigned i = 0; i < length; ++i) { |
| 1037 | if (RefPtr object = [[array objectAtIndex:i] axBackingObject]) |
| 1038 | vector.append(object.releaseNonNull()); |
| 1039 | } |
| 1040 | } |
| 1041 | |
| 1042 | - (AXTextMarkerRangeRef)selectedTextMarkerRange |
| 1043 | { |
| 1044 | RefPtr<AXCoreObject> backingObject = self.axBackingObject; |
| 1045 | if (!backingObject) |
| 1046 | return nilnullptr; |
| 1047 | |
| 1048 | auto range = backingObject->selectedTextMarkerRange(); |
| 1049 | if (!range.start().isValid() || !range.end().isValid()) |
| 1050 | return nilnullptr; |
| 1051 | |
| 1052 | return range; |
| 1053 | } |
| 1054 | |
| 1055 | - (id)_associatedPluginParent |
| 1056 | { |
| 1057 | RefPtr<AXCoreObject> backingObject = self.axBackingObject; |
| 1058 | return [self _associatedPluginParentWith:self.axBackingObject]; |
| 1059 | } |
| 1060 | |
| 1061 | - (id)_associatedPluginParentWith:(const RefPtr<AXCoreObject>&)backingObject |
| 1062 | { |
| 1063 | if (!backingObject || !backingObject->hasApplePDFAnnotationAttribute()) |
| 1064 | return nilnullptr; |
| 1065 | |
| 1066 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 1067 | RefPtr<AXCoreObject> backingObject = protectedSelf.get().axBackingObject; |
| 1068 | if (!backingObject || !backingObject->hasApplePDFAnnotationAttribute()) |
| 1069 | return nilnullptr; |
| 1070 | RefPtr document = dynamicDowncast<PluginDocument>(backingObject->document()); |
| 1071 | if (!document) |
| 1072 | return nilnullptr; |
| 1073 | RefPtr widget = document->pluginWidget(); |
| 1074 | if (!widget) |
| 1075 | return nilnullptr; |
| 1076 | RefPtr element = backingObject->element(); |
| 1077 | return widget->accessibilityAssociatedPluginParentForElement(element.get()); |
| 1078 | }); |
| 1079 | } |
| 1080 | |
| 1081 | static void WebTransformCGPathToNSBezierPath(void* info, const CGPathElement *element) |
| 1082 | { |
| 1083 | auto points = pointsSpan(element); |
| 1084 | NSBezierPath *bezierPath = (__bridge NSBezierPath *)info; |
| 1085 | switch (element->type) { |
| 1086 | case kCGPathElementMoveToPoint: |
| 1087 | [bezierPath moveToPoint:NSPointFromCGPoint(points[0])]; |
| 1088 | break; |
| 1089 | case kCGPathElementAddLineToPoint: |
| 1090 | [bezierPath lineToPoint:NSPointFromCGPoint(points[0])]; |
| 1091 | break; |
| 1092 | case kCGPathElementAddCurveToPoint: { |
| 1093 | [bezierPath curveToPoint:NSPointFromCGPoint(points[0]) controlPoint1:NSPointFromCGPoint(points[1]) controlPoint2:NSPointFromCGPoint(points[2])]; |
| 1094 | break; |
| 1095 | } |
| 1096 | case kCGPathElementCloseSubpath: |
| 1097 | [bezierPath closePath]; |
| 1098 | break; |
| 1099 | default: |
| 1100 | break; |
| 1101 | } |
| 1102 | } |
| 1103 | |
| 1104 | - (NSBezierPath *)bezierPathFromPath:(CGPathRef)path |
| 1105 | { |
| 1106 | NSBezierPath *bezierPath = [NSBezierPath bezierPath]; |
| 1107 | CGPathApply(path, (__bridge void*)bezierPath, WebTransformCGPathToNSBezierPath); |
| 1108 | return bezierPath; |
| 1109 | } |
| 1110 | |
| 1111 | - (NSBezierPath *)path |
| 1112 | { |
| 1113 | RefPtr<AXCoreObject> backingObject = self.axBackingObject; |
| 1114 | if (!backingObject) |
| 1115 | return NULL__null; |
| 1116 | |
| 1117 | Path path = backingObject->elementPath(); |
| 1118 | if (path.isEmpty()) |
| 1119 | return NULL__null; |
| 1120 | |
| 1121 | RetainPtr<CGPathRef> transformedPath = [self convertPathToScreenSpace:path]; |
| 1122 | return [self bezierPathFromPath:transformedPath.get()]; |
| 1123 | } |
| 1124 | |
| 1125 | // `unignoredChildren` must be the children of `backingObject`. |
| 1126 | static NSArray *transformSpecialChildrenCases(AXCoreObject& backingObject, const Vector<Ref<AXCoreObject>>& unignoredChildren) |
| 1127 | { |
| 1128 | #if ENABLE(MODEL_ELEMENT_ACCESSIBILITY)(defined 1 && 1) |
| 1129 | if (backingObject.isModel()) { |
| 1130 | auto modelChildren = backingObject.modelElementChildren(); |
| 1131 | if (modelChildren.children.size()) { |
| 1132 | return createNSArray(WTF::move(modelChildren.children), [](auto&& child) -> id { |
| 1133 | return child.get(); |
| 1134 | }).autorelease(); |
| 1135 | } |
| 1136 | } |
| 1137 | #endif |
| 1138 | |
| 1139 | if (!unignoredChildren.size()) { |
| 1140 | if (RetainPtr widgetChildren = renderWidgetChildren(backingObject)) |
| 1141 | return widgetChildren.unsafeGet(); |
| 1142 | } |
| 1143 | return nilnullptr; |
| 1144 | } |
| 1145 | |
| 1146 | static NSArray *children(AXCoreObject& backingObject) |
| 1147 | { |
| 1148 | const auto& unignoredChildren = backingObject.crossFrameUnignoredChildren(); |
| 1149 | RetainPtr<NSArray> specialChildren = transformSpecialChildrenCases(backingObject, unignoredChildren); |
| 1150 | if ([specialChildren count]) |
| 1151 | return specialChildren.unsafeGet(); |
| 1152 | |
| 1153 | // The tree's (AXOutline) children are supposed to be its rows and columns. |
| 1154 | // The ARIA spec doesn't have columns, so we just need rows. |
| 1155 | if (backingObject.isTree()) |
| 1156 | return makeNSArray(backingObject.ariaTreeRows()); |
| 1157 | |
| 1158 | // A tree item should only expose its content as its children (not its rows) |
| 1159 | if (backingObject.isTreeItem()) |
| 1160 | return makeNSArray(backingObject.ariaTreeItemContent()); |
| 1161 | |
| 1162 | return makeNSArray(unignoredChildren); |
| 1163 | } |
| 1164 | |
| 1165 | static RetainPtr<NSString> roleString(AXCoreObject& backingObject) |
| 1166 | { |
| 1167 | String roleString = backingObject.rolePlatformString(); |
| 1168 | if (!roleString.isEmpty()) |
| 1169 | return roleString.createNSString(); |
| 1170 | return NSAccessibilityUnknownRole; |
| 1171 | } |
| 1172 | |
| 1173 | static RetainPtr<NSString> subroleString(AXCoreObject& backingObject) |
| 1174 | { |
| 1175 | if (backingObject.isEmptyGroup()) |
| 1176 | return NSAccessibilityEmptyGroupSubrole@"AXEmptyGroup"; |
| 1177 | |
| 1178 | String subrole = backingObject.subrolePlatformString(); |
| 1179 | if (!subrole.isEmpty()) |
| 1180 | return subrole.createNSString(); |
| 1181 | return nilnullptr; |
| 1182 | } |
| 1183 | |
| 1184 | static RetainPtr<NSString> roleDescription(AXCoreObject& backingObject) |
| 1185 | { |
| 1186 | String roleDescription = backingObject.roleDescription(); |
| 1187 | if (!roleDescription.isEmpty()) |
| 1188 | return roleDescription.createNSString(); |
| 1189 | |
| 1190 | RetainPtr axRole = roleString(backingObject); |
| 1191 | RetainPtr subrole = subroleString(backingObject); |
| 1192 | // Fallback to the system role description. |
| 1193 | // If we get the same string back, then as a last resort, return unknown. |
| 1194 | RetainPtr<NSString> systemRoleDescription = NSAccessibilityRoleDescription(axRole.get(), subrole.get()); |
| 1195 | if (![systemRoleDescription isEqualToString:axRole.get()]) |
| 1196 | return systemRoleDescription; |
| 1197 | return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nilnullptr); |
| 1198 | } |
| 1199 | |
| 1200 | static id scrollViewParent(AXCoreObject& axObject) |
| 1201 | { |
| 1202 | if (!axObject.isScrollArea()) |
| 1203 | return nilnullptr; |
| 1204 | |
| 1205 | #if ENABLE_ACCESSIBILITY_LOCAL_FRAME0 |
| 1206 | if (RefPtr crossFrameParent = axObject.crossFrameParentObject()) |
| 1207 | return crossFrameParent->wrapper(); |
| 1208 | #endif |
| 1209 | |
| 1210 | // If this scroll view provides it's parent object (because it's a sub-frame), then |
| 1211 | // we should not find the remoteAccessibilityParent. |
| 1212 | if (axObject.parentObject()) |
| 1213 | return nilnullptr; |
| 1214 | |
| 1215 | if (RetainPtr platformWidget = axObject.platformWidget()) |
| 1216 | return NSAccessibilityUnignoredAncestor(platformWidget.get()); |
| 1217 | |
| 1218 | return axObject.remoteParent().unsafeGet(); |
| 1219 | } |
| 1220 | |
| 1221 | - (id)windowElement:(NSString *)attributeName |
| 1222 | { |
| 1223 | if (RetainPtr<id> remoteParent = self.remoteAccessibilityParentObject) { |
| 1224 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 1225 | return [remoteParent accessibilityAttributeValue:attributeName]; |
| 1226 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 1227 | } |
| 1228 | |
| 1229 | RefPtr<AXCoreObject> backingObject = self.axBackingObject; |
| 1230 | RefPtr axScrollView = backingObject ? backingObject->axScrollView() : nullptr; |
| 1231 | return axScrollView ? [protect(axScrollView->platformWidget()) window] : nilnullptr; |
| 1232 | } |
| 1233 | |
| 1234 | // FIXME: split up this function in a better way. |
| 1235 | // suggestions: Use a hash table that maps attribute names to function calls, |
| 1236 | // or maybe pointers to member functions |
| 1237 | ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-implementations" |
| 1238 | - (id)accessibilityAttributeValue:(NSString *)attributeName |
| 1239 | { |
| 1240 | AXTRACE(makeString("WebAccessibilityObjectWrapper accessibilityAttributeValue:"_s, String(attributeName)))(void)0; |
| 1241 | |
| 1242 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 1243 | if (!backingObject) { |
| 1244 | AXLOG(makeString("No backingObject for wrapper "_s, hex(reinterpret_cast<uintptr_t>(self))))(void)0; |
| 1245 | return nilnullptr; |
| 1246 | } |
| 1247 | |
| 1248 | if ([attributeName isEqualToString:NSAccessibilityRoleAttribute]) |
| 1249 | return roleString(*backingObject).autorelease(); |
| 1250 | |
| 1251 | if ([attributeName isEqualToString: NSAccessibilitySubroleAttribute]) |
| 1252 | return subroleString(*backingObject).autorelease(); |
| 1253 | |
| 1254 | if ([attributeName isEqualToString:NSAccessibilityRoleDescriptionAttribute]) |
| 1255 | return roleDescription(*backingObject).autorelease(); |
| 1256 | |
| 1257 | if ([attributeName isEqualToString: NSAccessibilityParentAttribute]) { |
| 1258 | // This will return the parent of the AXScrollArea, if this is a AccessibilityScrollView. |
| 1259 | if (RetainPtr scrollView = scrollViewParent(*backingObject)) |
| 1260 | return scrollView.unsafeGet(); |
| 1261 | |
| 1262 | // Tree item (changed to AXRows) can only report the tree (AXOutline) as its parent. |
| 1263 | if (backingObject->isTreeItem()) { |
| 1264 | RefPtr parent = backingObject->parentObjectUnignored(); |
| 1265 | while (parent) { |
| 1266 | if (parent->isTree()) |
| 1267 | return parent->wrapper(); |
| 1268 | parent = parent->parentObjectUnignored(); |
| 1269 | } |
| 1270 | } |
| 1271 | |
| 1272 | RefPtr parent = backingObject->parentObjectUnignored(); |
| 1273 | if (!parent) |
| 1274 | return nilnullptr; |
| 1275 | |
| 1276 | #if !ENABLE_ACCESSIBILITY_LOCAL_FRAME0 |
| 1277 | // In WebKit1, the scroll view is provided by the system (the attachment view), so the parent |
| 1278 | // should be reported directly as such. |
| 1279 | if (backingObject->isWebArea() && parent->isAttachment()) |
| 1280 | return [protect(parent->wrapper()) attachmentView]; |
| 1281 | #endif // !ENABLE_ACCESSIBILITY_LOCAL_FRAME |
| 1282 | |
| 1283 | return parent->wrapper(); |
| 1284 | } |
| 1285 | |
| 1286 | if ([attributeName isEqualToString:NSAccessibilityChildrenAttribute] || [attributeName isEqualToString:NSAccessibilityChildrenInNavigationOrderAttribute@"AXChildrenInNavigationOrder"]) |
| 1287 | return children(*backingObject); |
| 1288 | |
| 1289 | if ([attributeName isEqualToString:NSAccessibilitySelectedChildrenAttribute]) { |
| 1290 | auto selectedChildren = backingObject->selectedChildren(); |
| 1291 | return selectedChildren.size() ? makeNSArray(WTF::move(selectedChildren)) : nilnullptr; |
| 1292 | } |
| 1293 | |
| 1294 | if ([attributeName isEqualToString:NSAccessibilityActiveElementAttribute@"AXActiveElement"]) { |
| 1295 | RefPtr activeDescendant = backingObject->activeDescendant(); |
| 1296 | return activeDescendant ? activeDescendant->wrapper() : nilnullptr; |
| 1297 | } |
| 1298 | |
| 1299 | if ([attributeName isEqualToString:NSAccessibilityVisibleChildrenAttribute]) { |
| 1300 | if (backingObject->isListBox()) |
| 1301 | return makeNSArray(backingObject->visibleChildren()); |
| 1302 | if (backingObject->isList()) |
| 1303 | return children(*backingObject); |
| 1304 | return nilnullptr; |
| 1305 | } |
| 1306 | |
| 1307 | if (backingObject->isWebArea()) { |
| 1308 | if ([attributeName isEqualToString:NSAccessibilityLinkUIElementsAttribute@"AXLinkUIElements"]) |
| 1309 | return makeNSArray(backingObject->documentLinks()); |
| 1310 | |
| 1311 | if ([attributeName isEqualToString:NSAccessibilityLoadedAttribute@"AXLoaded"]) |
| 1312 | return [NSNumber numberWithBool:backingObject->isLoaded()]; |
| 1313 | if ([attributeName isEqualToString:NSAccessibilityLayoutCountAttribute@"AXLayoutCount"]) |
| 1314 | return @(backingObject->layoutCount()); |
| 1315 | if ([attributeName isEqualToString:NSAccessibilityLoadingProgressAttribute@"AXLoadingProgress"]) |
| 1316 | return @(backingObject->loadingProgress()); |
| 1317 | if ([attributeName isEqualToString:NSAccessibilityPreventKeyboardDOMEventDispatchAttribute@"AXPreventKeyboardDOMEventDispatch"]) |
| 1318 | return [NSNumber numberWithBool:backingObject->preventKeyboardDOMEventDispatch()]; |
| 1319 | if ([attributeName isEqualToString:NSAccessibilityCaretBrowsingEnabledAttribute@"AXCaretBrowsingEnabled"]) |
| 1320 | return [NSNumber numberWithBool:backingObject->caretBrowsingEnabled()]; |
| 1321 | } |
| 1322 | |
| 1323 | if (backingObject->isTextControl()) { |
| 1324 | if ([attributeName isEqualToString:NSAccessibilityNumberOfCharactersAttribute]) |
| 1325 | return @(backingObject->textLength()); |
| 1326 | |
| 1327 | if ([attributeName isEqualToString:NSAccessibilitySelectedTextAttribute]) { |
| 1328 | String selectedText = backingObject->selectedText(); |
| 1329 | if (selectedText.isNull()) |
| 1330 | return nilnullptr; |
| 1331 | return selectedText.createNSString().autorelease(); |
| 1332 | } |
| 1333 | |
| 1334 | if ([attributeName isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) |
| 1335 | return [NSValue valueWithRange:backingObject->selectedTextRange()]; |
| 1336 | |
| 1337 | if ([attributeName isEqualToString:NSAccessibilityInsertionPointLineNumberAttribute]) { |
| 1338 | int lineNumber = backingObject->insertionPointLineNumber(); |
| 1339 | return lineNumber >= 0 ? @(lineNumber) : nilnullptr; |
| 1340 | } |
| 1341 | } |
| 1342 | |
| 1343 | if (backingObject->isStaticText()) { |
| 1344 | if ([attributeName isEqualToString:NSAccessibilityIntersectionWithSelectionRangeAttribute@"AXIntersectionWithSelectionRange"]) |
| 1345 | return [self intersectionWithSelectionRange]; |
| 1346 | } |
| 1347 | |
| 1348 | if ([attributeName isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) { |
| 1349 | if (backingObject->isSecureField()) |
| 1350 | return nilnullptr; |
| 1351 | // FIXME: Get actual visible range. <rdar://problem/4712101> |
| 1352 | if (backingObject->isTextControl()) |
| 1353 | return [NSValue valueWithRange:NSMakeRange(0, backingObject->textLength())]; |
| 1354 | return [NSValue valueWithRange:[self accessibilityVisibleCharacterRange]]; |
| 1355 | } |
| 1356 | |
| 1357 | if ([attributeName isEqualToString: NSAccessibilityURLAttribute]) { |
| 1358 | URL url = backingObject->url(); |
| 1359 | if (url.isNull()) |
| 1360 | return nilnullptr; |
| 1361 | return url.createNSURL().autorelease(); |
| 1362 | } |
| 1363 | |
| 1364 | if ([attributeName isEqualToString:NSAccessibilityIncrementButtonAttribute]) { |
| 1365 | RefPtr incrementButton = backingObject->incrementButton(); |
| 1366 | return incrementButton ? incrementButton->wrapper() : nilnullptr; |
| 1367 | } |
| 1368 | |
| 1369 | if ([attributeName isEqualToString:NSAccessibilityDecrementButtonAttribute]) { |
| 1370 | RefPtr decrementButton = backingObject->decrementButton(); |
| 1371 | return decrementButton ? decrementButton->wrapper() : nilnullptr; |
| 1372 | } |
| 1373 | |
| 1374 | if ([attributeName isEqualToString: NSAccessibilityVisitedAttribute@"AXVisited"]) |
| 1375 | return [NSNumber numberWithBool: backingObject->isVisitedLink()]; |
| 1376 | |
| 1377 | if ([attributeName isEqualToString: NSAccessibilityTitleAttribute]) { |
| 1378 | if (backingObject->isAttachment()) { |
| 1379 | RetainPtr<id> attachmentView = [self attachmentView]; |
| 1380 | if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute]) |
| 1381 | return [attachmentView accessibilityAttributeValue:NSAccessibilityTitleAttribute]; |
| 1382 | } |
| 1383 | |
| 1384 | return backingObject->title().createNSString().autorelease(); |
| 1385 | } |
| 1386 | |
| 1387 | if ([attributeName isEqualToString:NSAccessibilityDescriptionAttribute]) { |
| 1388 | if (backingObject->isAttachment()) { |
| 1389 | id attachmentView = [self attachmentView]; |
| 1390 | if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute]) |
| 1391 | return [attachmentView accessibilityAttributeValue:NSAccessibilityDescriptionAttribute]; |
| 1392 | } |
| 1393 | return backingObject->descriptionAttributeValue().createNSString().autorelease(); |
| 1394 | } |
| 1395 | |
| 1396 | if ([attributeName isEqualToString:NSAccessibilityValueAttribute]) { |
| 1397 | if (backingObject->isAttachment()) { |
| 1398 | id attachmentView = [self attachmentView]; |
| 1399 | if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute]) |
| 1400 | return [attachmentView accessibilityAttributeValue:NSAccessibilityValueAttribute]; |
| 1401 | } |
| 1402 | |
| 1403 | auto value = backingObject->value(); |
| 1404 | return WTF::switchOn(value, |
| 1405 | [] (bool& typedValue) -> id { return @(typedValue); }, |
| 1406 | [] (unsigned& typedValue) -> id { return @(typedValue); }, |
| 1407 | [] (float& typedValue) -> id { return @(typedValue); }, |
| 1408 | [] (String& typedValue) -> id { return typedValue.createNSString().autorelease(); }, |
| 1409 | [&backingObject] (WallTime& typedValue) -> id { |
| 1410 | NSInteger offset = gmtToLocalTimeOffset(backingObject->dateTimeComponentsType()); |
| 1411 | auto time = typedValue.secondsSinceEpoch().value(); |
| 1412 | NSDate *gmtDate = [NSDate dateWithTimeIntervalSince1970:time]; |
| 1413 | return [NSDate dateWithTimeInterval:offset sinceDate:gmtDate]; |
| 1414 | }, |
| 1415 | [] (AccessibilityButtonState& typedValue) -> id { return @((unsigned)typedValue); }, |
| 1416 | [] (AXCoreObject*& typedValue) { return typedValue ? (id)typedValue->wrapper() : nilnullptr; }, |
| 1417 | [] (auto&) { return nilnullptr; } |
| 1418 | ); |
| 1419 | } |
| 1420 | |
| 1421 | if ([attributeName isEqualToString:NSAccessibilityDateTimeComponentsAttribute@"AXDateTimeComponents"]) |
| 1422 | return @(convertToAXFDateTimeComponents(backingObject->dateTimeComponentsType())); |
| 1423 | |
| 1424 | if ([attributeName isEqualToString:bridge_cast(kAXMenuItemMarkCharAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXMenuItemMarkChar" "")))]) { |
| 1425 | const unichar ch = 0x2713; // ✓ used on Mac for selected menu items. |
| 1426 | return (backingObject->isChecked()) ? [NSString stringWithCharacters:&ch length:1] : nilnullptr; |
| 1427 | } |
| 1428 | |
| 1429 | if ([attributeName isEqualToString: NSAccessibilityMinValueAttribute]) { |
| 1430 | // Indeterminate progress indicator should return 0. |
| 1431 | if (backingObject->isIndeterminate()) |
| 1432 | return @0; |
| 1433 | return [NSNumber numberWithFloat:backingObject->minValueForRange()]; |
| 1434 | } |
| 1435 | |
| 1436 | if ([attributeName isEqualToString: NSAccessibilityMaxValueAttribute]) { |
| 1437 | // Indeterminate progress indicator should return 0. |
| 1438 | if (backingObject->isIndeterminate()) |
| 1439 | return @0; |
| 1440 | return [NSNumber numberWithFloat:backingObject->maxValueForRange()]; |
| 1441 | } |
| 1442 | |
| 1443 | if ([attributeName isEqualToString: NSAccessibilityHelpAttribute]) |
| 1444 | return [self baseAccessibilityHelpText]; |
| 1445 | |
| 1446 | if ([attributeName isEqualToString:NSAccessibilityFocusedAttribute]) |
| 1447 | return @(backingObject->isFocused()); |
| 1448 | |
| 1449 | if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute]) |
| 1450 | return [NSNumber numberWithBool: backingObject->isEnabled()]; |
| 1451 | |
| 1452 | if ([attributeName isEqualToString:NSAccessibilitySizeAttribute]) { |
| 1453 | auto size = backingObject->size(); |
| 1454 | if (backingObject->isControl()) |
| 1455 | Accessibility::adjustControlSize(size); |
| 1456 | return [NSValue valueWithSize:(CGSize)size]; |
| 1457 | } |
| 1458 | |
| 1459 | if ([attributeName isEqualToString:NSAccessibilityPrimaryScreenHeightAttribute@"_AXPrimaryScreenHeight"]) |
| 1460 | return @(backingObject->primaryScreenRect().height()); |
| 1461 | |
| 1462 | if ([attributeName isEqualToString:NSAccessibilityPositionAttribute]) |
| 1463 | return [NSValue valueWithPoint:(CGPoint)backingObject->screenRelativePosition()]; |
| 1464 | |
| 1465 | if ([attributeName isEqualToString:NSAccessibilityPathAttribute@"AXPath"]) |
| 1466 | return [self path]; |
| 1467 | |
| 1468 | if ([attributeName isEqualToString:NSAccessibilityLineRectsAndTextAttribute@"AXLineRectsAndText"]) { |
| 1469 | return Accessibility::retrieveAutoreleasedValueFromMainThread<NSArray *>([protectedSelf = retainPtr(self)] () -> RetainPtr<NSArray> { |
| 1470 | return protectedSelf.get().lineRectsAndText; |
| 1471 | }); |
| 1472 | } |
| 1473 | |
| 1474 | if ([attributeName isEqualToString:NSAccessibilityImageOverlayElementsAttribute@"AXImageOverlayElements"]) { |
| 1475 | auto imageOverlayElements = backingObject->imageOverlayElements(); |
| 1476 | return imageOverlayElements ? makeNSArray(*imageOverlayElements) : nilnullptr; |
| 1477 | } |
| 1478 | |
| 1479 | if ([attributeName isEqualToString:NSAccessibilityEmbeddedImageDescriptionAttribute@"AXEmbeddedImageDescription"]) |
| 1480 | return backingObject->embeddedImageDescription().createNSString().autorelease(); |
| 1481 | |
| 1482 | if ([attributeName isEqualToString:NSAccessibilityWindowAttribute] |
| 1483 | || [attributeName isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) |
| 1484 | return [self windowElement:attributeName]; |
| 1485 | |
| 1486 | if ([attributeName isEqualToString:NSAccessibilityAccessKeyAttribute@"AXAccessKey"]) { |
| 1487 | auto accessKey = backingObject->accessKey(); |
| 1488 | if (accessKey.isNull()) |
| 1489 | return nilnullptr; |
| 1490 | return accessKey.createNSString().autorelease(); |
| 1491 | } |
| 1492 | |
| 1493 | if ([attributeName isEqualToString:NSAccessibilityLinkRelationshipTypeAttribute@"AXLinkRelationshipType"]) |
| 1494 | return backingObject->linkRelValue().createNSString().autorelease(); |
| 1495 | |
| 1496 | if ([attributeName isEqualToString:NSAccessibilityTabsAttribute] && backingObject->isTabList()) |
| 1497 | return makeNSArray(backingObject->tabChildren()); |
| 1498 | |
| 1499 | if ([attributeName isEqualToString:NSAccessibilityContentsAttribute]) |
| 1500 | return makeNSArray(backingObject->contents()); |
| 1501 | |
| 1502 | if (backingObject->isExposableTable()) { |
| 1503 | if ([attributeName isEqualToString:NSAccessibilityRowsAttribute]) |
| 1504 | return makeNSArray(backingObject->rows()); |
| 1505 | |
| 1506 | if ([attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) |
| 1507 | return makeNSArray(backingObject->visibleRows()); |
| 1508 | |
| 1509 | // TODO: distinguish between visible and non-visible columns |
| 1510 | if ([attributeName isEqualToString:NSAccessibilityColumnsAttribute] |
| 1511 | || [attributeName isEqualToString:NSAccessibilityVisibleColumnsAttribute]) |
| 1512 | return makeNSArray(backingObject->columns()); |
| 1513 | |
| 1514 | if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute]) { |
| 1515 | auto selectedChildren = backingObject->selectedChildren(); |
| 1516 | return selectedChildren.size() ? makeNSArray(WTF::move(selectedChildren)) : nilnullptr; |
| 1517 | } |
| 1518 | |
| 1519 | // HTML tables don't support this attribute yet. |
| 1520 | if ([attributeName isEqualToString:NSAccessibilitySelectedColumnsAttribute]) |
| 1521 | return nilnullptr; |
| 1522 | |
| 1523 | if ([attributeName isEqualToString:NSAccessibilitySelectedCellsAttribute]) |
| 1524 | return makeNSArray(backingObject->selectedCells()); |
| 1525 | |
| 1526 | if ([attributeName isEqualToString:NSAccessibilityColumnHeaderUIElementsAttribute]) |
| 1527 | return makeNSArray(backingObject->columnHeaders()); |
| 1528 | |
| 1529 | if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) { |
| 1530 | RefPtr headerContainer = backingObject->tableHeaderContainer(); |
| 1531 | return headerContainer ? headerContainer->wrapper() : nilnullptr; |
| 1532 | } |
| 1533 | |
| 1534 | if ([attributeName isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) |
| 1535 | return makeNSArray(backingObject->rowHeaders()); |
| 1536 | |
| 1537 | if ([attributeName isEqualToString:NSAccessibilityVisibleCellsAttribute]) |
| 1538 | return makeNSArray(backingObject->cells()); |
| 1539 | |
| 1540 | if ([attributeName isEqualToString:NSAccessibilityColumnCountAttribute]) |
| 1541 | return @(backingObject->columnCount()); |
| 1542 | |
| 1543 | if ([attributeName isEqualToString:NSAccessibilityRowCountAttribute]) |
| 1544 | return @(backingObject->rowCount()); |
| 1545 | |
| 1546 | if ([attributeName isEqualToString:NSAccessibilityARIAColumnCountAttribute@"AXARIAColumnCount"]) |
| 1547 | return @(backingObject->axColumnCount()); |
| 1548 | |
| 1549 | if ([attributeName isEqualToString:NSAccessibilityARIARowCountAttribute@"AXARIARowCount"]) |
| 1550 | return @(backingObject->axRowCount()); |
| 1551 | } |
| 1552 | |
| 1553 | if (backingObject->isTableColumn()) { |
| 1554 | if ([attributeName isEqualToString:NSAccessibilityIndexAttribute]) |
| 1555 | return @(backingObject->columnIndex()); |
| 1556 | |
| 1557 | // rows attribute for a column is the list of all the elements in that column at each row |
| 1558 | if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] |
| 1559 | || [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) |
| 1560 | return makeNSArray(backingObject->unignoredChildren()); |
| 1561 | |
| 1562 | if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) { |
| 1563 | RefPtr header = backingObject->columnHeader(); |
| 1564 | return header ? header->wrapper() : nilnullptr; |
| 1565 | } |
| 1566 | } |
| 1567 | |
| 1568 | if (backingObject->isExposedTableCell()) { |
| 1569 | if ([attributeName isEqualToString:NSAccessibilityRowIndexRangeAttribute]) { |
| 1570 | auto rowRange = backingObject->rowIndexRange(); |
| 1571 | return [NSValue valueWithRange:NSMakeRange(rowRange.first, rowRange.second)]; |
| 1572 | } |
| 1573 | |
| 1574 | if ([attributeName isEqualToString:NSAccessibilityColumnIndexRangeAttribute]) { |
| 1575 | auto columnRange = backingObject->columnIndexRange(); |
| 1576 | return [NSValue valueWithRange:NSMakeRange(columnRange.first, columnRange.second)]; |
| 1577 | } |
| 1578 | |
| 1579 | if ([attributeName isEqualToString:NSAccessibilityColumnHeaderUIElementsAttribute]) |
| 1580 | return makeNSArray(backingObject->columnHeaders()); |
| 1581 | |
| 1582 | if ([attributeName isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) |
| 1583 | return makeNSArray(backingObject->rowHeaders()); |
| 1584 | |
| 1585 | if ([attributeName isEqualToString:NSAccessibilityARIAColumnIndexAttribute@"AXARIAColumnIndex"]) { |
| 1586 | if (std::optional columnIndex = backingObject->axColumnIndex()) |
| 1587 | return @(*columnIndex); |
| 1588 | return @(-1); |
| 1589 | } |
| 1590 | |
| 1591 | if ([attributeName isEqualToString:NSAccessibilityARIARowIndexAttribute@"AXARIARowIndex"]) { |
| 1592 | if (std::optional rowIndex = backingObject->axRowIndex()) |
| 1593 | return @(*rowIndex); |
| 1594 | return @(-1); |
| 1595 | } |
| 1596 | |
| 1597 | if ([attributeName isEqualToString:NSAccessibilityColumnIndexDescriptionAttribute@"AXColumnIndexDescription"]) { |
| 1598 | String columnIndexText = backingObject->axColumnIndexText(); |
| 1599 | return !columnIndexText.isNull() ? columnIndexText.createNSString().autorelease() : nilnullptr; |
| 1600 | } |
| 1601 | |
| 1602 | if ([attributeName isEqualToString:NSAccessibilityRowIndexDescriptionAttribute@"AXRowIndexDescription"]) { |
| 1603 | String rowIndexText = backingObject->axRowIndexText(); |
| 1604 | return !rowIndexText.isNull() ? rowIndexText.createNSString().autorelease() : nilnullptr; |
| 1605 | } |
| 1606 | } |
| 1607 | |
| 1608 | if (backingObject->isTree()) { |
| 1609 | if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute]) { |
| 1610 | auto selectedChildren = backingObject->selectedChildren(); |
| 1611 | return selectedChildren.size() ? makeNSArray(WTF::move(selectedChildren)) : nilnullptr; |
| 1612 | } |
| 1613 | |
| 1614 | if ([attributeName isEqualToString:NSAccessibilityRowsAttribute]) |
| 1615 | return makeNSArray(backingObject->ariaTreeRows()); |
| 1616 | |
| 1617 | // TreeRoles do not support columns, but Mac AX expects to be able to ask about columns at the least. |
| 1618 | if ([attributeName isEqualToString:NSAccessibilityColumnsAttribute]) |
| 1619 | return @[]; |
| 1620 | } |
| 1621 | |
| 1622 | if ([attributeName isEqualToString:NSAccessibilityIndexAttribute]) { |
| 1623 | if (backingObject->isTreeItem()) { |
| 1624 | RefPtr parent = backingObject->parentObject(); |
| 1625 | for (; parent && !parent->isTree(); parent = parent->parentObject()) |
| 1626 | { } |
| 1627 | |
| 1628 | if (!parent) |
| 1629 | return nilnullptr; |
| 1630 | |
| 1631 | // Find the index of this item by iterating the parents. |
| 1632 | auto rowsCopy = parent->ariaTreeRows(); |
| 1633 | size_t count = rowsCopy.size(); |
| 1634 | for (size_t k = 0; k < count; ++k) { |
| 1635 | if (rowsCopy[k]->wrapper() == self) |
| 1636 | return @(k); |
| 1637 | } |
| 1638 | |
| 1639 | return nilnullptr; |
| 1640 | } |
| 1641 | |
| 1642 | if (backingObject->isExposedTableRow()) |
| 1643 | return @(backingObject->rowIndex()); |
| 1644 | } |
| 1645 | |
| 1646 | // The rows that are considered inside this row. |
| 1647 | if ([attributeName isEqualToString:NSAccessibilityDisclosedRowsAttribute]) { |
| 1648 | if (backingObject->isTreeItem() || backingObject->isARIATreeGridRow()) |
| 1649 | return makeNSArray(backingObject->disclosedRows()); |
| 1650 | } |
| 1651 | |
| 1652 | // The row that contains this row. It should be the same as the first parent that is a treeitem. |
| 1653 | if ([attributeName isEqualToString:NSAccessibilityDisclosedByRowAttribute]) { |
| 1654 | if (backingObject->isTreeItem()) { |
| 1655 | RefPtr<AXCoreObject> parent = backingObject->parentObject(); |
| 1656 | while (parent) { |
| 1657 | if (parent->isTreeItem()) |
| 1658 | return parent->wrapper(); |
| 1659 | // If the parent is the tree itself, then this value == nil. |
| 1660 | if (parent->isTree()) |
| 1661 | return nilnullptr; |
| 1662 | parent = parent->parentObject(); |
| 1663 | } |
| 1664 | return nilnullptr; |
| 1665 | } |
| 1666 | |
| 1667 | if (backingObject->isARIATreeGridRow()) { |
| 1668 | RefPtr row = backingObject->disclosedByRow(); |
| 1669 | return row ? row->wrapper() : nilnullptr; |
| 1670 | } |
| 1671 | } |
| 1672 | |
| 1673 | if ([attributeName isEqualToString:NSAccessibilityDisclosureLevelAttribute]) { |
| 1674 | // Convert from 1-based level (from aria-level spec) to 0-based level (Mac) |
| 1675 | int level = backingObject->hierarchicalLevel(); |
| 1676 | if (level > 0) |
| 1677 | level -= 1; |
| 1678 | return @(level); |
| 1679 | } |
| 1680 | if ([attributeName isEqualToString:NSAccessibilityDisclosingAttribute]) |
| 1681 | return [NSNumber numberWithBool:backingObject->isExpanded()]; |
| 1682 | |
| 1683 | if ([attributeName isEqualToString:NSAccessibilitySelectedTextMarkerRangeAttribute@"AXSelectedTextMarkerRange"]) |
| 1684 | return (id)[self selectedTextMarkerRange]; |
| 1685 | |
| 1686 | if ([attributeName isEqualToString:NSAccessibilityStartTextMarkerAttribute@"AXStartTextMarker"]) { |
| 1687 | if (AXObjectCache::useAXThreadTextApis()) { |
| 1688 | if (RefPtr tree = std::get<RefPtr<AXIsolatedTree>>(axTreeForID(backingObject->treeID()))) |
| 1689 | return tree->firstMarker().platformData().bridgingAutorelease(); |
| 1690 | } |
| 1691 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 1692 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 1693 | if (!backingObject) |
| 1694 | return nilnullptr; |
| 1695 | |
| 1696 | CheckedPtr cache = backingObject->axObjectCache(); |
| 1697 | RefPtr document = backingObject->document(); |
| 1698 | return (id)textMarkerForVisiblePosition(cache.get(), startOfDocument(document.get())); |
| 1699 | }); |
| 1700 | } |
| 1701 | |
| 1702 | if ([attributeName isEqualToString:NSAccessibilityEndTextMarkerAttribute@"AXEndTextMarker"]) { |
| 1703 | if (AXObjectCache::useAXThreadTextApis()) { |
| 1704 | if (RefPtr tree = std::get<RefPtr<AXIsolatedTree>>(axTreeForID(backingObject->treeID()))) |
| 1705 | return tree->lastMarker().platformData().bridgingAutorelease(); |
| 1706 | } |
| 1707 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 1708 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 1709 | if (!backingObject) |
| 1710 | return nilnullptr; |
| 1711 | |
| 1712 | CheckedPtr cache = backingObject->axObjectCache(); |
| 1713 | RefPtr document = backingObject->document(); |
| 1714 | return (id)textMarkerForVisiblePosition(cache.get(), endOfDocument(document.get())); |
| 1715 | }); |
| 1716 | } |
| 1717 | |
| 1718 | if ([attributeName isEqualToString:NSAccessibilityBlockQuoteLevelAttribute@"AXBlockQuoteLevel"]) |
| 1719 | return @(backingObject->blockquoteLevel()); |
| 1720 | if ([attributeName isEqualToString:NSAccessibilityTableLevelAttribute@"AXTableLevel"]) |
| 1721 | return @(backingObject->tableLevel()); |
| 1722 | |
| 1723 | if ([attributeName isEqualToString: NSAccessibilityLinkedUIElementsAttribute]) |
| 1724 | return makeNSArray(backingObject->linkedObjects()); |
| 1725 | |
| 1726 | if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) |
| 1727 | return [NSNumber numberWithBool:backingObject->isSelected()]; |
| 1728 | |
| 1729 | if ([attributeName isEqualToString: NSAccessibilityARIACurrentAttribute@"AXARIACurrent"]) |
| 1730 | return backingObject->currentValue().createNSString().autorelease(); |
| 1731 | |
| 1732 | if ([attributeName isEqualToString:NSAccessibilityTitleUIElementAttribute]) { |
| 1733 | // FIXME: change to return an array instead of a single object. |
| 1734 | RefPtr object = backingObject->titleUIElement(); |
| 1735 | return object ? object->wrapper() : nilnullptr; |
| 1736 | } |
| 1737 | |
| 1738 | if ([attributeName isEqualToString:NSAccessibilityValueDescriptionAttribute]) |
| 1739 | return backingObject->valueDescription().createNSString().autorelease(); |
| 1740 | |
| 1741 | if ([attributeName isEqualToString:NSAccessibilityOrientationAttribute]) { |
| 1742 | AccessibilityOrientation elementOrientation = backingObject->orientation(); |
| 1743 | if (elementOrientation == AccessibilityOrientation::Vertical) |
| 1744 | return NSAccessibilityVerticalOrientationValue; |
| 1745 | if (elementOrientation == AccessibilityOrientation::Horizontal) |
| 1746 | return NSAccessibilityHorizontalOrientationValue; |
| 1747 | if (elementOrientation == AccessibilityOrientation::Undefined) |
| 1748 | return NSAccessibilityUnknownOrientationValue; |
| 1749 | return nilnullptr; |
| 1750 | } |
| 1751 | |
| 1752 | if ([attributeName isEqualToString:NSAccessibilityHorizontalScrollBarAttribute]) { |
| 1753 | RefPtr scrollBar = backingObject->scrollBar(AccessibilityOrientation::Horizontal); |
| 1754 | return scrollBar ? scrollBar->wrapper() : nilnullptr; |
| 1755 | } |
| 1756 | |
| 1757 | if ([attributeName isEqualToString:NSAccessibilityVerticalScrollBarAttribute]) { |
| 1758 | RefPtr scrollBar = backingObject->scrollBar(AccessibilityOrientation::Vertical); |
| 1759 | return scrollBar ? scrollBar->wrapper() : nilnullptr; |
| 1760 | } |
| 1761 | |
| 1762 | if ([attributeName isEqualToString:NSAccessibilitySortDirectionAttribute]) { |
| 1763 | switch (backingObject->sortDirectionIncludingAncestors()) { |
| 1764 | case AccessibilitySortDirection::Ascending: |
| 1765 | return NSAccessibilityAscendingSortDirectionValue; |
| 1766 | case AccessibilitySortDirection::Descending: |
| 1767 | return NSAccessibilityDescendingSortDirectionValue; |
| 1768 | default: |
| 1769 | return NSAccessibilityUnknownSortDirectionValue; |
| 1770 | } |
| 1771 | } |
| 1772 | |
| 1773 | if ([attributeName isEqualToString:NSAccessibilityLanguageAttribute@"AXLanguage"]) |
| 1774 | return backingObject->languageIncludingAncestors().createNSString().autorelease(); |
| 1775 | |
| 1776 | if ([attributeName isEqualToString:NSAccessibilityExpandedAttribute]) |
| 1777 | return [NSNumber numberWithBool:backingObject->isExpanded()]; |
| 1778 | |
| 1779 | if ([attributeName isEqualToString:NSAccessibilityRequiredAttribute]) |
| 1780 | return [NSNumber numberWithBool:backingObject->isRequired()]; |
| 1781 | |
| 1782 | if ([attributeName isEqualToString:NSAccessibilityInvalidAttribute@"AXInvalid"]) |
| 1783 | return backingObject->invalidStatus().createNSString().autorelease(); |
| 1784 | |
| 1785 | if ([attributeName isEqualToString:NSAccessibilityOwnsAttribute@"AXOwns"]) |
| 1786 | return makeNSArray(backingObject->ownedObjects()); |
| 1787 | |
| 1788 | if ([attributeName isEqualToString:NSAccessibilityARIAPosInSetAttribute@"AXARIAPosInSet"]) |
| 1789 | return @(backingObject->posInSet()); |
| 1790 | if ([attributeName isEqualToString:NSAccessibilityARIASetSizeAttribute@"AXARIASetSize"]) |
| 1791 | return @(backingObject->setSize()); |
| 1792 | |
| 1793 | if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute@"AXGrabbed"]) |
| 1794 | return [NSNumber numberWithBool:backingObject->isGrabbed()]; |
| 1795 | |
| 1796 | if ([attributeName isEqualToString:NSAccessibilityDropEffectsAttribute@"AXDropEffects"]) |
| 1797 | return createNSArray(backingObject->determineDropEffects()).autorelease(); |
| 1798 | |
| 1799 | if ([attributeName isEqualToString:NSAccessibilityPlaceholderValueAttribute]) |
| 1800 | return backingObject->placeholderValue().createNSString().autorelease(); |
| 1801 | |
| 1802 | if ([attributeName isEqualToString:NSAccessibilityValueAutofillAvailableAttribute@"AXValueAutofillAvailable"]) |
| 1803 | return @(backingObject->isValueAutofillAvailable()); |
| 1804 | |
| 1805 | if ([attributeName isEqualToString:NSAccessibilityValueAutofillTypeAttribute@"AXValueAutofillType"]) { |
| 1806 | switch (backingObject->valueAutofillButtonType()) { |
| 1807 | case AutoFillButtonType::None: |
| 1808 | return @"none"; |
| 1809 | case AutoFillButtonType::Credentials: |
| 1810 | return @"credentials"; |
| 1811 | case AutoFillButtonType::Contacts: |
| 1812 | return @"contacts"; |
| 1813 | case AutoFillButtonType::StrongPassword: |
| 1814 | return @"strong password"; |
| 1815 | case AutoFillButtonType::CreditCard: |
| 1816 | return @"credit card"; |
| 1817 | case AutoFillButtonType::Loading: |
| 1818 | return @"loading"; |
| 1819 | } |
| 1820 | } |
| 1821 | |
| 1822 | if ([attributeName isEqualToString:NSAccessibilityHasPopupAttribute@"AXHasPopup"]) |
| 1823 | return [NSNumber numberWithBool:backingObject->selfOrAncestorLinkHasPopup()]; |
| 1824 | |
| 1825 | if ([attributeName isEqualToString:NSAccessibilityDatetimeValueAttribute@"AXDateTimeValue"]) |
| 1826 | return backingObject->datetimeAttributeValue().createNSString().autorelease(); |
| 1827 | |
| 1828 | if ([attributeName isEqualToString:NSAccessibilityInlineTextAttribute@"AXInlineText"]) { |
| 1829 | // FIXME: After the AccessibilityStitchedText feature ships, we can probably remove this |
| 1830 | // and all related code (making sure not to break backported versions of WebKit which won't |
| 1831 | // have the corresponding VoiceOver changes). |
| 1832 | return @(backingObject->isInlineText()); |
| 1833 | } |
| 1834 | |
| 1835 | // ARIA Live region attributes. |
| 1836 | if ([attributeName isEqualToString:NSAccessibilityARIALiveAttribute@"AXARIALive"]) |
| 1837 | return backingObject->liveRegionStatus().createNSString().autorelease(); |
| 1838 | if ([attributeName isEqualToString:NSAccessibilityARIARelevantAttribute@"AXARIARelevant"]) |
| 1839 | return backingObject->liveRegionRelevant().createNSString().autorelease(); |
| 1840 | if ([attributeName isEqualToString:NSAccessibilityARIAAtomicAttribute@"AXARIAAtomic"]) |
| 1841 | return [NSNumber numberWithBool:backingObject->liveRegionAtomic()]; |
| 1842 | if ([attributeName isEqualToString:NSAccessibilityElementBusyAttribute@"AXElementBusy"]) |
| 1843 | return [NSNumber numberWithBool:backingObject->isBusy()]; |
| 1844 | |
| 1845 | // MathML Attributes. |
| 1846 | if (backingObject->isMathElement()) { |
| 1847 | if ([attributeName isEqualToString:NSAccessibilityMathRootIndexAttribute@"AXMathRootIndex"]) { |
| 1848 | RefPtr rootIndex = backingObject->mathRootIndexObject(); |
| 1849 | return rootIndex ? rootIndex->wrapper() : nilnullptr; |
| 1850 | } |
| 1851 | |
| 1852 | if ([attributeName isEqualToString:NSAccessibilityMathRootRadicandAttribute@"AXMathRootRadicand"]) { |
| 1853 | auto radicand = backingObject->mathRadicand(); |
| 1854 | return radicand ? makeNSArray(*radicand) : nilnullptr; |
| 1855 | } |
| 1856 | |
| 1857 | if ([attributeName isEqualToString:NSAccessibilityMathFractionNumeratorAttribute@"AXMathFractionNumerator"]) |
| 1858 | return (backingObject->mathNumeratorObject()) ? backingObject->mathNumeratorObject()->wrapper() : 0; |
| 1859 | if ([attributeName isEqualToString:NSAccessibilityMathFractionDenominatorAttribute@"AXMathFractionDenominator"]) |
| 1860 | return (backingObject->mathDenominatorObject()) ? backingObject->mathDenominatorObject()->wrapper() : 0; |
| 1861 | if ([attributeName isEqualToString:NSAccessibilityMathBaseAttribute@"AXMathBase"]) |
| 1862 | return (backingObject->mathBaseObject()) ? backingObject->mathBaseObject()->wrapper() : 0; |
| 1863 | if ([attributeName isEqualToString:NSAccessibilityMathSubscriptAttribute@"AXMathSubscript"]) |
| 1864 | return (backingObject->mathSubscriptObject()) ? backingObject->mathSubscriptObject()->wrapper() : 0; |
| 1865 | if ([attributeName isEqualToString:NSAccessibilityMathSuperscriptAttribute@"AXMathSuperscript"]) |
| 1866 | return (backingObject->mathSuperscriptObject()) ? backingObject->mathSuperscriptObject()->wrapper() : 0; |
| 1867 | if ([attributeName isEqualToString:NSAccessibilityMathUnderAttribute@"AXMathUnder"]) |
| 1868 | return (backingObject->mathUnderObject()) ? backingObject->mathUnderObject()->wrapper() : 0; |
| 1869 | if ([attributeName isEqualToString:NSAccessibilityMathOverAttribute@"AXMathOver"]) |
| 1870 | return (backingObject->mathOverObject()) ? backingObject->mathOverObject()->wrapper() : 0; |
| 1871 | if ([attributeName isEqualToString:NSAccessibilityMathFencedOpenAttribute@"AXMathFencedOpen"]) |
| 1872 | return backingObject->mathFencedOpenString().createNSString().autorelease(); |
| 1873 | if ([attributeName isEqualToString:NSAccessibilityMathFencedCloseAttribute@"AXMathFencedClose"]) |
| 1874 | return backingObject->mathFencedCloseString().createNSString().autorelease(); |
| 1875 | if ([attributeName isEqualToString:NSAccessibilityMathLineThicknessAttribute@"AXMathLineThickness"]) |
| 1876 | return [NSNumber numberWithInteger:backingObject->mathLineThickness()]; |
| 1877 | if ([attributeName isEqualToString:NSAccessibilityMathPostscriptsAttribute@"AXMathPostscripts"]) |
| 1878 | return [self accessibilityMathPostscriptPairs]; |
| 1879 | if ([attributeName isEqualToString:NSAccessibilityMathPrescriptsAttribute@"AXMathPrescripts"]) |
| 1880 | return [self accessibilityMathPrescriptPairs]; |
| 1881 | } |
| 1882 | |
| 1883 | if ([attributeName isEqualToString:NSAccessibilityExpandedTextValueAttribute@"AXExpandedTextValue"]) |
| 1884 | return backingObject->expandedTextValue().createNSString().autorelease(); |
| 1885 | |
| 1886 | if ([attributeName isEqualToString:NSAccessibilityDOMIdentifierAttribute@"AXDOMIdentifier"]) |
| 1887 | return backingObject->identifierAttribute().createNSString().autorelease(); |
| 1888 | if ([attributeName isEqualToString:NSAccessibilityDOMClassListAttribute@"AXDOMClassList"]) |
| 1889 | return createNSArray(backingObject->classList()).autorelease(); |
| 1890 | |
| 1891 | // This allows us to connect to a plugin that creates a shadow node for editing (like PDFs). |
| 1892 | if ([attributeName isEqualToString:NSAccessibilityAssociatedPluginParentAttribute@"_AXAssociatedPluginParent"]) |
| 1893 | return [self _associatedPluginParentWith:backingObject]; |
| 1894 | |
| 1895 | // This used to be a testing-only attribute, but unfortunately some ATs do actually request it. |
| 1896 | if ([attributeName isEqualToString:NSAccessibilityDRTSpeechAttributeAttribute@"AXDRTSpeechAttribute"]) |
| 1897 | return [self baseAccessibilitySpeechHint]; |
| 1898 | |
| 1899 | if ([attributeName isEqualToString:NSAccessibilityPopupValueAttribute@"AXPopupValue"]) |
| 1900 | return backingObject->popupValue().createNSString().autorelease(); |
| 1901 | |
| 1902 | if ([attributeName isEqualToString:NSAccessibilityKeyShortcutsAttribute@"AXKeyShortcutsValue"]) |
| 1903 | return backingObject->keyShortcuts().createNSString().autorelease(); |
| 1904 | |
| 1905 | if ([attributeName isEqualToString:NSAccessibilityIsInDescriptionListTermAttribute@"AXIsInDescriptionListTerm"]) |
| 1906 | return [NSNumber numberWithBool:backingObject->isInDescriptionListTerm()]; |
| 1907 | |
| 1908 | if ([attributeName isEqualToString:NSAccessibilityDetailsElementsAttribute@"AXDetailsElements"]) |
| 1909 | return makeNSArray(backingObject->detailedByObjects()); |
| 1910 | |
| 1911 | if ([attributeName isEqualToString:NSAccessibilityBrailleLabelAttribute@"AXBrailleLabel"]) |
| 1912 | return backingObject->brailleLabel().createNSString().autorelease(); |
| 1913 | |
| 1914 | if ([attributeName isEqualToString:NSAccessibilityBrailleRoleDescriptionAttribute@"AXBrailleRoleDescription"]) |
| 1915 | return backingObject->brailleRoleDescription().createNSString().autorelease(); |
| 1916 | |
| 1917 | if ([attributeName isEqualToString:NSAccessibilityRelativeFrameAttribute@"AXRelativeFrame"]) { |
| 1918 | auto frame = backingObject->relativeFrame(); |
| 1919 | if (backingObject->isControl()) |
| 1920 | Accessibility::adjustControlSize(frame); |
| 1921 | return [NSValue valueWithRect:(NSRect)frame]; |
| 1922 | } |
| 1923 | |
| 1924 | if ([attributeName isEqualToString:NSAccessibilityErrorMessageElementsAttribute@"AXErrorMessageElements"]) { |
| 1925 | // Only expose error messages for objects in an invalid state. |
| 1926 | // https://www.w3.org/TR/wai-aria-1.2/#aria-errormessage |
| 1927 | if (backingObject->invalidStatus() == "false"_s) |
| 1928 | return nilnullptr; |
| 1929 | return makeNSArray(backingObject->errorMessageObjects()); |
| 1930 | } |
| 1931 | |
| 1932 | if ([attributeName isEqualToString:NSAccessibilityFocusableAncestorAttribute@"AXFocusableAncestor"]) { |
| 1933 | RefPtr object = backingObject->focusableAncestor(); |
| 1934 | return object ? object->wrapper() : nilnullptr; |
| 1935 | } |
| 1936 | |
| 1937 | if ([attributeName isEqualToString:NSAccessibilityEditableAncestorAttribute@"AXEditableAncestor"]) { |
| 1938 | RefPtr object = backingObject->editableAncestor(); |
| 1939 | return object ? object->wrapper() : nilnullptr; |
| 1940 | } |
| 1941 | |
| 1942 | if ([attributeName isEqualToString:NSAccessibilityHighestEditableAncestorAttribute@"AXHighestEditableAncestor"]) { |
| 1943 | RefPtr object = backingObject->highestEditableAncestor(); |
| 1944 | return object ? object->wrapper() : nilnullptr; |
| 1945 | } |
| 1946 | |
| 1947 | if ([attributeName isEqualToString:NSAccessibilityTextInputMarkedRangeAttribute@"AXTextInputMarkedRange"]) { |
| 1948 | auto range = backingObject->textInputMarkedTextMarkerRange(); |
| 1949 | auto nsRange = range.nsRange(); |
| 1950 | return range && nsRange ? [NSValue valueWithRange:*nsRange] : nilnullptr; |
| 1951 | } |
| 1952 | |
| 1953 | if ([attributeName isEqualToString:NSAccessibilityTextInputMarkedTextMarkerRangeAttribute@"AXTextInputMarkedTextMarkerRange"]) { |
| 1954 | auto range = backingObject->textInputMarkedTextMarkerRange(); |
| 1955 | return range ? range.platformData().bridgingAutorelease() : nilnullptr; |
| 1956 | } |
| 1957 | |
| 1958 | // VoiceOver property to ignore certain groups. |
| 1959 | if ([attributeName isEqualToString:NSAccessibilityAutoInteractableAttribute@"AXAutoInteractable"]) |
| 1960 | return @(backingObject->isRemoteFrame()); |
| 1961 | |
| 1962 | if (AXObjectCache::clientIsInTestMode()) |
| 1963 | return attributeValueForTesting(backingObject, attributeName); |
| 1964 | return nilnullptr; |
| 1965 | } |
| 1966 | ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop |
| 1967 | |
| 1968 | id attributeValueForTesting(const RefPtr<AXCoreObject>& backingObject, NSString *attributeName) |
| 1969 | { |
| 1970 | ASSERT_WITH_MESSAGE(AXObjectCache::clientIsInTestMode(), "Should be used for testing only, not for AT clients.")((void)0); |
| 1971 | |
| 1972 | if ([attributeName isEqualToString:NSAccessibilityARIARoleAttribute@"AXARIARole"]) |
| 1973 | return backingObject->computedRoleString().createNSString().autorelease(); |
| 1974 | |
| 1975 | if ([attributeName isEqualToString:NSAccessibilityStringValueAttribute@"AXStringValue"]) |
| 1976 | return backingObject->stringValue().createNSString().autorelease(); |
| 1977 | |
| 1978 | if ([attributeName isEqualToString:NSAccessibilityDateTimeComponentsTypeAttribute@"AXDateTimeComponentsType"]) |
| 1979 | return [NSNumber numberWithUnsignedShort:(uint8_t)backingObject->dateTimeComponentsType()]; |
| 1980 | |
| 1981 | if ([attributeName isEqualToString:NSAccessibilityControllersAttribute@"AXControllers"]) |
| 1982 | return makeNSArray(backingObject->controllers()); |
| 1983 | |
| 1984 | if ([attributeName isEqualToString:NSAccessibilityActionTargetsAttribute@"_AXActionTargets"]) |
| 1985 | return makeNSArray(backingObject->associatedActionElements()); |
| 1986 | |
| 1987 | if ([attributeName isEqualToString:NSAccessibilityControllerForAttribute@"AXControllerFor"]) |
| 1988 | return makeNSArray(backingObject->controlledObjects()); |
| 1989 | |
| 1990 | if ([attributeName isEqualToString:NSAccessibilityDescribedByAttribute@"AXDescribedBy"]) |
| 1991 | return makeNSArray(backingObject->describedByObjects()); |
| 1992 | |
| 1993 | if ([attributeName isEqualToString:NSAccessibilityDescriptionForAttribute@"AXDescriptionFor"]) |
| 1994 | return makeNSArray(backingObject->descriptionForObjects()); |
| 1995 | |
| 1996 | if ([attributeName isEqualToString:NSAccessibilityDetailsForAttribute@"AXDetailsFor"]) |
| 1997 | return makeNSArray(backingObject->detailsForObjects()); |
| 1998 | |
| 1999 | if ([attributeName isEqualToString:NSAccessibilityErrorMessageForAttribute@"AXErrorMessageFor"]) |
| 2000 | return makeNSArray(backingObject->errorMessageForObjects()); |
| 2001 | |
| 2002 | if ([attributeName isEqualToString:NSAccessibilityFlowFromAttribute@"AXFlowFrom"]) |
| 2003 | return makeNSArray(backingObject->flowFromObjects()); |
| 2004 | |
| 2005 | if ([attributeName isEqualToString:NSAccessibilityFlowToAttribute@"AXFlowTo"]) |
| 2006 | return makeNSArray(backingObject->flowToObjects()); |
| 2007 | |
| 2008 | if ([attributeName isEqualToString:NSAccessibilityLabelledByAttribute@"AXLabelledBy"]) |
| 2009 | return makeNSArray(backingObject->labeledByObjects()); |
| 2010 | |
| 2011 | if ([attributeName isEqualToString:NSAccessibilityLabelForAttribute@"AXLabelFor"]) |
| 2012 | return makeNSArray(backingObject->labelForObjects()); |
| 2013 | |
| 2014 | if ([attributeName isEqualToString:NSAccessibilityOwnersAttribute@"AXOwners"]) |
| 2015 | return makeNSArray(backingObject->owners()); |
| 2016 | |
| 2017 | if ([attributeName isEqualToString:NSAccessibilityARIAPressedIsPresentAttribute@"AXARIAPressedIsPresent"]) |
| 2018 | return [NSNumber numberWithBool:backingObject->pressedIsPresent()]; |
| 2019 | |
| 2020 | if ([attributeName isEqualToString:NSAccessibilityAutocompleteValueAttribute@"AXAutocompleteValue"]) |
| 2021 | return backingObject->autoCompleteValue().createNSString().autorelease(); |
| 2022 | |
| 2023 | if ([attributeName isEqualToString:NSAccessibilityClickPointAttribute@"AXClickPoint"]) |
| 2024 | return [NSValue valueWithPoint:backingObject->clickPoint()]; |
| 2025 | |
| 2026 | if ([attributeName isEqualToString:NSAccessibilityIsIndeterminateAttribute@"AXIsIndeterminate"]) |
| 2027 | return [NSNumber numberWithBool:backingObject->isIndeterminate()]; |
| 2028 | |
| 2029 | if ([attributeName isEqualToString:NSAccessibilityIsMultiSelectableAttribute@"AXIsMultiSelectable"]) |
| 2030 | return [NSNumber numberWithBool:backingObject->isMultiSelectable()]; |
| 2031 | |
| 2032 | if ([attributeName isEqualToString:NSAccessibilityIsOnScreenAttribute@"AXIsOnScreen"]) |
| 2033 | return [NSNumber numberWithBool:backingObject->isOnScreen()]; |
| 2034 | |
| 2035 | if ([attributeName isEqualToString:NSAccessibilityIsInTableAttribute@"_AXIsInTable"]) { |
| 2036 | RefPtr table = Accessibility::findAncestor(*backingObject, false, [&] (const auto& ancestor) { |
| 2037 | return ancestor.isTable(); |
| 2038 | }); |
| 2039 | return [NSNumber numberWithBool:!!table.get()]; |
| 2040 | } |
| 2041 | |
| 2042 | if ([attributeName isEqualToString:NSAccessibilityIsRemoteFrameAttribute@"AXIsRemoteFrame"]) |
| 2043 | return [NSNumber numberWithBool:backingObject->isRemoteFrame()]; |
| 2044 | |
| 2045 | if ([attributeName isEqualToString:NSAccessibilityInfoStringForTestingAttribute@"AXInfoStringForTesting"]) |
| 2046 | return backingObject->infoStringForTesting().createNSString().autorelease(); |
| 2047 | |
| 2048 | if ([attributeName isEqualToString:NSAccessibilityPageRelativePositionAttribute@"_AXPageRelativePosition"]) |
| 2049 | return [NSValue valueWithPoint:(CGPoint)backingObject->relativeFrame().location()]; |
| 2050 | |
| 2051 | return nilnullptr; |
| 2052 | } |
| 2053 | |
| 2054 | - (id)parameterizedAttributeValueForTesting:(NSString *)attribute parameter:(id)parameter backingObject:(const RefPtr<AXCoreObject>&)backingObject |
| 2055 | { |
| 2056 | // This should've been null-checked already. |
| 2057 | RELEASE_ASSERT(parameter)do { if (__builtin_expect(!!(!(parameter)), 0)) do { WTF::isIntegralOrPointerType (); compilerFenceForCrash(); WTFCrashWithInfo(2057, "/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 2058 | |
| 2059 | AXTextMarkerRef markerRef = nilnullptr; |
| 2060 | AXTextMarkerRangeRef markerRangeRef = nilnullptr; |
| 2061 | NSRange nsRange = { 0, 0 }; |
| 2062 | |
| 2063 | if (AXObjectIsTextMarker(parameter)) |
| 2064 | markerRef = (AXTextMarkerRef)parameter; |
| 2065 | else if (AXObjectIsTextMarkerRange(parameter)) |
| 2066 | markerRangeRef = (AXTextMarkerRangeRef)parameter; |
| 2067 | else if ([parameter isKindOfClass:[NSValue class]] && nsValueHasObjCType<NSRange>((NSValue *)parameter)) |
| 2068 | nsRange = [(NSValue*)parameter rangeValue]; |
| 2069 | else |
| 2070 | return nilnullptr; |
| 2071 | |
| 2072 | if ([attribute isEqualToString:NSAccessibilityTextMarkerIsNullParameterizedAttribute@"AXTextMarkerIsNull"]) |
| 2073 | return [NSNumber numberWithBool:AXTextMarker(markerRef).isNull()]; |
| 2074 | |
| 2075 | if ([attribute isEqualToString:NSAccessibilityTextMarkerRangeIsValidParameterizedAttribute@"AXTextMarkerRangeIsValid"]) { |
| 2076 | AXTextMarkerRange markerRange { markerRangeRef }; |
| 2077 | return [NSNumber numberWithBool:markerRange.start().isValid() && markerRange.end().isValid()]; |
| 2078 | } |
| 2079 | |
| 2080 | if ([attribute isEqualToString:_AXStartTextMarkerForTextMarkerRangeAttribute@"_AXStartTextMarkerForTextMarkerRange"]) { |
| 2081 | AXTextMarkerRange markerRange { markerRangeRef }; |
| 2082 | return markerRange.start().platformData().bridgingAutorelease(); |
| 2083 | } |
| 2084 | |
| 2085 | if ([attribute isEqualToString:_AXEndTextMarkerForTextMarkerRangeAttribute@"_AXEndTextMarkerForTextMarkerRange"]) { |
| 2086 | AXTextMarkerRange markerRange { markerRangeRef }; |
| 2087 | return markerRange.end().platformData().bridgingAutorelease(); |
| 2088 | } |
| 2089 | |
| 2090 | if ([attribute isEqualToString:_AXTextMarkerRangeForNSRangeAttribute@"_AXTextMarkerRangeForNSRange"]) |
| 2091 | return backingObject->textMarkerRangeForNSRange(nsRange).platformData().bridgingAutorelease(); |
| 2092 | |
| 2093 | if ([attribute isEqualToString:_AXPageBoundsForTextMarkerRangeAttribute@"_AXPageBoundsForTextMarkerRange"]) { |
| 2094 | NSRect rect = CGRectZero; |
| 2095 | if (backingObject) |
| 2096 | rect = [self computeTextBoundsForRange:nsRange backingObject:*backingObject]; |
| 2097 | |
| 2098 | return [NSValue valueWithRect:rect]; |
| 2099 | } |
| 2100 | |
| 2101 | return nilnullptr; |
| 2102 | } |
| 2103 | |
| 2104 | - (NSValue *)intersectionWithSelectionRange |
| 2105 | { |
| 2106 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2107 | if (!backingObject) |
| 2108 | return nilnullptr; |
| 2109 | |
| 2110 | auto objectRange = backingObject->textMarkerRange(); |
| 2111 | auto selectionRange = backingObject->selectedTextMarkerRange(); |
| 2112 | |
| 2113 | auto intersection = selectionRange.intersectionWith(objectRange); |
| 2114 | if (intersection.has_value()) { |
| 2115 | auto intersectionCharacterRange = intersection->characterRange(); |
| 2116 | if (intersectionCharacterRange.has_value()) |
| 2117 | return [NSValue valueWithRange:intersectionCharacterRange.value()]; |
| 2118 | } |
| 2119 | |
| 2120 | return nilnullptr; |
| 2121 | } |
| 2122 | |
| 2123 | - (NSString *)accessibilityPlatformMathSubscriptKey |
| 2124 | { |
| 2125 | return NSAccessibilityMathSubscriptAttribute@"AXMathSubscript"; |
| 2126 | } |
| 2127 | |
| 2128 | - (NSString *)accessibilityPlatformMathSuperscriptKey |
| 2129 | { |
| 2130 | return NSAccessibilityMathSuperscriptAttribute@"AXMathSuperscript"; |
| 2131 | } |
| 2132 | |
| 2133 | - (id)accessibilityFocusedUIElement |
| 2134 | { |
| 2135 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2136 | if (!backingObject) |
| 2137 | return nilnullptr; |
| 2138 | |
| 2139 | RefPtr focusedObject = backingObject->focusedUIElementInAnyLocalFrame(); |
| 2140 | return focusedObject ? focusedObject->platformElement().autorelease() : nilnullptr; |
| 2141 | } |
| 2142 | |
| 2143 | - (id)accessibilityHitTest:(NSPoint)point |
| 2144 | { |
| 2145 | return [self _accessibilityHitTest:point returnPlatformElements:YES__objc_yes]; |
| 2146 | } |
| 2147 | |
| 2148 | - (id)_accessibilityHitTest:(NSPoint)point returnPlatformElements:(BOOL)returnPlatformElements |
| 2149 | { |
| 2150 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2151 | if (!backingObject) |
| 2152 | return nilnullptr; |
| 2153 | |
| 2154 | if (RefPtr axObject = dynamicDowncast<AccessibilityObject>(backingObject)) |
| 2155 | axObject->updateChildrenIfNecessary(); |
| 2156 | RefPtr axObject = backingObject->accessibilityHitTest(IntPoint(point)); |
| 2157 | |
| 2158 | RetainPtr<id> hit = nilnullptr; |
| 2159 | if (axObject) { |
| 2160 | if (axObject->isAttachment()) { |
| 2161 | RetainPtr wrapper = axObject->wrapper(); |
| 2162 | if (RetainPtr<id> attachmentView = [wrapper.get() attachmentView]) |
| 2163 | return attachmentView.autorelease(); |
| 2164 | } else if (axObject->isRemoteFrame()) { |
| 2165 | if (returnPlatformElements) |
| 2166 | return axObject->remoteFramePlatformElement().autorelease(); |
| 2167 | } else if (axObject->isWidget()) { |
| 2168 | // Only call out to the main-thread if this object has a backing widget to query. |
| 2169 | hit = Accessibility::retrieveAutoreleasedValueFromMainThread<id>([axObject, &point] () -> RetainPtr<id> { |
| 2170 | RefPtr widget = axObject->widget(); |
| 2171 | if (is<PluginViewBase>(widget)) |
| 2172 | return widget->accessibilityHitTest(IntPoint(point)); |
| 2173 | return nilnullptr; |
| 2174 | }); |
| 2175 | } |
| 2176 | |
| 2177 | if (!hit) |
| 2178 | hit = axObject->wrapper(); |
| 2179 | } else |
| 2180 | hit = self; |
| 2181 | |
| 2182 | return NSAccessibilityUnignoredAncestor(hit.get()); |
| 2183 | } |
| 2184 | |
| 2185 | - (void)_accessibilityHitTestResolvingRemoteFrame:(NSPoint)point callback:(void(^)(NSString *))callback |
| 2186 | { |
| 2187 | if (!AXObjectCache::clientIsInTestMode()) { |
| 2188 | callback(@""); |
| 2189 | return; |
| 2190 | } |
| 2191 | |
| 2192 | RetainPtr<id> hitTestResult = [self accessibilityHitTest:point]; |
| 2193 | if (!hitTestResult) { |
| 2194 | callback(@""); |
| 2195 | return; |
| 2196 | } |
| 2197 | |
| 2198 | if ([hitTestResult isKindOfClass:[NSAccessibilityRemoteUIElement class]]) { |
| 2199 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2200 | if (!backingObject) |
| 2201 | return callback(@"no backing object"); |
| 2202 | |
| 2203 | RefPtr axObject = backingObject->accessibilityHitTest(IntPoint(point)); |
| 2204 | if (axObject && axObject->isRemoteFrame()) { |
| 2205 | RefPtr page = backingObject ? backingObject->page() : nullptr; |
| 2206 | RefPtr axRemoteFrame = dynamicDowncast<AXRemoteFrame>(axObject); |
| 2207 | if (page && axRemoteFrame) { |
| 2208 | auto clientCallback = [callback = makeBlockPtr(callback)] (String result) { |
| 2209 | callback(nsStringNilIfEmpty(result).get()); |
| 2210 | }; |
| 2211 | |
| 2212 | page->chrome().client().resolveAccessibilityHitTestForTesting(*axRemoteFrame->remoteFrameID(), IntPoint(point), WTF::move(clientCallback)); |
| 2213 | } |
| 2214 | } |
| 2215 | } else { |
| 2216 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 2217 | callback([hitTestResult accessibilityAttributeValue:NSAccessibilityInfoStringForTestingAttribute@"AXInfoStringForTesting"]); |
| 2218 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 2219 | } |
| 2220 | } |
| 2221 | |
| 2222 | ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-implementations" |
| 2223 | - (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName |
| 2224 | { |
| 2225 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2226 | if (!backingObject) |
| 2227 | return NO__objc_no; |
| 2228 | |
| 2229 | if ([attributeName isEqualToString:NSAccessibilitySelectedTextMarkerRangeAttribute@"AXSelectedTextMarkerRange"]) |
| 2230 | return YES__objc_yes; |
| 2231 | |
| 2232 | if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) |
| 2233 | return backingObject->canSetFocusAttribute(); |
| 2234 | |
| 2235 | if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) |
| 2236 | return backingObject->canSetValueAttribute(); |
| 2237 | |
| 2238 | if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) |
| 2239 | return backingObject->canSetSelectedAttribute(); |
| 2240 | |
| 2241 | if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) |
| 2242 | return backingObject->canSetSelectedChildren(); |
| 2243 | |
| 2244 | if ([attributeName isEqualToString:NSAccessibilityDisclosingAttribute] |
| 2245 | || [attributeName isEqualToString:NSAccessibilityExpandedAttribute]) |
| 2246 | return backingObject->canSetExpandedAttribute(); |
| 2247 | |
| 2248 | if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute]) |
| 2249 | return YES__objc_yes; |
| 2250 | |
| 2251 | if ([attributeName isEqualToString:NSAccessibilitySelectedTextAttribute] |
| 2252 | || [attributeName isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) |
| 2253 | return backingObject->canSetTextRangeAttributes(); |
| 2254 | |
| 2255 | if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute@"AXGrabbed"]) |
| 2256 | return YES__objc_yes; |
| 2257 | |
| 2258 | if (backingObject->isWebArea() |
| 2259 | && ([attributeName isEqualToString:NSAccessibilityPreventKeyboardDOMEventDispatchAttribute@"AXPreventKeyboardDOMEventDispatch"] |
| 2260 | || [attributeName isEqualToString:NSAccessibilityCaretBrowsingEnabledAttribute@"AXCaretBrowsingEnabled"])) |
| 2261 | return YES__objc_yes; |
| 2262 | |
| 2263 | return NO__objc_no; |
| 2264 | } |
| 2265 | ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop |
| 2266 | |
| 2267 | ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-implementations" |
| 2268 | - (BOOL)accessibilityIsIgnored |
| 2269 | { |
| 2270 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2271 | if (!backingObject) |
| 2272 | return YES__objc_yes; |
| 2273 | |
| 2274 | if (backingObject->isAttachment()) |
| 2275 | return [[self attachmentView] accessibilityIsIgnored]; |
| 2276 | return backingObject->isIgnored(); |
| 2277 | } |
| 2278 | ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop |
| 2279 | |
| 2280 | ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-implementations" |
| 2281 | - (NSArray *)accessibilityParameterizedAttributeNames |
| 2282 | { |
| 2283 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2284 | if (!backingObject) |
| 2285 | return nilnullptr; |
| 2286 | |
| 2287 | #if !ENABLE_ACCESSIBILITY_LOCAL_FRAME0 |
| 2288 | if (backingObject->isAttachment()) |
| 2289 | return nilnullptr; |
| 2290 | #endif // !ENABLE_ACCESSIBILITY_LOCAL_FRAME |
| 2291 | |
| 2292 | static NeverDestroyed<RetainPtr<NSArray>> paramAttrs = @[ |
| 2293 | NSAccessibilityUIElementForTextMarkerAttribute@"AXUIElementForTextMarker", |
| 2294 | NSAccessibilityTextMarkerRangeForUIElementAttribute@"AXTextMarkerRangeForUIElement", |
| 2295 | NSAccessibilityLineForTextMarkerAttribute@"AXLineForTextMarker", |
| 2296 | NSAccessibilityTextMarkerRangeForLineAttribute@"AXTextMarkerRangeForLine", |
| 2297 | NSAccessibilityStringForTextMarkerRangeAttribute@"AXStringForTextMarkerRange", |
| 2298 | NSAccessibilityTextMarkerForPositionAttribute@"AXTextMarkerForPosition", |
| 2299 | NSAccessibilityBoundsForTextMarkerRangeAttribute@"AXBoundsForTextMarkerRange", |
| 2300 | NSAccessibilityAttributedStringForTextMarkerRangeAttribute@"AXAttributedStringForTextMarkerRange", |
| 2301 | NSAccessibilityAttributedStringForTextMarkerRangeWithOptionsAttribute@"AXAttributedStringForTextMarkerRangeWithOptions", |
| 2302 | NSAccessibilityTextMarkerRangeForTextMarkersAttribute@"AXTextMarkerRangeForTextMarkers", |
| 2303 | NSAccessibilityTextMarkerRangeForUnorderedTextMarkersAttribute@"AXTextMarkerRangeForUnorderedTextMarkers", |
| 2304 | NSAccessibilityNextTextMarkerForTextMarkerAttribute@"AXNextTextMarkerForTextMarker", |
| 2305 | NSAccessibilityPreviousTextMarkerForTextMarkerAttribute@"AXPreviousTextMarkerForTextMarker", |
| 2306 | NSAccessibilityLeftWordTextMarkerRangeForTextMarkerAttribute@"AXLeftWordTextMarkerRangeForTextMarker", |
| 2307 | NSAccessibilityRightWordTextMarkerRangeForTextMarkerAttribute@"AXRightWordTextMarkerRangeForTextMarker", |
| 2308 | NSAccessibilityLeftLineTextMarkerRangeForTextMarkerAttribute@"AXLeftLineTextMarkerRangeForTextMarker", |
| 2309 | NSAccessibilityRightLineTextMarkerRangeForTextMarkerAttribute@"AXRightLineTextMarkerRangeForTextMarker", |
| 2310 | NSAccessibilitySentenceTextMarkerRangeForTextMarkerAttribute@"AXSentenceTextMarkerRangeForTextMarker", |
| 2311 | NSAccessibilityParagraphTextMarkerRangeForTextMarkerAttribute@"AXParagraphTextMarkerRangeForTextMarker", |
| 2312 | NSAccessibilityNextWordEndTextMarkerForTextMarkerAttribute@"AXNextWordEndTextMarkerForTextMarker", |
| 2313 | NSAccessibilityPreviousWordStartTextMarkerForTextMarkerAttribute@"AXPreviousWordStartTextMarkerForTextMarker", |
| 2314 | NSAccessibilityNextLineEndTextMarkerForTextMarkerAttribute@"AXNextLineEndTextMarkerForTextMarker", |
| 2315 | NSAccessibilityPreviousLineStartTextMarkerForTextMarkerAttribute@"AXPreviousLineStartTextMarkerForTextMarker", |
| 2316 | NSAccessibilityNextSentenceEndTextMarkerForTextMarkerAttribute@"AXNextSentenceEndTextMarkerForTextMarker", |
| 2317 | NSAccessibilityPreviousSentenceStartTextMarkerForTextMarkerAttribute@"AXPreviousSentenceStartTextMarkerForTextMarker", |
| 2318 | NSAccessibilityNextParagraphEndTextMarkerForTextMarkerAttribute@"AXNextParagraphEndTextMarkerForTextMarker", |
| 2319 | NSAccessibilityPreviousParagraphStartTextMarkerForTextMarkerAttribute@"AXPreviousParagraphStartTextMarkerForTextMarker", |
| 2320 | NSAccessibilityStyleTextMarkerRangeForTextMarkerAttribute@"AXStyleTextMarkerRangeForTextMarker", |
| 2321 | NSAccessibilityLengthForTextMarkerRangeAttribute@"AXLengthForTextMarkerRange", |
| 2322 | NSAccessibilityBoundsForRangeParameterizedAttribute, |
| 2323 | NSAccessibilityStringForRangeParameterizedAttribute, |
| 2324 | NSAccessibilityUIElementsForSearchPredicateParameterizedAttribute@"AXUIElementsForSearchPredicate", |
| 2325 | NSAccessibilityEndTextMarkerForBoundsAttribute@"AXEndTextMarkerForBounds", |
| 2326 | NSAccessibilityStartTextMarkerForBoundsAttribute@"AXStartTextMarkerForBounds", |
| 2327 | NSAccessibilityLineTextMarkerRangeForTextMarkerAttribute@"AXLineTextMarkerRangeForTextMarker", |
| 2328 | NSAccessibilitySelectTextWithCriteriaParameterizedAttribute@"AXSelectTextWithCriteria", |
| 2329 | NSAccessibilitySearchTextWithCriteriaParameterizedAttribute@"AXSearchTextWithCriteria", |
| 2330 | NSAccessibilityTextOperationParameterizedAttribute@"AXTextOperation" |
| 2331 | ]; |
| 2332 | |
| 2333 | static NeverDestroyed textParamAttrs = [] { |
| 2334 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:paramAttrs.get().get()]); |
| 2335 | [tempArray addObject:(NSString*)kAXLineForIndexParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXLineForIndex" ""))]; |
| 2336 | [tempArray addObject:(NSString*)kAXRangeForLineParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXRangeForLine" ""))]; |
| 2337 | [tempArray addObject:(NSString*)kAXStringForRangeParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXStringForRange" ""))]; |
| 2338 | [tempArray addObject:(NSString*)kAXRangeForPositionParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXRangeForPosition" ""))]; |
| 2339 | [tempArray addObject:(NSString*)kAXRangeForIndexParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXRangeForIndex" ""))]; |
| 2340 | [tempArray addObject:(NSString*)kAXBoundsForRangeParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXBoundsForRange" ""))]; |
| 2341 | [tempArray addObject:(NSString*)kAXRTFForRangeParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXRTFForRange" ""))]; |
| 2342 | [tempArray addObject:(NSString*)kAXAttributedStringForRangeParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXAttributedStringForRange" ""))]; |
| 2343 | [tempArray addObject:(NSString*)kAXStyleRangeForIndexParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXStyleRangeForIndex" ""))]; |
| 2344 | return tempArray; |
| 2345 | }(); |
| 2346 | static NeverDestroyed tableParamAttrs = [] { |
| 2347 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:paramAttrs.get().get()]); |
| 2348 | [tempArray addObject:NSAccessibilityCellForColumnAndRowParameterizedAttribute]; |
| 2349 | return tempArray; |
| 2350 | }(); |
| 2351 | static NeverDestroyed webAreaParamAttrs = [] { |
| 2352 | auto tempArray = adoptNS([[NSMutableArray alloc] initWithArray:paramAttrs.get().get()]); |
| 2353 | [tempArray addObject:NSAccessibilityTextMarkerForIndexAttribute@"AXTextMarkerForIndex"]; |
| 2354 | [tempArray addObject:NSAccessibilityTextMarkerIsValidAttribute@"AXTextMarkerIsValid"]; |
| 2355 | [tempArray addObject:NSAccessibilityIndexForTextMarkerAttribute@"AXIndexForTextMarker"]; |
| 2356 | return tempArray; |
| 2357 | }(); |
| 2358 | static NeverDestroyed secureFieldParamAttrs = [] { |
| 2359 | auto tempArray = adoptNS([[NSMutableArray alloc] init]); |
| 2360 | [tempArray addObject:NSAccessibilityUIElementsForSearchPredicateParameterizedAttribute@"AXUIElementsForSearchPredicate"]; |
| 2361 | return tempArray; |
| 2362 | }(); |
| 2363 | |
| 2364 | if (backingObject->isSecureField()) |
| 2365 | return secureFieldParamAttrs.get().get(); |
| 2366 | |
| 2367 | if (backingObject->isTextControl()) |
| 2368 | return textParamAttrs.get().get(); |
| 2369 | |
| 2370 | if (backingObject->isExposableTable()) |
| 2371 | return tableParamAttrs.get().get(); |
| 2372 | |
| 2373 | if (backingObject->isWebArea()) |
| 2374 | return webAreaParamAttrs.get().get(); |
| 2375 | |
| 2376 | // The object that serves up the remote frame also is the one that does the frame conversion. |
| 2377 | if (backingObject->hasRemoteFrameChild()) |
| 2378 | return [paramAttrs.get().get() arrayByAddingObject:NSAccessibilityConvertRelativeFrameParameterizedAttribute@"AXConvertRelativeFrame"]; |
| 2379 | |
| 2380 | return paramAttrs.get().get(); |
| 2381 | } |
| 2382 | ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop |
| 2383 | |
| 2384 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 2385 | |
| 2386 | - (NSString *)accessibilityIdentifier |
| 2387 | { |
| 2388 | if (AXObjectCache::accessibilityDOMIdentifiersEnabled()) { |
| 2389 | if (RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore) |
| 2390 | return backingObject->identifierAttribute().createNSString().autorelease(); |
| 2391 | } |
| 2392 | |
| 2393 | return nilnullptr; |
| 2394 | } |
| 2395 | |
| 2396 | - (void)accessibilityPerformPressAction |
| 2397 | { |
| 2398 | // In case anything we do by performing the press action causes an alert or other modal |
| 2399 | // behaviors, we need to return now, so that VoiceOver doesn't hang indefinitely. |
| 2400 | RunLoop::mainSingleton().dispatch([protectedSelf = retainPtr(self)] { |
| 2401 | [protectedSelf _accessibilityPerformPressAction]; |
| 2402 | }); |
| 2403 | } |
| 2404 | |
| 2405 | - (void)_accessibilityPerformPressAction |
| 2406 | { |
| 2407 | AX_ASSERT(isMainThread())((void)0); |
| 2408 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2409 | if (!backingObject) |
| 2410 | return; |
| 2411 | |
| 2412 | if (backingObject->isAttachment()) |
| 2413 | [[self attachmentView] accessibilityPerformAction:NSAccessibilityPressAction]; |
| 2414 | else |
| 2415 | backingObject->press(); |
| 2416 | } |
| 2417 | |
| 2418 | - (void)accessibilityPerformIncrementAction |
| 2419 | { |
| 2420 | RunLoop::mainSingleton().dispatch([protectedSelf = retainPtr(self)] { |
| 2421 | [protectedSelf _accessibilityPerformIncrementAction]; |
| 2422 | }); |
| 2423 | } |
| 2424 | |
| 2425 | - (void)_accessibilityPerformIncrementAction |
| 2426 | { |
| 2427 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2428 | if (!backingObject) |
| 2429 | return; |
| 2430 | |
| 2431 | if (backingObject->isAttachment()) |
| 2432 | [[self attachmentView] accessibilityPerformAction:NSAccessibilityIncrementAction]; |
| 2433 | else |
| 2434 | backingObject->increment(); |
| 2435 | } |
| 2436 | |
| 2437 | - (void)accessibilityPerformDecrementAction |
| 2438 | { |
| 2439 | RunLoop::mainSingleton().dispatch([protectedSelf = retainPtr(self)] { |
| 2440 | [protectedSelf _accessibilityPerformDecrementAction]; |
| 2441 | }); |
| 2442 | } |
| 2443 | |
| 2444 | - (void)_accessibilityPerformDecrementAction |
| 2445 | { |
| 2446 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2447 | if (!backingObject) |
| 2448 | return; |
| 2449 | |
| 2450 | if (backingObject->isAttachment()) |
| 2451 | [[self attachmentView] accessibilityPerformAction:NSAccessibilityDecrementAction]; |
| 2452 | else |
| 2453 | backingObject->decrement(); |
| 2454 | } |
| 2455 | |
| 2456 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 2457 | |
| 2458 | - (void)accessibilityPerformShowMenuAction |
| 2459 | { |
| 2460 | AXTRACE("WebAccessibilityObjectWrapper accessibilityPerformShowMenuAction"_s)(void)0; |
| 2461 | |
| 2462 | RefPtr<AXCoreObject> backingObject = self.axBackingObject; |
| 2463 | if (!backingObject) { |
| 2464 | AXLOG(makeString("No backingObject for wrapper "_s, hex(reinterpret_cast<uintptr_t>(self))))(void)0; |
| 2465 | return; |
| 2466 | } |
| 2467 | |
| 2468 | if (backingObject->role() == AccessibilityRole::ComboBox) { |
| 2469 | backingObject->setIsExpanded(true); |
| 2470 | return; |
| 2471 | } |
| 2472 | |
| 2473 | Accessibility::performFunctionOnMainThread([protectedSelf = retainPtr(self)] { |
| 2474 | // This needs to be performed in an iteration of the run loop that did not start from an AX call. |
| 2475 | // If it's the same run loop iteration, the menu open notification won't be sent. |
| 2476 | [protectedSelf performSelector:@selector(_accessibilityShowContextMenu) withObject:nilnullptr afterDelay:0.0]; |
| 2477 | }); |
| 2478 | } |
| 2479 | |
| 2480 | - (void)_accessibilityShowContextMenu |
| 2481 | { |
| 2482 | AXTRACE("WebAccessibilityObjectWrapper _accessibilityShowContextMenu"_s)(void)0; |
| 2483 | AX_ASSERT(isMainThread())((void)0); |
| 2484 | |
| 2485 | RefPtr<AccessibilityObject> backingObject = dynamicDowncast<AccessibilityObject>(self.axBackingObject); |
| 2486 | if (!backingObject) { |
| 2487 | AXLOG(makeString("No backingObject for wrapper "_s, hex(reinterpret_cast<uintptr_t>(self))))(void)0; |
| 2488 | return; |
| 2489 | } |
| 2490 | |
| 2491 | RefPtr page = backingObject->page(); |
| 2492 | if (!page) |
| 2493 | return; |
| 2494 | |
| 2495 | IntRect rect = snappedIntRect(backingObject->elementRect()); |
| 2496 | // On WK2, we need to account for the scroll position with regards to root view. |
| 2497 | // On WK1, we need to convert rect to window space to match mouse clicking. |
| 2498 | RefPtr frameView = backingObject->documentFrameView(); |
| 2499 | if (frameView) { |
| 2500 | // Find the appropriate scroll view to convert the coordinates to window space. |
| 2501 | RefPtr axScrollView = Accessibility::findAncestor(*backingObject, false, [] (const auto& ancestor) { |
| 2502 | return ancestor.isScrollArea() && ancestor.scrollView(); |
| 2503 | }); |
| 2504 | |
| 2505 | if (RefPtr scrollView = axScrollView ? axScrollView->scrollView() : nullptr) { |
| 2506 | if (!frameView->platformWidget()) |
| 2507 | rect = scrollView->contentsToRootView(rect); |
| 2508 | else |
| 2509 | rect = scrollView->contentsToWindow(rect); |
| 2510 | } |
| 2511 | } |
| 2512 | |
| 2513 | if (RefPtr localMainFrame = page->localMainFrame()) |
| 2514 | page->contextMenuController().showContextMenuAt(*localMainFrame, rect.center()); |
| 2515 | } |
| 2516 | |
| 2517 | - (void)accessibilityScrollToVisible |
| 2518 | { |
| 2519 | if (RefPtr<AXCoreObject> backingObject = self.axBackingObject) |
| 2520 | backingObject->scrollToMakeVisible(); |
| 2521 | } |
| 2522 | |
| 2523 | - (void)_accessibilityScrollToMakeVisibleWithSubFocus:(NSRect)rect |
| 2524 | { |
| 2525 | if (RefPtr<AXCoreObject> backingObject = self.axBackingObject) |
| 2526 | backingObject->scrollToMakeVisibleWithSubFocus(IntRect(rect)); |
| 2527 | } |
| 2528 | |
| 2529 | - (void)_accessibilityScrollToGlobalPoint:(NSPoint)point |
| 2530 | { |
| 2531 | if (RefPtr<AXCoreObject> backingObject = self.axBackingObject) |
| 2532 | backingObject->scrollToGlobalPoint(IntPoint(point)); |
| 2533 | } |
| 2534 | |
| 2535 | ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-implementations" |
| 2536 | - (void)accessibilityPerformAction:(NSString*)action |
| 2537 | { |
| 2538 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2539 | if (!backingObject) |
| 2540 | return; |
| 2541 | |
| 2542 | if ([action isEqualToString:NSAccessibilityPressAction]) |
| 2543 | [self accessibilityPerformPressAction]; |
| 2544 | else if ([action isEqualToString:NSAccessibilitySyncPressAction@"AXSyncPressAction"]) { |
| 2545 | // Used in layout tests, so that we don't have to wait for the async press action. |
| 2546 | [self _accessibilityPerformPressAction]; |
| 2547 | } else if ([action isEqualToString:NSAccessibilitySyncIncrementAction@"AXSyncIncrementAction"]) |
| 2548 | [self _accessibilityPerformIncrementAction]; |
| 2549 | else if ([action isEqualToString:NSAccessibilitySyncDecrementAction@"AXSyncDecrementAction"]) |
| 2550 | [self _accessibilityPerformDecrementAction]; |
| 2551 | else if ([action isEqualToString:NSAccessibilityShowMenuAction]) |
| 2552 | [self accessibilityPerformShowMenuAction]; |
| 2553 | else if ([action isEqualToString:NSAccessibilityIncrementAction]) |
| 2554 | [self accessibilityPerformIncrementAction]; |
| 2555 | else if ([action isEqualToString:NSAccessibilityDecrementAction]) |
| 2556 | [self accessibilityPerformDecrementAction]; |
| 2557 | else if ([action isEqualToString:NSAccessibilityScrollToVisibleAction@"AXScrollToVisible"]) |
| 2558 | [self accessibilityScrollToVisible]; |
| 2559 | else if ([action isEqualToString:NSAccessibilityDismissAction@"AXDismissAction"]) |
| 2560 | backingObject->performDismissActionIgnoringResult(); |
| 2561 | else if (AXObjectCache::clientIsInTestMode() && [action isEqualToString:@"AXLogTrees"]) |
| 2562 | [self _accessibilityPrintTrees]; |
| 2563 | } |
| 2564 | ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop |
| 2565 | |
| 2566 | // Internal method to print the accessibility trees to standard error. |
| 2567 | - (void)_accessibilityPrintTrees |
| 2568 | { |
| 2569 | Accessibility::performFunctionOnMainThread([protectedSelf = retainPtr(self)] { |
| 2570 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 2571 | if (!backingObject) |
| 2572 | return; |
| 2573 | |
| 2574 | CheckedPtr cache = backingObject->axObjectCache(); |
| 2575 | if (!cache) |
| 2576 | return; |
| 2577 | |
| 2578 | AXTreeData data = cache->treeData({ { AXStreamOptions::IdentifierAttribute, AXStreamOptions::OuterHTML, AXStreamOptions::RendererOrNode } }); // Can specify AXStreamOptions here if needed (e.g., TextRuns) |
| 2579 | |
| 2580 | data.dumpToStderr(); |
| 2581 | }); |
| 2582 | } |
| 2583 | |
| 2584 | - (BOOL)accessibilityReplaceRange:(NSRange)range withText:(NSString *)string |
| 2585 | { |
| 2586 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2587 | return backingObject ? backingObject->replaceTextInRange(String(string), range) : NO__objc_no; |
| 2588 | } |
| 2589 | |
| 2590 | - (BOOL)accessibilityInsertText:(NSString *)text |
| 2591 | { |
| 2592 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2593 | return backingObject ? backingObject->insertText(String(text)) : NO__objc_no; |
| 2594 | } |
| 2595 | |
| 2596 | ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-implementations" |
| 2597 | - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName |
| 2598 | { |
| 2599 | #if PLATFORM(MAC)(defined 1 && 1) |
| 2600 | |
| 2601 | RetainPtr retainedValue = value; |
| 2602 | if (AXObjectCache::useAXThreadTextApis()) { |
| 2603 | if (AXObjectIsTextMarkerRange(value)) |
| 2604 | retainedValue = AXTextMarkerRange { (AXTextMarkerRangeRef)value }.convertToDomOffsetRange().platformData().bridgingAutorelease(); |
| 2605 | } |
| 2606 | |
| 2607 | // In case anything we do by changing values causes an alert or other modal |
| 2608 | // behaviors, we need to return now, so that VoiceOver doesn't hang indefinitely. |
| 2609 | callOnMainThread([retainedValue = WTF::move(retainedValue), attributeName = retainPtr(attributeName), protectedSelf = retainPtr(self)] { |
| 2610 | [protectedSelf _accessibilitySetValue:retainedValue.get() forAttribute:attributeName.get()]; |
| 2611 | }); |
| 2612 | #else |
| 2613 | // dispatch_async on earlier versions can cause focus not to track. |
| 2614 | [self _accessibilitySetValue:value forAttribute:attributeName]; |
| 2615 | #endif // PLATFORM(MAC) |
| 2616 | } |
| 2617 | ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop |
| 2618 | |
| 2619 | - (void)_accessibilitySetValue:(id)value forAttribute:(NSString *)attributeName |
| 2620 | { |
| 2621 | AXTRACE(makeString("WebAccessibilityObjectWrapper _accessibilitySetValue: forAttribute:"_s, String(attributeName)))(void)0; |
| 2622 | |
| 2623 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2624 | if (!backingObject) { |
| 2625 | AXLOG(makeString("No backingObject for wrapper "_s, hex(reinterpret_cast<uintptr_t>(self))))(void)0; |
| 2626 | return; |
| 2627 | } |
| 2628 | |
| 2629 | AXTextMarkerRangeRef textMarkerRange = nilnullptr; |
| 2630 | NSNumber* number = nilnullptr; |
| 2631 | NSString* string = nilnullptr; |
| 2632 | NSRange range = { 0, 0 }; |
| 2633 | NSArray* array = nilnullptr; |
| 2634 | |
| 2635 | // decode the parameter |
| 2636 | if (AXObjectIsTextMarkerRange(value)) |
| 2637 | textMarkerRange = (AXTextMarkerRangeRef)value; |
| 2638 | else if ([value isKindOfClass:[NSNumber class]]) |
| 2639 | number = value; |
| 2640 | else if ([value isKindOfClass:[NSString class]]) |
| 2641 | string = value; |
| 2642 | else if ([value isKindOfClass:[NSValue class]]) |
| 2643 | range = [value rangeValue]; |
| 2644 | else if ([value isKindOfClass:[NSArray class]]) |
| 2645 | array = value; |
| 2646 | |
| 2647 | // handle the command |
| 2648 | if ([attributeName isEqualToString:NSAccessibilitySelectedTextMarkerRangeAttribute@"AXSelectedTextMarkerRange"]) { |
| 2649 | AX_ASSERT(textMarkerRange)((void)0); |
| 2650 | Accessibility::performFunctionOnMainThread([textMarkerRange = retainPtr(textMarkerRange), protectedSelf = retainPtr(self)] { |
| 2651 | if (RefPtr<AXCoreObject> backingObject = protectedSelf.get().axBackingObject) |
| 2652 | backingObject->setSelectedVisiblePositionRange(AXTextMarkerRange { textMarkerRange.get() }); |
| 2653 | }); |
| 2654 | } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) { |
| 2655 | backingObject->setFocused([number boolValue]); |
| 2656 | } else if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) { |
| 2657 | if (number && backingObject->canSetNumericValue()) |
| 2658 | backingObject->setValueIgnoringResult([number floatValue]); |
| 2659 | else if (string) |
| 2660 | backingObject->setValueIgnoringResult(string); |
| 2661 | } else if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) { |
| 2662 | if (!number) |
| 2663 | return; |
| 2664 | backingObject->setSelected([number boolValue]); |
| 2665 | } else if ([attributeName isEqualToString:NSAccessibilitySelectedChildrenAttribute]) { |
| 2666 | if (!array || !backingObject->canSetSelectedChildren()) |
| 2667 | return; |
| 2668 | |
| 2669 | AXCoreObject::AccessibilityChildrenVector selectedChildren; |
| 2670 | convertToVector(array, selectedChildren); |
| 2671 | backingObject->setSelectedChildren(selectedChildren); |
| 2672 | } else if (backingObject->isTextControl()) { |
| 2673 | if ([attributeName isEqualToString:NSAccessibilitySelectedTextAttribute]) |
| 2674 | backingObject->setSelectedText(string); |
| 2675 | else if ([attributeName isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) |
| 2676 | backingObject->setSelectedTextRange(range); |
| 2677 | } else if ([attributeName isEqualToString:NSAccessibilityDisclosingAttribute] || [attributeName isEqualToString:NSAccessibilityExpandedAttribute]) |
| 2678 | backingObject->setIsExpanded([number boolValue]); |
| 2679 | else if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute]) { |
| 2680 | AccessibilityObject::AccessibilityChildrenVector selectedRows; |
| 2681 | convertToVector(array, selectedRows); |
| 2682 | if (backingObject->isTree() || backingObject->isExposableTable()) |
| 2683 | backingObject->setSelectedRows(WTF::move(selectedRows)); |
| 2684 | } else if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute@"AXGrabbed"]) |
| 2685 | backingObject->setARIAGrabbed([number boolValue]); |
| 2686 | else if (backingObject->isWebArea() && [attributeName isEqualToString:NSAccessibilityPreventKeyboardDOMEventDispatchAttribute@"AXPreventKeyboardDOMEventDispatch"]) |
| 2687 | backingObject->setPreventKeyboardDOMEventDispatch([number boolValue]); |
| 2688 | else if (backingObject->isWebArea() && [attributeName isEqualToString:NSAccessibilityCaretBrowsingEnabledAttribute@"AXCaretBrowsingEnabled"]) |
| 2689 | backingObject->setCaretBrowsingEnabled([number boolValue]); |
| 2690 | } |
| 2691 | |
| 2692 | static RenderObject* rendererForView(NSView* view) |
| 2693 | { |
| 2694 | if (![view conformsToProtocol:@protocol(WebCoreFrameView)]) |
| 2695 | return nullptr; |
| 2696 | |
| 2697 | NSView<WebCoreFrameView>* frameView = (NSView<WebCoreFrameView>*)view; |
| 2698 | auto frame = [frameView _web_frame]; |
| 2699 | if (!frame) |
| 2700 | return nullptr; |
| 2701 | |
| 2702 | RefPtr<Node> node = protect(frame->document())->ownerElement(); |
| 2703 | if (!node) |
| 2704 | return nullptr; |
| 2705 | |
| 2706 | return node->renderer(); |
| 2707 | } |
| 2708 | |
| 2709 | - (id)_accessibilityParentForSubview:(NSView*)subview |
| 2710 | { |
| 2711 | CheckedPtr renderer = rendererForView(subview); |
| 2712 | if (!renderer) |
| 2713 | return nilnullptr; |
| 2714 | |
| 2715 | CheckedPtr cache = protect(renderer->document())->axObjectCache(); |
| 2716 | RefPtr object = cache ? cache->getOrCreate(*renderer) : nilnullptr; |
| 2717 | RefPtr parent = object ? object->parentObjectUnignored() : nilnullptr; |
| 2718 | return parent ? parent->wrapper() : nilnullptr; |
| 2719 | } |
| 2720 | |
| 2721 | ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-implementations" |
| 2722 | - (NSString*)accessibilityActionDescription:(NSString*)action |
| 2723 | { |
| 2724 | // we have no custom actions |
| 2725 | return NSAccessibilityActionDescription(action); |
| 2726 | } |
| 2727 | ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop |
| 2728 | |
| 2729 | - (AXTextMarkerRef)_textMarkerForIndex:(NSInteger)textIndex |
| 2730 | { |
| 2731 | return Accessibility::retrieveAutoreleasedValueFromMainThread<AXTextMarkerRef>([&textIndex, protectedSelf = retainPtr(self)] () -> RetainPtr<AXTextMarkerRef> { |
| 2732 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 2733 | if (!backingObject) |
| 2734 | return nilnullptr; |
| 2735 | |
| 2736 | CheckedPtr cache = backingObject->axObjectCache(); |
| 2737 | if (!cache) |
| 2738 | return nilnullptr; |
| 2739 | |
| 2740 | RefPtr document = backingObject->document(); |
| 2741 | if (!document) |
| 2742 | return nilnullptr; |
| 2743 | |
| 2744 | RefPtr documentElement = document->documentElement(); |
| 2745 | if (!documentElement) |
| 2746 | return nilnullptr; |
| 2747 | |
| 2748 | auto boundary = resolveCharacterLocation(makeRangeSelectingNodeContents(*documentElement), textIndex); |
| 2749 | auto characterOffset = cache->startOrEndCharacterOffsetForRange(makeSimpleRange(boundary), true); |
| 2750 | |
| 2751 | return textMarkerForCharacterOffset(cache.get(), characterOffset); |
| 2752 | }); |
| 2753 | } |
| 2754 | |
| 2755 | #if ENABLE(TREE_DEBUGGING)(defined ENABLE_TREE_DEBUGGING && ENABLE_TREE_DEBUGGING ) |
| 2756 | - (void)showNodeForTextMarker:(AXTextMarkerRef)textMarker |
| 2757 | { |
| 2758 | CheckedPtr cache = downcast<AccessibilityObject>(self.axBackingObject)->axObjectCache(); |
| 2759 | auto visiblePosition = visiblePositionForTextMarker(cache.get(), textMarker); |
| 2760 | RefPtr node = visiblePosition.deepEquivalent().deprecatedNode(); |
| 2761 | if (!node) |
| 2762 | return; |
| 2763 | node->showNode(); |
| 2764 | node->showNodePathForThis(); |
| 2765 | } |
| 2766 | |
| 2767 | - (void)showNodeTreeForTextMarker:(AXTextMarkerRef)textMarker |
| 2768 | { |
| 2769 | CheckedPtr cache = downcast<AccessibilityObject>(self.axBackingObject)->axObjectCache(); |
| 2770 | auto visiblePosition = visiblePositionForTextMarker(cache.get(), textMarker); |
| 2771 | RefPtr node = visiblePosition.deepEquivalent().deprecatedNode(); |
| 2772 | if (!node) |
| 2773 | return; |
| 2774 | node->showTreeForThis(); |
| 2775 | } |
| 2776 | #endif |
| 2777 | |
| 2778 | enum class TextUnit { |
| 2779 | LeftWord = 1, |
| 2780 | RightWord, |
| 2781 | NextWordEnd, |
| 2782 | PreviousWordStart, |
| 2783 | Sentence, |
| 2784 | NextSentenceEnd, |
| 2785 | PreviousSentenceStart, |
| 2786 | Paragraph, |
| 2787 | NextParagraphEnd, |
| 2788 | PreviousParagraphStart, |
| 2789 | Line, |
| 2790 | LeftLine, |
| 2791 | RightLine, |
| 2792 | NextLineEnd, |
| 2793 | PreviousLineStart, |
| 2794 | }; |
| 2795 | |
| 2796 | - (AXTextMarkerRangeRef)textMarkerRangeAtTextMarker:(AXTextMarkerRef)textMarker forUnit:(TextUnit)textUnit |
| 2797 | { |
| 2798 | if (AXObjectCache::useAXThreadTextApis()) { |
| 2799 | AXTextMarker inputMarker { textMarker }; |
| 2800 | switch (textUnit) { |
| 2801 | case TextUnit::LeftWord: |
| 2802 | return inputMarker.wordRange(WordRangeType::Left).platformData().autorelease(); |
| 2803 | case TextUnit::RightWord: |
| 2804 | return inputMarker.wordRange(WordRangeType::Right).platformData().autorelease(); |
| 2805 | case TextUnit::Sentence: |
| 2806 | return inputMarker.sentenceRange(SentenceRangeType::Current).platformData().autorelease(); |
| 2807 | case TextUnit::Paragraph: |
| 2808 | return inputMarker.paragraphRange().platformData().autorelease(); |
| 2809 | default: |
| 2810 | AX_ASSERT_NOT_REACHED()((void)0); |
| 2811 | return nilnullptr; |
| 2812 | } |
| 2813 | } |
| 2814 | return Accessibility::retrieveAutoreleasedValueFromMainThread<AXTextMarkerRangeRef>([textMarker = retainPtr(textMarker), &textUnit, protectedSelf = retainPtr(self)] () -> RetainPtr<AXTextMarkerRangeRef> { |
| 2815 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 2816 | if (!backingObject) |
| 2817 | return nilnullptr; |
| 2818 | |
| 2819 | CheckedPtr cache = backingObject->axObjectCache(); |
| 2820 | if (!cache) |
| 2821 | return nilnullptr; |
| 2822 | |
| 2823 | AXTextMarker marker { textMarker.get() }; |
| 2824 | std::optional<SimpleRange> range; |
| 2825 | switch (textUnit) { |
| 2826 | case TextUnit::LeftWord: |
| 2827 | range = cache->leftWordRange(marker); |
| 2828 | break; |
| 2829 | case TextUnit::RightWord: |
| 2830 | range = cache->rightWordRange(marker); |
| 2831 | break; |
| 2832 | case TextUnit::Sentence: |
| 2833 | range = cache->sentenceForCharacterOffset(marker); |
| 2834 | break; |
| 2835 | case TextUnit::Paragraph: |
| 2836 | range = cache->paragraphForCharacterOffset(marker); |
| 2837 | break; |
| 2838 | default: |
| 2839 | AX_ASSERT_NOT_REACHED()((void)0); |
| 2840 | break; |
| 2841 | } |
| 2842 | |
| 2843 | return textMarkerRangeFromRange(cache.get(), range); |
| 2844 | }); |
| 2845 | } |
| 2846 | |
| 2847 | - (id)lineTextMarkerRangeForTextMarker:(AXTextMarkerRef)textMarker forUnit:(TextUnit)textUnit |
| 2848 | { |
| 2849 | if (AXObjectCache::useAXThreadTextApis()) { |
| 2850 | auto rangeType = LineRangeType::Current; |
| 2851 | switch (textUnit) { |
| 2852 | case TextUnit::Line: |
| 2853 | break; |
| 2854 | case TextUnit::LeftLine: |
| 2855 | rangeType = LineRangeType::Left; |
| 2856 | break; |
| 2857 | case TextUnit::RightLine: |
| 2858 | rangeType = LineRangeType::Right; |
| 2859 | break; |
| 2860 | default: |
| 2861 | AX_ASSERT_NOT_REACHED()((void)0); |
| 2862 | break; |
| 2863 | } |
| 2864 | return AXTextMarker { textMarker }.lineRange(rangeType).platformData().bridgingAutorelease(); |
| 2865 | } |
| 2866 | |
| 2867 | return (id)Accessibility::retrieveAutoreleasedValueFromMainThread<AXTextMarkerRangeRef>([textMarker = retainPtr(textMarker), &textUnit, protectedSelf = retainPtr(self)] () -> RetainPtr<AXTextMarkerRangeRef> { |
| 2868 | RefPtr<AXCoreObject> backingObject = protectedSelf.get().axBackingObject; |
| 2869 | if (!backingObject) |
| 2870 | return nilnullptr; |
| 2871 | |
| 2872 | AXTextMarker marker { textMarker.get() }; |
| 2873 | VisiblePositionRange visiblePositionRange; |
| 2874 | switch (textUnit) { |
| 2875 | case TextUnit::Line: |
| 2876 | visiblePositionRange = backingObject->lineRangeForPosition(marker); |
| 2877 | break; |
| 2878 | case TextUnit::LeftLine: |
| 2879 | visiblePositionRange = backingObject->leftLineVisiblePositionRange(marker); |
| 2880 | break; |
| 2881 | case TextUnit::RightLine: |
| 2882 | visiblePositionRange = backingObject->rightLineVisiblePositionRange(marker); |
| 2883 | break; |
| 2884 | default: |
| 2885 | AX_ASSERT_NOT_REACHED()((void)0); |
| 2886 | break; |
| 2887 | } |
| 2888 | |
| 2889 | return AXTextMarkerRange(visiblePositionRange).platformData(); |
| 2890 | }); |
| 2891 | } |
| 2892 | |
| 2893 | - (AXTextMarkerRef)textMarkerForTextMarker:(AXTextMarkerRef)textMarkerRef atUnit:(TextUnit)textUnit |
| 2894 | { |
| 2895 | if (AXObjectCache::useAXThreadTextApis()) { |
| 2896 | AXTextMarker inputMarker { textMarkerRef }; |
| 2897 | switch (textUnit) { |
| 2898 | case TextUnit::NextSentenceEnd: |
| 2899 | return inputMarker.nextSentenceEnd().platformData().autorelease(); |
| 2900 | case TextUnit::PreviousSentenceStart: |
| 2901 | return inputMarker.previousSentenceStart().platformData().autorelease(); |
| 2902 | case TextUnit::NextParagraphEnd: |
| 2903 | return inputMarker.nextParagraphEnd().platformData().autorelease(); |
| 2904 | case TextUnit::PreviousParagraphStart: |
| 2905 | return inputMarker.previousParagraphStart().platformData().autorelease(); |
| 2906 | case TextUnit::NextWordEnd: |
| 2907 | return inputMarker.nextWordEnd().platformData().autorelease(); |
| 2908 | case TextUnit::PreviousWordStart: |
| 2909 | return inputMarker.previousWordStart().platformData().autorelease(); |
| 2910 | default: |
| 2911 | // TODO: Not implemented! |
| 2912 | break; |
| 2913 | } |
| 2914 | } |
| 2915 | return Accessibility::retrieveAutoreleasedValueFromMainThread<AXTextMarkerRef>([textMarkerRef = retainPtr(textMarkerRef), &textUnit, protectedSelf = retainPtr(self)] () -> RetainPtr<AXTextMarkerRef> { |
| 2916 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 2917 | if (!backingObject) |
| 2918 | return nilnullptr; |
| 2919 | |
| 2920 | CheckedPtr cache = backingObject->axObjectCache(); |
| 2921 | if (!cache) |
| 2922 | return nilnullptr; |
| 2923 | |
| 2924 | AXTextMarker oldMarker { textMarkerRef.get() }; |
| 2925 | AXTextMarker newMarker; |
| 2926 | switch (textUnit) { |
| 2927 | case TextUnit::NextWordEnd: |
| 2928 | newMarker = cache->nextWordEndCharacterOffset(oldMarker); |
| 2929 | break; |
| 2930 | case TextUnit::PreviousWordStart: |
| 2931 | newMarker = cache->previousWordStartCharacterOffset(oldMarker); |
| 2932 | break; |
| 2933 | case TextUnit::NextSentenceEnd: |
| 2934 | newMarker = cache->nextSentenceEndCharacterOffset(oldMarker); |
| 2935 | break; |
| 2936 | case TextUnit::PreviousSentenceStart: |
| 2937 | newMarker = cache->previousSentenceStartCharacterOffset(oldMarker); |
| 2938 | break; |
| 2939 | case TextUnit::NextParagraphEnd: |
| 2940 | newMarker = cache->nextParagraphEndCharacterOffset(oldMarker); |
| 2941 | break; |
| 2942 | case TextUnit::PreviousParagraphStart: |
| 2943 | newMarker = cache->previousParagraphStartCharacterOffset(oldMarker); |
| 2944 | break; |
| 2945 | case TextUnit::NextLineEnd: |
| 2946 | return textMarkerForVisiblePosition(cache.get(), backingObject->nextLineEndPosition(oldMarker)); |
| 2947 | case TextUnit::PreviousLineStart: |
| 2948 | return textMarkerForVisiblePosition(cache.get(), backingObject->previousLineStartPosition(oldMarker)); |
| 2949 | default: |
| 2950 | AX_ASSERT_NOT_REACHED()((void)0); |
| 2951 | break; |
| 2952 | } |
| 2953 | |
| 2954 | return newMarker.platformData(); |
| 2955 | }); |
| 2956 | } |
| 2957 | |
| 2958 | static bool isMatchingPlugin(AXCoreObject& axObject, const AccessibilitySearchCriteria& criteria) |
| 2959 | { |
| 2960 | if (!axObject.isPlugin()) |
| 2961 | return false; |
| 2962 | |
| 2963 | return criteria.searchKeys.contains(AccessibilitySearchKey::AnyType) |
| 2964 | && (!criteria.visibleOnly || axObject.isVisible()); |
| 2965 | } |
| 2966 | |
| 2967 | - (NSRect)computeTextBoundsForRange:(NSRange)range backingObject:(const AXCoreObject&)backingObject |
| 2968 | { |
| 2969 | if (!isMainThread()) { |
| 2970 | std::optional markerRange = Accessibility::markerRangeFrom(range, backingObject); |
| 2971 | return markerRange ? static_cast<CGRect>(markerRange->viewportRelativeFrame()) : CGRectZero; |
| 2972 | } |
| 2973 | |
| 2974 | auto start = backingObject.visiblePositionForIndex(range.location); |
| 2975 | auto end = backingObject.visiblePositionForIndex(range.location + range.length); |
| 2976 | auto webRange = makeSimpleRange({ start, end }); |
| 2977 | if (!webRange) |
| 2978 | return CGRectZero; |
| 2979 | |
| 2980 | return FloatRect(backingObject.boundsForRange(*webRange)); |
| 2981 | } |
| 2982 | |
| 2983 | ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-implementations" |
| 2984 | - (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter |
| 2985 | { |
| 2986 | AXTRACE(makeString("WebAccessibilityObjectWrapper accessibilityAttributeValue:"_s, String(attribute)))(void)0; |
| 2987 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 2988 | if (!backingObject) |
| 2989 | return nilnullptr; |
| 2990 | |
| 2991 | // Basic parameter validation. |
| 2992 | if (!attribute || !parameter) |
| 2993 | return nilnullptr; |
| 2994 | |
| 2995 | AXTextMarkerRef textMarker = nilnullptr; |
| 2996 | AXTextMarkerRangeRef textMarkerRange = nilnullptr; |
| 2997 | NSNumber *number = nilnullptr; |
| 2998 | NSArray *array = nilnullptr; |
| 2999 | NSDictionary *dictionary = nilnullptr; |
| 3000 | RefPtr<AXCoreObject> uiElement; |
| 3001 | NSPoint point = NSZeroPoint; |
| 3002 | bool pointSet = false; |
| 3003 | NSRange range = { 0, 0 }; |
| 3004 | bool rangeSet = false; |
| 3005 | NSRect rect = NSZeroRect; |
| 3006 | |
| 3007 | // common parameter type check/casting. Nil checks in handlers catch wrong type case. |
| 3008 | // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from |
| 3009 | // a parameter of the wrong type. |
| 3010 | if (AXObjectIsTextMarker(parameter)) |
| 3011 | textMarker = (AXTextMarkerRef)parameter; |
| 3012 | else if (AXObjectIsTextMarkerRange(parameter)) |
| 3013 | textMarkerRange = (AXTextMarkerRangeRef)parameter; |
| 3014 | else if ([parameter isKindOfClass:[WebAccessibilityObjectWrapper class]]) { |
| 3015 | uiElement = [(WebAccessibilityObjectWrapper*)parameter axBackingObject]; |
| 3016 | // The parameter wrapper object has lost its AX object since being given to the client, so bail early. |
| 3017 | if (!uiElement) |
| 3018 | return nilnullptr; |
| 3019 | } else if ([parameter isKindOfClass:[NSNumber class]]) |
| 3020 | number = parameter; |
| 3021 | else if ([parameter isKindOfClass:[NSArray class]]) |
| 3022 | array = parameter; |
| 3023 | else if ([parameter isKindOfClass:[NSDictionary class]]) |
| 3024 | dictionary = parameter; |
| 3025 | else if ([parameter isKindOfClass:[NSValue class]] && nsValueHasObjCType<NSPoint>((NSValue*)parameter)) { |
| 3026 | pointSet = true; |
| 3027 | point = [(NSValue*)parameter pointValue]; |
| 3028 | } else if ([parameter isKindOfClass:[NSValue class]] && nsValueHasObjCType<NSRange>((NSValue*)parameter)) { |
| 3029 | rangeSet = true; |
| 3030 | range = [(NSValue*)parameter rangeValue]; |
| 3031 | } else if ([parameter isKindOfClass:[NSValue class]] && nsValueHasObjCType<NSRect>((NSValue*)parameter)) |
| 3032 | rect = [(NSValue*)parameter rectValue]; |
| 3033 | else { |
| 3034 | // Attribute type is not supported. Allow super to handle. |
| 3035 | return [super accessibilityAttributeValue:attribute forParameter:parameter]; |
| 3036 | } |
| 3037 | |
| 3038 | // dispatch |
| 3039 | if ([attribute isEqualToString:NSAccessibilitySelectTextWithCriteriaParameterizedAttribute@"AXSelectTextWithCriteria"]) { |
| 3040 | // To be deprecated. |
| 3041 | auto result = Accessibility::retrieveValueFromMainThread<Vector<String>>([protectedDictionary = retainPtr(dictionary), protectedSelf = retainPtr(self)] () -> Vector<String> { |
| 3042 | RefPtr<AXCoreObject> backingObject = protectedSelf.get().axBackingObject; |
| 3043 | if (!backingObject) |
| 3044 | return Vector<String>(); |
| 3045 | |
| 3046 | auto criteria = accessibilityTextCriteriaForParameterizedAttribute(protectedDictionary.get()); |
| 3047 | criteria.second.textRanges = backingObject->findTextRanges(criteria.first); |
| 3048 | AX_ASSERT(criteria.second.textRanges.size() <= 1)((void)0); |
| 3049 | return backingObject->performTextOperation(criteria.second); |
| 3050 | }); |
| 3051 | AX_ASSERT(result.size() <= 1)((void)0); |
| 3052 | if (result.size() > 0) |
| 3053 | return result[0].createNSString().autorelease(); |
| 3054 | return @""; |
| 3055 | } |
| 3056 | |
| 3057 | if ([attribute isEqualToString:NSAccessibilitySearchTextWithCriteriaParameterizedAttribute@"AXSearchTextWithCriteria"]) { |
| 3058 | auto criteria = accessibilitySearchTextCriteriaForParameterizedAttribute(dictionary); |
| 3059 | return Accessibility::retrieveAutoreleasedValueFromMainThread<NSArray *>([&criteria, protectedSelf = retainPtr(self)] () -> RetainPtr<NSArray> { |
| 3060 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 3061 | if (!backingObject) |
| 3062 | return nilnullptr; |
| 3063 | auto ranges = backingObject->findTextRanges(criteria); |
| 3064 | if (ranges.isEmpty()) |
| 3065 | return nilnullptr; |
| 3066 | CheckedPtr cache = backingObject->axObjectCache(); |
| 3067 | return createNSArray(WTF::move(ranges), [&] (SimpleRange&& range) { |
| 3068 | return (id)textMarkerRangeFromRange(cache.get(), WTF::move(range)); |
| 3069 | }).autorelease(); |
| 3070 | }); |
| 3071 | } |
| 3072 | |
| 3073 | if ([attribute isEqualToString:NSAccessibilityTextOperationParameterizedAttribute@"AXTextOperation"]) { |
| 3074 | auto operationResult = Accessibility::retrieveValueFromMainThread<Vector<String>>([protectedDictionary = retainPtr(dictionary), protectedSelf = retainPtr(self)] () -> Vector<String> { |
| 3075 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 3076 | if (!backingObject) |
| 3077 | return Vector<String>(); |
| 3078 | |
| 3079 | CheckedPtr cache = backingObject->axObjectCache(); |
| 3080 | auto textOperation = accessibilityTextOperationForParameterizedAttribute(cache.get(), protectedDictionary.get()); |
| 3081 | return backingObject->performTextOperation(textOperation); |
| 3082 | }); |
| 3083 | if (operationResult.isEmpty()) |
| 3084 | return nilnullptr; |
| 3085 | return createNSArray(operationResult).autorelease(); |
| 3086 | } |
| 3087 | |
| 3088 | if ([attribute isEqualToString:NSAccessibilityRangesForSearchPredicateParameterizedAttribute@"AXRangesForSearchPredicate"]) { |
| 3089 | auto criteria = accessibilitySearchCriteriaForSearchPredicate(*backingObject, dictionary); |
| 3090 | if (criteria.searchKeys.size() == 1 && criteria.searchKeys[0] == AccessibilitySearchKey::MisspelledWord) { |
| 3091 | // Request for the next/previous misspelling. |
| 3092 | auto textMarkerRange = AXSearchManager().findMatchingRange(WTF::move(criteria)); |
| 3093 | if (!textMarkerRange) |
| 3094 | return nilnullptr; |
| 3095 | |
| 3096 | RefPtr object = textMarkerRange->start().object(); |
| 3097 | if (!object) |
| 3098 | return nilnullptr; |
| 3099 | |
| 3100 | RetainPtr result = adoptNS([[NSMutableDictionary alloc] initWithObjectsAndKeys: |
| 3101 | protect(object->wrapper()).get(), NSAccessibilitySearchResultElementKey@"AXSearchResultElement", |
| 3102 | textMarkerRange->platformData().bridgingAutorelease(), NSAccessibilitySearchResultRangeKey@"AXSearchResultRange", |
| 3103 | nilnullptr]); |
| 3104 | return [[[NSArray alloc] initWithObjects:result.get(), nilnullptr] autorelease]; |
| 3105 | } |
| 3106 | } |
| 3107 | |
| 3108 | if ([attribute isEqualToString:NSAccessibilityUIElementsForSearchPredicateParameterizedAttribute@"AXUIElementsForSearchPredicate"]) { |
| 3109 | auto criteria = accessibilitySearchCriteriaForSearchPredicate(*backingObject, dictionary); |
| 3110 | RetainPtr<NSArray> widgetChildren; |
| 3111 | if (isMatchingPlugin(*backingObject, criteria)) { |
| 3112 | // FIXME: We should also be searching the tree(s) resulting from `renderWidgetChildren` for matches. |
| 3113 | // This is tracked by https://bugs.webkit.org/show_bug.cgi?id=230167. |
| 3114 | if (RetainPtr children = renderWidgetChildren(*backingObject)) { |
| 3115 | NSUInteger includedChildrenCount = std::min([children count], NSUInteger(criteria.resultsLimit)); |
| 3116 | widgetChildren = [children subarrayWithRange:NSMakeRange(0, includedChildrenCount)]; |
| 3117 | if ([widgetChildren count] >= criteria.resultsLimit) |
| 3118 | return widgetChildren.autorelease(); |
| 3119 | criteria.resultsLimit -= [widgetChildren count]; |
| 3120 | } |
| 3121 | } else if (backingObject->isRemoteFrame() |
| 3122 | && criteria.searchKeys.contains(AccessibilitySearchKey::AnyType) |
| 3123 | && (!criteria.visibleOnly || backingObject->isVisible())) { |
| 3124 | RetainPtr remoteFrameChildren = children(*backingObject); |
| 3125 | AX_ASSERT([remoteFrameChildren count] == 1)((void)0); |
| 3126 | if ([remoteFrameChildren count] == 1) { |
| 3127 | NSUInteger includedChildrenCount = std::min([remoteFrameChildren count], NSUInteger(criteria.resultsLimit)); |
| 3128 | widgetChildren = [remoteFrameChildren subarrayWithRange:NSMakeRange(0, includedChildrenCount)]; |
| 3129 | if ([widgetChildren count] >= criteria.resultsLimit) |
| 3130 | return remoteFrameChildren.autorelease(); |
| 3131 | criteria.resultsLimit -= [widgetChildren count]; |
| 3132 | } |
| 3133 | } |
| 3134 | |
| 3135 | RetainPtr<NSArray> results = performSearchWithRemoteFrames(*backingObject, WTF::move(criteria)); |
| 3136 | if (widgetChildren) |
| 3137 | return [widgetChildren arrayByAddingObjectsFromArray:results.get()]; |
| 3138 | return results.autorelease(); |
| 3139 | } |
| 3140 | |
| 3141 | // TextMarker attributes. |
| 3142 | |
| 3143 | if ([attribute isEqualToString:NSAccessibilityEndTextMarkerForBoundsAttribute@"AXEndTextMarkerForBounds"]) { |
| 3144 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([&rect, protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 3145 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 3146 | CheckedPtr cache = backingObject ? backingObject->axObjectCache() : nullptr; |
| 3147 | if (!cache) |
| 3148 | return nilnullptr; |
| 3149 | |
| 3150 | IntRect webCoreRect = screenToContents(*backingObject, enclosingIntRect(rect)); |
| 3151 | CharacterOffset characterOffset = cache->characterOffsetForBounds(webCoreRect, false); |
| 3152 | |
| 3153 | return (id)textMarkerForCharacterOffset(cache.get(), characterOffset, TextMarkerOrigin::EndTextMarkerForBounds); |
| 3154 | }); |
| 3155 | } |
| 3156 | |
| 3157 | if ([attribute isEqualToString:NSAccessibilityStartTextMarkerForBoundsAttribute@"AXStartTextMarkerForBounds"]) { |
| 3158 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([&rect, protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 3159 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 3160 | CheckedPtr cache = backingObject ? backingObject->axObjectCache() : nullptr; |
| 3161 | if (!cache) |
| 3162 | return nilnullptr; |
| 3163 | |
| 3164 | IntRect webCoreRect = screenToContents(*backingObject, enclosingIntRect(rect)); |
| 3165 | CharacterOffset characterOffset = cache->characterOffsetForBounds(webCoreRect, true); |
| 3166 | |
| 3167 | return (id)textMarkerForCharacterOffset(cache.get(), characterOffset, TextMarkerOrigin::StartTextMarkerForBounds); |
| 3168 | }); |
| 3169 | } |
| 3170 | |
| 3171 | // TextMarkerRange attributes. |
| 3172 | if ([attribute isEqualToString:NSAccessibilityLineTextMarkerRangeForTextMarkerAttribute@"AXLineTextMarkerRangeForTextMarker"]) |
| 3173 | return [self lineTextMarkerRangeForTextMarker:textMarker forUnit:TextUnit::Line]; |
| 3174 | |
| 3175 | if ([attribute isEqualToString:NSAccessibilityMisspellingTextMarkerRangeAttribute@"AXMisspellingTextMarkerRange"]) { |
| 3176 | return (id)Accessibility::retrieveAutoreleasedValueFromMainThread<AXTextMarkerRangeRef>([protectedDictionary = retainPtr(dictionary), protectedSelf = retainPtr(self)] () -> RetainPtr<AXTextMarkerRangeRef> { |
| 3177 | RefPtr<AXCoreObject> backingObject = protectedSelf.get().axBackingObject; |
| 3178 | if (!backingObject) |
| 3179 | return nilnullptr; |
| 3180 | |
| 3181 | auto criteria = misspellingSearchCriteriaForParameterizedAttribute(protectedDictionary.get()); |
| 3182 | if (!criteria.first) |
| 3183 | return nilnullptr; |
| 3184 | |
| 3185 | auto characterRange = criteria.first.characterRange(); |
| 3186 | if (!characterRange) |
| 3187 | return nilnullptr; |
| 3188 | |
| 3189 | RefPtr startObject = criteria.second == AccessibilitySearchDirection::Next ? criteria.first.end().object() : criteria.first.start().object(); |
| 3190 | auto misspellingRange = AXSearchManager().findMatchingRange(AccessibilitySearchCriteria { |
| 3191 | backingObject.get(), startObject.get(), *characterRange, |
| 3192 | criteria.second, |
| 3193 | { AccessibilitySearchKey::MisspelledWord }, { }, 1 |
| 3194 | }); |
| 3195 | if (!misspellingRange) |
| 3196 | return nilnullptr; |
| 3197 | return misspellingRange->platformData(); |
| 3198 | }); |
| 3199 | } |
| 3200 | |
| 3201 | if ([attribute isEqualToString:NSAccessibilityTextMarkerIsValidAttribute@"AXTextMarkerIsValid"]) |
| 3202 | return [NSNumber numberWithBool:AXTextMarker(textMarker).isValid()]; |
| 3203 | |
| 3204 | if ([attribute isEqualToString:NSAccessibilityIndexForTextMarkerAttribute@"AXIndexForTextMarker"]) { |
| 3205 | auto marker = AXTextMarker { textMarker }; |
| 3206 | if (!isMainThread()) |
| 3207 | return [NSNumber numberWithUnsignedInt:marker.offsetFromRoot()]; |
| 3208 | if (!marker.isValid()) |
| 3209 | return @(NSNotFound); |
| 3210 | long markerLocation = makeNSRange(AXTextMarkerRange { marker, marker }.simpleRange()).location; |
| 3211 | return [NSNumber numberWithInteger:markerLocation]; |
| 3212 | } |
| 3213 | |
| 3214 | if ([attribute isEqualToString:NSAccessibilityTextMarkerForIndexAttribute@"AXTextMarkerForIndex"]) { |
| 3215 | if (AXObjectCache::useAXThreadTextApis()) { |
| 3216 | long index = [number longValue]; |
| 3217 | if (index < 0) |
| 3218 | return nilnullptr; |
| 3219 | |
| 3220 | RefPtr tree = std::get<RefPtr<AXIsolatedTree>>(axTreeForID(backingObject->treeID())); |
| 3221 | if (RefPtr root = tree ? tree->rootNode() : nullptr) { |
| 3222 | AXTextMarker rootMarker { root->treeID(), root->objectID(), 0 }; |
| 3223 | return rootMarker.nextMarkerFromOffset(static_cast<unsigned>(index)).platformData().bridgingAutorelease(); |
| 3224 | } |
| 3225 | return nilnullptr; |
| 3226 | } |
| 3227 | return (id)[self _textMarkerForIndex:[number integerValue]]; |
| 3228 | } |
| 3229 | |
| 3230 | if ([attribute isEqualToString:NSAccessibilityUIElementForTextMarkerAttribute@"AXUIElementForTextMarker"]) { |
| 3231 | AXTextMarker marker { textMarker }; |
| 3232 | RefPtr object = marker.object(); |
| 3233 | if (!object) |
| 3234 | return nilnullptr; |
| 3235 | |
| 3236 | RetainPtr wrapper = object->wrapper(); |
| 3237 | if (!wrapper) |
| 3238 | return nilnullptr; |
| 3239 | |
| 3240 | if (object->isAttachment()) { |
| 3241 | if (RetainPtr<id> attachmentView = [wrapper attachmentView]) |
| 3242 | return attachmentView.autorelease(); |
| 3243 | } |
| 3244 | return wrapper.autorelease(); |
| 3245 | } |
| 3246 | |
| 3247 | if ([attribute isEqualToString:NSAccessibilityTextMarkerRangeForUIElementAttribute@"AXTextMarkerRangeForUIElement"]) { |
| 3248 | if (uiElement) { |
| 3249 | if (auto markerRange = uiElement->textMarkerRange()) |
| 3250 | return markerRange.platformData().bridgingAutorelease(); |
| 3251 | } |
| 3252 | return nilnullptr; |
| 3253 | } |
| 3254 | |
| 3255 | if ([attribute isEqualToString:NSAccessibilityLineForTextMarkerAttribute@"AXLineForTextMarker"]) { |
| 3256 | if (!isMainThread()) |
| 3257 | return @(AXTextMarker { textMarker }.lineIndex()); |
| 3258 | |
| 3259 | RefPtr<AXCoreObject> backingObject = self.axBackingObject; |
| 3260 | if (!backingObject) |
| 3261 | return @(-1); |
| 3262 | return @(backingObject->lineForPosition(AXTextMarker { textMarker })); |
| 3263 | } |
| 3264 | |
| 3265 | if ([attribute isEqualToString:NSAccessibilityTextMarkerRangeForLineAttribute@"AXTextMarkerRangeForLine"]) { |
| 3266 | if (AXObjectCache::useAXThreadTextApis()) { |
| 3267 | // Unfortunately, the main-thread version of this function expects a 1-indexed line, so callers pass it that way. |
| 3268 | // The text marker code expects normal 0-based indices, so we need to subtract one from the given value. |
| 3269 | unsigned lineIndex = [number unsignedIntValue]; |
| 3270 | if (!lineIndex) { |
| 3271 | // Match the main-thread implementation's behavior. |
| 3272 | return nilnullptr; |
| 3273 | } |
| 3274 | if (RefPtr tree = std::get<RefPtr<AXIsolatedTree>>(axTreeForID(backingObject->treeID()))) |
| 3275 | return tree->firstMarker().markerRangeForLineIndex(lineIndex - 1).platformData().bridgingAutorelease(); |
| 3276 | return nilnullptr; |
| 3277 | } |
| 3278 | |
| 3279 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([protectedNumber = retainPtr(number), protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 3280 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 3281 | if (!backingObject) |
| 3282 | return nilnullptr; |
| 3283 | |
| 3284 | VisiblePositionRange vpRange; |
| 3285 | if ([protectedNumber.get() unsignedIntegerValue] != NSNotFound) |
| 3286 | vpRange = backingObject->visiblePositionRangeForLine([protectedNumber.get() unsignedIntValue]); |
| 3287 | |
| 3288 | CheckedPtr cache = backingObject->axObjectCache(); |
| 3289 | return (id)textMarkerRangeFromVisiblePositions(cache.get(), vpRange.start, vpRange.end); |
| 3290 | }); |
| 3291 | } |
| 3292 | |
| 3293 | if ([attribute isEqualToString:NSAccessibilityStringForTextMarkerRangeAttribute@"AXStringForTextMarkerRange"]) |
| 3294 | return AXTextMarkerRange { textMarkerRange }.toString().createNSString().autorelease(); |
| 3295 | |
| 3296 | if ([attribute isEqualToString:NSAccessibilityTextMarkerForPositionAttribute@"AXTextMarkerForPosition"]) { |
| 3297 | if (!pointSet) |
| 3298 | return nilnullptr; |
| 3299 | IntPoint webCorePoint = IntPoint(point); |
| 3300 | |
| 3301 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([&webCorePoint, protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 3302 | RefPtr<AXCoreObject> backingObject = protectedSelf.get().axBackingObject; |
| 3303 | if (!backingObject) |
| 3304 | return nilnullptr; |
| 3305 | |
| 3306 | return AXTextMarker(backingObject->visiblePositionForPoint(webCorePoint), TextMarkerOrigin::Position).platformData().bridgingAutorelease(); |
| 3307 | }); |
| 3308 | } |
| 3309 | |
| 3310 | if ([attribute isEqualToString:NSAccessibilityBoundsForTextMarkerRangeAttribute@"AXBoundsForTextMarkerRange"]) { |
| 3311 | if (!isMainThread()) { |
| 3312 | AXTextMarkerRange markerRange { textMarkerRange }; |
| 3313 | if (!markerRange) |
| 3314 | return [NSValue valueWithRect:CGRectZero]; |
| 3315 | |
| 3316 | return [NSValue valueWithRect:[self convertRectToSpace:markerRange.viewportRelativeFrame() space:AccessibilityConversionSpace::Screen]]; |
| 3317 | } |
| 3318 | |
| 3319 | RefPtr<AXCoreObject> backingObject = self.axBackingObject; |
| 3320 | if (!backingObject) |
| 3321 | return [NSValue valueWithRect:CGRectZero]; |
| 3322 | |
| 3323 | AXTextMarkerRange markerRange { textMarkerRange }; |
| 3324 | std::optional range = markerRange.simpleRange(); |
| 3325 | if (!range) |
| 3326 | return [NSValue valueWithRect:CGRectZero]; |
| 3327 | |
| 3328 | auto bounds = FloatRect(backingObject->boundsForRange(*range)); |
| 3329 | auto convertedRect = [self convertRectToSpace:bounds space:AccessibilityConversionSpace::Screen]; |
| 3330 | return [NSValue valueWithRect:convertedRect]; |
| 3331 | } |
| 3332 | |
| 3333 | if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) { |
| 3334 | NSRect rect = CGRectZero; |
| 3335 | if (backingObject) |
| 3336 | rect = [self computeTextBoundsForRange:range backingObject:*backingObject]; |
| 3337 | |
| 3338 | return [NSValue valueWithRect:[self convertRectToSpace:rect space:AccessibilityConversionSpace::Screen]]; |
| 3339 | } |
| 3340 | |
| 3341 | if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute]) { |
| 3342 | if (backingObject->isTextControl()) |
| 3343 | return backingObject->doAXStringForRange(range).createNSString().autorelease(); |
| 3344 | |
| 3345 | if (!isMainThread()) { |
| 3346 | std::optional markerRange = Accessibility::markerRangeFrom(range, *backingObject); |
| 3347 | return markerRange ? markerRange->toString().createNSString().autorelease() : @""; |
| 3348 | } |
| 3349 | |
| 3350 | RefPtr mainThreadBackingObject = downcast<AccessibilityObject>(self.axBackingObject); |
| 3351 | if (!mainThreadBackingObject) |
| 3352 | return @""; |
| 3353 | CheckedPtr cache = mainThreadBackingObject->axObjectCache(); |
| 3354 | if (!cache) |
| 3355 | return @""; |
| 3356 | |
| 3357 | auto start = cache->characterOffsetForIndex(range.location, mainThreadBackingObject.get()); |
| 3358 | auto end = cache->characterOffsetForIndex(range.location + range.length, mainThreadBackingObject.get()); |
| 3359 | auto characterRange = cache->rangeForUnorderedCharacterOffsets(start, end); |
| 3360 | return AXTextMarkerRange { characterRange }.toString().createNSString().autorelease(); |
| 3361 | } |
| 3362 | |
| 3363 | if ([attribute isEqualToString:NSAccessibilityAttributedStringForTextMarkerRangeAttribute@"AXAttributedStringForTextMarkerRange"]) |
| 3364 | return attributedStringForTextMarkerRange(*backingObject, textMarkerRange, AXCoreObject::SpellCheck::Yes); |
| 3365 | |
| 3366 | if ([attribute isEqualToString:NSAccessibilityAttributedStringForTextMarkerRangeWithOptionsAttribute@"AXAttributedStringForTextMarkerRangeWithOptions"]) { |
| 3367 | if (textMarkerRange) |
| 3368 | return attributedStringForTextMarkerRange(*backingObject, textMarkerRange, AXCoreObject::SpellCheck::No); |
| 3369 | |
| 3370 | if (dictionary) { |
| 3371 | RetainPtr<AXTextMarkerRangeRef> textMarkerRange = nilnullptr; |
| 3372 | RetainPtr<id> parameter = [dictionary objectForKey:NSAccessibilityTextMarkerRangeParam@"AXTextMarkerRange"]; |
| 3373 | if (AXObjectIsTextMarkerRange(parameter.get())) |
| 3374 | textMarkerRange = (AXTextMarkerRangeRef)parameter.get(); |
| 3375 | |
| 3376 | auto spellCheck = AXCoreObject::SpellCheck::No; |
| 3377 | parameter = [dictionary objectForKey:NSAccessibilitySpellCheckParam@"AXSpellCheck"]; |
| 3378 | if ([parameter isKindOfClass:[NSNumber class]] && [parameter boolValue]) |
| 3379 | spellCheck = AXCoreObject::SpellCheck::Yes; |
| 3380 | return attributedStringForTextMarkerRange(*backingObject, textMarkerRange.get(), spellCheck); |
| 3381 | } |
| 3382 | |
| 3383 | return nilnullptr; |
| 3384 | } |
| 3385 | |
| 3386 | if ([attribute isEqualToString:NSAccessibilityTextMarkerRangeForTextMarkersAttribute@"AXTextMarkerRangeForTextMarkers"] |
| 3387 | || [attribute isEqualToString:NSAccessibilityTextMarkerRangeForUnorderedTextMarkersAttribute@"AXTextMarkerRangeForUnorderedTextMarkers"]) { |
| 3388 | if (array.count < 2 |
| 3389 | || !AXObjectIsTextMarker([array objectAtIndex:0]) |
| 3390 | || !AXObjectIsTextMarker([array objectAtIndex:1])) |
| 3391 | return nilnullptr; |
| 3392 | |
| 3393 | return AXTextMarkerRange { { (AXTextMarkerRef)[array objectAtIndex:0] }, { (AXTextMarkerRef)[array objectAtIndex:1] } }.platformData().bridgingAutorelease(); |
| 3394 | } |
| 3395 | |
| 3396 | if ([attribute isEqualToString:NSAccessibilityNextTextMarkerForTextMarkerAttribute@"AXNextTextMarkerForTextMarker"]) { |
| 3397 | if (AXObjectCache::useAXThreadTextApis()) { |
| 3398 | AXTextMarker inputMarker { textMarker }; |
| 3399 | return inputMarker.findMarker(AXDirection::Next).platformData().bridgingAutorelease(); |
| 3400 | } |
| 3401 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([textMarker = retainPtr(textMarker), protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 3402 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 3403 | CheckedPtr cache = backingObject ? backingObject->axObjectCache() : nullptr; |
| 3404 | |
| 3405 | return nextTextMarker(cache.get(), AXTextMarker { textMarker.get() }).bridgingAutorelease(); |
| 3406 | }); |
| 3407 | } |
| 3408 | |
| 3409 | if ([attribute isEqualToString:NSAccessibilityPreviousTextMarkerForTextMarkerAttribute@"AXPreviousTextMarkerForTextMarker"]) { |
| 3410 | if (AXObjectCache::useAXThreadTextApis()) { |
| 3411 | AXTextMarker inputMarker { textMarker }; |
| 3412 | return inputMarker.findMarker(AXDirection::Previous).platformData().bridgingAutorelease(); |
| 3413 | } |
| 3414 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([textMarker = retainPtr(textMarker), protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 3415 | RefPtr backingObject = downcast<AccessibilityObject>(protectedSelf.get().axBackingObject); |
| 3416 | CheckedPtr cache = backingObject ? backingObject->axObjectCache() : nullptr; |
| 3417 | |
| 3418 | return previousTextMarker(cache.get(), AXTextMarker { textMarker.get() }).bridgingAutorelease(); |
| 3419 | }); |
| 3420 | } |
| 3421 | |
| 3422 | if ([attribute isEqualToString:NSAccessibilityLeftWordTextMarkerRangeForTextMarkerAttribute@"AXLeftWordTextMarkerRangeForTextMarker"]) |
| 3423 | return (id)[self textMarkerRangeAtTextMarker:textMarker forUnit:TextUnit::LeftWord]; |
| 3424 | |
| 3425 | if ([attribute isEqualToString:NSAccessibilityRightWordTextMarkerRangeForTextMarkerAttribute@"AXRightWordTextMarkerRangeForTextMarker"]) |
| 3426 | return (id)[self textMarkerRangeAtTextMarker:textMarker forUnit:TextUnit::RightWord]; |
| 3427 | |
| 3428 | if ([attribute isEqualToString:NSAccessibilityLeftLineTextMarkerRangeForTextMarkerAttribute@"AXLeftLineTextMarkerRangeForTextMarker"]) |
| 3429 | return [self lineTextMarkerRangeForTextMarker:textMarker forUnit:TextUnit::LeftLine]; |
| 3430 | |
| 3431 | if ([attribute isEqualToString:NSAccessibilityRightLineTextMarkerRangeForTextMarkerAttribute@"AXRightLineTextMarkerRangeForTextMarker"]) |
| 3432 | return [self lineTextMarkerRangeForTextMarker:textMarker forUnit:TextUnit::RightLine]; |
| 3433 | |
| 3434 | if ([attribute isEqualToString:NSAccessibilitySentenceTextMarkerRangeForTextMarkerAttribute@"AXSentenceTextMarkerRangeForTextMarker"]) |
| 3435 | return (id)[self textMarkerRangeAtTextMarker:textMarker forUnit:TextUnit::Sentence]; |
| 3436 | |
| 3437 | if ([attribute isEqualToString:NSAccessibilityParagraphTextMarkerRangeForTextMarkerAttribute@"AXParagraphTextMarkerRangeForTextMarker"]) |
| 3438 | return (id)[self textMarkerRangeAtTextMarker:textMarker forUnit:TextUnit::Paragraph]; |
| 3439 | |
| 3440 | if ([attribute isEqualToString:NSAccessibilityNextWordEndTextMarkerForTextMarkerAttribute@"AXNextWordEndTextMarkerForTextMarker"]) |
| 3441 | return (id)[self textMarkerForTextMarker:textMarker atUnit:TextUnit::NextWordEnd]; |
| 3442 | |
| 3443 | if ([attribute isEqualToString:NSAccessibilityPreviousWordStartTextMarkerForTextMarkerAttribute@"AXPreviousWordStartTextMarkerForTextMarker"]) |
| 3444 | return (id)[self textMarkerForTextMarker:textMarker atUnit:TextUnit::PreviousWordStart]; |
| 3445 | |
| 3446 | if ([attribute isEqualToString:NSAccessibilityNextLineEndTextMarkerForTextMarkerAttribute@"AXNextLineEndTextMarkerForTextMarker"]) { |
| 3447 | if (AXObjectCache::useAXThreadTextApis()) { |
| 3448 | AXTextMarker inputMarker { textMarker }; |
| 3449 | return inputMarker.nextLineEnd().platformData().bridgingAutorelease(); |
| 3450 | } |
| 3451 | return (id)[self textMarkerForTextMarker:textMarker atUnit:TextUnit::NextLineEnd]; |
| 3452 | } |
| 3453 | |
| 3454 | if ([attribute isEqualToString:NSAccessibilityPreviousLineStartTextMarkerForTextMarkerAttribute@"AXPreviousLineStartTextMarkerForTextMarker"]) { |
| 3455 | if (AXObjectCache::useAXThreadTextApis()) { |
| 3456 | AXTextMarker inputMarker { textMarker }; |
| 3457 | return inputMarker.previousLineStart().platformData().bridgingAutorelease(); |
| 3458 | } |
| 3459 | return (id)[self textMarkerForTextMarker:textMarker atUnit:TextUnit::PreviousLineStart]; |
| 3460 | } |
| 3461 | |
| 3462 | if ([attribute isEqualToString:NSAccessibilityNextSentenceEndTextMarkerForTextMarkerAttribute@"AXNextSentenceEndTextMarkerForTextMarker"]) |
| 3463 | return (id)[self textMarkerForTextMarker:textMarker atUnit:TextUnit::NextSentenceEnd]; |
| 3464 | |
| 3465 | if ([attribute isEqualToString:NSAccessibilityPreviousSentenceStartTextMarkerForTextMarkerAttribute@"AXPreviousSentenceStartTextMarkerForTextMarker"]) |
| 3466 | return (id)[self textMarkerForTextMarker:textMarker atUnit:TextUnit::PreviousSentenceStart]; |
| 3467 | |
| 3468 | if ([attribute isEqualToString:NSAccessibilityNextParagraphEndTextMarkerForTextMarkerAttribute@"AXNextParagraphEndTextMarkerForTextMarker"]) |
| 3469 | return (id)[self textMarkerForTextMarker:textMarker atUnit:TextUnit::NextParagraphEnd]; |
| 3470 | |
| 3471 | if ([attribute isEqualToString:NSAccessibilityPreviousParagraphStartTextMarkerForTextMarkerAttribute@"AXPreviousParagraphStartTextMarkerForTextMarker"]) |
| 3472 | return (id)[self textMarkerForTextMarker:textMarker atUnit:TextUnit::PreviousParagraphStart]; |
| 3473 | |
| 3474 | if ([attribute isEqualToString:NSAccessibilityStyleTextMarkerRangeForTextMarkerAttribute@"AXStyleTextMarkerRangeForTextMarker"]) { |
| 3475 | if (AXObjectCache::useAXThreadTextApis()) |
| 3476 | return AXTextMarker { textMarker }.rangeWithSameStyle().platformData().bridgingAutorelease(); |
| 3477 | |
| 3478 | return Accessibility::retrieveAutoreleasedValueFromMainThread<id>([textMarker = retainPtr(textMarker), protectedSelf = retainPtr(self)] () -> RetainPtr<id> { |
| 3479 | RefPtr<AXCoreObject> backingObject = protectedSelf.get().axBackingObject; |
| 3480 | if (!backingObject) |
| 3481 | return nilnullptr; |
| 3482 | return AXTextMarkerRange { backingObject->styleRangeForPosition(AXTextMarker { textMarker.get() }) }.platformData().bridgingAutorelease(); |
| 3483 | }); |
| 3484 | } |
| 3485 | |
| 3486 | if ([attribute isEqualToString:NSAccessibilityLengthForTextMarkerRangeAttribute@"AXLengthForTextMarkerRange"]) { |
| 3487 | if (!isMainThread()) { |
| 3488 | AXTextMarkerRange range = { textMarkerRange }; |
| 3489 | return @(range.toString().length()); |
| 3490 | } |
| 3491 | |
| 3492 | RefPtr<AXCoreObject> backingObject = self.axBackingObject; |
| 3493 | if (!backingObject) |
| 3494 | return @(0); |
| 3495 | |
| 3496 | AXTextMarkerRange markerRange { textMarkerRange }; |
| 3497 | auto range = markerRange.simpleRange(); |
| 3498 | return @(range ? AXObjectCache::lengthForRange(*range) : 0); |
| 3499 | } |
| 3500 | |
| 3501 | if ([attribute isEqualToString:NSAccessibilityIntersectTextMarkerRangesAttribute@"AXIntersectTextMarkerRanges"]) { |
| 3502 | if (array.count < 2 |
| 3503 | || !AXObjectIsTextMarkerRange([array objectAtIndex:0]) |
| 3504 | || !AXObjectIsTextMarkerRange([array objectAtIndex:1])) |
| 3505 | return nilnullptr; |
| 3506 | |
| 3507 | auto range1 = AXTextMarkerRange { (AXTextMarkerRangeRef)[array objectAtIndex:0] }; |
| 3508 | auto range2 = AXTextMarkerRange { (AXTextMarkerRangeRef)[array objectAtIndex:1] }; |
| 3509 | auto intersection = range1.intersectionWith(range2); |
| 3510 | return intersection ? (*intersection).platformData().bridgingAutorelease() : nilnullptr; |
| 3511 | } |
| 3512 | |
| 3513 | if (backingObject->isExposableTable()) { |
| 3514 | if ([attribute isEqualToString:NSAccessibilityCellForColumnAndRowParameterizedAttribute]) { |
| 3515 | if (array == nilnullptr || [array count] != 2) |
| 3516 | return nilnullptr; |
| 3517 | RefPtr cell = backingObject->cellForColumnAndRow([[array objectAtIndex:0] unsignedIntValue], [[array objectAtIndex:1] unsignedIntValue]); |
| 3518 | return cell ? cell->wrapper() : nilnullptr; |
| 3519 | } |
| 3520 | } |
| 3521 | |
| 3522 | if (backingObject->isTextControl()) { |
| 3523 | if ([attribute isEqualToString: bridge_cast(kAXLineForIndexParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXLineForIndex" "")))]) { |
| 3524 | int lineNumber = backingObject->doAXLineForIndex([number intValue]); |
| 3525 | if (lineNumber < 0) |
| 3526 | return nilnullptr; |
| 3527 | return @(lineNumber); |
| 3528 | } |
| 3529 | |
| 3530 | if ([attribute isEqualToString:bridge_cast(kAXRangeForLineParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXRangeForLine" "")))]) { |
| 3531 | auto textRange = backingObject->doAXRangeForLine([number intValue]); |
| 3532 | return [NSValue valueWithRange:textRange]; |
| 3533 | } |
| 3534 | |
| 3535 | if ([attribute isEqualToString:bridge_cast(kAXStringForRangeParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXStringForRange" "")))]) |
| 3536 | return rangeSet ? (id)backingObject->doAXStringForRange(range).createNSString().autorelease() : nilnullptr; |
| 3537 | |
| 3538 | if ([attribute isEqualToString:bridge_cast(kAXRangeForPositionParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXRangeForPosition" "")))]) { |
| 3539 | if (!pointSet) |
| 3540 | return nilnullptr; |
| 3541 | |
| 3542 | auto webCorePoint = IntPoint(point); |
| 3543 | auto textRange = backingObject->characterRangeForPoint(webCorePoint); |
| 3544 | return [NSValue valueWithRange:textRange]; |
| 3545 | } |
| 3546 | |
| 3547 | if ([attribute isEqualToString:bridge_cast(kAXRangeForIndexParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXRangeForIndex" "")))]) { |
| 3548 | auto textRange = backingObject->doAXRangeForIndex([number intValue]); |
| 3549 | return [NSValue valueWithRange:textRange]; |
| 3550 | } |
| 3551 | |
| 3552 | if ([attribute isEqualToString:bridge_cast(kAXBoundsForRangeParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXBoundsForRange" "")))]) { |
| 3553 | if (!rangeSet) |
| 3554 | return nilnullptr; |
| 3555 | |
| 3556 | auto bounds = FloatRect(backingObject->doAXBoundsForRangeUsingCharacterOffset(range)); |
| 3557 | NSRect rect = [self convertRectToSpace:bounds space:AccessibilityConversionSpace::Screen]; |
| 3558 | return [NSValue valueWithRect:rect]; |
| 3559 | } |
| 3560 | |
| 3561 | if ([attribute isEqualToString:bridge_cast(kAXRTFForRangeParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXRTFForRange" "")))]) |
| 3562 | return rangeSet ? rtfForNSRange(*backingObject, range) : nilnullptr; |
| 3563 | |
| 3564 | if ([attribute isEqualToString:bridge_cast(kAXAttributedStringForRangeParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXAttributedStringForRange" "")))]) |
| 3565 | return rangeSet ? attributedStringForNSRange(*backingObject, range) : nilnullptr; |
| 3566 | |
| 3567 | if ([attribute isEqualToString:bridge_cast(kAXStyleRangeForIndexParameterizedAttribute((CFStringRef) __builtin___CFStringMakeConstantString ("" "AXStyleRangeForIndex" "")))]) { |
| 3568 | auto textRange = backingObject->doAXStyleRangeForIndex([number intValue]); |
| 3569 | return [NSValue valueWithRange:textRange]; |
| 3570 | } |
| 3571 | } |
| 3572 | |
| 3573 | if ([attribute isEqualToString:NSAccessibilityConvertRelativeFrameParameterizedAttribute@"AXConvertRelativeFrame"]) { |
| 3574 | RefPtr parent = backingObject->parentObject(); |
| 3575 | return parent ? [NSValue valueWithRect:parent->convertFrameToSpace(FloatRect(rect), AccessibilityConversionSpace::Page)] : nilnullptr; |
| 3576 | } |
| 3577 | |
| 3578 | if ([attribute isEqualToString:NSAccessibilityTextMarkerDebugDescriptionAttribute@"AXTextMarkerDebugDescription"]) |
| 3579 | return AXTextMarker { textMarker }.debugDescription().createNSString().autorelease(); |
| 3580 | |
| 3581 | if ([attribute isEqualToString:NSAccessibilityTextMarkerRangeDebugDescriptionAttribute@"AXTextMarkerRangeDebugDescription"]) |
| 3582 | return AXTextMarkerRange { textMarkerRange }.debugDescription().createNSString().autorelease(); |
| 3583 | |
| 3584 | #if ENABLE(TREE_DEBUGGING)(defined ENABLE_TREE_DEBUGGING && ENABLE_TREE_DEBUGGING ) |
| 3585 | if ([attribute isEqualToString:AXTextMarkerNodeDebugDescriptionAttribute]) { |
| 3586 | [self showNodeForTextMarker:textMarker]; |
| 3587 | return nilnullptr; |
| 3588 | } |
| 3589 | |
| 3590 | if ([attribute isEqualToString:AXTextMarkerNodeTreeDebugDescriptionAttribute]) { |
| 3591 | [self showNodeTreeForTextMarker:textMarker]; |
| 3592 | return nilnullptr; |
| 3593 | } |
| 3594 | #endif // ENABLE(TREE_DEBUGGING) |
| 3595 | |
| 3596 | if (AXObjectCache::clientIsInTestMode()) { |
| 3597 | if (RetainPtr<id> value = [self parameterizedAttributeValueForTesting:attribute parameter:parameter backingObject:backingObject]) |
| 3598 | return value.autorelease(); |
| 3599 | } |
| 3600 | |
| 3601 | // There are some parameters that super handles that are not explicitly returned by the list of the element's attributes. |
| 3602 | // In that case it must be passed to super. |
| 3603 | return [super accessibilityAttributeValue:attribute forParameter:parameter]; |
| 3604 | } |
| 3605 | ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop |
| 3606 | |
| 3607 | - (BOOL)accessibilitySupportsOverriddenAttributes |
| 3608 | { |
| 3609 | return YES__objc_yes; |
| 3610 | } |
| 3611 | |
| 3612 | // accessibilityShouldUseUniqueId is an AppKit method we override so that |
| 3613 | // objects will be given a unique ID, and therefore allow AppKit to know when they |
| 3614 | // become obsolete (e.g. when the user navigates to a new web page, making this one |
| 3615 | // unrendered but not deallocated because it is in the back/forward cache). |
| 3616 | // It is important to call NSAccessibilityUnregisterUniqueIdForUIElement in the |
| 3617 | // appropriate place (e.g. dealloc) to remove these non-retained references from |
| 3618 | // AppKit's id mapping tables. We do this in detach by calling unregisterUniqueIdForUIElement. |
| 3619 | // |
| 3620 | // Registering an object is also required for observing notifications. Only registered objects can be observed. |
| 3621 | - (BOOL)accessibilityShouldUseUniqueId |
| 3622 | { |
| 3623 | // All AX object wrappers should use unique ID's because it's faster within AppKit to look them up. |
| 3624 | return YES__objc_yes; |
| 3625 | } |
| 3626 | |
| 3627 | // API that AppKit uses for faster access |
| 3628 | - (NSUInteger)accessibilityIndexOfChild:(id)targetChild |
| 3629 | { |
| 3630 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 3631 | if (!backingObject) |
| 3632 | return NSNotFound; |
| 3633 | |
| 3634 | // Tree objects return their rows as their children. We can use the original method |
| 3635 | // here, because we won't gain any speed up. |
| 3636 | if (backingObject->isTree()) |
| 3637 | return [super accessibilityIndexOfChild:targetChild]; |
| 3638 | |
| 3639 | const auto& children = backingObject->stitchedUnignoredChildren(); |
| 3640 | if (!children.size()) { |
| 3641 | if (RetainPtr widgetChildren = renderWidgetChildren(*backingObject)) |
| 3642 | return [widgetChildren.get() indexOfObject:targetChild]; |
| 3643 | #if ENABLE(MODEL_ELEMENT_ACCESSIBILITY)(defined 1 && 1) |
| 3644 | if (backingObject->isModel()) |
| 3645 | return backingObject->modelElementChildren().children.find(targetChild); |
| 3646 | #endif |
| 3647 | } |
| 3648 | |
| 3649 | size_t childCount = children.size(); |
| 3650 | for (size_t i = 0; i < childCount; i++) { |
| 3651 | const auto& child = children[i]; |
| 3652 | RetainPtr childWrapper = child->wrapper(); |
| 3653 | if (childWrapper == targetChild || (child->isAttachment() && [childWrapper attachmentView] == targetChild) |
| 3654 | || (child->isRemoteFrame() && child->remoteFramePlatformElement() == targetChild)) { |
| 3655 | return i; |
| 3656 | } |
| 3657 | } |
| 3658 | return NSNotFound; |
| 3659 | } |
| 3660 | |
| 3661 | ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push
GCC diagnostic ignored "-W" "deprecated-declarations" |
| 3662 | - (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute |
| 3663 | { |
| 3664 | AXTRACE(makeString("WebAccessibilityObjectWrapper accessibilityArrayAttributeCount:"_s, String(attribute)))(void)0; |
| 3665 | |
| 3666 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 3667 | if (!backingObject) |
| 3668 | return 0; |
| 3669 | |
| 3670 | if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { |
| 3671 | // Tree items object returns a different set of children than those that are in children() |
| 3672 | // because an AXOutline (the mac role is becomes) has some odd stipulations. |
| 3673 | if (backingObject->isTree() || backingObject->isTreeItem() || backingObject->isRemoteFrame()) |
| 3674 | return children(*backingObject).count; |
| 3675 | |
| 3676 | // FIXME: this is duplicating the logic in children(AXCoreObject&) so it should be reworked. |
| 3677 | size_t childrenSize = backingObject->stitchedUnignoredChildren().size(); |
| 3678 | if (!childrenSize) { |
| 3679 | #if ENABLE(MODEL_ELEMENT_ACCESSIBILITY)(defined 1 && 1) |
| 3680 | if (backingObject->isModel()) |
| 3681 | return backingObject->modelElementChildren().children.size(); |
| 3682 | #endif |
| 3683 | if (RetainPtr widgetChildren = renderWidgetChildren(*backingObject)) |
| 3684 | return [widgetChildren.get() count]; |
| 3685 | } |
| 3686 | return childrenSize; |
| 3687 | } |
| 3688 | |
| 3689 | return [super accessibilityArrayAttributeCount:attribute]; |
| 3690 | } |
| 3691 | ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop |
| 3692 | |
| 3693 | // Implement this for performance reasons, as the default AppKit implementation will iterate upwards |
| 3694 | // until it finds something that responds to this method. |
| 3695 | - (pid_t)accessibilityPresenterProcessIdentifier |
| 3696 | { |
| 3697 | RefPtr<AXCoreObject> backingObject = self.axBackingObject; |
| 3698 | return backingObject ? backingObject->processID() : 0; |
| 3699 | } |
| 3700 | |
| 3701 | - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount |
| 3702 | { |
| 3703 | AXTRACE(makeString("WebAccessibilityObjectWrapper accessibilityArrayAttributeValue:"_s, String(attribute)))(void)0; |
| 3704 | |
| 3705 | if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) |
| 3706 | return [self _accessibilityChildrenFromIndex:index maxCount:maxCount returnPlatformElements:YES__objc_yes]; |
| 3707 | |
| 3708 | return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount]; |
| 3709 | } |
| 3710 | |
| 3711 | - (NSArray *)_accessibilityChildrenFromIndex:(NSUInteger)index maxCount:(NSUInteger)maxCount returnPlatformElements:(BOOL)returnPlatformElements |
| 3712 | { |
| 3713 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 3714 | if (!backingObject) |
| 3715 | return nilnullptr; |
| 3716 | |
| 3717 | const auto& unignoredChildren = backingObject->crossFrameUnignoredChildren(); |
| 3718 | if (unignoredChildren.isEmpty()) { |
| 3719 | RetainPtr<NSArray> children = transformSpecialChildrenCases(*backingObject, unignoredChildren); |
| 3720 | if (!children) |
| 3721 | return nilnullptr; |
| 3722 | |
| 3723 | NSUInteger childCount = [children count]; |
| 3724 | if (index >= childCount) |
| 3725 | return nilnullptr; |
| 3726 | |
| 3727 | NSUInteger arrayLength = std::min(childCount - index, maxCount); |
| 3728 | return [children subarrayWithRange:NSMakeRange(index, arrayLength)]; |
| 3729 | } |
| 3730 | |
| 3731 | if (backingObject->isTree() || backingObject->isTreeItem()) { |
| 3732 | // Tree objects return their rows as their children & tree items return their contents sans rows. |
| 3733 | // We can use the original method in this case. |
| 3734 | return [super accessibilityArrayAttributeValues:NSAccessibilityChildrenAttribute index:index maxCount:maxCount]; |
| 3735 | } |
| 3736 | |
| 3737 | RetainPtr<NSArray> children = makeNSArray(unignoredChildren, returnPlatformElements); |
| 3738 | unsigned childCount = [children count]; |
| 3739 | if (index >= childCount) |
| 3740 | return nilnullptr; |
| 3741 | |
| 3742 | unsigned available = std::min(childCount - index, maxCount); |
| 3743 | |
| 3744 | NSMutableArray *subarray = [NSMutableArray arrayWithCapacity:available]; |
| 3745 | for (unsigned added = 0; added < available; ++index, ++added) { |
| 3746 | RetainPtr<WebAccessibilityObjectWrapper> wrapper = [children objectAtIndex:index]; |
| 3747 | |
| 3748 | // The attachment view should be returned, otherwise AX palindrome errors occur. |
| 3749 | RetainPtr<id> attachmentView; |
| 3750 | if (RefPtr childObject = [wrapper isKindOfClass:[WebAccessibilityObjectWrapper class]] ? wrapper.get().axBackingObject : nullptr) { |
| 3751 | if (childObject->isAttachment()) |
| 3752 | attachmentView = [wrapper attachmentView]; |
| 3753 | else if (childObject->isRemoteFrame() && returnPlatformElements) |
| 3754 | attachmentView = childObject->remoteFramePlatformElement(); |
| 3755 | } |
| 3756 | |
| 3757 | [subarray addObject:attachmentView ? attachmentView.get() : wrapper.get()]; |
| 3758 | } |
| 3759 | |
| 3760 | return subarray; |
| 3761 | } |
| 3762 | |
| 3763 | - (NSArray<NSAccessibilityCustomAction *> *)accessibilityCustomActions |
| 3764 | { |
| 3765 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 3766 | if (!backingObject) |
| 3767 | return nilnullptr; |
| 3768 | |
| 3769 | auto actionsData = [self baseAccessibilityCustomActionsData]; |
| 3770 | if (actionsData.isEmpty()) |
| 3771 | return nilnullptr; |
| 3772 | |
| 3773 | RetainPtr<NSMutableArray<NSAccessibilityCustomAction *>> actions = adoptNS([[NSMutableArray alloc] init]); |
| 3774 | for (auto& actionData : actionsData) { |
| 3775 | auto action = adoptNS([[NSAccessibilityCustomAction alloc] initWithName:actionData.name.createNSString().autorelease() handler:^BOOL { |
| 3776 | return Accessibility::performCustomActionPress(actionData.treeID, actionData.targetID); |
| 3777 | }]); |
| 3778 | [actions addObject:action.get()]; |
| 3779 | } |
| 3780 | |
| 3781 | return actions.autorelease(); |
| 3782 | } |
| 3783 | |
| 3784 | - (NSString *)debugDescription |
| 3785 | { |
| 3786 | String backingObjectDescription = "null backingObject"_s; |
| 3787 | RefPtr<AXCoreObject> backingObject = self.updateObjectBackingStore; |
| 3788 | if (backingObject) |
| 3789 | backingObjectDescription = backingObject->debugDescription(); |
| 3790 | return makeString("PID: "_s, getpid(), ", "_s, backingObjectDescription).createNSString().autorelease(); |
| 3791 | } |
| 3792 | @end |
| 3793 | |
| 3794 | #endif // PLATFORM(MAC) |