From 38e7ec52c3083ae33045cd8f796384a671fa5127 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Fri, 26 Jul 2013 15:30:35 +0200 Subject: [PATCH] Bug 851454 - Define the format of "downloads.json" and of the parameters of createDownload. r=enn --- .../jsdownloads/src/DownloadCore.jsm | 244 ++++++++++++++++-- .../jsdownloads/src/DownloadIntegration.jsm | 5 +- .../jsdownloads/src/DownloadLegacy.js | 6 +- .../jsdownloads/src/DownloadList.jsm | 5 +- .../jsdownloads/src/DownloadStore.jsm | 50 ++-- .../components/jsdownloads/src/Downloads.jsm | 104 +++----- .../components/jsdownloads/test/unit/head.js | 107 +++----- .../test/unit/test_DownloadCore.js | 157 ++++++----- .../test/unit/test_DownloadLegacy.js | 55 ++-- .../test/unit/test_DownloadList.js | 4 +- .../test/unit/test_DownloadStore.js | 77 +++--- .../jsdownloads/test/unit/test_Downloads.js | 53 ++-- 12 files changed, 504 insertions(+), 363 deletions(-) diff --git a/toolkit/components/jsdownloads/src/DownloadCore.jsm b/toolkit/components/jsdownloads/src/DownloadCore.jsm index 0d6e7d4763db..d9017aca644a 100644 --- a/toolkit/components/jsdownloads/src/DownloadCore.jsm +++ b/toolkit/components/jsdownloads/src/DownloadCore.jsm @@ -56,6 +56,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration", "resource://gre/modules/DownloadIntegration.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", @@ -69,6 +71,15 @@ const BackgroundFileSaverStreamListener = Components.Constructor( "@mozilla.org/network/background-file-saver;1?mode=streamlistener", "nsIBackgroundFileSaver"); +/** + * Returns true if the given value is a primitive string or a String object. + */ +function isString(aValue) { + // We cannot use the "instanceof" operator reliably across module boundaries. + return (typeof aValue == "string") || + (typeof aValue == "object" && "charAt" in aValue); +} + //////////////////////////////////////////////////////////////////////////////// //// Download @@ -422,6 +433,71 @@ Download.prototype = { } this._notifyChange(); }, + + /** + * Returns a static representation of the current object state. + * + * @return A JavaScript object that can be serialized to JSON. + */ + toSerializable: function () + { + let serializable = { + source: this.source.toSerializable(), + target: this.target.toSerializable(), + }; + + // Simplify the representation for the most common saver type. If the saver + // is an object instead of a simple string, we can't simplify it because we + // need to persist all its properties, not only "type". This may happen for + // savers of type "copy" as well as other types. + let saver = this.saver.toSerializable(); + if (saver !== "copy") { + serializable.saver = saver; + } + + return serializable; + }, +}; + +/** + * Creates a new Download object from a serializable representation. This + * function is used by the createDownload method of Downloads.jsm when a new + * Download object is requested, thus some properties may refer to live objects + * in place of their serializable representations. + * + * @param aSerializable + * An object with the following fields: + * { + * source: DownloadSource object, or its serializable representation. + * See DownloadSource.fromSerializable for details. + * target: DownloadTarget object, or its serializable representation. + * See DownloadTarget.fromSerializable for details. + * saver: Serializable representation of a DownloadSaver object. See + * DownloadSaver.fromSerializable for details. If omitted, + * defaults to "copy". + * } + * + * @return The newly created Download object. + */ +Download.fromSerializable = function (aSerializable) { + let download = new Download(); + if (aSerializable.source instanceof DownloadSource) { + download.source = aSerializable.source; + } else { + download.source = DownloadSource.fromSerializable(aSerializable.source); + } + if (aSerializable.target instanceof DownloadTarget) { + download.target = aSerializable.target; + } else { + download.target = DownloadTarget.fromSerializable(aSerializable.target); + } + if ("saver" in aSerializable) { + download.saver = DownloadSaver.fromSerializable(aSerializable.saver); + } else { + download.saver = DownloadSaver.fromSerializable("copy"); + } + download.saver.download = download; + return download; }; //////////////////////////////////////////////////////////////////////////////// @@ -434,20 +510,20 @@ function DownloadSource() { } DownloadSource.prototype = { /** - * The nsIURI for the download source. + * String containing the URI for the download source. */ - uri: null, + url: null, /** * Indicates whether the download originated from a private window. This - * determines the context of the network request that is made to retrieve the + * determines the context of the network request that is made to retrieve the * resource. */ isPrivate: false, /** - * The nsIURI for the referrer of the download source, or null if no referrer - * should be sent or the download source is not HTTP. + * String containing the referrer URI of the download source, or null if no + * referrer should be sent or the download source is not HTTP. */ referrer: null, @@ -456,19 +532,60 @@ DownloadSource.prototype = { * * @return A JavaScript object that can be serialized to JSON. */ - serialize: function DS_serialize() + toSerializable: function () { - let serialized = { uri: this.uri.spec }; + // Simplify the representation if we don't have other details. + if (!this.isPrivate && !this.referrer) { + return this.url; + } + + let serializable = { url: this.url }; if (this.isPrivate) { - serialized.isPrivate = true; + serializable.isPrivate = true; } if (this.referrer) { - serialized.referrer = this.referrer.spec; + serializable.referrer = this.referrer; } - return serialized; + return serializable; }, }; +/** + * Creates a new DownloadSource object from its serializable representation. + * + * @param aSerializable + * Serializable representation of a DownloadSource object. This may be a + * string containing the URI for the download source, an nsIURI, 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. + * } + * + * @return The newly created DownloadSource object. + */ +DownloadSource.fromSerializable = function (aSerializable) { + let source = new DownloadSource(); + if (isString(aSerializable)) { + source.url = aSerializable; + } else if (aSerializable instanceof Ci.nsIURI) { + source.url = aSerializable.spec; + } else { + source.url = aSerializable.url; + if ("isPrivate" in aSerializable) { + source.isPrivate = aSerializable.isPrivate; + } + if ("referrer" in aSerializable) { + source.referrer = aSerializable.referrer; + } + } + return source; +}; + //////////////////////////////////////////////////////////////////////////////// //// DownloadTarget @@ -480,21 +597,50 @@ function DownloadTarget() { } DownloadTarget.prototype = { /** - * The nsIFile for the download target. + * String containing the path of the target file. */ - file: null, + path: null, /** * Returns a static representation of the current object state. * * @return A JavaScript object that can be serialized to JSON. */ - serialize: function DT_serialize() + toSerializable: function () { - return { file: this.file.path }; + // Simplify the representation since we don't have other details for now. + return this.path; }, }; +/** + * Creates a new DownloadTarget object from its serializable representation. + * + * @param aSerializable + * Serializable representation of a DownloadTarget object. This may be a + * string containing the path of the target file, an nsIFile, or an + * object with the following properties: + * { + * path: String containing the path of the target file. + * } + * + * @return The newly created DownloadTarget object. + */ +DownloadTarget.fromSerializable = function (aSerializable) { + let target = new DownloadTarget(); + if (isString(aSerializable)) { + target.path = aSerializable; + } else if (aSerializable instanceof Ci.nsIFile) { + // Read the "path" property of nsIFile after checking the object type. + target.path = aSerializable.path; + } else { + // Read the "path" property of the serializable DownloadTarget + // representation. + target.path = aSerializable.path; + } + return target; +}; + //////////////////////////////////////////////////////////////////////////////// //// DownloadError @@ -610,12 +756,39 @@ DownloadSaver.prototype = { * * @return A JavaScript object that can be serialized to JSON. */ - serialize: function DS_serialize() + toSerializable: function () { throw new Error("Not implemented."); }, }; +/** + * Creates a new DownloadSaver object from its serializable representation. + * + * @param aSerializable + * Serializable representation of a DownloadSaver object. If no initial + * state information for the saver object is needed, can be a string + * representing the class of the download operation, for example "copy". + * + * @return The newly created DownloadSaver object. + */ +DownloadSaver.fromSerializable = function (aSerializable) { + let serializable = isString(aSerializable) ? { type: aSerializable } + : aSerializable; + let saver; + switch (serializable.type) { + case "copy": + saver = DownloadCopySaver.fromSerializable(serializable); + break; + case "legacy": + saver = DownloadLegacySaver.fromSerializable(serializable); + break; + default: + throw new Error("Unrecoginzed download saver type."); + } + return saver; +}; + //////////////////////////////////////////////////////////////////////////////// //// DownloadCopySaver @@ -665,15 +838,16 @@ DownloadCopySaver.prototype = { }; // Set the target file, that will be deleted if the download fails. - backgroundFileSaver.setTarget(download.target.file, false); + backgroundFileSaver.setTarget(new FileUtils.File(download.target.path), + false); // Create a channel from the source, and listen to progress notifications. - let channel = NetUtil.newChannel(download.source.uri); + let channel = NetUtil.newChannel(NetUtil.newURI(download.source.url)); if (channel instanceof Ci.nsIPrivateBrowsingChannel) { channel.setPrivate(download.source.isPrivate); } - if (channel instanceof Ci.nsIHttpChannel) { - channel.referrer = download.source.referrer; + if (channel instanceof Ci.nsIHttpChannel && download.source.referrer) { + channel.referrer = NetUtil.newURI(download.source.referrer); } channel.notificationCallbacks = { @@ -747,14 +921,29 @@ DownloadCopySaver.prototype = { }, /** - * Implements "DownloadSaver.serialize". + * Implements "DownloadSaver.toSerializable". */ - serialize: function DCS_serialize() + toSerializable: function () { - return { type: "copy" }; + // Simplify the representation since we don't have other details for now. + return "copy"; }, }; +/** + * Creates a new DownloadCopySaver object, with its initial state derived from + * its serializable representation. + * + * @param aSerializable + * Serializable representation of a DownloadCopySaver object. + * + * @return The newly created DownloadCopySaver object. + */ +DownloadCopySaver.fromSerializable = function (aSerializable) { + // We don't have other state details for now. + return new DownloadCopySaver(); +}; + //////////////////////////////////////////////////////////////////////////////// //// DownloadLegacySaver @@ -872,7 +1061,7 @@ DownloadLegacySaver.prototype = { // empty file is created as expected. try { // This atomic operation is more efficient than an existence check. - let file = yield OS.File.open(this.download.target.file.path, + let file = yield OS.File.open(this.download.target.path, { create: true }); yield file.close(); } catch (ex if ex instanceof OS.File.Error && ex.becauseExists) { } @@ -898,3 +1087,12 @@ DownloadLegacySaver.prototype = { "Download canceled.")); }, }; + +/** + * Returns a new DownloadLegacySaver object. This saver type has a + * deserializable form only when creating a new object in memory, because it + * cannot be serialized to disk. + */ +DownloadLegacySaver.fromSerializable = function () { + return new DownloadLegacySaver(); +}; diff --git a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm index b6f57eaf0a07..358ec5490462 100644 --- a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm +++ b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm @@ -29,6 +29,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadStore", "resource://gre/modules/DownloadStore.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", + "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Promise", @@ -254,7 +256,8 @@ this.DownloadIntegration = { // Log the event if required by parental controls settings. if (isEnabled && gParentalControlsService.loggingEnabled) { gParentalControlsService.log(gParentalControlsService.ePCLog_FileDownload, - shouldBlock, aDownload.source.uri, null); + shouldBlock, + NetUtil.newURI(aDownload.source.url), null); } return Promise.resolve(shouldBlock); diff --git a/toolkit/components/jsdownloads/src/DownloadLegacy.js b/toolkit/components/jsdownloads/src/DownloadLegacy.js index 91665855ee46..fad46a5066a7 100644 --- a/toolkit/components/jsdownloads/src/DownloadLegacy.js +++ b/toolkit/components/jsdownloads/src/DownloadLegacy.js @@ -161,9 +161,9 @@ DownloadLegacyTransfer.prototype = { // wait for it to be available. This operation may cause the entire // download system to initialize before the object is created. Downloads.createDownload({ - source: { uri: aSource, isPrivate: aIsPrivate }, - target: { file: aTarget.QueryInterface(Ci.nsIFileURL).file }, - saver: { type: "legacy" }, + source: { url: aSource.spec, isPrivate: aIsPrivate }, + target: aTarget.QueryInterface(Ci.nsIFileURL).file, + saver: "legacy", }).then(function DLT_I_onDownload(aDownload) { // Now that the saver is available, hook up the cancellation handler. aDownload.saver.deferCanceled.promise.then(() => { diff --git a/toolkit/components/jsdownloads/src/DownloadList.jsm b/toolkit/components/jsdownloads/src/DownloadList.jsm index 2f011a4664ab..b9fd386ff743 100644 --- a/toolkit/components/jsdownloads/src/DownloadList.jsm +++ b/toolkit/components/jsdownloads/src/DownloadList.jsm @@ -25,6 +25,8 @@ const Cr = Components.results; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", + "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Promise", @@ -234,7 +236,8 @@ DownloadList.prototype = { //// nsINavHistoryObserver onDeleteURI: function DL_onDeleteURI(aURI, aGUID) { - this._removeWhere(download => aURI.equals(download.source.uri)); + this._removeWhere(download => aURI.equals(NetUtil.newURI( + download.source.url))); }, onClearHistory: function DL_onClearHistory() { diff --git a/toolkit/components/jsdownloads/src/DownloadStore.jsm b/toolkit/components/jsdownloads/src/DownloadStore.jsm index cd956c4a0a11..6b3c9bab487e 100644 --- a/toolkit/components/jsdownloads/src/DownloadStore.jsm +++ b/toolkit/components/jsdownloads/src/DownloadStore.jsm @@ -7,6 +7,25 @@ /** * 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"; @@ -27,16 +46,11 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm") XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); -const LocalFile = Components.Constructor("@mozilla.org/file/local;1", - "nsIFile", "initWithPath"); - XPCOMUtils.defineLazyGetter(this, "gTextDecoder", function () { return new TextDecoder(); }); @@ -95,19 +109,9 @@ DownloadStore.prototype = { let storeData = JSON.parse(gTextDecoder.decode(bytes)); // Create live downloads based on the static snapshot. - for (let downloadData of storeData) { + for (let downloadData of storeData.list) { try { - let source = { uri: NetUtil.newURI(downloadData.source.uri) }; - if ("referrer" in downloadData.source) { - source.referrer = NetUtil.newURI(downloadData.source.referrer); - } - let download = yield Downloads.createDownload({ - source: source, - target: { file: new LocalFile(downloadData.target.file) }, - saver: downloadData.saver, - }); - - this.list.add(download); + this.list.add(yield Downloads.createDownload(downloadData)); } catch (ex) { // If an item is unrecognized, don't prevent others from being loaded. Cu.reportError(ex); @@ -131,19 +135,15 @@ DownloadStore.prototype = { let downloads = yield this.list.getAll(); // Take a static snapshot of the current state of all the downloads. - let storeData = []; + let storeData = { list: [] }; let atLeastOneDownload = false; for (let download of downloads) { try { - storeData.push({ - source: download.source.serialize(), - target: download.target.serialize(), - saver: download.saver.serialize(), - }); + storeData.list.push(download.toSerializable()); atLeastOneDownload = true; } catch (ex) { - // If an item cannot be serialized, don't prevent others from being - // saved. + // If an item cannot be converted to a serializable form, don't + // prevent others from being saved. Cu.reportError(ex); } } diff --git a/toolkit/components/jsdownloads/src/Downloads.jsm b/toolkit/components/jsdownloads/src/Downloads.jsm index 9a7e4be8efe1..6edaa2efe4bf 100644 --- a/toolkit/components/jsdownloads/src/Downloads.jsm +++ b/toolkit/components/jsdownloads/src/Downloads.jsm @@ -31,14 +31,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadList", "resource://gre/modules/DownloadList.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper", "resource://gre/modules/DownloadUIHelper.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/commonjs/sdk/core/promise.js"); -XPCOMUtils.defineLazyModuleGetter(this, "Services", - "resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); @@ -55,19 +49,29 @@ this.Downloads = { * * @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: { - * uri: The nsIURI for the download source. + * 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. + * 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: { - * file: The nsIFile for the download target. - * }, - * saver: { - * type: String representing the class of download operation - * handled by this saver object, for example "copy". + * 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} @@ -76,31 +80,11 @@ this.Downloads = { */ createDownload: function D_createDownload(aProperties) { - return Task.spawn(function task_D_createDownload() { - let download = new Download(); - - download.source = new DownloadSource(); - download.source.uri = aProperties.source.uri; - if ("isPrivate" in aProperties.source) { - download.source.isPrivate = aProperties.source.isPrivate; - } - if ("referrer" in aProperties.source) { - download.source.referrer = aProperties.source.referrer; - } - download.target = new DownloadTarget(); - download.target.file = aProperties.target.file; - - // Support for different aProperties.saver values isn't implemented yet. - download.saver = aProperties.saver.type == "legacy" - ? new DownloadLegacySaver() - : new DownloadCopySaver(); - download.saver.download = download; - - // This explicitly makes this function a generator for Task.jsm, so that - // exceptions in the above calls can be reported asynchronously. - yield; - throw new Task.Result(download); - }); + try { + return Promise.resolve(Download.fromSerializable(aProperties)); + } catch (ex) { + return Promise.reject(ex); + } }, /** @@ -111,15 +95,17 @@ this.Downloads = { * reference to a Download object using the createDownload function. * * @param aSource - * The nsIURI or string containing the URI spec for the download - * source, or alternative DownloadSource. + * String containing the URI for the download source. Alternatively, + * may be an nsIURI or a DownloadSource object. * @param aTarget - * The nsIFile or string containing the file path, or alternative - * DownloadTarget. + * String containing the path of the target file. Alternatively, may + * be an nsIFile or a DownloadTarget object. * @param aOptions - * The object contains different additional options or null. - * { isPrivate: Indicates whether the download originated from a - * private window. + * 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} @@ -127,31 +113,13 @@ this.Downloads = { * @rejects JavaScript exception if the download failed. */ simpleDownload: function D_simpleDownload(aSource, aTarget, aOptions) { - // Wrap the arguments into simple objects resembling DownloadSource and - // DownloadTarget, if they are not objects of that type already. - if (aSource instanceof Ci.nsIURI) { - aSource = { uri: aSource }; - } else if (typeof aSource == "string" || - (typeof aSource == "object" && "charAt" in aSource)) { - aSource = { uri: NetUtil.newURI(aSource) }; - } - - if (aSource && aOptions && ("isPrivate" in aOptions)) { - aSource.isPrivate = aOptions.isPrivate; - } - if (aTarget instanceof Ci.nsIFile) { - aTarget = { file: aTarget }; - } else if (typeof aTarget == "string" || - (typeof aTarget == "object" && "charAt" in aTarget)) { - aTarget = { file: new FileUtils.File(aTarget) }; - } - - // Create and start the actual download. return this.createDownload({ source: aSource, target: aTarget, - saver: { type: "copy" }, }).then(function D_SD_onSuccess(aDownload) { + if (aOptions && ("isPrivate" in aOptions)) { + aDownload.source.isPrivate = aOptions.isPrivate; + } return aDownload.start(); }); }, diff --git a/toolkit/components/jsdownloads/test/unit/head.js b/toolkit/components/jsdownloads/test/unit/head.js index 295113f5d262..7902c22f06e8 100644 --- a/toolkit/components/jsdownloads/test/unit/head.js +++ b/toolkit/components/jsdownloads/test/unit/head.js @@ -51,51 +51,11 @@ const BinaryOutputStream = Components.Constructor( "nsIBinaryOutputStream", "setOutputStream") -Object.defineProperty(this, "HTTP_BASE", {get: function() { - return "http://localhost:" + gHttpServer.identity.primaryPort; -}}); - -Object.defineProperty(this, "FAKE_BASE", {get: function() { - return "http://localhost:" + gFakeServerPort; -}}); - -Object.defineProperty(this, "TEST_REFERRER_URI", {get: function() { - return NetUtil.newURI(HTTP_BASE + "/referrer.html"); -}}); - -Object.defineProperty(this, "TEST_SOURCE_URI", {get: function() { - return NetUtil.newURI(HTTP_BASE + "/source.txt"); -}}); - -Object.defineProperty(this, "TEST_EMPTY_URI", {get: function() { - return NetUtil.newURI(HTTP_BASE + "/empty.txt"); -}}); - -Object.defineProperty(this, "TEST_FAKE_SOURCE_URI", {get: function() { - return NetUtil.newURI(FAKE_BASE + "/source.txt"); -}}); - -const TEST_EMPTY_NOPROGRESS_PATH = "/empty-noprogress.txt"; - -Object.defineProperty(this, "TEST_EMPTY_NOPROGRESS_URI", {get: function() { - return NetUtil.newURI(HTTP_BASE + TEST_EMPTY_NOPROGRESS_PATH); -}}); - -const TEST_INTERRUPTIBLE_PATH = "/interruptible.txt"; - -Object.defineProperty(this, "TEST_INTERRUPTIBLE_URI", {get: function() { - return NetUtil.newURI(HTTP_BASE + TEST_INTERRUPTIBLE_PATH); -}}); - -const TEST_INTERRUPTIBLE_GZIP_PATH = "/interruptible_gzip.txt"; - -Object.defineProperty(this, "TEST_INTERRUPTIBLE_GZIP_URI", {get: function() { - return NetUtil.newURI(HTTP_BASE + TEST_INTERRUPTIBLE_GZIP_PATH); -}}); - const TEST_TARGET_FILE_NAME = "test-download.txt"; const TEST_STORE_FILE_NAME = "test-downloads.json"; +const TEST_REFERRER_URL = "http://www.example.com/referrer.html"; + const TEST_DATA_SHORT = "This test string is downloaded."; // Generate using gzipCompressString in TelemetryPing.js. const TEST_DATA_SHORT_GZIP_ENCODED_FIRST = [ @@ -119,6 +79,20 @@ function run_test() //////////////////////////////////////////////////////////////////////////////// //// Support functions +/** + * HttpServer object initialized before tests start. + */ +let gHttpServer; + +/** + * Given a file name, returns a string containing an URI that points to the file + * on the currently running instance of the test HTTP server. + */ +function httpUrl(aFileName) { + return "http://localhost:" + gHttpServer.identity.primaryPort + "/" + + aFileName; +} + // While the previous test file should have deleted all the temporary files it // used, on Windows these might still be pending deletion on the physical file // system. Thus, start from a new base number every time, to make a collision @@ -190,18 +164,18 @@ function promiseTimeout(aTime) /** * Creates a new Download object, setting a temporary file as the target. * - * @param aSourceURI - * The nsIURI for the download source, or null to use TEST_SOURCE_URI. + * @param aSourceUrl + * String containing the URI for the download source, or null to use + * httpUrl("source.txt"). * * @return {Promise} * @resolves The newly created Download object. * @rejects JavaScript exception. */ -function promiseSimpleDownload(aSourceURI) { +function promiseSimpleDownload(aSourceUrl) { return Downloads.createDownload({ - source: { uri: aSourceURI || TEST_SOURCE_URI }, - target: { file: getTempFile(TEST_TARGET_FILE_NAME) }, - saver: { type: "copy" }, + source: aSourceUrl || httpUrl("source.txt"), + target: getTempFile(TEST_TARGET_FILE_NAME), }); } @@ -234,8 +208,9 @@ function promiseNewPrivateDownloadList() { /** * Ensures that the given file contents are equal to the given string. * - * @param aFile - * nsIFile whose contents should be verified. + * @param aPath + * String containing the path of the file whose contents should be + * verified. * @param aExpectedContents * String containing the octets that are expected in the file. * @@ -243,10 +218,11 @@ function promiseNewPrivateDownloadList() { * @resolves When the operation completes. * @rejects Never. */ -function promiseVerifyContents(aFile, aExpectedContents) +function promiseVerifyContents(aPath, aExpectedContents) { let deferred = Promise.defer(); - NetUtil.asyncFetch(aFile, function(aInputStream, aStatus) { + let file = new FileUtils.File(aPath); + NetUtil.asyncFetch(file, function(aInputStream, aStatus) { do_check_true(Components.isSuccessCode(aStatus)); let contents = NetUtil.readInputStreamToString(aInputStream, aInputStream.available()); @@ -265,17 +241,18 @@ function promiseVerifyContents(aFile, aExpectedContents) /** * Adds entry for download. * - * @param aSourceURI - * The nsIURI for the download source, or null to use TEST_SOURCE_URI. + * @param aSourceUrl + * String containing the URI for the download source, or null to use + * httpUrl("source.txt"). * * @return {Promise} * @rejects JavaScript exception. */ -function promiseAddDownloadToHistory(aSourceURI) { +function promiseAddDownloadToHistory(aSourceUrl) { let deferred = Promise.defer(); PlacesUtils.asyncHistory.updatePlaces( { - uri: aSourceURI || TEST_SOURCE_URI, + uri: NetUtil.newURI(aSourceUrl || httpUrl("source.txt")), visits: [{ transitionType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD, visitDate: Date.now() @@ -304,7 +281,6 @@ function promiseAddDownloadToHistory(aSourceURI) { function startFakeServer() { let serverSocket = new ServerSocket(-1, true, -1); - gFakeServerPort = serverSocket.port; serverSocket.asyncListen({ onSocketAccepted: function (aServ, aTransport) { aTransport.close(Cr.NS_BINDING_ABORTED); @@ -326,10 +302,10 @@ function startFakeServer() * handlers, you may call "deferNextResponse" to get a reference to an object * that allows you to control the next request. * - * For example, the handler accessible at the TEST_INTERRUPTIBLE_URI address - * returns the TEST_DATA_SHORT text, then waits until the "resolve" method is - * called on the object returned by the function. At this point, the handler - * sends the TEST_DATA_SHORT text again to complete the response. + * For example, the handler accessible at the httpUri("interruptible.txt") + * address returns the TEST_DATA_SHORT text, then waits until the "resolve" + * method is called on the object returned by the function. At this point, the + * handler sends the TEST_DATA_SHORT text again to complete the response. * * You can also call the "reject" method on the returned object to interrupt the * response midway. Because of how the network layer is implemented, this does @@ -429,9 +405,6 @@ function isValidDate(aDate) { //////////////////////////////////////////////////////////////////////////////// //// Initialization functions common to all tests -let gHttpServer; -let gFakeServerPort; - add_task(function test_common_initialize() { // Start the HTTP server. @@ -439,7 +412,7 @@ add_task(function test_common_initialize() gHttpServer.registerDirectory("/", do_get_file("../data")); gHttpServer.start(-1); - registerInterruptibleHandler(TEST_INTERRUPTIBLE_PATH, + registerInterruptibleHandler("/interruptible.txt", function firstPart(aRequest, aResponse) { aResponse.setHeader("Content-Type", "text/plain", false); aResponse.setHeader("Content-Length", "" + (TEST_DATA_SHORT.length * 2), @@ -449,13 +422,13 @@ add_task(function test_common_initialize() aResponse.write(TEST_DATA_SHORT); }); - registerInterruptibleHandler(TEST_EMPTY_NOPROGRESS_PATH, + registerInterruptibleHandler("/empty-noprogress.txt", function firstPart(aRequest, aResponse) { aResponse.setHeader("Content-Type", "text/plain", false); }, function secondPart(aRequest, aResponse) { }); - registerInterruptibleHandler(TEST_INTERRUPTIBLE_GZIP_PATH, + registerInterruptibleHandler("/interruptible_gzip.txt", function firstPart(aRequest, aResponse) { aResponse.setHeader("Content-Type", "text/plain", false); aResponse.setHeader("Content-Encoding", "gzip", false); diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadCore.js b/toolkit/components/jsdownloads/test/unit/test_DownloadCore.js index aa57bbc729c7..b34e3ca9ab81 100644 --- a/toolkit/components/jsdownloads/test/unit/test_DownloadCore.js +++ b/toolkit/components/jsdownloads/test/unit/test_DownloadCore.js @@ -17,23 +17,23 @@ */ add_task(function test_download_construction() { - let targetFile = getTempFile(TEST_TARGET_FILE_NAME); + let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; let download = yield Downloads.createDownload({ - source: { uri: TEST_SOURCE_URI }, - target: { file: targetFile }, + source: { url: httpUrl("source.txt") }, + target: { path: targetPath }, saver: { type: "copy" }, }); // Checks the generated DownloadSource and DownloadTarget properties. - do_check_true(download.source.uri.equals(TEST_SOURCE_URI)); - do_check_eq(download.target.file, targetFile); + do_check_eq(download.source.url, httpUrl("source.txt")); + do_check_eq(download.target.path, targetPath); do_check_true(download.source.referrer === null); // Starts the download and waits for completion. yield download.start(); - yield promiseVerifyContents(targetFile, TEST_DATA_SHORT); + yield promiseVerifyContents(targetPath, TEST_DATA_SHORT); }); /** @@ -41,46 +41,44 @@ add_task(function test_download_construction() */ add_task(function test_download_referrer() { - let source_path = "/test_download_referrer.txt"; - let source_uri = NetUtil.newURI(HTTP_BASE + source_path); - let target_uri = getTempFile(TEST_TARGET_FILE_NAME); + let sourcePath = "/test_download_referrer.txt"; + let sourceUrl = httpUrl("test_download_referrer.txt"); + let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; function cleanup() { - gHttpServer.registerPathHandler(source_path, null); + gHttpServer.registerPathHandler(sourcePath, null); } do_register_cleanup(cleanup); - gHttpServer.registerPathHandler(source_path, function (aRequest, aResponse) { + gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { aResponse.setHeader("Content-Type", "text/plain", false); do_check_true(aRequest.hasHeader("Referer")); - do_check_eq(aRequest.getHeader("Referer"), TEST_REFERRER_URI.spec); + do_check_eq(aRequest.getHeader("Referer"), TEST_REFERRER_URL); }); let download = yield Downloads.createDownload({ - source: { uri: source_uri, referrer: TEST_REFERRER_URI }, - target: { file: target_uri }, - saver: { type: "copy" }, + source: { url: sourceUrl, referrer: TEST_REFERRER_URL }, + target: targetPath, }); - do_check_true(download.source.referrer.equals(TEST_REFERRER_URI)); + do_check_eq(download.source.referrer, TEST_REFERRER_URL); yield download.start(); download = yield Downloads.createDownload({ - source: { uri: source_uri, referrer: TEST_REFERRER_URI, isPrivate: true }, - target: { file: target_uri }, - saver: { type: "copy" }, + source: { url: sourceUrl, referrer: TEST_REFERRER_URL, + isPrivate: true }, + target: targetPath, }); - do_check_true(download.source.referrer.equals(TEST_REFERRER_URI)); + do_check_eq(download.source.referrer, TEST_REFERRER_URL); yield download.start(); // Test the download still works for non-HTTP channel with referrer. - source_uri = NetUtil.newURI("data:text/html,"); + sourceUrl = "data:text/html,"; download = yield Downloads.createDownload({ - source: { uri: source_uri, referrer: TEST_REFERRER_URI }, - target: { file: target_uri }, - saver: { type: "copy" }, + source: { url: sourceUrl, referrer: TEST_REFERRER_URL }, + target: targetPath, }); - do_check_true(download.source.referrer.equals(TEST_REFERRER_URI)); + do_check_eq(download.source.referrer, TEST_REFERRER_URL); yield download.start(); cleanup(); @@ -143,7 +141,7 @@ add_task(function test_download_intermediate_progress() { let deferResponse = deferNextResponse(); - let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI); + let download = yield promiseSimpleDownload(httpUrl("interruptible.txt")); download.onchange = function () { if (download.progress == 50) { @@ -162,7 +160,7 @@ add_task(function test_download_intermediate_progress() do_check_true(download.stopped); do_check_eq(download.progress, 100); - yield promiseVerifyContents(download.target.file, + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT + TEST_DATA_SHORT); }); @@ -171,7 +169,7 @@ add_task(function test_download_intermediate_progress() */ add_task(function test_download_empty_progress() { - let download = yield promiseSimpleDownload(TEST_EMPTY_URI); + let download = yield promiseSimpleDownload(httpUrl("empty.txt")); yield download.start(); @@ -181,7 +179,7 @@ add_task(function test_download_empty_progress() do_check_eq(download.currentBytes, 0); do_check_eq(download.totalBytes, 0); - do_check_eq(download.target.file.fileSize, 0); + do_check_eq((yield OS.File.stat(download.target.path)).size, 0); }); /** @@ -192,7 +190,7 @@ add_task(function test_download_empty_noprogress() let deferResponse = deferNextResponse(); let promiseEmptyRequestReceived = promiseNextRequestReceived(); - let download = yield promiseSimpleDownload(TEST_EMPTY_NOPROGRESS_URI); + let download = yield promiseSimpleDownload(httpUrl("empty-noprogress.txt")); download.onchange = function () { if (!download.stopped) { @@ -228,7 +226,7 @@ add_task(function test_download_empty_noprogress() do_check_eq(download.currentBytes, 0); do_check_eq(download.totalBytes, 0); - do_check_eq(download.target.file.fileSize, 0); + do_check_eq((yield OS.File.stat(download.target.path)).size, 0); }); /** @@ -236,7 +234,7 @@ add_task(function test_download_empty_noprogress() */ add_task(function test_download_start_twice() { - let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI); + let download = yield promiseSimpleDownload(httpUrl("interruptible.txt")); // Ensure that the download cannot complete before start is called twice. let deferResponse = deferNextResponse(); @@ -257,7 +255,7 @@ add_task(function test_download_start_twice() do_check_false(download.canceled); do_check_true(download.error === null); - yield promiseVerifyContents(download.target.file, + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT + TEST_DATA_SHORT); }); @@ -266,7 +264,7 @@ add_task(function test_download_start_twice() */ add_task(function test_download_cancel_midway() { - let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI); + let download = yield promiseSimpleDownload(httpUrl("interruptible.txt")); let deferResponse = deferNextResponse(); try { @@ -292,7 +290,7 @@ add_task(function test_download_cancel_midway() do_check_true(download.canceled); do_check_true(download.error === null); - do_check_false(download.target.file.exists()); + do_check_false(yield OS.File.exists(download.target.path)); // Progress properties are not reset by canceling. do_check_eq(download.progress, 50); @@ -320,7 +318,7 @@ add_task(function test_download_cancel_immediately() // Ensure that the download cannot complete before cancel is called. let deferResponse = deferNextResponse(); try { - let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI); + let download = yield promiseSimpleDownload(httpUrl("interruptible.txt")); let promiseAttempt = download.start(); do_check_false(download.stopped); @@ -343,7 +341,7 @@ add_task(function test_download_cancel_immediately() do_check_true(download.canceled); do_check_true(download.error === null); - do_check_false(download.target.file.exists()); + do_check_false(yield OS.File.exists(download.target.path)); // Check that the promise returned by the "cancel" method has been resolved. yield promiseCancel; @@ -365,7 +363,7 @@ add_task(function test_download_cancel_immediately() */ add_task(function test_download_cancel_midway_restart() { - let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI); + let download = yield promiseSimpleDownload(httpUrl("interruptible.txt")); // The first time, cancel the download midway. let deferResponse = deferNextResponse(); @@ -407,7 +405,7 @@ add_task(function test_download_cancel_midway_restart() do_check_false(download.canceled); do_check_true(download.error === null); - yield promiseVerifyContents(download.target.file, + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT + TEST_DATA_SHORT); }); @@ -416,7 +414,7 @@ add_task(function test_download_cancel_midway_restart() */ add_task(function test_download_cancel_immediately_restart_immediately() { - let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI); + let download = yield promiseSimpleDownload(httpUrl("interruptible.txt")); // Ensure that the download cannot complete before cancel is called. let deferResponse = deferNextResponse(); @@ -467,7 +465,7 @@ add_task(function test_download_cancel_immediately_restart_immediately() do_check_false(download.canceled); do_check_true(download.error === null); - yield promiseVerifyContents(download.target.file, + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT + TEST_DATA_SHORT); }); @@ -476,7 +474,7 @@ add_task(function test_download_cancel_immediately_restart_immediately() */ add_task(function test_download_cancel_midway_restart_immediately() { - let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI); + let download = yield promiseSimpleDownload(httpUrl("interruptible.txt")); // The first time, cancel the download midway. let deferResponse = deferNextResponse(); @@ -525,7 +523,7 @@ add_task(function test_download_cancel_midway_restart_immediately() do_check_false(download.canceled); do_check_true(download.error === null); - yield promiseVerifyContents(download.target.file, + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT + TEST_DATA_SHORT); }); @@ -547,7 +545,7 @@ add_task(function test_download_cancel_successful() do_check_false(download.canceled); do_check_true(download.error === null); - yield promiseVerifyContents(download.target.file, TEST_DATA_SHORT); + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); }); /** @@ -555,7 +553,7 @@ add_task(function test_download_cancel_successful() */ add_task(function test_download_cancel_twice() { - let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI); + let download = yield promiseSimpleDownload(httpUrl("interruptible.txt")); // Ensure that the download cannot complete before cancel is called. let deferResponse = deferNextResponse(); @@ -584,7 +582,7 @@ add_task(function test_download_cancel_twice() do_check_true(download.canceled); do_check_true(download.error === null); - do_check_false(download.target.file.exists()); + do_check_false(yield OS.File.exists(download.target.path)); } finally { deferResponse.resolve(); } @@ -595,7 +593,7 @@ add_task(function test_download_cancel_twice() */ add_task(function test_download_whenSucceeded() { - let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI); + let download = yield promiseSimpleDownload(httpUrl("interruptible.txt")); // Ensure that the download cannot complete before cancel is called. let deferResponse = deferNextResponse(); @@ -620,7 +618,7 @@ add_task(function test_download_whenSucceeded() do_check_false(download.canceled); do_check_true(download.error === null); - yield promiseVerifyContents(download.target.file, + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT + TEST_DATA_SHORT); }); @@ -631,7 +629,9 @@ add_task(function test_download_error_source() { let serverSocket = startFakeServer(); try { - let download = yield promiseSimpleDownload(TEST_FAKE_SOURCE_URI); + let sourceUrl = "http://localhost:" + serverSocket.port + "/source.txt"; + + let download = yield promiseSimpleDownload(sourceUrl); do_check_true(download.error === null); @@ -662,7 +662,8 @@ add_task(function test_download_error_target() do_check_true(download.error === null); // Create a file without write access permissions before downloading. - download.target.file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0); + let targetFile = new FileUtils.File(download.target.path); + targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0); try { try { yield download.start(); @@ -678,9 +679,9 @@ add_task(function test_download_error_target() do_check_false(download.error.becauseSourceFailed); } finally { // Restore the default permissions to allow deleting the file on Windows. - if (download.target.file.exists()) { - download.target.file.permissions = FileUtils.PERMS_FILE; - download.target.file.remove(false); + if (targetFile.exists()) { + targetFile.permissions = FileUtils.PERMS_FILE; + targetFile.remove(false); } } }); @@ -695,7 +696,8 @@ add_task(function test_download_error_restart() do_check_true(download.error === null); // Create a file without write access permissions before downloading. - download.target.file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0); + let targetFile = new FileUtils.File(download.target.path); + targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0); try { yield download.start(); @@ -704,15 +706,14 @@ add_task(function test_download_error_restart() // A specific error object is thrown when writing to the target fails. } finally { // Restore the default permissions to allow deleting the file on Windows. - if (download.target.file.exists()) { - download.target.file.permissions = FileUtils.PERMS_FILE; + if (targetFile.exists()) { + targetFile.permissions = FileUtils.PERMS_FILE; // Also for Windows, rename the file before deleting. This makes the // current file name available immediately for a new file, while deleting // in place prevents creation of a file with the same name for some time. - let fileToRemove = download.target.file.clone(); - fileToRemove.moveTo(null, fileToRemove.leafName + ".delete.tmp"); - fileToRemove.remove(false); + targetFile.moveTo(null, targetFile.leafName + ".delete.tmp"); + targetFile.remove(false); } } @@ -725,7 +726,7 @@ add_task(function test_download_error_restart() do_check_true(download.error === null); do_check_eq(download.progress, 100); - yield promiseVerifyContents(download.target.file, TEST_DATA_SHORT); + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); }); /** @@ -733,8 +734,8 @@ add_task(function test_download_error_restart() */ add_task(function test_download_public_and_private() { - let source_path = "/test_download_public_and_private.txt"; - let source_uri = NetUtil.newURI(HTTP_BASE + source_path); + let sourcePath = "/test_download_public_and_private.txt"; + let sourceUrl = httpUrl("test_download_public_and_private.txt"); let testCount = 0; // Apply pref to allow all cookies. @@ -743,11 +744,11 @@ add_task(function test_download_public_and_private() function cleanup() { Services.prefs.clearUserPref("network.cookie.cookieBehavior"); Services.cookies.removeAll(); - gHttpServer.registerPathHandler(source_path, null); + gHttpServer.registerPathHandler(sourcePath, null); } do_register_cleanup(cleanup); - gHttpServer.registerPathHandler(source_path, function (aRequest, aResponse) { + gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { aResponse.setHeader("Content-Type", "text/plain", false); if (testCount == 0) { @@ -767,12 +768,11 @@ add_task(function test_download_public_and_private() }); let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - yield Downloads.simpleDownload(source_uri, targetFile); - yield Downloads.simpleDownload(source_uri, targetFile); + yield Downloads.simpleDownload(sourceUrl, targetFile); + yield Downloads.simpleDownload(sourceUrl, targetFile); let download = yield Downloads.createDownload({ - source: { uri: source_uri, isPrivate: true }, - target: { file: targetFile }, - saver: { type: "copy" }, + source: { url: sourceUrl, isPrivate: true }, + target: targetFile, }); yield download.start(); @@ -805,15 +805,15 @@ add_task(function test_download_cancel_immediately_restart_and_check_startTime() */ add_task(function test_download_with_content_encoding() { - let source_path = "/test_download_with_content_encoding.txt"; - let source_uri = NetUtil.newURI(HTTP_BASE + source_path); + let sourcePath = "/test_download_with_content_encoding.txt"; + let sourceUrl = httpUrl("test_download_with_content_encoding.txt"); function cleanup() { - gHttpServer.registerPathHandler(source_path, null); + gHttpServer.registerPathHandler(sourcePath, null); } do_register_cleanup(cleanup); - gHttpServer.registerPathHandler(source_path, function (aRequest, aResponse) { + gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { aResponse.setHeader("Content-Type", "text/plain", false); aResponse.setHeader("Content-Encoding", "gzip", false); aResponse.setHeader("Content-Length", @@ -825,9 +825,8 @@ add_task(function test_download_with_content_encoding() }); let download = yield Downloads.createDownload({ - source: { uri: source_uri }, - target: { file: getTempFile(TEST_TARGET_FILE_NAME) }, - saver: { type: "copy" }, + source: sourceUrl, + target: getTempFile(TEST_TARGET_FILE_NAME), }); yield download.start(); @@ -835,7 +834,7 @@ add_task(function test_download_with_content_encoding() do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); // Ensure the content matches the decoded test data. - yield promiseVerifyContents(download.target.file, TEST_DATA_SHORT); + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); }); /** @@ -843,7 +842,7 @@ add_task(function test_download_with_content_encoding() */ add_task(function test_download_cancel_midway_restart_with_content_encoding() { - let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_GZIP_URI); + let download = yield promiseSimpleDownload(httpUrl("interruptible_gzip.txt")); // The first time, cancel the download midway. let deferResponse = deferNextResponse(); @@ -870,7 +869,7 @@ add_task(function test_download_cancel_midway_restart_with_content_encoding() do_check_eq(download.progress, 100); do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); - yield promiseVerifyContents(download.target.file, TEST_DATA_SHORT); + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); }); /** diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadLegacy.js b/toolkit/components/jsdownloads/test/unit/test_DownloadLegacy.js index 75f2ec75b866..a98c550e4594 100644 --- a/toolkit/components/jsdownloads/test/unit/test_DownloadLegacy.js +++ b/toolkit/components/jsdownloads/test/unit/test_DownloadLegacy.js @@ -16,8 +16,9 @@ * Starts a new download using the nsIWebBrowserPersist interface, and controls * it using the legacy nsITransfer interface. * - * @param aSourceURI - * The nsIURI for the download source, or null to use TEST_SOURCE_URI. + * @param aSourceUrl + * String containing the URI for the download source, or null to use + * httpUrl("source.txt"). * @param isPrivate * Optional boolean indicates whether the download originated from a * private window. @@ -30,8 +31,8 @@ * download through the legacy nsITransfer interface. * @rejects Never. The current test fails in case of exceptions. */ -function promiseStartLegacyDownload(aSourceURI, aIsPrivate, aOutPersist) { - let sourceURI = aSourceURI || TEST_SOURCE_URI; +function promiseStartLegacyDownload(aSourceUrl, aIsPrivate, aOutPersist) { + let sourceURI = NetUtil.newURI(aSourceUrl || httpUrl("source.txt")); let targetFile = getTempFile(TEST_TARGET_FILE_NAME); let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"] @@ -94,15 +95,16 @@ add_task(function test_basic() let download = yield promiseStartLegacyDownload(); // Checks the generated DownloadSource and DownloadTarget properties. - do_check_true(download.source.uri.equals(TEST_SOURCE_URI)); - do_check_true(download.target.file.parent.equals(tempDirectory)); + do_check_eq(download.source.url, httpUrl("source.txt")); + do_check_true(new FileUtils.File(download.target.path).parent + .equals(tempDirectory)); // The download is already started, wait for completion and report any errors. if (!download.stopped) { yield download.start(); } - yield promiseVerifyContents(download.target.file, TEST_DATA_SHORT); + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); }); /** @@ -131,7 +133,7 @@ add_task(function test_intermediate_progress() { let deferResponse = deferNextResponse(); - let download = yield promiseStartLegacyDownload(TEST_INTERRUPTIBLE_URI); + let download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt")); let onchange = function () { if (download.progress == 50) { @@ -157,7 +159,7 @@ add_task(function test_intermediate_progress() do_check_true(download.stopped); do_check_eq(download.progress, 100); - yield promiseVerifyContents(download.target.file, + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT + TEST_DATA_SHORT); }); @@ -166,7 +168,7 @@ add_task(function test_intermediate_progress() */ add_task(function test_empty_progress() { - let download = yield promiseStartLegacyDownload(TEST_EMPTY_URI); + let download = yield promiseStartLegacyDownload(httpUrl("empty.txt")); // The download is already started, wait for completion and report any errors. if (!download.stopped) { @@ -179,7 +181,7 @@ add_task(function test_empty_progress() do_check_eq(download.currentBytes, 0); do_check_eq(download.totalBytes, 0); - do_check_eq(download.target.file.fileSize, 0); + do_check_eq((yield OS.File.stat(download.target.path)).size, 0); }); /** @@ -190,7 +192,8 @@ add_task(function test_empty_noprogress() let deferResponse = deferNextResponse(); let promiseEmptyRequestReceived = promiseNextRequestReceived(); - let download = yield promiseStartLegacyDownload(TEST_EMPTY_NOPROGRESS_URI); + let download = yield promiseStartLegacyDownload( + httpUrl("empty-noprogress.txt")); // Wait for the request to be received by the HTTP server, but don't allow the // request to finish yet. Before checking the download state, wait for the @@ -218,7 +221,7 @@ add_task(function test_empty_noprogress() do_check_eq(download.currentBytes, 0); do_check_eq(download.totalBytes, 0); - do_check_eq(download.target.file.fileSize, 0); + do_check_eq((yield OS.File.stat(download.target.path)).size, 0); }); /** @@ -228,8 +231,8 @@ add_task(function test_cancel_midway() { let deferResponse = deferNextResponse(); let outPersist = {}; - let download = yield promiseStartLegacyDownload(TEST_INTERRUPTIBLE_URI, false, - outPersist); + let download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt"), + false, outPersist); try { // Cancel the download after receiving the first part of the response. @@ -260,7 +263,7 @@ add_task(function test_cancel_midway() do_check_true(download.canceled); do_check_true(download.error === null); - do_check_false(download.target.file.exists()); + do_check_false(yield OS.File.exists(download.target.path)); // Progress properties are not reset by canceling. do_check_eq(download.progress, 50); @@ -278,7 +281,9 @@ add_task(function test_error() { let serverSocket = startFakeServer(); try { - let download = yield promiseStartLegacyDownload(TEST_FAKE_SOURCE_URI); + let sourceUrl = "http://localhost:" + serverSocket.port + "/source.txt"; + + let download = yield promiseStartLegacyDownload(sourceUrl); // We must check the download properties instead of calling the "start" // method because the download has been started and may already be stopped. @@ -307,8 +312,8 @@ add_task(function test_error() */ add_task(function test_download_public_and_private() { - let source_path = "/test_download_public_and_private.txt"; - let source_uri = NetUtil.newURI(HTTP_BASE + source_path); + let sourcePath = "/test_download_public_and_private.txt"; + let sourceUrl = httpUrl("test_download_public_and_private.txt"); let testCount = 0; // Apply pref to allow all cookies. @@ -317,12 +322,12 @@ add_task(function test_download_public_and_private() function cleanup() { Services.prefs.clearUserPref("network.cookie.cookieBehavior"); Services.cookies.removeAll(); - gHttpServer.registerPathHandler(source_path, null); + gHttpServer.registerPathHandler(sourcePath, null); } do_register_cleanup(cleanup); - gHttpServer.registerPathHandler(source_path, function (aRequest, aResponse) { + gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { aResponse.setHeader("Content-Type", "text/plain", false); if (testCount == 0) { @@ -342,9 +347,9 @@ add_task(function test_download_public_and_private() }); let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - yield Downloads.simpleDownload(source_uri, targetFile); - yield Downloads.simpleDownload(source_uri, targetFile); - let download = yield promiseStartLegacyDownload(source_uri, true); + yield Downloads.simpleDownload(sourceUrl, targetFile); + yield Downloads.simpleDownload(sourceUrl, targetFile); + let download = yield promiseStartLegacyDownload(sourceUrl, true); // The download is already started, wait for completion and report any errors. if (!download.stopped) { yield download.start(); @@ -373,7 +378,7 @@ add_task(function test_download_blocked_parental_controls() do_check_true(ex.becauseBlockedByParentalControls); } - do_check_false(download.target.file.exists()); + do_check_false(yield OS.File.exists(download.target.path)); cleanup(); }); diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadList.js b/toolkit/components/jsdownloads/test/unit/test_DownloadList.js index 4828f6113302..37c3f9d11a11 100644 --- a/toolkit/components/jsdownloads/test/unit/test_DownloadList.js +++ b/toolkit/components/jsdownloads/test/unit/test_DownloadList.js @@ -174,11 +174,11 @@ add_task(function test_history_expiration() // Add expirable visit for downloads. yield promiseAddDownloadToHistory(); - yield promiseAddDownloadToHistory(TEST_INTERRUPTIBLE_URI); + yield promiseAddDownloadToHistory(httpUrl("interruptible.txt")); let list = yield promiseNewDownloadList(); let downloadOne = yield promiseSimpleDownload(); - let downloadTwo = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI); + let downloadTwo = yield promiseSimpleDownload(httpUrl("interruptible.txt")); list.add(downloadOne); list.add(downloadTwo); diff --git a/toolkit/components/jsdownloads/test/unit/test_DownloadStore.js b/toolkit/components/jsdownloads/test/unit/test_DownloadStore.js index b9fc857b212c..dccb9489416f 100644 --- a/toolkit/components/jsdownloads/test/unit/test_DownloadStore.js +++ b/toolkit/components/jsdownloads/test/unit/test_DownloadStore.js @@ -49,12 +49,11 @@ add_task(function test_save_reload() let [listForLoad, storeForLoad] = yield promiseNewListAndStore( storeForSave.path); - listForSave.add(yield promiseSimpleDownload(TEST_SOURCE_URI)); + listForSave.add(yield promiseSimpleDownload(httpUrl("source.txt"))); listForSave.add(yield Downloads.createDownload({ - source: { uri: TEST_EMPTY_URI, - referrer: TEST_REFERRER_URI }, - target: { file: getTempFile(TEST_TARGET_FILE_NAME) }, - saver: { type: "copy" }, + source: { url: httpUrl("empty.txt"), + referrer: TEST_REFERRER_URL }, + target: getTempFile(TEST_TARGET_FILE_NAME), })); yield storeForSave.save(); @@ -71,16 +70,12 @@ add_task(function test_save_reload() do_check_neq(itemsForSave[i], itemsForLoad[i]); // The reloaded downloads have the same properties. - do_check_true(itemsForSave[i].source.uri.equals( - itemsForLoad[i].source.uri)); - if (itemsForSave[i].source.referrer) { - do_check_true(itemsForSave[i].source.referrer.equals( - itemsForLoad[i].source.referrer)); - } else { - do_check_true(itemsForLoad[i].source.referrer === null); - } - do_check_true(itemsForSave[i].target.file.equals( - itemsForLoad[i].target.file)); + do_check_eq(itemsForSave[i].source.url, + itemsForLoad[i].source.url); + do_check_eq(itemsForSave[i].source.referrer, + itemsForLoad[i].source.referrer); + do_check_eq(itemsForSave[i].target.path, + itemsForLoad[i].target.path); do_check_eq(itemsForSave[i].saver.type, itemsForLoad[i].saver.type); } @@ -129,19 +124,17 @@ add_task(function test_load_string_predefined() let [list, store] = yield promiseNewListAndStore(); // The platform-dependent file name should be generated dynamically. - let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - let filePathLiteral = JSON.stringify(targetFile.path); - let sourceUriLiteral = JSON.stringify(TEST_SOURCE_URI.spec); - let emptyUriLiteral = JSON.stringify(TEST_EMPTY_URI.spec); - let referrerUriLiteral = JSON.stringify(TEST_REFERRER_URI.spec); + let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; + let filePathLiteral = JSON.stringify(targetPath); + let sourceUriLiteral = JSON.stringify(httpUrl("source.txt")); + let emptyUriLiteral = JSON.stringify(httpUrl("empty.txt")); + let referrerUriLiteral = JSON.stringify(TEST_REFERRER_URL); - let string = "[{\"source\":{\"uri\":" + sourceUriLiteral + "}," + - "\"target\":{\"file\":" + filePathLiteral + "}," + - "\"saver\":{\"type\":\"copy\"}}," + - "{\"source\":{\"uri\":" + emptyUriLiteral + "," + + let string = "{\"list\":[{\"source\":" + sourceUriLiteral + "," + + "\"target\":" + filePathLiteral + "}," + + "{\"source\":{\"url\":" + emptyUriLiteral + "," + "\"referrer\":" + referrerUriLiteral + "}," + - "\"target\":{\"file\":" + filePathLiteral + "}," + - "\"saver\":{\"type\":\"copy\"}}]"; + "\"target\":" + filePathLiteral + "}]}"; yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string), @@ -153,12 +146,12 @@ add_task(function test_load_string_predefined() do_check_eq(items.length, 2); - do_check_true(items[0].source.uri.equals(TEST_SOURCE_URI)); - do_check_true(items[0].target.file.equals(targetFile)); + do_check_eq(items[0].source.url, httpUrl("source.txt")); + do_check_eq(items[0].target.path, targetPath); - do_check_true(items[1].source.uri.equals(TEST_EMPTY_URI)); - do_check_true(items[1].source.referrer.equals(TEST_REFERRER_URI)); - do_check_true(items[1].target.file.equals(targetFile)); + do_check_eq(items[1].source.url, httpUrl("empty.txt")); + do_check_eq(items[1].source.referrer, TEST_REFERRER_URL); + do_check_eq(items[1].target.path, targetPath); }); /** @@ -169,15 +162,15 @@ add_task(function test_load_string_unrecognized() let [list, store] = yield promiseNewListAndStore(); // The platform-dependent file name should be generated dynamically. - let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - let filePathLiteral = JSON.stringify(targetFile.path); - let sourceUriLiteral = JSON.stringify(TEST_SOURCE_URI.spec); + let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; + let filePathLiteral = JSON.stringify(targetPath); + let sourceUriLiteral = JSON.stringify(httpUrl("source.txt")); - let string = "[{\"source\":null," + + let string = "{\"list\":[{\"source\":null," + "\"target\":null}," + - "{\"source\":{\"uri\":" + sourceUriLiteral + "}," + - "\"target\":{\"file\":" + filePathLiteral + "}," + - "\"saver\":{\"type\":\"copy\"}}]"; + "{\"source\":{\"url\":" + sourceUriLiteral + "}," + + "\"target\":{\"path\":" + filePathLiteral + "}," + + "\"saver\":{\"type\":\"copy\"}}]}"; yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string), @@ -189,8 +182,8 @@ add_task(function test_load_string_unrecognized() do_check_eq(items.length, 1); - do_check_true(items[0].source.uri.equals(TEST_SOURCE_URI)); - do_check_true(items[0].target.file.equals(targetFile)); + do_check_eq(items[0].source.url, httpUrl("source.txt")); + do_check_eq(items[0].target.path, targetPath); }); /** @@ -200,8 +193,8 @@ add_task(function test_load_string_malformed() { let [list, store] = yield promiseNewListAndStore(); - let string = "[{\"source\":null,\"target\":null}," + - "{\"source\":{\"uri\":\"about:blank\"}}"; + let string = "{\"list\":[{\"source\":null,\"target\":null}," + + "{\"source\":{\"url\":\"about:blank\"}}}"; yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string), { tmpPath: store.path + ".tmp" }); diff --git a/toolkit/components/jsdownloads/test/unit/test_Downloads.js b/toolkit/components/jsdownloads/test/unit/test_Downloads.js index faf55fb6b211..f526a91085e3 100644 --- a/toolkit/components/jsdownloads/test/unit/test_Downloads.js +++ b/toolkit/components/jsdownloads/test/unit/test_Downloads.js @@ -20,21 +20,20 @@ add_task(function test_createDownload() { // Creates a simple Download object without starting the download. yield Downloads.createDownload({ - source: { uri: NetUtil.newURI("about:blank") }, - target: { file: getTempFile(TEST_TARGET_FILE_NAME) }, + source: { url: "about:blank" }, + target: { path: getTempFile(TEST_TARGET_FILE_NAME).path }, saver: { type: "copy" }, }); }); /** -* Tests createDownload for private download. + * Tests createDownload for private download. */ add_task(function test_createDownload_private() { let download = yield Downloads.createDownload({ - source: { uri: NetUtil.newURI("about:blank"), - isPrivate: true }, - target: { file: getTempFile(TEST_TARGET_FILE_NAME) }, + source: { url: "about:blank", isPrivate: true }, + target: { path: getTempFile(TEST_TARGET_FILE_NAME).path }, saver: { type: "copy" } }); do_check_true(download.source.isPrivate); @@ -45,21 +44,20 @@ add_task(function test_createDownload_private() */ add_task(function test_createDownload_public() { - let uri = NetUtil.newURI("about:blank"); - let tempFile = getTempFile(TEST_TARGET_FILE_NAME); + let tempPath = getTempFile(TEST_TARGET_FILE_NAME).path; let download = yield Downloads.createDownload({ - source: { uri: uri, isPrivate: false }, - target: { file: tempFile }, + source: { url: "about:blank", isPrivate: false }, + target: { path: tempPath }, saver: { type: "copy" } }); do_check_false(download.source.isPrivate); download = yield Downloads.createDownload({ - source: { uri: uri }, - target: { file: tempFile }, + source: { url: "about:blank" }, + target: { path: tempPath }, saver: { type: "copy" } }); - do_check_true(!download.source.isPrivate); + do_check_false(download.source.isPrivate); }); /** @@ -68,8 +66,9 @@ add_task(function test_createDownload_public() add_task(function test_simpleDownload_uri_file_arguments() { let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - yield Downloads.simpleDownload(TEST_SOURCE_URI, targetFile); - yield promiseVerifyContents(targetFile, TEST_DATA_SHORT); + yield Downloads.simpleDownload(NetUtil.newURI(httpUrl("source.txt")), + targetFile); + yield promiseVerifyContents(targetFile.path, TEST_DATA_SHORT); }); /** @@ -77,10 +76,10 @@ add_task(function test_simpleDownload_uri_file_arguments() */ add_task(function test_simpleDownload_object_arguments() { - let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - yield Downloads.simpleDownload({ uri: TEST_SOURCE_URI }, - { file: targetFile }); - yield promiseVerifyContents(targetFile, TEST_DATA_SHORT); + let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; + yield Downloads.simpleDownload({ url: httpUrl("source.txt") }, + { path: targetPath }); + yield promiseVerifyContents(targetPath, TEST_DATA_SHORT); }); /** @@ -88,15 +87,15 @@ add_task(function test_simpleDownload_object_arguments() */ add_task(function test_simpleDownload_string_arguments() { - let targetFile = getTempFile(TEST_TARGET_FILE_NAME); - yield Downloads.simpleDownload(TEST_SOURCE_URI.spec, - targetFile.path); - yield promiseVerifyContents(targetFile, TEST_DATA_SHORT); + let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; + yield Downloads.simpleDownload(httpUrl("source.txt"), + targetPath); + yield promiseVerifyContents(targetPath, TEST_DATA_SHORT); - targetFile = getTempFile(TEST_TARGET_FILE_NAME); - yield Downloads.simpleDownload(new String(TEST_SOURCE_URI.spec), - new String(targetFile.path)); - yield promiseVerifyContents(targetFile, TEST_DATA_SHORT); + targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; + yield Downloads.simpleDownload(new String(httpUrl("source.txt")), + new String(targetPath)); + yield promiseVerifyContents(targetPath, TEST_DATA_SHORT); }); /**