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 | |
38 | namespace WebCore { |
39 | |
40 | AccessibilityScrollView::AccessibilityScrollView(ScrollView* view) |
41 | : m_scrollView(view) |
42 | , m_childrenDirty(false) |
43 | { |
44 | } |
45 | |
46 | AccessibilityScrollView::~AccessibilityScrollView() |
47 | { |
48 | ASSERT(isDetached()); |
49 | } |
50 | |
51 | void AccessibilityScrollView::detach(AccessibilityDetachmentType detachmentType, AXObjectCache* cache) |
52 | { |
53 | AccessibilityObject::detach(detachmentType, cache); |
54 | m_scrollView = nullptr; |
55 | } |
56 | |
57 | Ref<AccessibilityScrollView> AccessibilityScrollView::create(ScrollView* view) |
58 | { |
59 | return adoptRef(*new AccessibilityScrollView(view)); |
60 | } |
61 | |
62 | AccessibilityObject* 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). |
81 | bool AccessibilityScrollView::isAttachment() const |
82 | { |
83 | return m_scrollView && m_scrollView->platformWidget(); |
84 | } |
85 | |
86 | Widget* AccessibilityScrollView::widgetForAttachmentView() const |
87 | { |
88 | return m_scrollView; |
89 | } |
90 | |
91 | bool AccessibilityScrollView::canSetFocusAttribute() const |
92 | { |
93 | AccessibilityObject* webArea = webAreaObject(); |
94 | return webArea && webArea->canSetFocusAttribute(); |
95 | } |
96 | |
97 | bool AccessibilityScrollView::isFocused() const |
98 | { |
99 | AccessibilityObject* webArea = webAreaObject(); |
100 | return webArea && webArea->isFocused(); |
101 | } |
102 | |
103 | void AccessibilityScrollView::setFocused(bool focused) |
104 | { |
105 | if (AccessibilityObject* webArea = webAreaObject()) |
106 | webArea->setFocused(focused); |
107 | } |
108 | |
109 | void 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 | |
118 | void 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 | |
138 | void 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 | |
147 | AccessibilityScrollbar* 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 | |
162 | void AccessibilityScrollView::clearChildren() |
163 | { |
164 | AccessibilityObject::clearChildren(); |
165 | m_verticalScrollbar = nullptr; |
166 | m_horizontalScrollbar = nullptr; |
167 | } |
168 | |
169 | bool AccessibilityScrollView::computeAccessibilityIsIgnored() const |
170 | { |
171 | AccessibilityObject* webArea = webAreaObject(); |
172 | if (!webArea) |
173 | return true; |
174 | |
175 | return webArea->accessibilityIsIgnored(); |
176 | } |
177 | |
178 | void AccessibilityScrollView::addChildren() |
179 | { |
180 | ASSERT(!m_haveChildren); |
181 | m_haveChildren = true; |
182 | |
183 | addChild(webAreaObject()); |
184 | updateScrollbars(); |
185 | } |
186 | |
187 | AccessibilityObject* 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 | |
202 | AccessibilityObjectInterface* 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 | |
216 | LayoutRect 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 | |
229 | FrameView* AccessibilityScrollView::documentFrameView() const |
230 | { |
231 | if (!is<FrameView>(m_scrollView)) |
232 | return nullptr; |
233 | |
234 | return downcast<FrameView>(m_scrollView); |
235 | } |
236 | |
237 | AccessibilityObject* 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 | |
253 | AccessibilityObject* 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 | |
269 | ScrollableArea* AccessibilityScrollView::getScrollableAreaIfScrollable() const |
270 | { |
271 | return m_scrollView; |
272 | } |
273 | |
274 | void AccessibilityScrollView::scrollTo(const IntPoint& point) const |
275 | { |
276 | if (m_scrollView) |
277 | m_scrollView->setScrollPosition(point); |
278 | } |
279 | |
280 | } // namespace WebCore |
281 | |