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