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