mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-09 04:39:03 +02:00
This patch was generated with a script. It doesn't include all files:
- Files that use the preprocessor or fail to parse are skipped
- Files that are loaded as JSMs but don't use the .jsm extension are skipped (those will be renamed in Bug 1609269)
It was generated with the following command using d855222aa2/no-this-property-read.js:
```
hg revert --all &&
cp .gitignore .rgignore &&
rg --files-without-match -g '*.jsm' '^#endif|^#include|^#filter' | jscodeshift --stdin --transform ~/Code/jsm-rewrites/no-this-property-read.js --ignore-pattern ./mobile/android/modules/Sanitizer.jsm --ignore-pattern ./js/xpconnect/tests/unit/syntax_error.jsm &&
./mach eslint `hg st | rg '^M ' | sed 's/^M //'`
```
Differential Revision: https://phabricator.services.mozilla.com/D60187
--HG--
extra : moz-landing-system : lando
192 lines
5.7 KiB
JavaScript
192 lines
5.7 KiB
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",
|
|
* "referrerInfo": serialized string represents referrerInfo object
|
|
* },
|
|
* "target": "/home/user/Downloads/download-2.txt"
|
|
* }
|
|
* ]
|
|
* }
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var EXPORTED_SYMBOLS = ["DownloadStore"];
|
|
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
|
);
|
|
|
|
ChromeUtils.defineModuleGetter(
|
|
this,
|
|
"Downloads",
|
|
"resource://gre/modules/Downloads.jsm"
|
|
);
|
|
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
|
|
|
XPCOMUtils.defineLazyGetter(this, "gTextDecoder", function() {
|
|
return new TextDecoder();
|
|
});
|
|
|
|
XPCOMUtils.defineLazyGetter(this, "gTextEncoder", function() {
|
|
return new TextEncoder();
|
|
});
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
var DownloadStore = function(aList, aPath) {
|
|
this.list = aList;
|
|
this.path = aPath;
|
|
};
|
|
|
|
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 (async () => {
|
|
let bytes;
|
|
try {
|
|
bytes = await 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 = await 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.
|
|
await download.refresh();
|
|
}
|
|
} finally {
|
|
// Add the download to the list if we succeeded in creating it,
|
|
// after we have updated its initial state.
|
|
await this.list.add(download);
|
|
}
|
|
} catch (ex) {
|
|
// If an item is unrecognized, don't prevent others from being loaded.
|
|
Cu.reportError(ex);
|
|
}
|
|
}
|
|
})();
|
|
},
|
|
|
|
/**
|
|
* 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 (async () => {
|
|
let downloads = await 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));
|
|
await OS.File.writeAtomic(this.path, bytes, {
|
|
tmpPath: this.path + ".tmp",
|
|
});
|
|
} else {
|
|
// Remove the file if there are no downloads to save at all.
|
|
try {
|
|
await 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.
|
|
}
|
|
}
|
|
})();
|
|
},
|
|
};
|