1138b3338ff79a4286766ac611ff3e93de070fbc
[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 "Chrome.h"
46 #import "ChromeClient.h"
47 #import "ColorMac.h"
48 #import "ContextMenuController.h"
49 #import "Font.h"
50 #import "Frame.h"
51 #import "FrameLoaderClient.h"
52 #import "FrameSelection.h"
53 #import "HTMLAnchorElement.h"
54 #import "HTMLAreaElement.h"
55 #import "HTMLFrameOwnerElement.h"
56 #import "HTMLImageElement.h"
57 #import "HTMLInputElement.h"
58 #import "HTMLNames.h"
59 #import "HTMLTextAreaElement.h"
60 #import "LocalizedStrings.h"
61 #import "Page.h"
62 #import "RenderTextControl.h"
63 #import "RenderView.h"
64 #import "RenderWidget.h"
65 #import "ScrollView.h"
66 #import "SimpleFontData.h"
67 #import "TextCheckerClient.h"
68 #import "TextCheckingHelper.h"
69 #import "VisibleUnits.h"
70 #import "WebCoreFrameView.h"
71 #import "WebCoreObjCExtras.h"
72 #import "WebCoreSystemInterface.h"
73 #import "htmlediting.h"
74
75 using namespace WebCore;
76 using namespace HTMLNames;
77
78 static NSArray *convertMathPairsToNSArray(const AccessibilityObject::AccessibilityMathMultiscriptPairs& pairs, NSString *subscriptKey, NSString *superscriptKey)
79 {
80     NSMutableArray *array = [NSMutableArray arrayWithCapacity:pairs.size()];
81     for (const auto& pair : pairs) {
82         NSMutableDictionary *pairDictionary = [NSMutableDictionary dictionary];
83         if (pair.first && pair.first->wrapper() && !pair.first->accessibilityIsIgnored())
84             [pairDictionary setObject:pair.first->wrapper() forKey:subscriptKey];
85         if (pair.second && pair.second->wrapper() && !pair.second->accessibilityIsIgnored())
86             [pairDictionary setObject:pair.second->wrapper() forKey:superscriptKey];
87         [array addObject:pairDictionary];
88     }
89     return array;
90 }
91
92 @implementation WebAccessibilityObjectWrapperBase
93
94 - (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
95 {
96     if (!(self = [super init]))
97         return nil;
98
99     m_object = axObject;
100     return self;
101 }
102
103 - (void)detach
104 {
105     m_object = nullptr;
106 }
107
108 - (BOOL)updateObjectBackingStore
109 {
110     // Calling updateBackingStore() can invalidate this element so self must be retained.
111     // If it does become invalidated, m_object will be nil.
112     [[self retain] autorelease];
113     
114     if (!m_object)
115         return NO;
116     
117     m_object->updateBackingStore();
118     if (!m_object)
119         return NO;
120     
121     return YES;
122 }
123
124 - (id)attachmentView
125 {
126     return nil;
127 }
128
129 - (AccessibilityObject*)accessibilityObject
130 {
131     return m_object;
132 }
133
134 // FIXME: Different kinds of elements are putting the title tag to use in different
135 // AX fields. This should be rectified, but in the initial patch I want to achieve
136 // parity with existing behavior.
137 - (BOOL)titleTagShouldBeUsedInDescriptionField
138 {
139     return (m_object->isLink() && !m_object->isImageMapLink()) || m_object->isImage();
140 }
141
142 // On iOS, we don't have to return the value in the title. We can return the actual title, given the API.
143 - (BOOL)fileUploadButtonReturnsValueInTitle
144 {
145     return YES;
146 }
147
148 // This should be the "visible" text that's actually on the screen if possible.
149 // If there's alternative text, that can override the title.
150 - (NSString *)accessibilityTitle
151 {
152     // Static text objects should not have a title. Its content is communicated in its AXValue.
153     if (m_object->roleValue() == StaticTextRole)
154         return [NSString string];
155
156     // A file upload button presents a challenge because it has button text and a value, but the
157     // API doesn't support this paradigm.
158     // The compromise is to return the button type in the role description and the value of the file path in the title
159     if (m_object->isFileUploadButton() && [self fileUploadButtonReturnsValueInTitle])
160         return m_object->stringValue();
161     
162     Vector<AccessibilityText> textOrder;
163     m_object->accessibilityText(textOrder);
164     
165     for (const auto& text : textOrder) {
166         // If we have alternative text, then we should not expose a title.
167         if (text.textSource == AlternativeText)
168             break;
169         
170         // Once we encounter visible text, or the text from our children that should be used foremost.
171         if (text.textSource == VisibleText || text.textSource == ChildrenText)
172             return text.text;
173         
174         // If there's an element that labels this object and it's not exposed, then we should use
175         // that text as our title.
176         if (text.textSource == LabelByElementText && !m_object->exposesTitleUIElement())
177             return text.text;
178     }
179     
180     return [NSString string];
181 }
182
183 - (NSString *)accessibilityDescription
184 {
185     // Static text objects should not have a description. Its content is communicated in its AXValue.
186     // One exception is the media control labels that have a value and a description. Those are set programatically.
187     if (m_object->roleValue() == StaticTextRole && !m_object->isMediaControlLabel())
188         return [NSString string];
189     
190     Vector<AccessibilityText> textOrder;
191     m_object->accessibilityText(textOrder);
192     
193     bool visibleTextAvailable = false;
194     for (const auto& text : textOrder) {
195         if (text.textSource == AlternativeText)
196             return text.text;
197         
198         switch (text.textSource) {
199         case VisibleText:
200         case ChildrenText:
201         case LabelByElementText:
202             visibleTextAvailable = true;
203             break;
204         default:
205             break;
206         }
207         
208         if (text.textSource == TitleTagText && !visibleTextAvailable)
209             return text.text;
210     }
211     
212     return [NSString string];
213 }
214
215 - (NSString *)accessibilityHelpText
216 {
217     Vector<AccessibilityText> textOrder;
218     m_object->accessibilityText(textOrder);
219     
220     bool descriptiveTextAvailable = false;
221     for (const auto& text : textOrder) {
222         if (text.textSource == HelpText || text.textSource == SummaryText)
223             return text.text;
224         
225         // If an element does NOT have other descriptive text the title tag should be used as its descriptive text.
226         // But, if those ARE available, then the title tag should be used for help text instead.
227         switch (text.textSource) {
228         case AlternativeText:
229         case VisibleText:
230         case ChildrenText:
231         case LabelByElementText:
232             descriptiveTextAvailable = true;
233             break;
234         default:
235             break;
236         }
237         
238         if (text.textSource == TitleTagText && descriptiveTextAvailable)
239             return text.text;
240     }
241     
242     return [NSString string];
243 }
244
245 struct PathConversionInfo {
246     WebAccessibilityObjectWrapperBase *wrapper;
247     CGMutablePathRef path;
248 };
249
250 static void ConvertPathToScreenSpaceFunction(void* info, const PathElement* element)
251 {
252     PathConversionInfo* conversion = (PathConversionInfo*)info;
253     WebAccessibilityObjectWrapperBase *wrapper = conversion->wrapper;
254     CGMutablePathRef newPath = conversion->path;
255     switch (element->type) {
256     case PathElementMoveToPoint:
257     {
258         CGPoint newPoint = [wrapper convertPointToScreenSpace:element->points[0]];
259         CGPathMoveToPoint(newPath, nil, newPoint.x, newPoint.y);
260         break;
261     }
262     case PathElementAddLineToPoint:
263     {
264         CGPoint newPoint = [wrapper convertPointToScreenSpace:element->points[0]];
265         CGPathAddLineToPoint(newPath, nil, newPoint.x, newPoint.y);
266         break;
267     }
268     case PathElementAddQuadCurveToPoint:
269     {
270         CGPoint newPoint1 = [wrapper convertPointToScreenSpace:element->points[0]];
271         CGPoint newPoint2 = [wrapper convertPointToScreenSpace:element->points[1]];
272         CGPathAddQuadCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y);
273         break;
274     }
275     case PathElementAddCurveToPoint:
276     {
277         CGPoint newPoint1 = [wrapper convertPointToScreenSpace:element->points[0]];
278         CGPoint newPoint2 = [wrapper convertPointToScreenSpace:element->points[1]];
279         CGPoint newPoint3 = [wrapper convertPointToScreenSpace:element->points[2]];
280         CGPathAddCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y, newPoint3.x, newPoint3.y);
281         break;
282     }
283     case PathElementCloseSubpath:
284     {
285         CGPathCloseSubpath(newPath);
286         break;
287     }
288     }
289 }
290
291 - (CGPathRef)convertPathToScreenSpace:(Path &)path
292 {
293     PathConversionInfo conversion = { self, CGPathCreateMutable() };
294     path.apply(&conversion, ConvertPathToScreenSpaceFunction);    
295     return (CGPathRef)[(id)conversion.path autorelease];
296 }
297
298 - (CGPoint)convertPointToScreenSpace:(FloatPoint &)point
299 {
300     UNUSED_PARAM(point);
301     ASSERT_NOT_REACHED();
302     return CGPointZero;
303 }
304
305 - (NSString *)ariaLandmarkRoleDescription
306 {
307     switch (m_object->roleValue()) {
308     case LandmarkApplicationRole:
309         return AXARIAContentGroupText(@"ARIALandmarkApplication");
310     case LandmarkBannerRole:
311         return AXARIAContentGroupText(@"ARIALandmarkBanner");
312     case LandmarkComplementaryRole:
313         return AXARIAContentGroupText(@"ARIALandmarkComplementary");
314     case LandmarkContentInfoRole:
315         return AXARIAContentGroupText(@"ARIALandmarkContentInfo");
316     case LandmarkMainRole:
317         return AXARIAContentGroupText(@"ARIALandmarkMain");
318     case LandmarkNavigationRole:
319         return AXARIAContentGroupText(@"ARIALandmarkNavigation");
320     case LandmarkSearchRole:
321         return AXARIAContentGroupText(@"ARIALandmarkSearch");
322     case ApplicationAlertRole:
323         return AXARIAContentGroupText(@"ARIAApplicationAlert");
324     case ApplicationAlertDialogRole:
325         return AXARIAContentGroupText(@"ARIAApplicationAlertDialog");
326     case ApplicationDialogRole:
327         return AXARIAContentGroupText(@"ARIAApplicationDialog");
328     case ApplicationLogRole:
329         return AXARIAContentGroupText(@"ARIAApplicationLog");
330     case ApplicationMarqueeRole:
331         return AXARIAContentGroupText(@"ARIAApplicationMarquee");
332     case ApplicationStatusRole:
333         return AXARIAContentGroupText(@"ARIAApplicationStatus");
334     case ApplicationTimerRole:
335         return AXARIAContentGroupText(@"ARIAApplicationTimer");
336     case DocumentRole:
337         return AXARIAContentGroupText(@"ARIADocument");
338     case DocumentArticleRole:
339         return AXARIAContentGroupText(@"ARIADocumentArticle");
340     case DocumentMathRole:
341         return AXARIAContentGroupText(@"ARIADocumentMath");
342     case DocumentNoteRole:
343         return AXARIAContentGroupText(@"ARIADocumentNote");
344     case DocumentRegionRole:
345         return AXARIAContentGroupText(@"ARIADocumentRegion");
346     case UserInterfaceTooltipRole:
347         return AXARIAContentGroupText(@"ARIAUserInterfaceTooltip");
348     case TabPanelRole:
349         return AXARIAContentGroupText(@"ARIATabPanel");
350     default:
351         return nil;
352     }
353 }
354
355 - (NSString *)accessibilityPlatformMathSubscriptKey
356 {
357     ASSERT_NOT_REACHED();
358     return nil;
359 }
360
361 - (NSString *)accessibilityPlatformMathSuperscriptKey
362 {
363     ASSERT_NOT_REACHED();
364     return nil;    
365 }
366
367 - (NSArray *)accessibilityMathPostscriptPairs
368 {
369     AccessibilityObject::AccessibilityMathMultiscriptPairs pairs;
370     m_object->mathPostscripts(pairs);
371     return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]);
372 }
373
374 - (NSArray *)accessibilityMathPrescriptPairs
375 {
376     AccessibilityObject::AccessibilityMathMultiscriptPairs pairs;
377     m_object->mathPrescripts(pairs);
378     return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]);
379 }
380
381 // This is set by DRT when it wants to listen for notifications.
382 static BOOL accessibilityShouldRepostNotifications;
383 + (void)accessibilitySetShouldRepostNotifications:(BOOL)repost
384 {
385     accessibilityShouldRepostNotifications = repost;
386 }
387
388 - (void)accessibilityPostedNotification:(NSString *)notificationName
389 {
390     if (accessibilityShouldRepostNotifications) {
391         NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:notificationName, @"notificationName", nil];
392         [[NSNotificationCenter defaultCenter] postNotificationName:@"AXDRTNotification" object:self userInfo:userInfo];
393     }
394 }
395
396 @end
397
398 #endif // HAVE(ACCESSIBILITY)