1/*
2 * Copyright (C) 2008 Nuanti Ltd.
3 * Copyright (C) 2009 Jan Alonzo
4 * Copyright (C) 2009, 2012 Igalia S.L.
5 *
6 * Portions from Mozilla a11y, copyright as follows:
7 *
8 * The Original Code is mozilla.org code.
9 *
10 * The Initial Developer of the Original Code is
11 * Sun Microsystems, Inc.
12 * Portions created by the Initial Developer are Copyright (C) 2002
13 * the Initial Developer. All Rights Reserved.
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Library General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Library General Public License for more details.
24 *
25 * You should have received a copy of the GNU Library General Public License
26 * along with this library; see the file COPYING.LIB. If not, write to
27 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 * Boston, MA 02110-1301, USA.
29 */
30
31#include "config.h"
32#include "WebKitAccessibleInterfaceComponent.h"
33
34#if HAVE(ACCESSIBILITY)
35
36#include "AccessibilityObject.h"
37#include "FrameView.h"
38#include "IntRect.h"
39#include "RenderLayer.h"
40#include "WebKitAccessible.h"
41#include "WebKitAccessibleUtil.h"
42
43using namespace WebCore;
44
45static IntPoint atkToContents(const AccessibilityObject& coreObject, AtkCoordType coordType, gint x, gint y)
46{
47 auto* frameView = coreObject.documentFrameView();
48 if (!frameView)
49 return { x, y };
50
51 switch (coordType) {
52 case ATK_XY_SCREEN:
53 return frameView->screenToContents({ x, y });
54 case ATK_XY_WINDOW:
55 return frameView->windowToContents({ x, y });
56#if ATK_CHECK_VERSION(2, 30, 0)
57 case ATK_XY_PARENT:
58 RELEASE_ASSERT_NOT_REACHED();
59#endif
60 }
61
62 return { x, y };
63}
64
65static AtkObject* webkitAccessibleComponentRefAccessibleAtPoint(AtkComponent* component, gint x, gint y, AtkCoordType coordType)
66{
67 auto* accessible = WEBKIT_ACCESSIBLE(component);
68 returnValIfWebKitAccessibleIsInvalid(accessible, nullptr);
69
70 auto& coreObject = webkitAccessibleGetAccessibilityObject(accessible);
71 auto* target = downcast<AccessibilityObject>(coreObject.accessibilityHitTest(atkToContents(coreObject, coordType, x, y)));
72 if (!target)
73 return nullptr;
74
75 if (auto* wrapper = target->wrapper())
76 return ATK_OBJECT(g_object_ref(wrapper));
77
78 return nullptr;
79}
80
81static void webkitAccessibleComponentGetExtents(AtkComponent* component, gint* x, gint* y, gint* width, gint* height, AtkCoordType coordType)
82{
83 auto* accessible = WEBKIT_ACCESSIBLE(component);
84 returnIfWebKitAccessibleIsInvalid(accessible);
85
86 auto& coreObject = webkitAccessibleGetAccessibilityObject(accessible);
87 contentsRelativeToAtkCoordinateType(&coreObject, coordType, snappedIntRect(coreObject.elementRect()), x, y, width, height);
88}
89
90static gboolean webkitAccessibleComponentGrabFocus(AtkComponent* component)
91{
92 auto* accessible = WEBKIT_ACCESSIBLE(component);
93 returnValIfWebKitAccessibleIsInvalid(accessible, FALSE);
94
95 auto& coreObject = webkitAccessibleGetAccessibilityObject(accessible);
96 coreObject.setFocused(true);
97 return coreObject.isFocused();
98}
99
100#if ATK_CHECK_VERSION(2, 30, 0)
101static gboolean webkitAccessibleComponentScrollTo(AtkComponent* component, AtkScrollType scrollType)
102{
103 auto* accessible = WEBKIT_ACCESSIBLE(component);
104 returnValIfWebKitAccessibleIsInvalid(accessible, FALSE);
105
106 ScrollAlignment alignX;
107 ScrollAlignment alignY;
108
109 switch (scrollType) {
110 case ATK_SCROLL_TOP_LEFT:
111 alignX = ScrollAlignment::alignLeftAlways;
112 alignY = ScrollAlignment::alignTopAlways;
113 break;
114 case ATK_SCROLL_BOTTOM_RIGHT:
115 alignX = ScrollAlignment::alignRightAlways;
116 alignY = ScrollAlignment::alignBottomAlways;
117 break;
118 case ATK_SCROLL_TOP_EDGE:
119 case ATK_SCROLL_BOTTOM_EDGE:
120 // Align to a particular edge is not supported, it's always the closest edge.
121 alignX = ScrollAlignment::alignCenterIfNeeded;
122 alignY = ScrollAlignment::alignToEdgeIfNeeded;
123 break;
124 case ATK_SCROLL_LEFT_EDGE:
125 case ATK_SCROLL_RIGHT_EDGE:
126 // Align to a particular edge is not supported, it's always the closest edge.
127 alignX = ScrollAlignment::alignToEdgeIfNeeded;
128 alignY = ScrollAlignment::alignCenterIfNeeded;
129 break;
130 case ATK_SCROLL_ANYWHERE:
131 alignX = ScrollAlignment::alignCenterIfNeeded;
132 alignY = ScrollAlignment::alignCenterIfNeeded;
133 break;
134 }
135
136 auto& coreObject = webkitAccessibleGetAccessibilityObject(accessible);
137 coreObject.scrollToMakeVisible({ SelectionRevealMode::Reveal, alignX, alignY, ShouldAllowCrossOriginScrolling::Yes });
138
139 return TRUE;
140}
141
142static gboolean webkitAccessibleComponentScrollToPoint(AtkComponent* component, AtkCoordType coordType, gint x, gint y)
143{
144 auto* accessible = WEBKIT_ACCESSIBLE(component);
145 returnValIfWebKitAccessibleIsInvalid(accessible, FALSE);
146
147 auto& coreObject = webkitAccessibleGetAccessibilityObject(accessible);
148
149 IntPoint point(x, y);
150 if (coordType == ATK_XY_SCREEN) {
151 if (auto* frameView = coreObject.documentFrameView())
152 point = frameView->contentsToWindow(frameView->screenToContents(point));
153 }
154
155 coreObject.scrollToGlobalPoint(point);
156
157 return TRUE;
158}
159#endif
160
161void webkitAccessibleComponentInterfaceInit(AtkComponentIface* iface)
162{
163 iface->ref_accessible_at_point = webkitAccessibleComponentRefAccessibleAtPoint;
164 iface->get_extents = webkitAccessibleComponentGetExtents;
165 iface->grab_focus = webkitAccessibleComponentGrabFocus;
166#if ATK_CHECK_VERSION(2, 30, 0)
167 iface->scroll_to = webkitAccessibleComponentScrollTo;
168 iface->scroll_to_point = webkitAccessibleComponentScrollToPoint;
169#endif
170}
171
172#endif // HAVE(ACCESSIBILITY)
173