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