mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-28 03:17:31 +09:00
Compare commits
No commits in common. "14b7011d74fe868179068bff52fdcd76b96e67dd" and "5ec3ae4a4cc9c6c602b934d958319ef343ebf2fb" have entirely different histories.
14b7011d74
...
5ec3ae4a4c
24 changed files with 4 additions and 874 deletions
|
|
@ -159,9 +159,6 @@
|
||||||
label="&mediaHideControls.label;"
|
label="&mediaHideControls.label;"
|
||||||
accesskey="&mediaHideControls.accesskey;"
|
accesskey="&mediaHideControls.accesskey;"
|
||||||
oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
|
oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
|
||||||
<menuitem id="context-video-pictureinpicture"
|
|
||||||
label="&pictureInPicture.title;"
|
|
||||||
oncommand="gContextMenu.pictureInPicture();"/>
|
|
||||||
<menuitem id="context-video-fullscreen"
|
<menuitem id="context-video-fullscreen"
|
||||||
accesskey="&videoFullScreen.accesskey;"
|
accesskey="&videoFullScreen.accesskey;"
|
||||||
label="&videoFullScreen.label;"
|
label="&videoFullScreen.label;"
|
||||||
|
|
|
||||||
|
|
@ -681,17 +681,6 @@ addMessageListener("ContextMenu:MediaCommand", (message) => {
|
||||||
() => {
|
() => {
|
||||||
let media = message.objects.element;
|
let media = message.objects.element;
|
||||||
switch (message.data.command) {
|
switch (message.data.command) {
|
||||||
case "pictureinpicture": {
|
|
||||||
let {PictureInPicture} = Cu.import("resource:///modules/PictureInPicture.jsm", {});
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
data = PictureInPicture.register(media, message.data.data);
|
|
||||||
} catch (error) {
|
|
||||||
data = {id: message.data.data, error: error.message};
|
|
||||||
}
|
|
||||||
sendAsyncMessage("PictureInPicture:Prepared", data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "play":
|
case "play":
|
||||||
media.play();
|
media.play();
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -442,11 +442,6 @@ nsContextMenu.prototype = {
|
||||||
this.showItem("context-media-loop", onMedia);
|
this.showItem("context-media-loop", onMedia);
|
||||||
this.showItem("context-media-showcontrols", onMedia && !this.target.controls);
|
this.showItem("context-media-showcontrols", onMedia && !this.target.controls);
|
||||||
this.showItem("context-media-hidecontrols", this.target.controls && (this.onVideo || (this.onAudio && !this.inSyntheticDoc)));
|
this.showItem("context-media-hidecontrols", this.target.controls && (this.onVideo || (this.onAudio && !this.inSyntheticDoc)));
|
||||||
this.showItem("context-video-pictureinpicture", this.onVideo);
|
|
||||||
this.setItemAttr("context-video-pictureinpicture", "disabled",
|
|
||||||
!this.onVideo || this.target.error ||
|
|
||||||
!this.target.videoWidth || !this.target.videoHeight ||
|
|
||||||
!!this.target.mediaKeys);
|
|
||||||
this.showItem("context-video-fullscreen", this.onVideo && this.target.ownerDocument.fullscreenElement == null);
|
this.showItem("context-video-fullscreen", this.onVideo && this.target.ownerDocument.fullscreenElement == null);
|
||||||
this.showItem("context-media-eme-learnmore", this.onDRMMedia);
|
this.showItem("context-media-eme-learnmore", this.onDRMMedia);
|
||||||
this.showItem("context-media-eme-separator", this.onDRMMedia);
|
this.showItem("context-media-eme-separator", this.onDRMMedia);
|
||||||
|
|
@ -1687,11 +1682,6 @@ nsContextMenu.prototype = {
|
||||||
this.browser.messageManager.sendAsyncMessage("SwitchDocumentDirection");
|
this.browser.messageManager.sendAsyncMessage("SwitchDocumentDirection");
|
||||||
},
|
},
|
||||||
|
|
||||||
pictureInPicture: function() {
|
|
||||||
let {PictureInPicture} = Cu.import("resource:///modules/PictureInPicture.jsm", {});
|
|
||||||
PictureInPicture.open(this.browser, this.target);
|
|
||||||
},
|
|
||||||
|
|
||||||
mediaCommand : function(command, data) {
|
mediaCommand : function(command, data) {
|
||||||
let mm = this.browser.messageManager;
|
let mm = this.browser.messageManager;
|
||||||
let win = this.browser.ownerGlobal;
|
let win = this.browser.ownerGlobal;
|
||||||
|
|
|
||||||
|
|
@ -1,191 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
browser[remote="true"] {
|
|
||||||
-moz-binding: url("chrome://global/content/bindings/remote-browser.xml#remote-browser");
|
|
||||||
}
|
|
||||||
|
|
||||||
#picture-in-picture {
|
|
||||||
min-width: 300px;
|
|
||||||
min-height: 170px;
|
|
||||||
background: black;
|
|
||||||
color: white;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#player-stack {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
-moz-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#player-container,
|
|
||||||
#player-overlay {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
min-width: 0;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#player-container {
|
|
||||||
background: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#player-container > browser {
|
|
||||||
display: -moz-box;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-width: 0;
|
|
||||||
min-height: 0;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#player-overlay {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
-moz-window-dragging: no-drag;
|
|
||||||
}
|
|
||||||
|
|
||||||
#drag-area {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pip-controls {
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
transition: opacity 150ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#player-overlay:hover .pip-controls,
|
|
||||||
#player-overlay[keyboard] .pip-controls,
|
|
||||||
#player-overlay[seeking] .pip-controls,
|
|
||||||
#player-stack[dragging] .pip-controls {
|
|
||||||
opacity: 1;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#top-controls {
|
|
||||||
display: flex;
|
|
||||||
flex-shrink: 0;
|
|
||||||
justify-content: flex-end;
|
|
||||||
padding: 8px;
|
|
||||||
background: linear-gradient(rgba(0, 0, 0, .55), transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
#playback-controls {
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding: 12px 10px 8px;
|
|
||||||
background: linear-gradient(transparent, rgba(0, 0, 0, .85));
|
|
||||||
-moz-window-dragging: no-drag;
|
|
||||||
}
|
|
||||||
|
|
||||||
#button-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pip-controls button {
|
|
||||||
-moz-appearance: none;
|
|
||||||
-moz-window-dragging: no-drag;
|
|
||||||
min-width: 0;
|
|
||||||
margin: 2px;
|
|
||||||
padding: 6px 8px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: rgba(30, 30, 30, .8);
|
|
||||||
color: white;
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pip-controls button:hover,
|
|
||||||
.pip-controls button:focus {
|
|
||||||
border-color: white;
|
|
||||||
background: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
#close {
|
|
||||||
font-size: 22px;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#seek {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0 0 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
-moz-window-dragging: no-drag;
|
|
||||||
}
|
|
||||||
|
|
||||||
#seek:disabled {
|
|
||||||
opacity: .45;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
#time {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
margin: 0 6px;
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-edge,
|
|
||||||
.resize-corner {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
-moz-window-dragging: no-drag;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-edge[data-resize="n"],
|
|
||||||
.resize-edge[data-resize="s"] {
|
|
||||||
left: 14px;
|
|
||||||
right: 14px;
|
|
||||||
height: 6px;
|
|
||||||
cursor: ns-resize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-edge[data-resize="e"],
|
|
||||||
.resize-edge[data-resize="w"] {
|
|
||||||
top: 14px;
|
|
||||||
bottom: 14px;
|
|
||||||
width: 6px;
|
|
||||||
cursor: ew-resize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resize-corner {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-resize*="n"] { top: 0; }
|
|
||||||
[data-resize*="s"] { bottom: 0; }
|
|
||||||
[data-resize*="e"] { right: 0; }
|
|
||||||
[data-resize*="w"] { left: 0; }
|
|
||||||
[data-resize="nw"], [data-resize="se"] { cursor: nwse-resize; }
|
|
||||||
[data-resize="ne"], [data-resize="sw"] { cursor: nesw-resize; }
|
|
||||||
|
|
||||||
@media (min-width: 700px) {
|
|
||||||
#playback-controls {
|
|
||||||
padding: 20px 20px 14px;
|
|
||||||
}
|
|
||||||
.pip-controls button {
|
|
||||||
padding: 8px 12px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
#time {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,250 +0,0 @@
|
||||||
/* 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";
|
|
||||||
|
|
||||||
// The browser XBL bindings resolve these names in their owning window.
|
|
||||||
const {utils: Cu, results: Cr} = Components;
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
|
|
||||||
var Player = {
|
|
||||||
browser: null,
|
|
||||||
source: null,
|
|
||||||
sourceTab: null,
|
|
||||||
seeking: false,
|
|
||||||
state: null,
|
|
||||||
drag: null,
|
|
||||||
|
|
||||||
startDrag(event) {
|
|
||||||
if (event.button != 0 ||
|
|
||||||
event.target.closest("button, input, #playback-controls")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let stack = document.getElementById("player-stack");
|
|
||||||
if (!stack.contains(event.target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
this.drag = {
|
|
||||||
direction: event.target.getAttribute("data-resize") || "",
|
|
||||||
pointerX: event.screenX,
|
|
||||||
pointerY: event.screenY,
|
|
||||||
x: window.screenX,
|
|
||||||
y: window.screenY,
|
|
||||||
width: window.outerWidth,
|
|
||||||
height: window.outerHeight,
|
|
||||||
};
|
|
||||||
// Capture keeps the gesture working after the pointer leaves the window.
|
|
||||||
stack.setCapture(true);
|
|
||||||
stack.setAttribute("dragging", "true");
|
|
||||||
},
|
|
||||||
|
|
||||||
moveDrag(event) {
|
|
||||||
let drag = this.drag;
|
|
||||||
if (!drag) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!(event.buttons & 1)) {
|
|
||||||
this.endDrag();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
let dx = event.screenX - drag.pointerX;
|
|
||||||
let dy = event.screenY - drag.pointerY;
|
|
||||||
if (!drag.direction) {
|
|
||||||
window.moveTo(drag.x + dx, drag.y + dy);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let west = drag.direction.includes("w");
|
|
||||||
let east = drag.direction.includes("e");
|
|
||||||
let north = drag.direction.includes("n");
|
|
||||||
let south = drag.direction.includes("s");
|
|
||||||
let width = Math.max(300, drag.width + (west ? -dx : east ? dx : 0));
|
|
||||||
let height = Math.max(170, drag.height + (north ? -dy : south ? dy : 0));
|
|
||||||
window.resizeTo(width, height);
|
|
||||||
// Anchor the opposite edge, using the actual size in case the platform
|
|
||||||
// imposed a constraint (for example, at a monitor boundary).
|
|
||||||
if (west || north) {
|
|
||||||
window.moveTo(west ? drag.x + drag.width - window.outerWidth : drag.x,
|
|
||||||
north ? drag.y + drag.height - window.outerHeight : drag.y);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
endDrag() {
|
|
||||||
if (this.drag) {
|
|
||||||
this.drag = null;
|
|
||||||
let stack = document.getElementById("player-stack");
|
|
||||||
stack.releaseCapture();
|
|
||||||
stack.removeAttribute("dragging");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
init() {
|
|
||||||
if (this.browser) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let args = window.arguments[0];
|
|
||||||
this.source = args.browser;
|
|
||||||
if (!this.source || !this.source.isConnected) {
|
|
||||||
window.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.sourceTab = this.source.ownerGlobal.gBrowser.getTabForBrowser(this.source);
|
|
||||||
this.close = () => window.close();
|
|
||||||
this.sourceTab.addEventListener("TabClose", this.close);
|
|
||||||
this.source.addEventListener("oop-browser-crashed", this.close);
|
|
||||||
|
|
||||||
this.playLabel = document.getElementById("play").label;
|
|
||||||
this.muteLabel = document.getElementById("mute").label;
|
|
||||||
let browser = this.browser = document.createElement("browser");
|
|
||||||
browser.setAttribute("type", "content");
|
|
||||||
browser.setAttribute("flex", "1");
|
|
||||||
browser.setAttribute("disablehistory", "true");
|
|
||||||
browser.setAttribute("remote", this.source.isRemoteBrowser ? "true" : "false");
|
|
||||||
if (this.source.isRemoteBrowser) {
|
|
||||||
browser.setAttribute("remoteType", this.source.getAttribute("remoteType"));
|
|
||||||
}
|
|
||||||
// The process-local source reference requires this process affinity.
|
|
||||||
browser.relatedBrowser = this.source;
|
|
||||||
document.getElementById("player-container").appendChild(browser);
|
|
||||||
let mm = browser.messageManager;
|
|
||||||
let initialized = false;
|
|
||||||
let ready = () => {
|
|
||||||
if (initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
initialized = true;
|
|
||||||
mm.removeMessageListener("PictureInPicture:Ready", ready);
|
|
||||||
mm.sendAsyncMessage("PictureInPicture:Init", {id: args.id});
|
|
||||||
};
|
|
||||||
mm.addMessageListener("PictureInPicture:Ready", ready);
|
|
||||||
mm.addMessageListener("PictureInPicture:Close", this.close);
|
|
||||||
mm.addMessageListener("PictureInPicture:State", message => {
|
|
||||||
this.updateState(message.data);
|
|
||||||
});
|
|
||||||
browser.addEventListener("oop-browser-crashed", this.close);
|
|
||||||
mm.loadFrameScript("chrome://browser/content/pictureInPictureContent.js", false);
|
|
||||||
},
|
|
||||||
|
|
||||||
command(command, data = {}) {
|
|
||||||
if (this.browser) {
|
|
||||||
this.browser.messageManager.sendAsyncMessage("PictureInPicture:Command",
|
|
||||||
Object.assign({command}, data));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
formatTime(seconds) {
|
|
||||||
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
||||||
return "0:00";
|
|
||||||
}
|
|
||||||
seconds = Math.floor(seconds);
|
|
||||||
let hours = Math.floor(seconds / 3600);
|
|
||||||
let minutes = Math.floor(seconds / 60) % 60;
|
|
||||||
let pad = value => value < 10 ? "0" + value : String(value);
|
|
||||||
return (hours ? hours + ":" + pad(minutes) : String(minutes)) +
|
|
||||||
":" + pad(seconds % 60);
|
|
||||||
},
|
|
||||||
|
|
||||||
updateTime(position) {
|
|
||||||
let duration = this.state && this.state.duration;
|
|
||||||
let total = Number.isFinite(duration) ? this.formatTime(duration) :
|
|
||||||
document.getElementById("live-label").value;
|
|
||||||
document.getElementById("time").value = this.formatTime(position) + " / " + total;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateState(state) {
|
|
||||||
this.state = state;
|
|
||||||
document.getElementById("play").label = state.paused ?
|
|
||||||
this.playLabel : document.getElementById("pause-label").value;
|
|
||||||
document.getElementById("mute").label = state.muted ?
|
|
||||||
document.getElementById("unmute-label").value : this.muteLabel;
|
|
||||||
if (!this.seeking) {
|
|
||||||
let seek = document.getElementById("seek");
|
|
||||||
seek.disabled = !(state.seekableEnd > state.seekableStart);
|
|
||||||
seek.min = state.seekableStart;
|
|
||||||
seek.max = state.seekableEnd || 1;
|
|
||||||
seek.value = state.currentTime;
|
|
||||||
this.updateTime(state.currentTime);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
seek(value) {
|
|
||||||
let position = Number(value);
|
|
||||||
if (!Number.isFinite(position) || document.getElementById("seek").disabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.seeking = true;
|
|
||||||
document.getElementById("player-overlay").setAttribute("seeking", "true");
|
|
||||||
this.updateTime(position);
|
|
||||||
this.command("seek", {time: position});
|
|
||||||
},
|
|
||||||
|
|
||||||
finishSeeking() {
|
|
||||||
this.seeking = false;
|
|
||||||
document.getElementById("player-overlay").removeAttribute("seeking");
|
|
||||||
},
|
|
||||||
|
|
||||||
returnToTab() {
|
|
||||||
if (this.source && this.source.isConnected) {
|
|
||||||
let win = this.source.ownerGlobal;
|
|
||||||
win.gBrowser.selectedTab = this.sourceTab;
|
|
||||||
win.focus();
|
|
||||||
}
|
|
||||||
window.close();
|
|
||||||
},
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.endDrag();
|
|
||||||
if (this.sourceTab) {
|
|
||||||
this.sourceTab.removeEventListener("TabClose", this.close);
|
|
||||||
this.source.removeEventListener("oop-browser-crashed", this.close);
|
|
||||||
}
|
|
||||||
if (this.browser) {
|
|
||||||
this.command("close");
|
|
||||||
}
|
|
||||||
this.source = this.sourceTab = null;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only consume the listener for the player document's own load, rather than
|
|
||||||
// a load from the embedded browser.
|
|
||||||
window.addEventListener("load", function onLoad(event) {
|
|
||||||
if (event.target == document) {
|
|
||||||
window.removeEventListener("load", onLoad);
|
|
||||||
Player.init();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.addEventListener("unload", function onUnload(event) {
|
|
||||||
if (event.target == document) {
|
|
||||||
window.removeEventListener("unload", onUnload);
|
|
||||||
Player.destroy();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.addEventListener("keydown", event => {
|
|
||||||
document.getElementById("player-overlay").setAttribute("keyboard", "true");
|
|
||||||
if (event.key == "Escape") {
|
|
||||||
window.close();
|
|
||||||
} else if (event.target.localName == "input" || event.target.localName == "button") {
|
|
||||||
return;
|
|
||||||
} else if (event.key == " ") {
|
|
||||||
event.preventDefault();
|
|
||||||
Player.command("playpause");
|
|
||||||
} else if (event.key == "ArrowLeft" || event.key == "ArrowRight") {
|
|
||||||
event.preventDefault();
|
|
||||||
if (Player.state) {
|
|
||||||
Player.command("seek", {time: Player.state.currentTime +
|
|
||||||
(event.key == "ArrowLeft" ? -5 : 5)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.addEventListener("mousedown", event => Player.startDrag(event));
|
|
||||||
window.addEventListener("mousemove", event => {
|
|
||||||
Player.moveDrag(event);
|
|
||||||
document.getElementById("player-overlay").removeAttribute("keyboard");
|
|
||||||
});
|
|
||||||
window.addEventListener("mouseup", () => Player.endDrag());
|
|
||||||
window.addEventListener("blur", event => {
|
|
||||||
if (event.target == window) {
|
|
||||||
Player.endDrag();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<!-- 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/. -->
|
|
||||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
|
||||||
<?xml-stylesheet href="chrome://browser/content/pictureInPicture.css" type="text/css"?>
|
|
||||||
<!DOCTYPE window SYSTEM "chrome://browser/locale/browser.dtd">
|
|
||||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
||||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
|
||||||
id="picture-in-picture" windowtype="Browser:PictureInPicture"
|
|
||||||
title="&pictureInPicture.title;" width="480" height="270"
|
|
||||||
hidechrome="true"
|
|
||||||
persist="screenX screenY width height">
|
|
||||||
<script type="application/javascript" src="chrome://browser/content/pictureInPicture.js"/>
|
|
||||||
<html:div id="player-stack">
|
|
||||||
<html:div id="player-container"/>
|
|
||||||
<html:div id="player-overlay">
|
|
||||||
<html:div class="pip-controls" id="top-controls">
|
|
||||||
<button id="close" label="×" tooltiptext="&pictureInPicture.close;"
|
|
||||||
aria-label="&pictureInPicture.close;" oncommand="window.close();"/>
|
|
||||||
</html:div>
|
|
||||||
<html:div id="drag-area"/>
|
|
||||||
<html:div class="pip-controls" id="playback-controls">
|
|
||||||
<html:input id="seek" type="range" min="0" max="1" step="0.1" value="0"
|
|
||||||
disabled="disabled" aria-label="&pictureInPicture.seek;"
|
|
||||||
oninput="Player.seek(this.value);" onchange="Player.finishSeeking();"/>
|
|
||||||
<html:div id="button-row">
|
|
||||||
<button id="play" label="&mediaPlay.label;" oncommand="Player.command('playpause');"/>
|
|
||||||
<button id="mute" label="&mediaMute.label;" oncommand="Player.command('mute');"/>
|
|
||||||
<label id="time" value="0:00 / 0:00" flex="1" crop="end"/>
|
|
||||||
<button id="return" label="&pictureInPicture.return;" oncommand="Player.returnToTab();"/>
|
|
||||||
</html:div>
|
|
||||||
</html:div>
|
|
||||||
</html:div>
|
|
||||||
<html:div class="resize-edge" data-resize="n"/>
|
|
||||||
<html:div class="resize-edge" data-resize="s"/>
|
|
||||||
<html:div class="resize-edge" data-resize="e"/>
|
|
||||||
<html:div class="resize-edge" data-resize="w"/>
|
|
||||||
<html:div class="resize-corner" data-resize="nw"/>
|
|
||||||
<html:div class="resize-corner" data-resize="ne"/>
|
|
||||||
<html:div class="resize-corner" data-resize="sw"/>
|
|
||||||
<html:div class="resize-corner" data-resize="se"/>
|
|
||||||
</html:div>
|
|
||||||
<label id="pause-label" value="&mediaPause.label;" hidden="true"/>
|
|
||||||
<label id="unmute-label" value="&mediaUnmute.label;" hidden="true"/>
|
|
||||||
<label id="live-label" value="&pictureInPicture.live;" hidden="true"/>
|
|
||||||
</window>
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
/* 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 {PictureInPicture} = Components.utils.import(
|
|
||||||
"resource:///modules/PictureInPicture.jsm", {});
|
|
||||||
var source = null;
|
|
||||||
var stream = null;
|
|
||||||
var output = null;
|
|
||||||
var still = null;
|
|
||||||
var observer = null;
|
|
||||||
var sourceWindow = null;
|
|
||||||
var initialized = false;
|
|
||||||
var events = ["play", "pause", "volumechange", "ended", "seeking", "seeked",
|
|
||||||
"resize", "timeupdate", "durationchange", "progress"];
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
if (!source) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Capture streams do not produce frames while their source is paused.
|
|
||||||
// Keep a snapshot for that case, without changing the source's play state.
|
|
||||||
still.hidden = !source.paused && !source.ended;
|
|
||||||
if (!still.hidden && source.readyState >= 2) {
|
|
||||||
still.width = source.videoWidth;
|
|
||||||
still.height = source.videoHeight;
|
|
||||||
still.getContext("2d").drawImage(source, 0, 0, still.width, still.height);
|
|
||||||
}
|
|
||||||
let ranges = source.seekable;
|
|
||||||
sendAsyncMessage("PictureInPicture:State", {
|
|
||||||
paused: source.paused || source.ended,
|
|
||||||
muted: source.muted,
|
|
||||||
currentTime: source.currentTime,
|
|
||||||
duration: source.duration,
|
|
||||||
seekableStart: ranges.length ? ranges.start(0) : 0,
|
|
||||||
seekableEnd: ranges.length ? ranges.end(ranges.length - 1) : 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function seekTo(time) {
|
|
||||||
if (typeof time != "number" || !Number.isFinite(time)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let ranges = source.seekable;
|
|
||||||
// Live streams may have no seekable range, or a moving DVR window.
|
|
||||||
// Pick the nearest valid point, including when there are gaps.
|
|
||||||
let position = null;
|
|
||||||
let distance = Infinity;
|
|
||||||
for (let i = 0; i < ranges.length; i++) {
|
|
||||||
let candidate = Math.max(ranges.start(i), Math.min(time, ranges.end(i)));
|
|
||||||
if (Math.abs(candidate - time) < distance) {
|
|
||||||
distance = Math.abs(candidate - time);
|
|
||||||
position = candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (position !== null) {
|
|
||||||
try {
|
|
||||||
source.currentTime = position;
|
|
||||||
} catch (error) {
|
|
||||||
Components.utils.reportError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
if (observer) {
|
|
||||||
observer.disconnect();
|
|
||||||
observer = null;
|
|
||||||
}
|
|
||||||
if (source) {
|
|
||||||
for (let name of events) {
|
|
||||||
source.removeEventListener(name, update);
|
|
||||||
}
|
|
||||||
source.removeEventListener("emptied", closePlayer);
|
|
||||||
source.removeEventListener("error", closePlayer);
|
|
||||||
sourceWindow.removeEventListener("pagehide", closePlayer);
|
|
||||||
source.mozPictureInPicture = false;
|
|
||||||
source = sourceWindow = null;
|
|
||||||
}
|
|
||||||
if (output) {
|
|
||||||
output.srcObject = null;
|
|
||||||
output = null;
|
|
||||||
}
|
|
||||||
if (stream) {
|
|
||||||
for (let track of stream.getTracks()) {
|
|
||||||
track.stop();
|
|
||||||
}
|
|
||||||
stream = null;
|
|
||||||
}
|
|
||||||
still = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function closePlayer() {
|
|
||||||
cleanup();
|
|
||||||
sendAsyncMessage("PictureInPicture:Close");
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessageListener("PictureInPicture:Init", message => {
|
|
||||||
// takeSource consumes the handoff. A duplicate initialization must not
|
|
||||||
// consume it again or close an already playing window.
|
|
||||||
if (initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
initialized = true;
|
|
||||||
try {
|
|
||||||
source = PictureInPicture.takeSource(message.data.id);
|
|
||||||
sourceWindow = source.ownerGlobal;
|
|
||||||
let doc = content.document;
|
|
||||||
doc.documentElement.style.cssText = "width:100%;height:100%;background:black;overflow:hidden";
|
|
||||||
doc.body.style.cssText = "margin:0;width:100%;height:100%;overflow:hidden";
|
|
||||||
output = doc.createElement("video");
|
|
||||||
still = doc.createElement("canvas");
|
|
||||||
for (let element of [output, still]) {
|
|
||||||
element.style.cssText = "position:fixed;left:0;top:0;width:100vw;height:100vh;" +
|
|
||||||
"max-width:none;max-height:none;object-fit:contain";
|
|
||||||
doc.body.appendChild(element);
|
|
||||||
}
|
|
||||||
// Decoder capture redirects audio to the captured stream. Its samples
|
|
||||||
// already include the source's volume/mute settings. MediaStream sources
|
|
||||||
// keep their own audio output, so mute only that case to avoid an echo.
|
|
||||||
output.muted = !!source.srcObject;
|
|
||||||
output.volume = 1;
|
|
||||||
source.mozPictureInPicture = true;
|
|
||||||
stream = source.mozCaptureStream();
|
|
||||||
output.srcObject = stream;
|
|
||||||
output.play().catch(error => {
|
|
||||||
Components.utils.reportError(error);
|
|
||||||
closePlayer();
|
|
||||||
});
|
|
||||||
for (let name of events) {
|
|
||||||
source.addEventListener(name, update);
|
|
||||||
}
|
|
||||||
source.addEventListener("emptied", closePlayer);
|
|
||||||
source.addEventListener("error", closePlayer);
|
|
||||||
sourceWindow.addEventListener("pagehide", closePlayer);
|
|
||||||
observer = new sourceWindow.MutationObserver(() => {
|
|
||||||
if (source && !source.isConnected) {
|
|
||||||
closePlayer();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
observer.observe(source.ownerDocument, {childList: true, subtree: true});
|
|
||||||
update();
|
|
||||||
} catch (error) {
|
|
||||||
Components.utils.reportError(error);
|
|
||||||
closePlayer();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
addMessageListener("PictureInPicture:Command", message => {
|
|
||||||
if (!source) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (message.data.command) {
|
|
||||||
case "playpause":
|
|
||||||
if (source.paused || source.ended) {
|
|
||||||
source.play().catch(Components.utils.reportError);
|
|
||||||
} else {
|
|
||||||
source.pause();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "mute":
|
|
||||||
source.muted = !source.muted;
|
|
||||||
break;
|
|
||||||
case "seek":
|
|
||||||
seekTo(message.data.time);
|
|
||||||
break;
|
|
||||||
case "close":
|
|
||||||
cleanup();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Frame-script unload also covers a parent window closing before an async
|
|
||||||
// close command can be delivered.
|
|
||||||
addEventListener("unload", cleanup);
|
|
||||||
sendAsyncMessage("PictureInPicture:Ready");
|
|
||||||
|
|
@ -2,10 +2,6 @@
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
browser.jar:
|
browser.jar:
|
||||||
content/browser/pictureInPicture.xul (content/pictureInPicture.xul)
|
|
||||||
content/browser/pictureInPicture.js (content/pictureInPicture.js)
|
|
||||||
content/browser/pictureInPicture.css (content/pictureInPicture.css)
|
|
||||||
content/browser/pictureInPictureContent.js (content/pictureInPictureContent.js)
|
|
||||||
% content browser %content/browser/ contentaccessible=yes
|
% content browser %content/browser/ contentaccessible=yes
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
% overlay chrome://mozapps/content/downloads/downloads.xul chrome://browser/content/downloadManagerOverlay.xul
|
% overlay chrome://mozapps/content/downloads/downloads.xul chrome://browser/content/downloadManagerOverlay.xul
|
||||||
|
|
|
||||||
|
|
@ -777,9 +777,3 @@ you can use these alternative items. Otherwise, their values should be empty. -
|
||||||
|
|
||||||
<!ENTITY emeLearnMoreContextMenu.label "Learn more about DRM…">
|
<!ENTITY emeLearnMoreContextMenu.label "Learn more about DRM…">
|
||||||
<!ENTITY emeLearnMoreContextMenu.accesskey "D">
|
<!ENTITY emeLearnMoreContextMenu.accesskey "D">
|
||||||
|
|
||||||
<!ENTITY pictureInPicture.title "Picture-in-Picture">
|
|
||||||
<!ENTITY pictureInPicture.return "Return to tab">
|
|
||||||
<!ENTITY pictureInPicture.close "Close">
|
|
||||||
<!ENTITY pictureInPicture.seek "Playback position">
|
|
||||||
<!ENTITY pictureInPicture.live "Live">
|
|
||||||
|
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
/* 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";
|
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = ["PictureInPicture"];
|
|
||||||
|
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
|
||||||
Cu.import("resource://gre/modules/Timer.jsm");
|
|
||||||
|
|
||||||
// Each content process has its own module instance. Only a weak reference to
|
|
||||||
// the source crosses between frame scripts; DOM objects never cross processes.
|
|
||||||
this.PictureInPicture = {
|
|
||||||
source: null,
|
|
||||||
window: null,
|
|
||||||
request: 0,
|
|
||||||
|
|
||||||
register(video, id) {
|
|
||||||
if (!video || video.localName != "video" || video.error ||
|
|
||||||
!video.videoWidth || !video.videoHeight || video.mediaKeys) {
|
|
||||||
throw new Error("This video is not available for Picture-in-Picture");
|
|
||||||
}
|
|
||||||
this.source = {id, video: Cu.getWeakReference(video)};
|
|
||||||
return {id, width: video.videoWidth, height: video.videoHeight};
|
|
||||||
},
|
|
||||||
|
|
||||||
takeSource(id) {
|
|
||||||
if (!this.source || this.source.id != id) {
|
|
||||||
throw new Error("Picture-in-Picture source process is unavailable");
|
|
||||||
}
|
|
||||||
let video = this.source.video.get();
|
|
||||||
this.source = null;
|
|
||||||
if (!video || !video.isConnected) {
|
|
||||||
throw new Error("Picture-in-Picture source has been removed");
|
|
||||||
}
|
|
||||||
return video;
|
|
||||||
},
|
|
||||||
|
|
||||||
open(browser, video) {
|
|
||||||
let id = Cc["@mozilla.org/uuid-generator;1"]
|
|
||||||
.getService(Ci.nsIUUIDGenerator).generateUUID().toString();
|
|
||||||
let request = ++this.request;
|
|
||||||
let mm = browser.messageManager;
|
|
||||||
let timer;
|
|
||||||
let ready = message => {
|
|
||||||
if (message.data.id != id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mm.removeMessageListener("PictureInPicture:Prepared", ready);
|
|
||||||
clearTimeout(timer);
|
|
||||||
if (request != this.request || !browser.isConnected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (message.data.error) {
|
|
||||||
Cu.reportError(message.data.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.openWindow(browser, message.data);
|
|
||||||
};
|
|
||||||
mm.addMessageListener("PictureInPicture:Prepared", ready);
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
mm.removeMessageListener("PictureInPicture:Prepared", ready);
|
|
||||||
}, 10000);
|
|
||||||
mm.sendAsyncMessage("ContextMenu:MediaCommand",
|
|
||||||
{command: "pictureinpicture", data: id},
|
|
||||||
{element: video});
|
|
||||||
},
|
|
||||||
|
|
||||||
openWindow(browser, data) {
|
|
||||||
if (this.window && !this.window.closed) {
|
|
||||||
this.window.close();
|
|
||||||
}
|
|
||||||
let screen = browser.ownerGlobal.screen;
|
|
||||||
let width = Math.min(480, screen.availWidth);
|
|
||||||
let height = Math.min(Math.round(width * data.height / data.width),
|
|
||||||
Math.round(screen.availHeight * 0.8));
|
|
||||||
this.window = browser.ownerGlobal.openDialog(
|
|
||||||
"chrome://browser/content/pictureInPicture.xul", "",
|
|
||||||
"chrome,dialog=no,resizable,alwaysRaised,width=" + width +
|
|
||||||
",height=" + height,
|
|
||||||
{browser, id: data.id});
|
|
||||||
return this.window;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -22,7 +22,6 @@ EXTRA_JS_MODULES += [
|
||||||
'NetworkPrioritizer.jsm',
|
'NetworkPrioritizer.jsm',
|
||||||
'offlineAppCache.jsm',
|
'offlineAppCache.jsm',
|
||||||
'PermissionUI.jsm',
|
'PermissionUI.jsm',
|
||||||
'PictureInPicture.jsm',
|
|
||||||
'PluginContent.jsm',
|
'PluginContent.jsm',
|
||||||
'ProcessHangMonitor.jsm',
|
'ProcessHangMonitor.jsm',
|
||||||
'ReaderParent.jsm',
|
'ReaderParent.jsm',
|
||||||
|
|
|
||||||
|
|
@ -5183,15 +5183,6 @@ bool HTMLMediaElement::IsActive() const
|
||||||
return ownerDoc && ownerDoc->IsActive() && ownerDoc->IsVisible();
|
return ownerDoc && ownerDoc->IsActive() && ownerDoc->IsVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLMediaElement::SetMozPictureInPicture(bool aEnabled)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
mPictureInPicture = aEnabled;
|
|
||||||
if (mDecoder && !mShuttingDown) {
|
|
||||||
mDecoder->NotifyOwnerActivityChanged(!IsHidden());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HTMLMediaElement::IsHidden() const
|
bool HTMLMediaElement::IsHidden() const
|
||||||
{
|
{
|
||||||
nsIDocument* ownerDoc;
|
nsIDocument* ownerDoc;
|
||||||
|
|
|
||||||
|
|
@ -211,9 +211,6 @@ public:
|
||||||
|
|
||||||
virtual bool IsHidden() const final override;
|
virtual bool IsHidden() const final override;
|
||||||
|
|
||||||
bool MozPictureInPicture() const { return mPictureInPicture; }
|
|
||||||
void SetMozPictureInPicture(bool aEnabled);
|
|
||||||
|
|
||||||
// Called by the media decoder and the video frame to get the
|
// Called by the media decoder and the video frame to get the
|
||||||
// ImageContainer containing the video data.
|
// ImageContainer containing the video data.
|
||||||
virtual VideoFrameContainer* GetVideoFrameContainer() final override;
|
virtual VideoFrameContainer* GetVideoFrameContainer() final override;
|
||||||
|
|
@ -1625,9 +1622,6 @@ protected:
|
||||||
// Info about the played media.
|
// Info about the played media.
|
||||||
MediaInfo mMediaInfo;
|
MediaInfo mMediaInfo;
|
||||||
|
|
||||||
// Set only by privileged Picture-in-Picture UI; cleared when it closes.
|
|
||||||
bool mPictureInPicture = false;
|
|
||||||
|
|
||||||
// True if the media has encryption information.
|
// True if the media has encryption information.
|
||||||
bool mIsEncrypted;
|
bool mIsEncrypted;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -881,20 +881,9 @@ ContentParent::CreateBrowser(const TabContext& aContext,
|
||||||
constructorSender = CreateContentBridgeParent(aContext, initialPriority,
|
constructorSender = CreateContentBridgeParent(aContext, initialPriority,
|
||||||
openerTabId, &tabId);
|
openerTabId, &tabId);
|
||||||
} else {
|
} else {
|
||||||
if (aOpenerContentParent && !aFreshProcess) {
|
constructorSender =
|
||||||
// relatedBrowser requires the same process, not another process from
|
GetNewOrUsedBrowserProcess(aContext.IsMozBrowserElement(),
|
||||||
// the pool. Picture-in-Picture shares a video through a process-local
|
initialPriority, nullptr, aFreshProcess);
|
||||||
// module, and cannot use a randomly selected content process.
|
|
||||||
if (!aOpenerContentParent->IsAlive() ||
|
|
||||||
aOpenerContentParent->IsForBrowser() != aContext.IsMozBrowserElement()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
constructorSender = aOpenerContentParent;
|
|
||||||
} else {
|
|
||||||
constructorSender =
|
|
||||||
GetNewOrUsedBrowserProcess(aContext.IsMozBrowserElement(),
|
|
||||||
initialPriority, nullptr, aFreshProcess);
|
|
||||||
}
|
|
||||||
if (!constructorSender) {
|
if (!constructorSender) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1250,11 +1250,7 @@ MediaDecoder::SetElementVisibility(bool aIsVisible)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
mElementVisible = aIsVisible;
|
mElementVisible = aIsVisible;
|
||||||
// A floating player remains visible when its source tab is in the
|
mIsVisible = !mForcedHidden && mElementVisible;
|
||||||
// background or the original video is scrolled out of view.
|
|
||||||
HTMLMediaElement* element = mOwner ? mOwner->GetMediaElement() : nullptr;
|
|
||||||
mIsVisible = (element && element->MozPictureInPicture()) ||
|
|
||||||
(!mForcedHidden && mElementVisible);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,6 @@ partial interface HTMLVideoElement {
|
||||||
// True if the video has an audio track available.
|
// True if the video has an audio track available.
|
||||||
readonly attribute boolean mozHasAudio;
|
readonly attribute boolean mozHasAudio;
|
||||||
|
|
||||||
// Browser UI only: keep the source decoder active for a floating player.
|
|
||||||
[ChromeOnly]
|
|
||||||
attribute boolean mozPictureInPicture;
|
|
||||||
|
|
||||||
// True if the video should use a screen wake lock.
|
// True if the video should use a screen wake lock.
|
||||||
[Pref="dom.wakelock.enabled", Func="Navigator::HasWakeLockSupport"]
|
[Pref="dom.wakelock.enabled", Func="Navigator::HasWakeLockSupport"]
|
||||||
attribute boolean mozUseScreenWakeLock;
|
attribute boolean mozUseScreenWakeLock;
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,6 @@ public:
|
||||||
NS_IMETHOD Move(double aX, double aY) override;
|
NS_IMETHOD Move(double aX, double aY) override;
|
||||||
virtual void SetSizeMode(nsSizeMode aMode) override;
|
virtual void SetSizeMode(nsSizeMode aMode) override;
|
||||||
NS_IMETHOD HideWindowChrome(bool aShouldHide) override;
|
NS_IMETHOD HideWindowChrome(bool aShouldHide) override;
|
||||||
void SetAlwaysOnTop(bool aAlwaysOnTop) override;
|
|
||||||
|
|
||||||
void EnteredFullScreen(bool aFullScreen, bool aNativeMode = true);
|
void EnteredFullScreen(bool aFullScreen, bool aNativeMode = true);
|
||||||
virtual bool PrepareForFullscreenTransition(nsISupports** aData) override;
|
virtual bool PrepareForFullscreenTransition(nsISupports** aData) override;
|
||||||
|
|
|
||||||
|
|
@ -1319,18 +1319,6 @@ nsCocoaWindow::SetSizeMode(nsSizeMode aMode)
|
||||||
// This has to preserve the window's frame bounds.
|
// This has to preserve the window's frame bounds.
|
||||||
// This method requires (as does the Windows impl.) that you call Resize shortly
|
// This method requires (as does the Windows impl.) that you call Resize shortly
|
||||||
// after calling HideWindowChrome. See bug 498835 for fixing this.
|
// after calling HideWindowChrome. See bug 498835 for fixing this.
|
||||||
void nsCocoaWindow::SetAlwaysOnTop(bool aAlwaysOnTop)
|
|
||||||
{
|
|
||||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
||||||
nsBaseWidget::SetAlwaysOnTop(aAlwaysOnTop);
|
|
||||||
if (mWindow && (mWindowType == eWindowType_toplevel ||
|
|
||||||
mWindowType == eWindowType_dialog)) {
|
|
||||||
[mWindow setLevel:aAlwaysOnTop ? NSFloatingWindowLevel : NSNormalWindowLevel];
|
|
||||||
[mWindow setHidesOnDeactivate:NO];
|
|
||||||
}
|
|
||||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP nsCocoaWindow::HideWindowChrome(bool aShouldHide)
|
NS_IMETHODIMP nsCocoaWindow::HideWindowChrome(bool aShouldHide)
|
||||||
{
|
{
|
||||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||||
|
|
@ -1365,9 +1353,6 @@ NS_IMETHODIMP nsCocoaWindow::HideWindowChrome(bool aShouldHide)
|
||||||
|
|
||||||
// Re-import state.
|
// Re-import state.
|
||||||
[mWindow importState:state];
|
[mWindow importState:state];
|
||||||
if (mAlwaysOnTop) {
|
|
||||||
SetAlwaysOnTop(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reparent the content view.
|
// Reparent the content view.
|
||||||
[mWindow setContentView:contentView];
|
[mWindow setContentView:contentView];
|
||||||
|
|
|
||||||
|
|
@ -1255,15 +1255,6 @@ nsWindow::NativeMove()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
nsWindow::SetAlwaysOnTop(bool aAlwaysOnTop)
|
|
||||||
{
|
|
||||||
nsBaseWidget::SetAlwaysOnTop(aAlwaysOnTop);
|
|
||||||
if (mShell) {
|
|
||||||
gtk_window_set_keep_above(GTK_WINDOW(mShell), aAlwaysOnTop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
nsWindow::SetZIndex(int32_t aZIndex)
|
nsWindow::SetZIndex(int32_t aZIndex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,6 @@ public:
|
||||||
virtual bool IsEnabled() const override;
|
virtual bool IsEnabled() const override;
|
||||||
|
|
||||||
void SetZIndex(int32_t aZIndex) override;
|
void SetZIndex(int32_t aZIndex) override;
|
||||||
void SetAlwaysOnTop(bool aAlwaysOnTop) override;
|
|
||||||
virtual void SetSizeMode(nsSizeMode aMode) override;
|
virtual void SetSizeMode(nsSizeMode aMode) override;
|
||||||
NS_IMETHOD Enable(bool aState) override;
|
NS_IMETHOD Enable(bool aState) override;
|
||||||
NS_IMETHOD SetFocus(bool aRaise = false) override;
|
NS_IMETHOD SetFocus(bool aRaise = false) override;
|
||||||
|
|
|
||||||
|
|
@ -803,12 +803,6 @@ class nsIWidget : public nsISupports
|
||||||
*/
|
*/
|
||||||
virtual void SetZIndex(int32_t aZIndex) = 0;
|
virtual void SetZIndex(int32_t aZIndex) = 0;
|
||||||
|
|
||||||
// Keep a top-level window above ordinary windows, including other apps.
|
|
||||||
virtual void SetAlwaysOnTop(bool aAlwaysOnTop)
|
|
||||||
{
|
|
||||||
mAlwaysOnTop = aAlwaysOnTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the widget's z-index.
|
* Gets the widget's z-index.
|
||||||
*/
|
*/
|
||||||
|
|
@ -2038,7 +2032,6 @@ protected:
|
||||||
bool mOnDestroyCalled;
|
bool mOnDestroyCalled;
|
||||||
nsWindowType mWindowType;
|
nsWindowType mWindowType;
|
||||||
int32_t mZIndex;
|
int32_t mZIndex;
|
||||||
bool mAlwaysOnTop = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIWidget, NS_IWIDGET_IID)
|
NS_DEFINE_STATIC_IID_ACCESSOR(nsIWidget, NS_IWIDGET_IID)
|
||||||
|
|
|
||||||
|
|
@ -2033,17 +2033,6 @@ nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent,
|
||||||
*
|
*
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
|
|
||||||
void
|
|
||||||
nsWindow::SetAlwaysOnTop(bool aAlwaysOnTop)
|
|
||||||
{
|
|
||||||
nsBaseWidget::SetAlwaysOnTop(aAlwaysOnTop);
|
|
||||||
if (mWnd && (mWindowType == eWindowType_toplevel ||
|
|
||||||
mWindowType == eWindowType_dialog)) {
|
|
||||||
::SetWindowPos(mWnd, aAlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
|
|
||||||
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position the window behind the given window
|
// Position the window behind the given window
|
||||||
void
|
void
|
||||||
nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
|
nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
|
||||||
|
|
@ -2070,10 +2059,6 @@ nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
|
||||||
flags |= SWP_NOACTIVATE;
|
flags |= SWP_NOACTIVATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application z-order changes must not demote an always-on-top window.
|
|
||||||
if (mAlwaysOnTop) {
|
|
||||||
behind = HWND_TOPMOST;
|
|
||||||
}
|
|
||||||
::SetWindowPos(mWnd, behind, 0, 0, 0, 0, flags);
|
::SetWindowPos(mWnd, behind, 0, 0, 0, 0, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,6 @@ public:
|
||||||
virtual nsresult MakeFullScreen(bool aFullScreen,
|
virtual nsresult MakeFullScreen(bool aFullScreen,
|
||||||
nsIScreen* aScreen = nullptr) override;
|
nsIScreen* aScreen = nullptr) override;
|
||||||
NS_IMETHOD HideWindowChrome(bool aShouldHide) override;
|
NS_IMETHOD HideWindowChrome(bool aShouldHide) override;
|
||||||
void SetAlwaysOnTop(bool aAlwaysOnTop) override;
|
|
||||||
NS_IMETHOD Invalidate(bool aEraseBackground = false,
|
NS_IMETHOD Invalidate(bool aEraseBackground = false,
|
||||||
bool aUpdateNCArea = false,
|
bool aUpdateNCArea = false,
|
||||||
bool aIncludeChildren = false);
|
bool aIncludeChildren = false);
|
||||||
|
|
|
||||||
|
|
@ -228,9 +228,6 @@ NS_IMETHODIMP nsXULWindow::SetZLevel(uint32_t aLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// do it
|
// do it
|
||||||
if (mWindow) {
|
|
||||||
mWindow->SetAlwaysOnTop(aLevel >= nsIXULWindow::raisedZ);
|
|
||||||
}
|
|
||||||
mediator->SetZLevel(this, aLevel);
|
mediator->SetZLevel(this, aLevel);
|
||||||
PersistentAttributesDirty(PAD_MISC);
|
PersistentAttributesDirty(PAD_MISC);
|
||||||
SavePersistentAttributes();
|
SavePersistentAttributes();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue