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