2009-07-02 Pierre d'Herbemont <pdherbemont@apple.com>
[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 #import "LocalizedStrings.h"
36 #include "EventNames.h"
37 #include "FloatConversion.h"
38 #include "Frame.h"
39 #include "HTMLNames.h"
40 #include "MouseEvent.h"
41 #include "RenderMedia.h"
42 #include "RenderSlider.h"
43 #include "RenderTheme.h"
44 #include "CString.h"
45
46 namespace WebCore {
47
48 using namespace HTMLNames;
49
50 // FIXME: These constants may need to be tweaked to better match the seeking in the QT plugin
51 static const float cSeekRepeatDelay = 0.1f;
52 static const float cStepTime = 0.07f;
53 static const float cSeekTime = 0.2f;
54
55 MediaControlShadowRootElement::MediaControlShadowRootElement(Document* doc, HTMLMediaElement* mediaElement) 
56     : HTMLDivElement(divTag, doc)
57     , m_mediaElement(mediaElement) 
58 {
59     RefPtr<RenderStyle> rootStyle = RenderStyle::create();
60     rootStyle->inheritFrom(mediaElement->renderer()->style());
61     rootStyle->setDisplay(BLOCK);
62     rootStyle->setPosition(RelativePosition);
63     RenderMediaControlShadowRoot* renderer = new (mediaElement->renderer()->renderArena()) RenderMediaControlShadowRoot(this);
64     renderer->setStyle(rootStyle.release());
65     setRenderer(renderer);
66     setAttached();
67     setInDocument(true);
68 }
69
70 void MediaControlShadowRootElement::updateStyle()
71 {
72     if (renderer()) {
73         RenderStyle* timelineContainerStyle = m_mediaElement->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_TIMELINE_CONTAINER);
74         renderer()->setStyle(timelineContainerStyle);
75     }
76 }
77
78
79 // ----------------------------
80     
81
82 MediaControlElement::MediaControlElement(Document* doc, PseudoId pseudo, HTMLMediaElement* mediaElement) 
83     : HTMLDivElement(divTag, doc)
84     , m_mediaElement(mediaElement)
85     , m_pseudoStyleId(pseudo)
86 {
87     setInDocument(true);
88 }
89
90 void MediaControlElement::attachToParent(Element* parent)
91 {
92     parent->addChild(this);
93 }
94
95 void MediaControlElement::update()
96 {
97     if (renderer())
98         renderer()->updateFromElement();
99     updateStyle();
100 }
101
102 void MediaControlElement::updateStyle()
103 {
104     if (!m_mediaElement || !m_mediaElement->renderer())
105         return;
106
107     RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
108     if (!style)
109         return;
110
111     bool needsRenderer = rendererIsNeeded(style) && parent() && parent()->renderer();
112     if (renderer() && !needsRenderer)
113         detach();
114     else if (!renderer() && needsRenderer) {
115         RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style);
116         if (!renderer)
117             return;
118         renderer->setStyle(style);
119         setRenderer(renderer);
120         setAttached();
121         if (parent() && parent()->renderer()) {
122             // Find next sibling with a renderer to determine where to insert.
123             Node* sibling = nextSibling();
124             while (sibling && !sibling->renderer())
125                 sibling = sibling->nextSibling();
126             parent()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0);
127         }
128     } else if (renderer())
129         renderer()->setStyle(style);
130 }
131
132 // ----------------------------
133
134 MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document* doc, HTMLMediaElement* element)
135 : MediaControlElement(doc, MEDIA_CONTROLS_TIMELINE_CONTAINER, element)
136 {
137 }
138
139 bool MediaControlTimelineContainerElement::rendererIsNeeded(RenderStyle* style)
140 {
141     if (!HTMLDivElement::rendererIsNeeded(style))
142         return false;
143
144     // This is for MediaControllerThemeClassic:
145     // If there is no style for MediaControlStatusDisplayElement style, don't hide
146     // the timeline.
147     if (!m_mediaElement->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_STATUS_DISPLAY))
148         return true;
149
150     float duration = m_mediaElement->duration();
151     return !isnan(duration) && !isinf(duration);
152 }
153
154     
155 // ----------------------------
156
157 MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document* doc, HTMLMediaElement* element)
158 : MediaControlElement(doc, MEDIA_CONTROLS_STATUS_DISPLAY, element)
159 , m_stateBeingDisplayed(Nothing)
160 {
161 }
162
163 void MediaControlStatusDisplayElement::update()
164 {
165     MediaControlElement::update();
166
167     // Get the new state that we'll have to display.
168     StateBeingDisplayed newStateToDisplay = Nothing;
169     if (m_mediaElement->readyState() != HTMLMediaElement::HAVE_ENOUGH_DATA)
170         newStateToDisplay = Loading;
171     else if (m_mediaElement->isStreaming())
172         newStateToDisplay = LiveBroadcast;
173
174     // Propagate only if needed.
175     if (newStateToDisplay == m_stateBeingDisplayed)
176         return;
177     m_stateBeingDisplayed = newStateToDisplay;
178
179     ExceptionCode e;
180     switch (m_stateBeingDisplayed) {
181     case Nothing:
182         setInnerText("", e);
183         break;
184     case Loading:
185         setInnerText(mediaElementLoadingStateText(), e);
186         break;
187     case LiveBroadcast:
188         setInnerText(mediaElementLiveBroadcastStateText(), e);
189         break;
190     }
191 }
192
193 bool MediaControlStatusDisplayElement::rendererIsNeeded(RenderStyle* style)
194 {
195     if (!HTMLDivElement::rendererIsNeeded(style))
196         return false;
197     float duration = m_mediaElement->duration();
198     return (isnan(duration) || isinf(duration));
199 }
200
201 // ----------------------------
202     
203 MediaControlInputElement::MediaControlInputElement(Document* doc, PseudoId pseudo, const String& type, HTMLMediaElement* mediaElement, MediaControlElementType displayType) 
204     : HTMLInputElement(inputTag, doc)
205     , m_mediaElement(mediaElement)
206     , m_pseudoStyleId(pseudo)
207     , m_displayType(displayType)
208 {
209     setInputType(type);
210     setInDocument(true);
211 }
212
213 void MediaControlInputElement::attachToParent(Element* parent)
214 {
215     parent->addChild(this);
216 }
217
218 void MediaControlInputElement::update()
219 {
220     updateDisplayType();
221     if (renderer())
222         renderer()->updateFromElement();
223     updateStyle();
224 }
225
226 void MediaControlInputElement::updateStyle()
227 {
228     if (!m_mediaElement || !m_mediaElement->renderer())
229         return;
230
231     RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
232     if (!style)
233         return;
234
235     bool needsRenderer = rendererIsNeeded(style) && parent() && parent()->renderer();
236     if (renderer() && !needsRenderer)
237         detach();
238     else if (!renderer() && needsRenderer) {
239         RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style);
240         if (!renderer)
241             return;
242         renderer->setStyle(style);
243         setRenderer(renderer);
244         setAttached();
245         if (parent() && parent()->renderer()) {
246             // Find next sibling with a renderer to determine where to insert.
247             Node* sibling = nextSibling();
248             while (sibling && !sibling->renderer())
249                 sibling = sibling->nextSibling();
250             parent()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0);
251         }
252     } else if (renderer())
253         renderer()->setStyle(style);
254 }
255
256 bool MediaControlInputElement::hitTest(const IntPoint& absPoint)
257 {
258     if (renderer() && renderer()->style()->hasAppearance())
259         return renderer()->theme()->hitTestMediaControlPart(renderer(), absPoint);
260
261     return false;
262 }
263
264 void MediaControlInputElement::setDisplayType(MediaControlElementType displayType)
265 {
266     if (displayType == m_displayType)
267         return;
268
269     m_displayType = displayType;
270     if (RenderObject* o = renderer())
271         o->repaint();
272 }
273
274 // ----------------------------
275
276 MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* doc, HTMLMediaElement* element)
277     : MediaControlInputElement(doc, MEDIA_CONTROLS_MUTE_BUTTON, "button", element, element->muted() ? MediaUnMuteButton : MediaMuteButton)
278 {
279 }
280
281 void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
282 {
283     if (event->type() == eventNames().clickEvent) {
284         m_mediaElement->setMuted(!m_mediaElement->muted());
285         event->setDefaultHandled();
286     }
287     HTMLInputElement::defaultEventHandler(event);
288 }
289
290 void MediaControlMuteButtonElement::updateDisplayType()
291 {
292     setDisplayType(m_mediaElement->muted() ? MediaUnMuteButton : MediaMuteButton);
293 }
294
295 // ----------------------------
296
297 MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* doc, HTMLMediaElement* element)
298     : MediaControlInputElement(doc, MEDIA_CONTROLS_PLAY_BUTTON, "button", element, element->canPlay() ? MediaPlayButton : MediaPauseButton)
299 {
300 }
301
302 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
303 {
304     if (event->type() == eventNames().clickEvent) {
305         m_mediaElement->togglePlayState();
306         event->setDefaultHandled();
307     }
308     HTMLInputElement::defaultEventHandler(event);
309 }
310
311 void MediaControlPlayButtonElement::updateDisplayType()
312 {
313     setDisplayType(m_mediaElement->canPlay() ? MediaPlayButton : MediaPauseButton);
314 }
315
316 // ----------------------------
317
318 MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* doc, HTMLMediaElement* element, bool forward)
319     : MediaControlInputElement(doc, forward ? MEDIA_CONTROLS_SEEK_FORWARD_BUTTON : MEDIA_CONTROLS_SEEK_BACK_BUTTON,
320                                "button", element, forward ? MediaSeekForwardButton : MediaSeekBackButton)
321     , m_forward(forward)
322     , m_seeking(false)
323     , m_capturing(false)
324     , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
325 {
326 }
327
328 void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
329 {
330     if (event->type() == eventNames().mousedownEvent) {
331         if (Frame* frame = document()->frame()) {
332             m_capturing = true;
333             frame->eventHandler()->setCapturingMouseEventsNode(this);
334         }
335         m_mediaElement->pause();
336         m_seekTimer.startRepeating(cSeekRepeatDelay);
337         event->setDefaultHandled();
338     } else if (event->type() == eventNames().mouseupEvent) {
339         if (m_capturing)
340             if (Frame* frame = document()->frame()) {
341                 m_capturing = false;
342                 frame->eventHandler()->setCapturingMouseEventsNode(0);
343             }
344         ExceptionCode ec;
345         if (m_seeking || m_seekTimer.isActive()) {
346             if (!m_seeking) {
347                 float stepTime = m_forward ? cStepTime : -cStepTime;
348                 m_mediaElement->setCurrentTime(m_mediaElement->currentTime() + stepTime, ec);
349             }
350             m_seekTimer.stop();
351             m_seeking = false;
352             event->setDefaultHandled();
353         }
354     }
355     HTMLInputElement::defaultEventHandler(event);
356 }
357
358 void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
359 {
360     ExceptionCode ec;
361     m_seeking = true;
362     float seekTime = m_forward ? cSeekTime : -cSeekTime;
363     m_mediaElement->setCurrentTime(m_mediaElement->currentTime() + seekTime, ec);
364 }
365
366 // ----------------------------
367
368 MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document* doc, HTMLMediaElement* element)
369 : MediaControlInputElement(doc, MEDIA_CONTROLS_REWIND_BUTTON, "button", element, MediaRewindButton)
370 {
371 }
372
373 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
374 {
375     if (event->type() == eventNames().clickEvent) {
376         m_mediaElement->rewind(30);
377         event->setDefaultHandled();
378     }    
379     HTMLInputElement::defaultEventHandler(event);
380 }
381
382 // ----------------------------
383
384 MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document* doc, HTMLMediaElement* element)
385 : MediaControlInputElement(doc, MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON, "button", element, MediaReturnToRealtimeButton)
386 {
387 }
388
389 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
390 {
391     if (event->type() == eventNames().clickEvent) {
392         m_mediaElement->returnToRealtime();
393         event->setDefaultHandled();
394     }
395     HTMLInputElement::defaultEventHandler(event);
396 }
397
398 bool MediaControlReturnToRealtimeButtonElement::rendererIsNeeded(RenderStyle* style)
399 {
400     return HTMLInputElement::rendererIsNeeded(style) && m_mediaElement->isStreaming();
401 }
402
403 // ----------------------------
404
405 MediaControlTimelineElement::MediaControlTimelineElement(Document* document, HTMLMediaElement* element)
406     : MediaControlInputElement(document, MEDIA_CONTROLS_TIMELINE, "range", element, MediaTimelineContainer)
407
408 }
409
410 void MediaControlTimelineElement::defaultEventHandler(Event* event)
411 {
412     if (event->type() == eventNames().mousedownEvent)
413         m_mediaElement->beginScrubbing();
414
415     MediaControlInputElement::defaultEventHandler(event);
416
417      if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent ) {
418         return;
419     }
420
421     float time = narrowPrecisionToFloat(value().toDouble());
422     if (time != m_mediaElement->currentTime()) {
423         ExceptionCode ec;
424         m_mediaElement->setCurrentTime(time, ec);
425     }
426
427     RenderSlider* slider = static_cast<RenderSlider*>(renderer());
428     if (slider && slider->inDragMode())
429         static_cast<RenderMedia*>(m_mediaElement->renderer())->updateTimeDisplay();
430
431     if (event->type() == eventNames().mouseupEvent)
432         m_mediaElement->endScrubbing();
433 }
434
435 void MediaControlTimelineElement::update(bool updateDuration) 
436 {
437     if (updateDuration) {
438         float dur = m_mediaElement->duration();
439         setAttribute(maxAttr, String::number(isfinite(dur) ? dur : 0));
440     }
441     setValue(String::number(m_mediaElement->currentTime()));
442     MediaControlInputElement::update();
443 }
444
445 // ----------------------------
446
447 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* doc, HTMLMediaElement* element)
448     : MediaControlInputElement(doc, MEDIA_CONTROLS_FULLSCREEN_BUTTON, "button", element, MediaFullscreenButton)
449 {
450 }
451
452 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
453 {
454     if (event->type() == eventNames().clickEvent) {
455         event->setDefaultHandled();
456     }
457     HTMLInputElement::defaultEventHandler(event);
458 }
459
460 bool MediaControlFullscreenButtonElement::rendererIsNeeded(RenderStyle* style)
461 {
462     return MediaControlInputElement::rendererIsNeeded(style) && m_mediaElement->supportsFullscreen();
463 }
464
465
466 // ----------------------------
467
468 } //namespace WebCore
469 #endif // enable(video)