AX: Implement updated CSS3 Speech for 'speak' and 'speak-as' properties
[WebKit-https.git] / Source / WebCore / accessibility / mac / WebAccessibilityObjectWrapperBase.mm
1 /*
2  * Copyright (C) 2008, 2009, 2010, 2011 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 "WebAccessibilityObjectWrapperBase.h"
31
32 #if HAVE(ACCESSIBILITY)
33
34 #import "AXObjectCache.h"
35 #import "AccessibilityARIAGridRow.h"
36 #import "AccessibilityList.h"
37 #import "AccessibilityListBox.h"
38 #import "AccessibilityRenderObject.h"
39 #import "AccessibilityScrollView.h"
40 #import "AccessibilitySpinButton.h"
41 #import "AccessibilityTable.h"
42 #import "AccessibilityTableCell.h"
43 #import "AccessibilityTableColumn.h"
44 #import "AccessibilityTableRow.h"
45 #import "ColorMac.h"
46 #import "ContextMenuController.h"
47 #import "Editing.h"
48 #import "Font.h"
49 #import "FontCascade.h"
50 #import "Frame.h"
51 #import "FrameLoaderClient.h"
52 #import "FrameSelection.h"
53 #import "HTMLNames.h"
54 #import "LocalizedStrings.h"
55 #import "Page.h"
56 #import "RenderTextControl.h"
57 #import "RenderView.h"
58 #import "RenderWidget.h"
59 #import "ScrollView.h"
60 #import "TextCheckerClient.h"
61 #import "TextCheckingHelper.h"
62 #import "VisibleUnits.h"
63 #import "WebCoreFrameView.h"
64
65 using namespace WebCore;
66 using namespace HTMLNames;
67
68 // Search Keys
69 #ifndef NSAccessibilityAnyTypeSearchKey
70 #define NSAccessibilityAnyTypeSearchKey @"AXAnyTypeSearchKey"
71 #endif
72
73 #ifndef NSAccessibilityArticleSearchKey
74 #define NSAccessibilityArticleSearchKey @"AXArticleSearchKey"
75 #endif
76
77 #ifndef NSAccessibilityBlockquoteSameLevelSearchKey
78 #define NSAccessibilityBlockquoteSameLevelSearchKey @"AXBlockquoteSameLevelSearchKey"
79 #endif
80
81 #ifndef NSAccessibilityBlockquoteSearchKey
82 #define NSAccessibilityBlockquoteSearchKey @"AXBlockquoteSearchKey"
83 #endif
84
85 #ifndef NSAccessibilityBoldFontSearchKey
86 #define NSAccessibilityBoldFontSearchKey @"AXBoldFontSearchKey"
87 #endif
88
89 #ifndef NSAccessibilityButtonSearchKey
90 #define NSAccessibilityButtonSearchKey @"AXButtonSearchKey"
91 #endif
92
93 #ifndef NSAccessibilityCheckBoxSearchKey
94 #define NSAccessibilityCheckBoxSearchKey @"AXCheckBoxSearchKey"
95 #endif
96
97 #ifndef NSAccessibilityControlSearchKey
98 #define NSAccessibilityControlSearchKey @"AXControlSearchKey"
99 #endif
100
101 #ifndef NSAccessibilityDifferentTypeSearchKey
102 #define NSAccessibilityDifferentTypeSearchKey @"AXDifferentTypeSearchKey"
103 #endif
104
105 #ifndef NSAccessibilityFontChangeSearchKey
106 #define NSAccessibilityFontChangeSearchKey @"AXFontChangeSearchKey"
107 #endif
108
109 #ifndef NSAccessibilityFontColorChangeSearchKey
110 #define NSAccessibilityFontColorChangeSearchKey @"AXFontColorChangeSearchKey"
111 #endif
112
113 #ifndef NSAccessibilityFrameSearchKey
114 #define NSAccessibilityFrameSearchKey @"AXFrameSearchKey"
115 #endif
116
117 #ifndef NSAccessibilityGraphicSearchKey
118 #define NSAccessibilityGraphicSearchKey @"AXGraphicSearchKey"
119 #endif
120
121 #ifndef NSAccessibilityHeadingLevel1SearchKey
122 #define NSAccessibilityHeadingLevel1SearchKey @"AXHeadingLevel1SearchKey"
123 #endif
124
125 #ifndef NSAccessibilityHeadingLevel2SearchKey
126 #define NSAccessibilityHeadingLevel2SearchKey @"AXHeadingLevel2SearchKey"
127 #endif
128
129 #ifndef NSAccessibilityHeadingLevel3SearchKey
130 #define NSAccessibilityHeadingLevel3SearchKey @"AXHeadingLevel3SearchKey"
131 #endif
132
133 #ifndef NSAccessibilityHeadingLevel4SearchKey
134 #define NSAccessibilityHeadingLevel4SearchKey @"AXHeadingLevel4SearchKey"
135 #endif
136
137 #ifndef NSAccessibilityHeadingLevel5SearchKey
138 #define NSAccessibilityHeadingLevel5SearchKey @"AXHeadingLevel5SearchKey"
139 #endif
140
141 #ifndef NSAccessibilityHeadingLevel6SearchKey
142 #define NSAccessibilityHeadingLevel6SearchKey @"AXHeadingLevel6SearchKey"
143 #endif
144
145 #ifndef NSAccessibilityHeadingSameLevelSearchKey
146 #define NSAccessibilityHeadingSameLevelSearchKey @"AXHeadingSameLevelSearchKey"
147 #endif
148
149 #ifndef NSAccessibilityHeadingSearchKey
150 #define NSAccessibilityHeadingSearchKey @"AXHeadingSearchKey"
151 #endif
152
153 #ifndef NSAccessibilityHighlightedSearchKey
154 #define NSAccessibilityHighlightedSearchKey @"AXHighlightedSearchKey"
155 #endif
156
157 #ifndef NSAccessibilityItalicFontSearchKey
158 #define NSAccessibilityItalicFontSearchKey @"AXItalicFontSearchKey"
159 #endif
160
161 #ifndef NSAccessibilityLandmarkSearchKey
162 #define NSAccessibilityLandmarkSearchKey @"AXLandmarkSearchKey"
163 #endif
164
165 #ifndef NSAccessibilityLinkSearchKey
166 #define NSAccessibilityLinkSearchKey @"AXLinkSearchKey"
167 #endif
168
169 #ifndef NSAccessibilityListSearchKey
170 #define NSAccessibilityListSearchKey @"AXListSearchKey"
171 #endif
172
173 #ifndef NSAccessibilityLiveRegionSearchKey
174 #define NSAccessibilityLiveRegionSearchKey @"AXLiveRegionSearchKey"
175 #endif
176
177 #ifndef NSAccessibilityMisspelledWordSearchKey
178 #define NSAccessibilityMisspelledWordSearchKey @"AXMisspelledWordSearchKey"
179 #endif
180
181 #ifndef NSAccessibilityOutlineSearchKey
182 #define NSAccessibilityOutlineSearchKey @"AXOutlineSearchKey"
183 #endif
184
185 #ifndef NSAccessibilityPlainTextSearchKey
186 #define NSAccessibilityPlainTextSearchKey @"AXPlainTextSearchKey"
187 #endif
188
189 #ifndef NSAccessibilityRadioGroupSearchKey
190 #define NSAccessibilityRadioGroupSearchKey @"AXRadioGroupSearchKey"
191 #endif
192
193 #ifndef NSAccessibilitySameTypeSearchKey
194 #define NSAccessibilitySameTypeSearchKey @"AXSameTypeSearchKey"
195 #endif
196
197 #ifndef NSAccessibilityStaticTextSearchKey
198 #define NSAccessibilityStaticTextSearchKey @"AXStaticTextSearchKey"
199 #endif
200
201 #ifndef NSAccessibilityStyleChangeSearchKey
202 #define NSAccessibilityStyleChangeSearchKey @"AXStyleChangeSearchKey"
203 #endif
204
205 #ifndef NSAccessibilityTableSameLevelSearchKey
206 #define NSAccessibilityTableSameLevelSearchKey @"AXTableSameLevelSearchKey"
207 #endif
208
209 #ifndef NSAccessibilityTableSearchKey
210 #define NSAccessibilityTableSearchKey @"AXTableSearchKey"
211 #endif
212
213 #ifndef NSAccessibilityTextFieldSearchKey
214 #define NSAccessibilityTextFieldSearchKey @"AXTextFieldSearchKey"
215 #endif
216
217 #ifndef NSAccessibilityUnderlineSearchKey
218 #define NSAccessibilityUnderlineSearchKey @"AXUnderlineSearchKey"
219 #endif
220
221 #ifndef NSAccessibilityUnvisitedLinkSearchKey
222 #define NSAccessibilityUnvisitedLinkSearchKey @"AXUnvisitedLinkSearchKey"
223 #endif
224
225 #ifndef NSAccessibilityVisitedLinkSearchKey
226 #define NSAccessibilityVisitedLinkSearchKey @"AXVisitedLinkSearchKey"
227 #endif
228
229 // Search
230 #ifndef NSAccessibilityImmediateDescendantsOnly
231 #define NSAccessibilityImmediateDescendantsOnly @"AXImmediateDescendantsOnly"
232 #endif
233
234 static NSArray *convertMathPairsToNSArray(const AccessibilityObject::AccessibilityMathMultiscriptPairs& pairs, NSString *subscriptKey, NSString *superscriptKey)
235 {
236     NSMutableArray *array = [NSMutableArray arrayWithCapacity:pairs.size()];
237     for (const auto& pair : pairs) {
238         NSMutableDictionary *pairDictionary = [NSMutableDictionary dictionary];
239         if (pair.first && pair.first->wrapper() && !pair.first->accessibilityIsIgnored())
240             [pairDictionary setObject:pair.first->wrapper() forKey:subscriptKey];
241         if (pair.second && pair.second->wrapper() && !pair.second->accessibilityIsIgnored())
242             [pairDictionary setObject:pair.second->wrapper() forKey:superscriptKey];
243         [array addObject:pairDictionary];
244     }
245     return array;
246 }
247
248
249 NSArray *convertToNSArray(const AccessibilityObject::AccessibilityChildrenVector& vector)
250 {
251     NSMutableArray *array = [NSMutableArray arrayWithCapacity:vector.size()];
252     for (const auto& child : vector) {
253         WebAccessibilityObjectWrapper* wrapper = child->wrapper();
254         ASSERT(wrapper);
255         if (wrapper) {
256             // we want to return the attachment view instead of the object representing the attachment.
257             // otherwise, we get palindrome errors in the AX hierarchy
258             if (child->isAttachment() && [wrapper attachmentView])
259                 [array addObject:[wrapper attachmentView]];
260             else
261                 [array addObject:wrapper];
262         }
263     }
264     return [[array copy] autorelease];
265 }
266
267 @implementation WebAccessibilityObjectWrapperBase
268
269 - (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
270 {
271     if (!(self = [super init]))
272         return nil;
273
274     m_object = axObject;
275     return self;
276 }
277
278 - (void)detach
279 {
280     m_object = nullptr;
281 }
282
283 - (BOOL)updateObjectBackingStore
284 {
285     // Calling updateBackingStore() can invalidate this element so self must be retained.
286     // If it does become invalidated, m_object will be nil.
287     [[self retain] autorelease];
288     
289     if (!m_object)
290         return NO;
291     
292     m_object->updateBackingStore();
293     if (!m_object)
294         return NO;
295     
296     return YES;
297 }
298
299 - (id)attachmentView
300 {
301     return nil;
302 }
303
304 - (AccessibilityObject*)accessibilityObject
305 {
306     return m_object;
307 }
308
309 // FIXME: Different kinds of elements are putting the title tag to use in different
310 // AX fields. This should be rectified, but in the initial patch I want to achieve
311 // parity with existing behavior.
312 - (BOOL)titleTagShouldBeUsedInDescriptionField
313 {
314     return (m_object->isLink() && !m_object->isImageMapLink()) || m_object->isImage();
315 }
316
317 // On iOS, we don't have to return the value in the title. We can return the actual title, given the API.
318 - (BOOL)fileUploadButtonReturnsValueInTitle
319 {
320     return YES;
321 }
322
323 // This should be the "visible" text that's actually on the screen if possible.
324 // If there's alternative text, that can override the title.
325 - (NSString *)baseAccessibilityTitle
326 {
327     // Static text objects should not have a title. Its content is communicated in its AXValue.
328     if (m_object->roleValue() == AccessibilityRole::StaticText)
329         return [NSString string];
330
331     // A file upload button presents a challenge because it has button text and a value, but the
332     // API doesn't support this paradigm.
333     // The compromise is to return the button type in the role description and the value of the file path in the title
334     if (m_object->isFileUploadButton() && [self fileUploadButtonReturnsValueInTitle])
335         return m_object->stringValue();
336     
337     Vector<AccessibilityText> textOrder;
338     m_object->accessibilityText(textOrder);
339     
340     for (const auto& text : textOrder) {
341         // If we have alternative text, then we should not expose a title.
342         if (text.textSource == AccessibilityTextSource::Alternative)
343             break;
344         
345         // Once we encounter visible text, or the text from our children that should be used foremost.
346         if (text.textSource == AccessibilityTextSource::Visible || text.textSource == AccessibilityTextSource::Children)
347             return text.text;
348         
349         // If there's an element that labels this object and it's not exposed, then we should use
350         // that text as our title.
351         if (text.textSource == AccessibilityTextSource::LabelByElement && !m_object->exposesTitleUIElement())
352             return text.text;
353     }
354     
355     return [NSString string];
356 }
357
358 - (NSString *)baseAccessibilityDescription
359 {
360     // Static text objects should not have a description. Its content is communicated in its AXValue.
361     // One exception is the media control labels that have a value and a description. Those are set programatically.
362     if (m_object->roleValue() == AccessibilityRole::StaticText && !m_object->isMediaControlLabel())
363         return [NSString string];
364     
365     Vector<AccessibilityText> textOrder;
366     m_object->accessibilityText(textOrder);
367
368     NSMutableString *returnText = [NSMutableString string];
369     bool visibleTextAvailable = false;
370     for (const auto& text : textOrder) {
371         if (text.textSource == AccessibilityTextSource::Alternative) {
372             [returnText appendString:text.text];
373             break;
374         }
375         
376         switch (text.textSource) {
377         // These are sub-components of one element (Attachment) that are re-combined in OSX and iOS.
378         case AccessibilityTextSource::Title:
379         case AccessibilityTextSource::Subtitle:
380         case AccessibilityTextSource::Action: {
381             if (!text.text.length())
382                 break;
383             if ([returnText length])
384                 [returnText appendString:@", "];
385             [returnText appendString:text.text];
386             break;
387         }
388         case AccessibilityTextSource::Visible:
389         case AccessibilityTextSource::Children:
390         case AccessibilityTextSource::LabelByElement:
391             visibleTextAvailable = true;
392             break;
393         default:
394             break;
395         }
396         
397         if (text.textSource == AccessibilityTextSource::TitleTag && !visibleTextAvailable) {
398             [returnText appendString:text.text];
399             break;
400         }
401     }
402     
403     return returnText;
404 }
405
406 - (NSArray<NSString *> *)baseAccessibilitySpeechHint
407 {
408     ESpeakAs speak = m_object->speakAsProperty();
409     NSMutableArray<NSString *> *hints = [NSMutableArray array];
410     if (speak & SpeakSpellOut)
411         [hints addObject:@"spell-out"];
412     else
413         [hints addObject:@"normal"];
414
415     if (speak & SpeakDigits)
416         [hints addObject:@"digits"];
417     if (speak & SpeakLiteralPunctuation)
418         [hints addObject:@"literal-punctuation"];
419     if (speak & SpeakNoPunctuation)
420         [hints addObject:@"no-punctuation"];
421     
422     return hints;
423 }
424
425 - (NSString *)baseAccessibilityHelpText
426 {
427     Vector<AccessibilityText> textOrder;
428     m_object->accessibilityText(textOrder);
429     
430     bool descriptiveTextAvailable = false;
431     for (const auto& text : textOrder) {
432         if (text.textSource == AccessibilityTextSource::Help || text.textSource == AccessibilityTextSource::Summary)
433             return text.text;
434         
435         // If an element does NOT have other descriptive text the title tag should be used as its descriptive text.
436         // But, if those ARE available, then the title tag should be used for help text instead.
437         switch (text.textSource) {
438         case AccessibilityTextSource::Alternative:
439         case AccessibilityTextSource::Visible:
440         case AccessibilityTextSource::Children:
441         case AccessibilityTextSource::LabelByElement:
442             descriptiveTextAvailable = true;
443             break;
444         default:
445             break;
446         }
447         
448         if (text.textSource == AccessibilityTextSource::TitleTag && descriptiveTextAvailable)
449             return text.text;
450     }
451     
452     return [NSString string];
453 }
454
455 struct PathConversionInfo {
456     WebAccessibilityObjectWrapperBase *wrapper;
457     CGMutablePathRef path;
458 };
459
460 static void convertPathToScreenSpaceFunction(PathConversionInfo& conversion, const PathElement& element)
461 {
462     WebAccessibilityObjectWrapperBase *wrapper = conversion.wrapper;
463     CGMutablePathRef newPath = conversion.path;
464     switch (element.type) {
465     case PathElementMoveToPoint:
466     {
467         CGPoint newPoint = [wrapper convertPointToScreenSpace:element.points[0]];
468         CGPathMoveToPoint(newPath, nil, newPoint.x, newPoint.y);
469         break;
470     }
471     case PathElementAddLineToPoint:
472     {
473         CGPoint newPoint = [wrapper convertPointToScreenSpace:element.points[0]];
474         CGPathAddLineToPoint(newPath, nil, newPoint.x, newPoint.y);
475         break;
476     }
477     case PathElementAddQuadCurveToPoint:
478     {
479         CGPoint newPoint1 = [wrapper convertPointToScreenSpace:element.points[0]];
480         CGPoint newPoint2 = [wrapper convertPointToScreenSpace:element.points[1]];
481         CGPathAddQuadCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y);
482         break;
483     }
484     case PathElementAddCurveToPoint:
485     {
486         CGPoint newPoint1 = [wrapper convertPointToScreenSpace:element.points[0]];
487         CGPoint newPoint2 = [wrapper convertPointToScreenSpace:element.points[1]];
488         CGPoint newPoint3 = [wrapper convertPointToScreenSpace:element.points[2]];
489         CGPathAddCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y, newPoint3.x, newPoint3.y);
490         break;
491     }
492     case PathElementCloseSubpath:
493     {
494         CGPathCloseSubpath(newPath);
495         break;
496     }
497     }
498 }
499
500 - (CGPathRef)convertPathToScreenSpace:(Path &)path
501 {
502     PathConversionInfo conversion = { self, CGPathCreateMutable() };
503     path.apply([&conversion](const PathElement& pathElement) {
504         convertPathToScreenSpaceFunction(conversion, pathElement);
505     });
506     return (CGPathRef)[(id)conversion.path autorelease];
507 }
508
509 - (CGPoint)convertPointToScreenSpace:(FloatPoint &)point
510 {
511     UNUSED_PARAM(point);
512     ASSERT_NOT_REACHED();
513     return CGPointZero;
514 }
515
516 - (NSString *)ariaLandmarkRoleDescription
517 {
518     switch (m_object->roleValue()) {
519     case AccessibilityRole::LandmarkBanner:
520         return AXARIAContentGroupText(@"ARIALandmarkBanner");
521     case AccessibilityRole::LandmarkComplementary:
522         return AXARIAContentGroupText(@"ARIALandmarkComplementary");
523     case AccessibilityRole::LandmarkContentInfo:
524         return AXARIAContentGroupText(@"ARIALandmarkContentInfo");
525     case AccessibilityRole::LandmarkMain:
526         return AXARIAContentGroupText(@"ARIALandmarkMain");
527     case AccessibilityRole::LandmarkNavigation:
528         return AXARIAContentGroupText(@"ARIALandmarkNavigation");
529     case AccessibilityRole::LandmarkDocRegion:
530     case AccessibilityRole::LandmarkRegion:
531         return AXARIAContentGroupText(@"ARIALandmarkRegion");
532     case AccessibilityRole::LandmarkSearch:
533         return AXARIAContentGroupText(@"ARIALandmarkSearch");
534     case AccessibilityRole::ApplicationAlert:
535         return AXARIAContentGroupText(@"ARIAApplicationAlert");
536     case AccessibilityRole::ApplicationAlertDialog:
537         return AXARIAContentGroupText(@"ARIAApplicationAlertDialog");
538     case AccessibilityRole::ApplicationDialog:
539         return AXARIAContentGroupText(@"ARIAApplicationDialog");
540     case AccessibilityRole::ApplicationLog:
541         return AXARIAContentGroupText(@"ARIAApplicationLog");
542     case AccessibilityRole::ApplicationMarquee:
543         return AXARIAContentGroupText(@"ARIAApplicationMarquee");
544     case AccessibilityRole::ApplicationStatus:
545         return AXARIAContentGroupText(@"ARIAApplicationStatus");
546     case AccessibilityRole::ApplicationTimer:
547         return AXARIAContentGroupText(@"ARIAApplicationTimer");
548     case AccessibilityRole::Document:
549         return AXARIAContentGroupText(@"ARIADocument");
550     case AccessibilityRole::DocumentArticle:
551         return AXARIAContentGroupText(@"ARIADocumentArticle");
552     case AccessibilityRole::DocumentMath:
553         return AXARIAContentGroupText(@"ARIADocumentMath");
554     case AccessibilityRole::DocumentNote:
555         return AXARIAContentGroupText(@"ARIADocumentNote");
556     case AccessibilityRole::UserInterfaceTooltip:
557         return AXARIAContentGroupText(@"ARIAUserInterfaceTooltip");
558     case AccessibilityRole::TabPanel:
559         return AXARIAContentGroupText(@"ARIATabPanel");
560     case AccessibilityRole::WebApplication:
561         return AXARIAContentGroupText(@"ARIAWebApplication");
562     default:
563         return nil;
564     }
565 }
566
567 - (NSString *)accessibilityPlatformMathSubscriptKey
568 {
569     ASSERT_NOT_REACHED();
570     return nil;
571 }
572
573 - (NSString *)accessibilityPlatformMathSuperscriptKey
574 {
575     ASSERT_NOT_REACHED();
576     return nil;    
577 }
578
579 - (NSArray *)accessibilityMathPostscriptPairs
580 {
581     AccessibilityObject::AccessibilityMathMultiscriptPairs pairs;
582     m_object->mathPostscripts(pairs);
583     return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]);
584 }
585
586 - (NSArray *)accessibilityMathPrescriptPairs
587 {
588     AccessibilityObject::AccessibilityMathMultiscriptPairs pairs;
589     m_object->mathPrescripts(pairs);
590     return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]);
591 }
592
593 // This is set by DRT when it wants to listen for notifications.
594 static BOOL accessibilityShouldRepostNotifications;
595 + (void)accessibilitySetShouldRepostNotifications:(BOOL)repost
596 {
597     accessibilityShouldRepostNotifications = repost;
598 #if PLATFORM(MAC)
599     AXObjectCache::setShouldRepostNotificationsForTests(repost);
600 #endif
601 }
602
603 - (void)accessibilityPostedNotification:(NSString *)notificationName
604 {
605     if (accessibilityShouldRepostNotifications)
606         [self accessibilityPostedNotification:notificationName userInfo:nil];
607 }
608
609 static bool isValueTypeSupported(id value)
610 {
611     return [value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[WebAccessibilityObjectWrapperBase class]];
612 }
613
614 static NSArray *arrayRemovingNonSupportedTypes(NSArray *array)
615 {
616     ASSERT([array isKindOfClass:[NSArray class]]);
617     NSMutableArray *mutableArray = [array mutableCopy];
618     for (NSUInteger i = 0; i < [mutableArray count];) {
619         id value = [mutableArray objectAtIndex:i];
620         if ([value isKindOfClass:[NSDictionary class]])
621             [mutableArray replaceObjectAtIndex:i withObject:dictionaryRemovingNonSupportedTypes(value)];
622         else if ([value isKindOfClass:[NSArray class]])
623             [mutableArray replaceObjectAtIndex:i withObject:arrayRemovingNonSupportedTypes(value)];
624         else if (!isValueTypeSupported(value)) {
625             [mutableArray removeObjectAtIndex:i];
626             continue;
627         }
628         i++;
629     }
630     return [mutableArray autorelease];
631 }
632
633 static NSDictionary *dictionaryRemovingNonSupportedTypes(NSDictionary *dictionary)
634 {
635     if (!dictionary)
636         return nil;
637     ASSERT([dictionary isKindOfClass:[NSDictionary class]]);
638     NSMutableDictionary *mutableDictionary = [dictionary mutableCopy];
639     for (NSString *key in dictionary) {
640         id value = [dictionary objectForKey:key];
641         if ([value isKindOfClass:[NSDictionary class]])
642             [mutableDictionary setObject:dictionaryRemovingNonSupportedTypes(value) forKey:key];
643         else if ([value isKindOfClass:[NSArray class]])
644             [mutableDictionary setObject:arrayRemovingNonSupportedTypes(value) forKey:key];
645         else if (!isValueTypeSupported(value))
646             [mutableDictionary removeObjectForKey:key];
647     }
648     return [mutableDictionary autorelease];
649 }
650
651 - (void)accessibilityPostedNotification:(NSString *)notificationName userInfo:(NSDictionary *)userInfo
652 {
653     if (accessibilityShouldRepostNotifications) {
654         ASSERT(notificationName);
655         userInfo = dictionaryRemovingNonSupportedTypes(userInfo);
656         NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:notificationName, @"notificationName", userInfo, @"userInfo", nil];
657         [[NSNotificationCenter defaultCenter] postNotificationName:@"AXDRTNotification" object:self userInfo:info];
658     }
659 }
660
661 #pragma mark Search helpers
662
663 typedef HashMap<String, AccessibilitySearchKey> AccessibilitySearchKeyMap;
664
665 struct SearchKeyEntry {
666     String key;
667     AccessibilitySearchKey value;
668 };
669
670 static AccessibilitySearchKeyMap* createAccessibilitySearchKeyMap()
671 {
672     const SearchKeyEntry searchKeys[] = {
673         { NSAccessibilityAnyTypeSearchKey, AccessibilitySearchKey::AnyType },
674         { NSAccessibilityArticleSearchKey, AccessibilitySearchKey::Article },
675         { NSAccessibilityBlockquoteSameLevelSearchKey, AccessibilitySearchKey::BlockquoteSameLevel },
676         { NSAccessibilityBlockquoteSearchKey, AccessibilitySearchKey::Blockquote },
677         { NSAccessibilityBoldFontSearchKey, AccessibilitySearchKey::BoldFont },
678         { NSAccessibilityButtonSearchKey, AccessibilitySearchKey::Button },
679         { NSAccessibilityCheckBoxSearchKey, AccessibilitySearchKey::CheckBox },
680         { NSAccessibilityControlSearchKey, AccessibilitySearchKey::Control },
681         { NSAccessibilityDifferentTypeSearchKey, AccessibilitySearchKey::DifferentType },
682         { NSAccessibilityFontChangeSearchKey, AccessibilitySearchKey::FontChange },
683         { NSAccessibilityFontColorChangeSearchKey, AccessibilitySearchKey::FontColorChange },
684         { NSAccessibilityFrameSearchKey, AccessibilitySearchKey::Frame },
685         { NSAccessibilityGraphicSearchKey, AccessibilitySearchKey::Graphic },
686         { NSAccessibilityHeadingLevel1SearchKey, AccessibilitySearchKey::HeadingLevel1 },
687         { NSAccessibilityHeadingLevel2SearchKey, AccessibilitySearchKey::HeadingLevel2 },
688         { NSAccessibilityHeadingLevel3SearchKey, AccessibilitySearchKey::HeadingLevel3 },
689         { NSAccessibilityHeadingLevel4SearchKey, AccessibilitySearchKey::HeadingLevel4 },
690         { NSAccessibilityHeadingLevel5SearchKey, AccessibilitySearchKey::HeadingLevel5 },
691         { NSAccessibilityHeadingLevel6SearchKey, AccessibilitySearchKey::HeadingLevel6 },
692         { NSAccessibilityHeadingSameLevelSearchKey, AccessibilitySearchKey::HeadingSameLevel },
693         { NSAccessibilityHeadingSearchKey, AccessibilitySearchKey::Heading },
694         { NSAccessibilityHighlightedSearchKey, AccessibilitySearchKey::Highlighted },
695         { NSAccessibilityItalicFontSearchKey, AccessibilitySearchKey::ItalicFont },
696         { NSAccessibilityLandmarkSearchKey, AccessibilitySearchKey::Landmark },
697         { NSAccessibilityLinkSearchKey, AccessibilitySearchKey::Link },
698         { NSAccessibilityListSearchKey, AccessibilitySearchKey::List },
699         { NSAccessibilityLiveRegionSearchKey, AccessibilitySearchKey::LiveRegion },
700         { NSAccessibilityMisspelledWordSearchKey, AccessibilitySearchKey::MisspelledWord },
701         { NSAccessibilityOutlineSearchKey, AccessibilitySearchKey::Outline },
702         { NSAccessibilityPlainTextSearchKey, AccessibilitySearchKey::PlainText },
703         { NSAccessibilityRadioGroupSearchKey, AccessibilitySearchKey::RadioGroup },
704         { NSAccessibilitySameTypeSearchKey, AccessibilitySearchKey::SameType },
705         { NSAccessibilityStaticTextSearchKey, AccessibilitySearchKey::StaticText },
706         { NSAccessibilityStyleChangeSearchKey, AccessibilitySearchKey::StyleChange },
707         { NSAccessibilityTableSameLevelSearchKey, AccessibilitySearchKey::TableSameLevel },
708         { NSAccessibilityTableSearchKey, AccessibilitySearchKey::Table },
709         { NSAccessibilityTextFieldSearchKey, AccessibilitySearchKey::TextField },
710         { NSAccessibilityUnderlineSearchKey, AccessibilitySearchKey::Underline },
711         { NSAccessibilityUnvisitedLinkSearchKey, AccessibilitySearchKey::UnvisitedLink },
712         { NSAccessibilityVisitedLinkSearchKey, AccessibilitySearchKey::VisitedLink }
713     };
714     
715     AccessibilitySearchKeyMap* searchKeyMap = new AccessibilitySearchKeyMap;
716     for (size_t i = 0; i < WTF_ARRAY_LENGTH(searchKeys); i++)
717         searchKeyMap->set(searchKeys[i].key, searchKeys[i].value);
718     
719     return searchKeyMap;
720 }
721
722 static AccessibilitySearchKey accessibilitySearchKeyForString(const String& value)
723 {
724     if (value.isEmpty())
725         return AccessibilitySearchKey::AnyType;
726     
727     static const AccessibilitySearchKeyMap* searchKeyMap = createAccessibilitySearchKeyMap();
728     AccessibilitySearchKey searchKey = searchKeyMap->get(value);    
729     return static_cast<int>(searchKey) ? searchKey : AccessibilitySearchKey::AnyType;
730 }
731
732 AccessibilitySearchCriteria accessibilitySearchCriteriaForSearchPredicateParameterizedAttribute(const NSDictionary *parameterizedAttribute)
733 {
734     NSString *directionParameter = [parameterizedAttribute objectForKey:@"AXDirection"];
735     NSNumber *immediateDescendantsOnlyParameter = [parameterizedAttribute objectForKey:NSAccessibilityImmediateDescendantsOnly];
736     NSNumber *resultsLimitParameter = [parameterizedAttribute objectForKey:@"AXResultsLimit"];
737     NSString *searchTextParameter = [parameterizedAttribute objectForKey:@"AXSearchText"];
738     WebAccessibilityObjectWrapperBase *startElementParameter = [parameterizedAttribute objectForKey:@"AXStartElement"];
739     NSNumber *visibleOnlyParameter = [parameterizedAttribute objectForKey:@"AXVisibleOnly"];
740     id searchKeyParameter = [parameterizedAttribute objectForKey:@"AXSearchKey"];
741     
742     AccessibilitySearchDirection direction = AccessibilitySearchDirection::Next;
743     if ([directionParameter isKindOfClass:[NSString class]])
744         direction = [directionParameter isEqualToString:@"AXDirectionNext"] ? AccessibilitySearchDirection::Next : AccessibilitySearchDirection::Previous;
745     
746     bool immediateDescendantsOnly = false;
747     if ([immediateDescendantsOnlyParameter isKindOfClass:[NSNumber class]])
748         immediateDescendantsOnly = [immediateDescendantsOnlyParameter boolValue];
749     
750     unsigned resultsLimit = 0;
751     if ([resultsLimitParameter isKindOfClass:[NSNumber class]])
752         resultsLimit = [resultsLimitParameter unsignedIntValue];
753     
754     String searchText;
755     if ([searchTextParameter isKindOfClass:[NSString class]])
756         searchText = searchTextParameter;
757     
758     AccessibilityObject* startElement = nullptr;
759     if ([startElementParameter isKindOfClass:[WebAccessibilityObjectWrapperBase class]])
760         startElement = [startElementParameter accessibilityObject];
761     
762     bool visibleOnly = false;
763     if ([visibleOnlyParameter isKindOfClass:[NSNumber class]])
764         visibleOnly = [visibleOnlyParameter boolValue];
765     
766     AccessibilitySearchCriteria criteria = AccessibilitySearchCriteria(startElement, direction, searchText, resultsLimit, visibleOnly, immediateDescendantsOnly);
767     
768     if ([searchKeyParameter isKindOfClass:[NSString class]])
769         criteria.searchKeys.append(accessibilitySearchKeyForString(searchKeyParameter));
770     else if ([searchKeyParameter isKindOfClass:[NSArray class]]) {
771         size_t searchKeyCount = static_cast<size_t>([searchKeyParameter count]);
772         criteria.searchKeys.reserveInitialCapacity(searchKeyCount);
773         for (size_t i = 0; i < searchKeyCount; ++i) {
774             NSString *searchKey = [searchKeyParameter objectAtIndex:i];
775             if ([searchKey isKindOfClass:[NSString class]])
776                 criteria.searchKeys.uncheckedAppend(accessibilitySearchKeyForString(searchKey));
777         }
778     }
779     
780     return criteria;
781 }
782
783 @end
784
785 #endif // HAVE(ACCESSIBILITY)