mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-15 03:00:30 +00:00
Bug 1546954 - Clean up Picture-in-Picture keyboard support for the player window. r=JSON_voorhees
This patch does a couple of things, based on trial-and-error, as well as a conversation with yzen from the Accessibility team. 1. Auto-focuses the playpause button, and puts unpip and then close as the next items that can be focused with the keyboard. The underlying <browser> element is no longer keyboard focusable. 2. It adds a white outline with a black box-shadow around the focused control when using keyboard navigation. That outline and box-shadow do not appear when using the mouse only. Keyboard navigation can be aborted by hitting Esc. 3. The controls remain visible while the video is paused. 4. When transitioning from the paused to playing state, we wait CONTROLS_FADE_TIMEOUT_MS milliseconds to hide the controls again. Differential Revision: https://phabricator.services.mozilla.com/D37887 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
fa7b029ef3
commit
b0d89f3530
@ -43,7 +43,7 @@ function setIsPlayingState(isPlaying) {
|
||||
* events for updating state.
|
||||
*/
|
||||
let Player = {
|
||||
WINDOW_EVENTS: ["click", "mouseout", "resize", "unload"],
|
||||
WINDOW_EVENTS: ["click", "keydown", "mouseout", "resize", "unload"],
|
||||
mm: null,
|
||||
/**
|
||||
* Used for resizing Telemetry to avoid recording an event for every resize
|
||||
@ -59,6 +59,12 @@ let Player = {
|
||||
lastScreenY: -1,
|
||||
id: -1,
|
||||
|
||||
/**
|
||||
* When set to a non-null value, a timer is scheduled to hide the controls
|
||||
* after CONTROLS_FADE_TIMEOUT_MS milliseconds.
|
||||
*/
|
||||
showingTimeout: null,
|
||||
|
||||
/**
|
||||
* Initializes the player browser, and sets up the initial state.
|
||||
*
|
||||
@ -93,12 +99,7 @@ let Player = {
|
||||
// just close the window for now.
|
||||
browser.addEventListener("oop-browser-crashed", this);
|
||||
|
||||
// Show the controls immediately, but set them up to fade out after
|
||||
// CONTROLS_FADE_TIMEOUT_MS if the mouse isn't hovering them.
|
||||
this.controls.setAttribute("showing", true);
|
||||
setTimeout(() => {
|
||||
this.controls.removeAttribute("showing");
|
||||
}, CONTROLS_FADE_TIMEOUT_MS);
|
||||
this.revealControls(false);
|
||||
|
||||
Services.telemetry.setEventRecordingEnabled("pictureinpicture", true);
|
||||
|
||||
@ -129,6 +130,16 @@ let Player = {
|
||||
switch (event.type) {
|
||||
case "click": {
|
||||
this.onClick(event);
|
||||
this.controls.removeAttribute("keying");
|
||||
break;
|
||||
}
|
||||
|
||||
case "keydown": {
|
||||
if (event.keyCode == KeyEvent.DOM_VK_TAB) {
|
||||
this.controls.setAttribute("keying", true);
|
||||
} else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) {
|
||||
this.controls.removeAttribute("keying");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -164,9 +175,12 @@ let Player = {
|
||||
case "playpause": {
|
||||
if (!this.isPlaying) {
|
||||
this.mm.sendAsyncMessage("PictureInPicture:Play");
|
||||
this.revealControls(false);
|
||||
} else {
|
||||
this.mm.sendAsyncMessage("PictureInPicture:Pause");
|
||||
this.revealControls(true);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -230,4 +244,25 @@ let Player = {
|
||||
args
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes the player controls visible.
|
||||
*
|
||||
* @param revealIndefinitely (Boolean)
|
||||
* If false, this will hide the controls again after
|
||||
* CONTROLS_FADE_TIMEOUT_MS milliseconds has passed. If true, the controls
|
||||
* will remain visible until revealControls is called again with
|
||||
* revealIndefinitely set to false.
|
||||
*/
|
||||
revealControls(revealIndefinitely) {
|
||||
clearTimeout(this.showingTimeout);
|
||||
this.showingTimeout = null;
|
||||
|
||||
this.controls.setAttribute("showing", true);
|
||||
if (!revealIndefinitely) {
|
||||
this.showingTimeout = setTimeout(() => {
|
||||
this.controls.removeAttribute("showing");
|
||||
}, CONTROLS_FADE_TIMEOUT_MS);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -19,15 +19,15 @@
|
||||
|
||||
<body>
|
||||
<div class="player-holder">
|
||||
<xul:browser type="content" primary="true" remote="true" remoteType="web" id="browser"></xul:browser>
|
||||
<xul:browser type="content" primary="true" remote="true" remoteType="web" id="browser" tabindex="-1"></xul:browser>
|
||||
</div>
|
||||
<div id="controls">
|
||||
<button id="close" class="control-item"></button>
|
||||
<button id="close" class="control-item" tabindex="3"></button>
|
||||
|
||||
<div id="controls-bottom">
|
||||
<button id="unpip" class="control-item"></button>
|
||||
<button id="unpip" class="control-item" tabindex="2"></button>
|
||||
<div id="gap"></div>
|
||||
<button id="playpause" class="control-item"></button>
|
||||
<button id="playpause" class="control-item" tabindex="1" autofocus="true"></button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -8,6 +8,10 @@
|
||||
--resize-margin: 5px;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
@ -38,6 +42,11 @@ browser {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#controls[keying] button:-moz-focusring {
|
||||
outline: 2px solid #fff;
|
||||
box-shadow: 1px 2px 5px #000;
|
||||
}
|
||||
|
||||
#controls-bottom {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
@ -72,10 +81,15 @@ browser {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#controls[keying] .control-item:focus {
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
#controls:hover .control-item {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
#controls[keying] .control-item,
|
||||
#controls[showing] .control-item,
|
||||
#controls:hover .control-item:hover {
|
||||
opacity: 1;
|
||||
|
Loading…
Reference in New Issue
Block a user