1/*
2 * Copyright (C) 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 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "AccessibilityScrollView.h"
28
29#include "AXObjectCache.h"
30#include "AccessibilityScrollbar.h"
31#include "Frame.h"
32#include "FrameView.h"
33#include "HTMLFrameOwnerElement.h"
34#include "RenderElement.h"
35#include "ScrollView.h"
36#include "Widget.h"
37
38namespace WebCore {
39
40AccessibilityScrollView::AccessibilityScrollView(ScrollView* view)
41 : m_scrollView(view)
42 , m_childrenDirty(false)
43{
44}
45
46AccessibilityScrollView::~AccessibilityScrollView()
47{
48 ASSERT(isDetached());
49}
50
51void AccessibilityScrollView::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache)
52{
53 AccessibilityObject::detach(detachmentType, cache);
54 m_scrollView = nullptr;
55}
56
57Ref<AccessibilityScrollView> AccessibilityScrollView::create(ScrollView* view)
58{
59 return adoptRef(*new AccessibilityScrollView(view));
60}
61
62AccessibilityObject* AccessibilityScrollView::scrollBar(AccessibilityOrientation orientation)
63{
64 updateScrollbars();
65
66 switch (orientation) {
67 // ARIA 1.1 Elements with the role scrollbar have an implicit aria-orientation value of vertical.
68 case AccessibilityOrientation::Undefined:
69 case AccessibilityOrientation::Vertical:
70 return m_verticalScrollbar ? m_verticalScrollbar.get() : nullptr;
71 case AccessibilityOrientation::Horizontal:
72 return m_horizontalScrollbar ? m_horizontalScrollbar.get() : nullptr;
73 }
74
75 return nullptr;
76}
77
78// If this is WebKit1 then the native scroll view needs to return the
79// AX information (because there are no scroll bar children in the ScrollView object in WK1).
80// In WebKit2, the ScrollView object will return the AX information (because there are no platform widgets).
81bool AccessibilityScrollView::isAttachment() const
82{
83 return m_scrollView && m_scrollView->platformWidget();
84}
85
86Widget* AccessibilityScrollView::widgetForAttachmentView() const
87{
88 return m_scrollView;
89}
90
91bool AccessibilityScrollView::canSetFocusAttribute() const
92{
93 AccessibilityObject* webArea = webAreaObject();
94 return webArea && webArea->canSetFocusAttribute();
95}
96
97bool AccessibilityScrollView::isFocused() const
98{
99 AccessibilityObject* webArea = webAreaObject();
100 return webArea && webArea->isFocused();
101}
102
103void AccessibilityScrollView::setFocused(bool focused)
104{
105 if (AccessibilityObject* webArea = webAreaObject())
106 webArea->setFocused(focused);
107}
108
109void AccessibilityScrollView::updateChildrenIfNecessary()
110{
111 // Always update our children when asked for them so that we don't inadvertently cache them after
112 // a new web area has been created for this scroll view (like when moving back and forth through history).
113 // Since a ScrollViews children will always be relatively small and limited this should not be a performance problem.
114 clearChildren();
115 addChildren();
116}
117
118void AccessibilityScrollView::updateScrollbars()
119{
120 if (!m_scrollView)
121 return;
122
123 if (m_scrollView->horizontalScrollbar() && !m_horizontalScrollbar)
124 m_horizontalScrollbar = addChildScrollbar(m_scrollView->horizontalScrollbar());
125 else if (!m_scrollView->horizontalScrollbar() && m_horizontalScrollbar) {
126 removeChildScrollbar(m_horizontalScrollbar.get());
127 m_horizontalScrollbar = nullptr;
128 }
129
130 if (m_scrollView->verticalScrollbar() && !m_verticalScrollbar)
131 m_verticalScrollbar = addChildScrollbar(m_scrollView->verticalScrollbar());
132 else if (!m_scrollView->verticalScrollbar() && m_verticalScrollbar) {
133 removeChildScrollbar(m_verticalScrollbar.get());
134 m_verticalScrollbar = nullptr;
135 }
136}
137
138void AccessibilityScrollView::removeChildScrollbar(AccessibilityObject* scrollbar)
139{
140 size_t pos = m_children.find(scrollbar);
141 if (pos != WTF::notFound) {
142 m_children[pos]->detachFromParent();
143 m_children.remove(pos);
144 }
145}
146
147AccessibilityScrollbar* AccessibilityScrollView::addChildScrollbar(Scrollbar* scrollbar)
148{
149 if (!scrollbar)
150 return nullptr;
151
152 AXObjectCache* cache = axObjectCache();
153 if (!cache)
154 return nullptr;
155
156 auto& scrollBarObject = downcast<AccessibilityScrollbar>(*cache->getOrCreate(scrollbar));
157 scrollBarObject.setParent(this);
158 m_children.append(&scrollBarObject);
159 return &scrollBarObject;
160}
161
162void AccessibilityScrollView::clearChildren()
163{
164 AccessibilityObject::clearChildren();
165 m_verticalScrollbar = nullptr;
166 m_horizontalScrollbar = nullptr;
167}
168
169bool AccessibilityScrollView::computeAccessibilityIsIgnored() const
170{
171 AccessibilityObject* webArea = webAreaObject();
172 if (!webArea)
173 return true;
174
175 return webArea->accessibilityIsIgnored();
176}
177
178void AccessibilityScrollView::addChildren()
179{
180 ASSERT(!m_haveChildren);
181 m_haveChildren = true;
182
183 addChild(webAreaObject());
184 updateScrollbars();
185}
186
187AccessibilityObject* AccessibilityScrollView::webAreaObject() const
188{
189 if (!is<FrameView>(m_scrollView))
190 return nullptr;
191
192 Document* document = downcast<FrameView>(*m_scrollView).frame().document();
193 if (!document || !document->hasLivingRenderTree())
194 return nullptr;
195
196 if (AXObjectCache* cache = axObjectCache())
197 return cache->getOrCreate(document);
198
199 return nullptr;
200}
201
202AccessibilityObjectInterface* AccessibilityScrollView::accessibilityHitTest(const IntPoint& point) const
203{
204 AccessibilityObject* webArea = webAreaObject();
205 if (!webArea)
206 return nullptr;
207
208 if (m_horizontalScrollbar && m_horizontalScrollbar->elementRect().contains(point))
209 return m_horizontalScrollbar.get();
210 if (m_verticalScrollbar && m_verticalScrollbar->elementRect().contains(point))
211 return m_verticalScrollbar.get();
212
213 return webArea->accessibilityHitTest(point);
214}
215
216LayoutRect AccessibilityScrollView::elementRect() const
217{
218 if (!m_scrollView)
219 return LayoutRect();
220
221 LayoutRect rect = m_scrollView->frameRect();
222 float topContentInset = m_scrollView->topContentInset();
223 // Top content inset pushes the frame down and shrinks it.
224 rect.move(0, topContentInset);
225 rect.contract(0, topContentInset);
226 return rect;
227}
228
229FrameView* AccessibilityScrollView::documentFrameView() const
230{
231 if (!is<FrameView>(m_scrollView))
232 return nullptr;
233
234 return downcast<FrameView>(m_scrollView);
235}
236
237AccessibilityObject* AccessibilityScrollView::parentObject() const
238{
239 if (!is<FrameView>(m_scrollView))
240 return nullptr;
241
242 AXObjectCache* cache = axObjectCache();
243 if (!cache)
244 return nullptr;
245
246 HTMLFrameOwnerElement* owner = downcast<FrameView>(*m_scrollView).frame().ownerElement();
247 if (owner && owner->renderer())
248 return cache->getOrCreate(owner);
249
250 return nullptr;
251}
252
253AccessibilityObject* AccessibilityScrollView::parentObjectIfExists() const
254{
255 if (!is<FrameView>(m_scrollView))
256 return nullptr;
257
258 AXObjectCache* cache = axObjectCache();
259 if (!cache)
260 return nullptr;
261
262 HTMLFrameOwnerElement* owner = downcast<FrameView>(m_scrollView)->frame().ownerElement();
263 if (owner && owner->renderer())
264 return cache->get(owner);
265
266 return nullptr;
267}
268
269ScrollableArea* AccessibilityScrollView::getScrollableAreaIfScrollable() const
270{
271 return m_scrollView;
272}
273
274void AccessibilityScrollView::scrollTo(const IntPoint& point) const
275{
276 if (m_scrollView)
277 m_scrollView->setScrollPosition(point);
278}
279
280} // namespace WebCore
281