fc2790ca4e4b3ee1d56787937b1229c9a1a7cc0e
[WebKit-https.git] / WebCore / rendering / MediaControlElements.cpp
1 /*
2  * Copyright (C) 2008, 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 Computer, 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 #include "config.h"
30
31 #if ENABLE(VIDEO)
32
33 #include "MediaControlElements.h"
34
35 #include "EventNames.h"
36 #include "FloatConversion.h"
37 #include "Frame.h"
38 #include "HTMLNames.h"
39 #include "MouseEvent.h"
40 #include "RenderMedia.h"
41 #include "RenderSlider.h"
42 #include "RenderTheme.h"
43
44 namespace WebCore {
45
46 using namespace HTMLNames;
47
48 // FIXME: These constants may need to be tweaked to better match the seeking in the QT plugin
49 static const float cSeekRepeatDelay = 0.1f;
50 static const float cStepTime = 0.07f;
51 static const float cSeekTime = 0.2f;
52
53 MediaControlShadowRootElement::MediaControlShadowRootElement(Document* doc, HTMLMediaElement* mediaElement) 
54     : HTMLDivElement(divTag, doc)
55     , m_mediaElement(mediaElement) 
56 {
57     RefPtr<RenderStyle> rootStyle = RenderStyle::create();
58     rootStyle->inheritFrom(mediaElement->renderer()->style());
59     rootStyle->setDisplay(BLOCK);
60     rootStyle->setPosition(RelativePosition);
61     RenderMediaControlShadowRoot* renderer = new (mediaElement->renderer()->renderArena()) RenderMediaControlShadowRoot(this);
62     renderer->setStyle(rootStyle.release());
63     setRenderer(renderer);
64     setAttached();
65     setInDocument(true);
66 }
67
68 void MediaControlShadowRootElement::updateStyle()
69 {
70     if (renderer()) {
71         RenderStyle* timelineContainerStyle = m_mediaElement->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_TIMELINE_CONTAINER);
72         renderer()->setStyle(timelineContainerStyle);
73     }
74 }
75
76 // ----------------------------
77
78 MediaTextDisplayElement::MediaTextDisplayElement(Document* doc, PseudoId pseudo, HTMLMediaElement* mediaElement) 
79     : HTMLDivElement(divTag, doc)
80     , m_mediaElement(mediaElement)
81     , m_pseudoStyleId(pseudo)
82 {
83     RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
84     RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style);
85     if (renderer) {
86         setRenderer(renderer);
87         renderer->setStyle(style);
88     }
89     setAttached();
90     setInDocument(true);
91 }
92
93 void MediaTextDisplayElement::attachToParent(Element* parent)
94 {
95     parent->addChild(this);
96     if (renderer() && parent->renderer())
97         parent->renderer()->addChild(renderer());
98 }
99
100 void MediaTextDisplayElement::update()
101 {
102     if (renderer())
103         renderer()->updateFromElement();
104     updateStyle();
105 }
106
107 void MediaTextDisplayElement::updateStyle()
108 {
109     if (renderer() && m_mediaElement->renderer()) {
110         RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
111         renderer()->setStyle(style);
112     }
113 }
114
115 MediaTimeDisplayElement::MediaTimeDisplayElement(Document* doc, HTMLMediaElement* element, bool currentTime)
116     : MediaTextDisplayElement(doc, currentTime ? MEDIA_CONTROLS_CURRENT_TIME_DISPLAY : MEDIA_CONTROLS_TIME_REMAINING_DISPLAY, element)
117 {
118 }
119
120 // ----------------------------
121
122 MediaControlInputElement::MediaControlInputElement(Document* doc, PseudoId pseudo, const String& type, HTMLMediaElement* mediaElement, MediaControlElementType displayType) 
123     : HTMLInputElement(inputTag, doc)
124     , m_mediaElement(mediaElement)
125     , m_pseudoStyleId(pseudo)
126     , m_displayType(displayType)
127 {
128     setInputType(type);
129     updateStyle();
130     setInDocument(true);
131 }
132
133 void MediaControlInputElement::attachToParent(Element* parent)
134 {
135     parent->addChild(this);
136     if (renderer() && parent->renderer())
137         parent->renderer()->addChild(renderer());
138 }
139
140 void MediaControlInputElement::update()
141 {
142     updateDisplayType();
143     if (renderer())
144         renderer()->updateFromElement();
145     updateStyle();
146 }
147
148 void MediaControlInputElement::updateStyle()
149 {
150     if (!m_mediaElement || !m_mediaElement->renderer())
151         return;
152     
153     RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
154     
155     bool needsRenderer = rendererIsNeeded(style);
156     if (renderer() && !needsRenderer)
157         detach();
158     else if (!renderer() && needsRenderer) {
159         RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style);
160         if (!renderer)
161             return;
162         renderer->setStyle(style);
163         setRenderer(renderer);
164         setAttached();
165         if (parent() && parent()->renderer()) {
166             // Find next sibling with a renderer to determine where to insert.
167             Node* sibling = nextSibling();
168             while (sibling && !sibling->renderer())
169                 sibling = sibling->nextSibling();
170             parent()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0);
171         }
172     } else if (renderer())
173         renderer()->setStyle(style);
174 }
175
176 bool MediaControlInputElement::hitTest(const IntPoint& absPoint)
177 {
178     if (renderer() && renderer()->style()->hasAppearance())
179         return renderer()->theme()->hitTestMediaControlPart(renderer(), absPoint);
180
181     return false;
182 }
183
184 void MediaControlInputElement::setDisplayType(MediaControlElementType displayType)
185 {
186     if (displayType == m_displayType)
187         return;
188
189     m_displayType = displayType;
190     if (RenderObject* o = renderer())
191         o->repaint();
192 }
193
194 // ----------------------------
195
196 MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* doc, HTMLMediaElement* element)
197     : MediaControlInputElement(doc, MEDIA_CONTROLS_MUTE_BUTTON, "button", element, element->muted() ? MediaUnMuteButton : MediaMuteButton)
198 {
199 }
200
201 void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
202 {
203     if (event->type() == eventNames().clickEvent) {
204         m_mediaElement->setMuted(!m_mediaElement->muted());
205         event->setDefaultHandled();
206     }
207     HTMLInputElement::defaultEventHandler(event);
208 }
209
210 void MediaControlMuteButtonElement::updateDisplayType()
211 {
212     setDisplayType(m_mediaElement->muted() ? MediaUnMuteButton : MediaMuteButton);
213 }
214
215 // ----------------------------
216
217 MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* doc, HTMLMediaElement* element)
218     : MediaControlInputElement(doc, MEDIA_CONTROLS_PLAY_BUTTON, "button", element, element->canPlay() ? MediaPlayButton : MediaPauseButton)
219 {
220 }
221
222 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
223 {
224     if (event->type() == eventNames().clickEvent) {
225         m_mediaElement->togglePlayState();
226         event->setDefaultHandled();
227     }
228     HTMLInputElement::defaultEventHandler(event);
229 }
230
231 void MediaControlPlayButtonElement::updateDisplayType()
232 {
233     setDisplayType(m_mediaElement->canPlay() ? MediaPlayButton : MediaPauseButton);
234 }
235
236 // ----------------------------
237
238 MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* doc, HTMLMediaElement* element, bool forward)
239     : MediaControlInputElement(doc, forward ? MEDIA_CONTROLS_SEEK_FORWARD_BUTTON : MEDIA_CONTROLS_SEEK_BACK_BUTTON,
240                                "button", element, forward ? MediaSeekForwardButton : MediaSeekBackButton)
241     , m_forward(forward)
242     , m_seeking(false)
243     , m_capturing(false)
244     , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
245 {
246 }
247
248 void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
249 {
250     if (event->type() == eventNames().mousedownEvent) {
251         if (Frame* frame = document()->frame()) {
252             m_capturing = true;
253             frame->eventHandler()->setCapturingMouseEventsNode(this);
254         }
255         m_mediaElement->pause();
256         m_seekTimer.startRepeating(cSeekRepeatDelay);
257         event->setDefaultHandled();
258     } else if (event->type() == eventNames().mouseupEvent) {
259         if (m_capturing)
260             if (Frame* frame = document()->frame()) {
261                 m_capturing = false;
262                 frame->eventHandler()->setCapturingMouseEventsNode(0);
263             }
264         ExceptionCode ec;
265         if (m_seeking || m_seekTimer.isActive()) {
266             if (!m_seeking) {
267                 float stepTime = m_forward ? cStepTime : -cStepTime;
268                 m_mediaElement->setCurrentTime(m_mediaElement->currentTime() + stepTime, ec);
269             }
270             m_seekTimer.stop();
271             m_seeking = false;
272             event->setDefaultHandled();
273         }
274     }
275     HTMLInputElement::defaultEventHandler(event);
276 }
277
278 void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
279 {
280     ExceptionCode ec;
281     m_seeking = true;
282     float seekTime = m_forward ? cSeekTime : -cSeekTime;
283     m_mediaElement->setCurrentTime(m_mediaElement->currentTime() + seekTime, ec);
284 }
285
286 // ----------------------------
287
288 MediaControlTimelineElement::MediaControlTimelineElement(Document* document, HTMLMediaElement* element)
289     : MediaControlInputElement(document, MEDIA_CONTROLS_TIMELINE, "range", element, MediaTimelineContainer)
290
291 }
292
293 void MediaControlTimelineElement::defaultEventHandler(Event* event)
294 {
295     if (event->type() == eventNames().mousedownEvent)
296         m_mediaElement->beginScrubbing();
297
298     HTMLInputElement::defaultEventHandler(event);
299
300      if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent ) {
301         return;
302     }
303
304     float time = narrowPrecisionToFloat(value().toDouble());
305     if (time != m_mediaElement->currentTime()) {
306         ExceptionCode ec;
307         m_mediaElement->setCurrentTime(time, ec);
308     }
309
310     RenderSlider* slider = static_cast<RenderSlider*>(renderer());
311     if (slider && slider->inDragMode())
312         static_cast<RenderMedia*>(m_mediaElement->renderer())->updateTimeDisplay();
313
314     if (event->type() == eventNames().mouseupEvent)
315         m_mediaElement->endScrubbing();
316 }
317
318 void MediaControlTimelineElement::update(bool updateDuration) 
319 {
320     if (updateDuration) {
321         float dur = m_mediaElement->duration();
322         setAttribute(maxAttr, String::number(isfinite(dur) ? dur : 0));
323     }
324     setValue(String::number(m_mediaElement->currentTime()));
325 }
326
327 // ----------------------------
328
329 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* doc, HTMLMediaElement* element)
330     : MediaControlInputElement(doc, MEDIA_CONTROLS_FULLSCREEN_BUTTON, "button", element, MediaFullscreenButton)
331 {
332 }
333
334 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
335 {
336     if (event->type() == eventNames().clickEvent) {
337         event->setDefaultHandled();
338     }
339     HTMLInputElement::defaultEventHandler(event);
340 }
341
342 bool MediaControlFullscreenButtonElement::rendererIsNeeded(RenderStyle* style)
343 {
344     return m_mediaElement->supportsFullscreen() && MediaControlInputElement::rendererIsNeeded(style);
345 }
346
347
348 // ----------------------------
349
350 } //namespace WebCore
351 #endif // enable(video)