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 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. 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 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 96 PassRefPtrWillBeRawPtr<MediaControlPanelElement> MediaControlPanelElement::create(MediaControls& mediaControls) 97 { 98 return adoptRefWillBeNoop(new MediaControlPanelElement(mediaControls)); 99 } 100 101 const AtomicString& MediaControlPanelElement::shadowPseudoId() const 102 { 103 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel", AtomicString::ConstructFromLiteral)); 104 return id; 105 } 106 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 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 129 void MediaControlPanelElement::stopTimer() 130 { 131 if (m_transitionTimer.isActive()) 132 m_transitionTimer.stop(); 133 } 134 135 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*) 136 { 137 if (!m_opaque) 138 hide(); 139 140 stopTimer(); 141 } 142 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 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 166 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed) 167 { 168 m_isDisplayed = isDisplayed; 169 } 170 171 bool MediaControlPanelElement::keepEventInNode(Event* event) 172 { 173 return isUserInteractionEvent(event); 174 } 175 176 // ---------------------------- 177 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 184 PassRefPtrWillBeRawPtr<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(MediaControls& mediaControls) 185 { 186 return adoptRefWillBeNoop(new MediaControlPanelEnclosureElement(mediaControls)); 187 } 188 189 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 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 203 PassRefPtrWillBeRawPtr<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(MediaControls& mediaControls) 204 { 205 return adoptRefWillBeNoop(new MediaControlOverlayEnclosureElement(mediaControls)); 206 } 207 208 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 216 MediaControlMuteButtonElement::MediaControlMuteButtonElement(MediaControls& mediaControls) 217 : MediaControlInputElement(mediaControls, MediaMuteButton) 218 { 219 } 220 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 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 239 void MediaControlMuteButtonElement::updateDisplayType() 240 { 241 setDisplayType(mediaElement().muted() ? MediaUnMuteButton : MediaMuteButton); 242 } 243 244 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 252 MediaControlPlayButtonElement::MediaControlPlayButtonElement(MediaControls& mediaControls) 253 : MediaControlInputElement(mediaControls, MediaPlayButton) 254 { 255 } 256 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 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 275 void MediaControlPlayButtonElement::updateDisplayType() 276 { 277 setDisplayType(mediaElement().togglePlayStateWillPlay() ? MediaPlayButton : MediaPauseButton); 278 } 279 280 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 288 MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(MediaControls& mediaControls) 289 : MediaControlInputElement(mediaControls, MediaOverlayPlayButton) 290 { 291 } 292 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 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 310 void MediaControlOverlayPlayButtonElement::updateDisplayType() 311 { 312 if (mediaElement().togglePlayStateWillPlay()) { 313 show(); 314 } else 315 hide(); 316 } 317 318 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 324 bool MediaControlOverlayPlayButtonElement::keepEventInNode(Event* event) 325 { 326 return isUserInteractionEvent(event); 327 } 328 329 330 // ---------------------------- 331 332 MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(MediaControls& mediaControls) 333 : MediaControlInputElement(mediaControls, MediaShowClosedCaptionsButton) 334 { 335 } 336 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 346 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() 347 { 348 bool captionsVisible = mediaElement().closedCaptionsVisible(); 349 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton); 350 setChecked(captionsVisible); 351 } 352 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 365 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 373 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaControls) 374 : MediaControlInputElement(mediaControls, MediaSlider) 375 { 376 } 377 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 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 421 bool MediaControlTimelineElement::willRespondToMouseClickEvents() 422 { 423 return inDocument() && document().isActive(); 424 } 425 426 void MediaControlTimelineElement::setPosition(double currentTime) 427 { 428 setValue(String::number(currentTime)); 429 } 430 431 void MediaControlTimelineElement::setDuration(double duration) 432 { 433 setFloatingPointAttribute(maxAttr, std::isfinite(duration) ? duration : 0); 434 } 435 436 437 const AtomicString& MediaControlTimelineElement::shadowPseudoId() const 438 { 439 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline", AtomicString::ConstructFromLiteral)); 440 return id; 441 } 442 443 bool MediaControlTimelineElement::keepEventInNode(Event* event) 444 { 445 return isUserInteractionEventForSlider(event); 446 } 447 448 // ---------------------------- 449 450 MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(MediaControls& mediaControls) 451 : MediaControlInputElement(mediaControls, MediaVolumeSlider) 452 { 453 } 454 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 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 483 bool MediaControlVolumeSliderElement::willRespondToMouseMoveEvents() 484 { 485 if (!inDocument() || !document().isActive()) 486 return false; 487 488 return MediaControlInputElement::willRespondToMouseMoveEvents(); 489 } 490 491 bool MediaControlVolumeSliderElement::willRespondToMouseClickEvents() 492 { 493 if (!inDocument() || !document().isActive()) 494 return false; 495 496 return MediaControlInputElement::willRespondToMouseClickEvents(); 497 } 498 499 void MediaControlVolumeSliderElement::setVolume(double volume) 500 { 501 if (value().toDouble() != volume) 502 setValue(String::number(volume)); 503 } 504 505 const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const 506 { 507 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider", AtomicString::ConstructFromLiteral)); 508 return id; 509 } 510 511 bool MediaControlVolumeSliderElement::keepEventInNode(Event* event) 512 { 513 return isUserInteractionEventForSlider(event); 514 } 515 516 // ---------------------------- 517 518 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(MediaControls& mediaControls) 519 : MediaControlInputElement(mediaControls, MediaEnterFullscreenButton) 520 { 521 } 522 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 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 544 const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const 545 { 546 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button", AtomicString::ConstructFromLiteral)); 547 return id; 548 } 549 550 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen) 551 { 552 setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton); 553 } 554 555 // ---------------------------- 556 557 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(MediaControls& mediaControls) 558 : MediaControlTimeDisplayElement(mediaControls, MediaTimeRemainingDisplay) 559 { 560 } 561 562 PassRefPtrWillBeRawPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(MediaControls& mediaControls) 563 { 564 return adoptRefWillBeNoop(new MediaControlTimeRemainingDisplayElement(mediaControls)); 565 } 566 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 573 const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const 574 { 575 return getMediaControlTimeRemainingDisplayElementShadowPseudoId(); 576 } 577 578 // ---------------------------- 579 580 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(MediaControls& mediaControls) 581 : MediaControlTimeDisplayElement(mediaControls, MediaCurrentTimeDisplay) 582 { 583 } 584 585 PassRefPtrWillBeRawPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(MediaControls& mediaControls) 586 { 587 return adoptRefWillBeNoop(new MediaControlCurrentTimeDisplayElement(mediaControls)); 588 } 589 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 596 const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const 597 { 598 return getMediaControlCurrentTimeDisplayElementShadowPseudoId(); 599 } 600 601 // ---------------------------- 602 603 MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(MediaControls& mediaControls) 604 : MediaControlDivElement(mediaControls, MediaTextTrackDisplayContainer) 605 , m_fontSize(0) 606 { 607 } 608 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 616 RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderStyle*) 617 { 618 return new RenderTextTrackContainerElement(this); 619 } 620 621 const AtomicString& MediaControlTextTrackContainerElement::textTrackContainerElementShadowPseudoId() 622 { 623 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container", AtomicString::ConstructFromLiteral)); 624 return id; 625 } 626 627 const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const 628 { 629 return textTrackContainerElementShadowPseudoId(); 630 } 631 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 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