1/*
2 * Copyright (C) 2008 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#include "config.h"
27#include "AccessibilityUIElement.h"
28
29#include "AccessibilityController.h"
30#include "DumpRenderTree.h"
31#include "FrameLoadDelegate.h"
32#include <JavaScriptCore/JSStringRef.h>
33#include <tchar.h>
34#include <string>
35
36using std::wstring;
37
38static COMPtr<IAccessibleComparable> comparableObject(IAccessible* accessible)
39{
40    COMPtr<IServiceProvider> serviceProvider(Query, accessible);
41    if (!serviceProvider)
42        return 0;
43    COMPtr<IAccessibleComparable> comparable;
44    serviceProvider->QueryService(SID_AccessibleComparable, __uuidof(IAccessibleComparable), reinterpret_cast<void**>(&comparable));
45    return comparable;
46}
47
48AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
49    : m_element(element)
50{
51}
52
53AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
54    : m_element(other.m_element)
55{
56}
57
58AccessibilityUIElement::~AccessibilityUIElement()
59{
60}
61
62bool AccessibilityUIElement::isEqual(AccessibilityUIElement* otherElement)
63{
64    COMPtr<IAccessibleComparable> comparable = comparableObject(m_element.get());
65    COMPtr<IAccessibleComparable> otherComparable = comparableObject(otherElement->m_element.get());
66    if (!comparable || !otherComparable)
67        return false;
68    BOOL isSame = FALSE;
69    if (FAILED(comparable->isSameObject(otherComparable.get(), &isSame)))
70        return false;
71    return isSame;
72}
73
74void AccessibilityUIElement::getLinkedUIElements(Vector<AccessibilityUIElement>&)
75{
76}
77
78void AccessibilityUIElement::getDocumentLinks(Vector<AccessibilityUIElement>&)
79{
80}
81
82void AccessibilityUIElement::getChildren(Vector<AccessibilityUIElement>& children)
83{
84    long childCount;
85    if (FAILED(m_element->get_accChildCount(&childCount)))
86        return;
87    for (long i = 0; i < childCount; ++i)
88        children.append(getChildAtIndex(i));
89}
90
91void AccessibilityUIElement::getChildrenWithRange(Vector<AccessibilityUIElement>& elementVector, unsigned location, unsigned length)
92{
93    long childCount;
94    unsigned appendedCount = 0;
95    if (FAILED(m_element->get_accChildCount(&childCount)))
96        return;
97    for (long i = location; i < childCount && appendedCount < length; ++i, ++appendedCount)
98        elementVector.append(getChildAtIndex(i));
99}
100
101int AccessibilityUIElement::childrenCount()
102{
103    long childCount;
104    m_element->get_accChildCount(&childCount);
105    return childCount;
106}
107
108int AccessibilityUIElement::rowCount()
109{
110    // FIXME: implement
111    return 0;
112}
113
114int AccessibilityUIElement::columnCount()
115{
116    // FIXME: implement
117    return 0;
118}
119
120AccessibilityUIElement AccessibilityUIElement::elementAtPoint(int x, int y)
121{
122    return 0;
123}
124
125AccessibilityUIElement AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
126{
127    // FIXME: implement
128    return 0;
129}
130
131AccessibilityUIElement AccessibilityUIElement::getChildAtIndex(unsigned index)
132{
133    COMPtr<IDispatch> child;
134    VARIANT vChild;
135    ::VariantInit(&vChild);
136    V_VT(&vChild) = VT_I4;
137    // In MSAA, index 0 is the object itself.
138    V_I4(&vChild) = index + 1;
139    if (FAILED(m_element->get_accChild(vChild, &child)))
140        return 0;
141    return COMPtr<IAccessible>(Query, child);
142}
143
144unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
145{
146    // FIXME: implement
147    return 0;
148}
149
150JSStringRef AccessibilityUIElement::allAttributes()
151{
152    return JSStringCreateWithCharacters(0, 0);
153}
154
155JSStringRef AccessibilityUIElement::attributesOfLinkedUIElements()
156{
157    return JSStringCreateWithCharacters(0, 0);
158}
159
160JSStringRef AccessibilityUIElement::attributesOfDocumentLinks()
161{
162    return JSStringCreateWithCharacters(0, 0);
163}
164
165AccessibilityUIElement AccessibilityUIElement::titleUIElement()
166{
167    return 0;
168}
169
170AccessibilityUIElement AccessibilityUIElement::parentElement()
171{
172    COMPtr<IDispatch> parent;
173    m_element->get_accParent(&parent);
174
175    COMPtr<IAccessible> parentAccessible(Query, parent);
176    return parentAccessible;
177}
178
179JSStringRef AccessibilityUIElement::attributesOfChildren()
180{
181    return JSStringCreateWithCharacters(0, 0);
182}
183
184JSStringRef AccessibilityUIElement::parameterizedAttributeNames()
185{
186    return JSStringCreateWithCharacters(0, 0);
187}
188
189static VARIANT& self()
190{
191    static VARIANT vSelf;
192    static bool haveInitialized;
193
194    if (!haveInitialized) {
195        ::VariantInit(&vSelf);
196        V_VT(&vSelf) = VT_I4;
197        V_I4(&vSelf) = CHILDID_SELF;
198    }
199    return vSelf;
200}
201
202JSStringRef AccessibilityUIElement::role()
203{
204    VARIANT vRole;
205    if (FAILED(m_element->get_accRole(self(), &vRole)))
206        return JSStringCreateWithCharacters(0, 0);
207
208    ASSERT(V_VT(&vRole) == VT_I4 || V_VT(&vRole) == VT_BSTR);
209
210    wstring result;
211    if (V_VT(&vRole) == VT_I4) {
212        unsigned roleTextLength = ::GetRoleText(V_I4(&vRole), 0, 0) + 1;
213
214        Vector<TCHAR> roleText(roleTextLength);
215
216        ::GetRoleText(V_I4(&vRole), roleText.data(), roleTextLength);
217
218        result = roleText.data();
219    } else if (V_VT(&vRole) == VT_BSTR)
220        result = wstring(V_BSTR(&vRole), ::SysStringLen(V_BSTR(&vRole)));
221
222    ::VariantClear(&vRole);
223
224    return JSStringCreateWithCharacters(result.data(), result.length());
225}
226
227JSStringRef AccessibilityUIElement::subrole()
228{
229    return 0;
230}
231
232JSStringRef AccessibilityUIElement::roleDescription()
233{
234    return 0;
235}
236
237JSStringRef AccessibilityUIElement::title()
238{
239    BSTR titleBSTR;
240    if (FAILED(m_element->get_accName(self(), &titleBSTR)) || !titleBSTR)
241        return JSStringCreateWithCharacters(0, 0);
242    wstring title(titleBSTR, SysStringLen(titleBSTR));
243    ::SysFreeString(titleBSTR);
244    return JSStringCreateWithCharacters(title.data(), title.length());
245}
246
247JSStringRef AccessibilityUIElement::description()
248{
249    BSTR descriptionBSTR;
250    if (FAILED(m_element->get_accDescription(self(), &descriptionBSTR)) || !descriptionBSTR)
251        return JSStringCreateWithCharacters(0, 0);
252    wstring description(descriptionBSTR, SysStringLen(descriptionBSTR));
253    ::SysFreeString(descriptionBSTR);
254    return JSStringCreateWithCharacters(description.data(), description.length());
255}
256
257JSStringRef AccessibilityUIElement::stringValue()
258{
259    return JSStringCreateWithCharacters(0, 0);
260}
261
262JSStringRef AccessibilityUIElement::language()
263{
264    return JSStringCreateWithCharacters(0, 0);
265}
266
267JSStringRef AccessibilityUIElement::helpText() const
268{
269    return 0;
270}
271
272double AccessibilityUIElement::x()
273{
274    long x, y, width, height;
275    if (FAILED(m_element->accLocation(&x, &y, &width, &height, self())))
276        return 0;
277    return x;
278}
279
280double AccessibilityUIElement::y()
281{
282    long x, y, width, height;
283    if (FAILED(m_element->accLocation(&x, &y, &width, &height, self())))
284        return 0;
285    return y;
286}
287
288double AccessibilityUIElement::width()
289{
290    long x, y, width, height;
291    if (FAILED(m_element->accLocation(&x, &y, &width, &height, self())))
292        return 0;
293    return width;
294}
295
296double AccessibilityUIElement::height()
297{
298    long x, y, width, height;
299    if (FAILED(m_element->accLocation(&x, &y, &width, &height, self())))
300        return 0;
301    return height;
302}
303
304double AccessibilityUIElement::clickPointX()
305{
306    return 0;
307}
308
309double AccessibilityUIElement::clickPointY()
310{
311    return 0;
312}
313
314JSStringRef AccessibilityUIElement::valueDescription()
315{
316    return 0;
317}
318
319static DWORD accessibilityState(COMPtr<IAccessible> element)
320{
321    VARIANT state;
322    element->get_accState(self(), &state);
323
324    ASSERT(V_VT(&state) == VT_I4);
325
326    DWORD result = state.lVal;
327    VariantClear(&state);
328
329    return result;
330}
331
332bool AccessibilityUIElement::isFocused() const
333{
334    // FIXME: implement
335    return false;
336}
337
338bool AccessibilityUIElement::isSelected() const
339{
340    DWORD state = accessibilityState(m_element);
341    return (state & STATE_SYSTEM_SELECTED) == STATE_SYSTEM_SELECTED;
342}
343
344int AccessibilityUIElement::hierarchicalLevel() const
345{
346    return 0;
347}
348
349bool AccessibilityUIElement::ariaIsGrabbed() const
350{
351    return false;
352}
353
354JSStringRef AccessibilityUIElement::ariaDropEffects() const
355{
356    return 0;
357}
358
359bool AccessibilityUIElement::isExpanded() const
360{
361    return false;
362}
363
364bool AccessibilityUIElement::isChecked() const
365{
366    VARIANT vState;
367    if (FAILED(m_element->get_accState(self(), &vState)))
368        return false;
369
370    return vState.lVal & STATE_SYSTEM_CHECKED;
371}
372
373JSStringRef AccessibilityUIElement::orientation() const
374{
375    return 0;
376}
377
378double AccessibilityUIElement::intValue() const
379{
380    BSTR valueBSTR;
381    if (FAILED(m_element->get_accValue(self(), &valueBSTR)) || !valueBSTR)
382        return 0;
383    wstring value(valueBSTR, SysStringLen(valueBSTR));
384    ::SysFreeString(valueBSTR);
385    TCHAR* ignored;
386    return _tcstod(value.data(), &ignored);
387}
388
389double AccessibilityUIElement::minValue()
390{
391    return 0;
392}
393
394double AccessibilityUIElement::maxValue()
395{
396    return 0;
397}
398
399bool AccessibilityUIElement::isActionSupported(JSStringRef action)
400{
401    return false;
402}
403
404bool AccessibilityUIElement::isEnabled()
405{
406    DWORD state = accessibilityState(m_element);
407    return (state & STATE_SYSTEM_UNAVAILABLE) != STATE_SYSTEM_UNAVAILABLE;
408}
409
410bool AccessibilityUIElement::isRequired() const
411{
412    return false;
413}
414
415
416int AccessibilityUIElement::insertionPointLineNumber()
417{
418    return 0;
419}
420
421JSStringRef AccessibilityUIElement::attributesOfColumnHeaders()
422{
423    return JSStringCreateWithCharacters(0, 0);
424}
425
426JSStringRef AccessibilityUIElement::attributesOfRowHeaders()
427{
428    return JSStringCreateWithCharacters(0, 0);
429}
430
431JSStringRef AccessibilityUIElement::attributesOfColumns()
432{
433    return JSStringCreateWithCharacters(0, 0);
434}
435
436JSStringRef AccessibilityUIElement::attributesOfRows()
437{
438    return JSStringCreateWithCharacters(0, 0);
439}
440
441JSStringRef AccessibilityUIElement::attributesOfVisibleCells()
442{
443    return JSStringCreateWithCharacters(0, 0);
444}
445
446JSStringRef AccessibilityUIElement::attributesOfHeader()
447{
448    return JSStringCreateWithCharacters(0, 0);
449}
450
451int AccessibilityUIElement::indexInTable()
452{
453    return 0;
454}
455
456JSStringRef AccessibilityUIElement::rowIndexRange()
457{
458    return JSStringCreateWithCharacters(0, 0);
459}
460
461JSStringRef AccessibilityUIElement::columnIndexRange()
462{
463    return JSStringCreateWithCharacters(0, 0);
464}
465
466int AccessibilityUIElement::lineForIndex(int)
467{
468    return 0;
469}
470
471JSStringRef AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
472{
473    return JSStringCreateWithCharacters(0, 0);
474}
475
476JSStringRef AccessibilityUIElement::stringForRange(unsigned, unsigned)
477{
478    return JSStringCreateWithCharacters(0, 0);
479}
480
481JSStringRef AccessibilityUIElement::attributedStringForRange(unsigned, unsigned)
482{
483    return JSStringCreateWithCharacters(0, 0);
484}
485
486bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned, unsigned)
487{
488    return false;
489}
490
491AccessibilityUIElement AccessibilityUIElement::cellForColumnAndRow(unsigned column, unsigned row)
492{
493    return 0;
494}
495
496JSStringRef AccessibilityUIElement::selectedTextRange()
497{
498    return JSStringCreateWithCharacters(0, 0);
499}
500
501void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
502{
503}
504
505JSStringRef AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
506{
507    // FIXME: implement
508    return JSStringCreateWithCharacters(0, 0);
509}
510
511bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
512{
513    // FIXME: implement
514    return false;
515}
516
517bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
518{
519    return false;
520}
521
522bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
523{
524    return false;
525}
526
527void AccessibilityUIElement::increment()
528{
529}
530
531void AccessibilityUIElement::decrement()
532{
533}
534
535void AccessibilityUIElement::showMenu()
536{
537    ASSERT(hasPopup());
538    m_element->accDoDefaultAction(self());
539}
540
541void AccessibilityUIElement::press()
542{
543    // FIXME: implement
544}
545
546AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
547{
548    return 0;
549}
550
551AccessibilityUIElement AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
552{
553    return 0;
554}
555
556AccessibilityUIElement AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
557{
558    return 0;
559}
560
561AccessibilityUIElement AccessibilityUIElement::selectedRowAtIndex(unsigned index)
562{
563    return 0;
564}
565
566AccessibilityUIElement AccessibilityUIElement::disclosedByRow()
567{
568    return 0;
569}
570
571JSStringRef AccessibilityUIElement::accessibilityValue() const
572{
573    BSTR valueBSTR;
574    if (FAILED(m_element->get_accValue(self(), &valueBSTR)) || !valueBSTR)
575        return JSStringCreateWithCharacters(0, 0);
576
577    wstring value(valueBSTR, SysStringLen(valueBSTR));
578    ::SysFreeString(valueBSTR);
579
580    return JSStringCreateWithCharacters(value.data(), value.length());
581}
582
583
584JSStringRef AccessibilityUIElement::documentEncoding()
585{
586    return JSStringCreateWithCharacters(0, 0);
587}
588
589JSStringRef AccessibilityUIElement::documentURI()
590{
591    return JSStringCreateWithCharacters(0, 0);
592}
593
594JSStringRef AccessibilityUIElement::url()
595{
596    // FIXME: implement
597    return JSStringCreateWithCharacters(0, 0);
598}
599
600bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallback)
601{
602    if (!functionCallback)
603        return false;
604
605    sharedFrameLoadDelegate->accessibilityController()->addNotificationListener(m_element, functionCallback);
606    return true;
607}
608
609void AccessibilityUIElement::removeNotificationListener()
610{
611    // FIXME: implement
612}
613
614bool AccessibilityUIElement::isFocusable() const
615{
616    // FIXME: implement
617    return false;
618}
619
620bool AccessibilityUIElement::isSelectable() const
621{
622    DWORD state = accessibilityState(m_element);
623    return (state & STATE_SYSTEM_SELECTABLE) == STATE_SYSTEM_SELECTABLE;
624}
625
626bool AccessibilityUIElement::isMultiSelectable() const
627{
628    DWORD multiSelectable = STATE_SYSTEM_EXTSELECTABLE | STATE_SYSTEM_MULTISELECTABLE;
629    DWORD state = accessibilityState(m_element);
630    return (state & multiSelectable) == multiSelectable;
631}
632
633bool AccessibilityUIElement::isVisible() const
634{
635    DWORD state = accessibilityState(m_element);
636    return (state & STATE_SYSTEM_INVISIBLE) != STATE_SYSTEM_INVISIBLE;
637}
638
639bool AccessibilityUIElement::isOffScreen() const
640{
641    DWORD state = accessibilityState(m_element);
642    return (state & STATE_SYSTEM_OFFSCREEN) == STATE_SYSTEM_OFFSCREEN;
643}
644
645bool AccessibilityUIElement::isCollapsed() const
646{
647    DWORD state = accessibilityState(m_element);
648    return (state & STATE_SYSTEM_COLLAPSED) == STATE_SYSTEM_COLLAPSED;
649}
650
651bool AccessibilityUIElement::isIgnored() const
652{
653    // FIXME: implement
654    return false;
655}
656
657bool AccessibilityUIElement::hasPopup() const
658{
659    DWORD state = accessibilityState(m_element);
660    return (state & STATE_SYSTEM_HASPOPUP) == STATE_SYSTEM_HASPOPUP;
661}
662
663void AccessibilityUIElement::takeFocus()
664{
665    m_element->accSelect(SELFLAG_TAKEFOCUS, self());
666}
667
668void AccessibilityUIElement::takeSelection()
669{
670    m_element->accSelect(SELFLAG_TAKESELECTION, self());
671}
672
673void AccessibilityUIElement::addSelection()
674{
675    m_element->accSelect(SELFLAG_ADDSELECTION, self());
676}
677
678void AccessibilityUIElement::removeSelection()
679{
680    m_element->accSelect(SELFLAG_REMOVESELECTION, self());
681}
682