1/*
2 * Copyright (C) 2009 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
46namespace WebCore {
47
48using namespace HTMLNames;
49
50
51AccessibilityMediaControl::AccessibilityMediaControl(RenderObject* renderer)
52    : AccessibilityRenderObject(renderer)
53{
54}
55
56PassRefPtr<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
76MediaControlElementType 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
84const String& AccessibilityMediaControl::controlTypeName() const
85{
86    static NeverDestroyed<const String> mediaEnterFullscreenButtonName(ASCIILiteral("EnterFullscreenButton"));
87    static NeverDestroyed<const String> mediaExitFullscreenButtonName(ASCIILiteral("ExitFullscreenButton"));
88    static NeverDestroyed<const String> mediaMuteButtonName(ASCIILiteral("MuteButton"));
89    static NeverDestroyed<const String> mediaPlayButtonName(ASCIILiteral("PlayButton"));
90    static NeverDestroyed<const String> mediaSeekBackButtonName(ASCIILiteral("SeekBackButton"));
91    static NeverDestroyed<const String> mediaSeekForwardButtonName(ASCIILiteral("SeekForwardButton"));
92    static NeverDestroyed<const String> mediaRewindButtonName(ASCIILiteral("RewindButton"));
93    static NeverDestroyed<const String> mediaReturnToRealtimeButtonName(ASCIILiteral("ReturnToRealtimeButton"));
94    static NeverDestroyed<const String> mediaUnMuteButtonName(ASCIILiteral("UnMuteButton"));
95    static NeverDestroyed<const String> mediaPauseButtonName(ASCIILiteral("PauseButton"));
96    static NeverDestroyed<const String> mediaStatusDisplayName(ASCIILiteral("StatusDisplay"));
97    static NeverDestroyed<const String> mediaCurrentTimeDisplay(ASCIILiteral("CurrentTimeDisplay"));
98    static NeverDestroyed<const String> mediaTimeRemainingDisplay(ASCIILiteral("TimeRemainingDisplay"));
99    static NeverDestroyed<const String> mediaShowClosedCaptionsButtonName(ASCIILiteral("ShowClosedCaptionsButton"));
100    static NeverDestroyed<const String> mediaHideClosedCaptionsButtonName(ASCIILiteral("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
141void AccessibilityMediaControl::accessibilityText(Vector<AccessibilityText>& textOrder)
142{
143    String description = accessibilityDescription();
144    if (!description.isEmpty())
145        textOrder.append(AccessibilityText(description, AlternativeText));
146
147    String title = this->title();
148    if (!title.isEmpty())
149        textOrder.append(AccessibilityText(title, AlternativeText));
150
151    String helptext = helpText();
152    if (!helptext.isEmpty())
153        textOrder.append(AccessibilityText(helptext, HelpText));
154}
155
156
157String AccessibilityMediaControl::title() const
158{
159    static NeverDestroyed<const String> controlsPanel(ASCIILiteral("ControlsPanel"));
160
161    if (controlType() == MediaControlsPanel)
162        return localizedMediaControlElementString(controlsPanel);
163
164    return AccessibilityRenderObject::title();
165}
166
167String AccessibilityMediaControl::accessibilityDescription() const
168{
169    return localizedMediaControlElementString(controlTypeName());
170}
171
172String AccessibilityMediaControl::helpText() const
173{
174    return localizedMediaControlElementHelpText(controlTypeName());
175}
176
177bool AccessibilityMediaControl::computeAccessibilityIsIgnored() const
178{
179    if (!m_renderer || m_renderer->style().visibility() != VISIBLE || controlType() == MediaTimelineContainer)
180        return true;
181
182    return accessibilityIsIgnoredByDefault();
183}
184
185AccessibilityRole 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 ButtonRole;
201
202    case MediaStatusDisplay:
203        return StaticTextRole;
204
205    case MediaTimelineContainer:
206        return GroupRole;
207
208    default:
209        break;
210    }
211
212    return UnknownRole;
213}
214
215
216
217//
218// AccessibilityMediaControlsContainer
219
220AccessibilityMediaControlsContainer::AccessibilityMediaControlsContainer(RenderObject* renderer)
221    : AccessibilityMediaControl(renderer)
222{
223}
224
225PassRefPtr<AccessibilityObject> AccessibilityMediaControlsContainer::create(RenderObject* renderer)
226{
227    return adoptRef(new AccessibilityMediaControlsContainer(renderer));
228}
229
230String AccessibilityMediaControlsContainer::accessibilityDescription() const
231{
232    return localizedMediaControlElementString(elementTypeName());
233}
234
235String AccessibilityMediaControlsContainer::helpText() const
236{
237    return localizedMediaControlElementHelpText(elementTypeName());
238}
239
240bool AccessibilityMediaControlsContainer::controllingVideoElement() const
241{
242    auto element = parentMediaElement(*m_renderer);
243    return !element || element->isVideo();
244}
245
246const String& AccessibilityMediaControlsContainer::elementTypeName() const
247{
248    static NeverDestroyed<const String> videoElement(ASCIILiteral("VideoElement"));
249    static NeverDestroyed<const String> audioElement(ASCIILiteral("AudioElement"));
250
251    if (controllingVideoElement())
252        return videoElement;
253    return audioElement;
254}
255
256bool AccessibilityMediaControlsContainer::computeAccessibilityIsIgnored() const
257{
258    return accessibilityIsIgnoredByDefault();
259}
260
261//
262// AccessibilityMediaTimeline
263
264AccessibilityMediaTimeline::AccessibilityMediaTimeline(RenderObject* renderer)
265    : AccessibilitySlider(renderer)
266{
267}
268
269PassRefPtr<AccessibilityObject> AccessibilityMediaTimeline::create(RenderObject* renderer)
270{
271    return adoptRef(new AccessibilityMediaTimeline(renderer));
272}
273
274String AccessibilityMediaTimeline::valueDescription() const
275{
276    Node* node = m_renderer->node();
277    if (!isHTMLInputElement(node))
278        return String();
279
280    float time = toHTMLInputElement(node)->value().toFloat();
281    return localizedMediaTimeDescription(time);
282}
283
284String AccessibilityMediaTimeline::helpText() const
285{
286    static NeverDestroyed<const String> slider(ASCIILiteral("Slider"));
287    return localizedMediaControlElementHelpText(slider);
288}
289
290
291//
292// AccessibilityMediaTimeDisplay
293
294AccessibilityMediaTimeDisplay::AccessibilityMediaTimeDisplay(RenderObject* renderer)
295    : AccessibilityMediaControl(renderer)
296{
297}
298
299PassRefPtr<AccessibilityObject> AccessibilityMediaTimeDisplay::create(RenderObject* renderer)
300{
301    return adoptRef(new AccessibilityMediaTimeDisplay(renderer));
302}
303
304bool AccessibilityMediaTimeDisplay::computeAccessibilityIsIgnored() const
305{
306    if (!m_renderer || m_renderer->style().visibility() != VISIBLE)
307        return true;
308
309    if (!m_renderer->style().width().value())
310        return true;
311
312    return accessibilityIsIgnoredByDefault();
313}
314
315String AccessibilityMediaTimeDisplay::accessibilityDescription() const
316{
317    static NeverDestroyed<const String> currentTimeDisplay(ASCIILiteral("CurrentTimeDisplay"));
318    static NeverDestroyed<const String> timeRemainingDisplay(ASCIILiteral("TimeRemainingDisplay"));
319
320    if (controlType() == MediaCurrentTimeDisplay)
321        return localizedMediaControlElementString(currentTimeDisplay);
322
323    return localizedMediaControlElementString(timeRemainingDisplay);
324}
325
326String AccessibilityMediaTimeDisplay::stringValue() const
327{
328    if (!m_renderer || !m_renderer->node())
329        return String();
330
331    MediaControlTimeDisplayElement* element = static_cast<MediaControlTimeDisplayElement*>(m_renderer->node());
332    float time = element->currentValue();
333    return localizedMediaTimeDescription(fabsf(time));
334}
335
336} // namespace WebCore
337
338#endif // ENABLE(VIDEO)
339