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/EventNames.h" 35 #include "core/dom/FullscreenElementStack.h" 36 #include "core/dom/MouseEvent.h" 37 #include "core/html/DOMTokenList.h" 38 #include "core/html/HTMLVideoElement.h" 39 #include "core/html/shadow/MediaControls.h" 40 #include "core/html/track/TextTrack.h" 41 #include "core/html/track/TextTrackRegionList.h" 42 #include "core/page/EventHandler.h" 43 #include "core/page/Frame.h" 44 #include "core/page/Page.h" 45 #include "core/page/Settings.h" 46 #include "core/rendering/RenderMediaControlElements.h" 47 #include "core/rendering/RenderSlider.h" 48 #include "core/rendering/RenderTheme.h" 49 #include "core/rendering/RenderVideo.h" 50 51 namespace WebCore { 52 53 using namespace HTMLNames; 54 55 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId(); 56 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId(); 57 58 static const char* textTracksOffAttrValue = "-1"; // This must match HTMLMediaElement::textTracksOffIndex() 59 60 MediaControlPanelElement::MediaControlPanelElement(Document* document) 61 : MediaControlDivElement(document, MediaControlsPanel) 62 , m_canBeDragged(false) 63 , m_isBeingDragged(false) 64 , m_isDisplayed(false) 65 , m_opaque(true) 66 , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired) 67 { 68 } 69 70 PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(Document* document) 71 { 72 return adoptRef(new MediaControlPanelElement(document)); 73 } 74 75 const AtomicString& MediaControlPanelElement::part() const 76 { 77 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel", AtomicString::ConstructFromLiteral)); 78 return id; 79 } 80 81 void MediaControlPanelElement::startDrag(const LayoutPoint& eventLocation) 82 { 83 if (!m_canBeDragged) 84 return; 85 86 if (m_isBeingDragged) 87 return; 88 89 RenderObject* renderer = this->renderer(); 90 if (!renderer || !renderer->isBox()) 91 return; 92 93 Frame* frame = document()->frame(); 94 if (!frame) 95 return; 96 97 m_lastDragEventLocation = eventLocation; 98 99 frame->eventHandler()->setCapturingMouseEventsNode(this); 100 101 m_isBeingDragged = true; 102 } 103 104 void MediaControlPanelElement::continueDrag(const LayoutPoint& eventLocation) 105 { 106 if (!m_isBeingDragged) 107 return; 108 109 LayoutSize distanceDragged = eventLocation - m_lastDragEventLocation; 110 m_cumulativeDragOffset.move(distanceDragged); 111 m_lastDragEventLocation = eventLocation; 112 setPosition(m_cumulativeDragOffset); 113 } 114 115 void MediaControlPanelElement::endDrag() 116 { 117 if (!m_isBeingDragged) 118 return; 119 120 m_isBeingDragged = false; 121 122 Frame* frame = document()->frame(); 123 if (!frame) 124 return; 125 126 frame->eventHandler()->setCapturingMouseEventsNode(0); 127 } 128 129 void MediaControlPanelElement::startTimer() 130 { 131 stopTimer(); 132 133 // The timer is required to set the property display:'none' on the panel, 134 // such that captions are correctly displayed at the bottom of the video 135 // at the end of the fadeout transition. 136 double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0; 137 m_transitionTimer.startOneShot(duration); 138 } 139 140 void MediaControlPanelElement::stopTimer() 141 { 142 if (m_transitionTimer.isActive()) 143 m_transitionTimer.stop(); 144 } 145 146 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*) 147 { 148 if (!m_opaque) 149 hide(); 150 151 stopTimer(); 152 } 153 154 void MediaControlPanelElement::setPosition(const LayoutPoint& position) 155 { 156 double left = position.x(); 157 double top = position.y(); 158 159 // Set the left and top to control the panel's position; this depends on it being absolute positioned. 160 // Set the margin to zero since the position passed in will already include the effect of the margin. 161 setInlineStyleProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX); 162 setInlineStyleProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX); 163 setInlineStyleProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX); 164 setInlineStyleProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX); 165 166 classList()->add("dragged", IGNORE_EXCEPTION); 167 } 168 169 void MediaControlPanelElement::resetPosition() 170 { 171 removeInlineStyleProperty(CSSPropertyLeft); 172 removeInlineStyleProperty(CSSPropertyTop); 173 removeInlineStyleProperty(CSSPropertyMarginLeft); 174 removeInlineStyleProperty(CSSPropertyMarginTop); 175 176 classList()->remove("dragged", IGNORE_EXCEPTION); 177 178 m_cumulativeDragOffset.setX(0); 179 m_cumulativeDragOffset.setY(0); 180 } 181 182 void MediaControlPanelElement::makeOpaque() 183 { 184 if (m_opaque) 185 return; 186 187 double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeInDuration() : 0; 188 189 setInlineStyleProperty(CSSPropertyTransitionProperty, CSSPropertyOpacity); 190 setInlineStyleProperty(CSSPropertyTransitionDuration, duration, CSSPrimitiveValue::CSS_S); 191 setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER); 192 193 m_opaque = true; 194 195 if (m_isDisplayed) 196 show(); 197 } 198 199 void MediaControlPanelElement::makeTransparent() 200 { 201 if (!m_opaque) 202 return; 203 204 double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0; 205 206 setInlineStyleProperty(CSSPropertyTransitionProperty, CSSPropertyOpacity); 207 setInlineStyleProperty(CSSPropertyTransitionDuration, duration, CSSPrimitiveValue::CSS_S); 208 setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER); 209 210 m_opaque = false; 211 startTimer(); 212 } 213 214 void MediaControlPanelElement::defaultEventHandler(Event* event) 215 { 216 MediaControlDivElement::defaultEventHandler(event); 217 218 if (event->isMouseEvent()) { 219 LayoutPoint location = toMouseEvent(event)->absoluteLocation(); 220 if (event->type() == eventNames().mousedownEvent && event->target() == this) { 221 startDrag(location); 222 event->setDefaultHandled(); 223 } else if (event->type() == eventNames().mousemoveEvent && m_isBeingDragged) 224 continueDrag(location); 225 else if (event->type() == eventNames().mouseupEvent && m_isBeingDragged) { 226 continueDrag(location); 227 endDrag(); 228 event->setDefaultHandled(); 229 } 230 } 231 } 232 233 void MediaControlPanelElement::setCanBeDragged(bool canBeDragged) 234 { 235 if (m_canBeDragged == canBeDragged) 236 return; 237 238 m_canBeDragged = canBeDragged; 239 240 if (!canBeDragged) 241 endDrag(); 242 } 243 244 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed) 245 { 246 m_isDisplayed = isDisplayed; 247 } 248 249 // ---------------------------- 250 251 MediaControlPanelEnclosureElement::MediaControlPanelEnclosureElement(Document* document) 252 // Mapping onto same MediaControlElementType as panel element, since it has similar properties. 253 : MediaControlDivElement(document, MediaControlsPanel) 254 { 255 } 256 257 PassRefPtr<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(Document* document) 258 { 259 return adoptRef(new MediaControlPanelEnclosureElement(document)); 260 } 261 262 const AtomicString& MediaControlPanelEnclosureElement::part() const 263 { 264 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-enclosure", AtomicString::ConstructFromLiteral)); 265 return id; 266 } 267 268 // ---------------------------- 269 270 MediaControlOverlayEnclosureElement::MediaControlOverlayEnclosureElement(Document* document) 271 // Mapping onto same MediaControlElementType as panel element, since it has similar properties. 272 : MediaControlDivElement(document, MediaControlsPanel) 273 { 274 } 275 276 PassRefPtr<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(Document* document) 277 { 278 return adoptRef(new MediaControlOverlayEnclosureElement(document)); 279 } 280 281 const AtomicString& MediaControlOverlayEnclosureElement::part() const 282 { 283 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-enclosure", AtomicString::ConstructFromLiteral)); 284 return id; 285 } 286 287 // ---------------------------- 288 289 MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document* document, MediaControls* controls) 290 : MediaControlMuteButtonElement(document, MediaMuteButton) 291 , m_controls(controls) 292 { 293 } 294 295 PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document* document, MediaControls* controls) 296 { 297 ASSERT(controls); 298 299 RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(document, controls)); 300 button->ensureUserAgentShadowRoot(); 301 button->setType("button"); 302 return button.release(); 303 } 304 305 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event) 306 { 307 if (event->type() == eventNames().mouseoverEvent) 308 m_controls->showVolumeSlider(); 309 310 MediaControlMuteButtonElement::defaultEventHandler(event); 311 } 312 313 const AtomicString& MediaControlPanelMuteButtonElement::part() const 314 { 315 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button", AtomicString::ConstructFromLiteral)); 316 return id; 317 } 318 319 // ---------------------------- 320 321 MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document* document) 322 : MediaControlMuteButtonElement(document, MediaMuteButton) 323 { 324 } 325 326 PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document* document) 327 { 328 RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(document)); 329 button->ensureUserAgentShadowRoot(); 330 button->setType("button"); 331 return button.release(); 332 } 333 334 const AtomicString& MediaControlVolumeSliderMuteButtonElement::part() const 335 { 336 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button", AtomicString::ConstructFromLiteral)); 337 return id; 338 } 339 340 // ---------------------------- 341 342 MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* document) 343 : MediaControlInputElement(document, MediaPlayButton) 344 { 345 } 346 347 PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document* document) 348 { 349 RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(document)); 350 button->ensureUserAgentShadowRoot(); 351 button->setType("button"); 352 return button.release(); 353 } 354 355 void MediaControlPlayButtonElement::defaultEventHandler(Event* event) 356 { 357 if (event->type() == eventNames().clickEvent) { 358 if (mediaController()->canPlay()) 359 mediaController()->play(); 360 else 361 mediaController()->pause(); 362 updateDisplayType(); 363 event->setDefaultHandled(); 364 } 365 HTMLInputElement::defaultEventHandler(event); 366 } 367 368 void MediaControlPlayButtonElement::updateDisplayType() 369 { 370 setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton); 371 } 372 373 const AtomicString& MediaControlPlayButtonElement::part() const 374 { 375 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button", AtomicString::ConstructFromLiteral)); 376 return id; 377 } 378 379 // ---------------------------- 380 381 MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document* document) 382 : MediaControlInputElement(document, MediaOverlayPlayButton) 383 { 384 } 385 386 PassRefPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(Document* document) 387 { 388 RefPtr<MediaControlOverlayPlayButtonElement> button = adoptRef(new MediaControlOverlayPlayButtonElement(document)); 389 button->ensureUserAgentShadowRoot(); 390 button->setType("button"); 391 return button.release(); 392 } 393 394 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event) 395 { 396 if (event->type() == eventNames().clickEvent && mediaController()->canPlay()) { 397 mediaController()->play(); 398 updateDisplayType(); 399 event->setDefaultHandled(); 400 } 401 HTMLInputElement::defaultEventHandler(event); 402 } 403 404 void MediaControlOverlayPlayButtonElement::updateDisplayType() 405 { 406 if (mediaController()->canPlay()) { 407 show(); 408 } else 409 hide(); 410 } 411 412 const AtomicString& MediaControlOverlayPlayButtonElement::part() const 413 { 414 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button", AtomicString::ConstructFromLiteral)); 415 return id; 416 } 417 418 419 // ---------------------------- 420 421 MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* document, MediaControls* controls) 422 : MediaControlInputElement(document, MediaShowClosedCaptionsButton) 423 { 424 UNUSED_PARAM(controls); 425 } 426 427 PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document* document, MediaControls* controls) 428 { 429 ASSERT(controls); 430 431 RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document, controls)); 432 button->ensureUserAgentShadowRoot(); 433 button->setType("button"); 434 button->hide(); 435 return button.release(); 436 } 437 438 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() 439 { 440 bool captionsVisible = mediaController()->closedCaptionsVisible(); 441 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton); 442 setChecked(captionsVisible); 443 } 444 445 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event) 446 { 447 if (event->type() == eventNames().clickEvent) { 448 mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible()); 449 setChecked(mediaController()->closedCaptionsVisible()); 450 updateDisplayType(); 451 event->setDefaultHandled(); 452 } 453 454 HTMLInputElement::defaultEventHandler(event); 455 } 456 457 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::part() const 458 { 459 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral)); 460 return id; 461 } 462 463 // ---------------------------- 464 465 MediaControlTimelineElement::MediaControlTimelineElement(Document* document, MediaControls* controls) 466 : MediaControlInputElement(document, MediaSlider) 467 , m_controls(controls) 468 { 469 } 470 471 PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(Document* document, MediaControls* controls) 472 { 473 ASSERT(controls); 474 475 RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(document, controls)); 476 timeline->ensureUserAgentShadowRoot(); 477 timeline->setType("range"); 478 timeline->setAttribute(precisionAttr, "float"); 479 return timeline.release(); 480 } 481 482 void MediaControlTimelineElement::defaultEventHandler(Event* event) 483 { 484 // Left button is 0. Rejects mouse events not from left button. 485 if (event->isMouseEvent() && toMouseEvent(event)->button()) 486 return; 487 488 if (!attached()) 489 return; 490 491 if (event->type() == eventNames().mousedownEvent) 492 mediaController()->beginScrubbing(); 493 494 if (event->type() == eventNames().mouseupEvent) 495 mediaController()->endScrubbing(); 496 497 MediaControlInputElement::defaultEventHandler(event); 498 499 if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent) 500 return; 501 502 double time = value().toDouble(); 503 if (event->type() == eventNames().inputEvent && time != mediaController()->currentTime()) 504 mediaController()->setCurrentTime(time, IGNORE_EXCEPTION); 505 506 RenderSlider* slider = toRenderSlider(renderer()); 507 if (slider && slider->inDragMode()) 508 m_controls->updateCurrentTimeDisplay(); 509 } 510 511 bool MediaControlTimelineElement::willRespondToMouseClickEvents() 512 { 513 if (!attached()) 514 return false; 515 516 return true; 517 } 518 519 void MediaControlTimelineElement::setPosition(double currentTime) 520 { 521 setValue(String::number(currentTime)); 522 } 523 524 void MediaControlTimelineElement::setDuration(double duration) 525 { 526 setAttribute(maxAttr, String::number(std::isfinite(duration) ? duration : 0)); 527 } 528 529 530 const AtomicString& MediaControlTimelineElement::part() const 531 { 532 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline", AtomicString::ConstructFromLiteral)); 533 return id; 534 } 535 536 // ---------------------------- 537 538 MediaControlPanelVolumeSliderElement::MediaControlPanelVolumeSliderElement(Document* document) 539 : MediaControlVolumeSliderElement(document) 540 { 541 } 542 543 PassRefPtr<MediaControlPanelVolumeSliderElement> MediaControlPanelVolumeSliderElement::create(Document* document) 544 { 545 RefPtr<MediaControlPanelVolumeSliderElement> slider = adoptRef(new MediaControlPanelVolumeSliderElement(document)); 546 slider->ensureUserAgentShadowRoot(); 547 slider->setType("range"); 548 slider->setAttribute(precisionAttr, "float"); 549 slider->setAttribute(maxAttr, "1"); 550 return slider.release(); 551 } 552 553 const AtomicString& MediaControlPanelVolumeSliderElement::part() const 554 { 555 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider", AtomicString::ConstructFromLiteral)); 556 return id; 557 } 558 559 // ---------------------------- 560 561 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* document) 562 : MediaControlInputElement(document, MediaEnterFullscreenButton) 563 { 564 } 565 566 PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(Document* document) 567 { 568 RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(document)); 569 button->ensureUserAgentShadowRoot(); 570 button->setType("button"); 571 button->hide(); 572 return button.release(); 573 } 574 575 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event) 576 { 577 if (event->type() == eventNames().clickEvent) { 578 // Only use the new full screen API if the fullScreenEnabled setting has 579 // been explicitly enabled. Otherwise, use the old fullscreen API. This 580 // allows apps which embed a WebView to retain the existing full screen 581 // video implementation without requiring them to implement their own full 582 // screen behavior. 583 if (document()->settings() && document()->settings()->fullScreenEnabled()) { 584 if (FullscreenElementStack::isActiveFullScreenElement(toParentMediaElement(this))) 585 FullscreenElementStack::from(document())->webkitCancelFullScreen(); 586 else 587 FullscreenElementStack::from(document())->requestFullScreenForElement(toParentMediaElement(this), 0, FullscreenElementStack::ExemptIFrameAllowFullScreenRequirement); 588 } else 589 mediaController()->enterFullscreen(); 590 event->setDefaultHandled(); 591 } 592 HTMLInputElement::defaultEventHandler(event); 593 } 594 595 const AtomicString& MediaControlFullscreenButtonElement::part() const 596 { 597 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button", AtomicString::ConstructFromLiteral)); 598 return id; 599 } 600 601 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen) 602 { 603 setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton); 604 } 605 606 // ---------------------------- 607 608 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document* document) 609 : MediaControlTimeDisplayElement(document, MediaTimeRemainingDisplay) 610 { 611 } 612 613 PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document* document) 614 { 615 return adoptRef(new MediaControlTimeRemainingDisplayElement(document)); 616 } 617 618 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId() 619 { 620 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display", AtomicString::ConstructFromLiteral)); 621 return id; 622 } 623 624 const AtomicString& MediaControlTimeRemainingDisplayElement::part() const 625 { 626 return getMediaControlTimeRemainingDisplayElementShadowPseudoId(); 627 } 628 629 // ---------------------------- 630 631 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document* document) 632 : MediaControlTimeDisplayElement(document, MediaCurrentTimeDisplay) 633 { 634 } 635 636 PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document* document) 637 { 638 return adoptRef(new MediaControlCurrentTimeDisplayElement(document)); 639 } 640 641 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId() 642 { 643 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display", AtomicString::ConstructFromLiteral)); 644 return id; 645 } 646 647 const AtomicString& MediaControlCurrentTimeDisplayElement::part() const 648 { 649 return getMediaControlCurrentTimeDisplayElementShadowPseudoId(); 650 } 651 652 // ---------------------------- 653 654 MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document* document) 655 : MediaControlDivElement(document, MediaTextTrackDisplayContainer) 656 , m_fontSize(0) 657 { 658 } 659 660 PassRefPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document* document) 661 { 662 RefPtr<MediaControlTextTrackContainerElement> element = adoptRef(new MediaControlTextTrackContainerElement(document)); 663 element->hide(); 664 return element.release(); 665 } 666 667 RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderStyle*) 668 { 669 return new RenderTextTrackContainerElement(this); 670 } 671 672 const AtomicString& MediaControlTextTrackContainerElement::textTrackContainerElementShadowPseudoId() 673 { 674 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container", AtomicString::ConstructFromLiteral)); 675 return id; 676 } 677 678 const AtomicString& MediaControlTextTrackContainerElement::part() const 679 { 680 return textTrackContainerElementShadowPseudoId(); 681 } 682 683 void MediaControlTextTrackContainerElement::updateDisplay() 684 { 685 if (!mediaController()->closedCaptionsVisible()) { 686 removeChildren(); 687 return; 688 } 689 690 HTMLMediaElement* mediaElement = toParentMediaElement(this); 691 // 1. If the media element is an audio element, or is another playback 692 // mechanism with no rendering area, abort these steps. There is nothing to 693 // render. 694 if (!mediaElement || !mediaElement->isVideo()) 695 return; 696 697 // 2. Let video be the media element or other playback mechanism. 698 HTMLVideoElement* video = toHTMLVideoElement(mediaElement); 699 700 // 3. Let output be an empty list of absolutely positioned CSS block boxes. 701 Vector<RefPtr<HTMLDivElement> > output; 702 703 // 4. If the user agent is exposing a user interface for video, add to 704 // output one or more completely transparent positioned CSS block boxes that 705 // cover the same region as the user interface. 706 707 // 5. If the last time these rules were run, the user agent was not exposing 708 // a user interface for video, but now it is, let reset be true. Otherwise, 709 // let reset be false. 710 711 // There is nothing to be done explicitly for 4th and 5th steps, as 712 // everything is handled through CSS. The caption box is on top of the 713 // controls box, in a container set with the -webkit-box display property. 714 715 // 6. Let tracks be the subset of video's list of text tracks that have as 716 // their rules for updating the text track rendering these rules for 717 // updating the display of WebVTT text tracks, and whose text track mode is 718 // showing or showing by default. 719 // 7. Let cues be an empty list of text track cues. 720 // 8. For each track track in tracks, append to cues all the cues from 721 // track's list of cues that have their text track cue active flag set. 722 CueList activeCues = video->currentlyActiveCues(); 723 724 // 9. If reset is false, then, for each text track cue cue in cues: if cue's 725 // text track cue display state has a set of CSS boxes, then add those boxes 726 // to output, and remove cue from cues. 727 728 // There is nothing explicitly to be done here, as all the caching occurs 729 // within the TextTrackCue instance itself. If parameters of the cue change, 730 // the display tree is cleared. 731 732 // 10. For each text track cue cue in cues that has not yet had 733 // corresponding CSS boxes added to output, in text track cue order, run the 734 // following substeps: 735 for (size_t i = 0; i < activeCues.size(); ++i) { 736 TextTrackCue* cue = activeCues[i].data(); 737 738 ASSERT(cue->isActive()); 739 if (!cue->track() || !cue->track()->isRendered() || !cue->isActive()) 740 continue; 741 742 RefPtr<TextTrackCueBox> displayBox = cue->getDisplayTree(m_videoDisplaySize.size()); 743 744 #if ENABLE(WEBVTT_REGIONS) 745 String regionId = cue->regionId(); 746 TextTrackRegion* region = cue->track()->regions()->getRegionById(regionId); 747 if (!region) { 748 // If cue has an empty text track cue region identifier or there is no 749 // WebVTT region whose region identifier is identical to cue's text 750 // track cue region identifier, run the following substeps: 751 #endif 752 if (displayBox->hasChildNodes() && !contains(displayBox.get())) { 753 // Note: the display tree of a cue is removed when the active flag of the cue is unset. 754 appendChild(displayBox, ASSERT_NO_EXCEPTION, AttachNow); 755 } 756 #if ENABLE(WEBVTT_REGIONS) 757 } else { 758 // Let region be the WebVTT region whose region identifier 759 // matches the text track cue region identifier of cue. 760 RefPtr<HTMLDivElement> regionNode = region->getDisplayTree(); 761 762 // Append the region to the viewport, if it was not already. 763 if (!contains(regionNode.get())) 764 appendChild(region->getDisplayTree()); 765 766 region->appendTextTrackCueBox(displayBox); 767 } 768 #endif 769 } 770 771 // 11. Return output. 772 if (hasChildNodes()) 773 show(); 774 else 775 hide(); 776 } 777 778 void MediaControlTextTrackContainerElement::updateSizes(bool forceUpdate) 779 { 780 HTMLMediaElement* mediaElement = toParentMediaElement(this); 781 if (!mediaElement) 782 return; 783 784 if (!document()->page()) 785 return; 786 787 IntRect videoBox; 788 789 if (!mediaElement->renderer() || !mediaElement->renderer()->isVideo()) 790 return; 791 videoBox = toRenderVideo(mediaElement->renderer())->videoBox(); 792 793 if (!forceUpdate && m_videoDisplaySize == videoBox) 794 return; 795 m_videoDisplaySize = videoBox; 796 797 float smallestDimension = std::min(m_videoDisplaySize.size().height(), m_videoDisplaySize.size().width()); 798 799 float fontSize = smallestDimension * 0.05f; 800 if (fontSize != m_fontSize) { 801 m_fontSize = fontSize; 802 setInlineStyleProperty(CSSPropertyFontSize, fontSize, CSSPrimitiveValue::CSS_PX); 803 } 804 805 CueList activeCues = mediaElement->currentlyActiveCues(); 806 for (size_t i = 0; i < activeCues.size(); ++i) { 807 TextTrackCue* cue = activeCues[i].data(); 808 cue->videoSizeDidChange(m_videoDisplaySize.size()); 809 } 810 } 811 812 // ---------------------------- 813 814 } // namespace WebCore 815