зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1736429 - Set up jswindowactors for screenshots and pass dimensions and scroll offsets from the child. r=mconley,sfoster
* Add jswindowactors for the Screenshots component, to facilitate communication between the UI (lives in the parent) and the content document. * Fetch content document dimensions/offsets with e.g: `ScreenshotsUtils.fetchContentBounds(gBrowser.selectedBrowser)`. Differential Revision: https://phabricator.services.mozilla.com/D129326
This commit is contained in:
Родитель
62b77fa25e
Коммит
861b4bbc43
|
@ -0,0 +1,156 @@
|
||||||
|
/* 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/. */
|
||||||
|
/* eslint-env mozilla/browser-window */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var EXPORTED_SYMBOLS = ["ScreenshotsComponentChild"];
|
||||||
|
|
||||||
|
class ScreenshotsComponentChild extends JSWindowActorChild {
|
||||||
|
receiveMessage(message) {
|
||||||
|
switch (message.name) {
|
||||||
|
case "Screenshots:ShowOverlay":
|
||||||
|
return this.startScreenshotsOverlay();
|
||||||
|
case "Screenshots:HideOverlay":
|
||||||
|
return this.endScreenshotsOverlay();
|
||||||
|
case "Screenshots:getFullPageBounds":
|
||||||
|
return this.getFullPageBounds();
|
||||||
|
case "Screenshots:getVisibleBounds":
|
||||||
|
return this.getVisibleBounds();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves when the document is ready to have an overlay injected into it.
|
||||||
|
*
|
||||||
|
* @returns {Promise}
|
||||||
|
* @resolves {Boolean} true when document is ready or rejects
|
||||||
|
*/
|
||||||
|
documentIsReady() {
|
||||||
|
const document = this.document;
|
||||||
|
// Some pages take ages to finish loading - if at all.
|
||||||
|
// We want to respond to enable the screenshots UI as soon that is possible
|
||||||
|
function readyEnough() {
|
||||||
|
return (
|
||||||
|
document.readyState !== "uninitialized" && document.documentElement
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readyEnough()) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
function onChange(event) {
|
||||||
|
if (event.type === "pagehide") {
|
||||||
|
document.removeEventListener("readystatechange", onChange);
|
||||||
|
this.contentWindow.removeEventListener("pagehide", onChange);
|
||||||
|
reject(new Error("document unloaded before it was ready"));
|
||||||
|
} else if (readyEnough()) {
|
||||||
|
document.removeEventListener("readystatechange", onChange);
|
||||||
|
this.contentWindow.removeEventListener("pagehide", onChange);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("readystatechange", onChange);
|
||||||
|
this.contentWindow.addEventListener("pagehide", onChange, { once: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until the document is ready and then show the screenshots overlay
|
||||||
|
*
|
||||||
|
* @returns {Boolean} true when document is ready and the overlay is shown
|
||||||
|
* otherwise false
|
||||||
|
*/
|
||||||
|
async startScreenshotsOverlay(details = {}) {
|
||||||
|
try {
|
||||||
|
await this.documentIsReady();
|
||||||
|
} catch (ex) {
|
||||||
|
console.warn(`ScreenshotsComponentChild: ${ex.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the screenshots overlay.
|
||||||
|
*
|
||||||
|
* @returns {Boolean}
|
||||||
|
* true when the overlay has been removed otherwise false
|
||||||
|
*/
|
||||||
|
endScreenshotsOverlay() {
|
||||||
|
// this function will be implemented soon
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the full page bounds for a full page screenshot.
|
||||||
|
*
|
||||||
|
* @returns { object }
|
||||||
|
* The device pixel ratio and a DOMRect of the scrollable content bounds.
|
||||||
|
*
|
||||||
|
* devicePixelRatio (float):
|
||||||
|
* The device pixel ratio of the screen
|
||||||
|
*
|
||||||
|
* rect (object):
|
||||||
|
* top (int):
|
||||||
|
* The scroll top position for the content window.
|
||||||
|
*
|
||||||
|
* left (int):
|
||||||
|
* The scroll left position for the content window.
|
||||||
|
*
|
||||||
|
* width (int):
|
||||||
|
* The scroll width of the content window.
|
||||||
|
*
|
||||||
|
* height (int):
|
||||||
|
* The scroll height of the content window.
|
||||||
|
*/
|
||||||
|
getFullPageBounds() {
|
||||||
|
let doc = this.document.documentElement;
|
||||||
|
let rect = new DOMRect(
|
||||||
|
doc.scrollTop,
|
||||||
|
doc.scrollLeft,
|
||||||
|
doc.scrollWidth,
|
||||||
|
doc.scrollHeight
|
||||||
|
);
|
||||||
|
let devicePixelRatio = this.document.ownerGlobal.devicePixelRatio;
|
||||||
|
return { devicePixelRatio, rect };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the visible page bounds for a visible screenshot.
|
||||||
|
*
|
||||||
|
* @returns { object }
|
||||||
|
* The device pixel ratio and a DOMRect of the current visible
|
||||||
|
* content bounds.
|
||||||
|
*
|
||||||
|
* devicePixelRatio (float):
|
||||||
|
* The device pixel ratio of the screen
|
||||||
|
*
|
||||||
|
* rect (object):
|
||||||
|
* top (int):
|
||||||
|
* The top position for the content window.
|
||||||
|
*
|
||||||
|
* left (int):
|
||||||
|
* The left position for the content window.
|
||||||
|
*
|
||||||
|
* width (int):
|
||||||
|
* The width of the content window.
|
||||||
|
*
|
||||||
|
* height (int):
|
||||||
|
* The height of the content window.
|
||||||
|
*/
|
||||||
|
getVisibleBounds() {
|
||||||
|
let doc = this.document.documentElement;
|
||||||
|
let rect = new DOMRect(
|
||||||
|
doc.clientTop,
|
||||||
|
doc.clientLeft,
|
||||||
|
doc.clientWidth,
|
||||||
|
doc.clientHeight
|
||||||
|
);
|
||||||
|
let devicePixelRatio = this.document.ownerGlobal.devicePixelRatio;
|
||||||
|
return { devicePixelRatio, rect };
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,9 @@ with Files("PageStyleChild.jsm"):
|
||||||
with Files("PluginChild.jsm"):
|
with Files("PluginChild.jsm"):
|
||||||
BUG_COMPONENT = ("Core", "Plug-ins")
|
BUG_COMPONENT = ("Core", "Plug-ins")
|
||||||
|
|
||||||
|
with Files("ScreenshotsComponentChild.jsm"):
|
||||||
|
BUG_COMPONENT = ("Firefox", "Screenshots")
|
||||||
|
|
||||||
with Files("WebRTCChild.jsm"):
|
with Files("WebRTCChild.jsm"):
|
||||||
BUG_COMPONENT = ("Firefox", "Site Permissions")
|
BUG_COMPONENT = ("Firefox", "Site Permissions")
|
||||||
|
|
||||||
|
@ -79,6 +82,7 @@ FINAL_TARGET_FILES.actors += [
|
||||||
"RefreshBlockerParent.jsm",
|
"RefreshBlockerParent.jsm",
|
||||||
"RFPHelperChild.jsm",
|
"RFPHelperChild.jsm",
|
||||||
"RFPHelperParent.jsm",
|
"RFPHelperParent.jsm",
|
||||||
|
"ScreenshotsComponentChild.jsm",
|
||||||
"SearchSERPTelemetryChild.jsm",
|
"SearchSERPTelemetryChild.jsm",
|
||||||
"SearchSERPTelemetryParent.jsm",
|
"SearchSERPTelemetryParent.jsm",
|
||||||
"SwitchDocumentDirectionChild.jsm",
|
"SwitchDocumentDirectionChild.jsm",
|
||||||
|
|
|
@ -645,6 +645,13 @@ let JSWINDOWACTORS = {
|
||||||
enablePreference: "accessibility.blockautorefresh",
|
enablePreference: "accessibility.blockautorefresh",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ScreenshotsComponent: {
|
||||||
|
child: {
|
||||||
|
moduleURI: "resource:///actors/ScreenshotsComponentChild.jsm",
|
||||||
|
},
|
||||||
|
enablePreference: "screenshots.browser.component.enabled",
|
||||||
|
},
|
||||||
|
|
||||||
SearchSERPTelemetry: {
|
SearchSERPTelemetry: {
|
||||||
parent: {
|
parent: {
|
||||||
moduleURI: "resource:///actors/SearchSERPTelemetryParent.jsm",
|
moduleURI: "resource:///actors/SearchSERPTelemetryParent.jsm",
|
||||||
|
|
|
@ -14,6 +14,14 @@ const PanelOffsetY = -8;
|
||||||
|
|
||||||
var ScreenshotsUtils = {
|
var ScreenshotsUtils = {
|
||||||
initialize() {
|
initialize() {
|
||||||
|
if (
|
||||||
|
!Services.prefs.getBoolPref(
|
||||||
|
"screenshots.browser.component.enabled",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Services.obs.addObserver(this, "menuitem-screenshot");
|
Services.obs.addObserver(this, "menuitem-screenshot");
|
||||||
Services.obs.addObserver(this, "screenshots-take-screenshot");
|
Services.obs.addObserver(this, "screenshots-take-screenshot");
|
||||||
},
|
},
|
||||||
|
@ -23,6 +31,8 @@ var ScreenshotsUtils = {
|
||||||
|
|
||||||
let currDialogBox = browser.tabDialogBox;
|
let currDialogBox = browser.tabDialogBox;
|
||||||
|
|
||||||
|
let zoom = subj.ZoomManager.getZoomForBrowser(browser);
|
||||||
|
|
||||||
switch (topic) {
|
switch (topic) {
|
||||||
case "menuitem-screenshot":
|
case "menuitem-screenshot":
|
||||||
// if dialog box exists then find the correct dialog box and close it
|
// if dialog box exists then find the correct dialog box and close it
|
||||||
|
@ -56,7 +66,7 @@ var ScreenshotsUtils = {
|
||||||
// init UI as a tab dialog box
|
// init UI as a tab dialog box
|
||||||
let dialogBox = gBrowser.getTabDialogBox(browser);
|
let dialogBox = gBrowser.getTabDialogBox(browser);
|
||||||
|
|
||||||
return dialogBox.open(
|
let { dialog } = dialogBox.open(
|
||||||
`chrome://browser/content/screenshots/screenshots.html?browsingContextId=${browser.browsingContext.id}`,
|
`chrome://browser/content/screenshots/screenshots.html?browsingContextId=${browser.browsingContext.id}`,
|
||||||
{
|
{
|
||||||
features: "resizable=no",
|
features: "resizable=no",
|
||||||
|
@ -64,9 +74,15 @@ var ScreenshotsUtils = {
|
||||||
allowDuplicateDialogs: false,
|
allowDuplicateDialogs: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
this.doScreenshot(browser, dialog, zoom, data);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Notify screenshots when screenshot command is used.
|
||||||
|
* @param window The current window the screenshot command was used.
|
||||||
|
* @param type The type of screenshot taken. Used for telemetry.
|
||||||
|
*/
|
||||||
notify(window, type) {
|
notify(window, type) {
|
||||||
if (Services.prefs.getBoolPref("screenshots.browser.component.enabled")) {
|
if (Services.prefs.getBoolPref("screenshots.browser.component.enabled")) {
|
||||||
Services.obs.notifyObservers(
|
Services.obs.notifyObservers(
|
||||||
|
@ -77,16 +93,40 @@ var ScreenshotsUtils = {
|
||||||
Services.obs.notifyObservers(null, "menuitem-screenshot-extension", type);
|
Services.obs.notifyObservers(null, "menuitem-screenshot-extension", type);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Creates and returns a Screenshots actor.
|
||||||
|
* @param browser The current browser.
|
||||||
|
* @returns JSWindowActor The screenshot actor.
|
||||||
|
*/
|
||||||
|
getActor(browser) {
|
||||||
|
let actor = browser.browsingContext.currentWindowGlobal.getActor(
|
||||||
|
"ScreenshotsComponent"
|
||||||
|
);
|
||||||
|
return actor;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* If the buttons panel exists and the panel is open we will hipe the panel
|
||||||
|
* popup and hide the screenshot overlay.
|
||||||
|
* Otherwise create or display the buttons.
|
||||||
|
* @param browser The current browser.
|
||||||
|
*/
|
||||||
togglePreview(browser) {
|
togglePreview(browser) {
|
||||||
let buttonsPanel = browser.ownerDocument.querySelector(
|
let buttonsPanel = browser.ownerDocument.querySelector(
|
||||||
"#screenshotsPagePanel"
|
"#screenshotsPagePanel"
|
||||||
);
|
);
|
||||||
if (buttonsPanel && buttonsPanel.state !== "closed") {
|
if (buttonsPanel && buttonsPanel.state !== "closed") {
|
||||||
buttonsPanel.hidePopup();
|
buttonsPanel.hidePopup();
|
||||||
} else {
|
let actor = this.getActor(browser);
|
||||||
this.createOrDisplayButtons(browser);
|
return actor.sendQuery("Screenshots:HideOverlay");
|
||||||
}
|
}
|
||||||
|
return this.createOrDisplayButtons(browser);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* If the buttons panel does not exist then we will replace the buttons
|
||||||
|
* panel template with the buttons panel then open the buttons panel and
|
||||||
|
* show the screenshots overaly.
|
||||||
|
* @param browser The current browser.
|
||||||
|
*/
|
||||||
createOrDisplayButtons(browser) {
|
createOrDisplayButtons(browser) {
|
||||||
let doc = browser.ownerDocument;
|
let doc = browser.ownerDocument;
|
||||||
let buttonsPanel = doc.querySelector("#screenshotsPagePanel");
|
let buttonsPanel = doc.querySelector("#screenshotsPagePanel");
|
||||||
|
@ -98,5 +138,90 @@ var ScreenshotsUtils = {
|
||||||
}
|
}
|
||||||
let anchor = doc.querySelector("#navigator-toolbox");
|
let anchor = doc.querySelector("#navigator-toolbox");
|
||||||
buttonsPanel.openPopup(anchor, PanelPosition, PanelOffsetX, PanelOffsetY);
|
buttonsPanel.openPopup(anchor, PanelPosition, PanelOffsetX, PanelOffsetY);
|
||||||
|
let actor = this.getActor(browser);
|
||||||
|
return actor.sendQuery("Screenshots:ShowOverlay");
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Gets the full page bounds from the screenshots child actor.
|
||||||
|
* @param browser The current browser.
|
||||||
|
* @returns { object }
|
||||||
|
* Contains the full page bounds from the screenshots child actor.
|
||||||
|
*/
|
||||||
|
fetchFullPageBounds(browser) {
|
||||||
|
let actor = this.getActor(browser);
|
||||||
|
return actor.sendQuery("Screenshots:getFullPageBounds");
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Gets the visible bounds from the screenshots child actor.
|
||||||
|
* @param browser The current browser.
|
||||||
|
* @returns { object }
|
||||||
|
* Contains the visible bounds from the screenshots child actor.
|
||||||
|
*/
|
||||||
|
fetchVisibleBounds(browser) {
|
||||||
|
let actor = this.getActor(browser);
|
||||||
|
return actor.sendQuery("Screenshots:getVisibleBounds");
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Add screenshot-ui to the dialog box and then take the screenshot
|
||||||
|
* @param browser The current browser.
|
||||||
|
* @param dialog The dialog box to show the screenshot preview.
|
||||||
|
* @param zoom The current zoom level.
|
||||||
|
* @param type The type of screenshot taken.
|
||||||
|
*/
|
||||||
|
async doScreenshot(browser, dialog, zoom, type) {
|
||||||
|
await dialog._dialogReady;
|
||||||
|
let screenshotsUI = dialog._frame.contentDocument.createElement(
|
||||||
|
"screenshots-ui"
|
||||||
|
);
|
||||||
|
dialog._frame.contentDocument.body.appendChild(screenshotsUI);
|
||||||
|
|
||||||
|
let rect;
|
||||||
|
if (type === "full-page") {
|
||||||
|
({ rect } = await this.fetchFullPageBounds(browser));
|
||||||
|
} else {
|
||||||
|
({ rect } = await this.fetchVisibleBounds(browser));
|
||||||
|
}
|
||||||
|
return this.takeScreenshot(browser, dialog, rect, zoom);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Take the screenshot and add the image to the dialog box
|
||||||
|
* @param browser The current browser.
|
||||||
|
* @param dialog The dialog box to show the screenshot preview.
|
||||||
|
* @param rect DOMRect containing bounds of the screenshot.
|
||||||
|
* @param zoom The current zoom level.
|
||||||
|
*/
|
||||||
|
async takeScreenshot(browser, dialog, rect, zoom) {
|
||||||
|
let browsingContext = BrowsingContext.get(browser.browsingContext.id);
|
||||||
|
|
||||||
|
let snapshot = await browsingContext.currentWindowGlobal.drawSnapshot(
|
||||||
|
rect,
|
||||||
|
zoom,
|
||||||
|
"rgb(255,255,255)"
|
||||||
|
);
|
||||||
|
|
||||||
|
let canvas = dialog._frame.contentDocument.createElementNS(
|
||||||
|
"http://www.w3.org/1999/xhtml",
|
||||||
|
"html:canvas"
|
||||||
|
);
|
||||||
|
let context = canvas.getContext("2d");
|
||||||
|
|
||||||
|
canvas.width = snapshot.width;
|
||||||
|
canvas.height = snapshot.height;
|
||||||
|
|
||||||
|
context.drawImage(snapshot, 0, 0);
|
||||||
|
|
||||||
|
canvas.toBlob(function(blob) {
|
||||||
|
let newImg = dialog._frame.contentDocument.createElement("img");
|
||||||
|
let url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
newImg.id = "placeholder-image";
|
||||||
|
|
||||||
|
newImg.src = url;
|
||||||
|
dialog._frame.contentDocument
|
||||||
|
.getElementById("preview-image-div")
|
||||||
|
.appendChild(newImg);
|
||||||
|
});
|
||||||
|
|
||||||
|
snapshot.close();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,7 +37,5 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<screenshots-ui></screenshots-ui>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -21,8 +21,6 @@ class ScreenshotsUI extends HTMLElement {
|
||||||
}
|
}
|
||||||
async connectedCallback() {
|
async connectedCallback() {
|
||||||
this.initialize();
|
this.initialize();
|
||||||
|
|
||||||
await this.takeVisibleScreenshot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче