1 | /* |
2 | * Copyright (C) 2009-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 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
14 | * its contributors may be used to endorse or promote products derived |
15 | * from this software without specific prior written permission. |
16 | * |
17 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
20 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | |
30 | #include "config.h" |
31 | |
32 | #if ENABLE(VIDEO) |
33 | |
34 | #include "AccessibilityMediaControls.h" |
35 | |
36 | #include "AXObjectCache.h" |
37 | #include "HTMLInputElement.h" |
38 | #include "HTMLMediaElement.h" |
39 | #include "HTMLNames.h" |
40 | #include "LocalizedStrings.h" |
41 | #include "MediaControlElements.h" |
42 | #include "RenderObject.h" |
43 | #include "RenderSlider.h" |
44 | #include <wtf/NeverDestroyed.h> |
45 | |
46 | namespace WebCore { |
47 | |
48 | using namespace HTMLNames; |
49 | |
50 | |
51 | AccessibilityMediaControl::AccessibilityMediaControl(RenderObject* renderer) |
52 | : AccessibilityRenderObject(renderer) |
53 | { |
54 | } |
55 | |
56 | Ref<AccessibilityObject> AccessibilityMediaControl::create(RenderObject* renderer) |
57 | { |
58 | ASSERT(renderer->node()); |
59 | |
60 | switch (mediaControlElementType(renderer->node())) { |
61 | case MediaSlider: |
62 | return AccessibilityMediaTimeline::create(renderer); |
63 | |
64 | case MediaCurrentTimeDisplay: |
65 | case MediaTimeRemainingDisplay: |
66 | return AccessibilityMediaTimeDisplay::create(renderer); |
67 | |
68 | case MediaControlsPanel: |
69 | return AccessibilityMediaControlsContainer::create(renderer); |
70 | |
71 | default: |
72 | return adoptRef(*new AccessibilityMediaControl(renderer)); |
73 | } |
74 | } |
75 | |
76 | MediaControlElementType AccessibilityMediaControl::controlType() const |
77 | { |
78 | if (!renderer() || !renderer()->node()) |
79 | return MediaTimelineContainer; // Timeline container is not accessible. |
80 | |
81 | return mediaControlElementType(renderer()->node()); |
82 | } |
83 | |
84 | const String& AccessibilityMediaControl::controlTypeName() const |
85 | { |
86 | static NeverDestroyed<const String> mediaEnterFullscreenButtonName(MAKE_STATIC_STRING_IMPL("EnterFullscreenButton" )); |
87 | static NeverDestroyed<const String> mediaExitFullscreenButtonName(MAKE_STATIC_STRING_IMPL("ExitFullscreenButton" )); |
88 | static NeverDestroyed<const String> mediaMuteButtonName(MAKE_STATIC_STRING_IMPL("MuteButton" )); |
89 | static NeverDestroyed<const String> mediaPlayButtonName(MAKE_STATIC_STRING_IMPL("PlayButton" )); |
90 | static NeverDestroyed<const String> mediaSeekBackButtonName(MAKE_STATIC_STRING_IMPL("SeekBackButton" )); |
91 | static NeverDestroyed<const String> mediaSeekForwardButtonName(MAKE_STATIC_STRING_IMPL("SeekForwardButton" )); |
92 | static NeverDestroyed<const String> mediaRewindButtonName(MAKE_STATIC_STRING_IMPL("RewindButton" )); |
93 | static NeverDestroyed<const String> mediaReturnToRealtimeButtonName(MAKE_STATIC_STRING_IMPL("ReturnToRealtimeButton" )); |
94 | static NeverDestroyed<const String> mediaUnMuteButtonName(MAKE_STATIC_STRING_IMPL("UnMuteButton" )); |
95 | static NeverDestroyed<const String> mediaPauseButtonName(MAKE_STATIC_STRING_IMPL("PauseButton" )); |
96 | static NeverDestroyed<const String> mediaStatusDisplayName(MAKE_STATIC_STRING_IMPL("StatusDisplay" )); |
97 | static NeverDestroyed<const String> mediaCurrentTimeDisplay(MAKE_STATIC_STRING_IMPL("CurrentTimeDisplay" )); |
98 | static NeverDestroyed<const String> mediaTimeRemainingDisplay(MAKE_STATIC_STRING_IMPL("TimeRemainingDisplay" )); |
99 | static NeverDestroyed<const String> mediaShowClosedCaptionsButtonName(MAKE_STATIC_STRING_IMPL("ShowClosedCaptionsButton" )); |
100 | static NeverDestroyed<const String> mediaHideClosedCaptionsButtonName(MAKE_STATIC_STRING_IMPL("HideClosedCaptionsButton" )); |
101 | |
102 | switch (controlType()) { |
103 | case MediaEnterFullscreenButton: |
104 | return mediaEnterFullscreenButtonName; |
105 | case MediaExitFullscreenButton: |
106 | return mediaExitFullscreenButtonName; |
107 | case MediaMuteButton: |
108 | return mediaMuteButtonName; |
109 | case MediaPlayButton: |
110 | return mediaPlayButtonName; |
111 | case MediaSeekBackButton: |
112 | return mediaSeekBackButtonName; |
113 | case MediaSeekForwardButton: |
114 | return mediaSeekForwardButtonName; |
115 | case MediaRewindButton: |
116 | return mediaRewindButtonName; |
117 | case MediaReturnToRealtimeButton: |
118 | return mediaReturnToRealtimeButtonName; |
119 | case MediaUnMuteButton: |
120 | return mediaUnMuteButtonName; |
121 | case MediaPauseButton: |
122 | return mediaPauseButtonName; |
123 | case MediaStatusDisplay: |
124 | return mediaStatusDisplayName; |
125 | case MediaCurrentTimeDisplay: |
126 | return mediaCurrentTimeDisplay; |
127 | case MediaTimeRemainingDisplay: |
128 | return mediaTimeRemainingDisplay; |
129 | case MediaShowClosedCaptionsButton: |
130 | return mediaShowClosedCaptionsButtonName; |
131 | case MediaHideClosedCaptionsButton: |
132 | return mediaHideClosedCaptionsButtonName; |
133 | |
134 | default: |
135 | break; |
136 | } |
137 | |
138 | return nullAtom(); |
139 | } |
140 | |
141 | void AccessibilityMediaControl::accessibilityText(Vector<AccessibilityText>& textOrder) const |
142 | { |
143 | String description = accessibilityDescription(); |
144 | if (!description.isEmpty()) |
145 | textOrder.append(AccessibilityText(description, AccessibilityTextSource::Alternative)); |
146 | |
147 | String title = this->title(); |
148 | if (!title.isEmpty()) |
149 | textOrder.append(AccessibilityText(title, AccessibilityTextSource::Alternative)); |
150 | |
151 | String helptext = helpText(); |
152 | if (!helptext.isEmpty()) |
153 | textOrder.append(AccessibilityText(helptext, AccessibilityTextSource::Help)); |
154 | } |
155 | |
156 | |
157 | String AccessibilityMediaControl::title() const |
158 | { |
159 | static NeverDestroyed<const String> controlsPanel(MAKE_STATIC_STRING_IMPL("ControlsPanel" )); |
160 | |
161 | if (controlType() == MediaControlsPanel) |
162 | return localizedMediaControlElementString(controlsPanel); |
163 | |
164 | return AccessibilityRenderObject::title(); |
165 | } |
166 | |
167 | String AccessibilityMediaControl::accessibilityDescription() const |
168 | { |
169 | return localizedMediaControlElementString(controlTypeName()); |
170 | } |
171 | |
172 | String AccessibilityMediaControl::helpText() const |
173 | { |
174 | return localizedMediaControlElementHelpText(controlTypeName()); |
175 | } |
176 | |
177 | bool AccessibilityMediaControl::computeAccessibilityIsIgnored() const |
178 | { |
179 | if (!m_renderer || m_renderer->style().visibility() != Visibility::Visible || controlType() == MediaTimelineContainer) |
180 | return true; |
181 | |
182 | return accessibilityIsIgnoredByDefault(); |
183 | } |
184 | |
185 | AccessibilityRole AccessibilityMediaControl::roleValue() const |
186 | { |
187 | switch (controlType()) { |
188 | case MediaEnterFullscreenButton: |
189 | case MediaExitFullscreenButton: |
190 | case MediaMuteButton: |
191 | case MediaPlayButton: |
192 | case MediaSeekBackButton: |
193 | case MediaSeekForwardButton: |
194 | case MediaRewindButton: |
195 | case MediaReturnToRealtimeButton: |
196 | case MediaUnMuteButton: |
197 | case MediaPauseButton: |
198 | case MediaShowClosedCaptionsButton: |
199 | case MediaHideClosedCaptionsButton: |
200 | return AccessibilityRole::Button; |
201 | |
202 | case MediaStatusDisplay: |
203 | return AccessibilityRole::StaticText; |
204 | |
205 | case MediaTimelineContainer: |
206 | return AccessibilityRole::Group; |
207 | |
208 | default: |
209 | break; |
210 | } |
211 | |
212 | return AccessibilityRole::Unknown; |
213 | } |
214 | |
215 | |
216 | // |
217 | // AccessibilityMediaControlsContainer |
218 | |
219 | AccessibilityMediaControlsContainer::AccessibilityMediaControlsContainer(RenderObject* renderer) |
220 | : AccessibilityMediaControl(renderer) |
221 | { |
222 | } |
223 | |
224 | Ref<AccessibilityObject> AccessibilityMediaControlsContainer::create(RenderObject* renderer) |
225 | { |
226 | return adoptRef(*new AccessibilityMediaControlsContainer(renderer)); |
227 | } |
228 | |
229 | String AccessibilityMediaControlsContainer::accessibilityDescription() const |
230 | { |
231 | return localizedMediaControlElementString(elementTypeName()); |
232 | } |
233 | |
234 | String AccessibilityMediaControlsContainer::helpText() const |
235 | { |
236 | return localizedMediaControlElementHelpText(elementTypeName()); |
237 | } |
238 | |
239 | bool AccessibilityMediaControlsContainer::controllingVideoElement() const |
240 | { |
241 | auto element = parentMediaElement(*m_renderer); |
242 | return !element || element->isVideo(); |
243 | } |
244 | |
245 | const String& AccessibilityMediaControlsContainer::elementTypeName() const |
246 | { |
247 | static NeverDestroyed<const String> videoElement(MAKE_STATIC_STRING_IMPL("VideoElement" )); |
248 | static NeverDestroyed<const String> audioElement(MAKE_STATIC_STRING_IMPL("AudioElement" )); |
249 | |
250 | if (controllingVideoElement()) |
251 | return videoElement; |
252 | return audioElement; |
253 | } |
254 | |
255 | bool AccessibilityMediaControlsContainer::computeAccessibilityIsIgnored() const |
256 | { |
257 | return accessibilityIsIgnoredByDefault(); |
258 | } |
259 | |
260 | // |
261 | // AccessibilityMediaTimeline |
262 | |
263 | AccessibilityMediaTimeline::AccessibilityMediaTimeline(RenderObject* renderer) |
264 | : AccessibilitySlider(renderer) |
265 | { |
266 | } |
267 | |
268 | Ref<AccessibilityObject> AccessibilityMediaTimeline::create(RenderObject* renderer) |
269 | { |
270 | return adoptRef(*new AccessibilityMediaTimeline(renderer)); |
271 | } |
272 | |
273 | String AccessibilityMediaTimeline::valueDescription() const |
274 | { |
275 | Node* node = m_renderer->node(); |
276 | if (!is<HTMLInputElement>(*node)) |
277 | return String(); |
278 | |
279 | float time = downcast<HTMLInputElement>(*node).value().toFloat(); |
280 | return localizedMediaTimeDescription(time); |
281 | } |
282 | |
283 | String AccessibilityMediaTimeline::helpText() const |
284 | { |
285 | static NeverDestroyed<const String> slider(MAKE_STATIC_STRING_IMPL("Slider" )); |
286 | return localizedMediaControlElementHelpText(slider); |
287 | } |
288 | |
289 | |
290 | // |
291 | // AccessibilityMediaTimeDisplay |
292 | |
293 | AccessibilityMediaTimeDisplay::AccessibilityMediaTimeDisplay(RenderObject* renderer) |
294 | : AccessibilityMediaControl(renderer) |
295 | { |
296 | } |
297 | |
298 | Ref<AccessibilityObject> AccessibilityMediaTimeDisplay::create(RenderObject* renderer) |
299 | { |
300 | return adoptRef(*new AccessibilityMediaTimeDisplay(renderer)); |
301 | } |
302 | |
303 | bool AccessibilityMediaTimeDisplay::computeAccessibilityIsIgnored() const |
304 | { |
305 | if (!m_renderer || m_renderer->style().visibility() != Visibility::Visible) |
306 | return true; |
307 | |
308 | if (!m_renderer->style().width().value()) |
309 | return true; |
310 | |
311 | return accessibilityIsIgnoredByDefault(); |
312 | } |
313 | |
314 | String AccessibilityMediaTimeDisplay::accessibilityDescription() const |
315 | { |
316 | static NeverDestroyed<const String> currentTimeDisplay(MAKE_STATIC_STRING_IMPL("CurrentTimeDisplay" )); |
317 | static NeverDestroyed<const String> timeRemainingDisplay(MAKE_STATIC_STRING_IMPL("TimeRemainingDisplay" )); |
318 | |
319 | if (controlType() == MediaCurrentTimeDisplay) |
320 | return localizedMediaControlElementString(currentTimeDisplay); |
321 | |
322 | return localizedMediaControlElementString(timeRemainingDisplay); |
323 | } |
324 | |
325 | String AccessibilityMediaTimeDisplay::stringValue() const |
326 | { |
327 | if (!m_renderer || !m_renderer->node()) |
328 | return String(); |
329 | |
330 | float time = static_cast<MediaControlTimeDisplayElement*>(m_renderer->node())->currentValue(); |
331 | return localizedMediaTimeDescription(std::abs(time)); |
332 | } |
333 | |
334 | } // namespace WebCore |
335 | |
336 | #endif // ENABLE(VIDEO) |
337 | |