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 #include "core/html/shadow/MediaControlElements.h"
32
33 #include "bindings/v8/ExceptionStatePlaceholder.h"
34 #include "core/dom/DOMTokenList.h"
35 #include "core/dom/FullscreenElementStack.h"
36 #include "core/dom/shadow/ShadowRoot.h"
37 #include "core/events/MouseEvent.h"
38 #include "core/frame/LocalFrame.h"
39 #include "core/html/HTMLVideoElement.h"
40 #include "core/html/MediaController.h"
41 #include "core/html/shadow/MediaControls.h"
42 #include "core/html/track/TextTrack.h"
43 #include "core/html/track/vtt/VTTRegionList.h"
44 #include "core/page/EventHandler.h"
45 #include "core/rendering/RenderMediaControlElements.h"
46 #include "core/rendering/RenderSlider.h"
47 #include "core/rendering/RenderTheme.h"
48 #include "core/rendering/RenderVideo.h"
49 #include "platform/RuntimeEnabledFeatures.h"
50
51 namespace WebCore {
52
53 using namespace HTMLNames;
54
55 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId();
56 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId();
57
58 // This is the duration from mediaControls.css
59 static const double fadeOutDuration = 0.3;
60
isUserInteractionEvent(Event * event)61 static bool isUserInteractionEvent(Event* event)
62 {
63 const AtomicString& type = event->type();
64 return type == EventTypeNames::mousedown
65 || type == EventTypeNames::mouseup
66 || type == EventTypeNames::click
67 || type == EventTypeNames::dblclick
68 || event->isKeyboardEvent()
69 || event->isTouchEvent();
70 }
71
72 // Sliders (the volume control and timeline) need to capture some additional events used when dragging the thumb.
isUserInteractionEventForSlider(Event * event)73 static bool isUserInteractionEventForSlider(Event* event)
74 {
75 const AtomicString& type = event->type();
76 return type == EventTypeNames::mousedown
77 || type == EventTypeNames::mouseup
78 || type == EventTypeNames::click
79 || type == EventTypeNames::dblclick
80 || type == EventTypeNames::mouseover
81 || type == EventTypeNames::mouseout
82 || type == EventTypeNames::mousemove
83 || event->isKeyboardEvent()
84 || event->isTouchEvent();
85 }
86
87
MediaControlPanelElement(MediaControls & mediaControls)88 MediaControlPanelElement::MediaControlPanelElement(MediaControls& mediaControls)
89 : MediaControlDivElement(mediaControls, MediaControlsPanel)
90 , m_isDisplayed(false)
91 , m_opaque(true)
92 , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired)
93 {
94 }
95
create(MediaControls & mediaControls)96 PassRefPtrWillBeRawPtr<MediaControlPanelElement> MediaControlPanelElement::create(MediaControls& mediaControls)
97 {
98 return adoptRefWillBeNoop(new MediaControlPanelElement(mediaControls));
99 }
100
shadowPseudoId() const101 const AtomicString& MediaControlPanelElement::shadowPseudoId() const
102 {
103 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel", AtomicString::ConstructFromLiteral));
104 return id;
105 }
106
defaultEventHandler(Event * event)107 void MediaControlPanelElement::defaultEventHandler(Event* event)
108 {
109 // Suppress the media element activation behavior (toggle play/pause) when
110 // any part of the control panel is clicked.
111 if (event->type() == EventTypeNames::click) {
112 event->setDefaultHandled();
113 return;
114 }
115 HTMLDivElement::defaultEventHandler(event);
116 }
117
startTimer()118 void MediaControlPanelElement::startTimer()
119 {
120 stopTimer();
121
122 // The timer is required to set the property display:'none' on the panel,
123 // such that captions are correctly displayed at the bottom of the video
124 // at the end of the fadeout transition.
125 // FIXME: Racing a transition with a setTimeout like this is wrong.
126 m_transitionTimer.startOneShot(fadeOutDuration, FROM_HERE);
127 }
128
stopTimer()129 void MediaControlPanelElement::stopTimer()
130 {
131 if (m_transitionTimer.isActive())
132 m_transitionTimer.stop();
133 }
134
transitionTimerFired(Timer<MediaControlPanelElement> *)135 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*)
136 {
137 if (!m_opaque)
138 hide();
139
140 stopTimer();
141 }
142
makeOpaque()143 void MediaControlPanelElement::makeOpaque()
144 {
145 if (m_opaque)
146 return;
147
148 setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER);
149 m_opaque = true;
150
151 if (m_isDisplayed)
152 show();
153 }
154
makeTransparent()155 void MediaControlPanelElement::makeTransparent()
156 {
157 if (!m_opaque)
158 return;
159
160 setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
161
162 m_opaque = false;
163 startTimer();
164 }
165
setIsDisplayed(bool isDisplayed)166 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed)
167 {
168 m_isDisplayed = isDisplayed;
169 }
170
keepEventInNode(Event * event)171 bool MediaControlPanelElement::keepEventInNode(Event* event)
172 {
173 return isUserInteractionEvent(event);
174 }
175
176 // ----------------------------
177
MediaControlPanelEnclosureElement(MediaControls & mediaControls)178 MediaControlPanelEnclosureElement::MediaControlPanelEnclosureElement(MediaControls& mediaControls)
179 // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
180 : MediaControlDivElement(mediaControls, MediaControlsPanel)
181 {
182 }
183
create(MediaControls & mediaControls)184 PassRefPtrWillBeRawPtr<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(MediaControls& mediaControls)
185 {
186 return adoptRefWillBeNoop(new MediaControlPanelEnclosureElement(mediaControls));
187 }
188
shadowPseudoId() const189 const AtomicString& MediaControlPanelEnclosureElement::shadowPseudoId() const
190 {
191 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-enclosure", AtomicString::ConstructFromLiteral));
192 return id;
193 }
194
195 // ----------------------------
196
MediaControlOverlayEnclosureElement(MediaControls & mediaControls)197 MediaControlOverlayEnclosureElement::MediaControlOverlayEnclosureElement(MediaControls& mediaControls)
198 // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
199 : MediaControlDivElement(mediaControls, MediaControlsPanel)
200 {
201 }
202
create(MediaControls & mediaControls)203 PassRefPtrWillBeRawPtr<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(MediaControls& mediaControls)
204 {
205 return adoptRefWillBeNoop(new MediaControlOverlayEnclosureElement(mediaControls));
206 }
207
shadowPseudoId() const208 const AtomicString& MediaControlOverlayEnclosureElement::shadowPseudoId() const
209 {
210 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-enclosure", AtomicString::ConstructFromLiteral));
211 return id;
212 }
213
214 // ----------------------------
215
MediaControlMuteButtonElement(MediaControls & mediaControls)216 MediaControlMuteButtonElement::MediaControlMuteButtonElement(MediaControls& mediaControls)
217 : MediaControlInputElement(mediaControls, MediaMuteButton)
218 {
219 }
220
create(MediaControls & mediaControls)221 PassRefPtrWillBeRawPtr<MediaControlMuteButtonElement> MediaControlMuteButtonElement::create(MediaControls& mediaControls)
222 {
223 RefPtrWillBeRawPtr<MediaControlMuteButtonElement> button = adoptRefWillBeNoop(new MediaControlMuteButtonElement(mediaControls));
224 button->ensureUserAgentShadowRoot();
225 button->setType("button");
226 return button.release();
227 }
228
defaultEventHandler(Event * event)229 void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
230 {
231 if (event->type() == EventTypeNames::click) {
232 mediaElement().setMuted(!mediaElement().muted());
233 event->setDefaultHandled();
234 }
235
236 HTMLInputElement::defaultEventHandler(event);
237 }
238
updateDisplayType()239 void MediaControlMuteButtonElement::updateDisplayType()
240 {
241 setDisplayType(mediaElement().muted() ? MediaUnMuteButton : MediaMuteButton);
242 }
243
shadowPseudoId() const244 const AtomicString& MediaControlMuteButtonElement::shadowPseudoId() const
245 {
246 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button", AtomicString::ConstructFromLiteral));
247 return id;
248 }
249
250 // ----------------------------
251
MediaControlPlayButtonElement(MediaControls & mediaControls)252 MediaControlPlayButtonElement::MediaControlPlayButtonElement(MediaControls& mediaControls)
253 : MediaControlInputElement(mediaControls, MediaPlayButton)
254 {
255 }
256
create(MediaControls & mediaControls)257 PassRefPtrWillBeRawPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(MediaControls& mediaControls)
258 {
259 RefPtrWillBeRawPtr<MediaControlPlayButtonElement> button = adoptRefWillBeNoop(new MediaControlPlayButtonElement(mediaControls));
260 button->ensureUserAgentShadowRoot();
261 button->setType("button");
262 return button.release();
263 }
264
defaultEventHandler(Event * event)265 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
266 {
267 if (event->type() == EventTypeNames::click) {
268 mediaElement().togglePlayState();
269 updateDisplayType();
270 event->setDefaultHandled();
271 }
272 HTMLInputElement::defaultEventHandler(event);
273 }
274
updateDisplayType()275 void MediaControlPlayButtonElement::updateDisplayType()
276 {
277 setDisplayType(mediaElement().togglePlayStateWillPlay() ? MediaPlayButton : MediaPauseButton);
278 }
279
shadowPseudoId() const280 const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const
281 {
282 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button", AtomicString::ConstructFromLiteral));
283 return id;
284 }
285
286 // ----------------------------
287
MediaControlOverlayPlayButtonElement(MediaControls & mediaControls)288 MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(MediaControls& mediaControls)
289 : MediaControlInputElement(mediaControls, MediaOverlayPlayButton)
290 {
291 }
292
create(MediaControls & mediaControls)293 PassRefPtrWillBeRawPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(MediaControls& mediaControls)
294 {
295 RefPtrWillBeRawPtr<MediaControlOverlayPlayButtonElement> button = adoptRefWillBeNoop(new MediaControlOverlayPlayButtonElement(mediaControls));
296 button->ensureUserAgentShadowRoot();
297 button->setType("button");
298 return button.release();
299 }
300
defaultEventHandler(Event * event)301 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event)
302 {
303 if (event->type() == EventTypeNames::click && mediaElement().togglePlayStateWillPlay()) {
304 mediaElement().togglePlayState();
305 updateDisplayType();
306 event->setDefaultHandled();
307 }
308 }
309
updateDisplayType()310 void MediaControlOverlayPlayButtonElement::updateDisplayType()
311 {
312 if (mediaElement().togglePlayStateWillPlay()) {
313 show();
314 } else
315 hide();
316 }
317
shadowPseudoId() const318 const AtomicString& MediaControlOverlayPlayButtonElement::shadowPseudoId() const
319 {
320 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button", AtomicString::ConstructFromLiteral));
321 return id;
322 }
323
keepEventInNode(Event * event)324 bool MediaControlOverlayPlayButtonElement::keepEventInNode(Event* event)
325 {
326 return isUserInteractionEvent(event);
327 }
328
329
330 // ----------------------------
331
MediaControlToggleClosedCaptionsButtonElement(MediaControls & mediaControls)332 MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(MediaControls& mediaControls)
333 : MediaControlInputElement(mediaControls, MediaShowClosedCaptionsButton)
334 {
335 }
336
create(MediaControls & mediaControls)337 PassRefPtrWillBeRawPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(MediaControls& mediaControls)
338 {
339 RefPtrWillBeRawPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRefWillBeNoop(new MediaControlToggleClosedCaptionsButtonElement(mediaControls));
340 button->ensureUserAgentShadowRoot();
341 button->setType("button");
342 button->hide();
343 return button.release();
344 }
345
updateDisplayType()346 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
347 {
348 bool captionsVisible = mediaElement().closedCaptionsVisible();
349 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
350 setChecked(captionsVisible);
351 }
352
defaultEventHandler(Event * event)353 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
354 {
355 if (event->type() == EventTypeNames::click) {
356 mediaElement().setClosedCaptionsVisible(!mediaElement().closedCaptionsVisible());
357 setChecked(mediaElement().closedCaptionsVisible());
358 updateDisplayType();
359 event->setDefaultHandled();
360 }
361
362 HTMLInputElement::defaultEventHandler(event);
363 }
364
shadowPseudoId() const365 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
366 {
367 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral));
368 return id;
369 }
370
371 // ----------------------------
372
MediaControlTimelineElement(MediaControls & mediaControls)373 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaControls)
374 : MediaControlInputElement(mediaControls, MediaSlider)
375 {
376 }
377
create(MediaControls & mediaControls)378 PassRefPtrWillBeRawPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(MediaControls& mediaControls)
379 {
380 RefPtrWillBeRawPtr<MediaControlTimelineElement> timeline = adoptRefWillBeNoop(new MediaControlTimelineElement(mediaControls));
381 timeline->ensureUserAgentShadowRoot();
382 timeline->setType("range");
383 timeline->setAttribute(stepAttr, "any");
384 return timeline.release();
385 }
386
defaultEventHandler(Event * event)387 void MediaControlTimelineElement::defaultEventHandler(Event* event)
388 {
389 if (event->isMouseEvent() && toMouseEvent(event)->button() != LeftButton)
390 return;
391
392 if (!inDocument() || !document().isActive())
393 return;
394
395 if (event->type() == EventTypeNames::mousedown)
396 mediaControls().beginScrubbing();
397
398 if (event->type() == EventTypeNames::mouseup)
399 mediaControls().endScrubbing();
400
401 MediaControlInputElement::defaultEventHandler(event);
402
403 if (event->type() == EventTypeNames::mouseover || event->type() == EventTypeNames::mouseout || event->type() == EventTypeNames::mousemove)
404 return;
405
406 double time = value().toDouble();
407 if (event->type() == EventTypeNames::input) {
408 // FIXME: This will need to take the timeline offset into consideration
409 // once that concept is supported, see https://crbug.com/312699
410 if (mediaElement().controller())
411 mediaElement().controller()->setCurrentTime(time, IGNORE_EXCEPTION);
412 else
413 mediaElement().setCurrentTime(time, IGNORE_EXCEPTION);
414 }
415
416 RenderSlider* slider = toRenderSlider(renderer());
417 if (slider && slider->inDragMode())
418 mediaControls().updateCurrentTimeDisplay();
419 }
420
willRespondToMouseClickEvents()421 bool MediaControlTimelineElement::willRespondToMouseClickEvents()
422 {
423 return inDocument() && document().isActive();
424 }
425
setPosition(double currentTime)426 void MediaControlTimelineElement::setPosition(double currentTime)
427 {
428 setValue(String::number(currentTime));
429 }
430
setDuration(double duration)431 void MediaControlTimelineElement::setDuration(double duration)
432 {
433 setFloatingPointAttribute(maxAttr, std::isfinite(duration) ? duration : 0);
434 }
435
436
shadowPseudoId() const437 const AtomicString& MediaControlTimelineElement::shadowPseudoId() const
438 {
439 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline", AtomicString::ConstructFromLiteral));
440 return id;
441 }
442
keepEventInNode(Event * event)443 bool MediaControlTimelineElement::keepEventInNode(Event* event)
444 {
445 return isUserInteractionEventForSlider(event);
446 }
447
448 // ----------------------------
449
MediaControlVolumeSliderElement(MediaControls & mediaControls)450 MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(MediaControls& mediaControls)
451 : MediaControlInputElement(mediaControls, MediaVolumeSlider)
452 {
453 }
454
create(MediaControls & mediaControls)455 PassRefPtrWillBeRawPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(MediaControls& mediaControls)
456 {
457 RefPtrWillBeRawPtr<MediaControlVolumeSliderElement> slider = adoptRefWillBeNoop(new MediaControlVolumeSliderElement(mediaControls));
458 slider->ensureUserAgentShadowRoot();
459 slider->setType("range");
460 slider->setAttribute(stepAttr, "any");
461 slider->setAttribute(maxAttr, "1");
462 return slider.release();
463 }
464
defaultEventHandler(Event * event)465 void MediaControlVolumeSliderElement::defaultEventHandler(Event* event)
466 {
467 if (event->isMouseEvent() && toMouseEvent(event)->button() != LeftButton)
468 return;
469
470 if (!inDocument() || !document().isActive())
471 return;
472
473 MediaControlInputElement::defaultEventHandler(event);
474
475 if (event->type() == EventTypeNames::mouseover || event->type() == EventTypeNames::mouseout || event->type() == EventTypeNames::mousemove)
476 return;
477
478 double volume = value().toDouble();
479 mediaElement().setVolume(volume, ASSERT_NO_EXCEPTION);
480 mediaElement().setMuted(false);
481 }
482
willRespondToMouseMoveEvents()483 bool MediaControlVolumeSliderElement::willRespondToMouseMoveEvents()
484 {
485 if (!inDocument() || !document().isActive())
486 return false;
487
488 return MediaControlInputElement::willRespondToMouseMoveEvents();
489 }
490
willRespondToMouseClickEvents()491 bool MediaControlVolumeSliderElement::willRespondToMouseClickEvents()
492 {
493 if (!inDocument() || !document().isActive())
494 return false;
495
496 return MediaControlInputElement::willRespondToMouseClickEvents();
497 }
498
setVolume(double volume)499 void MediaControlVolumeSliderElement::setVolume(double volume)
500 {
501 if (value().toDouble() != volume)
502 setValue(String::number(volume));
503 }
504
shadowPseudoId() const505 const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const
506 {
507 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider", AtomicString::ConstructFromLiteral));
508 return id;
509 }
510
keepEventInNode(Event * event)511 bool MediaControlVolumeSliderElement::keepEventInNode(Event* event)
512 {
513 return isUserInteractionEventForSlider(event);
514 }
515
516 // ----------------------------
517
MediaControlFullscreenButtonElement(MediaControls & mediaControls)518 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(MediaControls& mediaControls)
519 : MediaControlInputElement(mediaControls, MediaEnterFullscreenButton)
520 {
521 }
522
create(MediaControls & mediaControls)523 PassRefPtrWillBeRawPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(MediaControls& mediaControls)
524 {
525 RefPtrWillBeRawPtr<MediaControlFullscreenButtonElement> button = adoptRefWillBeNoop(new MediaControlFullscreenButtonElement(mediaControls));
526 button->ensureUserAgentShadowRoot();
527 button->setType("button");
528 button->hide();
529 return button.release();
530 }
531
defaultEventHandler(Event * event)532 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
533 {
534 if (event->type() == EventTypeNames::click) {
535 if (FullscreenElementStack::isActiveFullScreenElement(&mediaElement()))
536 FullscreenElementStack::from(document()).webkitCancelFullScreen();
537 else
538 FullscreenElementStack::from(document()).requestFullScreenForElement(&mediaElement(), 0, FullscreenElementStack::ExemptIFrameAllowFullScreenRequirement);
539 event->setDefaultHandled();
540 }
541 HTMLInputElement::defaultEventHandler(event);
542 }
543
shadowPseudoId() const544 const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const
545 {
546 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button", AtomicString::ConstructFromLiteral));
547 return id;
548 }
549
setIsFullscreen(bool isFullscreen)550 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen)
551 {
552 setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton);
553 }
554
555 // ----------------------------
556
MediaControlTimeRemainingDisplayElement(MediaControls & mediaControls)557 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(MediaControls& mediaControls)
558 : MediaControlTimeDisplayElement(mediaControls, MediaTimeRemainingDisplay)
559 {
560 }
561
create(MediaControls & mediaControls)562 PassRefPtrWillBeRawPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(MediaControls& mediaControls)
563 {
564 return adoptRefWillBeNoop(new MediaControlTimeRemainingDisplayElement(mediaControls));
565 }
566
getMediaControlTimeRemainingDisplayElementShadowPseudoId()567 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId()
568 {
569 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display", AtomicString::ConstructFromLiteral));
570 return id;
571 }
572
shadowPseudoId() const573 const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const
574 {
575 return getMediaControlTimeRemainingDisplayElementShadowPseudoId();
576 }
577
578 // ----------------------------
579
MediaControlCurrentTimeDisplayElement(MediaControls & mediaControls)580 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(MediaControls& mediaControls)
581 : MediaControlTimeDisplayElement(mediaControls, MediaCurrentTimeDisplay)
582 {
583 }
584
create(MediaControls & mediaControls)585 PassRefPtrWillBeRawPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(MediaControls& mediaControls)
586 {
587 return adoptRefWillBeNoop(new MediaControlCurrentTimeDisplayElement(mediaControls));
588 }
589
getMediaControlCurrentTimeDisplayElementShadowPseudoId()590 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId()
591 {
592 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display", AtomicString::ConstructFromLiteral));
593 return id;
594 }
595
shadowPseudoId() const596 const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const
597 {
598 return getMediaControlCurrentTimeDisplayElementShadowPseudoId();
599 }
600
601 // ----------------------------
602
MediaControlTextTrackContainerElement(MediaControls & mediaControls)603 MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(MediaControls& mediaControls)
604 : MediaControlDivElement(mediaControls, MediaTextTrackDisplayContainer)
605 , m_fontSize(0)
606 {
607 }
608
create(MediaControls & mediaControls)609 PassRefPtrWillBeRawPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(MediaControls& mediaControls)
610 {
611 RefPtrWillBeRawPtr<MediaControlTextTrackContainerElement> element = adoptRefWillBeNoop(new MediaControlTextTrackContainerElement(mediaControls));
612 element->hide();
613 return element.release();
614 }
615
createRenderer(RenderStyle *)616 RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderStyle*)
617 {
618 return new RenderTextTrackContainerElement(this);
619 }
620
textTrackContainerElementShadowPseudoId()621 const AtomicString& MediaControlTextTrackContainerElement::textTrackContainerElementShadowPseudoId()
622 {
623 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container", AtomicString::ConstructFromLiteral));
624 return id;
625 }
626
shadowPseudoId() const627 const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const
628 {
629 return textTrackContainerElementShadowPseudoId();
630 }
631
updateDisplay()632 void MediaControlTextTrackContainerElement::updateDisplay()
633 {
634 if (!mediaElement().closedCaptionsVisible()) {
635 removeChildren();
636 return;
637 }
638
639 // 1. If the media element is an audio element, or is another playback
640 // mechanism with no rendering area, abort these steps. There is nothing to
641 // render.
642 if (isHTMLAudioElement(mediaElement()))
643 return;
644
645 // 2. Let video be the media element or other playback mechanism.
646 HTMLVideoElement& video = toHTMLVideoElement(mediaElement());
647
648 // 3. Let output be an empty list of absolutely positioned CSS block boxes.
649
650 // 4. If the user agent is exposing a user interface for video, add to
651 // output one or more completely transparent positioned CSS block boxes that
652 // cover the same region as the user interface.
653
654 // 5. If the last time these rules were run, the user agent was not exposing
655 // a user interface for video, but now it is, let reset be true. Otherwise,
656 // let reset be false.
657
658 // There is nothing to be done explicitly for 4th and 5th steps, as
659 // everything is handled through CSS. The caption box is on top of the
660 // controls box, in a container set with the -webkit-box display property.
661
662 // 6. Let tracks be the subset of video's list of text tracks that have as
663 // their rules for updating the text track rendering these rules for
664 // updating the display of WebVTT text tracks, and whose text track mode is
665 // showing or showing by default.
666 // 7. Let cues be an empty list of text track cues.
667 // 8. For each track track in tracks, append to cues all the cues from
668 // track's list of cues that have their text track cue active flag set.
669 CueList activeCues = video.currentlyActiveCues();
670
671 // 9. If reset is false, then, for each text track cue cue in cues: if cue's
672 // text track cue display state has a set of CSS boxes, then add those boxes
673 // to output, and remove cue from cues.
674
675 // There is nothing explicitly to be done here, as all the caching occurs
676 // within the TextTrackCue instance itself. If parameters of the cue change,
677 // the display tree is cleared.
678
679 // 10. For each text track cue cue in cues that has not yet had
680 // corresponding CSS boxes added to output, in text track cue order, run the
681 // following substeps:
682 for (size_t i = 0; i < activeCues.size(); ++i) {
683 TextTrackCue* cue = activeCues[i].data();
684
685 ASSERT(cue->isActive());
686 if (!cue->track() || !cue->track()->isRendered() || !cue->isActive())
687 continue;
688
689 cue->updateDisplay(m_videoDisplaySize.size(), *this);
690 }
691
692 // 11. Return output.
693 if (hasChildren())
694 show();
695 else
696 hide();
697 }
698
updateSizes()699 void MediaControlTextTrackContainerElement::updateSizes()
700 {
701 if (!document().isActive())
702 return;
703
704 IntRect videoBox;
705
706 if (!mediaElement().renderer() || !mediaElement().renderer()->isVideo())
707 return;
708 videoBox = toRenderVideo(mediaElement().renderer())->videoBox();
709
710 if (m_videoDisplaySize == videoBox)
711 return;
712 m_videoDisplaySize = videoBox;
713
714 float smallestDimension = std::min(m_videoDisplaySize.size().height(), m_videoDisplaySize.size().width());
715
716 float fontSize = smallestDimension * 0.05f;
717 if (fontSize != m_fontSize) {
718 m_fontSize = fontSize;
719 setInlineStyleProperty(CSSPropertyFontSize, fontSize, CSSPrimitiveValue::CSS_PX);
720 }
721 }
722
723 // ----------------------------
724
725 } // namespace WebCore
726