AX: Make SVG Group containers accessible elements
[WebKit-https.git] / Tools / DumpRenderTree / ios / AccessibilityUIElementIOS.mm
1 /*
2  * Copyright (C) 2009 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "DumpRenderTree.h"
28 #import "AccessibilityUIElement.h"
29
30 #import "AccessibilityCommonMac.h"
31 #import <Foundation/Foundation.h>
32 #import <JavaScriptCore/JSRetainPtr.h>
33 #import <JavaScriptCore/JSStringRef.h>
34 #import <JavaScriptCore/JSStringRefCF.h>
35 #import <WebCore/TextGranularity.h>
36 #import <WebKit/WebFrame.h>
37 #import <WebKit/WebHTMLView.h>
38 #import <WebKit/WebTypesInternal.h>
39 #import <wtf/RetainPtr.h>
40 #import <wtf/Vector.h>
41
42 #if PLATFORM(IOS)
43
44 #import <UIKit/UIKit.h>
45
46 typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, void* context);
47
48 AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
49     : m_element(element)
50 {
51     [m_element retain];
52 }
53
54 AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
55     : m_element(other.m_element)
56 {
57     [m_element retain];
58 }
59
60 AccessibilityUIElement::~AccessibilityUIElement()
61 {
62     [m_element release];
63 }
64
65 @interface NSObject (UIAccessibilityHidden)
66 - (id)accessibilityHitTest:(CGPoint)point;
67 - (id)accessibilityLinkedElement;
68 - (NSRange)accessibilityColumnRange;
69 - (NSRange)accessibilityRowRange;
70 - (id)accessibilityElementForRow:(NSInteger)row andColumn:(NSInteger)column;
71 - (NSURL *)accessibilityURL;
72 - (NSArray *)accessibilityHeaderElements;
73 - (NSString *)accessibilityPlaceholderValue;
74 - (NSString *)stringForRange:(NSRange)range;
75 - (NSArray *)elementsForRange:(NSRange)range;
76 - (NSString *)selectionRangeString;
77 - (CGPoint)accessibilityClickPoint;
78 - (void)accessibilityModifySelection:(WebCore::TextGranularity)granularity increase:(BOOL)increase;
79 - (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context;
80 @end
81
82 static JSStringRef concatenateAttributeAndValue(NSString* attribute, NSString* value)
83 {
84     Vector<UniChar> buffer([attribute length]);
85     [attribute getCharacters:buffer.data()];
86     buffer.append(':');
87     buffer.append(' ');
88     
89     Vector<UniChar> valueBuffer([value length]);
90     [value getCharacters:valueBuffer.data()];
91     buffer.append(valueBuffer);
92     
93     return JSStringCreateWithCharacters(buffer.data(), buffer.size());
94 }
95
96 #pragma mark iPhone Attributes
97
98 JSStringRef AccessibilityUIElement::iphoneLabel()
99 {
100     return concatenateAttributeAndValue(@"AXLabel", [m_element accessibilityLabel]);
101 }
102
103 JSStringRef AccessibilityUIElement::iphoneHint()
104 {
105     return concatenateAttributeAndValue(@"AXHint", [m_element accessibilityHint]);
106 }
107
108 JSStringRef AccessibilityUIElement::iphoneValue()
109 {
110     return concatenateAttributeAndValue(@"AXValue", [m_element accessibilityValue]);
111 }
112
113 JSStringRef AccessibilityUIElement::iphoneIdentifier()
114 {
115     return concatenateAttributeAndValue(@"AXIdentifier", [m_element accessibilityIdentifier]);
116 }
117
118 JSStringRef AccessibilityUIElement::iphoneTraits()
119 {
120     return concatenateAttributeAndValue(@"AXTraits", [NSString stringWithFormat:@"%qu", [m_element accessibilityTraits]]);
121 }
122
123 bool AccessibilityUIElement::iphoneIsElement()
124 {
125     return [m_element isAccessibilityElement];
126 }
127
128 int AccessibilityUIElement::iphoneElementTextPosition()
129 {
130     NSRange range = [[m_element valueForKey:@"elementTextRange"] rangeValue];
131     return range.location;
132 }
133
134 int AccessibilityUIElement::iphoneElementTextLength()
135 {
136     NSRange range = [[m_element valueForKey:@"elementTextRange"] rangeValue];
137     return range.length;    
138 }
139
140 JSStringRef AccessibilityUIElement::url()
141 {
142     NSURL *url = [m_element accessibilityURL];
143     return [[url absoluteString] createJSStringRef];    
144 }
145
146 double AccessibilityUIElement::x()
147 {
148     CGRect frame = [m_element accessibilityFrame];
149     return frame.origin.x;
150 }
151
152 double AccessibilityUIElement::y()
153 {
154     CGRect frame = [m_element accessibilityFrame];
155     return frame.origin.y;
156 }
157
158 double AccessibilityUIElement::width()
159 {
160     CGRect frame = [m_element accessibilityFrame];
161     return frame.size.width;
162 }
163
164 double AccessibilityUIElement::height()
165 {
166     CGRect frame = [m_element accessibilityFrame];
167     return frame.size.height;
168 }
169
170 double AccessibilityUIElement::clickPointX()
171 {
172     CGPoint centerPoint = [m_element accessibilityClickPoint];
173     return centerPoint.x;
174 }
175
176 double AccessibilityUIElement::clickPointY()
177 {
178     CGPoint centerPoint = [m_element accessibilityClickPoint];
179     return centerPoint.y;
180 }
181
182 void AccessibilityUIElement::getChildren(Vector<AccessibilityUIElement>& elementVector)
183 {
184     NSInteger childCount = [m_element accessibilityElementCount];
185     for (NSInteger k = 0; k < childCount; ++k)
186         elementVector.append(AccessibilityUIElement([m_element accessibilityElementAtIndex:k]));
187 }
188
189 void AccessibilityUIElement::getChildrenWithRange(Vector<AccessibilityUIElement>& elementVector, unsigned location, unsigned length)
190 {
191     NSUInteger childCount = [m_element accessibilityElementCount];
192     for (NSUInteger k = location; k < childCount && k < (location+length); ++k)
193         elementVector.append(AccessibilityUIElement([m_element accessibilityElementAtIndex:k]));    
194 }
195
196 int AccessibilityUIElement::childrenCount()
197 {
198     Vector<AccessibilityUIElement> children;
199     getChildren(children);
200     
201     return children.size();
202 }
203
204 AccessibilityUIElement AccessibilityUIElement::elementAtPoint(int x, int y)
205 {
206     id element = [m_element accessibilityHitTest:NSMakePoint(x, y)];
207     if (!element)
208         return nil;
209     
210     return AccessibilityUIElement(element); 
211 }
212
213 unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
214 {
215     return 0;
216 }
217
218 AccessibilityUIElement AccessibilityUIElement::getChildAtIndex(unsigned index)
219 {
220     Vector<AccessibilityUIElement> children;
221     getChildrenWithRange(children, index, 1);
222     
223     if (children.size() == 1)
224         return children[0];
225     return nil;
226 }
227
228 AccessibilityUIElement AccessibilityUIElement::headerElementAtIndex(unsigned index)
229 {
230     NSArray *headers = [m_element accessibilityHeaderElements];
231     if (index < [headers count])
232         return [headers objectAtIndex:index];
233     
234     return 0;
235 }
236
237 AccessibilityUIElement AccessibilityUIElement::linkedElement()
238 {
239     id linkedElement = [m_element accessibilityLinkedElement];
240     if (linkedElement)
241         return AccessibilityUIElement(linkedElement);
242     
243     return 0;
244 }
245
246 AccessibilityUIElement AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
247 {
248     // FIXME: implement
249     return 0;
250 }
251
252 AccessibilityUIElement AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
253 {
254     return 0;
255 }
256
257 AccessibilityUIElement AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
258 {
259     return 0;
260 }
261
262 AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
263 {
264     return 0;
265 }
266
267 AccessibilityUIElement AccessibilityUIElement::selectedRowAtIndex(unsigned index)
268 {
269     return 0;
270 }
271
272 AccessibilityUIElement AccessibilityUIElement::rowAtIndex(unsigned index)
273 {
274     return 0;
275 }
276
277 AccessibilityUIElement AccessibilityUIElement::titleUIElement()
278 {
279     return 0;
280 }
281
282 AccessibilityUIElement AccessibilityUIElement::parentElement()
283 {
284     id accessibilityObject = [m_element accessibilityContainer];
285     if (accessibilityObject)
286         return AccessibilityUIElement(accessibilityObject);
287     
288     return nil;
289 }
290
291 AccessibilityUIElement AccessibilityUIElement::disclosedByRow()
292 {
293     return 0;
294 }
295
296 void AccessibilityUIElement::increaseTextSelection()
297 {
298     [m_element accessibilityModifySelection:WebCore::CharacterGranularity increase:YES];
299 }
300
301 void AccessibilityUIElement::decreaseTextSelection()
302 {
303     [m_element accessibilityModifySelection:WebCore::CharacterGranularity increase:NO];    
304 }
305
306 JSStringRef AccessibilityUIElement::stringForSelection() 
307
308     NSString *stringForRange = [m_element selectionRangeString];
309     if (!stringForRange)
310         return 0;
311     
312     return [stringForRange createJSStringRef];
313 }
314
315 JSStringRef AccessibilityUIElement::stringForRange(unsigned location, unsigned length) 
316
317     NSString *stringForRange = [m_element stringForRange:NSMakeRange(location, length)];
318     if (!stringForRange)
319         return 0;
320     
321     return [stringForRange createJSStringRef];
322 }
323
324 JSStringRef AccessibilityUIElement::attributedStringForRange(unsigned, unsigned)
325 {
326     return JSStringCreateWithCharacters(0, 0);
327 }
328
329 bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned, unsigned)
330 {
331     return false;
332 }
333
334
335 void AccessibilityUIElement::elementsForRange(unsigned location, unsigned length, Vector<AccessibilityUIElement>& elements)
336
337     NSArray *elementsForRange = [m_element elementsForRange:NSMakeRange(location, length)];
338     for (id object in elementsForRange) {
339         AccessibilityUIElement element = AccessibilityUIElement(object);
340         elements.append(element);
341     }
342 }
343
344 #pragma mark Unused
345
346 void AccessibilityUIElement::getLinkedUIElements(Vector<AccessibilityUIElement>& elementVector)
347 {
348 }
349
350 void AccessibilityUIElement::getDocumentLinks(Vector<AccessibilityUIElement>& elementVector)
351 {
352 }
353
354 JSStringRef AccessibilityUIElement::attributesOfLinkedUIElements()
355 {
356     return JSStringCreateWithCharacters(0, 0);
357 }
358
359 JSStringRef AccessibilityUIElement::attributesOfDocumentLinks()
360 {
361     return JSStringCreateWithCharacters(0, 0);
362 }
363
364 JSStringRef AccessibilityUIElement::attributesOfChildren()
365 {
366     return JSStringCreateWithCharacters(0, 0);
367 }
368
369 JSStringRef AccessibilityUIElement::allAttributes()
370 {
371     return JSStringCreateWithCharacters(0, 0);
372 }
373
374 JSStringRef AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
375 {
376     if (JSStringIsEqualToUTF8CString(attribute, "AXPlaceholderValue"))
377         return [[m_element accessibilityPlaceholderValue] createJSStringRef];
378     
379     return JSStringCreateWithCharacters(0, 0);
380 }
381
382 bool AccessibilityUIElement::isPressActionSupported()
383 {
384     return false;
385 }
386
387 bool AccessibilityUIElement::isIncrementActionSupported()
388 {
389     return false;
390 }
391
392 bool AccessibilityUIElement::isDecrementActionSupported()
393 {
394     return false;
395 }
396
397 bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
398 {
399     return false;
400 }
401
402 bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
403 {
404     return false;
405 }
406
407 bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
408 {
409     return false;
410 }
411
412 JSStringRef AccessibilityUIElement::parameterizedAttributeNames()
413 {
414     return JSStringCreateWithCharacters(0, 0);
415 }
416
417 JSStringRef AccessibilityUIElement::role()
418 {
419     return JSStringCreateWithCharacters(0, 0);
420 }
421
422 JSStringRef AccessibilityUIElement::subrole()
423 {
424     return JSStringCreateWithCharacters(0, 0);
425 }
426
427 JSStringRef AccessibilityUIElement::roleDescription()
428 {
429     return JSStringCreateWithCharacters(0, 0);
430 }
431
432 JSStringRef AccessibilityUIElement::title()
433 {
434     return JSStringCreateWithCharacters(0, 0);
435 }
436
437 JSStringRef AccessibilityUIElement::description()
438 {
439     return JSStringCreateWithCharacters(0, 0);
440 }    
441
442 JSStringRef AccessibilityUIElement::orientation() const
443 {
444     return JSStringCreateWithCharacters(0, 0);
445 }
446
447 JSStringRef AccessibilityUIElement::stringValue()
448 {
449     return JSStringCreateWithCharacters(0, 0);
450 }
451
452 JSStringRef AccessibilityUIElement::language()
453 {
454     return JSStringCreateWithCharacters(0, 0);
455 }
456
457 JSStringRef AccessibilityUIElement::helpText() const
458 {
459     return JSStringCreateWithCharacters(0, 0);
460 }
461
462 double AccessibilityUIElement::intValue() const
463 {
464     return 0.0f;
465 }
466
467 double AccessibilityUIElement::minValue()
468 {
469     return 0.0f;
470 }
471
472 double AccessibilityUIElement::maxValue()
473 {
474     return 0.0;
475 }
476
477 JSStringRef AccessibilityUIElement::valueDescription()
478 {
479     return JSStringCreateWithCharacters(0, 0);
480 }
481
482 int AccessibilityUIElement::insertionPointLineNumber()
483 {
484     return -1;
485 }
486
487 bool AccessibilityUIElement::isEnabled()
488 {
489     return false;
490 }
491
492 bool AccessibilityUIElement::isRequired() const
493 {
494     return false;
495 }
496
497 bool AccessibilityUIElement::isFocused() const
498 {
499     // FIXME: implement
500     return false;
501 }
502
503 bool AccessibilityUIElement::isSelected() const
504 {
505     UIAccessibilityTraits traits = [m_element accessibilityTraits];
506     return (traits & UIAccessibilityTraitSelected);
507 }
508
509 bool AccessibilityUIElement::isExpanded() const
510 {
511     return false;
512 }
513
514 bool AccessibilityUIElement::isChecked() const
515 {
516     return false;
517 }
518
519 int AccessibilityUIElement::hierarchicalLevel() const
520 {
521     return 0;
522 }
523
524 bool AccessibilityUIElement::ariaIsGrabbed() const
525 {
526     return false;
527 }
528
529 JSStringRef AccessibilityUIElement::ariaDropEffects() const
530 {
531     return JSStringCreateWithCharacters(0, 0);
532 }
533
534 int AccessibilityUIElement::lineForIndex(int index)
535 {
536     return -1;
537 }
538
539 JSStringRef AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
540 {
541     return JSStringCreateWithCharacters(0, 0);
542 }
543
544 JSStringRef AccessibilityUIElement::attributesOfColumnHeaders()
545 {
546     return JSStringCreateWithCharacters(0, 0);
547 }
548
549 JSStringRef AccessibilityUIElement::attributesOfRowHeaders()
550 {
551     return JSStringCreateWithCharacters(0, 0);
552 }
553
554 JSStringRef AccessibilityUIElement::attributesOfColumns()
555 {
556     return JSStringCreateWithCharacters(0, 0);
557 }
558
559 JSStringRef AccessibilityUIElement::attributesOfRows()
560 {
561     return JSStringCreateWithCharacters(0, 0);
562 }
563
564 JSStringRef AccessibilityUIElement::attributesOfVisibleCells()
565 {
566     return JSStringCreateWithCharacters(0, 0);
567 }
568
569 JSStringRef AccessibilityUIElement::attributesOfHeader()
570 {
571     return JSStringCreateWithCharacters(0, 0);
572 }
573
574 int AccessibilityUIElement::rowCount()
575 {
576     return -1;
577 }
578
579 int AccessibilityUIElement::columnCount()
580 {
581     return -1;
582 }
583
584 int AccessibilityUIElement::indexInTable()
585 {
586     return -1;
587 }
588
589 JSStringRef AccessibilityUIElement::rowIndexRange()
590 {
591     NSRange range = [m_element accessibilityRowRange];
592     NSMutableString* rangeDescription = [NSMutableString stringWithFormat:@"{%lu, %lu}", (unsigned long)range.location, (unsigned long)range.length];
593     return [rangeDescription createJSStringRef];
594 }
595
596 JSStringRef AccessibilityUIElement::columnIndexRange()
597 {
598     NSRange range = [m_element accessibilityColumnRange];
599     NSMutableString* rangeDescription = [NSMutableString stringWithFormat:@"{%lu, %lu}", (unsigned long)range.location, (unsigned long)range.length];
600     return [rangeDescription createJSStringRef];    
601 }
602
603 AccessibilityUIElement AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
604 {
605     return AccessibilityUIElement([m_element accessibilityElementForRow:row andColumn:col]);
606 }
607
608 JSStringRef AccessibilityUIElement::selectedTextRange()
609 {
610     return JSStringCreateWithCharacters(0, 0);
611 }
612
613 void AccessibilityUIElement::assistiveTechnologySimulatedFocus()
614 {
615     [m_element accessibilityElementDidBecomeFocused];
616 }
617
618 void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
619 {
620 }
621
622 void AccessibilityUIElement::increment()
623 {
624     [m_element accessibilityIncrement];
625 }
626
627 void AccessibilityUIElement::decrement()
628 {
629     [m_element accessibilityDecrement];
630 }
631
632 void AccessibilityUIElement::showMenu()
633 {
634 }
635
636 void AccessibilityUIElement::press()
637 {
638 }
639
640 JSStringRef AccessibilityUIElement::accessibilityValue() const
641 {
642     // FIXME: implement
643     return JSStringCreateWithCharacters(0, 0);
644 }
645
646 JSStringRef AccessibilityUIElement::documentEncoding()
647 {
648     return JSStringCreateWithCharacters(0, 0);
649 }
650
651 JSStringRef AccessibilityUIElement::documentURI()
652 {
653     return JSStringCreateWithCharacters(0, 0);
654 }
655
656 static void _accessibilityNotificationCallback(id element, NSString* notification, void* context)
657 {
658     if (!context)
659         return;
660     
661     JSObjectRef functionCallback = static_cast<JSObjectRef>(context);
662     
663     JSRetainPtr<JSStringRef> jsNotification(Adopt, [notification createJSStringRef]);
664     JSValueRef argument = JSValueMakeString([mainFrame globalContext], jsNotification.get());
665     JSObjectCallAsFunction([mainFrame globalContext], functionCallback, NULL, 1, &argument, NULL);
666 }
667
668 bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallback)
669 {
670     if (!functionCallback)
671         return false;
672     
673     m_notificationFunctionCallback = functionCallback;
674     [platformUIElement() accessibilitySetPostedNotificationCallback:_accessibilityNotificationCallback withContext:reinterpret_cast<void*>(m_notificationFunctionCallback)];
675     return true;    
676 }
677
678 void AccessibilityUIElement::removeNotificationListener()
679 {
680     m_notificationFunctionCallback = 0;
681     [platformUIElement() accessibilitySetPostedNotificationCallback:nil withContext:nil];
682 }
683
684 bool AccessibilityUIElement::isFocusable() const
685 {
686     // FIXME: implement
687     return false;
688 }
689
690 bool AccessibilityUIElement::isSelectable() const
691 {
692     // FIXME: implement
693     return false;
694 }
695
696 bool AccessibilityUIElement::isMultiSelectable() const
697 {
698     // FIXME: implement
699     return false;
700 }
701
702 bool AccessibilityUIElement::isSelectedOptionActive() const
703 {
704     // FIXME: implement
705     return false;
706 }
707
708 bool AccessibilityUIElement::isVisible() const
709 {
710     // FIXME: implement
711     return false;
712 }
713
714 bool AccessibilityUIElement::isOffScreen() const
715 {
716     // FIXME: implement
717     return false;
718 }
719
720 bool AccessibilityUIElement::isCollapsed() const
721 {
722     // FIXME: implement
723     return false;
724 }
725
726 bool AccessibilityUIElement::isIgnored() const
727 {
728     // FIXME: implement
729     return false;
730 }
731
732 bool AccessibilityUIElement::hasPopup() const
733 {
734     // FIXME: implement
735     return false;
736 }
737
738 void AccessibilityUIElement::takeFocus()
739 {
740     // FIXME: implement
741 }
742
743 void AccessibilityUIElement::takeSelection()
744 {
745     // FIXME: implement
746 }
747
748 void AccessibilityUIElement::addSelection()
749 {
750     // FIXME: implement
751 }
752
753 void AccessibilityUIElement::removeSelection()
754 {
755     // FIXME: implement
756 }
757
758 AccessibilityUIElement AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText)
759 {
760     // FIXME: implement
761     return 0;
762 }
763
764 double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute)
765 {
766     // FIXME: implement
767     return 0;
768 }
769 #endif // PLATFORM(IOS)