• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 
MediaControlShadowRootElement(Document * doc,HTMLMediaElement * mediaElement)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 
updateStyle()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 
MediaControlElement(Document * doc,PseudoId pseudo,HTMLMediaElement * mediaElement)81 MediaControlElement::MediaControlElement(Document* doc, PseudoId pseudo, HTMLMediaElement* mediaElement)
82     : HTMLDivElement(divTag, doc)
83     , m_mediaElement(mediaElement)
84     , m_pseudoStyleId(pseudo)
85 {
86     setInDocument(true);
87 }
88 
attachToParent(Element * parent)89 void MediaControlElement::attachToParent(Element* parent)
90 {
91     parent->addChild(this);
92 }
93 
update()94 void MediaControlElement::update()
95 {
96     if (renderer())
97         renderer()->updateFromElement();
98     updateStyle();
99 }
100 
styleForElement()101 PassRefPtr<RenderStyle> MediaControlElement::styleForElement()
102 {
103     RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
104     if (!style)
105         return 0;
106 
107     // text-decoration can't be overrided from CSS. So we do it here.
108     // See https://bugs.webkit.org/show_bug.cgi?id=27015
109     style->setTextDecoration(TDNONE);
110     style->setTextDecorationsInEffect(TDNONE);
111 
112     return style;
113 }
114 
rendererIsNeeded(RenderStyle * style)115 bool MediaControlElement::rendererIsNeeded(RenderStyle* style)
116 {
117     return HTMLDivElement::rendererIsNeeded(style) && parent() && parent()->renderer();
118 }
119 
attach()120 void MediaControlElement::attach()
121 {
122     RefPtr<RenderStyle> style = styleForElement();
123     if (!style)
124         return;
125     bool needsRenderer = rendererIsNeeded(style.get());
126     if (!needsRenderer)
127         return;
128     RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style.get());
129     if (!renderer)
130         return;
131     renderer->setStyle(style.get());
132     setRenderer(renderer);
133     if (parent() && parent()->renderer()) {
134         // Find next sibling with a renderer to determine where to insert.
135         Node* sibling = nextSibling();
136         while (sibling && !sibling->renderer())
137             sibling = sibling->nextSibling();
138         parent()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0);
139     }
140     ContainerNode::attach();
141 }
142 
updateStyle()143 void MediaControlElement::updateStyle()
144 {
145     if (!m_mediaElement || !m_mediaElement->renderer())
146         return;
147 
148     RefPtr<RenderStyle> style = styleForElement();
149     if (!style)
150         return;
151 
152     bool needsRenderer = rendererIsNeeded(style.get()) && parent() && parent()->renderer();
153     if (renderer() && !needsRenderer)
154         detach();
155     else if (!renderer() && needsRenderer)
156         attach();
157     else if (renderer()) {
158         renderer()->setStyle(style.get());
159 
160         // Make sure that if there is any innerText renderer, it is updated as well.
161         if (firstChild() && firstChild()->renderer())
162             firstChild()->renderer()->setStyle(style.get());
163     }
164 }
165 
166 // ----------------------------
167 
MediaControlTimelineContainerElement(Document * doc,HTMLMediaElement * element)168 MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document* doc, HTMLMediaElement* element)
169 : MediaControlElement(doc, MEDIA_CONTROLS_TIMELINE_CONTAINER, element)
170 {
171 }
172 
rendererIsNeeded(RenderStyle * style)173 bool MediaControlTimelineContainerElement::rendererIsNeeded(RenderStyle* style)
174 {
175     if (!MediaControlElement::rendererIsNeeded(style))
176         return false;
177 
178     // This is for MediaControllerThemeClassic:
179     // If there is no style for MediaControlStatusDisplayElement style, don't hide
180     // the timeline.
181     if (!m_mediaElement->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_STATUS_DISPLAY))
182         return true;
183 
184     float duration = m_mediaElement->duration();
185     return !isnan(duration) && !isinf(duration);
186 }
187 
188 
189 // ----------------------------
190 
MediaControlStatusDisplayElement(Document * doc,HTMLMediaElement * element)191 MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document* doc, HTMLMediaElement* element)
192 : MediaControlElement(doc, MEDIA_CONTROLS_STATUS_DISPLAY, element)
193 , m_stateBeingDisplayed(Nothing)
194 {
195 }
196 
update()197 void MediaControlStatusDisplayElement::update()
198 {
199     MediaControlElement::update();
200 
201     // Get the new state that we'll have to display.
202     StateBeingDisplayed newStateToDisplay = Nothing;
203 
204     if (m_mediaElement->readyState() != HTMLMediaElement::HAVE_ENOUGH_DATA && !m_mediaElement->currentSrc().isEmpty())
205         newStateToDisplay = Loading;
206     else if (m_mediaElement->movieLoadType() == MediaPlayer::LiveStream)
207         newStateToDisplay = LiveBroadcast;
208 
209     // Propagate only if needed.
210     if (newStateToDisplay == m_stateBeingDisplayed)
211         return;
212     m_stateBeingDisplayed = newStateToDisplay;
213 
214     ExceptionCode e;
215     switch (m_stateBeingDisplayed) {
216     case Nothing:
217         setInnerText("", e);
218         break;
219     case Loading:
220         setInnerText(mediaElementLoadingStateText(), e);
221         break;
222     case LiveBroadcast:
223         setInnerText(mediaElementLiveBroadcastStateText(), e);
224         break;
225     }
226 }
227 
rendererIsNeeded(RenderStyle * style)228 bool MediaControlStatusDisplayElement::rendererIsNeeded(RenderStyle* style)
229 {
230     if (!MediaControlElement::rendererIsNeeded(style))
231         return false;
232     float duration = m_mediaElement->duration();
233     return (isnan(duration) || isinf(duration));
234 }
235 
236 // ----------------------------
237 
MediaControlInputElement(Document * doc,PseudoId pseudo,const String & type,HTMLMediaElement * mediaElement,MediaControlElementType displayType)238 MediaControlInputElement::MediaControlInputElement(Document* doc, PseudoId pseudo, const String& type, HTMLMediaElement* mediaElement, MediaControlElementType displayType)
239     : HTMLInputElement(inputTag, doc)
240     , m_mediaElement(mediaElement)
241     , m_pseudoStyleId(pseudo)
242     , m_displayType(displayType)
243 {
244     setInputType(type);
245     setInDocument(true);
246 }
247 
attachToParent(Element * parent)248 void MediaControlInputElement::attachToParent(Element* parent)
249 {
250     parent->addChild(this);
251 }
252 
update()253 void MediaControlInputElement::update()
254 {
255     updateDisplayType();
256     if (renderer())
257         renderer()->updateFromElement();
258     updateStyle();
259 }
260 
styleForElement()261 PassRefPtr<RenderStyle> MediaControlInputElement::styleForElement()
262 {
263     return m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
264 }
265 
rendererIsNeeded(RenderStyle * style)266 bool MediaControlInputElement::rendererIsNeeded(RenderStyle* style)
267 {
268     return HTMLInputElement::rendererIsNeeded(style) && parent() && parent()->renderer();
269 }
270 
attach()271 void MediaControlInputElement::attach()
272 {
273     RefPtr<RenderStyle> style = styleForElement();
274     if (!style)
275         return;
276 
277     bool needsRenderer = rendererIsNeeded(style.get());
278     if (!needsRenderer)
279         return;
280     RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style.get());
281     if (!renderer)
282         return;
283     renderer->setStyle(style.get());
284     setRenderer(renderer);
285     if (parent() && parent()->renderer()) {
286         // Find next sibling with a renderer to determine where to insert.
287         Node* sibling = nextSibling();
288         while (sibling && !sibling->renderer())
289             sibling = sibling->nextSibling();
290         parent()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0);
291     }
292     ContainerNode::attach();
293 }
294 
updateStyle()295 void MediaControlInputElement::updateStyle()
296 {
297     if (!m_mediaElement || !m_mediaElement->renderer())
298         return;
299 
300     RefPtr<RenderStyle> style = styleForElement();
301     if (!style)
302         return;
303 
304     bool needsRenderer = rendererIsNeeded(style.get()) && parent() && parent()->renderer();
305     if (renderer() && !needsRenderer)
306         detach();
307     else if (!renderer() && needsRenderer)
308         attach();
309     else if (renderer())
310         renderer()->setStyle(style.get());
311 }
312 
hitTest(const IntPoint & absPoint)313 bool MediaControlInputElement::hitTest(const IntPoint& absPoint)
314 {
315     if (renderer() && renderer()->style()->hasAppearance())
316         return renderer()->theme()->hitTestMediaControlPart(renderer(), absPoint);
317 
318     return false;
319 }
320 
setDisplayType(MediaControlElementType displayType)321 void MediaControlInputElement::setDisplayType(MediaControlElementType displayType)
322 {
323     if (displayType == m_displayType)
324         return;
325 
326     m_displayType = displayType;
327     if (RenderObject* o = renderer())
328         o->repaint();
329 }
330 
331 // ----------------------------
332 
MediaControlMuteButtonElement(Document * doc,HTMLMediaElement * element)333 MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* doc, HTMLMediaElement* element)
334     : MediaControlInputElement(doc, MEDIA_CONTROLS_MUTE_BUTTON, "button", element, element->muted() ? MediaUnMuteButton : MediaMuteButton)
335 {
336 }
337 
defaultEventHandler(Event * event)338 void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
339 {
340     if (event->type() == eventNames().clickEvent) {
341         m_mediaElement->setMuted(!m_mediaElement->muted());
342         event->setDefaultHandled();
343     }
344     HTMLInputElement::defaultEventHandler(event);
345 }
346 
updateDisplayType()347 void MediaControlMuteButtonElement::updateDisplayType()
348 {
349     setDisplayType(m_mediaElement->muted() ? MediaUnMuteButton : MediaMuteButton);
350 }
351 
352 // ----------------------------
353 
MediaControlPlayButtonElement(Document * doc,HTMLMediaElement * element)354 MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* doc, HTMLMediaElement* element)
355     : MediaControlInputElement(doc, MEDIA_CONTROLS_PLAY_BUTTON, "button", element, element->canPlay() ? MediaPlayButton : MediaPauseButton)
356 {
357 }
358 
defaultEventHandler(Event * event)359 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
360 {
361     if (event->type() == eventNames().clickEvent) {
362         m_mediaElement->togglePlayState();
363         event->setDefaultHandled();
364     }
365     HTMLInputElement::defaultEventHandler(event);
366 }
367 
updateDisplayType()368 void MediaControlPlayButtonElement::updateDisplayType()
369 {
370     setDisplayType(m_mediaElement->canPlay() ? MediaPlayButton : MediaPauseButton);
371 }
372 
373 // ----------------------------
374 
MediaControlSeekButtonElement(Document * doc,HTMLMediaElement * element,bool forward)375 MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* doc, HTMLMediaElement* element, bool forward)
376     : MediaControlInputElement(doc, forward ? MEDIA_CONTROLS_SEEK_FORWARD_BUTTON : MEDIA_CONTROLS_SEEK_BACK_BUTTON,
377                                "button", element, forward ? MediaSeekForwardButton : MediaSeekBackButton)
378     , m_forward(forward)
379     , m_seeking(false)
380     , m_capturing(false)
381     , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
382 {
383 }
384 
defaultEventHandler(Event * event)385 void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
386 {
387     if (event->type() == eventNames().mousedownEvent) {
388         if (Frame* frame = document()->frame()) {
389             m_capturing = true;
390             frame->eventHandler()->setCapturingMouseEventsNode(this);
391         }
392         m_mediaElement->pause();
393         m_seekTimer.startRepeating(cSeekRepeatDelay);
394         event->setDefaultHandled();
395     } else if (event->type() == eventNames().mouseupEvent) {
396         if (m_capturing)
397             if (Frame* frame = document()->frame()) {
398                 m_capturing = false;
399                 frame->eventHandler()->setCapturingMouseEventsNode(0);
400             }
401         ExceptionCode ec;
402         if (m_seeking || m_seekTimer.isActive()) {
403             if (!m_seeking) {
404                 float stepTime = m_forward ? cStepTime : -cStepTime;
405                 m_mediaElement->setCurrentTime(m_mediaElement->currentTime() + stepTime, ec);
406             }
407             m_seekTimer.stop();
408             m_seeking = false;
409             event->setDefaultHandled();
410         }
411     }
412     HTMLInputElement::defaultEventHandler(event);
413 }
414 
seekTimerFired(Timer<MediaControlSeekButtonElement> *)415 void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
416 {
417     ExceptionCode ec;
418     m_seeking = true;
419     float seekTime = m_forward ? cSeekTime : -cSeekTime;
420     m_mediaElement->setCurrentTime(m_mediaElement->currentTime() + seekTime, ec);
421 }
422 
detach()423 void MediaControlSeekButtonElement::detach()
424 {
425     if (m_capturing) {
426         if (Frame* frame = document()->frame())
427             frame->eventHandler()->setCapturingMouseEventsNode(0);
428     }
429     MediaControlInputElement::detach();
430 }
431 
432 
433 // ----------------------------
434 
MediaControlRewindButtonElement(Document * doc,HTMLMediaElement * element)435 MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document* doc, HTMLMediaElement* element)
436 : MediaControlInputElement(doc, MEDIA_CONTROLS_REWIND_BUTTON, "button", element, MediaRewindButton)
437 {
438 }
439 
defaultEventHandler(Event * event)440 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
441 {
442     if (event->type() == eventNames().clickEvent) {
443         m_mediaElement->rewind(30);
444         event->setDefaultHandled();
445     }
446     HTMLInputElement::defaultEventHandler(event);
447 }
448 
rendererIsNeeded(RenderStyle * style)449 bool MediaControlRewindButtonElement::rendererIsNeeded(RenderStyle* style)
450 {
451     return MediaControlInputElement::rendererIsNeeded(style) && m_mediaElement->movieLoadType() != MediaPlayer::LiveStream;
452 }
453 
454 
455 // ----------------------------
456 
MediaControlReturnToRealtimeButtonElement(Document * doc,HTMLMediaElement * element)457 MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document* doc, HTMLMediaElement* element)
458 : MediaControlInputElement(doc, MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON, "button", element, MediaReturnToRealtimeButton)
459 {
460 }
461 
defaultEventHandler(Event * event)462 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
463 {
464     if (event->type() == eventNames().clickEvent) {
465         m_mediaElement->returnToRealtime();
466         event->setDefaultHandled();
467     }
468     HTMLInputElement::defaultEventHandler(event);
469 }
470 
rendererIsNeeded(RenderStyle * style)471 bool MediaControlReturnToRealtimeButtonElement::rendererIsNeeded(RenderStyle* style)
472 {
473     return MediaControlInputElement::rendererIsNeeded(style) && m_mediaElement->movieLoadType() == MediaPlayer::LiveStream;
474 }
475 
476 // ----------------------------
477 
MediaControlTimelineElement(Document * document,HTMLMediaElement * element)478 MediaControlTimelineElement::MediaControlTimelineElement(Document* document, HTMLMediaElement* element)
479     : MediaControlInputElement(document, MEDIA_CONTROLS_TIMELINE, "range", element, MediaTimelineContainer)
480 {
481 }
482 
defaultEventHandler(Event * event)483 void MediaControlTimelineElement::defaultEventHandler(Event* event)
484 {
485     // Left button is 0. Accepts only if mouse event is from left button.
486     if (!event->isMouseEvent() || static_cast<MouseEvent*>(event)->button())
487         return;
488 
489     if (event->type() == eventNames().mousedownEvent)
490         m_mediaElement->beginScrubbing();
491 
492     MediaControlInputElement::defaultEventHandler(event);
493 
494     if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
495         return;
496 
497     float time = narrowPrecisionToFloat(value().toDouble());
498     if (time != m_mediaElement->currentTime()) {
499         ExceptionCode ec;
500         m_mediaElement->setCurrentTime(time, ec);
501     }
502 
503     RenderSlider* slider = toRenderSlider(renderer());
504     if (slider && slider->inDragMode())
505         toRenderMedia(m_mediaElement->renderer())->updateTimeDisplay();
506 
507     if (event->type() == eventNames().mouseupEvent)
508         m_mediaElement->endScrubbing();
509 }
510 
update(bool updateDuration)511 void MediaControlTimelineElement::update(bool updateDuration)
512 {
513     if (updateDuration) {
514         float dur = m_mediaElement->duration();
515         setAttribute(maxAttr, String::number(isfinite(dur) ? dur : 0));
516     }
517     setValue(String::number(m_mediaElement->currentTime()));
518     MediaControlInputElement::update();
519 }
520 
521 // ----------------------------
522 
MediaControlFullscreenButtonElement(Document * doc,HTMLMediaElement * element)523 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* doc, HTMLMediaElement* element)
524     : MediaControlInputElement(doc, MEDIA_CONTROLS_FULLSCREEN_BUTTON, "button", element, MediaFullscreenButton)
525 {
526 }
527 
defaultEventHandler(Event * event)528 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
529 {
530     if (event->type() == eventNames().clickEvent) {
531         event->setDefaultHandled();
532     }
533     HTMLInputElement::defaultEventHandler(event);
534 }
535 
rendererIsNeeded(RenderStyle * style)536 bool MediaControlFullscreenButtonElement::rendererIsNeeded(RenderStyle* style)
537 {
538     return MediaControlInputElement::rendererIsNeeded(style) && m_mediaElement->supportsFullscreen();
539 }
540 
541 
542 // ----------------------------
543 
MediaControlTimeDisplayElement(Document * doc,PseudoId pseudo,HTMLMediaElement * element)544 MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(Document* doc, PseudoId pseudo, HTMLMediaElement* element)
545     : MediaControlElement(doc, pseudo, element)
546     , m_isVisible(true)
547 {
548 }
549 
styleForElement()550 PassRefPtr<RenderStyle> MediaControlTimeDisplayElement::styleForElement()
551 {
552     RefPtr<RenderStyle> style = MediaControlElement::styleForElement();
553     if (!m_isVisible) {
554         style = RenderStyle::clone(style.get());
555         style->setWidth(Length(0, Fixed));
556     }
557     return style;
558 }
559 
setVisible(bool visible)560 void MediaControlTimeDisplayElement::setVisible(bool visible)
561 {
562     if (visible == m_isVisible)
563         return;
564     m_isVisible = visible;
565 
566     // This function is used during the RenderMedia::layout()
567     // call, where we cannot change the renderer at this time.
568     if (!renderer() || !renderer()->style())
569         return;
570 
571     RefPtr<RenderStyle> style = styleForElement();
572     renderer()->setStyle(style.get());
573 }
574 
575 
576 } //namespace WebCore
577 #endif // enable(video)
578