зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1592286 - Add URL-mapped policy support to the Picture-in-Picture toggle. r=mstriemer
I went with "policy" rather than "position" since "hidden" isn't really a position. Differential Revision: https://phabricator.services.mozilla.com/D57177 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
016cd664f6
Коммит
8416f879eb
|
@ -16,6 +16,20 @@ ChromeUtils.defineModuleGetter(
|
|||
"Services",
|
||||
"resource://gre/modules/Services.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"TOGGLE_POLICIES",
|
||||
"resource://gre/modules/PictureInPictureTogglePolicy.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"TOGGLE_POLICY_STRINGS",
|
||||
"resource://gre/modules/PictureInPictureTogglePolicy.jsm"
|
||||
);
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
const TOGGLE_ENABLED_PREF =
|
||||
"media.videocontrols.picture-in-picture.video-toggle.enabled";
|
||||
|
@ -35,6 +49,14 @@ var gWeakPlayerContent = null;
|
|||
// mouseover
|
||||
var gWeakIntersectingVideosForTesting = new WeakSet();
|
||||
|
||||
// Overrides are expected to stay constant for the lifetime of a
|
||||
// content process, so we set this as a lazy process global.
|
||||
// See PictureInPictureToggleChild.getToggleOverrides for a sense
|
||||
// of what the return type is.
|
||||
XPCOMUtils.defineLazyGetter(this, "gToggleOverrides", () => {
|
||||
return PictureInPictureToggleChild.getToggleOverrides();
|
||||
});
|
||||
|
||||
/**
|
||||
* The PictureInPictureToggleChild is responsible for displaying the overlaid
|
||||
* Picture-in-Picture toggle over top of <video> elements that the mouse is
|
||||
|
@ -120,6 +142,8 @@ class PictureInPictureToggleChild extends JSWindowActorChild {
|
|||
// then this will be true. If there are no videos worth tracking, then
|
||||
// this is false.
|
||||
isTrackingVideos: false,
|
||||
togglePolicy: TOGGLE_POLICIES.DEFAULT,
|
||||
hasCheckedPolicy: false,
|
||||
};
|
||||
this.weakDocStates.set(this.document, state);
|
||||
}
|
||||
|
@ -659,8 +683,38 @@ class PictureInPictureToggleChild extends JSWindowActorChild {
|
|||
return;
|
||||
}
|
||||
|
||||
let state = this.docState;
|
||||
let toggle = shadowRoot.getElementById("pictureInPictureToggleButton");
|
||||
let controlsOverlay = shadowRoot.querySelector(".controlsOverlay");
|
||||
|
||||
if (!state.hasCheckedPolicy) {
|
||||
// We cache the matchers process-wide. We'll skip this while running tests to make that
|
||||
// easier.
|
||||
let toggleOverrides = this.toggleTesting
|
||||
? PictureInPictureToggleChild.getToggleOverrides()
|
||||
: gToggleOverrides;
|
||||
|
||||
// Do we have any toggle overrides? If so, try to apply them.
|
||||
for (let [override, policy] of toggleOverrides) {
|
||||
if (override.matches(this.document.documentURI)) {
|
||||
state.togglePolicy = policy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state.hasCheckedPolicy = true;
|
||||
}
|
||||
|
||||
// The built-in <video> controls are along the bottom, which would overlap the
|
||||
// toggle if the override is set to BOTTOM, so we ignore overrides that set
|
||||
// a policy of BOTTOM for <video> elements with controls.
|
||||
if (
|
||||
state.togglePolicy != TOGGLE_POLICIES.DEFAULT &&
|
||||
!(state.togglePolicy == TOGGLE_POLICIES.BOTTOM && video.controls)
|
||||
) {
|
||||
toggle.setAttribute("policy", TOGGLE_POLICY_STRINGS[state.togglePolicy]);
|
||||
}
|
||||
|
||||
controlsOverlay.removeAttribute("hidetoggle");
|
||||
|
||||
// The hideToggleDeferredTask we create here is for automatically hiding
|
||||
|
@ -670,7 +724,6 @@ class PictureInPictureToggleChild extends JSWindowActorChild {
|
|||
//
|
||||
// We disable the toggle hiding timeout during testing to reduce
|
||||
// non-determinism from timers when testing the toggle.
|
||||
let state = this.docState;
|
||||
if (!state.hideToggleDeferredTask && !this.toggleTesting) {
|
||||
state.hideToggleDeferredTask = new DeferredTask(() => {
|
||||
controlsOverlay.setAttribute("hidetoggle", true);
|
||||
|
@ -804,6 +857,27 @@ class PictureInPictureToggleChild extends JSWindowActorChild {
|
|||
static isTracking(video) {
|
||||
return gWeakIntersectingVideosForTesting.has(video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any Picture-in-Picture toggle overrides stored in the sharedData
|
||||
* struct, and returns them as an Array of two-element Arrays, where the first
|
||||
* element is a MatchPattern and the second element is a policy.
|
||||
*
|
||||
* @returns {Array<Array<2>>} Array of 2-element Arrays where the first element
|
||||
* is a MatchPattern and the second element is a Number representing a toggle
|
||||
* policy.
|
||||
*/
|
||||
static getToggleOverrides() {
|
||||
let result = [];
|
||||
let patterns = Services.cpmm.sharedData.get(
|
||||
"PictureInPicture:ToggleOverrides"
|
||||
);
|
||||
for (let pattern in patterns) {
|
||||
let matcher = new MatchPattern(pattern);
|
||||
result.push([matcher, patterns[pattern]]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class PictureInPictureChild extends JSWindowActorChild {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["TOGGLE_POLICIES", "TOGGLE_POLICY_STRINGS"];
|
||||
|
||||
// These are the possible toggle positions along the right side of
|
||||
// a qualified video element.
|
||||
this.TOGGLE_POLICIES = {
|
||||
DEFAULT: 1,
|
||||
HIDDEN: 2,
|
||||
TOP: 3,
|
||||
ONE_QUARTER: 4,
|
||||
THREE_QUARTERS: 5,
|
||||
BOTTOM: 6,
|
||||
};
|
||||
|
||||
// These strings are used in the videocontrols.css stylesheet as
|
||||
// toggle policy attribute values for setting rules on the position
|
||||
// of the toggle.
|
||||
this.TOGGLE_POLICY_STRINGS = {
|
||||
[TOGGLE_POLICIES.DEFAULT]: "default",
|
||||
[TOGGLE_POLICIES.HIDDEN]: "hidden",
|
||||
[TOGGLE_POLICIES.TOP]: "top",
|
||||
[TOGGLE_POLICIES.ONE_QUARTER]: "one-quarter",
|
||||
[TOGGLE_POLICIES.THREE_QUARTERS]: "three-quarters",
|
||||
[TOGGLE_POLICIES.BOTTOM]: "bottom",
|
||||
};
|
|
@ -8,6 +8,7 @@ JAR_MANIFESTS += ['jar.mn']
|
|||
|
||||
EXTRA_JS_MODULES += [
|
||||
'PictureInPicture.jsm',
|
||||
'PictureInPictureTogglePolicy.jsm',
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
|
|
|
@ -44,6 +44,7 @@ skip-if = true # Bug 1546455
|
|||
[browser_toggleOpaqueOverlay.js]
|
||||
skip-if = true # Bug 1546455
|
||||
[browser_togglePointerEventsNone.js]
|
||||
[browser_togglePolicies.js]
|
||||
[browser_toggleSimple.js]
|
||||
skip-if = os == 'linux' # Bug 1546455
|
||||
[browser_toggleTransparentOverlay-1.js]
|
||||
|
|
|
@ -21,7 +21,7 @@ add_task(async () => {
|
|||
await ensureVideosReady(browser);
|
||||
let videoID = "no-controls";
|
||||
|
||||
let { toggleClientRect } = await prepareForToggleClick(browser, videoID);
|
||||
await prepareForToggleClick(browser, videoID);
|
||||
|
||||
// Hover the mouse over the video to reveal the toggle, which is necessary
|
||||
// if we want to click on the toggle.
|
||||
|
@ -47,6 +47,8 @@ add_task(async () => {
|
|||
HOVER_VIDEO_OPACITY
|
||||
);
|
||||
|
||||
let toggleClientRect = await getToggleClientRect(browser, videoID);
|
||||
|
||||
// The toggle center, because of how it slides out, is actually outside
|
||||
// of the bounds of a click event. For now, we move the mouse in by a
|
||||
// hard-coded 2 pixels along the x and y axis to achieve the hover.
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const SHARED_DATA_KEY = "PictureInPicture:ToggleOverrides";
|
||||
|
||||
const { TOGGLE_POLICIES } = ChromeUtils.import(
|
||||
"resource://gre/modules/PictureInPictureTogglePolicy.jsm"
|
||||
);
|
||||
|
||||
/**
|
||||
* Tests that by setting a Picture-in-Picture toggle position policy
|
||||
* in the sharedData structure, that the toggle position can be
|
||||
* change for a particular URI.
|
||||
*/
|
||||
add_task(async () => {
|
||||
let positionPolicies = [
|
||||
TOGGLE_POLICIES.TOP,
|
||||
TOGGLE_POLICIES.ONE_QUARTER,
|
||||
TOGGLE_POLICIES.THREE_QUARTERS,
|
||||
TOGGLE_POLICIES.BOTTOM,
|
||||
];
|
||||
|
||||
for (let policy of positionPolicies) {
|
||||
Services.ppmm.sharedData.set(SHARED_DATA_KEY, {
|
||||
"*://example.com/*": policy,
|
||||
});
|
||||
Services.ppmm.sharedData.flush();
|
||||
|
||||
let expectations = {
|
||||
"with-controls": { canToggle: true, policy },
|
||||
"no-controls": { canToggle: true, policy },
|
||||
};
|
||||
|
||||
// For <video> elements with controls, the video controls overlap the
|
||||
// toggle when its on the bottom and can't be clicked, so we'll ignore
|
||||
// that case.
|
||||
if (policy == TOGGLE_POLICIES.BOTTOM) {
|
||||
expectations["with-controls"] = { canToggle: true };
|
||||
}
|
||||
|
||||
await testToggle(TEST_PAGE, expectations);
|
||||
|
||||
// And ensure that other pages aren't affected by this override.
|
||||
await testToggle(TEST_PAGE_2, {
|
||||
"with-controls": { canToggle: true },
|
||||
"no-controls": { canToggle: true },
|
||||
});
|
||||
}
|
||||
|
||||
Services.ppmm.sharedData.set(SHARED_DATA_KEY, {});
|
||||
Services.ppmm.sharedData.flush();
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that by setting a Picture-in-Picture toggle hidden policy
|
||||
* in the sharedData structure, that the toggle can be suppressed.
|
||||
*/
|
||||
add_task(async () => {
|
||||
Services.ppmm.sharedData.set(SHARED_DATA_KEY, {
|
||||
"*://example.com/*": TOGGLE_POLICIES.HIDDEN,
|
||||
});
|
||||
Services.ppmm.sharedData.flush();
|
||||
|
||||
await testToggle(TEST_PAGE, {
|
||||
"with-controls": { canToggle: false, policy: TOGGLE_POLICIES.HIDDEN },
|
||||
"no-controls": { canToggle: false, policy: TOGGLE_POLICIES.HIDDEN },
|
||||
});
|
||||
|
||||
// And ensure that other pages aren't affected by this override.
|
||||
await testToggle(TEST_PAGE_2, {
|
||||
"with-controls": { canToggle: true },
|
||||
"no-controls": { canToggle: true },
|
||||
});
|
||||
|
||||
Services.ppmm.sharedData.set(SHARED_DATA_KEY, {});
|
||||
Services.ppmm.sharedData.flush();
|
||||
});
|
|
@ -12,6 +12,7 @@ const TEST_ROOT_2 = getRootDirectory(gTestPath).replace(
|
|||
"http://example.org"
|
||||
);
|
||||
const TEST_PAGE = TEST_ROOT + "test-page.html";
|
||||
const TEST_PAGE_2 = TEST_ROOT_2 + "test-page.html";
|
||||
const TEST_PAGE_WITH_IFRAME = TEST_ROOT_2 + "test-page-with-iframe.html";
|
||||
const WINDOW_TYPE = "Toolkit:PictureInPicture";
|
||||
const TOGGLE_ID = "pictureInPictureToggleButton";
|
||||
|
@ -129,6 +130,7 @@ async function toggleOpacityReachesThreshold(
|
|||
let args = { videoID, TOGGLE_ID, opacityThreshold };
|
||||
await SpecialPowers.spawn(browser, [args], async args => {
|
||||
let { videoID, TOGGLE_ID, opacityThreshold } = args;
|
||||
|
||||
let video = content.document.getElementById(videoID);
|
||||
let shadowRoot = video.openOrClosedShadowRoot;
|
||||
let toggle = shadowRoot.getElementById(TOGGLE_ID);
|
||||
|
@ -147,6 +149,54 @@ async function toggleOpacityReachesThreshold(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the toggle has the correct policy attribute set. This should be called
|
||||
* either when the toggle is visible, or events have been queued such that the toggle
|
||||
* will soon be visible.
|
||||
*
|
||||
* @param {Element} browser The <xul:browser> that has the <video> in it.
|
||||
* @param {String} videoID The ID of the video element that we expect the toggle
|
||||
* to appear on.
|
||||
* @param {Number} policy Optional argument. If policy is defined, then it should
|
||||
* be one of the values in the TOGGLE_POLICIES from PictureInPictureTogglePolicy.jsm.
|
||||
* If undefined, this function will ensure no policy attribute is set.
|
||||
*
|
||||
* @return Promise
|
||||
* @resolves When the check has completed.
|
||||
*/
|
||||
async function assertTogglePolicy(browser, videoID, policy) {
|
||||
let args = { videoID, TOGGLE_ID, policy };
|
||||
await SpecialPowers.spawn(browser, [args], async args => {
|
||||
let { videoID, TOGGLE_ID, policy } = args;
|
||||
|
||||
let video = content.document.getElementById(videoID);
|
||||
let shadowRoot = video.openOrClosedShadowRoot;
|
||||
let controlsOverlay = shadowRoot.querySelector(".controlsOverlay");
|
||||
let toggle = shadowRoot.getElementById(TOGGLE_ID);
|
||||
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
return controlsOverlay.classList.contains("hovering");
|
||||
}, "Waiting for the hovering state to be set on the video.");
|
||||
|
||||
if (policy) {
|
||||
const { TOGGLE_POLICY_STRINGS } = ChromeUtils.import(
|
||||
"resource://gre/modules/PictureInPictureTogglePolicy.jsm"
|
||||
);
|
||||
let policyAttr = toggle.getAttribute("policy");
|
||||
Assert.equal(
|
||||
policyAttr,
|
||||
TOGGLE_POLICY_STRINGS[policy],
|
||||
"The correct toggle policy is set."
|
||||
);
|
||||
} else {
|
||||
Assert.ok(
|
||||
!toggle.hasAttribute("policy"),
|
||||
"No toggle policy should be set."
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that either all or none of the expected mousebutton events
|
||||
* fire in web content when clicking on the page.
|
||||
|
@ -202,29 +252,21 @@ async function assertSawMouseEvents(
|
|||
* @return Promise
|
||||
* @resolves With the following Object structure:
|
||||
* {
|
||||
* toggleClientRect: {
|
||||
* top: <Number>,
|
||||
* right: <Number>,
|
||||
* left: <Number>,
|
||||
* bottom: <Number>,
|
||||
* },
|
||||
* controls: <Boolean>,
|
||||
* }
|
||||
*
|
||||
* Where toggleClientRect represents the client rectangle that the toggle is
|
||||
* positioned in, and controls represents whether or not the video has the
|
||||
* default control set displayed.
|
||||
* Where controls represents whether or not the video has the default control set
|
||||
* displayed.
|
||||
*/
|
||||
async function prepareForToggleClick(browser, videoID) {
|
||||
// For each video, make sure it's scrolled into view, and get the rect for
|
||||
// the toggle while we're at it.
|
||||
let args = { videoID, TOGGLE_ID };
|
||||
let args = { videoID };
|
||||
return SpecialPowers.spawn(browser, [args], async args => {
|
||||
let { videoID, TOGGLE_ID } = args;
|
||||
let { videoID } = args;
|
||||
|
||||
let video = content.document.getElementById(videoID);
|
||||
video.scrollIntoView({ behaviour: "instant" });
|
||||
let shadowRoot = video.openOrClosedShadowRoot;
|
||||
let toggle = shadowRoot.getElementById(TOGGLE_ID);
|
||||
|
||||
if (!video.controls) {
|
||||
// For no-controls <video> elements, an IntersectionObserver is used
|
||||
|
@ -244,19 +286,52 @@ async function prepareForToggleClick(browser, videoID) {
|
|||
100
|
||||
);
|
||||
}
|
||||
let rect = toggle.getBoundingClientRect();
|
||||
|
||||
return {
|
||||
toggleClientRect: {
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
left: rect.left,
|
||||
bottom: rect.bottom,
|
||||
},
|
||||
controls: video.controls,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client rect for the toggle if it's supposed to be visible
|
||||
* on hover. Otherwise, returns the client rect for the video with the
|
||||
* associated ID.
|
||||
*
|
||||
* @param {Element} browser The <xul:browser> that has the <video> loaded in it.
|
||||
* @param {String} videoID The ID of the video that has the toggle.
|
||||
*
|
||||
* @return Promise
|
||||
* @resolves With the following Object structure:
|
||||
* {
|
||||
* top: <Number>,
|
||||
* right: <Number>,
|
||||
* left: <Number>,
|
||||
* bottom: <Number>,
|
||||
* }
|
||||
*/
|
||||
async function getToggleClientRect(browser, videoID) {
|
||||
let args = { videoID, TOGGLE_ID };
|
||||
return ContentTask.spawn(browser, args, async args => {
|
||||
let { videoID, TOGGLE_ID } = args;
|
||||
let video = content.document.getElementById(videoID);
|
||||
let shadowRoot = video.openOrClosedShadowRoot;
|
||||
let toggle = shadowRoot.getElementById(TOGGLE_ID);
|
||||
let rect = toggle.getBoundingClientRect();
|
||||
|
||||
if (!rect.width && !rect.height) {
|
||||
rect = video.getBoundingClientRect();
|
||||
}
|
||||
|
||||
return {
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
left: rect.left,
|
||||
bottom: rect.bottom,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test helper for the Picture-in-Picture toggle. Loads a page, and then
|
||||
* tests the provided video elements for the toggle both appearing and
|
||||
|
@ -266,11 +341,16 @@ async function prepareForToggleClick(browser, videoID) {
|
|||
* @param {Object} expectations An object with the following schema:
|
||||
* <video-element-id>: {
|
||||
* canToggle: Boolean
|
||||
* policy: Number (optional)
|
||||
* }
|
||||
* If canToggle is true, then it's expected that moving the mouse over the
|
||||
* video and then clicking in the toggle region should open a
|
||||
* Picture-in-Picture window. If canToggle is false, we expect that a click
|
||||
* in this region will not result in the window opening.
|
||||
*
|
||||
* If policy is defined, then it should be one of the values in the
|
||||
* TOGGLE_POLICIES from PictureInPictureTogglePolicy.jsm.
|
||||
*
|
||||
* @param {async Function} prepFn An optional asynchronous function to run
|
||||
* before running the toggle test. The function is passed the opened
|
||||
* <xul:browser> as its only argument once the testURL has finished loading.
|
||||
|
@ -289,11 +369,13 @@ async function testToggle(testURL, expectations, prepFn = async () => {}) {
|
|||
await prepFn(browser);
|
||||
await ensureVideosReady(browser);
|
||||
|
||||
for (let [videoID, { canToggle }] of Object.entries(expectations)) {
|
||||
for (let [videoID, { canToggle, policy }] of Object.entries(
|
||||
expectations
|
||||
)) {
|
||||
await SimpleTest.promiseFocus(browser);
|
||||
info(`Testing video with id: ${videoID}`);
|
||||
|
||||
await testToggleHelper(browser, videoID, canToggle);
|
||||
await testToggleHelper(browser, videoID, canToggle, policy);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -308,15 +390,14 @@ async function testToggle(testURL, expectations, prepFn = async () => {}) {
|
|||
* @param {String} videoID The ID of the video that has the toggle.
|
||||
* @param {Boolean} canToggle True if we expect the toggle to be visible and
|
||||
* clickable by the mouse for the associated video.
|
||||
* @param {Number} policy Optional argument. If policy is defined, then it should
|
||||
* be one of the values in the TOGGLE_POLICIES from PictureInPictureTogglePolicy.jsm.
|
||||
*
|
||||
* @return Promise
|
||||
* @resolves When the check for the toggle is complete.
|
||||
*/
|
||||
async function testToggleHelper(browser, videoID, canToggle) {
|
||||
let { toggleClientRect, controls } = await prepareForToggleClick(
|
||||
browser,
|
||||
videoID
|
||||
);
|
||||
async function testToggleHelper(browser, videoID, canToggle, policy) {
|
||||
let { controls } = await prepareForToggleClick(browser, videoID);
|
||||
|
||||
// Hover the mouse over the video to reveal the toggle.
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
|
@ -334,11 +415,21 @@ async function testToggleHelper(browser, videoID, canToggle) {
|
|||
browser
|
||||
);
|
||||
|
||||
info("Checking toggle policy");
|
||||
await assertTogglePolicy(browser, videoID, policy);
|
||||
|
||||
if (canToggle) {
|
||||
info("Waiting for toggle to become visible");
|
||||
await toggleOpacityReachesThreshold(browser, videoID, HOVER_VIDEO_OPACITY);
|
||||
await toggleOpacityReachesThreshold(
|
||||
browser,
|
||||
videoID,
|
||||
HOVER_VIDEO_OPACITY,
|
||||
policy
|
||||
);
|
||||
}
|
||||
|
||||
let toggleClientRect = await getToggleClientRect(browser, videoID);
|
||||
|
||||
info("Hovering the toggle rect now.");
|
||||
// The toggle center, because of how it slides out, is actually outside
|
||||
// of the bounds of a click event. For now, we move the mouse in by a
|
||||
|
@ -364,7 +455,12 @@ async function testToggleHelper(browser, videoID, canToggle) {
|
|||
|
||||
if (canToggle) {
|
||||
info("Waiting for toggle to reach full opacity");
|
||||
await toggleOpacityReachesThreshold(browser, videoID, HOVER_TOGGLE_OPACITY);
|
||||
await toggleOpacityReachesThreshold(
|
||||
browser,
|
||||
videoID,
|
||||
HOVER_TOGGLE_OPACITY,
|
||||
policy
|
||||
);
|
||||
}
|
||||
|
||||
// First, ensure that a non-primary mouse click is ignored.
|
||||
|
@ -377,16 +473,13 @@ async function testToggleHelper(browser, videoID, canToggle) {
|
|||
browser
|
||||
);
|
||||
|
||||
if (canToggle) {
|
||||
// For videos without the built-in controls, we expect that all mouse events
|
||||
// should have fired - otherwise, the events are all suppressed.
|
||||
// Note that the right-click does not result in a "click" event firing.
|
||||
await assertSawMouseEvents(browser, !controls, false);
|
||||
} else {
|
||||
// If we aren't showing the toggle, we expect all mouse events to be seen.
|
||||
// Note that the right-click does not result in a "click" event firing.
|
||||
await assertSawMouseEvents(browser, true, false);
|
||||
}
|
||||
// For videos without the built-in controls, we expect that all mouse events
|
||||
// should have fired - otherwise, the events are all suppressed. For videos
|
||||
// with controls, none of the events should be fired, as the controls overlay
|
||||
// absorbs them all.
|
||||
//
|
||||
// Note that the right-click does not result in a "click" event firing.
|
||||
await assertSawMouseEvents(browser, !controls, false);
|
||||
|
||||
// The message to open the Picture-in-Picture window would normally be sent
|
||||
// immediately before this Promise resolved, so the window should have opened
|
||||
|
@ -432,7 +525,7 @@ async function testToggleHelper(browser, videoID, canToggle) {
|
|||
);
|
||||
|
||||
// If we aren't showing the toggle, we expect all mouse events to be seen.
|
||||
await assertSawMouseEvents(browser, true);
|
||||
await assertSawMouseEvents(browser, !controls);
|
||||
|
||||
// The message to open the Picture-in-Picture window would normally be sent
|
||||
// immediately before this Promise resolved, so the window should have opened
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
--pip-toggle-text-and-icon-color: rgb(255, 255, 255);
|
||||
--pip-toggle-padding: 5px;
|
||||
--pip-toggle-icon-width-height: 16px;
|
||||
--pip-toggle-translate-x: calc(100% - var(--pip-toggle-icon-width-height) - 2 * var(--pip-toggle-padding));
|
||||
}
|
||||
.controlsContainer.touch {
|
||||
--clickToPlay-size: 64px;
|
||||
|
@ -449,13 +450,35 @@
|
|||
padding: var(--pip-toggle-padding);
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) translateX(calc(100% - var(--pip-toggle-icon-width-height) - 2 * var(--pip-toggle-padding)));
|
||||
transition: opacity 160ms linear, transform 160ms linear;
|
||||
translate: var(--pip-toggle-translate-x) -50%;
|
||||
transition: opacity 160ms linear, translate 160ms linear;
|
||||
min-width: max-content;
|
||||
pointer-events: auto;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.pictureInPictureToggleButton[policy="hidden"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pictureInPictureToggleButton[policy="top"] {
|
||||
top: 0%;
|
||||
translate: var(--pip-toggle-translate-x);
|
||||
}
|
||||
|
||||
.pictureInPictureToggleButton[policy="one-quarter"] {
|
||||
top: 25%;
|
||||
}
|
||||
|
||||
.pictureInPictureToggleButton[policy="three-quarters"] {
|
||||
top: 75%;
|
||||
}
|
||||
|
||||
.pictureInPictureToggleButton[policy="bottom"] {
|
||||
top: 100%;
|
||||
translate: var(--pip-toggle-translate-x) -100%;
|
||||
}
|
||||
|
||||
.pictureInPictureToggleIcon {
|
||||
display: inline-block;
|
||||
background-image: url(chrome://global/skin/media/pictureinpicture.svg);
|
||||
|
@ -496,7 +519,15 @@
|
|||
|
||||
.controlsOverlay.hovering > .pictureInPictureToggleButton.hovering {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) translateX(0);
|
||||
translate: 0 -50%;
|
||||
}
|
||||
|
||||
.controlsOverlay.hovering > .pictureInPictureToggleButton.hovering[policy="top"] {
|
||||
translate: 0;
|
||||
}
|
||||
|
||||
.controlsOverlay.hovering > .pictureInPictureToggleButton.hovering[policy="bottom"] {
|
||||
translate: 0 -100%
|
||||
}
|
||||
|
||||
@supports -moz-bool-pref("media.videocontrols.picture-in-picture.video-toggle.testing") {
|
||||
|
|
Загрузка…
Ссылка в новой задаче