| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. | 2 * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. |
| 3 * Copyright (C) 2012 Google Inc. All rights reserved. | 3 * Copyright (C) 2012 Google Inc. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
| 7 * are met: | 7 * are met: |
| 8 * | 8 * |
| 9 * 1. Redistributions of source code must retain the above copyright | 9 * 1. Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 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 | 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. | 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 */ | 28 */ |
| 29 | 29 |
| 30 #include "core/html/shadow/MediaControlElements.h" | 30 #include "core/html/shadow/MediaControlElements.h" |
| 31 | 31 |
| 32 #include "bindings/core/v8/ExceptionStatePlaceholder.h" | 32 #include "bindings/core/v8/ExceptionStatePlaceholder.h" |
| 33 #include "core/InputTypeNames.h" | 33 #include "core/InputTypeNames.h" |
| 34 #include "core/dom/ClientRect.h" | 34 #include "core/dom/ClientRect.h" |
| 35 #include "core/dom/Text.h" |
| 35 #include "core/dom/shadow/ShadowRoot.h" | 36 #include "core/dom/shadow/ShadowRoot.h" |
| 36 #include "core/events/MouseEvent.h" | 37 #include "core/events/MouseEvent.h" |
| 37 #include "core/frame/LocalFrame.h" | 38 #include "core/frame/LocalFrame.h" |
| 39 #include "core/html/HTMLLabelElement.h" |
| 38 #include "core/html/HTMLMediaSource.h" | 40 #include "core/html/HTMLMediaSource.h" |
| 41 #include "core/html/HTMLSpanElement.h" |
| 39 #include "core/html/HTMLVideoElement.h" | 42 #include "core/html/HTMLVideoElement.h" |
| 40 #include "core/html/TimeRanges.h" | 43 #include "core/html/TimeRanges.h" |
| 41 #include "core/html/shadow/MediaControls.h" | 44 #include "core/html/shadow/MediaControls.h" |
| 45 #include "core/html/track/TextTrackList.h" |
| 42 #include "core/input/EventHandler.h" | 46 #include "core/input/EventHandler.h" |
| 43 #include "core/layout/LayoutTheme.h" | 47 #include "core/layout/LayoutTheme.h" |
| 44 #include "core/layout/LayoutVideo.h" | 48 #include "core/layout/LayoutVideo.h" |
| 45 #include "core/layout/api/LayoutSliderItem.h" | 49 #include "core/layout/api/LayoutSliderItem.h" |
| 50 #include "platform/EventDispatchForbiddenScope.h" |
| 46 #include "platform/Histogram.h" | 51 #include "platform/Histogram.h" |
| 47 #include "platform/RuntimeEnabledFeatures.h" | 52 #include "platform/RuntimeEnabledFeatures.h" |
| 53 #include "platform/text/PlatformLocale.h" |
| 48 #include "public/platform/Platform.h" | 54 #include "public/platform/Platform.h" |
| 49 #include "public/platform/UserMetricsAction.h" | 55 #include "public/platform/UserMetricsAction.h" |
| 50 | 56 |
| 51 namespace blink { | 57 namespace blink { |
| 52 | 58 |
| 53 using namespace HTMLNames; | 59 using namespace HTMLNames; |
| 54 | 60 |
| 55 namespace { | 61 namespace { |
| 56 | 62 |
| 57 // This is the duration from mediaControls.css | 63 // This is the duration from mediaControls.css |
| 58 const double fadeOutDuration = 0.3; | 64 const double fadeOutDuration = 0.3; |
| 59 | 65 |
| 66 const QualifiedName& trackIndexAttrName() |
| 67 { |
| 68 // Save the track index in an attribute to avoid holding a pointer to the te
xt track. |
| 69 DEFINE_STATIC_LOCAL(QualifiedName, trackIndexAttr, (nullAtom, "data-track-in
dex", nullAtom)); |
| 70 return trackIndexAttr; |
| 71 } |
| 72 |
| 73 // When specified as trackIndex, disable text tracks. |
| 74 const int trackIndexOffValue = -1; |
| 75 |
| 60 bool isUserInteractionEvent(Event* event) | 76 bool isUserInteractionEvent(Event* event) |
| 61 { | 77 { |
| 62 const AtomicString& type = event->type(); | 78 const AtomicString& type = event->type(); |
| 63 return type == EventTypeNames::mousedown | 79 return type == EventTypeNames::mousedown |
| 64 || type == EventTypeNames::mouseup | 80 || type == EventTypeNames::mouseup |
| 65 || type == EventTypeNames::click | 81 || type == EventTypeNames::click |
| 66 || type == EventTypeNames::dblclick | 82 || type == EventTypeNames::dblclick |
| 67 || event->isKeyboardEvent() | 83 || event->isKeyboardEvent() |
| 68 || event->isTouchEvent(); | 84 || event->isTouchEvent(); |
| 69 } | 85 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 89 | 105 |
| 90 Element* elementFromCenter(Element& element) | 106 Element* elementFromCenter(Element& element) |
| 91 { | 107 { |
| 92 ClientRect* clientRect = element.getBoundingClientRect(); | 108 ClientRect* clientRect = element.getBoundingClientRect(); |
| 93 int centerX = static_cast<int>((clientRect->left() + clientRect->right()) /
2); | 109 int centerX = static_cast<int>((clientRect->left() + clientRect->right()) /
2); |
| 94 int centerY = static_cast<int>((clientRect->top() + clientRect->bottom()) /
2); | 110 int centerY = static_cast<int>((clientRect->top() + clientRect->bottom()) /
2); |
| 95 | 111 |
| 96 return element.document().elementFromPoint(centerX , centerY); | 112 return element.document().elementFromPoint(centerX , centerY); |
| 97 } | 113 } |
| 98 | 114 |
| 115 bool hasDuplicateLabel(TextTrack* currentTrack) |
| 116 { |
| 117 DCHECK(currentTrack); |
| 118 TextTrackList* trackList = currentTrack->trackList(); |
| 119 // The runtime of this method is quadratic but since there are usually very
few text tracks it won't |
| 120 // affect the performance much. |
| 121 String currentTrackLabel = currentTrack->label(); |
| 122 for (unsigned i = 0; i < trackList->length(); i++) { |
| 123 TextTrack* track = trackList->anonymousIndexedGetter(i); |
| 124 if (currentTrack != track && currentTrackLabel == track->label()) |
| 125 return true; |
| 126 } |
| 127 return false; |
| 128 } |
| 129 |
| 99 } // anonymous namespace | 130 } // anonymous namespace |
| 100 | 131 |
| 101 MediaControlPanelElement::MediaControlPanelElement(MediaControls& mediaControls) | 132 MediaControlPanelElement::MediaControlPanelElement(MediaControls& mediaControls) |
| 102 : MediaControlDivElement(mediaControls, MediaControlsPanel) | 133 : MediaControlDivElement(mediaControls, MediaControlsPanel) |
| 103 , m_isDisplayed(false) | 134 , m_isDisplayed(false) |
| 104 , m_opaque(true) | 135 , m_opaque(true) |
| 105 , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired) | 136 , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired) |
| 106 { | 137 { |
| 107 } | 138 } |
| 108 | 139 |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 364 MediaControlToggleClosedCaptionsButtonElement* button = new MediaControlTogg
leClosedCaptionsButtonElement(mediaControls); | 395 MediaControlToggleClosedCaptionsButtonElement* button = new MediaControlTogg
leClosedCaptionsButtonElement(mediaControls); |
| 365 button->ensureUserAgentShadowRoot(); | 396 button->ensureUserAgentShadowRoot(); |
| 366 button->setType(InputTypeNames::button); | 397 button->setType(InputTypeNames::button); |
| 367 button->setShadowPseudoId(AtomicString("-webkit-media-controls-toggle-closed
-captions-button")); | 398 button->setShadowPseudoId(AtomicString("-webkit-media-controls-toggle-closed
-captions-button")); |
| 368 button->setIsWanted(false); | 399 button->setIsWanted(false); |
| 369 return button; | 400 return button; |
| 370 } | 401 } |
| 371 | 402 |
| 372 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() | 403 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() |
| 373 { | 404 { |
| 374 bool captionsVisible = mediaElement().closedCaptionsVisible(); | 405 bool captionsVisible = mediaElement().textTracksVisible(); |
| 375 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowCl
osedCaptionsButton); | 406 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowCl
osedCaptionsButton); |
| 376 setChecked(captionsVisible); | |
| 377 } | 407 } |
| 378 | 408 |
| 379 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* e
vent) | 409 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* e
vent) |
| 380 { | 410 { |
| 381 if (event->type() == EventTypeNames::click) { | 411 if (event->type() == EventTypeNames::click) { |
| 382 if (mediaElement().closedCaptionsVisible()) | 412 mediaControls().toggleTextTrackList(); |
| 383 Platform::current()->recordAction(UserMetricsAction("Media.Controls.
ClosedCaptionHide")); | |
| 384 else | |
| 385 Platform::current()->recordAction(UserMetricsAction("Media.Controls.
ClosedCaptionShow")); | |
| 386 mediaElement().setClosedCaptionsVisible(!mediaElement().closedCaptionsVi
sible()); | |
| 387 setChecked(mediaElement().closedCaptionsVisible()); | |
| 388 updateDisplayType(); | 413 updateDisplayType(); |
| 389 event->setDefaultHandled(); | 414 event->setDefaultHandled(); |
| 390 } | 415 } |
| 391 | 416 |
| 392 HTMLInputElement::defaultEventHandler(event); | 417 HTMLInputElement::defaultEventHandler(event); |
| 393 } | 418 } |
| 394 | 419 |
| 395 // ---------------------------- | 420 // ---------------------------- |
| 396 | 421 |
| 422 MediaControlTextTrackListElement::MediaControlTextTrackListElement(MediaControls
& mediaControls) |
| 423 : MediaControlDivElement(mediaControls, MediaTextTrackList) |
| 424 { |
| 425 } |
| 426 |
| 427 MediaControlTextTrackListElement* MediaControlTextTrackListElement::create(Media
Controls& mediaControls) |
| 428 { |
| 429 MediaControlTextTrackListElement* element = new MediaControlTextTrackListEle
ment(mediaControls); |
| 430 element->setShadowPseudoId(AtomicString("-internal-media-controls-text-track
-list")); |
| 431 element->setIsWanted(false); |
| 432 return element; |
| 433 } |
| 434 |
| 435 void MediaControlTextTrackListElement::defaultEventHandler(Event* event) |
| 436 { |
| 437 if (event->type() == EventTypeNames::change) { |
| 438 // Identify which input element was selected and set track to showing |
| 439 Node* target = event->target()->toNode(); |
| 440 if (!target || !target->isElementNode()) |
| 441 return; |
| 442 |
| 443 disableShowingTextTracks(); |
| 444 int trackIndex = toElement(target)->getIntegralAttribute(trackIndexAttrN
ame()); |
| 445 if (trackIndex != trackIndexOffValue) { |
| 446 ASSERT(trackIndex >= 0); |
| 447 showTextTrackAtIndex(trackIndex); |
| 448 mediaElement().disableAutomaticTextTrackSelection(); |
| 449 } |
| 450 |
| 451 mediaControls().toggleTextTrackList(); |
| 452 event->setDefaultHandled(); |
| 453 } |
| 454 MediaControlDivElement::defaultEventHandler(event); |
| 455 } |
| 456 |
| 457 void MediaControlTextTrackListElement::setVisible(bool visible) |
| 458 { |
| 459 if (visible) { |
| 460 setIsWanted(true); |
| 461 refreshTextTrackListMenu(); |
| 462 } else { |
| 463 setIsWanted(false); |
| 464 } |
| 465 } |
| 466 |
| 467 void MediaControlTextTrackListElement::showTextTrackAtIndex(unsigned indexToEnab
le) |
| 468 { |
| 469 TextTrackList* trackList = mediaElement().textTracks(); |
| 470 if (indexToEnable >= trackList->length()) |
| 471 return; |
| 472 TextTrack* track = trackList->anonymousIndexedGetter(indexToEnable); |
| 473 if (track && track->canBeRendered()) |
| 474 track->setMode(TextTrack::showingKeyword()); |
| 475 } |
| 476 |
| 477 void MediaControlTextTrackListElement::disableShowingTextTracks() |
| 478 { |
| 479 TextTrackList* trackList = mediaElement().textTracks(); |
| 480 for (unsigned i = 0; i < trackList->length(); ++i) { |
| 481 TextTrack* track = trackList->anonymousIndexedGetter(i); |
| 482 if (track->mode() == TextTrack::showingKeyword()) |
| 483 track->setMode(TextTrack::disabledKeyword()); |
| 484 } |
| 485 } |
| 486 |
| 487 String MediaControlTextTrackListElement::getTextTrackLabel(TextTrack* track) |
| 488 { |
| 489 if (!track) |
| 490 return mediaElement().locale().queryString(WebLocalizedString::TextTrack
sOff); |
| 491 |
| 492 String trackLabel = track->label(); |
| 493 |
| 494 if (trackLabel.isEmpty()) |
| 495 trackLabel = String(mediaElement().locale().queryString(WebLocalizedStri
ng::TextTracksNoLabel)); |
| 496 |
| 497 return trackLabel; |
| 498 } |
| 499 |
| 500 // TextTrack parameter when passed in as a nullptr, creates the "Off" list item
in the track list. |
| 501 Element* MediaControlTextTrackListElement::createTextTrackListItem(TextTrack* tr
ack) |
| 502 { |
| 503 int trackIndex = track ? track->trackIndex() : trackIndexOffValue; |
| 504 HTMLLabelElement* trackItem = HTMLLabelElement::create(document(), nullptr); |
| 505 trackItem->setShadowPseudoId(AtomicString("-internal-media-controls-text-tra
ck-list-item")); |
| 506 HTMLInputElement* trackItemInput = HTMLInputElement::create(document(), null
ptr, false); |
| 507 trackItemInput->setShadowPseudoId(AtomicString("-internal-media-controls-tex
t-track-list-item-input")); |
| 508 trackItemInput->setType(InputTypeNames::checkbox); |
| 509 trackItemInput->setIntegralAttribute(trackIndexAttrName(), trackIndex); |
| 510 if (!mediaElement().textTracksVisible()) { |
| 511 if (!track) |
| 512 trackItemInput->setChecked(true); |
| 513 } else { |
| 514 // If there are multiple text tracks set to showing, they must all have |
| 515 // checkmarks displayed. |
| 516 if (track && track->mode() == TextTrack::showingKeyword()) |
| 517 trackItemInput->setChecked(true); |
| 518 } |
| 519 |
| 520 trackItem->appendChild(trackItemInput); |
| 521 String trackLabel = getTextTrackLabel(track); |
| 522 trackItem->appendChild(Text::create(document(), trackLabel)); |
| 523 // Add a track kind marker icon if there are multiple tracks with the same l
abel or if the track has no label. |
| 524 if (track && (track->label().isEmpty() || hasDuplicateLabel(track))) { |
| 525 HTMLSpanElement* trackKindMarker = HTMLSpanElement::create(document()); |
| 526 if (track->kind() == track->captionsKeyword()) { |
| 527 trackKindMarker->setShadowPseudoId(AtomicString("-internal-media-con
trols-text-track-list-kind-captions")); |
| 528 } else { |
| 529 ASSERT(track->kind() == track->subtitlesKeyword()); |
| 530 trackKindMarker->setShadowPseudoId(AtomicString("-internal-media-con
trols-text-track-list-kind-subtitles")); |
| 531 } |
| 532 trackItem->appendChild(trackKindMarker); |
| 533 } |
| 534 return trackItem; |
| 535 } |
| 536 |
| 537 void MediaControlTextTrackListElement::refreshTextTrackListMenu() |
| 538 { |
| 539 if (!mediaElement().hasClosedCaptions() || !mediaElement().textTracksAreRead
y()) |
| 540 return; |
| 541 |
| 542 EventDispatchForbiddenScope::AllowUserAgentEvents allowEvents; |
| 543 removeChildren(OmitSubtreeModifiedEvent); |
| 544 |
| 545 // Construct a menu for subtitles and captions |
| 546 // Pass in a nullptr to createTextTrackListItem to create the "Off" track it
em. |
| 547 appendChild(createTextTrackListItem(nullptr)); |
| 548 |
| 549 TextTrackList* trackList = mediaElement().textTracks(); |
| 550 for (unsigned i = 0; i < trackList->length(); i++) { |
| 551 TextTrack* track = trackList->anonymousIndexedGetter(i); |
| 552 if (!track->canBeRendered()) |
| 553 continue; |
| 554 appendChild(createTextTrackListItem(track)); |
| 555 } |
| 556 } |
| 557 |
| 558 // ---------------------------- |
| 559 |
| 397 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaCon
trols) | 560 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaCon
trols) |
| 398 : MediaControlInputElement(mediaControls, MediaSlider) | 561 : MediaControlInputElement(mediaControls, MediaSlider) |
| 399 { | 562 { |
| 400 } | 563 } |
| 401 | 564 |
| 402 MediaControlTimelineElement* MediaControlTimelineElement::create(MediaControls&
mediaControls) | 565 MediaControlTimelineElement* MediaControlTimelineElement::create(MediaControls&
mediaControls) |
| 403 { | 566 { |
| 404 MediaControlTimelineElement* timeline = new MediaControlTimelineElement(medi
aControls); | 567 MediaControlTimelineElement* timeline = new MediaControlTimelineElement(medi
aControls); |
| 405 timeline->ensureUserAgentShadowRoot(); | 568 timeline->ensureUserAgentShadowRoot(); |
| 406 timeline->setType(InputTypeNames::range); | 569 timeline->setType(InputTypeNames::range); |
| (...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 691 } | 854 } |
| 692 | 855 |
| 693 MediaControlCurrentTimeDisplayElement* MediaControlCurrentTimeDisplayElement::cr
eate(MediaControls& mediaControls) | 856 MediaControlCurrentTimeDisplayElement* MediaControlCurrentTimeDisplayElement::cr
eate(MediaControls& mediaControls) |
| 694 { | 857 { |
| 695 MediaControlCurrentTimeDisplayElement* element = new MediaControlCurrentTime
DisplayElement(mediaControls); | 858 MediaControlCurrentTimeDisplayElement* element = new MediaControlCurrentTime
DisplayElement(mediaControls); |
| 696 element->setShadowPseudoId(AtomicString("-webkit-media-controls-current-time
-display")); | 859 element->setShadowPseudoId(AtomicString("-webkit-media-controls-current-time
-display")); |
| 697 return element; | 860 return element; |
| 698 } | 861 } |
| 699 | 862 |
| 700 } // namespace blink | 863 } // namespace blink |
| OLD | NEW |