1/*
2 * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2012 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1.  Redistributions of source code must retain the above copyright
10 *     notice, this list of conditions and the following disclaimer.
11 * 2.  Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 *     its contributors may be used to endorse or promote products derived
16 *     from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "config.h"
31
32#if ENABLE(VIDEO)
33#include "MediaControlElementTypes.h"
34
35#include "CSSValueKeywords.h"
36#include "ExceptionCodePlaceholder.h"
37#include "HTMLNames.h"
38#include "MouseEvent.h"
39#include "RenderMedia.h"
40#include "RenderMediaControlElements.h"
41#include "StylePropertySet.h"
42
43namespace WebCore {
44
45using namespace HTMLNames;
46
47class Event;
48
49// FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in.
50static const double cSkipRepeatDelay = 0.1;
51static const double cSkipTime = 0.2;
52static const double cScanRepeatDelay = 1.5;
53static const double cScanMaximumRate = 8;
54
55HTMLMediaElement* toParentMediaElement(Node* node)
56{
57    if (!node)
58        return 0;
59    Node* mediaNode = node->shadowHost();
60    if (!mediaNode)
61        mediaNode = node;
62    if (!mediaNode || !mediaNode->isElementNode() || !toElement(mediaNode)->isMediaElement())
63        return 0;
64
65    return static_cast<HTMLMediaElement*>(mediaNode);
66}
67
68MediaControlElementType mediaControlElementType(Node* node)
69{
70    ASSERT_WITH_SECURITY_IMPLICATION(node->isMediaControlElement());
71    HTMLElement* element = toHTMLElement(node);
72    if (element->hasTagName(inputTag))
73        return static_cast<MediaControlInputElement*>(element)->displayType();
74    return static_cast<MediaControlDivElement*>(element)->displayType();
75}
76
77MediaControlElement::MediaControlElement(MediaControlElementType displayType, HTMLElement* element)
78    : m_mediaController(0)
79    , m_displayType(displayType)
80    , m_element(element)
81{
82}
83
84void MediaControlElement::hide()
85{
86    m_element->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
87}
88
89void MediaControlElement::show()
90{
91    m_element->removeInlineStyleProperty(CSSPropertyDisplay);
92}
93
94bool MediaControlElement::isShowing() const
95{
96    const StylePropertySet* propertySet = m_element->inlineStyle();
97    // Following the code from show() and hide() above, we only have
98    // to check for the presense of inline display.
99    return (!propertySet || !propertySet->getPropertyCSSValue(CSSPropertyDisplay));
100}
101
102void MediaControlElement::setDisplayType(MediaControlElementType displayType)
103{
104    if (displayType == m_displayType)
105        return;
106
107    m_displayType = displayType;
108    if (RenderObject* object = m_element->renderer())
109        object->repaint();
110}
111
112// ----------------------------
113
114MediaControlDivElement::MediaControlDivElement(Document* document, MediaControlElementType displayType)
115    : HTMLDivElement(divTag, document)
116    , MediaControlElement(displayType, this)
117{
118}
119
120// ----------------------------
121
122MediaControlInputElement::MediaControlInputElement(Document* document, MediaControlElementType displayType)
123    : HTMLInputElement(inputTag, document, 0, false)
124    , MediaControlElement(displayType, this)
125{
126}
127
128// ----------------------------
129
130MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(Document* document, MediaControlElementType displayType)
131    : MediaControlDivElement(document, displayType)
132    , m_currentValue(0)
133{
134}
135
136void MediaControlTimeDisplayElement::setCurrentValue(double time)
137{
138    m_currentValue = time;
139}
140
141// ----------------------------
142
143MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* document, MediaControlElementType displayType)
144    : MediaControlInputElement(document, displayType)
145{
146}
147
148void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
149{
150    if (event->type() == eventNames().clickEvent) {
151        mediaController()->setMuted(!mediaController()->muted());
152        event->setDefaultHandled();
153    }
154
155    HTMLInputElement::defaultEventHandler(event);
156}
157
158void MediaControlMuteButtonElement::changedMute()
159{
160    updateDisplayType();
161}
162
163void MediaControlMuteButtonElement::updateDisplayType()
164{
165    setDisplayType(mediaController()->muted() ? MediaUnMuteButton : MediaMuteButton);
166}
167
168// ----------------------------
169
170MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* document, MediaControlElementType displayType)
171    : MediaControlInputElement(document, displayType)
172    , m_actionOnStop(Nothing)
173    , m_seekType(Skip)
174    , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
175{
176}
177
178void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
179{
180    // Set the mousedown and mouseup events as defaultHandled so they
181    // do not trigger drag start or end actions in MediaControlPanelElement.
182    if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent)
183        event->setDefaultHandled();
184}
185
186void MediaControlSeekButtonElement::setActive(bool flag, bool pause)
187{
188    if (flag == active())
189        return;
190
191    if (flag)
192        startTimer();
193    else
194        stopTimer();
195
196    MediaControlInputElement::setActive(flag, pause);
197}
198
199void MediaControlSeekButtonElement::startTimer()
200{
201    m_seekType = mediaController()->supportsScanning() ? Scan : Skip;
202
203    if (m_seekType == Skip) {
204        // Seeking by skipping requires the video to be paused during seeking.
205        m_actionOnStop = mediaController()->paused() ? Nothing : Play;
206        mediaController()->pause();
207    } else {
208        // Seeking by scanning requires the video to be playing during seeking.
209        m_actionOnStop = mediaController()->paused() ? Pause : Nothing;
210        mediaController()->play();
211        mediaController()->setPlaybackRate(nextRate());
212    }
213
214    m_seekTimer.start(0, m_seekType == Skip ? cSkipRepeatDelay : cScanRepeatDelay);
215}
216
217void MediaControlSeekButtonElement::stopTimer()
218{
219    if (m_seekType == Scan)
220        mediaController()->setPlaybackRate(mediaController()->defaultPlaybackRate());
221
222    if (m_actionOnStop == Play)
223        mediaController()->play();
224    else if (m_actionOnStop == Pause)
225        mediaController()->pause();
226
227    if (m_seekTimer.isActive())
228        m_seekTimer.stop();
229}
230
231double MediaControlSeekButtonElement::nextRate() const
232{
233    double rate = std::min(cScanMaximumRate, fabs(mediaController()->playbackRate() * 2));
234    if (!isForwardButton())
235        rate *= -1;
236    return rate;
237}
238
239void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
240{
241    if (m_seekType == Skip) {
242        double skipTime = isForwardButton() ? cSkipTime : -cSkipTime;
243        mediaController()->setCurrentTime(mediaController()->currentTime() + skipTime, IGNORE_EXCEPTION);
244    } else
245        mediaController()->setPlaybackRate(nextRate());
246}
247
248// ----------------------------
249
250MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(Document* document)
251    : MediaControlInputElement(document, MediaVolumeSlider)
252    , m_clearMutedOnUserInteraction(false)
253{
254}
255
256void MediaControlVolumeSliderElement::defaultEventHandler(Event* event)
257{
258    // Left button is 0. Rejects mouse events not from left button.
259    if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
260        return;
261
262    if (!attached())
263        return;
264
265    MediaControlInputElement::defaultEventHandler(event);
266
267    if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
268        return;
269
270    double volume = value().toDouble();
271    if (volume != mediaController()->volume())
272        mediaController()->setVolume(volume, ASSERT_NO_EXCEPTION);
273    if (m_clearMutedOnUserInteraction)
274        mediaController()->setMuted(false);
275    event->setDefaultHandled();
276}
277
278bool MediaControlVolumeSliderElement::willRespondToMouseMoveEvents()
279{
280    if (!attached())
281        return false;
282
283    return MediaControlInputElement::willRespondToMouseMoveEvents();
284}
285
286bool MediaControlVolumeSliderElement::willRespondToMouseClickEvents()
287{
288    if (!attached())
289        return false;
290
291    return MediaControlInputElement::willRespondToMouseClickEvents();
292}
293
294void MediaControlVolumeSliderElement::setVolume(double volume)
295{
296    if (value().toDouble() != volume)
297        setValue(String::number(volume));
298}
299
300void MediaControlVolumeSliderElement::setClearMutedOnUserInteraction(bool clearMute)
301{
302    m_clearMutedOnUserInteraction = clearMute;
303}
304
305} // namespace WebCore
306
307#endif // ENABLE(VIDEO)
308