1/*
2 * Copyright (C) 2008, 2009, 2010, 2013 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2012 Serotek Corporation. All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27
28#include "config.h"
29#include "AXObjectCache.h"
30
31#include "AccessibilityObject.h"
32#include "Chrome.h"
33#include "ChromeClient.h"
34#include "Document.h"
35#include "Page.h"
36#include "RenderObject.h"
37
38// Provided by IAccessibleEventID.idl
39#define IA2_EVENT_DOCUMENT_LOAD_COMPLETE    261
40
41using namespace std;
42
43namespace WebCore {
44
45void AXObjectCache::detachWrapper(AccessibilityObject* obj, AccessibilityDetachmentType)
46{
47    // On Windows, AccessibilityObjects are created when get_accChildCount is
48    // called, but they are not wrapped until get_accChild is called, so this
49    // object may not have a wrapper.
50    if (AccessibilityObjectWrapper* wrapper = obj->wrapper())
51        wrapper->detach();
52}
53
54void AXObjectCache::attachWrapper(AccessibilityObject*)
55{
56    // On Windows, AccessibilityObjects are wrapped when the accessibility
57    // software requests them via get_accChild.
58}
59
60void AXObjectCache::handleScrolledToAnchor(const Node* anchorNode)
61{
62    // The anchor node may not be accessible. Post the notification for the
63    // first accessible object.
64    postPlatformNotification(AccessibilityObject::firstAccessibleObjectFromNode(anchorNode), AXScrolledToAnchor);
65}
66
67void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification)
68{
69    if (!obj)
70        return;
71
72    Document* document = obj->document();
73    if (!document)
74        return;
75
76    Page* page = document->page();
77    if (!page || !page->chrome().platformPageClient())
78        return;
79
80    DWORD msaaEvent;
81    switch (notification) {
82        case AXCheckedStateChanged:
83            msaaEvent = EVENT_OBJECT_STATECHANGE;
84            break;
85
86        case AXFocusedUIElementChanged:
87        case AXActiveDescendantChanged:
88            msaaEvent = EVENT_OBJECT_FOCUS;
89            break;
90
91        case AXScrolledToAnchor:
92            msaaEvent = EVENT_SYSTEM_SCROLLINGSTART;
93            break;
94
95        case AXLayoutComplete:
96            msaaEvent = EVENT_OBJECT_REORDER;
97            break;
98
99        case AXLoadComplete:
100            msaaEvent = IA2_EVENT_DOCUMENT_LOAD_COMPLETE;
101            break;
102
103        case AXValueChanged:
104        case AXMenuListValueChanged:
105            msaaEvent = EVENT_OBJECT_VALUECHANGE;
106            break;
107
108        case AXMenuListItemSelected:
109            msaaEvent = EVENT_OBJECT_SELECTION;
110            break;
111
112        default:
113            return;
114    }
115
116    // Windows will end up calling get_accChild() on the root accessible
117    // object for the WebView, passing the child ID that we specify below. We
118    // negate the AXID so we know that the caller is passing the ID of an
119    // element, not the index of a child element.
120
121    ASSERT(obj->axObjectID() >= 1);
122    ASSERT(obj->axObjectID() <= numeric_limits<LONG>::max());
123
124    NotifyWinEvent(msaaEvent, page->chrome().platformPageClient(), OBJID_CLIENT, -static_cast<LONG>(obj->axObjectID()));
125}
126
127void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&)
128{
129}
130
131void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject* obj, AXLoadingEvent notification)
132{
133    if (!obj)
134        return;
135
136    Document* document = obj->document();
137    if (!document)
138        return;
139
140    Page* page = document->page();
141    if (!page)
142        return;
143
144    if (notification == AXLoadingStarted)
145        page->chrome().client().AXStartFrameLoad();
146    else if (notification == AXLoadingFinished)
147        page->chrome().client().AXFinishFrameLoad();
148}
149
150AXID AXObjectCache::platformGenerateAXID() const
151{
152    static AXID lastUsedID = 0;
153
154    // Generate a new ID. Windows accessibility relies on a positive AXID,
155    // ranging from 1 to LONG_MAX.
156    AXID objID = lastUsedID;
157    do {
158        ++objID;
159        objID %= std::numeric_limits<LONG>::max();
160    } while (objID == 0 || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
161
162    ASSERT(objID >= 1 && objID <= std::numeric_limits<LONG>::max());
163
164    lastUsedID = objID;
165
166    return objID;
167}
168
169void AXObjectCache::platformHandleFocusedUIElementChanged(Node*, Node* newFocusedNode)
170{
171    if (!newFocusedNode)
172        return;
173
174    Page* page = newFocusedNode->document().page();
175    if (!page || !page->chrome().platformPageClient())
176        return;
177
178    AccessibilityObject* focusedObject = focusedUIElementForPage(page);
179    if (!focusedObject)
180        return;
181
182    ASSERT(!focusedObject->accessibilityIsIgnored());
183
184    postPlatformNotification(focusedObject, AXFocusedUIElementChanged);
185}
186
187} // namespace WebCore
188