Support text track selection in video controls
Enable the user to select between text tracks with a menu that is displayed when the CC button is clicked. BUG=353105,495851 TEST=run-webkit-tests media* Review URL: https://codereview.chromium.org/1079323002 Cr-Commit-Position: refs/heads/master@{#389201}
This commit is contained in:
content
third_party/WebKit
LayoutTests
media
media-controls.js
track
cue-style-invalidation.htmltext-track-selection-menu-add-track-expected.txttext-track-selection-menu-add-track.htmltext-track-selection-menu-multiple-tracks-expected.txttext-track-selection-menu-multiple-tracks.html
video-controls-captions-expected.txtvideo-controls-captions-load-by-lang-expected.txtvideo-controls-captions-load-by-lang.htmlvideo-controls-captions-on-off-expected.txtvideo-controls-captions-on-off.htmlvideo-controls-captions.htmlvideo-controls-track-selection-menu-expected.txtvideo-controls-track-selection-menu.htmlSource
core
css
html
HTMLMediaElement.cppHTMLMediaElement.h
shadow
MediaControlElementTypes.hMediaControlElements.cppMediaControlElements.hMediaControls.cppMediaControls.h
track
paint
platform
public
@ -856,6 +856,14 @@ below:
|
||||
Please lengthen this text to <ph name="MIN_CHARACTERS">$2<ex>101</ex></ph> characters or more (you are currently using <ph name="CURRENT_LENGTH">$1<ex>100</ex></ph> characters).
|
||||
</message>
|
||||
|
||||
<message name="IDS_MEDIA_TRACKS_NO_LABEL" desc="Menu item label for a text track that has no name specified">
|
||||
Unknown
|
||||
</message>
|
||||
|
||||
<message name="IDS_MEDIA_TRACKS_OFF" desc="Menu item label for a text track that represents disabling closed captions">
|
||||
Off
|
||||
</message>
|
||||
|
||||
<message name="IDS_PLUGIN_INITIALIZATION_ERROR" desc="A message displayed when a plugin failed to load">
|
||||
Couldn't load plugin.
|
||||
</message>
|
||||
|
@ -337,6 +337,10 @@ static int ToMessageID(WebLocalizedString::Name name) {
|
||||
return IDS_FORM_INPUT_WEEK_TEMPLATE;
|
||||
case WebLocalizedString::WeekNumberLabel:
|
||||
return IDS_FORM_WEEK_NUMBER_LABEL;
|
||||
case WebLocalizedString::TextTracksNoLabel:
|
||||
return IDS_MEDIA_TRACKS_NO_LABEL;
|
||||
case WebLocalizedString::TextTracksOff:
|
||||
return IDS_MEDIA_TRACKS_OFF;
|
||||
// This "default:" line exists to avoid compile warnings about enum
|
||||
// coverage when we add a new symbol to WebLocalizedString.h in WebKit.
|
||||
// After a planned WebKit patch is landed, we need to add a case statement
|
||||
@ -673,6 +677,24 @@ const DataResource kDataResources[] = {
|
||||
{"mediaplayerOverlayPlayNew",
|
||||
IDR_MEDIAPLAYER_OVERLAY_PLAY_BUTTON_NEW,
|
||||
ui::SCALE_FACTOR_100P},
|
||||
{"mediaplayerTrackSelectionCheckmark",
|
||||
IDR_MEDIAPLAYER_TRACKSELECTION_CHECKMARK,
|
||||
ui::SCALE_FACTOR_100P},
|
||||
{"mediaplayerTrackSelectionCheckmarkNew",
|
||||
IDR_MEDIAPLAYER_TRACKSELECTION_CHECKMARK_NEW,
|
||||
ui::SCALE_FACTOR_100P},
|
||||
{"mediaplayerClosedCaptionsIcon",
|
||||
IDR_MEDIAPLAYER_CLOSEDCAPTIONS_ICON,
|
||||
ui::SCALE_FACTOR_100P},
|
||||
{"mediaplayerClosedCaptionsIconNew",
|
||||
IDR_MEDIAPLAYER_CLOSEDCAPTIONS_ICON_NEW,
|
||||
ui::SCALE_FACTOR_100P},
|
||||
{"mediaplayerSubtitlesIcon",
|
||||
IDR_MEDIAPLAYER_SUBTITLES_ICON,
|
||||
ui::SCALE_FACTOR_100P},
|
||||
{"mediaplayerSubtitlesIconNew",
|
||||
IDR_MEDIAPLAYER_SUBTITLES_ICON_NEW,
|
||||
ui::SCALE_FACTOR_100P},
|
||||
{"searchCancel", IDR_SEARCH_CANCEL, ui::SCALE_FACTOR_100P},
|
||||
{"searchCancelPressed", IDR_SEARCH_CANCEL_PRESSED, ui::SCALE_FACTOR_100P},
|
||||
{"searchMagnifier", IDR_SEARCH_MAGNIFIER, ui::SCALE_FACTOR_100P},
|
||||
|
@ -47,13 +47,18 @@ function mediaControlsButton(element, id)
|
||||
return button;
|
||||
}
|
||||
|
||||
function elementCoordinates(element)
|
||||
{
|
||||
var elementBoundingRect = element.getBoundingClientRect();
|
||||
var x = elementBoundingRect.left + elementBoundingRect.width / 2;
|
||||
var y = elementBoundingRect.top + elementBoundingRect.height / 2;
|
||||
return new Array(x, y);
|
||||
}
|
||||
|
||||
function mediaControlsButtonCoordinates(element, id)
|
||||
{
|
||||
var button = mediaControlsButton(element, id);
|
||||
var buttonBoundingRect = button.getBoundingClientRect();
|
||||
var x = buttonBoundingRect.left + buttonBoundingRect.width / 2;
|
||||
var y = buttonBoundingRect.top + buttonBoundingRect.height / 2;
|
||||
return new Array(x, y);
|
||||
return elementCoordinates(button);
|
||||
}
|
||||
|
||||
function mediaControlsButtonDimensions(element, id)
|
||||
@ -119,12 +124,45 @@ function testClosedCaptionsButtonVisibility(expected)
|
||||
}
|
||||
}
|
||||
|
||||
function clickAtCoordinates(x, y)
|
||||
{
|
||||
eventSender.mouseMoveTo(x, y);
|
||||
eventSender.mouseDown();
|
||||
eventSender.mouseUp();
|
||||
}
|
||||
|
||||
function clickCCButton()
|
||||
{
|
||||
consoleWrite("*** Click the CC button.");
|
||||
eventSender.mouseMoveTo(captionsButtonCoordinates[0], captionsButtonCoordinates[1]);
|
||||
eventSender.mouseDown();
|
||||
eventSender.mouseUp();
|
||||
clickAtCoordinates(captionsButtonCoordinates[0], captionsButtonCoordinates[1]);
|
||||
}
|
||||
|
||||
function textTrackListItemAtIndex(video, index)
|
||||
{
|
||||
var textTrackListElementID = "-internal-media-controls-text-track-list";
|
||||
var textTrackListElement = mediaControlsElement(internals.shadowRoot(video).firstChild, textTrackListElementID);
|
||||
if (!textTrackListElement)
|
||||
throw "Failed to find text track list element";
|
||||
|
||||
var trackListItems = textTrackListElement.childNodes;
|
||||
for (var i = 0; i < trackListItems.length; i++) {
|
||||
var trackListItem = trackListItems[i];
|
||||
if (trackListItem.firstChild.getAttribute("data-track-index") == index)
|
||||
return trackListItem;
|
||||
}
|
||||
}
|
||||
|
||||
function selectTextTrack(video, index)
|
||||
{
|
||||
clickCCButton();
|
||||
var trackListItemElement = textTrackListItemAtIndex(video, index);
|
||||
var trackListItemCoordinates = elementCoordinates(trackListItemElement);
|
||||
clickAtCoordinates(trackListItemCoordinates[0], trackListItemCoordinates[1]);
|
||||
}
|
||||
|
||||
function turnClosedCaptionsOff(video)
|
||||
{
|
||||
selectTextTrack(video, -1);
|
||||
}
|
||||
|
||||
function runAfterHideMediaControlsTimerFired(func, mediaElement)
|
||||
|
@ -37,7 +37,7 @@ async_test(function(t) {
|
||||
ascendant.offsetTop;
|
||||
ascendant.classList.add("cue");
|
||||
if (window.internals)
|
||||
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 8);
|
||||
assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 9);
|
||||
assert_equals(getComputedStyle(cueNode).backgroundColor, green);
|
||||
|
||||
assert_equals(getComputedStyle(cNode).backgroundColor, red);
|
||||
|
22
third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-add-track-expected.txt
vendored
Normal file
22
third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-add-track-expected.txt
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Test that we can add a track dynamically and it is displayed on the track selection menu
|
||||
EVENT(canplaythrough)
|
||||
|
||||
** Caption button should be visible and enabled.
|
||||
EXPECTED (captionsButtonCoordinates[0] > '0') OK
|
||||
EXPECTED (captionsButtonCoordinates[1] > '0') OK
|
||||
EXPECTED (captionsButtonElement.disabled == 'false') OK
|
||||
|
||||
EXPECTED (video.textTracks.length == '2') OK
|
||||
EXPECTED (video.textTracks[0].mode == 'showing') OK
|
||||
EXPECTED (video.textTracks[1].mode == 'hidden') OK
|
||||
|
||||
Verify the default track is being displayed
|
||||
EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'Lorem') OK
|
||||
|
||||
Select the newly added track
|
||||
*** Click the CC button.
|
||||
EXPECTED (video.textTracks[1].mode == 'showing') OK
|
||||
EXPECTED (video.textTracks[0].mode == 'disabled') OK
|
||||
EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'Bonjour') OK
|
||||
END OF TEST
|
||||
|
51
third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-add-track.html
vendored
Normal file
51
third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-add-track.html
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../media-file.js"></script>
|
||||
<!-- TODO(srivats): Convert test to testharness.js. crbug.com/588956
|
||||
(Please avoid writing new tests using video-test.js) -->
|
||||
<script src="../video-test.js"></script>
|
||||
<script src="../media-controls.js"></script>
|
||||
<script>
|
||||
|
||||
var trackCueText = "Bonjour";
|
||||
|
||||
function selectTrackAdded()
|
||||
{
|
||||
findMediaElement();
|
||||
testClosedCaptionsButtonVisibility(true);
|
||||
consoleWrite("");
|
||||
testExpected("video.textTracks.length", 2);
|
||||
testExpected("video.textTracks[0].mode", "showing");
|
||||
testExpected("video.textTracks[1].mode", "hidden");
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("Verify the default track is being displayed");
|
||||
testExpected("textTrackDisplayElement(video, 'display').innerText", "Lorem");
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("Select the newly added track");
|
||||
selectTextTrack(video, 1);
|
||||
testExpected("video.textTracks[1].mode", "showing");
|
||||
testExpected("video.textTracks[0].mode", "disabled");
|
||||
testExpected("textTrackDisplayElement(video, 'display').innerText", trackCueText);
|
||||
endTest();
|
||||
}
|
||||
|
||||
function addTextTrack()
|
||||
{
|
||||
track = video.addTextTrack("captions", "French", "fr");
|
||||
track.addCue(new VTTCue(0.0, 1.0, trackCueText));
|
||||
selectTrackAdded();
|
||||
}
|
||||
|
||||
window.onload = function()
|
||||
{
|
||||
consoleWrite("Test that we can add a track dynamically and it is displayed on the track selection menu");
|
||||
findMediaElement();
|
||||
video.src = findMediaFile("video", "../content/test");
|
||||
waitForEvent("canplaythrough", addTextTrack);
|
||||
}
|
||||
|
||||
</script>
|
||||
<video controls>
|
||||
<track src="captions-webvtt/captions.vtt" kind="captions" label="English" srclang="en" default>
|
||||
</video>
|
57
third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-multiple-tracks-expected.txt
vendored
Normal file
57
third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-multiple-tracks-expected.txt
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
Test that we can add multiple tracks and select between them from the track selection menu
|
||||
EVENT(canplaythrough)
|
||||
|
||||
** Caption button should be visible and enabled.
|
||||
EXPECTED (captionsButtonCoordinates[0] > '0') OK
|
||||
EXPECTED (captionsButtonCoordinates[1] > '0') OK
|
||||
EXPECTED (captionsButtonElement.disabled == 'false') OK
|
||||
|
||||
EXPECTED (video.textTracks.length == '5') OK
|
||||
|
||||
Select track at index 0
|
||||
*** Click the CC button.
|
||||
EXPECTED (video.textTracks[0].mode == 'showing') OK
|
||||
EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'English') OK
|
||||
EXPECTED (video.textTracks[1].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[2].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[3].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[4].mode == 'disabled') OK
|
||||
|
||||
Select track at index 1
|
||||
*** Click the CC button.
|
||||
EXPECTED (video.textTracks[1].mode == 'showing') OK
|
||||
EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'Russian') OK
|
||||
EXPECTED (video.textTracks[0].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[2].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[3].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[4].mode == 'disabled') OK
|
||||
|
||||
Select track at index 2
|
||||
*** Click the CC button.
|
||||
EXPECTED (video.textTracks[2].mode == 'showing') OK
|
||||
EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'French') OK
|
||||
EXPECTED (video.textTracks[0].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[1].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[3].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[4].mode == 'disabled') OK
|
||||
|
||||
Select track at index 3
|
||||
*** Click the CC button.
|
||||
EXPECTED (video.textTracks[3].mode == 'showing') OK
|
||||
EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'Japanese') OK
|
||||
EXPECTED (video.textTracks[0].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[1].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[2].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[4].mode == 'disabled') OK
|
||||
|
||||
Select track at index 4
|
||||
*** Click the CC button.
|
||||
EXPECTED (video.textTracks[4].mode == 'showing') OK
|
||||
EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'German') OK
|
||||
EXPECTED (video.textTracks[0].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[1].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[2].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[3].mode == 'disabled') OK
|
||||
|
||||
END OF TEST
|
||||
|
53
third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-multiple-tracks.html
vendored
Normal file
53
third_party/WebKit/LayoutTests/media/track/text-track-selection-menu-multiple-tracks.html
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../media-file.js"></script>
|
||||
<!-- TODO(srivats): Convert test to testharness.js. crbug.com/588956
|
||||
(Please avoid writing new tests using video-test.js) -->
|
||||
<script src="../video-test.js"></script>
|
||||
<script src="../media-controls.js"></script>
|
||||
<script>
|
||||
|
||||
var trackLanguages = ["en", "ru", "fr", "jp", "de"];
|
||||
var trackCueText = ["English", "Russian", "French", "Japanese", "German"];
|
||||
|
||||
function startTest()
|
||||
{
|
||||
findMediaElement();
|
||||
testClosedCaptionsButtonVisibility(true);
|
||||
consoleWrite("");
|
||||
testExpected("video.textTracks.length", trackLanguages.length);
|
||||
consoleWrite("");
|
||||
|
||||
for (var i = 0; i < trackLanguages.length; i++) {
|
||||
consoleWrite("Select track at index " + i);
|
||||
selectTextTrack(video, i);
|
||||
testExpected("video.textTracks[" + i + "].mode", "showing");
|
||||
testExpected("textTrackDisplayElement(video, 'display').innerText", trackCueText[i]);
|
||||
for (var j = 0; j < trackLanguages.length; j++) {
|
||||
if (j != i)
|
||||
testExpected("video.textTracks[" + j + "].mode", "disabled");
|
||||
}
|
||||
consoleWrite("");
|
||||
}
|
||||
endTest();
|
||||
}
|
||||
|
||||
function addTextTracks()
|
||||
{
|
||||
for (var i = 0; i < trackLanguages.length; i++) {
|
||||
var track = video.addTextTrack("captions", trackCueText[i], trackLanguages[i]);
|
||||
track.addCue(new VTTCue(0.0, 1.0, trackCueText[i]));
|
||||
track.mode = "disabled";
|
||||
}
|
||||
startTest();
|
||||
}
|
||||
|
||||
window.onload = function()
|
||||
{
|
||||
consoleWrite("Test that we can add multiple tracks and select between them from the track selection menu");
|
||||
findMediaElement();
|
||||
video.src = findMediaFile("video", "../content/test");
|
||||
waitForEvent("canplaythrough", addTextTracks);
|
||||
}
|
||||
|
||||
</script>
|
||||
<video controls></video>
|
@ -1,4 +1,4 @@
|
||||
Tests that the closed captions button, when toggled, updates the text track display area.
|
||||
Tests that the closed captions button enables track switching
|
||||
|
||||
EVENT(canplaythrough)
|
||||
|
||||
@ -12,11 +12,11 @@ EXPECTED (video.textTracks.length == '1') OK
|
||||
EXPECTED (video.textTracks[0].mode == 'disabled') OK
|
||||
Failed to find text track container element
|
||||
|
||||
** Captions track should load and captions should become visible after button is clicked **
|
||||
** Captions track should load and captions should become visible after a track is selected **
|
||||
*** Click the CC button.
|
||||
EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'Lorem') OK
|
||||
|
||||
** Captions should not be visible after button is clicked again **
|
||||
** Captions should not be visible after Off is clicked **
|
||||
*** Click the CC button.
|
||||
No text track cue with display id '-webkit-media-text-track-display' is currently visible
|
||||
|
||||
|
@ -1,19 +0,0 @@
|
||||
Tests that appropriate language track is loaded, according to user preferences.
|
||||
|
||||
EVENT(canplaythrough)
|
||||
|
||||
** Caption button should be visible and enabled.
|
||||
EXPECTED (captionsButtonCoordinates[0] > '0') OK
|
||||
EXPECTED (captionsButtonCoordinates[1] > '0') OK
|
||||
EXPECTED (captionsButtonElement.disabled == 'false') OK
|
||||
|
||||
** The captions track should be listed in textTracks, but disabled. **
|
||||
EXPECTED (video.textTracks.length == '2') OK
|
||||
EXPECTED (video.textTracks[0].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[1].mode == 'disabled') OK
|
||||
|
||||
** Set the user language preference so that the track will be chosen when the CC button is clicked. **
|
||||
RUN(internals.setUserPreferredLanguages(['ar']))
|
||||
*** Click the CC button.
|
||||
END OF TEST
|
||||
|
@ -1,52 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Test non-default track load according to user language preference.</title>
|
||||
<script src=media-file.js></script>
|
||||
<script src=media-controls.js></script>
|
||||
<!-- TODO(philipj): Convert test to testharness.js. crbug.com/588956
|
||||
(Please avoid writing new tests using video-test.js) -->
|
||||
<script src=video-test.js></script>
|
||||
<script>
|
||||
var track;
|
||||
|
||||
function startTest()
|
||||
{
|
||||
if (!window.eventSender) {
|
||||
consoleWrite("No eventSender found.");
|
||||
failTest();
|
||||
}
|
||||
|
||||
findMediaElement();
|
||||
testClosedCaptionsButtonVisibility(true);
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("** The captions track should be listed in textTracks, but disabled. **");
|
||||
testExpected("video.textTracks.length", 2);
|
||||
testExpected("video.textTracks[0].mode", "disabled");
|
||||
testExpected("video.textTracks[1].mode", "disabled");
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("** Set the user language preference so that the track will be chosen when the CC button is clicked. **");
|
||||
run("internals.setUserPreferredLanguages(['ar'])");
|
||||
clickCCButton();
|
||||
}
|
||||
|
||||
function loaded()
|
||||
{
|
||||
findMediaElement();
|
||||
waitForEvent('canplaythrough', startTest);
|
||||
|
||||
video.src = findMediaFile('video', 'content/counting');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="loaded()">
|
||||
<p>Tests that appropriate language track is loaded, according to user preferences.</p>
|
||||
<video controls>
|
||||
<track src="track/captions-webvtt/captions-fast.vtt" kind="captions">
|
||||
<track src="track/captions-webvtt/captions-fast.vtt" kind="captions" srclang="ar" onload="endTest()">
|
||||
</video>
|
||||
</body>
|
||||
</html>
|
@ -1,4 +1,4 @@
|
||||
Tests that multiple toggles of the closed captions button still display captions
|
||||
Tests that tracks can be turned on and off through the track selection menu
|
||||
|
||||
EVENT(canplaythrough)
|
||||
|
||||
@ -14,19 +14,19 @@ No text track cue with display id '-webkit-media-text-track-display' is currentl
|
||||
No text track cue with display id '-webkit-media-text-track-display' is currently visible
|
||||
No text track cue with display id '-webkit-media-text-track-display' is currently visible
|
||||
|
||||
** Captions track should become visible after button is clicked **
|
||||
** Captions track should become visible after the track is selected **
|
||||
*** Click the CC button.
|
||||
EXPECTED (displayElement.innerText == 'First') OK
|
||||
EXPECTED (displayElement.innerText == 'Second') OK
|
||||
EXPECTED (displayElement.innerText == 'Third') OK
|
||||
|
||||
** Captions should not be visible after button is clicked again **
|
||||
** Captions should not be visible after they're turned off through the menu **
|
||||
*** Click the CC button.
|
||||
No text track cue with display id '-webkit-media-text-track-display' is currently visible
|
||||
No text track cue with display id '-webkit-media-text-track-display' is currently visible
|
||||
No text track cue with display id '-webkit-media-text-track-display' is currently visible
|
||||
|
||||
** Captions should become visible after button is clicked again **
|
||||
** Captions track should become visible after the track is selected again **
|
||||
*** Click the CC button.
|
||||
EXPECTED (displayElement.innerText == 'First') OK
|
||||
EXPECTED (displayElement.innerText == 'Second') OK
|
@ -2,22 +2,21 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Test closed caption button toggling.</title>
|
||||
<title>Test closed caption track selection on and off.</title>
|
||||
<script src=media-file.js></script>
|
||||
<script src=media-controls.js></script>
|
||||
<!-- TODO(philipj): Convert test to testharness.js. crbug.com/588956
|
||||
(Please avoid writing new tests using video-test.js) -->
|
||||
<script src=video-test.js></script>
|
||||
<script>
|
||||
var displayElement;
|
||||
var track;
|
||||
var text = ["First", "Second", "Third"];
|
||||
var displayElement;
|
||||
|
||||
function addTextTrack()
|
||||
{
|
||||
track = video.addTextTrack('captions');
|
||||
var track = video.addTextTrack('captions');
|
||||
|
||||
for(var i = 0; i < 3; i++) {
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var cue = new VTTCue(0, 120, text[i]);
|
||||
track.addCue(cue);
|
||||
}
|
||||
@ -27,9 +26,9 @@
|
||||
{
|
||||
for (var i = 0; i < 3; i++) {
|
||||
try {
|
||||
displayElement = textTrackDisplayElement(video, 'display', i);
|
||||
displayElement = textTrackDisplayElement(video, "display", i);
|
||||
testExpected("displayElement.innerText", text[i]);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
consoleWrite(e);
|
||||
}
|
||||
}
|
||||
@ -54,18 +53,18 @@
|
||||
checkCaptionsDisplay();
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("** Captions track should become visible after button is clicked **");
|
||||
clickCCButton();
|
||||
consoleWrite("** Captions track should become visible after the track is selected **");
|
||||
selectTextTrack(video, 0);
|
||||
checkCaptionsDisplay();
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("** Captions should not be visible after button is clicked again **");
|
||||
clickCCButton();
|
||||
consoleWrite("** Captions should not be visible after they're turned off through the menu **");
|
||||
turnClosedCaptionsOff(video);
|
||||
checkCaptionsDisplay();
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("** Captions should become visible after button is clicked again **");
|
||||
clickCCButton();
|
||||
consoleWrite("** Captions track should become visible after the track is selected again **");
|
||||
selectTextTrack(video, 0);
|
||||
checkCaptionsDisplay();
|
||||
|
||||
consoleWrite("");
|
||||
@ -82,7 +81,7 @@
|
||||
</script>
|
||||
</head>
|
||||
<body onload="loaded()">
|
||||
<p>Tests that multiple toggles of the closed captions button still display captions</p>
|
||||
<p>Tests that tracks can be turned on and off through the track selection menu</p>
|
||||
<video controls></video>
|
||||
</body>
|
||||
</html>
|
@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Test closed caption button toggling.</title>
|
||||
<title>Test closed caption track selection functionality.</title>
|
||||
<script src=media-file.js></script>
|
||||
<script src=media-controls.js></script>
|
||||
<!-- TODO(philipj): Convert test to testharness.js. crbug.com/588956
|
||||
@ -70,21 +70,21 @@
|
||||
checkCaptionsDisplay();
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("** Captions track should load and captions should become visible after button is clicked **");
|
||||
consoleWrite("** Captions track should load and captions should become visible after a track is selected **");
|
||||
|
||||
// Note: the test flow continues with "testCCButtonToggling" when the
|
||||
// Note: the test flow continues with "testCCTrackSelectionFunctionality" when the
|
||||
// "load" event of the single TextTrack fires up. While the test structure
|
||||
// might seem weird, this avoids timeouts.
|
||||
clickCCButton();
|
||||
selectTextTrack(video, 0);
|
||||
}
|
||||
|
||||
function testCCButtonToggling()
|
||||
function testCCTrackSelectionFunctionality()
|
||||
{
|
||||
checkCaptionsDisplay();
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("** Captions should not be visible after button is clicked again **");
|
||||
clickCCButton();
|
||||
consoleWrite("** Captions should not be visible after Off is clicked **");
|
||||
turnClosedCaptionsOff(video);
|
||||
checkCaptionsDisplay();
|
||||
|
||||
removeHTMLTrackElement();
|
||||
@ -94,7 +94,7 @@
|
||||
testClosedCaptionsButtonVisibility(true);
|
||||
|
||||
consoleWrite("");
|
||||
clickCCButton();
|
||||
selectTextTrack(video, 0);
|
||||
}
|
||||
|
||||
function trackError()
|
||||
@ -118,9 +118,9 @@
|
||||
</script>
|
||||
</head>
|
||||
<body onload="loaded()">
|
||||
<p>Tests that the closed captions button, when toggled, updates the text track display area.</p>
|
||||
<p>Tests that the closed captions button enables track switching</p>
|
||||
<video controls>
|
||||
<track src="track/captions-webvtt/captions-fast.vtt" kind="captions" onload="testCCButtonToggling()">
|
||||
<track src="track/captions-webvtt/captions-fast.vtt" kind="captions" onload="testCCTrackSelectionFunctionality()">
|
||||
</video>
|
||||
</body>
|
||||
</html>
|
||||
|
26
third_party/WebKit/LayoutTests/media/video-controls-track-selection-menu-expected.txt
vendored
Normal file
26
third_party/WebKit/LayoutTests/media/video-controls-track-selection-menu-expected.txt
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
Test that we can display a track list menu and select tracks from the list
|
||||
EVENT(canplaythrough)
|
||||
|
||||
** Caption button should be visible and enabled.
|
||||
EXPECTED (captionsButtonCoordinates[0] > '0') OK
|
||||
EXPECTED (captionsButtonCoordinates[1] > '0') OK
|
||||
EXPECTED (captionsButtonElement.disabled == 'false') OK
|
||||
|
||||
EXPECTED (video.textTracks.length == '2') OK
|
||||
EXPECTED (video.textTracks[0].mode == 'hidden') OK
|
||||
EXPECTED (video.textTracks[1].mode == 'hidden') OK
|
||||
|
||||
Select track 0 and verify it is displayed
|
||||
*** Click the CC button.
|
||||
EXPECTED (video.textTracks[0].mode == 'showing') OK
|
||||
EXPECTED (video.textTracks[1].mode == 'hidden') OK
|
||||
EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'Lorem') OK
|
||||
|
||||
Select track 1 and verify it is displayed
|
||||
*** Click the CC button.
|
||||
EXPECTED (video.textTracks[0].mode == 'disabled') OK
|
||||
EXPECTED (video.textTracks[1].mode == 'showing') OK
|
||||
EXPECTED (textTrackDisplayElement(video, 'display').innerText == 'first caption') OK
|
||||
|
||||
END OF TEST
|
||||
|
49
third_party/WebKit/LayoutTests/media/video-controls-track-selection-menu.html
vendored
Normal file
49
third_party/WebKit/LayoutTests/media/video-controls-track-selection-menu.html
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="media-file.js"></script>
|
||||
<!-- TODO(srivats): Convert test to testharness.js. crbug.com/588956
|
||||
(Please avoid writing new tests using video-test.js) -->
|
||||
<script src="video-test.js"></script>
|
||||
<script src="media-controls.js"></script>
|
||||
<script>
|
||||
|
||||
function startTest()
|
||||
{
|
||||
findMediaElement();
|
||||
testClosedCaptionsButtonVisibility(true);
|
||||
consoleWrite("");
|
||||
testExpected("video.textTracks.length", 2);
|
||||
testExpected("video.textTracks[0].mode", "hidden");
|
||||
testExpected("video.textTracks[1].mode", "hidden");
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("Select track 0 and verify it is displayed");
|
||||
selectTextTrack(video, 0);
|
||||
testExpected("video.textTracks[0].mode", "showing");
|
||||
testExpected("video.textTracks[1].mode", "hidden");
|
||||
testExpected("textTrackDisplayElement(video, 'display').innerText", "Lorem");
|
||||
|
||||
consoleWrite("");
|
||||
consoleWrite("Select track 1 and verify it is displayed");
|
||||
selectTextTrack(video, 1);
|
||||
testExpected("video.textTracks[0].mode", "disabled");
|
||||
testExpected("video.textTracks[1].mode", "showing");
|
||||
testExpected("textTrackDisplayElement(video, 'display').innerText", "first caption");
|
||||
|
||||
consoleWrite("");
|
||||
endTest();
|
||||
}
|
||||
|
||||
window.onload = function()
|
||||
{
|
||||
consoleWrite("Test that we can display a track list menu and select tracks from the list");
|
||||
findMediaElement();
|
||||
video.src = findMediaFile("video", "content/test");
|
||||
enableAllTextTracks();
|
||||
waitForEvent("canplaythrough", startTest);
|
||||
}
|
||||
|
||||
</script>
|
||||
<video controls>
|
||||
<track src="track/captions-webvtt/captions.vtt" kind="captions" label="Track1">
|
||||
<track src="track/captions-webvtt/long-word.vtt" kind="captions" label="Track2">
|
||||
</video>
|
@ -456,6 +456,15 @@ template<> inline CSSPrimitiveValue::CSSPrimitiveValue(ControlPart e)
|
||||
case MediaTimeRemainingPart:
|
||||
m_value.valueID = CSSValueMediaTimeRemainingDisplay;
|
||||
break;
|
||||
case MediaTrackSelectionCheckmarkPart:
|
||||
m_value.valueID = CSSValueInternalMediaTrackSelectionCheckmark;
|
||||
break;
|
||||
case MediaClosedCaptionsIconPart:
|
||||
m_value.valueID = CSSValueInternalMediaClosedCaptionsIcon;
|
||||
break;
|
||||
case MediaSubtitlesIconPart:
|
||||
m_value.valueID = CSSValueInternalMediaSubtitlesIcon;
|
||||
break;
|
||||
case MenulistPart:
|
||||
m_value.valueID = CSSValueMenulist;
|
||||
break;
|
||||
|
@ -285,6 +285,11 @@ const static NameToPseudoStruct pseudoTypeWithoutArgumentsMap[] = {
|
||||
{"-internal-list-box", CSSSelector::PseudoListBox},
|
||||
{"-internal-media-controls-cast-button", CSSSelector::PseudoWebKitCustomElement},
|
||||
{"-internal-media-controls-overlay-cast-button", CSSSelector::PseudoWebKitCustomElement},
|
||||
{"-internal-media-controls-text-track-list", CSSSelector::PseudoWebKitCustomElement},
|
||||
{"-internal-media-controls-text-track-list-item", CSSSelector::PseudoWebKitCustomElement},
|
||||
{"-internal-media-controls-text-track-list-item-input", CSSSelector::PseudoWebKitCustomElement},
|
||||
{"-internal-media-controls-text-track-list-kind-captions", CSSSelector::PseudoWebKitCustomElement},
|
||||
{"-internal-media-controls-text-track-list-kind-subtitles", CSSSelector::PseudoWebKitCustomElement},
|
||||
{"-internal-spatial-navigation-focus", CSSSelector::PseudoSpatialNavigationFocus},
|
||||
{"-webkit-any-link", CSSSelector::PseudoAnyLink},
|
||||
{"-webkit-autofill", CSSSelector::PseudoAutofill},
|
||||
|
@ -636,6 +636,9 @@ media-current-time-display
|
||||
media-time-remaining-display
|
||||
-internal-media-cast-off-button
|
||||
-internal-media-overlay-cast-off-button
|
||||
-internal-media-track-selection-checkmark
|
||||
-internal-media-closed-captions-icon
|
||||
-internal-media-subtitles-icon
|
||||
menulist
|
||||
menulist-button
|
||||
menulist-text
|
||||
|
@ -44,6 +44,7 @@ video::-webkit-media-controls {
|
||||
direction: ltr;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
@ -204,7 +205,6 @@ audio::-webkit-media-controls-time-remaining-display, video::-webkit-media-contr
|
||||
padding: 0;
|
||||
|
||||
line-height: 30px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
@ -338,6 +338,82 @@ audio::-webkit-media-controls-fullscreen-volume-max-button, video::-webkit-media
|
||||
display: none;
|
||||
}
|
||||
|
||||
video::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
video::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 6px rgba(20, 20, 20, 0.3);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
video::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
background-color: rgba(80, 80, 80, 0.8);
|
||||
box-shadow: inset 0 0 6px rgba(20, 20, 20, 0.5);
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list {
|
||||
position: absolute;
|
||||
bottom: 35px;
|
||||
right: 5px;
|
||||
background: rgba(20, 20, 20, 0.8);
|
||||
max-width: 50%;
|
||||
max-height: 250px;
|
||||
border-radius: 5px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 5px;
|
||||
white-space: nowrap;
|
||||
padding-top: 10px;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-item {
|
||||
display: block;
|
||||
color: white;
|
||||
padding: 4px 30px 4px 10px;
|
||||
border-bottom: 1px solid #555;
|
||||
text-align: start;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-item:hover {
|
||||
background-color: rgba(105, 105, 105, 0.8);
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-item-input {
|
||||
-webkit-appearance: -internal-media-track-selection-checkmark;
|
||||
visibility: hidden;
|
||||
left: 0;
|
||||
vertical-align: middle;
|
||||
margin: 0 5px 0 0;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-item-input:checked {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-kind-captions {
|
||||
-webkit-appearance: -internal-media-closed-captions-icon;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin-left: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-kind-subtitles {
|
||||
-webkit-appearance: -internal-media-subtitles-icon;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin-left: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
video::-webkit-media-text-track-container {
|
||||
position: relative;
|
||||
width: inherit;
|
||||
|
@ -44,6 +44,7 @@ video::-webkit-media-controls {
|
||||
direction: ltr;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: Segoe, "Helvetica Neue", Roboto, Arial, Helvetica, sans-serif;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
@ -80,7 +81,6 @@ audio::-webkit-media-controls-panel, video::-webkit-media-controls-panel {
|
||||
/* The duration is also specified in MediaControlElements.cpp and LayoutTests/media/media-controls.js */
|
||||
transition: opacity 0.3s;
|
||||
|
||||
font-family: Segoe, "Helvetica Neue", Roboto, Arial, Helvetica, sans-serif ;
|
||||
font-size: 14px;
|
||||
font-weight: normal; /* Make sure that we don't inherit non-defaults. */
|
||||
font-style: normal;
|
||||
@ -359,6 +359,65 @@ audio::-webkit-media-controls-fullscreen-volume-max-button, video::-webkit-media
|
||||
display: none;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list {
|
||||
position: absolute;
|
||||
bottom: 48px;
|
||||
right: 0px;
|
||||
background-color: #fafafa;
|
||||
max-width: 50%;
|
||||
max-height: 250px;
|
||||
min-width: 150px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
padding: 8px 0px;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-item {
|
||||
display: block;
|
||||
color: #424242;
|
||||
text-align: start;
|
||||
line-height: 40px;
|
||||
padding-right: 16px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-item:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-item-input {
|
||||
-webkit-appearance: -internal-media-track-selection-checkmark;
|
||||
visibility: hidden;
|
||||
left: 0;
|
||||
vertical-align: middle;
|
||||
margin: 0 5px 0 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-item-input:checked {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-kind-captions {
|
||||
-webkit-appearance: -internal-media-closed-captions-icon;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-left: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
video::-internal-media-controls-text-track-list-kind-subtitles {
|
||||
-webkit-appearance: -internal-media-subtitles-icon;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-left: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
video::-webkit-media-text-track-container {
|
||||
position: relative;
|
||||
width: inherit;
|
||||
|
@ -413,8 +413,9 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
|
||||
, m_seeking(false)
|
||||
, m_sentStalledEvent(false)
|
||||
, m_sentEndEvent(false)
|
||||
, m_closedCaptionsVisible(false)
|
||||
, m_ignorePreloadNone(false)
|
||||
, m_textTracksVisible(false)
|
||||
, m_shouldPerformAutomaticTrackSelection(true)
|
||||
, m_tracksAreReady(true)
|
||||
, m_processingPreferenceChange(false)
|
||||
, m_remoteRoutesAvailable(false)
|
||||
@ -1231,6 +1232,11 @@ void HTMLMediaElement::textTrackModeChanged(TextTrack* track)
|
||||
textTracks()->scheduleChangeEvent();
|
||||
}
|
||||
|
||||
void HTMLMediaElement::disableAutomaticTextTrackSelection()
|
||||
{
|
||||
m_shouldPerformAutomaticTrackSelection = false;
|
||||
}
|
||||
|
||||
bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidURLAction actionIfInvalid)
|
||||
{
|
||||
if (!url.isValid()) {
|
||||
@ -2592,10 +2598,13 @@ void HTMLMediaElement::honorUserPreferencesForAutomaticTextTrackSelection()
|
||||
if (!m_textTracks || !m_textTracks->length())
|
||||
return;
|
||||
|
||||
if (!m_shouldPerformAutomaticTrackSelection)
|
||||
return;
|
||||
|
||||
AutomaticTrackSelection::Configuration configuration;
|
||||
if (m_processingPreferenceChange)
|
||||
configuration.disableCurrentlyEnabledTracks = true;
|
||||
if (m_closedCaptionsVisible)
|
||||
if (m_textTracksVisible)
|
||||
configuration.forceEnableSubtitleOrCaptionTrack = true;
|
||||
|
||||
Settings* settings = document().settings();
|
||||
@ -3265,9 +3274,9 @@ bool HTMLMediaElement::hasClosedCaptions() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HTMLMediaElement::closedCaptionsVisible() const
|
||||
bool HTMLMediaElement::textTracksVisible() const
|
||||
{
|
||||
return m_closedCaptionsVisible;
|
||||
return m_textTracksVisible;
|
||||
}
|
||||
|
||||
static void assertShadowRootChildren(ShadowRoot& shadowRoot)
|
||||
@ -3323,30 +3332,10 @@ void HTMLMediaElement::mediaControlsDidBecomeVisible()
|
||||
// When the user agent starts exposing a user interface for a video element,
|
||||
// the user agent should run the rules for updating the text track rendering
|
||||
// of each of the text tracks in the video element's list of text tracks ...
|
||||
if (isHTMLVideoElement() && closedCaptionsVisible())
|
||||
if (isHTMLVideoElement() && textTracksVisible())
|
||||
ensureTextTrackContainer().updateDisplay(*this, TextTrackContainer::DidStartExposingControls);
|
||||
}
|
||||
|
||||
void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible)
|
||||
{
|
||||
WTF_LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%p, %s)", this, boolString(closedCaptionVisible));
|
||||
|
||||
if (!hasClosedCaptions())
|
||||
return;
|
||||
|
||||
m_closedCaptionsVisible = closedCaptionVisible;
|
||||
|
||||
markCaptionAndSubtitleTracksAsUnconfigured();
|
||||
m_processingPreferenceChange = true;
|
||||
honorUserPreferencesForAutomaticTextTrackSelection();
|
||||
m_processingPreferenceChange = false;
|
||||
|
||||
// As track visibility changed while m_processingPreferenceChange was set,
|
||||
// there was no call to updateTextTrackDisplay(). This call is not in the
|
||||
// spec, see the note in configureTextTrackDisplay().
|
||||
updateTextTrackDisplay();
|
||||
}
|
||||
|
||||
void HTMLMediaElement::setTextTrackKindUserPreferenceForAllMediaElements(Document* document)
|
||||
{
|
||||
auto it = documentToElementSetMap().find(document);
|
||||
@ -3365,13 +3354,13 @@ void HTMLMediaElement::automaticTrackSelectionForUpdatedUserPreference()
|
||||
|
||||
markCaptionAndSubtitleTracksAsUnconfigured();
|
||||
m_processingPreferenceChange = true;
|
||||
m_closedCaptionsVisible = false;
|
||||
m_textTracksVisible = false;
|
||||
honorUserPreferencesForAutomaticTextTrackSelection();
|
||||
m_processingPreferenceChange = false;
|
||||
|
||||
// If a track is set to 'showing' post performing automatic track selection,
|
||||
// set closed captions state to visible to update the CC button and display the track.
|
||||
m_closedCaptionsVisible = m_textTracks->hasShowingTracks();
|
||||
// set text tracks state to visible to update the CC button and display the track.
|
||||
m_textTracksVisible = m_textTracks->hasShowingTracks();
|
||||
updateTextTrackDisplay();
|
||||
}
|
||||
|
||||
@ -3496,7 +3485,7 @@ void HTMLMediaElement::configureTextTrackDisplay()
|
||||
return;
|
||||
|
||||
bool haveVisibleTextTrack = m_textTracks->hasShowingTracks();
|
||||
m_closedCaptionsVisible = haveVisibleTextTrack;
|
||||
m_textTracksVisible = haveVisibleTextTrack;
|
||||
|
||||
if (!haveVisibleTextTrack && !mediaControls())
|
||||
return;
|
||||
|
@ -207,6 +207,7 @@ public:
|
||||
void textTrackReadyStateChanged(TextTrack*);
|
||||
|
||||
void textTrackModeChanged(TextTrack*);
|
||||
void disableAutomaticTextTrackSelection();
|
||||
|
||||
// EventTarget function.
|
||||
// Both Node (via HTMLElement) and ActiveDOMObject define this method, which
|
||||
@ -223,8 +224,7 @@ public:
|
||||
virtual bool usesOverlayFullscreenVideo() const { return false; }
|
||||
|
||||
bool hasClosedCaptions() const;
|
||||
bool closedCaptionsVisible() const;
|
||||
void setClosedCaptionsVisible(bool);
|
||||
bool textTracksVisible() const;
|
||||
|
||||
static void setTextTrackKindUserPreferenceForAllMediaElements(Document*);
|
||||
void automaticTrackSelectionForUpdatedUserPreference();
|
||||
@ -561,9 +561,11 @@ private:
|
||||
// time has not changed since sending an "ended" event
|
||||
bool m_sentEndEvent : 1;
|
||||
|
||||
bool m_closedCaptionsVisible : 1;
|
||||
|
||||
bool m_ignorePreloadNone : 1;
|
||||
|
||||
bool m_textTracksVisible : 1;
|
||||
bool m_shouldPerformAutomaticTrackSelection : 1;
|
||||
|
||||
bool m_tracksAreReady : 1;
|
||||
bool m_processingPreferenceChange : 1;
|
||||
bool m_remoteRoutesAvailable : 1;
|
||||
|
@ -48,11 +48,13 @@ enum MediaControlElementType {
|
||||
MediaSliderThumb,
|
||||
MediaShowClosedCaptionsButton,
|
||||
MediaHideClosedCaptionsButton,
|
||||
MediaTextTrackList,
|
||||
MediaUnMuteButton,
|
||||
MediaPauseButton,
|
||||
MediaTimelineContainer,
|
||||
MediaCurrentTimeDisplay,
|
||||
MediaTimeRemainingDisplay,
|
||||
MediaTrackSelectionCheckmark,
|
||||
MediaControlsPanel,
|
||||
MediaVolumeSliderContainer,
|
||||
MediaVolumeSlider,
|
||||
|
@ -32,19 +32,25 @@
|
||||
#include "bindings/core/v8/ExceptionStatePlaceholder.h"
|
||||
#include "core/InputTypeNames.h"
|
||||
#include "core/dom/ClientRect.h"
|
||||
#include "core/dom/Text.h"
|
||||
#include "core/dom/shadow/ShadowRoot.h"
|
||||
#include "core/events/MouseEvent.h"
|
||||
#include "core/frame/LocalFrame.h"
|
||||
#include "core/html/HTMLLabelElement.h"
|
||||
#include "core/html/HTMLMediaSource.h"
|
||||
#include "core/html/HTMLSpanElement.h"
|
||||
#include "core/html/HTMLVideoElement.h"
|
||||
#include "core/html/TimeRanges.h"
|
||||
#include "core/html/shadow/MediaControls.h"
|
||||
#include "core/html/track/TextTrackList.h"
|
||||
#include "core/input/EventHandler.h"
|
||||
#include "core/layout/LayoutTheme.h"
|
||||
#include "core/layout/LayoutVideo.h"
|
||||
#include "core/layout/api/LayoutSliderItem.h"
|
||||
#include "platform/EventDispatchForbiddenScope.h"
|
||||
#include "platform/Histogram.h"
|
||||
#include "platform/RuntimeEnabledFeatures.h"
|
||||
#include "platform/text/PlatformLocale.h"
|
||||
#include "public/platform/Platform.h"
|
||||
#include "public/platform/UserMetricsAction.h"
|
||||
|
||||
@ -57,6 +63,16 @@ namespace {
|
||||
// This is the duration from mediaControls.css
|
||||
const double fadeOutDuration = 0.3;
|
||||
|
||||
const QualifiedName& trackIndexAttrName()
|
||||
{
|
||||
// Save the track index in an attribute to avoid holding a pointer to the text track.
|
||||
DEFINE_STATIC_LOCAL(QualifiedName, trackIndexAttr, (nullAtom, "data-track-index", nullAtom));
|
||||
return trackIndexAttr;
|
||||
}
|
||||
|
||||
// When specified as trackIndex, disable text tracks.
|
||||
const int trackIndexOffValue = -1;
|
||||
|
||||
bool isUserInteractionEvent(Event* event)
|
||||
{
|
||||
const AtomicString& type = event->type();
|
||||
@ -96,6 +112,21 @@ Element* elementFromCenter(Element& element)
|
||||
return element.document().elementFromPoint(centerX , centerY);
|
||||
}
|
||||
|
||||
bool hasDuplicateLabel(TextTrack* currentTrack)
|
||||
{
|
||||
DCHECK(currentTrack);
|
||||
TextTrackList* trackList = currentTrack->trackList();
|
||||
// The runtime of this method is quadratic but since there are usually very few text tracks it won't
|
||||
// affect the performance much.
|
||||
String currentTrackLabel = currentTrack->label();
|
||||
for (unsigned i = 0; i < trackList->length(); i++) {
|
||||
TextTrack* track = trackList->anonymousIndexedGetter(i);
|
||||
if (currentTrack != track && currentTrackLabel == track->label())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
MediaControlPanelElement::MediaControlPanelElement(MediaControls& mediaControls)
|
||||
@ -371,20 +402,14 @@ MediaControlToggleClosedCaptionsButtonElement* MediaControlToggleClosedCaptionsB
|
||||
|
||||
void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
|
||||
{
|
||||
bool captionsVisible = mediaElement().closedCaptionsVisible();
|
||||
bool captionsVisible = mediaElement().textTracksVisible();
|
||||
setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
|
||||
setChecked(captionsVisible);
|
||||
}
|
||||
|
||||
void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
|
||||
{
|
||||
if (event->type() == EventTypeNames::click) {
|
||||
if (mediaElement().closedCaptionsVisible())
|
||||
Platform::current()->recordAction(UserMetricsAction("Media.Controls.ClosedCaptionHide"));
|
||||
else
|
||||
Platform::current()->recordAction(UserMetricsAction("Media.Controls.ClosedCaptionShow"));
|
||||
mediaElement().setClosedCaptionsVisible(!mediaElement().closedCaptionsVisible());
|
||||
setChecked(mediaElement().closedCaptionsVisible());
|
||||
mediaControls().toggleTextTrackList();
|
||||
updateDisplayType();
|
||||
event->setDefaultHandled();
|
||||
}
|
||||
@ -394,6 +419,144 @@ void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* e
|
||||
|
||||
// ----------------------------
|
||||
|
||||
MediaControlTextTrackListElement::MediaControlTextTrackListElement(MediaControls& mediaControls)
|
||||
: MediaControlDivElement(mediaControls, MediaTextTrackList)
|
||||
{
|
||||
}
|
||||
|
||||
MediaControlTextTrackListElement* MediaControlTextTrackListElement::create(MediaControls& mediaControls)
|
||||
{
|
||||
MediaControlTextTrackListElement* element = new MediaControlTextTrackListElement(mediaControls);
|
||||
element->setShadowPseudoId(AtomicString("-internal-media-controls-text-track-list"));
|
||||
element->setIsWanted(false);
|
||||
return element;
|
||||
}
|
||||
|
||||
void MediaControlTextTrackListElement::defaultEventHandler(Event* event)
|
||||
{
|
||||
if (event->type() == EventTypeNames::change) {
|
||||
// Identify which input element was selected and set track to showing
|
||||
Node* target = event->target()->toNode();
|
||||
if (!target || !target->isElementNode())
|
||||
return;
|
||||
|
||||
disableShowingTextTracks();
|
||||
int trackIndex = toElement(target)->getIntegralAttribute(trackIndexAttrName());
|
||||
if (trackIndex != trackIndexOffValue) {
|
||||
ASSERT(trackIndex >= 0);
|
||||
showTextTrackAtIndex(trackIndex);
|
||||
mediaElement().disableAutomaticTextTrackSelection();
|
||||
}
|
||||
|
||||
mediaControls().toggleTextTrackList();
|
||||
event->setDefaultHandled();
|
||||
}
|
||||
MediaControlDivElement::defaultEventHandler(event);
|
||||
}
|
||||
|
||||
void MediaControlTextTrackListElement::setVisible(bool visible)
|
||||
{
|
||||
if (visible) {
|
||||
setIsWanted(true);
|
||||
refreshTextTrackListMenu();
|
||||
} else {
|
||||
setIsWanted(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaControlTextTrackListElement::showTextTrackAtIndex(unsigned indexToEnable)
|
||||
{
|
||||
TextTrackList* trackList = mediaElement().textTracks();
|
||||
if (indexToEnable >= trackList->length())
|
||||
return;
|
||||
TextTrack* track = trackList->anonymousIndexedGetter(indexToEnable);
|
||||
if (track && track->canBeRendered())
|
||||
track->setMode(TextTrack::showingKeyword());
|
||||
}
|
||||
|
||||
void MediaControlTextTrackListElement::disableShowingTextTracks()
|
||||
{
|
||||
TextTrackList* trackList = mediaElement().textTracks();
|
||||
for (unsigned i = 0; i < trackList->length(); ++i) {
|
||||
TextTrack* track = trackList->anonymousIndexedGetter(i);
|
||||
if (track->mode() == TextTrack::showingKeyword())
|
||||
track->setMode(TextTrack::disabledKeyword());
|
||||
}
|
||||
}
|
||||
|
||||
String MediaControlTextTrackListElement::getTextTrackLabel(TextTrack* track)
|
||||
{
|
||||
if (!track)
|
||||
return mediaElement().locale().queryString(WebLocalizedString::TextTracksOff);
|
||||
|
||||
String trackLabel = track->label();
|
||||
|
||||
if (trackLabel.isEmpty())
|
||||
trackLabel = String(mediaElement().locale().queryString(WebLocalizedString::TextTracksNoLabel));
|
||||
|
||||
return trackLabel;
|
||||
}
|
||||
|
||||
// TextTrack parameter when passed in as a nullptr, creates the "Off" list item in the track list.
|
||||
Element* MediaControlTextTrackListElement::createTextTrackListItem(TextTrack* track)
|
||||
{
|
||||
int trackIndex = track ? track->trackIndex() : trackIndexOffValue;
|
||||
HTMLLabelElement* trackItem = HTMLLabelElement::create(document(), nullptr);
|
||||
trackItem->setShadowPseudoId(AtomicString("-internal-media-controls-text-track-list-item"));
|
||||
HTMLInputElement* trackItemInput = HTMLInputElement::create(document(), nullptr, false);
|
||||
trackItemInput->setShadowPseudoId(AtomicString("-internal-media-controls-text-track-list-item-input"));
|
||||
trackItemInput->setType(InputTypeNames::checkbox);
|
||||
trackItemInput->setIntegralAttribute(trackIndexAttrName(), trackIndex);
|
||||
if (!mediaElement().textTracksVisible()) {
|
||||
if (!track)
|
||||
trackItemInput->setChecked(true);
|
||||
} else {
|
||||
// If there are multiple text tracks set to showing, they must all have
|
||||
// checkmarks displayed.
|
||||
if (track && track->mode() == TextTrack::showingKeyword())
|
||||
trackItemInput->setChecked(true);
|
||||
}
|
||||
|
||||
trackItem->appendChild(trackItemInput);
|
||||
String trackLabel = getTextTrackLabel(track);
|
||||
trackItem->appendChild(Text::create(document(), trackLabel));
|
||||
// Add a track kind marker icon if there are multiple tracks with the same label or if the track has no label.
|
||||
if (track && (track->label().isEmpty() || hasDuplicateLabel(track))) {
|
||||
HTMLSpanElement* trackKindMarker = HTMLSpanElement::create(document());
|
||||
if (track->kind() == track->captionsKeyword()) {
|
||||
trackKindMarker->setShadowPseudoId(AtomicString("-internal-media-controls-text-track-list-kind-captions"));
|
||||
} else {
|
||||
ASSERT(track->kind() == track->subtitlesKeyword());
|
||||
trackKindMarker->setShadowPseudoId(AtomicString("-internal-media-controls-text-track-list-kind-subtitles"));
|
||||
}
|
||||
trackItem->appendChild(trackKindMarker);
|
||||
}
|
||||
return trackItem;
|
||||
}
|
||||
|
||||
void MediaControlTextTrackListElement::refreshTextTrackListMenu()
|
||||
{
|
||||
if (!mediaElement().hasClosedCaptions() || !mediaElement().textTracksAreReady())
|
||||
return;
|
||||
|
||||
EventDispatchForbiddenScope::AllowUserAgentEvents allowEvents;
|
||||
removeChildren(OmitSubtreeModifiedEvent);
|
||||
|
||||
// Construct a menu for subtitles and captions
|
||||
// Pass in a nullptr to createTextTrackListItem to create the "Off" track item.
|
||||
appendChild(createTextTrackListItem(nullptr));
|
||||
|
||||
TextTrackList* trackList = mediaElement().textTracks();
|
||||
for (unsigned i = 0; i < trackList->length(); i++) {
|
||||
TextTrack* track = trackList->anonymousIndexedGetter(i);
|
||||
if (!track->canBeRendered())
|
||||
continue;
|
||||
appendChild(createTextTrackListItem(track));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------
|
||||
|
||||
MediaControlTimelineElement::MediaControlTimelineElement(MediaControls& mediaControls)
|
||||
: MediaControlInputElement(mediaControls, MediaSlider)
|
||||
{
|
||||
|
@ -34,6 +34,8 @@
|
||||
|
||||
namespace blink {
|
||||
|
||||
class TextTrack;
|
||||
|
||||
// ----------------------------
|
||||
|
||||
class MediaControlPanelElement final : public MediaControlDivElement {
|
||||
@ -146,6 +148,32 @@ private:
|
||||
|
||||
// ----------------------------
|
||||
|
||||
class MediaControlTextTrackListElement final : public MediaControlDivElement {
|
||||
public:
|
||||
static MediaControlTextTrackListElement* create(MediaControls&);
|
||||
|
||||
bool willRespondToMouseClickEvents() override { return true; }
|
||||
|
||||
void setVisible(bool);
|
||||
|
||||
private:
|
||||
explicit MediaControlTextTrackListElement(MediaControls&);
|
||||
|
||||
void defaultEventHandler(Event*) override;
|
||||
|
||||
void refreshTextTrackListMenu();
|
||||
|
||||
// Returns the label for the track when a valid track is passed in and "Off" when the parameter is null.
|
||||
String getTextTrackLabel(TextTrack*);
|
||||
// Creates the track element in the list when a valid track is passed in and the "Off" item when the parameter is null.
|
||||
Element* createTextTrackListItem(TextTrack*);
|
||||
|
||||
void showTextTrackAtIndex(unsigned);
|
||||
void disableShowingTextTracks();
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
|
||||
class MediaControlTimelineElement final : public MediaControlInputElement {
|
||||
public:
|
||||
static MediaControlTimelineElement* create(MediaControls&);
|
||||
|
@ -116,6 +116,7 @@ MediaControls::MediaControls(HTMLMediaElement& mediaElement)
|
||||
, m_muteButton(nullptr)
|
||||
, m_volumeSlider(nullptr)
|
||||
, m_toggleClosedCaptionsButton(nullptr)
|
||||
, m_textTrackList(nullptr)
|
||||
, m_castButton(nullptr)
|
||||
, m_fullScreenButton(nullptr)
|
||||
, m_hideMediaControlsTimer(this, &MediaControls::hideMediaControlsTimerFired)
|
||||
@ -157,6 +158,12 @@ MediaControls* MediaControls::create(HTMLMediaElement& mediaElement)
|
||||
// +-MediaControlToggleClosedCaptionsButtonElement (-webkit-media-controls-toggle-closed-captions-button)
|
||||
// +-MediaControlCastButtonElement (-internal-media-controls-cast-button)
|
||||
// \-MediaControlFullscreenButtonElement (-webkit-media-controls-fullscreen-button)
|
||||
// +-MediaControlTextTrackListElement (-internal-media-controls-text-track-list)
|
||||
// | {for each renderable text track}
|
||||
// \-MediaControlTextTrackListItem (-internal-media-controls-text-track-list-item)
|
||||
// +-MediaControlTextTrackListItemInput (-internal-media-controls-text-track-list-item-input)
|
||||
// +-MediaControlTextTrackListItemCaptions (-internal-media-controls-text-track-list-kind-captions)
|
||||
// +-MediaControlTextTrackListItemSubtitles (-internal-media-controls-text-track-list-kind-subtitles)
|
||||
void MediaControls::initializeControls()
|
||||
{
|
||||
const bool useNewUi = RuntimeEnabledFeatures::newMediaPlaybackUiEnabled();
|
||||
@ -231,6 +238,10 @@ void MediaControls::initializeControls()
|
||||
|
||||
m_enclosure = enclosure;
|
||||
appendChild(enclosure);
|
||||
|
||||
MediaControlTextTrackListElement* textTrackList = MediaControlTextTrackListElement::create(*this);
|
||||
m_textTrackList = textTrackList;
|
||||
appendChild(textTrackList);
|
||||
}
|
||||
|
||||
void MediaControls::reset()
|
||||
@ -334,6 +345,9 @@ bool MediaControls::shouldHideMediaControls(unsigned behaviorFlags) const
|
||||
const bool ignoreFocus = behaviorFlags & IgnoreFocus;
|
||||
if (!ignoreFocus && (mediaElement().focused() || contains(document().focusedElement())))
|
||||
return false;
|
||||
// Don't hide the media controls when the text track list is showing.
|
||||
if (m_textTrackList->isWanted())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -460,6 +474,16 @@ void MediaControls::refreshClosedCaptionsButtonVisibility()
|
||||
BatchedControlUpdate batch(this);
|
||||
}
|
||||
|
||||
void MediaControls::toggleTextTrackList()
|
||||
{
|
||||
if (!mediaElement().hasClosedCaptions()) {
|
||||
m_textTrackList->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_textTrackList->setVisible(!m_textTrackList->isWanted());
|
||||
}
|
||||
|
||||
void MediaControls::refreshCastButtonVisibility()
|
||||
{
|
||||
refreshCastButtonVisibilityWithoutUpdate();
|
||||
@ -759,6 +783,7 @@ DEFINE_TRACE(MediaControls)
|
||||
visitor->trace(m_fullScreenButton);
|
||||
visitor->trace(m_durationDisplay);
|
||||
visitor->trace(m_enclosure);
|
||||
visitor->trace(m_textTrackList);
|
||||
visitor->trace(m_castButton);
|
||||
visitor->trace(m_overlayCastButton);
|
||||
HTMLDivElement::trace(visitor);
|
||||
|
@ -59,6 +59,7 @@ public:
|
||||
|
||||
void changedClosedCaptionsVisibility();
|
||||
void refreshClosedCaptionsButtonVisibility();
|
||||
void toggleTextTrackList();
|
||||
|
||||
void enteredFullscreen();
|
||||
void exitedFullscreen();
|
||||
@ -141,6 +142,7 @@ private:
|
||||
Member<MediaControlMuteButtonElement> m_muteButton;
|
||||
Member<MediaControlVolumeSliderElement> m_volumeSlider;
|
||||
Member<MediaControlToggleClosedCaptionsButtonElement> m_toggleClosedCaptionsButton;
|
||||
Member<MediaControlTextTrackListElement> m_textTrackList;
|
||||
Member<MediaControlCastButtonElement> m_castButton;
|
||||
Member<MediaControlFullscreenButtonElement> m_fullScreenButton;
|
||||
|
||||
|
@ -414,7 +414,7 @@ void TextTrack::invalidateTrackIndex()
|
||||
m_renderedTrackIndex = invalidTrackIndex;
|
||||
}
|
||||
|
||||
bool TextTrack::isRendered()
|
||||
bool TextTrack::isRendered() const
|
||||
{
|
||||
if (kind() != captionsKeyword() && kind() != subtitlesKeyword())
|
||||
return false;
|
||||
@ -425,6 +425,18 @@ bool TextTrack::isRendered()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextTrack::canBeRendered() const
|
||||
{
|
||||
// A track can be displayed when it's of kind captions or subtitles and hasn't failed to load.
|
||||
if (kind() != captionsKeyword() && kind() != subtitlesKeyword())
|
||||
return false;
|
||||
|
||||
if (getReadinessState() == FailedToLoad)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TextTrackCueList* TextTrack::ensureTextTrackCueList()
|
||||
{
|
||||
if (!m_cues)
|
||||
|
@ -102,7 +102,8 @@ public:
|
||||
int trackIndex();
|
||||
void invalidateTrackIndex();
|
||||
|
||||
bool isRendered();
|
||||
bool isRendered() const;
|
||||
bool canBeRendered() const;
|
||||
int trackIndexRelativeToRenderedTracks();
|
||||
|
||||
bool hasBeenConfigured() const { return m_hasBeenConfigured; }
|
||||
|
@ -54,7 +54,7 @@ LayoutObject* TextTrackContainer::createLayoutObject(const ComputedStyle&)
|
||||
|
||||
void TextTrackContainer::updateDisplay(HTMLMediaElement& mediaElement, ExposingControls exposingControls)
|
||||
{
|
||||
if (!mediaElement.closedCaptionsVisible()) {
|
||||
if (!mediaElement.textTracksVisible()) {
|
||||
removeChildren();
|
||||
return;
|
||||
}
|
||||
|
@ -504,7 +504,7 @@ bool MediaControlsPainter::paintMediaToggleClosedCaptionsButton(const LayoutObje
|
||||
|
||||
bool isEnabled = mediaElement->hasClosedCaptions();
|
||||
|
||||
if (mediaElement->closedCaptionsVisible())
|
||||
if (mediaElement->textTracksVisible())
|
||||
return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionButton, isEnabled);
|
||||
|
||||
return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionButtonDisabled, isEnabled);
|
||||
@ -541,6 +541,39 @@ bool MediaControlsPainter::paintMediaCastButton(const LayoutObject& object, cons
|
||||
}
|
||||
}
|
||||
|
||||
bool MediaControlsPainter::paintMediaTrackSelectionCheckmark(const LayoutObject& object, const PaintInfo& paintInfo, const IntRect& rect)
|
||||
{
|
||||
const HTMLMediaElement* mediaElement = toParentMediaElement(object);
|
||||
if (!mediaElement)
|
||||
return false;
|
||||
|
||||
static Image* mediaTrackSelectionCheckmark = platformResource("mediaplayerTrackSelectionCheckmark",
|
||||
"mediaplayerTrackSelectionCheckmarkNew");
|
||||
return paintMediaButton(paintInfo.context, rect, mediaTrackSelectionCheckmark);
|
||||
}
|
||||
|
||||
bool MediaControlsPainter::paintMediaClosedCaptionsIcon(const LayoutObject& object, const PaintInfo& paintInfo, const IntRect& rect)
|
||||
{
|
||||
const HTMLMediaElement* mediaElement = toParentMediaElement(object);
|
||||
if (!mediaElement)
|
||||
return false;
|
||||
|
||||
static Image* mediaClosedCaptionsIcon = platformResource("mediaplayerClosedCaptionsIcon",
|
||||
"mediaplayerClosedCaptionsIconNew");
|
||||
return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionsIcon);
|
||||
}
|
||||
|
||||
bool MediaControlsPainter::paintMediaSubtitlesIcon(const LayoutObject& object, const PaintInfo& paintInfo, const IntRect& rect)
|
||||
{
|
||||
const HTMLMediaElement* mediaElement = toParentMediaElement(object);
|
||||
if (!mediaElement)
|
||||
return false;
|
||||
|
||||
static Image* mediaSubtitlesIcon = platformResource("mediaplayerSubtitlesIcon",
|
||||
"mediaplayerSubtitlesIconNew");
|
||||
return paintMediaButton(paintInfo.context, rect, mediaSubtitlesIcon);
|
||||
}
|
||||
|
||||
void MediaControlsPainter::adjustMediaSliderThumbSize(ComputedStyle& style)
|
||||
{
|
||||
static Image* mediaSliderThumb = platformResource("mediaplayerSliderThumb",
|
||||
|
@ -51,6 +51,9 @@ public:
|
||||
static bool paintMediaFullscreenButton(const LayoutObject&, const PaintInfo&, const IntRect&);
|
||||
static bool paintMediaOverlayPlayButton(const LayoutObject&, const PaintInfo&, const IntRect&);
|
||||
static bool paintMediaCastButton(const LayoutObject&, const PaintInfo&, const IntRect&);
|
||||
static bool paintMediaTrackSelectionCheckmark(const LayoutObject&, const PaintInfo&, const IntRect&);
|
||||
static bool paintMediaClosedCaptionsIcon(const LayoutObject&, const PaintInfo&, const IntRect&);
|
||||
static bool paintMediaSubtitlesIcon(const LayoutObject&, const PaintInfo&, const IntRect&);
|
||||
static void adjustMediaSliderThumbSize(ComputedStyle&);
|
||||
|
||||
private:
|
||||
|
@ -137,6 +137,12 @@ bool ThemePainter::paint(const LayoutObject& o, const PaintInfo& paintInfo, cons
|
||||
case MediaCastOffButtonPart:
|
||||
case MediaOverlayCastOffButtonPart:
|
||||
return MediaControlsPainter::paintMediaCastButton(o, paintInfo, r);
|
||||
case MediaTrackSelectionCheckmarkPart:
|
||||
return MediaControlsPainter::paintMediaTrackSelectionCheckmark(o, paintInfo, r);
|
||||
case MediaClosedCaptionsIconPart:
|
||||
return MediaControlsPainter::paintMediaClosedCaptionsIcon(o, paintInfo, r);
|
||||
case MediaSubtitlesIconPart:
|
||||
return MediaControlsPainter::paintMediaSubtitlesIcon(o, paintInfo, r);
|
||||
case MenulistButtonPart:
|
||||
case TextFieldPart:
|
||||
case TextAreaPart:
|
||||
|
@ -50,7 +50,8 @@ enum ControlPart {
|
||||
MediaEnterFullscreenButtonPart, MediaExitFullscreenButtonPart, MediaFullScreenVolumeSliderPart, MediaFullScreenVolumeSliderThumbPart, MediaMuteButtonPart, MediaPlayButtonPart,
|
||||
MediaOverlayPlayButtonPart, MediaToggleClosedCaptionsButtonPart,
|
||||
MediaSliderPart, MediaSliderThumbPart, MediaVolumeSliderContainerPart, MediaVolumeSliderPart, MediaVolumeSliderThumbPart,
|
||||
MediaControlsBackgroundPart, MediaControlsFullscreenBackgroundPart, MediaCurrentTimePart, MediaTimeRemainingPart, MediaCastOffButtonPart, MediaOverlayCastOffButtonPart,
|
||||
MediaControlsBackgroundPart, MediaControlsFullscreenBackgroundPart, MediaCurrentTimePart, MediaTimeRemainingPart, MediaCastOffButtonPart,
|
||||
MediaOverlayCastOffButtonPart, MediaTrackSelectionCheckmarkPart, MediaClosedCaptionsIconPart, MediaSubtitlesIconPart,
|
||||
MenulistPart, MenulistButtonPart, MenulistTextPart, MenulistTextFieldPart, MeterPart, ProgressBarPart, ProgressBarValuePart,
|
||||
SliderHorizontalPart, SliderVerticalPart, SliderThumbHorizontalPart,
|
||||
SliderThumbVerticalPart, CaretPart, SearchFieldPart, SearchFieldDecorationPart,
|
||||
|
@ -44,6 +44,12 @@
|
||||
<structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_OVERLAY_CAST_BUTTON_ON_NEW" file="blink/mediaplayer_overlay_cast_on_new.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_OVERLAY_PLAY_BUTTON" file="blink/mediaplayer_overlay_play.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_OVERLAY_PLAY_BUTTON_NEW" file="blink/mediaplayer_overlay_play_new.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_TRACKSELECTION_CHECKMARK" file="blink/mediaplayer_trackselection_checkmark.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_TRACKSELECTION_CHECKMARK_NEW" file="blink/mediaplayer_trackselection_checkmark_new.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_CLOSEDCAPTIONS_ICON" file="blink/mediaplayer_closedcaptions_icon.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_CLOSEDCAPTIONS_ICON_NEW" file="blink/mediaplayer_closedcaptions_icon_new.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_SUBTITLES_ICON" file="blink/mediaplayer_subtitles_icon.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_SUBTITLES_ICON_NEW" file="blink/mediaplayer_subtitles_icon_new.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_SEARCH_CANCEL" file="blink/search_cancel.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_SEARCH_CANCEL_PRESSED" file="blink/search_cancel_pressed.png" />
|
||||
<structure type="chrome_scaled_image" name="IDR_SEARCH_MAGNIFIER" file="blink/search_magnifier.png" />
|
||||
|
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_closedcaptions_icon.png
vendored
Normal file
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_closedcaptions_icon.png
vendored
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.9 KiB |
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_closedcaptions_icon_new.png
vendored
Normal file
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_closedcaptions_icon_new.png
vendored
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.2 KiB |
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_subtitles_icon.png
vendored
Normal file
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_subtitles_icon.png
vendored
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.5 KiB |
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_subtitles_icon_new.png
vendored
Normal file
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_subtitles_icon_new.png
vendored
Normal file
Binary file not shown.
After ![]() (image error) Size: 896 B |
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_trackselection_checkmark.png
vendored
Normal file
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_trackselection_checkmark.png
vendored
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.6 KiB |
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_trackselection_checkmark_new.png
vendored
Normal file
BIN
third_party/WebKit/public/default_100_percent/blink/mediaplayer_trackselection_checkmark_new.png
vendored
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.2 KiB |
@ -138,6 +138,8 @@ struct WebLocalizedString {
|
||||
SearchMenuRecentSearchesText, // Deprecated.
|
||||
SelectMenuListText,
|
||||
SubmitButtonDefaultLabel,
|
||||
TextTracksNoLabel,
|
||||
TextTracksOff,
|
||||
ThisMonthButtonLabel,
|
||||
ThisWeekButtonLabel,
|
||||
ValidationBadInputForNumber,
|
||||
|
Reference in New Issue
Block a user