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