mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-15 19:57:29 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
2871
toolkit/components/jsdownloads/src/DownloadCore.jsm
Normal file
2871
toolkit/components/jsdownloads/src/DownloadCore.jsm
Normal file
File diff suppressed because it is too large
Load diff
193
toolkit/components/jsdownloads/src/DownloadImport.jsm
Normal file
193
toolkit/components/jsdownloads/src/DownloadImport.jsm
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
|
||||
/* 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 = [
|
||||
"DownloadImport",
|
||||
];
|
||||
|
||||
// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm")
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
|
||||
"resource://gre/modules/Sqlite.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
/**
|
||||
* These values come from the previous interface
|
||||
* nsIDownloadManager, which has now been deprecated.
|
||||
* These are the only types of download states that
|
||||
* we will import.
|
||||
*/
|
||||
const DOWNLOAD_NOTSTARTED = -1;
|
||||
const DOWNLOAD_DOWNLOADING = 0;
|
||||
const DOWNLOAD_PAUSED = 4;
|
||||
const DOWNLOAD_QUEUED = 5;
|
||||
|
||||
// DownloadImport
|
||||
|
||||
/**
|
||||
* Provides an object that has a method to import downloads
|
||||
* from the previous SQLite storage format.
|
||||
*
|
||||
* @param aList A DownloadList where each successfully
|
||||
* imported download will be added.
|
||||
* @param aPath The path to the database file.
|
||||
*/
|
||||
this.DownloadImport = function (aList, aPath)
|
||||
{
|
||||
this.list = aList;
|
||||
this.path = aPath;
|
||||
}
|
||||
|
||||
this.DownloadImport.prototype = {
|
||||
/**
|
||||
* Imports unfinished downloads from the previous SQLite storage
|
||||
* format (supporting schemas 7 and up), to the new Download object
|
||||
* format. Each imported download will be added to the DownloadList
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the operation has completed (i.e., every download
|
||||
* from the previous database has been read and added to
|
||||
* the DownloadList)
|
||||
*/
|
||||
import: function () {
|
||||
return Task.spawn(function* task_DI_import() {
|
||||
let connection = yield Sqlite.openConnection({ path: this.path });
|
||||
|
||||
try {
|
||||
let schemaVersion = yield connection.getSchemaVersion();
|
||||
// We don't support schemas older than version 7 (from 2007)
|
||||
// - Version 7 added the columns mimeType, preferredApplication
|
||||
// and preferredAction in 2007
|
||||
// - Version 8 added the column autoResume in 2007
|
||||
// (if we encounter version 7 we will treat autoResume = false)
|
||||
// - Version 9 is the last known version, which added a unique
|
||||
// GUID text column that is not used here
|
||||
if (schemaVersion < 7) {
|
||||
throw new Error("Unable to import in-progress downloads because "
|
||||
+ "the existing profile is too old.");
|
||||
}
|
||||
|
||||
let rows = yield connection.execute("SELECT * FROM moz_downloads");
|
||||
|
||||
for (let row of rows) {
|
||||
try {
|
||||
// Get the DB row data
|
||||
let source = row.getResultByName("source");
|
||||
let target = row.getResultByName("target");
|
||||
let tempPath = row.getResultByName("tempPath");
|
||||
let startTime = row.getResultByName("startTime");
|
||||
let state = row.getResultByName("state");
|
||||
let referrer = row.getResultByName("referrer");
|
||||
let maxBytes = row.getResultByName("maxBytes");
|
||||
let mimeType = row.getResultByName("mimeType");
|
||||
let preferredApplication = row.getResultByName("preferredApplication");
|
||||
let preferredAction = row.getResultByName("preferredAction");
|
||||
let entityID = row.getResultByName("entityID");
|
||||
|
||||
let autoResume = false;
|
||||
try {
|
||||
autoResume = (row.getResultByName("autoResume") == 1);
|
||||
} catch (ex) {
|
||||
// autoResume wasn't present in schema version 7
|
||||
}
|
||||
|
||||
if (!source) {
|
||||
throw new Error("Attempted to import a row with an empty " +
|
||||
"source column.");
|
||||
}
|
||||
|
||||
let resumeDownload = false;
|
||||
|
||||
switch (state) {
|
||||
case DOWNLOAD_NOTSTARTED:
|
||||
case DOWNLOAD_QUEUED:
|
||||
case DOWNLOAD_DOWNLOADING:
|
||||
resumeDownload = true;
|
||||
break;
|
||||
|
||||
case DOWNLOAD_PAUSED:
|
||||
resumeDownload = autoResume;
|
||||
break;
|
||||
|
||||
default:
|
||||
// We won't import downloads in other states
|
||||
continue;
|
||||
}
|
||||
|
||||
// Transform the data
|
||||
let targetPath = NetUtil.newURI(target)
|
||||
.QueryInterface(Ci.nsIFileURL).file.path;
|
||||
|
||||
let launchWhenSucceeded = (preferredAction != Ci.nsIMIMEInfo.saveToDisk);
|
||||
|
||||
let downloadOptions = {
|
||||
source: {
|
||||
url: source,
|
||||
referrer: referrer
|
||||
},
|
||||
target: {
|
||||
path: targetPath,
|
||||
partFilePath: tempPath,
|
||||
},
|
||||
saver: {
|
||||
type: "copy",
|
||||
entityID: entityID
|
||||
},
|
||||
startTime: new Date(startTime / 1000),
|
||||
totalBytes: maxBytes,
|
||||
hasPartialData: !!tempPath,
|
||||
tryToKeepPartialData: true,
|
||||
launchWhenSucceeded: launchWhenSucceeded,
|
||||
contentType: mimeType,
|
||||
launcherPath: preferredApplication
|
||||
};
|
||||
|
||||
// Paused downloads that should not be auto-resumed are considered
|
||||
// in a "canceled" state.
|
||||
if (!resumeDownload) {
|
||||
downloadOptions.canceled = true;
|
||||
}
|
||||
|
||||
let download = yield Downloads.createDownload(downloadOptions);
|
||||
|
||||
yield this.list.add(download);
|
||||
|
||||
if (resumeDownload) {
|
||||
download.start().catch(() => {});
|
||||
} else {
|
||||
yield download.refresh();
|
||||
}
|
||||
|
||||
} catch (ex) {
|
||||
Cu.reportError("Error importing download: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
} finally {
|
||||
yield connection.close();
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
1273
toolkit/components/jsdownloads/src/DownloadIntegration.jsm
Normal file
1273
toolkit/components/jsdownloads/src/DownloadIntegration.jsm
Normal file
File diff suppressed because it is too large
Load diff
309
toolkit/components/jsdownloads/src/DownloadLegacy.js
Normal file
309
toolkit/components/jsdownloads/src/DownloadLegacy.js
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* This component implements the XPCOM interfaces required for integration with
|
||||
* the legacy download components.
|
||||
*
|
||||
* New code is expected to use the "Downloads.jsm" module directly, without
|
||||
* going through the interfaces implemented in this XPCOM component. These
|
||||
* interfaces are only maintained for backwards compatibility with components
|
||||
* that still work synchronously on the main thread.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
|
||||
// DownloadLegacyTransfer
|
||||
|
||||
/**
|
||||
* nsITransfer implementation that provides a bridge to a Download object.
|
||||
*
|
||||
* Legacy downloads work differently than the JavaScript implementation. In the
|
||||
* latter, the caller only provides the properties for the Download object and
|
||||
* the entire process is handled by the "start" method. In the legacy
|
||||
* implementation, the caller must create a separate object to execute the
|
||||
* download, and then make the download visible to the user by hooking it up to
|
||||
* an nsITransfer instance.
|
||||
*
|
||||
* Since nsITransfer instances may be created before the download system is
|
||||
* initialized, and initialization as well as other operations are asynchronous,
|
||||
* this implementation is able to delay all progress and status notifications it
|
||||
* receives until the associated Download object is finally created.
|
||||
*
|
||||
* Conversely, the DownloadLegacySaver object can also receive execution and
|
||||
* cancellation requests asynchronously, before or after it is connected to
|
||||
* this nsITransfer instance. For that reason, those requests are communicated
|
||||
* in a potentially deferred way, using promise objects.
|
||||
*
|
||||
* The component that executes the download implements nsICancelable to receive
|
||||
* cancellation requests, but after cancellation it cannot be reused again.
|
||||
*
|
||||
* Since the components that execute the download may be different and they
|
||||
* don't always give consistent results, this bridge takes care of enforcing the
|
||||
* expectations, for example by ensuring the target file exists when the
|
||||
* download is successful, even if the source has a size of zero bytes.
|
||||
*/
|
||||
function DownloadLegacyTransfer()
|
||||
{
|
||||
this._deferDownload = Promise.defer();
|
||||
}
|
||||
|
||||
DownloadLegacyTransfer.prototype = {
|
||||
classID: Components.ID("{1b4c85df-cbdd-4bb6-b04e-613caece083c}"),
|
||||
|
||||
// nsISupports
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsIWebProgressListener2,
|
||||
Ci.nsITransfer]),
|
||||
|
||||
// nsIWebProgressListener
|
||||
|
||||
onStateChange: function DLT_onStateChange(aWebProgress, aRequest, aStateFlags,
|
||||
aStatus)
|
||||
{
|
||||
if (!Components.isSuccessCode(aStatus)) {
|
||||
this._componentFailed = true;
|
||||
}
|
||||
|
||||
if ((aStateFlags & Ci.nsIWebProgressListener.STATE_START) &&
|
||||
(aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) {
|
||||
|
||||
let blockedByParentalControls = false;
|
||||
// If it is a failed download, aRequest.responseStatus doesn't exist.
|
||||
// (missing file on the server, network failure to download)
|
||||
try {
|
||||
// If the request's response has been blocked by Windows Parental Controls
|
||||
// with an HTTP 450 error code, we must cancel the request synchronously.
|
||||
blockedByParentalControls = aRequest instanceof Ci.nsIHttpChannel &&
|
||||
aRequest.responseStatus == 450;
|
||||
} catch (e) {
|
||||
if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) {
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
}
|
||||
}
|
||||
|
||||
if (blockedByParentalControls) {
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
}
|
||||
|
||||
// The main request has just started. Wait for the associated Download
|
||||
// object to be available before notifying.
|
||||
this._deferDownload.promise.then(download => {
|
||||
// If the request was blocked, now that we have the download object we
|
||||
// should set a flag that can be retrieved later when handling the
|
||||
// cancellation so that the proper error can be thrown.
|
||||
if (blockedByParentalControls) {
|
||||
download._blockedByParentalControls = true;
|
||||
}
|
||||
|
||||
download.saver.onTransferStarted(
|
||||
aRequest,
|
||||
this._cancelable instanceof Ci.nsIHelperAppLauncher);
|
||||
|
||||
// To handle asynchronous cancellation properly, we should hook up the
|
||||
// handler only after we have been notified that the main request
|
||||
// started. We will wait until the main request stopped before
|
||||
// notifying that the download has been canceled. Since the request has
|
||||
// not completed yet, deferCanceled is guaranteed to be set.
|
||||
return download.saver.deferCanceled.promise.then(() => {
|
||||
// Only cancel if the object executing the download is still running.
|
||||
if (this._cancelable && !this._componentFailed) {
|
||||
this._cancelable.cancel(Cr.NS_ERROR_ABORT);
|
||||
if (this._cancelable instanceof Ci.nsIWebBrowserPersist) {
|
||||
// This component will not send the STATE_STOP notification.
|
||||
download.saver.onTransferFinished(aRequest, Cr.NS_ERROR_ABORT);
|
||||
this._cancelable = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}).then(null, Cu.reportError);
|
||||
} else if ((aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
|
||||
(aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) {
|
||||
// The last file has been received, or the download failed. Wait for the
|
||||
// associated Download object to be available before notifying.
|
||||
this._deferDownload.promise.then(download => {
|
||||
// At this point, the hash has been set and we need to copy it to the
|
||||
// DownloadSaver.
|
||||
if (Components.isSuccessCode(aStatus)) {
|
||||
download.saver.setSha256Hash(this._sha256Hash);
|
||||
download.saver.setSignatureInfo(this._signatureInfo);
|
||||
download.saver.setRedirects(this._redirects);
|
||||
}
|
||||
download.saver.onTransferFinished(aRequest, aStatus);
|
||||
}).then(null, Cu.reportError);
|
||||
|
||||
// Release the reference to the component executing the download.
|
||||
this._cancelable = null;
|
||||
}
|
||||
},
|
||||
|
||||
onProgressChange: function DLT_onProgressChange(aWebProgress, aRequest,
|
||||
aCurSelfProgress,
|
||||
aMaxSelfProgress,
|
||||
aCurTotalProgress,
|
||||
aMaxTotalProgress)
|
||||
{
|
||||
this.onProgressChange64(aWebProgress, aRequest, aCurSelfProgress,
|
||||
aMaxSelfProgress, aCurTotalProgress,
|
||||
aMaxTotalProgress);
|
||||
},
|
||||
|
||||
onLocationChange: function () { },
|
||||
|
||||
onStatusChange: function DLT_onStatusChange(aWebProgress, aRequest, aStatus,
|
||||
aMessage)
|
||||
{
|
||||
// The status change may optionally be received in addition to the state
|
||||
// change, but if no network request actually started, it is possible that
|
||||
// we only receive a status change with an error status code.
|
||||
if (!Components.isSuccessCode(aStatus)) {
|
||||
this._componentFailed = true;
|
||||
|
||||
// Wait for the associated Download object to be available.
|
||||
this._deferDownload.promise.then(function DLT_OSC_onDownload(aDownload) {
|
||||
aDownload.saver.onTransferFinished(aRequest, aStatus);
|
||||
}).then(null, Cu.reportError);
|
||||
}
|
||||
},
|
||||
|
||||
onSecurityChange: function () { },
|
||||
|
||||
// nsIWebProgressListener2
|
||||
|
||||
onProgressChange64: function DLT_onProgressChange64(aWebProgress, aRequest,
|
||||
aCurSelfProgress,
|
||||
aMaxSelfProgress,
|
||||
aCurTotalProgress,
|
||||
aMaxTotalProgress)
|
||||
{
|
||||
// Wait for the associated Download object to be available.
|
||||
this._deferDownload.promise.then(function DLT_OPC64_onDownload(aDownload) {
|
||||
aDownload.saver.onProgressBytes(aCurTotalProgress, aMaxTotalProgress);
|
||||
}).then(null, Cu.reportError);
|
||||
},
|
||||
|
||||
onRefreshAttempted: function DLT_onRefreshAttempted(aWebProgress, aRefreshURI,
|
||||
aMillis, aSameURI)
|
||||
{
|
||||
// Indicate that refreshes and redirects are allowed by default. However,
|
||||
// note that download components don't usually call this method at all.
|
||||
return true;
|
||||
},
|
||||
|
||||
// nsITransfer
|
||||
|
||||
init: function DLT_init(aSource, aTarget, aDisplayName, aMIMEInfo, aStartTime,
|
||||
aTempFile, aCancelable, aIsPrivate)
|
||||
{
|
||||
this._cancelable = aCancelable;
|
||||
|
||||
let launchWhenSucceeded = false, contentType = null, launcherPath = null;
|
||||
|
||||
if (aMIMEInfo instanceof Ci.nsIMIMEInfo) {
|
||||
launchWhenSucceeded =
|
||||
aMIMEInfo.preferredAction != Ci.nsIMIMEInfo.saveToDisk;
|
||||
contentType = aMIMEInfo.type;
|
||||
|
||||
let appHandler = aMIMEInfo.preferredApplicationHandler;
|
||||
if (aMIMEInfo.preferredAction == Ci.nsIMIMEInfo.useHelperApp &&
|
||||
appHandler instanceof Ci.nsILocalHandlerApp) {
|
||||
launcherPath = appHandler.executable.path;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new Download object associated to a DownloadLegacySaver, and
|
||||
// wait for it to be available. This operation may cause the entire
|
||||
// download system to initialize before the object is created.
|
||||
Downloads.createDownload({
|
||||
source: { url: aSource.spec, isPrivate: aIsPrivate },
|
||||
target: { path: aTarget.QueryInterface(Ci.nsIFileURL).file.path,
|
||||
partFilePath: aTempFile && aTempFile.path },
|
||||
saver: "legacy",
|
||||
launchWhenSucceeded: launchWhenSucceeded,
|
||||
contentType: contentType,
|
||||
launcherPath: launcherPath
|
||||
}).then(function DLT_I_onDownload(aDownload) {
|
||||
// Legacy components keep partial data when they use a ".part" file.
|
||||
if (aTempFile) {
|
||||
aDownload.tryToKeepPartialData = true;
|
||||
}
|
||||
|
||||
// Start the download before allowing it to be controlled. Ignore errors.
|
||||
aDownload.start().catch(() => {});
|
||||
|
||||
// Start processing all the other events received through nsITransfer.
|
||||
this._deferDownload.resolve(aDownload);
|
||||
|
||||
// Add the download to the list, allowing it to be seen and canceled.
|
||||
return Downloads.getList(Downloads.ALL).then(list => list.add(aDownload));
|
||||
}.bind(this)).then(null, Cu.reportError);
|
||||
},
|
||||
|
||||
setSha256Hash: function (hash)
|
||||
{
|
||||
this._sha256Hash = hash;
|
||||
},
|
||||
|
||||
setSignatureInfo: function (signatureInfo)
|
||||
{
|
||||
this._signatureInfo = signatureInfo;
|
||||
},
|
||||
|
||||
setRedirects: function (redirects)
|
||||
{
|
||||
this._redirects = redirects;
|
||||
},
|
||||
|
||||
// Private methods and properties
|
||||
|
||||
/**
|
||||
* This deferred object contains a promise that is resolved with the Download
|
||||
* object associated with this nsITransfer instance, when it is available.
|
||||
*/
|
||||
_deferDownload: null,
|
||||
|
||||
/**
|
||||
* Reference to the component that is executing the download. This component
|
||||
* allows cancellation through its nsICancelable interface.
|
||||
*/
|
||||
_cancelable: null,
|
||||
|
||||
/**
|
||||
* Indicates that the component that executes the download has notified a
|
||||
* failure condition. In this case, we should never use the component methods
|
||||
* that cancel the download.
|
||||
*/
|
||||
_componentFailed: false,
|
||||
|
||||
/**
|
||||
* Save the SHA-256 hash in raw bytes of the downloaded file.
|
||||
*/
|
||||
_sha256Hash: null,
|
||||
|
||||
/**
|
||||
* Save the signature info in a serialized protobuf of the downloaded file.
|
||||
*/
|
||||
_signatureInfo: null,
|
||||
};
|
||||
|
||||
// Module
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadLegacyTransfer]);
|
||||
559
toolkit/components/jsdownloads/src/DownloadList.jsm
Normal file
559
toolkit/components/jsdownloads/src/DownloadList.jsm
Normal file
|
|
@ -0,0 +1,559 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* This file includes the following constructors and global objects:
|
||||
*
|
||||
* DownloadList
|
||||
* Represents a collection of Download objects that can be viewed and managed by
|
||||
* the user interface, and persisted across sessions.
|
||||
*
|
||||
* DownloadCombinedList
|
||||
* Provides a unified, unordered list combining public and private downloads.
|
||||
*
|
||||
* DownloadSummary
|
||||
* Provides an aggregated view on the contents of a DownloadList.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"DownloadList",
|
||||
"DownloadCombinedList",
|
||||
"DownloadSummary",
|
||||
];
|
||||
|
||||
// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
// DownloadList
|
||||
|
||||
/**
|
||||
* Represents a collection of Download objects that can be viewed and managed by
|
||||
* the user interface, and persisted across sessions.
|
||||
*/
|
||||
this.DownloadList = function ()
|
||||
{
|
||||
this._downloads = [];
|
||||
this._views = new Set();
|
||||
}
|
||||
|
||||
this.DownloadList.prototype = {
|
||||
/**
|
||||
* Array of Download objects currently in the list.
|
||||
*/
|
||||
_downloads: null,
|
||||
|
||||
/**
|
||||
* Retrieves a snapshot of the downloads that are currently in the list. The
|
||||
* returned array does not change when downloads are added or removed, though
|
||||
* the Download objects it contains are still updated in real time.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves An array of Download objects.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
getAll: function DL_getAll() {
|
||||
return Promise.resolve(Array.slice(this._downloads, 0));
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a new download to the end of the items list.
|
||||
*
|
||||
* @note When a download is added to the list, its "onchange" event is
|
||||
* registered by the list, thus it cannot be used to monitor the
|
||||
* download. To receive change notifications for downloads that are
|
||||
* added to the list, use the addView method to register for
|
||||
* onDownloadChanged notifications.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object to add.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the download has been added.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
add: function DL_add(aDownload) {
|
||||
this._downloads.push(aDownload);
|
||||
aDownload.onchange = this._change.bind(this, aDownload);
|
||||
this._notifyAllViews("onDownloadAdded", aDownload);
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a download from the list. If the download was already removed,
|
||||
* this method has no effect.
|
||||
*
|
||||
* This method does not change the state of the download, to allow adding it
|
||||
* to another list, or control it directly. If you want to dispose of the
|
||||
* download object, you should cancel it afterwards, and remove any partially
|
||||
* downloaded data if needed.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object to remove.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the download has been removed.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
remove: function DL_remove(aDownload) {
|
||||
let index = this._downloads.indexOf(aDownload);
|
||||
if (index != -1) {
|
||||
this._downloads.splice(index, 1);
|
||||
aDownload.onchange = null;
|
||||
this._notifyAllViews("onDownloadRemoved", aDownload);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* This function is called when "onchange" events of downloads occur.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object that changed.
|
||||
*/
|
||||
_change: function DL_change(aDownload) {
|
||||
this._notifyAllViews("onDownloadChanged", aDownload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set of currently registered views.
|
||||
*/
|
||||
_views: null,
|
||||
|
||||
/**
|
||||
* Adds a view that will be notified of changes to downloads. The newly added
|
||||
* view will receive onDownloadAdded notifications for all the downloads that
|
||||
* are already in the list.
|
||||
*
|
||||
* @param aView
|
||||
* The view object to add. The following methods may be defined:
|
||||
* {
|
||||
* onDownloadAdded: function (aDownload) {
|
||||
* // Called after aDownload is added to the end of the list.
|
||||
* },
|
||||
* onDownloadChanged: function (aDownload) {
|
||||
* // Called after the properties of aDownload change.
|
||||
* },
|
||||
* onDownloadRemoved: function (aDownload) {
|
||||
* // Called after aDownload is removed from the list.
|
||||
* },
|
||||
* }
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the view has been registered and all the onDownloadAdded
|
||||
* notifications for the existing downloads have been sent.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
addView: function DL_addView(aView)
|
||||
{
|
||||
this._views.add(aView);
|
||||
|
||||
if ("onDownloadAdded" in aView) {
|
||||
for (let download of this._downloads) {
|
||||
try {
|
||||
aView.onDownloadAdded(download);
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a view that was previously added using addView.
|
||||
*
|
||||
* @param aView
|
||||
* The view object to remove.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the view has been removed. At this point, the removed view
|
||||
* will not receive any more notifications.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
removeView: function DL_removeView(aView)
|
||||
{
|
||||
this._views.delete(aView);
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies all the views of a download addition, change, or removal.
|
||||
*
|
||||
* @param aMethodName
|
||||
* String containing the name of the method to call on the view.
|
||||
* @param aDownload
|
||||
* The Download object that changed.
|
||||
*/
|
||||
_notifyAllViews: function (aMethodName, aDownload) {
|
||||
for (let view of this._views) {
|
||||
try {
|
||||
if (aMethodName in view) {
|
||||
view[aMethodName](aDownload);
|
||||
}
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes downloads from the list that have finished, have failed, or have
|
||||
* been canceled without keeping partial data. A filter function may be
|
||||
* specified to remove only a subset of those downloads.
|
||||
*
|
||||
* This method finalizes each removed download, ensuring that any partially
|
||||
* downloaded data associated with it is also removed.
|
||||
*
|
||||
* @param aFilterFn
|
||||
* The filter function is called with each download as its only
|
||||
* argument, and should return true to remove the download and false
|
||||
* to keep it. This parameter may be null or omitted to have no
|
||||
* additional filter.
|
||||
*/
|
||||
removeFinished: function DL_removeFinished(aFilterFn) {
|
||||
Task.spawn(function* () {
|
||||
let list = yield this.getAll();
|
||||
for (let download of list) {
|
||||
// Remove downloads that have been canceled, even if the cancellation
|
||||
// operation hasn't completed yet so we don't check "stopped" here.
|
||||
// Failed downloads with partial data are also removed.
|
||||
if (download.stopped && (!download.hasPartialData || download.error) &&
|
||||
(!aFilterFn || aFilterFn(download))) {
|
||||
// Remove the download first, so that the views don't get the change
|
||||
// notifications that may occur during finalization.
|
||||
yield this.remove(download);
|
||||
// Ensure that the download is stopped and no partial data is kept.
|
||||
// This works even if the download state has changed meanwhile. We
|
||||
// don't need to wait for the procedure to be complete before
|
||||
// processing the other downloads in the list.
|
||||
download.finalize(true).then(null, Cu.reportError);
|
||||
}
|
||||
}
|
||||
}.bind(this)).then(null, Cu.reportError);
|
||||
},
|
||||
};
|
||||
|
||||
// DownloadCombinedList
|
||||
|
||||
/**
|
||||
* Provides a unified, unordered list combining public and private downloads.
|
||||
*
|
||||
* Download objects added to this list are also added to one of the two
|
||||
* underlying lists, based on their "source.isPrivate" property. Views on this
|
||||
* list will receive notifications for both public and private downloads.
|
||||
*
|
||||
* @param aPublicList
|
||||
* Underlying DownloadList containing public downloads.
|
||||
* @param aPrivateList
|
||||
* Underlying DownloadList containing private downloads.
|
||||
*/
|
||||
this.DownloadCombinedList = function (aPublicList, aPrivateList)
|
||||
{
|
||||
DownloadList.call(this);
|
||||
this._publicList = aPublicList;
|
||||
this._privateList = aPrivateList;
|
||||
aPublicList.addView(this).then(null, Cu.reportError);
|
||||
aPrivateList.addView(this).then(null, Cu.reportError);
|
||||
}
|
||||
|
||||
this.DownloadCombinedList.prototype = {
|
||||
__proto__: DownloadList.prototype,
|
||||
|
||||
/**
|
||||
* Underlying DownloadList containing public downloads.
|
||||
*/
|
||||
_publicList: null,
|
||||
|
||||
/**
|
||||
* Underlying DownloadList containing private downloads.
|
||||
*/
|
||||
_privateList: null,
|
||||
|
||||
/**
|
||||
* Adds a new download to the end of the items list.
|
||||
*
|
||||
* @note When a download is added to the list, its "onchange" event is
|
||||
* registered by the list, thus it cannot be used to monitor the
|
||||
* download. To receive change notifications for downloads that are
|
||||
* added to the list, use the addView method to register for
|
||||
* onDownloadChanged notifications.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object to add.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the download has been added.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
add: function (aDownload)
|
||||
{
|
||||
if (aDownload.source.isPrivate) {
|
||||
return this._privateList.add(aDownload);
|
||||
}
|
||||
return this._publicList.add(aDownload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a download from the list. If the download was already removed,
|
||||
* this method has no effect.
|
||||
*
|
||||
* This method does not change the state of the download, to allow adding it
|
||||
* to another list, or control it directly. If you want to dispose of the
|
||||
* download object, you should cancel it afterwards, and remove any partially
|
||||
* downloaded data if needed.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object to remove.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the download has been removed.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
remove: function (aDownload)
|
||||
{
|
||||
if (aDownload.source.isPrivate) {
|
||||
return this._privateList.remove(aDownload);
|
||||
}
|
||||
return this._publicList.remove(aDownload);
|
||||
},
|
||||
|
||||
// DownloadList view
|
||||
|
||||
onDownloadAdded: function (aDownload)
|
||||
{
|
||||
this._downloads.push(aDownload);
|
||||
this._notifyAllViews("onDownloadAdded", aDownload);
|
||||
},
|
||||
|
||||
onDownloadChanged: function (aDownload)
|
||||
{
|
||||
this._notifyAllViews("onDownloadChanged", aDownload);
|
||||
},
|
||||
|
||||
onDownloadRemoved: function (aDownload)
|
||||
{
|
||||
let index = this._downloads.indexOf(aDownload);
|
||||
if (index != -1) {
|
||||
this._downloads.splice(index, 1);
|
||||
}
|
||||
this._notifyAllViews("onDownloadRemoved", aDownload);
|
||||
},
|
||||
};
|
||||
|
||||
// DownloadSummary
|
||||
|
||||
/**
|
||||
* Provides an aggregated view on the contents of a DownloadList.
|
||||
*/
|
||||
this.DownloadSummary = function ()
|
||||
{
|
||||
this._downloads = [];
|
||||
this._views = new Set();
|
||||
}
|
||||
|
||||
this.DownloadSummary.prototype = {
|
||||
/**
|
||||
* Array of Download objects that are currently part of the summary.
|
||||
*/
|
||||
_downloads: null,
|
||||
|
||||
/**
|
||||
* Underlying DownloadList whose contents should be summarized.
|
||||
*/
|
||||
_list: null,
|
||||
|
||||
/**
|
||||
* This method may be called once to bind this object to a DownloadList.
|
||||
*
|
||||
* Views on the summarized data can be registered before this object is bound
|
||||
* to an actual list. This allows the summary to be used without requiring
|
||||
* the initialization of the DownloadList first.
|
||||
*
|
||||
* @param aList
|
||||
* Underlying DownloadList whose contents should be summarized.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the view on the underlying list has been registered.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
bindToList: function (aList)
|
||||
{
|
||||
if (this._list) {
|
||||
throw new Error("bindToList may be called only once.");
|
||||
}
|
||||
|
||||
return aList.addView(this).then(() => {
|
||||
// Set the list reference only after addView has returned, so that we don't
|
||||
// send a notification to our views for each download that is added.
|
||||
this._list = aList;
|
||||
this._onListChanged();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Set of currently registered views.
|
||||
*/
|
||||
_views: null,
|
||||
|
||||
/**
|
||||
* Adds a view that will be notified of changes to the summary. The newly
|
||||
* added view will receive an initial onSummaryChanged notification.
|
||||
*
|
||||
* @param aView
|
||||
* The view object to add. The following methods may be defined:
|
||||
* {
|
||||
* onSummaryChanged: function () {
|
||||
* // Called after any property of the summary has changed.
|
||||
* },
|
||||
* }
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the view has been registered and the onSummaryChanged
|
||||
* notification has been sent.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
addView: function (aView)
|
||||
{
|
||||
this._views.add(aView);
|
||||
|
||||
if ("onSummaryChanged" in aView) {
|
||||
try {
|
||||
aView.onSummaryChanged();
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a view that was previously added using addView.
|
||||
*
|
||||
* @param aView
|
||||
* The view object to remove.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the view has been removed. At this point, the removed view
|
||||
* will not receive any more notifications.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
removeView: function (aView)
|
||||
{
|
||||
this._views.delete(aView);
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates whether all the downloads are currently stopped.
|
||||
*/
|
||||
allHaveStopped: true,
|
||||
|
||||
/**
|
||||
* Indicates the total number of bytes to be transferred before completing all
|
||||
* the downloads that are currently in progress.
|
||||
*
|
||||
* For downloads that do not have a known final size, the number of bytes
|
||||
* currently transferred is reported as part of this property.
|
||||
*
|
||||
* This is zero if no downloads are currently in progress.
|
||||
*/
|
||||
progressTotalBytes: 0,
|
||||
|
||||
/**
|
||||
* Number of bytes currently transferred as part of all the downloads that are
|
||||
* currently in progress.
|
||||
*
|
||||
* This is zero if no downloads are currently in progress.
|
||||
*/
|
||||
progressCurrentBytes: 0,
|
||||
|
||||
/**
|
||||
* This function is called when any change in the list of downloads occurs,
|
||||
* and will recalculate the summary and notify the views in case the
|
||||
* aggregated properties are different.
|
||||
*/
|
||||
_onListChanged: function () {
|
||||
let allHaveStopped = true;
|
||||
let progressTotalBytes = 0;
|
||||
let progressCurrentBytes = 0;
|
||||
|
||||
// Recalculate the aggregated state. See the description of the individual
|
||||
// properties for an explanation of the summarization logic.
|
||||
for (let download of this._downloads) {
|
||||
if (!download.stopped) {
|
||||
allHaveStopped = false;
|
||||
progressTotalBytes += download.hasProgress ? download.totalBytes
|
||||
: download.currentBytes;
|
||||
progressCurrentBytes += download.currentBytes;
|
||||
}
|
||||
}
|
||||
|
||||
// Exit now if the properties did not change.
|
||||
if (this.allHaveStopped == allHaveStopped &&
|
||||
this.progressTotalBytes == progressTotalBytes &&
|
||||
this.progressCurrentBytes == progressCurrentBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.allHaveStopped = allHaveStopped;
|
||||
this.progressTotalBytes = progressTotalBytes;
|
||||
this.progressCurrentBytes = progressCurrentBytes;
|
||||
|
||||
// Notify all the views that our properties changed.
|
||||
for (let view of this._views) {
|
||||
try {
|
||||
if ("onSummaryChanged" in view) {
|
||||
view.onSummaryChanged();
|
||||
}
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// DownloadList view
|
||||
|
||||
onDownloadAdded: function (aDownload)
|
||||
{
|
||||
this._downloads.push(aDownload);
|
||||
if (this._list) {
|
||||
this._onListChanged();
|
||||
}
|
||||
},
|
||||
|
||||
onDownloadChanged: function (aDownload)
|
||||
{
|
||||
this._onListChanged();
|
||||
},
|
||||
|
||||
onDownloadRemoved: function (aDownload)
|
||||
{
|
||||
let index = this._downloads.indexOf(aDownload);
|
||||
if (index != -1) {
|
||||
this._downloads.splice(index, 1);
|
||||
}
|
||||
this._onListChanged();
|
||||
},
|
||||
};
|
||||
275
toolkit/components/jsdownloads/src/DownloadPlatform.cpp
Normal file
275
toolkit/components/jsdownloads/src/DownloadPlatform.cpp
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "DownloadPlatform.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsString.h"
|
||||
#include "nsINestedURI.h"
|
||||
#include "nsIProtocolHandler.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
#define PREF_BDM_ADDTORECENTDOCS "browser.download.manager.addToRecentDocs"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <shlobj.h>
|
||||
#include <urlmon.h>
|
||||
#include "nsILocalFileWin.h"
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include "../../../../xpcom/io/CocoaFileUtils.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include "FennecJNIWrappers.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
#include <gtk/gtk.h>
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
DownloadPlatform *DownloadPlatform::gDownloadPlatformService = nullptr;
|
||||
|
||||
NS_IMPL_ISUPPORTS(DownloadPlatform, mozIDownloadPlatform);
|
||||
|
||||
DownloadPlatform* DownloadPlatform::GetDownloadPlatform()
|
||||
{
|
||||
if (!gDownloadPlatformService) {
|
||||
gDownloadPlatformService = new DownloadPlatform();
|
||||
}
|
||||
|
||||
NS_ADDREF(gDownloadPlatformService);
|
||||
|
||||
#if defined(MOZ_WIDGET_GTK)
|
||||
g_type_init();
|
||||
#endif
|
||||
|
||||
return gDownloadPlatformService;
|
||||
}
|
||||
|
||||
#ifdef MOZ_ENABLE_GIO
|
||||
static void gio_set_metadata_done(GObject *source_obj, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
GError *err = nullptr;
|
||||
g_file_set_attributes_finish(G_FILE(source_obj), res, nullptr, &err);
|
||||
if (err) {
|
||||
#ifdef DEBUG
|
||||
NS_DebugBreak(NS_DEBUG_WARNING, "Set file metadata failed: ", err->message, __FILE__, __LINE__);
|
||||
#endif
|
||||
g_error_free(err);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// Caller is responsible for freeing any result (CF Create Rule)
|
||||
CFURLRef CreateCFURLFromNSIURI(nsIURI *aURI) {
|
||||
nsAutoCString spec;
|
||||
if (aURI) {
|
||||
aURI->GetSpec(spec);
|
||||
}
|
||||
|
||||
CFStringRef urlStr = ::CFStringCreateWithCString(kCFAllocatorDefault,
|
||||
spec.get(),
|
||||
kCFStringEncodingUTF8);
|
||||
if (!urlStr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFURLRef url = ::CFURLCreateWithString(kCFAllocatorDefault,
|
||||
urlStr,
|
||||
NULL);
|
||||
|
||||
::CFRelease(urlStr);
|
||||
|
||||
return url;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult DownloadPlatform::DownloadDone(nsIURI* aSource, nsIURI* aReferrer, nsIFile* aTarget,
|
||||
const nsACString& aContentType, bool aIsPrivate)
|
||||
{
|
||||
#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) \
|
||||
|| defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GONK)
|
||||
|
||||
nsAutoString path;
|
||||
if (aTarget && NS_SUCCEEDED(aTarget->GetPath(path))) {
|
||||
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)
|
||||
// On Windows and Gtk, add the download to the system's "recent documents"
|
||||
// list, with a pref to disable.
|
||||
{
|
||||
bool addToRecentDocs = Preferences::GetBool(PREF_BDM_ADDTORECENTDOCS);
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if (jni::IsFennec() && addToRecentDocs) {
|
||||
java::DownloadsIntegration::ScanMedia(path, aContentType);
|
||||
}
|
||||
#else
|
||||
if (addToRecentDocs && !aIsPrivate) {
|
||||
#ifdef XP_WIN
|
||||
::SHAddToRecentDocs(SHARD_PATHW, path.get());
|
||||
#elif defined(MOZ_WIDGET_GTK)
|
||||
GtkRecentManager* manager = gtk_recent_manager_get_default();
|
||||
|
||||
gchar* uri = g_filename_to_uri(NS_ConvertUTF16toUTF8(path).get(),
|
||||
nullptr, nullptr);
|
||||
if (uri) {
|
||||
gtk_recent_manager_add_item(manager, uri);
|
||||
g_free(uri);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_ENABLE_GIO
|
||||
// Use GIO to store the source URI for later display in the file manager.
|
||||
GFile* gio_file = g_file_new_for_path(NS_ConvertUTF16toUTF8(path).get());
|
||||
nsCString source_uri;
|
||||
nsresult rv = aSource->GetSpec(source_uri);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
GFileInfo *file_info = g_file_info_new();
|
||||
g_file_info_set_attribute_string(file_info, "metadata::download-uri", source_uri.get());
|
||||
g_file_set_attributes_async(gio_file,
|
||||
file_info,
|
||||
G_FILE_QUERY_INFO_NONE,
|
||||
G_PRIORITY_DEFAULT,
|
||||
nullptr, gio_set_metadata_done, nullptr);
|
||||
g_object_unref(file_info);
|
||||
g_object_unref(gio_file);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// On OS X, make the downloads stack bounce.
|
||||
CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault,
|
||||
NS_ConvertUTF16toUTF8(path).get(),
|
||||
kCFStringEncodingUTF8);
|
||||
CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter();
|
||||
::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"),
|
||||
observedObject, nullptr, TRUE);
|
||||
::CFRelease(observedObject);
|
||||
|
||||
// Add OS X origin and referrer file metadata
|
||||
CFStringRef pathCFStr = NULL;
|
||||
if (!path.IsEmpty()) {
|
||||
pathCFStr = ::CFStringCreateWithCharacters(kCFAllocatorDefault,
|
||||
(const UniChar*)path.get(),
|
||||
path.Length());
|
||||
}
|
||||
if (pathCFStr) {
|
||||
bool isFromWeb = IsURLPossiblyFromWeb(aSource);
|
||||
|
||||
CFURLRef sourceCFURL = CreateCFURLFromNSIURI(aSource);
|
||||
CFURLRef referrerCFURL = CreateCFURLFromNSIURI(aReferrer);
|
||||
|
||||
CocoaFileUtils::AddOriginMetadataToFile(pathCFStr,
|
||||
sourceCFURL,
|
||||
referrerCFURL);
|
||||
CocoaFileUtils::AddQuarantineMetadataToFile(pathCFStr,
|
||||
sourceCFURL,
|
||||
referrerCFURL,
|
||||
isFromWeb);
|
||||
|
||||
::CFRelease(pathCFStr);
|
||||
if (sourceCFURL) {
|
||||
::CFRelease(sourceCFURL);
|
||||
}
|
||||
if (referrerCFURL) {
|
||||
::CFRelease(referrerCFURL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult DownloadPlatform::MapUrlToZone(const nsAString& aURL,
|
||||
uint32_t* aZone)
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
RefPtr<IInternetSecurityManager> inetSecMgr;
|
||||
if (FAILED(CoCreateInstance(CLSID_InternetSecurityManager, NULL,
|
||||
CLSCTX_ALL, IID_IInternetSecurityManager,
|
||||
getter_AddRefs(inetSecMgr)))) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
DWORD zone;
|
||||
if (inetSecMgr->MapUrlToZone(PromiseFlatString(aURL).get(),
|
||||
&zone, 0) != S_OK) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
} else {
|
||||
*aZone = zone;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
#else
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Check if a URI is likely to be web-based, by checking its URI flags.
|
||||
// If in doubt (e.g. if anything fails during the check) claims things
|
||||
// are from the web.
|
||||
bool DownloadPlatform::IsURLPossiblyFromWeb(nsIURI* aURI)
|
||||
{
|
||||
nsCOMPtr<nsIIOService> ios = do_GetIOService();
|
||||
nsCOMPtr<nsIURI> uri = aURI;
|
||||
if (!ios) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while (uri) {
|
||||
// We're not using nsIIOService::ProtocolHasFlags because it doesn't
|
||||
// take per-URI flags into account. We're also not using
|
||||
// NS_URIChainHasFlags because we're checking for *any* of 3 flags
|
||||
// to be present on *all* of the nested URIs, which it can't do.
|
||||
nsAutoCString scheme;
|
||||
nsresult rv = uri->GetScheme(scheme);
|
||||
if (NS_FAILED(rv)) {
|
||||
return true;
|
||||
}
|
||||
nsCOMPtr<nsIProtocolHandler> ph;
|
||||
rv = ios->GetProtocolHandler(scheme.get(), getter_AddRefs(ph));
|
||||
if (NS_FAILED(rv)) {
|
||||
return true;
|
||||
}
|
||||
uint32_t flags;
|
||||
rv = ph->DoGetProtocolFlags(uri, &flags);
|
||||
if (NS_FAILED(rv)) {
|
||||
return true;
|
||||
}
|
||||
// If not dangerous to load, not a UI resource and not a local file,
|
||||
// assume this is from the web:
|
||||
if (!(flags & nsIProtocolHandler::URI_DANGEROUS_TO_LOAD) &&
|
||||
!(flags & nsIProtocolHandler::URI_IS_UI_RESOURCE) &&
|
||||
!(flags & nsIProtocolHandler::URI_IS_LOCAL_FILE)) {
|
||||
return true;
|
||||
}
|
||||
// Otherwise, check if the URI is nested, and if so go through
|
||||
// the loop again:
|
||||
nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri);
|
||||
uri = nullptr;
|
||||
if (nestedURI) {
|
||||
rv = nestedURI->GetInnerURI(getter_AddRefs(uri));
|
||||
if (NS_FAILED(rv)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
34
toolkit/components/jsdownloads/src/DownloadPlatform.h
Normal file
34
toolkit/components/jsdownloads/src/DownloadPlatform.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* 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/. */
|
||||
|
||||
#ifndef __DownloadPlatform_h__
|
||||
#define __DownloadPlatform_h__
|
||||
|
||||
#include "mozIDownloadPlatform.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
class nsIURI;
|
||||
|
||||
class DownloadPlatform : public mozIDownloadPlatform
|
||||
{
|
||||
protected:
|
||||
|
||||
virtual ~DownloadPlatform() { }
|
||||
|
||||
public:
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZIDOWNLOADPLATFORM
|
||||
|
||||
DownloadPlatform() { }
|
||||
|
||||
static DownloadPlatform *gDownloadPlatformService;
|
||||
|
||||
static DownloadPlatform* GetDownloadPlatform();
|
||||
|
||||
private:
|
||||
static bool IsURLPossiblyFromWeb(nsIURI* aURI);
|
||||
};
|
||||
|
||||
#endif
|
||||
203
toolkit/components/jsdownloads/src/DownloadStore.jsm
Normal file
203
toolkit/components/jsdownloads/src/DownloadStore.jsm
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Handles serialization of Download objects and persistence into a file, so
|
||||
* that the state of downloads can be restored across sessions.
|
||||
*
|
||||
* The file is stored in JSON format, without indentation. With indentation
|
||||
* applied, the file would look like this:
|
||||
*
|
||||
* {
|
||||
* "list": [
|
||||
* {
|
||||
* "source": "http://www.example.com/download.txt",
|
||||
* "target": "/home/user/Downloads/download.txt"
|
||||
* },
|
||||
* {
|
||||
* "source": {
|
||||
* "url": "http://www.example.com/download.txt",
|
||||
* "referrer": "http://www.example.com/referrer.html"
|
||||
* },
|
||||
* "target": "/home/user/Downloads/download-2.txt"
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"DownloadStore",
|
||||
];
|
||||
|
||||
// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm")
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gTextDecoder", function () {
|
||||
return new TextDecoder();
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gTextEncoder", function () {
|
||||
return new TextEncoder();
|
||||
});
|
||||
|
||||
// DownloadStore
|
||||
|
||||
/**
|
||||
* Handles serialization of Download objects and persistence into a file, so
|
||||
* that the state of downloads can be restored across sessions.
|
||||
*
|
||||
* @param aList
|
||||
* DownloadList object to be populated or serialized.
|
||||
* @param aPath
|
||||
* String containing the file path where data should be saved.
|
||||
*/
|
||||
this.DownloadStore = function (aList, aPath)
|
||||
{
|
||||
this.list = aList;
|
||||
this.path = aPath;
|
||||
}
|
||||
|
||||
this.DownloadStore.prototype = {
|
||||
/**
|
||||
* DownloadList object to be populated or serialized.
|
||||
*/
|
||||
list: null,
|
||||
|
||||
/**
|
||||
* String containing the file path where data should be saved.
|
||||
*/
|
||||
path: "",
|
||||
|
||||
/**
|
||||
* This function is called with a Download object as its first argument, and
|
||||
* should return true if the item should be saved.
|
||||
*/
|
||||
onsaveitem: () => true,
|
||||
|
||||
/**
|
||||
* Loads persistent downloads from the file to the list.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the operation finished successfully.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
load: function DS_load()
|
||||
{
|
||||
return Task.spawn(function* task_DS_load() {
|
||||
let bytes;
|
||||
try {
|
||||
bytes = yield OS.File.read(this.path);
|
||||
} catch (ex) {
|
||||
if (!(ex instanceof OS.File.Error) || !ex.becauseNoSuchFile) {
|
||||
throw ex;
|
||||
}
|
||||
// If the file does not exist, there are no downloads to load.
|
||||
return;
|
||||
}
|
||||
|
||||
let storeData = JSON.parse(gTextDecoder.decode(bytes));
|
||||
|
||||
// Create live downloads based on the static snapshot.
|
||||
for (let downloadData of storeData.list) {
|
||||
try {
|
||||
let download = yield Downloads.createDownload(downloadData);
|
||||
try {
|
||||
if (!download.succeeded && !download.canceled && !download.error) {
|
||||
// Try to restart the download if it was in progress during the
|
||||
// previous session. Ignore errors.
|
||||
download.start().catch(() => {});
|
||||
} else {
|
||||
// If the download was not in progress, try to update the current
|
||||
// progress from disk. This is relevant in case we retained
|
||||
// partially downloaded data.
|
||||
yield download.refresh();
|
||||
}
|
||||
} finally {
|
||||
// Add the download to the list if we succeeded in creating it,
|
||||
// after we have updated its initial state.
|
||||
yield this.list.add(download);
|
||||
}
|
||||
} catch (ex) {
|
||||
// If an item is unrecognized, don't prevent others from being loaded.
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Saves persistent downloads from the list to the file.
|
||||
*
|
||||
* If an error occurs, the previous file is not deleted.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the operation finished successfully.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
save: function DS_save()
|
||||
{
|
||||
return Task.spawn(function* task_DS_save() {
|
||||
let downloads = yield this.list.getAll();
|
||||
|
||||
// Take a static snapshot of the current state of all the downloads.
|
||||
let storeData = { list: [] };
|
||||
let atLeastOneDownload = false;
|
||||
for (let download of downloads) {
|
||||
try {
|
||||
if (!this.onsaveitem(download)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let serializable = download.toSerializable();
|
||||
if (!serializable) {
|
||||
// This item cannot be persisted across sessions.
|
||||
continue;
|
||||
}
|
||||
storeData.list.push(serializable);
|
||||
atLeastOneDownload = true;
|
||||
} catch (ex) {
|
||||
// If an item cannot be converted to a serializable form, don't
|
||||
// prevent others from being saved.
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (atLeastOneDownload) {
|
||||
// Create or overwrite the file if there are downloads to save.
|
||||
let bytes = gTextEncoder.encode(JSON.stringify(storeData));
|
||||
yield OS.File.writeAtomic(this.path, bytes,
|
||||
{ tmpPath: this.path + ".tmp" });
|
||||
} else {
|
||||
// Remove the file if there are no downloads to save at all.
|
||||
try {
|
||||
yield OS.File.remove(this.path);
|
||||
} catch (ex) {
|
||||
if (!(ex instanceof OS.File.Error) ||
|
||||
!(ex.becauseNoSuchFile || ex.becauseAccessDenied)) {
|
||||
throw ex;
|
||||
}
|
||||
// On Windows, we may get an access denied error instead of a no such
|
||||
// file error if the file existed before, and was recently deleted.
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
};
|
||||
243
toolkit/components/jsdownloads/src/DownloadUIHelper.jsm
Normal file
243
toolkit/components/jsdownloads/src/DownloadUIHelper.jsm
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Provides functions to handle status and messages in the user interface.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"DownloadUIHelper",
|
||||
];
|
||||
|
||||
// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
const kStringBundleUrl =
|
||||
"chrome://mozapps/locale/downloads/downloads.properties";
|
||||
|
||||
const kStringsRequiringFormatting = {
|
||||
fileExecutableSecurityWarning: true,
|
||||
cancelDownloadsOKTextMultiple: true,
|
||||
quitCancelDownloadsAlertMsgMultiple: true,
|
||||
quitCancelDownloadsAlertMsgMacMultiple: true,
|
||||
offlineCancelDownloadsAlertMsgMultiple: true,
|
||||
leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple2: true
|
||||
};
|
||||
|
||||
// DownloadUIHelper
|
||||
|
||||
/**
|
||||
* Provides functions to handle status and messages in the user interface.
|
||||
*/
|
||||
this.DownloadUIHelper = {
|
||||
/**
|
||||
* Returns an object that can be used to display prompts related to downloads.
|
||||
*
|
||||
* The prompts may be either anchored to a specified window, or anchored to
|
||||
* the most recently active window, for example if the prompt is displayed in
|
||||
* response to global notifications that are not associated with any window.
|
||||
*
|
||||
* @param aParent
|
||||
* If specified, should reference the nsIDOMWindow to which the prompts
|
||||
* should be attached. If omitted, the prompts will be attached to the
|
||||
* most recently active window.
|
||||
*
|
||||
* @return A DownloadPrompter object.
|
||||
*/
|
||||
getPrompter: function (aParent)
|
||||
{
|
||||
return new DownloadPrompter(aParent || null);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an object whose keys are the string names from the downloads string
|
||||
* bundle, and whose values are either the translated strings or functions
|
||||
* returning formatted strings.
|
||||
*/
|
||||
XPCOMUtils.defineLazyGetter(DownloadUIHelper, "strings", function () {
|
||||
let strings = {};
|
||||
let sb = Services.strings.createBundle(kStringBundleUrl);
|
||||
let enumerator = sb.getSimpleEnumeration();
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
|
||||
let stringName = string.key;
|
||||
if (stringName in kStringsRequiringFormatting) {
|
||||
strings[stringName] = function () {
|
||||
// Convert "arguments" to a real array before calling into XPCOM.
|
||||
return sb.formatStringFromName(stringName,
|
||||
Array.slice(arguments, 0),
|
||||
arguments.length);
|
||||
};
|
||||
} else {
|
||||
strings[stringName] = string.value;
|
||||
}
|
||||
}
|
||||
return strings;
|
||||
});
|
||||
|
||||
// DownloadPrompter
|
||||
|
||||
/**
|
||||
* Allows displaying prompts related to downloads.
|
||||
*
|
||||
* @param aParent
|
||||
* The nsIDOMWindow to which prompts should be attached, or null to
|
||||
* attach prompts to the most recently active window.
|
||||
*/
|
||||
this.DownloadPrompter = function (aParent)
|
||||
{
|
||||
if (AppConstants.MOZ_B2G) {
|
||||
// On B2G there is no prompter implementation.
|
||||
this._prompter = null;
|
||||
} else {
|
||||
this._prompter = Services.ww.getNewPrompter(aParent);
|
||||
}
|
||||
}
|
||||
|
||||
this.DownloadPrompter.prototype = {
|
||||
/**
|
||||
* Constants with the different type of prompts.
|
||||
*/
|
||||
ON_QUIT: "prompt-on-quit",
|
||||
ON_OFFLINE: "prompt-on-offline",
|
||||
ON_LEAVE_PRIVATE_BROWSING: "prompt-on-leave-private-browsing",
|
||||
|
||||
/**
|
||||
* nsIPrompt instance for displaying messages.
|
||||
*/
|
||||
_prompter: null,
|
||||
|
||||
/**
|
||||
* Displays a warning message box that informs that the specified file is
|
||||
* executable, and asks whether the user wants to launch it. The user is
|
||||
* given the option of disabling future instances of this warning.
|
||||
*
|
||||
* @param aPath
|
||||
* String containing the full path to the file to be opened.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves Boolean indicating whether the launch operation can continue.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
confirmLaunchExecutable: function (aPath)
|
||||
{
|
||||
const kPrefAlertOnEXEOpen = "browser.download.manager.alertOnEXEOpen";
|
||||
|
||||
try {
|
||||
// Always launch in case we have no prompter implementation.
|
||||
if (!this._prompter) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!Services.prefs.getBoolPref(kPrefAlertOnEXEOpen)) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
} catch (ex) {
|
||||
// If the preference does not exist, continue with the prompt.
|
||||
}
|
||||
|
||||
let leafName = OS.Path.basename(aPath);
|
||||
|
||||
let s = DownloadUIHelper.strings;
|
||||
let checkState = { value: false };
|
||||
let shouldLaunch = this._prompter.confirmCheck(
|
||||
s.fileExecutableSecurityWarningTitle,
|
||||
s.fileExecutableSecurityWarning(leafName, leafName),
|
||||
s.fileExecutableSecurityWarningDontAsk,
|
||||
checkState);
|
||||
|
||||
if (shouldLaunch) {
|
||||
Services.prefs.setBoolPref(kPrefAlertOnEXEOpen, !checkState.value);
|
||||
}
|
||||
|
||||
return Promise.resolve(shouldLaunch);
|
||||
} catch (ex) {
|
||||
return Promise.reject(ex);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays a warning message box that informs that there are active
|
||||
* downloads, and asks whether the user wants to cancel them or not.
|
||||
*
|
||||
* @param aDownloadsCount
|
||||
* The current downloads count.
|
||||
* @param aPromptType
|
||||
* The type of prompt notification depending on the observer.
|
||||
*
|
||||
* @return False to cancel the downloads and continue, true to abort the
|
||||
* operation.
|
||||
*/
|
||||
confirmCancelDownloads: function DP_confirmCancelDownload(aDownloadsCount,
|
||||
aPromptType)
|
||||
{
|
||||
// Always continue in case we have no prompter implementation, or if there
|
||||
// are no active downloads.
|
||||
if (!this._prompter || aDownloadsCount <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let s = DownloadUIHelper.strings;
|
||||
let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
|
||||
(Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1);
|
||||
let okButton = aDownloadsCount > 1 ? s.cancelDownloadsOKTextMultiple(aDownloadsCount)
|
||||
: s.cancelDownloadsOKText;
|
||||
let title, message, cancelButton;
|
||||
|
||||
switch (aPromptType) {
|
||||
case this.ON_QUIT:
|
||||
title = s.quitCancelDownloadsAlertTitle;
|
||||
if (AppConstants.platform != "macosx") {
|
||||
message = aDownloadsCount > 1
|
||||
? s.quitCancelDownloadsAlertMsgMultiple(aDownloadsCount)
|
||||
: s.quitCancelDownloadsAlertMsg;
|
||||
cancelButton = s.dontQuitButtonWin;
|
||||
} else {
|
||||
message = aDownloadsCount > 1
|
||||
? s.quitCancelDownloadsAlertMsgMacMultiple(aDownloadsCount)
|
||||
: s.quitCancelDownloadsAlertMsgMac;
|
||||
cancelButton = s.dontQuitButtonMac;
|
||||
}
|
||||
break;
|
||||
case this.ON_OFFLINE:
|
||||
title = s.offlineCancelDownloadsAlertTitle;
|
||||
message = aDownloadsCount > 1
|
||||
? s.offlineCancelDownloadsAlertMsgMultiple(aDownloadsCount)
|
||||
: s.offlineCancelDownloadsAlertMsg;
|
||||
cancelButton = s.dontGoOfflineButton;
|
||||
break;
|
||||
case this.ON_LEAVE_PRIVATE_BROWSING:
|
||||
title = s.leavePrivateBrowsingCancelDownloadsAlertTitle;
|
||||
message = aDownloadsCount > 1
|
||||
? s.leavePrivateBrowsingWindowsCancelDownloadsAlertMsgMultiple2(aDownloadsCount)
|
||||
: s.leavePrivateBrowsingWindowsCancelDownloadsAlertMsg2;
|
||||
cancelButton = s.dontLeavePrivateBrowsingButton2;
|
||||
break;
|
||||
}
|
||||
|
||||
let rv = this._prompter.confirmEx(title, message, buttonFlags, okButton,
|
||||
cancelButton, null, null, {});
|
||||
return (rv == 1);
|
||||
}
|
||||
};
|
||||
305
toolkit/components/jsdownloads/src/Downloads.jsm
Normal file
305
toolkit/components/jsdownloads/src/Downloads.jsm
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Main entry point to get references to all the back-end objects.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"Downloads",
|
||||
];
|
||||
|
||||
// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/Integration.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/DownloadCore.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadCombinedList",
|
||||
"resource://gre/modules/DownloadList.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadList",
|
||||
"resource://gre/modules/DownloadList.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadSummary",
|
||||
"resource://gre/modules/DownloadList.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
|
||||
"resource://gre/modules/DownloadUIHelper.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
Integration.downloads.defineModuleGetter(this, "DownloadIntegration",
|
||||
"resource://gre/modules/DownloadIntegration.jsm");
|
||||
|
||||
// Downloads
|
||||
|
||||
/**
|
||||
* This object is exposed directly to the consumers of this JavaScript module,
|
||||
* and provides the only entry point to get references to back-end objects.
|
||||
*/
|
||||
this.Downloads = {
|
||||
/**
|
||||
* Work on downloads that were not started from a private browsing window.
|
||||
*/
|
||||
get PUBLIC() {
|
||||
return "{Downloads.PUBLIC}";
|
||||
},
|
||||
/**
|
||||
* Work on downloads that were started from a private browsing window.
|
||||
*/
|
||||
get PRIVATE() {
|
||||
return "{Downloads.PRIVATE}";
|
||||
},
|
||||
/**
|
||||
* Work on both Downloads.PRIVATE and Downloads.PUBLIC downloads.
|
||||
*/
|
||||
get ALL() {
|
||||
return "{Downloads.ALL}";
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new Download object.
|
||||
*
|
||||
* @param aProperties
|
||||
* Provides the initial properties for the newly created download.
|
||||
* This matches the serializable representation of a Download object.
|
||||
* Some of the most common properties in this object include:
|
||||
* {
|
||||
* source: String containing the URI for the download source.
|
||||
* Alternatively, may be an nsIURI, a DownloadSource object,
|
||||
* or an object with the following properties:
|
||||
* {
|
||||
* url: String containing the URI for the download source.
|
||||
* isPrivate: Indicates whether the download originated from a
|
||||
* private window. If omitted, the download is public.
|
||||
* referrer: String containing the referrer URI of the download
|
||||
* source. Can be omitted or null if no referrer should
|
||||
* be sent or the download source is not HTTP.
|
||||
* },
|
||||
* target: String containing the path of the target file.
|
||||
* Alternatively, may be an nsIFile, a DownloadTarget object,
|
||||
* or an object with the following properties:
|
||||
* {
|
||||
* path: String containing the path of the target file.
|
||||
* },
|
||||
* saver: String representing the class of the download operation.
|
||||
* If omitted, defaults to "copy". Alternatively, may be the
|
||||
* serializable representation of a DownloadSaver object.
|
||||
* }
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The newly created Download object.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
createDownload: function D_createDownload(aProperties)
|
||||
{
|
||||
try {
|
||||
return Promise.resolve(Download.fromSerializable(aProperties));
|
||||
} catch (ex) {
|
||||
return Promise.reject(ex);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Downloads data from a remote network location to a local file.
|
||||
*
|
||||
* This download method does not provide user interface, or the ability to
|
||||
* cancel or restart the download programmatically. For that, you should
|
||||
* obtain a reference to a Download object using the createDownload function.
|
||||
*
|
||||
* Since the download cannot be restarted, any partially downloaded data will
|
||||
* not be kept in case the download fails.
|
||||
*
|
||||
* @param aSource
|
||||
* String containing the URI for the download source. Alternatively,
|
||||
* may be an nsIURI or a DownloadSource object.
|
||||
* @param aTarget
|
||||
* String containing the path of the target file. Alternatively, may
|
||||
* be an nsIFile or a DownloadTarget object.
|
||||
* @param aOptions
|
||||
* An optional object used to control the behavior of this function.
|
||||
* You may pass an object with a subset of the following fields:
|
||||
* {
|
||||
* isPrivate: Indicates whether the download originated from a
|
||||
* private window.
|
||||
* }
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the download has finished successfully.
|
||||
* @rejects JavaScript exception if the download failed.
|
||||
*/
|
||||
fetch: function (aSource, aTarget, aOptions) {
|
||||
return this.createDownload({
|
||||
source: aSource,
|
||||
target: aTarget,
|
||||
}).then(function D_SD_onSuccess(aDownload) {
|
||||
if (aOptions && ("isPrivate" in aOptions)) {
|
||||
aDownload.source.isPrivate = aOptions.isPrivate;
|
||||
}
|
||||
return aDownload.start();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the specified type of DownloadList object. There is one download
|
||||
* list for each type, and this method always retrieves a reference to the
|
||||
* same download list when called with the same argument.
|
||||
*
|
||||
* Calling this function may cause the list of public downloads to be reloaded
|
||||
* from the previous session, if it wasn't loaded already.
|
||||
*
|
||||
* @param aType
|
||||
* This can be Downloads.PUBLIC, Downloads.PRIVATE, or Downloads.ALL.
|
||||
* Downloads added to the Downloads.PUBLIC and Downloads.PRIVATE lists
|
||||
* are reflected in the Downloads.ALL list, and downloads added to the
|
||||
* Downloads.ALL list are also added to either the Downloads.PUBLIC or
|
||||
* the Downloads.PRIVATE list based on their properties.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The requested DownloadList or DownloadCombinedList object.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
getList: function (aType)
|
||||
{
|
||||
if (!this._promiseListsInitialized) {
|
||||
this._promiseListsInitialized = Task.spawn(function* () {
|
||||
let publicList = new DownloadList();
|
||||
let privateList = new DownloadList();
|
||||
let combinedList = new DownloadCombinedList(publicList, privateList);
|
||||
|
||||
try {
|
||||
yield DownloadIntegration.addListObservers(publicList, false);
|
||||
yield DownloadIntegration.addListObservers(privateList, true);
|
||||
yield DownloadIntegration.initializePublicDownloadList(publicList);
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
|
||||
let publicSummary = yield this.getSummary(Downloads.PUBLIC);
|
||||
let privateSummary = yield this.getSummary(Downloads.PRIVATE);
|
||||
let combinedSummary = yield this.getSummary(Downloads.ALL);
|
||||
|
||||
yield publicSummary.bindToList(publicList);
|
||||
yield privateSummary.bindToList(privateList);
|
||||
yield combinedSummary.bindToList(combinedList);
|
||||
|
||||
this._lists[Downloads.PUBLIC] = publicList;
|
||||
this._lists[Downloads.PRIVATE] = privateList;
|
||||
this._lists[Downloads.ALL] = combinedList;
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
return this._promiseListsInitialized.then(() => this._lists[aType]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Promise resolved when the initialization of the download lists has
|
||||
* completed, or null if initialization has never been requested.
|
||||
*/
|
||||
_promiseListsInitialized: null,
|
||||
|
||||
/**
|
||||
* After initialization, this object is populated with one key for each type
|
||||
* of download list that can be returned (Downloads.PUBLIC, Downloads.PRIVATE,
|
||||
* or Downloads.ALL). The values are the DownloadList objects.
|
||||
*/
|
||||
_lists: {},
|
||||
|
||||
/**
|
||||
* Retrieves the specified type of DownloadSummary object. There is one
|
||||
* download summary for each type, and this method always retrieves a
|
||||
* reference to the same download summary when called with the same argument.
|
||||
*
|
||||
* Calling this function does not cause the list of public downloads to be
|
||||
* reloaded from the previous session. The summary will behave as if no
|
||||
* downloads are present until the getList method is called.
|
||||
*
|
||||
* @param aType
|
||||
* This can be Downloads.PUBLIC, Downloads.PRIVATE, or Downloads.ALL.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The requested DownloadList or DownloadCombinedList object.
|
||||
* @rejects JavaScript exception.
|
||||
*/
|
||||
getSummary: function (aType)
|
||||
{
|
||||
if (aType != Downloads.PUBLIC && aType != Downloads.PRIVATE &&
|
||||
aType != Downloads.ALL) {
|
||||
throw new Error("Invalid aType argument.");
|
||||
}
|
||||
|
||||
if (!(aType in this._summaries)) {
|
||||
this._summaries[aType] = new DownloadSummary();
|
||||
}
|
||||
|
||||
return Promise.resolve(this._summaries[aType]);
|
||||
},
|
||||
|
||||
/**
|
||||
* This object is populated by the getSummary method with one key for each
|
||||
* type of object that can be returned (Downloads.PUBLIC, Downloads.PRIVATE,
|
||||
* or Downloads.ALL). The values are the DownloadSummary objects.
|
||||
*/
|
||||
_summaries: {},
|
||||
|
||||
/**
|
||||
* Returns the system downloads directory asynchronously.
|
||||
* Mac OSX:
|
||||
* User downloads directory
|
||||
* XP/2K:
|
||||
* My Documents/Downloads
|
||||
* Vista and others:
|
||||
* User downloads directory
|
||||
* Linux:
|
||||
* XDG user dir spec, with a fallback to Home/Downloads
|
||||
* Android:
|
||||
* standard downloads directory i.e. /sdcard
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The downloads directory string path.
|
||||
*/
|
||||
getSystemDownloadsDirectory: function D_getSystemDownloadsDirectory() {
|
||||
return DownloadIntegration.getSystemDownloadsDirectory();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the preferred downloads directory based on the user preferences
|
||||
* in the current profile asynchronously.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The downloads directory string path.
|
||||
*/
|
||||
getPreferredDownloadsDirectory: function D_getPreferredDownloadsDirectory() {
|
||||
return DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the temporary directory where downloads are placed before the
|
||||
* final location is chosen, or while the document is opened temporarily
|
||||
* with an external application. This may or may not be the system temporary
|
||||
* directory, based on the platform asynchronously.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves The downloads directory string path.
|
||||
*/
|
||||
getTemporaryDownloadsDirectory: function D_getTemporaryDownloadsDirectory() {
|
||||
return DownloadIntegration.getTemporaryDownloadsDirectory();
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructor for a DownloadError object. When you catch an exception during
|
||||
* a download, you can use this to verify if "ex instanceof Downloads.Error",
|
||||
* before reading the exception properties with the error details.
|
||||
*/
|
||||
Error: DownloadError,
|
||||
};
|
||||
2
toolkit/components/jsdownloads/src/Downloads.manifest
Normal file
2
toolkit/components/jsdownloads/src/Downloads.manifest
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
component {1b4c85df-cbdd-4bb6-b04e-613caece083c} DownloadLegacy.js
|
||||
contract @mozilla.org/transfer;1 {1b4c85df-cbdd-4bb6-b04e-613caece083c}
|
||||
31
toolkit/components/jsdownloads/src/moz.build
Normal file
31
toolkit/components/jsdownloads/src/moz.build
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
SOURCES += [
|
||||
'DownloadPlatform.cpp',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'DownloadLegacy.js',
|
||||
'Downloads.manifest',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'DownloadCore.jsm',
|
||||
'DownloadImport.jsm',
|
||||
'DownloadList.jsm',
|
||||
'Downloads.jsm',
|
||||
'DownloadStore.jsm',
|
||||
'DownloadUIHelper.jsm',
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'DownloadIntegration.jsm',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
CXXFLAGS += CONFIG['TK_CFLAGS']
|
||||
Loading…
Add table
Add a link
Reference in a new issue