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 294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
364 MediaControlToggleClosedCaptionsButtonElement* button = new MediaControlTogg leClosedCaptionsButtonElement(mediaControls); | 380 MediaControlToggleClosedCaptionsButtonElement* button = new MediaControlTogg leClosedCaptionsButtonElement(mediaControls); |
365 button->ensureUserAgentShadowRoot(); | 381 button->ensureUserAgentShadowRoot(); |
366 button->setType(InputTypeNames::button); | 382 button->setType(InputTypeNames::button); |
367 button->setShadowPseudoId(AtomicString("-webkit-media-controls-toggle-closed -captions-button")); | 383 button->setShadowPseudoId(AtomicString("-webkit-media-controls-toggle-closed -captions-button")); |
368 button->setIsWanted(false); | 384 button->setIsWanted(false); |
369 return button; | 385 return button; |
370 } | 386 } |
371 | 387 |
372 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() | 388 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() |
373 { | 389 { |
374 bool captionsVisible = mediaElement().closedCaptionsVisible(); | 390 bool captionsVisible = mediaElement().textTracksVisible(); |
375 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowCl osedCaptionsButton); | 391 setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowCl osedCaptionsButton); |
376 setChecked(captionsVisible); | |
377 } | 392 } |
378 | 393 |
379 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* e vent) | 394 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* e vent) |
380 { | 395 { |
381 if (event->type() == EventTypeNames::click) { | 396 if (event->type() == EventTypeNames::click) { |
382 if (mediaElement().closedCaptionsVisible()) | 397 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(); | 398 updateDisplayType(); |
389 event->setDefaultHandled(); | 399 event->setDefaultHandled(); |
390 } | 400 } |
391 | 401 |
392 HTMLInputElement::defaultEventHandler(event); | 402 HTMLInputElement::defaultEventHandler(event); |
393 } | 403 } |
394 | 404 |
395 // ---------------------------- | 405 // ---------------------------- |
396 | 406 |
407 MediaControlTextTrackListElement::MediaControlTextTrackListElement(MediaControls & mediaControls) | |
408 : MediaControlDivElement(mediaControls, MediaTextTrackList) | |
409 { | |
410 } | |
411 | |
412 MediaControlTextTrackListElement* MediaControlTextTrackListElement::create(Media Controls& mediaControls) | |
413 { | |
414 MediaControlTextTrackListElement* element = new MediaControlTextTrackListEle ment(mediaControls); | |
415 element->setShadowPseudoId(AtomicString("-internal-media-controls-text-track -list")); | |
416 element->setIsWanted(false); | |
417 return element; | |
418 } | |
419 | |
420 void MediaControlTextTrackListElement::defaultEventHandler(Event* event) | |
421 { | |
422 if (event->type() == EventTypeNames::change) { | |
423 // Identify which input element was selected and set track to showing | |
424 Node* target = event->target()->toNode(); | |
425 if (!target || !target->isElementNode()) | |
426 return; | |
427 | |
428 disableShowingTextTracks(); | |
429 int trackIndex = toElement(target)->getIntegralAttribute(trackIndexAttrN ame()); | |
430 if (trackIndex != trackIndexOffValue) { | |
431 ASSERT(trackIndex >= 0); | |
432 showTextTrackAtIndex(trackIndex); | |
433 mediaElement().disableAutomaticTextTrackSelection(); | |
434 } | |
435 | |
436 mediaControls().toggleTextTrackList(); | |
437 event->setDefaultHandled(); | |
438 } | |
439 MediaControlDivElement::defaultEventHandler(event); | |
440 } | |
441 | |
442 void MediaControlTextTrackListElement::setVisible(bool visible) | |
443 { | |
444 if (visible) { | |
445 setIsWanted(true); | |
446 refreshTextTrackListMenu(); | |
447 } else { | |
448 setIsWanted(false); | |
449 } | |
450 } | |
451 | |
452 void MediaControlTextTrackListElement::showTextTrackAtIndex(unsigned indexToEnab le) | |
453 { | |
454 TextTrackList* trackList = mediaElement().textTracks(); | |
455 if (indexToEnable >= trackList->length()) | |
456 return; | |
457 TextTrack* track = trackList->anonymousIndexedGetter(indexToEnable); | |
458 if (track && track->canBeRendered()) | |
459 track->setMode(TextTrack::showingKeyword()); | |
460 } | |
461 | |
462 void MediaControlTextTrackListElement::disableShowingTextTracks() | |
463 { | |
464 TextTrackList* trackList = mediaElement().textTracks(); | |
465 for (unsigned i = 0; i < trackList->length(); ++i) { | |
466 TextTrack* track = trackList->anonymousIndexedGetter(i); | |
467 if (track->mode() == TextTrack::showingKeyword()) | |
468 track->setMode(TextTrack::disabledKeyword()); | |
469 } | |
470 } | |
471 | |
472 String MediaControlTextTrackListElement::getTextTrackLabel(TextTrack* track) | |
473 { | |
474 if (!track) | |
475 return mediaElement().locale().queryString(WebLocalizedString::TextTrack sOff); | |
476 | |
477 String trackLabel = track->label(); | |
478 | |
479 if (trackLabel.isEmpty()) | |
480 trackLabel = String(mediaElement().locale().queryString(WebLocalizedStri ng::TextTracksNoLabel)); | |
481 | |
482 return trackLabel; | |
483 } | |
484 | |
485 bool MediaControlTextTrackListElement::hasDuplicateLabel(const TextTrack& curren tTrack) | |
486 { | |
487 TextTrackList* trackList = currentTrack.trackList(); | |
488 // The runtime of this method is quadratic but since there are very few text tracks it won't | |
489 // affect the performance much. | |
490 for (unsigned i = 0; i < trackList->length(); i++) { | |
491 TextTrack* track = trackList->anonymousIndexedGetter(i); | |
philipj_slow
2016/04/20 15:27:22
Oh, now I see why TextTrack::trackList() wasn't co
| |
492 if (¤tTrack != track && currentTrack.label() == track->label()) | |
philipj_slow
2016/04/20 15:27:22
Can you extract currentTrack.label() into a variab
| |
493 return true; | |
494 } | |
495 return false; | |
496 } | |
497 | |
498 // TextTrack parameter when passed in as a nullptr, creates the "Off" list item in the track list. | |
499 Element* MediaControlTextTrackListElement::createTextTrackListItem(TextTrack* tr ack) | |
500 { | |
501 int trackIndex = track ? track->trackIndex() : trackIndexOffValue; | |
502 HTMLLabelElement* trackItem = HTMLLabelElement::create(document(), nullptr); | |
503 trackItem->setShadowPseudoId(AtomicString("-internal-media-controls-text-tra ck-list-item")); | |
504 HTMLInputElement* trackItemInput = HTMLInputElement::create(document(), null ptr, false); | |
505 trackItemInput->setShadowPseudoId(AtomicString("-internal-media-controls-tex t-track-list-item-input")); | |
506 trackItemInput->setType(InputTypeNames::checkbox); | |
507 trackItemInput->setIntegralAttribute(trackIndexAttrName(), trackIndex); | |
508 if (!mediaElement().textTracksVisible()) { | |
509 if (!track) | |
510 trackItemInput->setChecked(true); | |
511 } else { | |
512 // If there are multiple text tracks set to showing, they must all have | |
513 // checkmarks displayed. | |
514 if (track && track->mode() == TextTrack::showingKeyword()) | |
515 trackItemInput->setChecked(true); | |
516 } | |
517 | |
518 trackItem->appendChild(trackItemInput); | |
519 String trackLabel = getTextTrackLabel(track); | |
520 trackItem->appendChild(Text::create(document(), trackLabel)); | |
521 // Add a track kind marker icon if there are multiple tracks with the same l abel. | |
522 if (track && (hasDuplicateLabel(*track) || track->label().isEmpty())) { | |
philipj_slow
2016/04/20 15:27:22
Put the isEmpty() check first to avoid the loop in
| |
523 HTMLSpanElement* trackKindMarker = HTMLSpanElement::create(document()); | |
524 if (track->kind() == track->captionsKeyword()) { | |
525 trackKindMarker->setShadowPseudoId(AtomicString("-internal-media-con trols-text-track-list-kind-captions")); | |
526 } else { | |
527 ASSERT(track->kind() == track->subtitlesKeyword()); | |
528 trackKindMarker->setShadowPseudoId(AtomicString("-internal-media-con trols-text-track-list-kind-subtitles")); | |
529 } | |
530 trackItem->appendChild(trackKindMarker); | |
531 } | |
532 return trackItem; | |
533 } | |
534 | |
535 void MediaControlTextTrackListElement::refreshTextTrackListMenu() | |
536 { | |
537 if (!mediaElement().hasClosedCaptions() || !mediaElement().textTracksAreRead y()) | |
538 return; | |
539 | |
540 EventDispatchForbiddenScope::AllowUserAgentEvents allowEvents; | |
541 removeChildren(OmitSubtreeModifiedEvent); | |
542 | |
543 // Construct a menu for subtitles and captions | |
544 // Pass in a nullptr to createTextTrackListItem to create the "Off" track it em. | |
545 appendChild(createTextTrackListItem(nullptr)); | |
546 | |
547 TextTrackList* trackList = mediaElement().textTracks(); | |
548 for (unsigned i = 0; i < trackList->length(); i++) { | |
549 TextTrack* track = trackList->anonymousIndexedGetter(i); | |
550 if (!track->canBeRendered()) | |
551 continue; | |
552 appendChild(createTextTrackListItem(track)); | |
553 } | |
554 } | |
555 | |
556 // ---------------------------- | |
557 | |
397 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaCon trols) | 558 MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaCon trols) |
398 : MediaControlInputElement(mediaControls, MediaSlider) | 559 : MediaControlInputElement(mediaControls, MediaSlider) |
399 { | 560 { |
400 } | 561 } |
401 | 562 |
402 MediaControlTimelineElement* MediaControlTimelineElement::create(MediaControls& mediaControls) | 563 MediaControlTimelineElement* MediaControlTimelineElement::create(MediaControls& mediaControls) |
403 { | 564 { |
404 MediaControlTimelineElement* timeline = new MediaControlTimelineElement(medi aControls); | 565 MediaControlTimelineElement* timeline = new MediaControlTimelineElement(medi aControls); |
405 timeline->ensureUserAgentShadowRoot(); | 566 timeline->ensureUserAgentShadowRoot(); |
406 timeline->setType(InputTypeNames::range); | 567 timeline->setType(InputTypeNames::range); |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
691 } | 852 } |
692 | 853 |
693 MediaControlCurrentTimeDisplayElement* MediaControlCurrentTimeDisplayElement::cr eate(MediaControls& mediaControls) | 854 MediaControlCurrentTimeDisplayElement* MediaControlCurrentTimeDisplayElement::cr eate(MediaControls& mediaControls) |
694 { | 855 { |
695 MediaControlCurrentTimeDisplayElement* element = new MediaControlCurrentTime DisplayElement(mediaControls); | 856 MediaControlCurrentTimeDisplayElement* element = new MediaControlCurrentTime DisplayElement(mediaControls); |
696 element->setShadowPseudoId(AtomicString("-webkit-media-controls-current-time -display")); | 857 element->setShadowPseudoId(AtomicString("-webkit-media-controls-current-time -display")); |
697 return element; | 858 return element; |
698 } | 859 } |
699 | 860 |
700 } // namespace blink | 861 } // namespace blink |
OLD | NEW |