--- a/devtools/shared/webconsole/screenshot-helper.js
+++ b/devtools/shared/webconsole/screenshot-helper.js
@@ -1,25 +1,21 @@
/* 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";
-const { Cc, Ci, Cr } = require("chrome");
-const ChromeUtils = require("ChromeUtils");
+const { Cc, Ci } = require("chrome");
const { LocalizationHelper } = require("devtools/shared/l10n");
const Services = require("Services");
-const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
loader.lazyImporter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
loader.lazyImporter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
-loader.lazyImporter(this, "PrivateBrowsingUtils",
- "resource://gre/modules/PrivateBrowsingUtils.jsm");
const STRINGS_URI = "devtools/shared/locales/screenshot.properties";
const L10N = new LocalizationHelper(STRINGS_URI);
const screenshotDescription = L10N.getStr("screenshotDesc");
const screenshotGroupOptions = L10N.getStr("screenshotGroupOptions");
const screenshotCommandParams = [
{
@@ -157,17 +153,17 @@ function simulateCameraShutter(document)
* Response messages from processing the screenshot.
*/
async function saveScreenshot(window, args, image) {
const fileNeeded = args.filename ||
!args.clipboard || args.file;
const results = [];
if (args.clipboard) {
- const result = await saveToClipboard(window, image.data);
+ const result = saveToClipboard(window, image.data);
results.push(result);
}
if (fileNeeded) {
const result = await saveToFile(window, image);
results.push(result);
}
return results;
@@ -175,164 +171,62 @@ async function saveScreenshot(window, ar
/**
* Save the image data to the clipboard. This returns a promise, so it can
* be treated exactly like file processing.
*
* @param object window
* The Debugger Client window.
*
- * @param string data
- * The image data encoded in base64 that was sent from the server.
+ * @param string base64URI
+ * The image data encoded in a base64 URI that was sent from the server.
*
* @return string
* Response message from processing the screenshot.
*/
-function saveToClipboard(window, data) {
- return new Promise(resolve => {
- try {
- const channel = NetUtil.newChannel({
- uri: data,
- loadUsingSystemPrincipal: true,
- contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
- });
- const input = channel.open2();
-
- const loadContext = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsILoadContext);
-
- const callback = {
- onImageReady(container, status) {
- if (!container) {
- console.error("imgTools.decodeImageAsync failed");
- resolve(L10N.getStr("screenshotErrorCopying"));
- return;
- }
-
- try {
- const wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
- .createInstance(Ci.nsISupportsInterfacePointer);
- wrapped.data = container;
+function saveToClipboard(window, base64URI) {
+ try {
+ const imageTools = Cc["@mozilla.org/image/tools;1"]
+ .getService(Ci.imgITools);
- const trans = Cc["@mozilla.org/widget/transferable;1"]
- .createInstance(Ci.nsITransferable);
- trans.init(loadContext);
- trans.addDataFlavor(channel.contentType);
- trans.setTransferData(channel.contentType, wrapped, -1);
-
- Services.clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
+ const base64Data = base64URI.replace("data:image/png;base64,", "");
- resolve(L10N.getStr("screenshotCopied"));
- } catch (ex) {
- console.error(ex);
- resolve(L10N.getStr("screenshotErrorCopying"));
- }
- }
- };
-
- const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
- const imgTools = Cc["@mozilla.org/image/tools;1"]
- .getService(Ci.imgITools);
- imgTools.decodeImageAsync(input, channel.contentType, callback,
- threadManager.currentThread);
- } catch (ex) {
- console.error(ex);
- resolve(L10N.getStr("screenshotErrorCopying"));
- }
- });
-}
+ const image = atob(base64Data);
+ const imgPtr = Cc["@mozilla.org/supports-interface-pointer;1"]
+ .createInstance(Ci.nsISupportsInterfacePointer);
+ imgPtr.data = imageTools.decodeImageFromBuffer(image, image.length, "image/png");
-/**
- * Progress listener that forwards calls to a transfer object.
- *
- * This is used below in saveToFile to forward progress updates from the
- * nsIWebBrowserPersist object that does the actual saving to the nsITransfer
- * which just represents the operation for the Download Manager. This keeps the
- * Download Manager updated on saving progress and completion, so that it gives
- * visual feedback from the downloads toolbar button when the save is done.
- *
- * It also allows the browser window to show auth prompts if needed (should not
- * be needed for saving screenshots).
- *
- * This code is borrowed directly from contentAreaUtils.js.
- *
- * @param object win
- * The Debugger Client window.
- *
- * @param object transfer
- * The transfer object.
- *
- */
-function DownloadListener(win, transfer) {
- this.window = win;
- this.transfer = transfer;
-
- // For most method calls, forward to the transfer object.
- for (const name in transfer) {
- if (name != "QueryInterface" &&
- name != "onStateChange") {
- this[name] = (...args) => transfer[name].apply(transfer, args);
- }
- }
+ const transferable = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+ transferable.init(null);
+ transferable.addDataFlavor("image/png");
+ transferable.setTransferData("image/png", imgPtr, -1);
- // Allow saveToFile to await completion for error handling
- this._completedDeferred = {};
- this.completed = new Promise((resolve, reject) => {
- this._completedDeferred.resolve = resolve;
- this._completedDeferred.reject = reject;
- });
+ Services.clipboard.setData(transferable, null, Services.clipboard.kGlobalClipboard);
+ return L10N.getStr("screenshotCopied");
+ } catch (ex) {
+ console.error(ex);
+ return L10N.getStr("screenshotErrorCopying");
+ }
}
-DownloadListener.prototype = {
- QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor",
- "nsIWebProgressListener",
- "nsIWebProgressListener2"]),
-
- getInterface: function(iid) {
- if (iid.equals(Ci.nsIAuthPrompt) ||
- iid.equals(Ci.nsIAuthPrompt2)) {
- const ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
- .getService(Ci.nsIPromptFactory);
- return ww.getPrompt(this.window, iid);
- }
-
- throw Cr.NS_ERROR_NO_INTERFACE;
- },
-
- onStateChange: function(webProgress, request, state, status) {
- // Check if the download has completed
- if ((state & Ci.nsIWebProgressListener.STATE_STOP) &&
- (state & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) {
- if (status == Cr.NS_OK) {
- this._completedDeferred.resolve();
- } else {
- this._completedDeferred.reject();
- }
- }
-
- this.transfer.onStateChange.apply(this.transfer, arguments);
- }
-};
-
/**
* Save the screenshot data to disk, returning a promise which is resolved on
* completion.
*
* @param object window
* The Debugger Client window.
*
* @param object image
* The image object that was sent from the server.
*
* @return string
* Response message from processing the screenshot.
*/
async function saveToFile(window, image) {
- const document = window.document;
let filename = image.filename;
// Check there is a .png extension to filename
if (!filename.match(/.png$/i)) {
filename += ".png";
}
const downloadsDir = await Downloads.getPreferredDownloadsDirectory();
@@ -340,58 +234,28 @@ async function saveToFile(window, image)
if (downloadsDirExists) {
// If filename is absolute, it will override the downloads directory and
// still be applied as expected.
filename = OS.Path.join(downloadsDir, filename);
}
const sourceURI = Services.io.newURI(image.data);
const targetFile = new FileUtils.File(filename);
- const targetFileURI = Services.io.newFileURI(targetFile);
// Create download and track its progress.
- // This is adapted from saveURL in contentAreaUtils.js, but simplified greatly
- // and modified to allow saving to arbitrary paths on disk. Using these
- // objects as opposed to just writing with OS.File allows us to tie into the
- // download manager to record a download entry and to get visual feedback from
- // the downloads toolbar button when the save is done.
- const nsIWBP = Ci.nsIWebBrowserPersist;
- const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
- nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
- nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
- const isPrivate =
- PrivateBrowsingUtils.isContentWindowPrivate(document.defaultView);
- const persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
- .createInstance(Ci.nsIWebBrowserPersist);
- persist.persistFlags = flags;
- const tr = Cc["@mozilla.org/transfer;1"].createInstance(Ci.nsITransfer);
- tr.init(sourceURI,
- targetFileURI,
- "",
- null,
- null,
- null,
- persist,
- isPrivate);
- const listener = new DownloadListener(window, tr);
- persist.progressListener = listener;
- const principal = Services.scriptSecurityManager.getSystemPrincipal();
- persist.savePrivacyAwareURI(sourceURI,
- principal,
- 0,
- document.documentURIObject,
- Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
- null,
- null,
- targetFileURI,
- isPrivate);
-
try {
- // Await successful completion of the save via the listener
- await listener.completed;
+ const download = await Downloads.createDownload({
+ source: sourceURI,
+ target: targetFile
+ });
+ const list = await Downloads.getList(Downloads.ALL);
+ // add the download to the download list in the Downloads list in the Browser UI
+ list.add(download);
+ // Await successful completion of the save via the download manager
+ await download.start();
return L10N.getFormatStr("screenshotSavedToFile", filename);
} catch (ex) {
console.error(ex);
return L10N.getFormatStr("screenshotErrorSavingToFile", filename);
}
}
module.exports = processScreenshot;