Skip to content

Commit

Permalink
Bug 1811318 - Urlbar entry point for PiP. r=cmkm,pip-reviewers,deskto…
Browse files Browse the repository at this point in the history
…p-theme-reviewers,dao,mconley,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D170899
  • Loading branch information
Niklas Baumgardner committed Mar 21, 2023
1 parent 6bd1264 commit 5cef412
Show file tree
Hide file tree
Showing 12 changed files with 405 additions and 7 deletions.
1 change: 1 addition & 0 deletions browser/app/profile/firefox.js
Original file line number Diff line number Diff line change
Expand Up @@ -1826,6 +1826,7 @@ pref("media.videocontrols.picture-in-picture.audio-toggle.enabled", true);
pref("media.videocontrols.picture-in-picture.video-toggle.enabled", true);
pref("media.videocontrols.picture-in-picture.video-toggle.visibility-threshold", "1.0");
pref("media.videocontrols.picture-in-picture.keyboard-controls.enabled", true);
pref("media.videocontrols.picture-in-picture.urlbar-button.enabled", true);

// Preferences for the older translation service backed by external services. This is
// planned to be replaced with an integration of the Firefox Translations service.
Expand Down
2 changes: 2 additions & 0 deletions browser/base/content/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -5359,6 +5359,8 @@ var XULBrowserWindow = {

AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);

PictureInPicture.updateUrlbarToggle(gBrowser.selectedBrowser);

if (!gMultiProcessBrowser) {
// Bug 1108553 - Cannot rotate images with e10s
gGestureSupport.restoreRotationState();
Expand Down
9 changes: 9 additions & 0 deletions browser/base/content/navigator-toolbox.inc.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,15 @@
<image id="reader-mode-button-icon"
class="urlbar-icon"/>
</hbox>
<hbox id="picture-in-picture-button"
class="urlbar-page-action"
role="button"
hidden="true"
data-l10n-id="picture-in-picture-urlbar-button"
onclick="PictureInPicture.toggleUrlbar(event)">
<image id="picture-in-picture-button-icon"
class="urlbar-icon"/>
</hbox>
<toolbarbutton id="urlbar-zoom-button"
onclick="FullZoom.reset(); FullZoom.resetScalingZoom();"
tooltip="dynamic-shortcut-tooltip"
Expand Down
5 changes: 5 additions & 0 deletions browser/locales/en-US/browser/browser.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,11 @@ reader-view-enter-button =
reader-view-close-button =
.aria-label = Close Reader View
## Picture-in-Picture urlbar button

picture-in-picture-urlbar-button =
.tooltiptext = Toggle Picture-in-Picture
## Full Screen and Pointer Lock UI

# Please ensure that the domain stays in the `<span data-l10n-name="domain">` markup.
Expand Down
13 changes: 13 additions & 0 deletions browser/themes/shared/urlbar-searchbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,19 @@
fill-opacity: 1;
}

/* Picture-in-Picture icon */
#picture-in-picture-button > .urlbar-icon {
list-style-image: url("chrome://global/skin/media/picture-in-picture-open.svg");
}

#picture-in-picture-button[pipactive] > .urlbar-icon {
list-style-image: url("chrome://global/skin/media/picture-in-picture-closed.svg");
}

#picture-in-picture-button:-moz-locale-dir(rtl) > .urlbar-icon {
transform: scaleX(-1);
}

/* Zoom button */

#urlbar-zoom-button {
Expand Down
127 changes: 127 additions & 0 deletions toolkit/actors/PictureInPictureChild.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,24 @@ XPCOMUtils.defineLazyPreferenceGetter(
"media.videocontrols.picture-in-picture.improved-video-controls.enabled",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"MIN_VIDEO_LENGTH",
"media.videocontrols.picture-in-picture.video-toggle.min-video-secs",
45
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"PIP_TOGGLE_ALWAYS_SHOW",
"media.videocontrols.picture-in-picture.video-toggle.always-show",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"PIP_URLBAR_BUTTON",
"media.videocontrols.picture-in-picture.urlbar-button.enabled",
false
);

const TOGGLE_ENABLED_PREF =
"media.videocontrols.picture-in-picture.video-toggle.enabled";
Expand Down Expand Up @@ -260,6 +278,18 @@ export class PictureInPictureToggleChild extends JSWindowActorChild {
Services.prefs.addObserver(TOGGLE_ENABLED_PREF, this.observerFunction);
Services.prefs.addObserver(PIP_ENABLED_PREF, this.observerFunction);
Services.cpmm.sharedData.addEventListener("change", this);

this.eligiblePipVideos = new WeakSet();
}

receiveMessage(message) {
switch (message.name) {
case "PictureInPicture:UrlbarToggle": {
this.urlbarToggle();
break;
}
}
return null;
}

didDestroy() {
Expand All @@ -278,6 +308,14 @@ export class PictureInPictureToggleChild extends JSWindowActorChild {
this.videoWrapper?.destroy();
this.videoWrapper = null;

for (let video of ChromeUtils.nondeterministicGetWeakSetKeys(
this.eligiblePipVideos
)) {
video.removeEventListener("emptied", this);
video.removeEventListener("loadedmetadata", this);
video.removeEventListener("durationchange", this);
}

// ensure we don't access the state
this.isDestroyed = true;
}
Expand Down Expand Up @@ -476,6 +514,14 @@ export class PictureInPictureToggleChild extends JSWindowActorChild {
this.onPageHide(event);
break;
}
case "durationchange":
// Intentional fall-through
case "emptied":
// Intentional fall-through
case "loadedmetadata": {
this.updatePipVideoEligibility(event.target);
break;
}
}
}

Expand All @@ -498,6 +544,87 @@ export class PictureInPictureToggleChild extends JSWindowActorChild {
}

state.intersectionObserver.observe(video);

this.updatePipVideoEligibility(video);
}

updatePipVideoEligibility(video) {
if (this.isVideoPiPEligible(video)) {
if (!this.eligiblePipVideos.has(video)) {
this.eligiblePipVideos.add(video);

let mutationObserver = new this.contentWindow.MutationObserver(
mutationList => {
this.handleEligiblePipVideoMutation(mutationList);
}
);
mutationObserver.observe(video.parentElement, { childList: true });

this.sendAsyncMessage("PictureInPicture:UpdateEligiblePipVideoCount", {
count: ChromeUtils.nondeterministicGetWeakSetKeys(
this.eligiblePipVideos
).length,
});
}
}
}

handleEligiblePipVideoMutation(mutationList) {
for (let mutationRecord of mutationList) {
let video = mutationRecord.removedNodes[0];
this.eligiblePipVideos.delete(video);
}

this.sendAsyncMessage("PictureInPicture:UpdateEligiblePipVideoCount", {
count: ChromeUtils.nondeterministicGetWeakSetKeys(this.eligiblePipVideos)
.length,
});
}

urlbarToggle() {
let video = ChromeUtils.nondeterministicGetWeakSetKeys(
this.eligiblePipVideos
)[0];
if (video) {
let pipEvent = new this.contentWindow.CustomEvent(
"MozTogglePictureInPicture",
{
bubbles: true,
}
);
video.dispatchEvent(pipEvent);
}
}

isVideoPiPEligible(video) {
if (!lazy.PIP_URLBAR_BUTTON) {
return false;
}

if (lazy.PIP_TOGGLE_ALWAYS_SHOW) {
return true;
}

if (isNaN(video.duration)) {
video.addEventListener("emptied", this);
video.addEventListener("loadedmetadata", this);
video.addEventListener("durationchange", this);
return false;
}

if (video.duration < lazy.MIN_VIDEO_LENGTH) {
return false;
}

const MIN_VIDEO_DIMENSION = 140; // pixels
if (
video.clientWidth < MIN_VIDEO_DIMENSION ||
video.clientHeight < MIN_VIDEO_DIMENSION
) {
return false;
}

return true;
}

/**
Expand Down
Loading

0 comments on commit 5cef412

Please sign in to comment.