From d6c976bf65e309d298c250d7466b2cb0e523d088 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Sun, 29 Jul 2018 11:41:22 +0200 Subject: [PALEMOON] Bug 1117141 - Part 2 of 2 - Refactor notifications and remove the DownloadsDataItem object --- .../components/downloads/DownloadsCommon.jsm | 527 +++++---------------- .../downloads/content/allDownloadsViewOverlay.js | 165 +++---- .../components/downloads/content/downloads.js | 121 ++--- .../downloads/content/downloadsViewCommon.js | 14 +- 4 files changed, 234 insertions(+), 593 deletions(-) (limited to 'application') diff --git a/application/palemoon/components/downloads/DownloadsCommon.jsm b/application/palemoon/components/downloads/DownloadsCommon.jsm index c2185f623..e110aa904 100644 --- a/application/palemoon/components/downloads/DownloadsCommon.jsm +++ b/application/palemoon/components/downloads/DownloadsCommon.jsm @@ -8,7 +8,6 @@ this.EXPORTED_SYMBOLS = [ "DownloadsCommon", - "DownloadsDataItem", ]; /** @@ -22,14 +21,9 @@ this.EXPORTED_SYMBOLS = [ * * DownloadsData * Retrieves the list of past and completed downloads from the underlying - * Download Manager data, and provides asynchronous notifications allowing + * Downloads API data, and provides asynchronous notifications allowing * to build a consistent view of the available data. * - * DownloadsDataItem - * Represents a single item in the list of downloads. This object wraps the - * Download object from the JavaScript API for downloads. A specialized version - * of this object is implemented in the Places front-end view. - * * DownloadsIndicatorData * This object registers itself with DownloadsData as a view, and transforms the * notifications it receives into overall status data, that is then broadcast to @@ -399,10 +393,10 @@ this.DownloadsCommon = { }, /** - * Given an iterable collection of DownloadDataItems, generates and returns + * Given an iterable collection of Download objects, generates and returns * statistics about that collection. * - * @param aDataItems An iterable collection of DownloadDataItems. + * @param downloads An iterable collection of Download objects. * * @return Object whose properties are the generated statistics. Currently, * we return the following properties: @@ -419,7 +413,7 @@ this.DownloadsCommon = { * complete. * percentComplete : The percentage of bytes successfully downloaded. */ - summarizeDownloads: function DC_summarizeDownloads(aDataItems) +summarizeDownloads(downloads) { { let summary = { numActive: 0, @@ -431,14 +425,13 @@ this.DownloadsCommon = { // slowestSpeed is Infinity so that we can use Math.min to // find the slowest speed. We'll set this to 0 afterwards if // it's still at Infinity by the time we're done iterating all - // dataItems. + // download. slowestSpeed: Infinity, rawTimeLeft: -1, percentComplete: -1 } - for (let dataItem of aDataItems) { - let download = dataItem.download; + for (let download of downloads) { let state = DownloadsCommon.stateOfDownload(download); let maxBytes = DownloadsCommon.maxBytesOfDownload(download); @@ -670,16 +663,12 @@ XPCOMUtils.defineLazyGetter(DownloadsCommon, "useJSTransfer", function () { function DownloadsDataCtor(aPrivate) { this._isPrivate = aPrivate; - // Contains all the available DownloadsDataItem objects. - this.dataItems = new Set(); + // Contains all the available Download objects and their integer state. this.oldDownloadStates = new Map(); // Array of view objects that should be notified when the available download // data changes. this._views = []; - - // Maps Download objects to DownloadDataItem objects. - this._downloadToDataItemMap = new Map(); } DownloadsDataCtor.prototype = { @@ -705,13 +694,18 @@ DownloadsDataCtor.prototype = { return; }, + /** + * Iterator for all the available Download objects. This is empty until the + * data has been loaded using the JavaScript API for downloads. + */ + get downloads() this.oldDownloadStates.keys(), + /** * True if there are finished downloads that can be removed from the list. */ get canRemoveFinished() { - for (let dataItem of this.dataItems) { - let download = dataItem.download; + for (let download of this.oldDownloadStates.keys()) { // Stopped, paused, and failed downloads with partial data are removed. if (download.stopped && !(download.canceled && download.hasPartialData)) { return true; @@ -734,37 +728,32 @@ DownloadsDataCtor.prototype = { ////////////////////////////////////////////////////////////////////////////// //// Integration with the asynchronous Downloads back-end - onDownloadAdded: function (aDownload) - { - let dataItem = new DownloadsDataItem(aDownload); - this._downloadToDataItemMap.set(aDownload, dataItem); - this.dataItems.add(dataItem); - this.oldDownloadStates.set(aDownload, - DownloadsCommon.stateOfDownload(aDownload)); + onDownloadAdded(download) { + // Download objects do not store the end time of downloads, as the Downloads + // API does not need to persist this information for all platforms. Once a + // download terminates on a Desktop browser, it becomes a history download, + // for which the end time is stored differently, as a Places annotation. + download.endTime = Date.now(); + + this.oldDownloadStates.set(download, + DownloadsCommon.stateOfDownload(download)); for (let view of this._views) { - view.onDataItemAdded(dataItem, true); + view.onDownloadAdded(download, true); } }, - onDownloadChanged: function (aDownload) - { - let aDataItem = this._downloadToDataItemMap.get(aDownload); - if (!aDataItem) { - Cu.reportError("Download doesn't exist."); - return; - } - - let oldState = this.oldDownloadStates.get(aDownload); - let newState = DownloadsCommon.stateOfDownload(aDownload); - this.oldDownloadStates.set(aDownload, newState); + onDownloadChanged(download) { + let oldState = this.oldDownloadStates.get(download); + let newState = DownloadsCommon.stateOfDownload(download); + this.oldDownloadStates.set(download, newState); if (oldState != newState) { - if (aDownload.succeeded || - (aDownload.canceled && !aDownload.hasPartialData) || - aDownload.error) { + if (download.succeeded || + (download.canceled && !download.hasPartialData) || + download.error) { // Store the end time that may be displayed by the views. - aDownload.endTime = Date.now(); + download.endTime = Date.now(); // This state transition code should actually be located in a Downloads // API module (bug 941009). Moreover, the fact that state is stored as @@ -773,17 +762,17 @@ DownloadsDataCtor.prototype = { if (!this._isPrivate) { try { let downloadMetaData = { - state: DownloadsCommon.stateOfDownload(aDownload), - endTime: aDownload.endTime, + state: DownloadsCommon.stateOfDownload(download), + endTime: download.endTime, }; - if (aDownload.succeeded || - (aDownload.error && aDownload.error.becauseBlocked)) { + if (download.succeeded || + (download.error && download.error.becauseBlocked)) { downloadMetaData.fileSize = - DownloadsCommon.maxBytesOfDownload(aDataItem.download); + DownloadsCommon.maxBytesOfDownload(download); } PlacesUtils.annotations.setPageAnnotation( - NetUtil.newURI(aDownload.source.url), + NetUtil.newURI(download.source.url), "downloads/metaData", JSON.stringify(downloadMetaData), 0, PlacesUtils.annotations.EXPIRE_WITH_HISTORY); @@ -795,41 +784,33 @@ DownloadsDataCtor.prototype = { for (let view of this._views) { try { - view.onDataItemStateChanged(aDataItem); + view.onDownloadStateChanged(download); } catch (ex) { Cu.reportError(ex); } } - if (aDownload.succeeded || - (aDownload.error && aDownload.error.becauseBlocked)) { + if (download.succeeded || + (download.error && download.error.becauseBlocked)) { this._notifyDownloadEvent("finish"); } } - if (!aDownload.newDownloadNotified) { - aDownload.newDownloadNotified = true; + if (!download.newDownloadNotified) { + download.newDownloadNotified = true; this._notifyDownloadEvent("start"); } for (let view of this._views) { - view.onDataItemChanged(aDataItem); + view.onDownloadChanged(download); } }, - onDownloadRemoved: function (aDownload) - { - let dataItem = this._downloadToDataItemMap.get(aDownload); - if (!dataItem) { - Cu.reportError("Download doesn't exist."); - return; - } + onDownloadRemoved(download) { + this.oldDownloadStates.delete(download); - this._downloadToDataItemMap.delete(aDownload); - this.dataItems.delete(dataItem); - this.oldDownloadStates.delete(aDownload); for (let view of this._views) { - view.onDataItemRemoved(dataItem); + view.onDownloadRemoved(download); } }, @@ -881,12 +862,9 @@ DownloadsDataCtor.prototype = { //let loadedItemsArray = [dataItem // for each (dataItem in this.dataItems) // if (dataItem)]; - let loadedItemsArray = [...this.dataItems]; - - loadedItemsArray.sort(function(a, b) b.download.startTime - a.download.startTime); - loadedItemsArray.forEach( - function (dataItem) aView.onDataItemAdded(dataItem, false) - ); + let downloadsArray = [...this.oldDownloadStates.keys()]; + downloadsArray.sort((a, b) => b.startTime - a.startTime); + downloadsArray.forEach(download => aView.onDownloadAdded(download, false)); // Notify the view that all data is available unless loading is in progress. if (!this._pendingStatement) { @@ -1337,254 +1315,6 @@ XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() { return new DownloadsDataCtor(false); }); -//////////////////////////////////////////////////////////////////////////////// -//// DownloadsDataItem - -/** - * Represents a single item in the list of downloads. This object either wraps - * an existing nsIDownload from the Download Manager, or provides the same - * information read directly from the downloads database, with the possibility - * of querying the nsIDownload lazily, for performance reasons. - * - * @param aSource - * Object containing the data with which the item should be initialized. - * This should implement either nsIDownload or mozIStorageRow. If the - * JavaScript API for downloads is enabled, this is a Download object. - */ -function DownloadsDataItem(aSource) -{ - this._initFromJSDownload(aSource); -} - -DownloadsDataItem.prototype = { - get state() DownloadsCommon.stateOfDownload(this.download), - - /** - * Initializes this object from the JavaScript API for downloads. - * - * The endTime property is initialized to the current date and time. - * - * @param aDownload - * The Download object with the current state. - */ - _initFromJSDownload: function (aDownload) - { - this.download = aDownload; - this.download.endTime = Date.now(); - }, - - /** - * Initializes this object from a download object of the Download Manager. - * - * The endTime property is initialized to the current date and time. - * - * @param aDownload - * The nsIDownload with the current state. - */ - _initFromDownload: function DDI_initFromDownload(aDownload) - { - this._download = aDownload; - - // Fetch all the download properties eagerly. - this.downloadGuid = aDownload.guid; - this.file = aDownload.target.spec; - this.target = aDownload.displayName; - this.uri = aDownload.source.spec; - this.referrer = aDownload.referrer && aDownload.referrer.spec; - this.state = aDownload.state; - this.startTime = Math.round(aDownload.startTime / 1000); - this.endTime = Date.now(); - this.currBytes = aDownload.amountTransferred; - this.maxBytes = aDownload.size; - this.resumable = aDownload.resumable; - this.speed = aDownload.speed; - this.percentComplete = aDownload.percentComplete; - }, - - /** - * Initializes this object from a data row in the downloads database, without - * querying the associated nsIDownload object, to improve performance when - * loading the list of downloads asynchronously. - * - * When this object is initialized in this way, accessing the "download" - * property loads the underlying nsIDownload object synchronously, and should - * be avoided unless the object is really required. - * - * @param aStorageRow - * The mozIStorageRow from the downloads database. - */ - _initFromDataRow: function DDI_initFromDataRow(aStorageRow) - { - // Get the download properties from the data row. - this._download = null; - this.downloadGuid = aStorageRow.getResultByName("guid"); - this.file = aStorageRow.getResultByName("target"); - this.target = aStorageRow.getResultByName("name"); - this.uri = aStorageRow.getResultByName("source"); - this.referrer = aStorageRow.getResultByName("referrer"); - this.state = aStorageRow.getResultByName("state"); - this.startTime = Math.round(aStorageRow.getResultByName("startTime") / 1000); - this.endTime = Math.round(aStorageRow.getResultByName("endTime") / 1000); - this.currBytes = aStorageRow.getResultByName("currBytes"); - this.maxBytes = aStorageRow.getResultByName("maxBytes"); - - // Now we have to determine if the download is resumable, but don't want to - // access the underlying download object unnecessarily. The only case where - // the property is relevant is when we are currently downloading data, and - // in this case the download object is already loaded in memory or will be - // loaded very soon in any case. In all the other cases, including a paused - // download, we assume that the download is resumable. The property will be - // updated as soon as the underlying download state changes. - - // We'll start by assuming we're resumable, and then if we're downloading, - // update resumable property in case we were wrong. - this.resumable = true; - - if (this.state == nsIDM.DOWNLOAD_DOWNLOADING) { - this.getDownload(function(aDownload) { - this.resumable = aDownload.resumable; - }.bind(this)); - } - - // Compute the other properties without accessing the download object. - this.speed = 0; - this.percentComplete = this.maxBytes <= 0 - ? -1 - : Math.round(this.currBytes / this.maxBytes * 100); - }, - - /** - * Asynchronous getter for the download object corresponding to this data item. - * - * @param aCallback - * A callback function which will be called when the download object is - * available. It should accept one argument which will be the download - * object. - */ - getDownload: function DDI_getDownload(aCallback) { - if (this._download) { - // Return the download object asynchronously to the caller - let download = this._download; - Services.tm.mainThread.dispatch(function () aCallback(download), - Ci.nsIThread.DISPATCH_NORMAL); - } else { - Services.downloads.getDownloadByGUID(this.downloadGuid, - function(aStatus, aResult) { - if (!Components.isSuccessCode(aStatus)) { - Cu.reportError( - new Components.Exception("Cannot retrieve download for GUID: " + - this.downloadGuid)); - } else { - this._download = aResult; - aCallback(aResult); - } - }.bind(this)); - } - }, - - /** - * Indicates whether the download is proceeding normally, and not finished - * yet. This includes paused downloads. When this property is true, the - * "progress" property represents the current progress of the download. - */ - get inProgress() - { - return [ - nsIDM.DOWNLOAD_NOTSTARTED, - nsIDM.DOWNLOAD_QUEUED, - nsIDM.DOWNLOAD_DOWNLOADING, - nsIDM.DOWNLOAD_PAUSED, - nsIDM.DOWNLOAD_SCANNING, - ].indexOf(this.state) != -1; - }, - - /** - * This is true during the initial phases of a download, before the actual - * download of data bytes starts. - */ - get starting() - { - return this.state == nsIDM.DOWNLOAD_NOTSTARTED || - this.state == nsIDM.DOWNLOAD_QUEUED; - }, - - /** - * Indicates whether the download is paused. - */ - get paused() - { - return this.state == nsIDM.DOWNLOAD_PAUSED; - }, - - /** - * Indicates whether the download is in a final state, either because it - * completed successfully or because it was blocked. - */ - get done() - { - return [ - nsIDM.DOWNLOAD_FINISHED, - nsIDM.DOWNLOAD_BLOCKED_PARENTAL, - nsIDM.DOWNLOAD_BLOCKED_POLICY, - nsIDM.DOWNLOAD_DIRTY, - ].indexOf(this.state) != -1; - }, - - /** - * Indicates whether the download stopped because of an error, and can be - * resumed manually. - */ - get canRetry() - { - return this.state == nsIDM.DOWNLOAD_CANCELED || - this.state == nsIDM.DOWNLOAD_FAILED; - }, - - /** - * Returns the nsILocalFile for the download target. - * - * @throws if the native path is not valid. This can happen if the same - * profile is used on different platforms, for example if a native - * Windows path is stored and then the item is accessed on a Mac. - * - * @deprecated Callers should use OS.File and "download.target.path". - */ - get localFile() - { - // We should remove should use this.download.target.partFilePath and check asyncrhonously. - return new FileUtils.File(this.download.target.path); - }, - - /** - * Returns the nsILocalFile for the partially downloaded target. - * - * @throws if the native path is not valid. This can happen if the same - * profile is used on different platforms, for example if a native - * Windows path is stored and then the item is accessed on a Mac. - * - * @deprecated Callers should use OS.File and "download.target.partFilePath". - */ - get partFile() - { - return new FileUtils.File(this.download.target.path + kPartialDownloadSuffix); - }, - - /** - * Support function that deletes the local file for a download. This is - * used in cases where the Download Manager service doesn't delete the file - * from disk when cancelling. See bug 732924. - */ - _ensureLocalFileRemoved: function DDI__ensureLocalFileRemoved() - { - try { - let localFile = this.localFile; - if (localFile.exists()) { - localFile.remove(false); - } - } catch (ex) { } - } -}; - //////////////////////////////////////////////////////////////////////////////// //// DownloadsViewPrototype @@ -1712,9 +1442,9 @@ const DownloadsViewPrototype = { * Called when a new download data item is available, either during the * asynchronous data load or when a new download is started. * - * @param aDataItem - * DownloadsDataItem object that was just added. - * @param aNewest + * @param download + * Download object that was just added. + * @param newest * When true, indicates that this item is the most recent and should be * added in the topmost position. This happens when a new download is * started. When false, indicates that the item is the least recent @@ -1723,46 +1453,47 @@ const DownloadsViewPrototype = { * * @note Subclasses should override this. */ - onDataItemAdded: function DVP_onDataItemAdded(aDataItem, aNewest) - { + onDownloadAdded(download, newest) { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; }, /** - * Called when a data item is removed, ensures that the widget associated with - * the view item is removed from the user interface. + * Called when the overall state of a Download has changed. In particular, + * this is called only once when the download succeeds or is blocked + * permanently, and is never called if only the current progress changed. * - * @param aDataItem - * DownloadsDataItem object that is being removed. + * The onDownloadChanged notification will always be sent afterwards. * * @note Subclasses should override this. */ - onDataItemRemoved: function DVP_onDataItemRemoved(aDataItem) - { + onDownloadStateChanged(download) { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; }, /** - * Called when the "state" property of a DownloadsDataItem has changed. + * Called every time any state property of a Download may have changed, + * including progress properties. * - * The onDataItemChanged notification will be sent afterwards. + * Note that progress notification changes are throttled at the Downloads.jsm + * API level, and there is no throttling mechanism in the front-end. * * @note Subclasses should override this. */ - onDataItemStateChanged(aDataItem) { + onDownloadChanged(download) { + { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; }, /** - * Called every time any state property of a DownloadsDataItem may have - * changed, including progress properties and the "state" property. + * Called when a data item is removed, ensures that the widget associated with + * the view item is removed from the user interface. * - * Note that progress notification changes are throttled at the Downloads.jsm - * API level, and there is no throttling mechanism in the front-end. + * @param download + * Download object that is being removed. * * @note Subclasses should override this. */ - onDataItemChanged(aDataItem) { + onDownloadRemoved(download) { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; }, @@ -1826,9 +1557,6 @@ DownloadsIndicatorDataCtor.prototype = { ////////////////////////////////////////////////////////////////////////////// //// Callback functions from DownloadsData - /** - * Called after data loading finished. - */ onDataLoadCompleted: function DID_onDataLoadCompleted() { DownloadsViewPrototype.onDataLoadCompleted.call(this); @@ -1845,42 +1573,13 @@ DownloadsIndicatorDataCtor.prototype = { this._itemCount = 0; }, - /** - * Called when a new download data item is available, either during the - * asynchronous data load or when a new download is started. - * - * @param aDataItem - * DownloadsDataItem object that was just added. - * @param aNewest - * When true, indicates that this item is the most recent and should be - * added in the topmost position. This happens when a new download is - * started. When false, indicates that the item is the least recent - * with regard to the items that have been already added. The latter - * generally happens during the asynchronous data load. - */ - onDataItemAdded: function DID_onDataItemAdded(aDataItem, aNewest) + onDownloadAdded(download, newest) { { this._itemCount++; this._updateViews(); }, - /** - * Called when a data item is removed, ensures that the widget associated with - * the view item is removed from the user interface. - * - * @param aDataItem - * DownloadsDataItem object that is being removed. - */ - onDataItemRemoved: function DID_onDataItemRemoved(aDataItem) - { - this._itemCount--; - this._updateViews(); - }, - - // DownloadsView - onDataItemStateChanged(aDataItem) { - let download = aDataItem.download; - + onDownloadStateChanged(download) { if (download.succeeded || download.error) { this.attention = true; } @@ -1890,8 +1589,12 @@ DownloadsIndicatorDataCtor.prototype = { this._lastTimeLeft = -1; }, - // DownloadsView - onDataItemChanged() { + onDownloadChanged(download) { + this._updateViews(); + }, + + onDownloadRemoved(download) { + this._itemCount--; this._updateViews(); }, @@ -1983,22 +1686,17 @@ DownloadsIndicatorDataCtor.prototype = { _lastTimeLeft: -1, /** - * A generator function for the dataItems that this summary is currently + * A generator function for the Download objects this summary is currently * interested in. This generator is passed off to summarizeDownloads in order - * to generate statistics about the dataItems we care about - in this case, - * it's all dataItems for active downloads. + * to generate statistics about the downloads we care about - in this case, + * it's all active downloads. */ - _activeDataItems: function DID_activeDataItems() - { - let dataItems = this._isPrivate ? PrivateDownloadsData.dataItems - : DownloadsData.dataItems; - for (let dataItem of dataItems) { - if (!dataItem) { - continue; - } - let download = dataItem.download; + _activeDownloads() { + let downloads = this._isPrivate ? PrivateDownloadsData.downloads + : DownloadsData.downloads; + for (let download of downloads) { if (!download.stopped || (download.canceled && download.hasPartialData)) { - yield dataItem; + yield download; } } }, @@ -2009,7 +1707,7 @@ DownloadsIndicatorDataCtor.prototype = { _refreshProperties: function DID_refreshProperties() { let summary = - DownloadsCommon.summarizeDownloads(this._activeDataItems()); + DownloadsCommon.summarizeDownloads(this._activeDownloads()); // Determine if the indicator should be shown or get attention. this._hasDownloads = (this._itemCount > 0); @@ -2070,7 +1768,7 @@ function DownloadsSummaryData(aIsPrivate, aNumToExclude) { // completely separated from one another. this._loading = false; - this._dataItems = []; + this._downloads = []; // Floating point value indicating the last number of seconds estimated until // the longest download will finish. We need to store this value so that we @@ -2110,9 +1808,9 @@ DownloadsSummaryData.prototype = { DownloadsViewPrototype.removeView.call(this, aView); if (this._views.length == 0) { - // Clear out our collection of DownloadDataItems. If we ever have + // Clear out our collection of Download objects. If we ever have // another view registered with us, this will get re-populated. - this._dataItems = []; + this._downloads = []; } }, @@ -2132,33 +1830,29 @@ DownloadsSummaryData.prototype = { this._dataItems = []; }, - onDataItemAdded: function DSD_onDataItemAdded(aDataItem, aNewest) - { - if (aNewest) { - this._dataItems.unshift(aDataItem); + onDownloadAdded(download, newest) { + if (newest) { + this._downloads.unshift(download); } else { - this._dataItems.push(aDataItem); + this._downloads.push(download); } this._updateViews(); }, - onDataItemRemoved: function DSD_onDataItemRemoved(aDataItem) - { - let itemIndex = this._dataItems.indexOf(aDataItem); - this._dataItems.splice(itemIndex, 1); - this._updateViews(); - }, - - // DownloadsView - onDataItemStateChanged() { + onDownloadStateChanged() { // Since the state of a download changed, reset the estimated time left. this._lastRawTimeLeft = -1; this._lastTimeLeft = -1; }, - // DownloadsView - onDataItemChanged() { + onDownloadChanged() { + this._updateViews(); + }, + + onDownloadRemoved(download) { + let itemIndex = this._downloads.indexOf(download); + this._downloads.splice(itemIndex, 1); this._updateViews(); }, @@ -2197,17 +1891,16 @@ DownloadsSummaryData.prototype = { //// Property updating based on current download status /** - * A generator function for the dataItems that this summary is currently + * A generator function for the Download objects this summary is currently * interested in. This generator is passed off to summarizeDownloads in order - * to generate statistics about the dataItems we care about - in this case, - * it's the dataItems in this._dataItems after the first few to exclude, + * to generate statistics about the downloads we care about - in this case, + * it's the downloads in this._downloads after the first few to exclude, * which was set when constructing this DownloadsSummaryData instance. */ - _dataItemsForSummary: function DSD_dataItemsForSummary() - { - if (this._dataItems.length > 0) { - for (let i = this._numToExclude; i < this._dataItems.length; ++i) { - yield this._dataItems[i]; + _downloadsForSummary() { + if (this._downloads.length > 0) { + for (let i = this._numToExclude; i < this._downloads.length; ++i) { + yield this._downloads[i]; } } }, @@ -2219,7 +1912,7 @@ DownloadsSummaryData.prototype = { { // Pre-load summary with default values. let summary = - DownloadsCommon.summarizeDownloads(this._dataItemsForSummary()); + DownloadsCommon.summarizeDownloads(this._downloadsForSummary()); this._description = DownloadsCommon.strings .otherDownloads2(summary.numActive); diff --git a/application/palemoon/components/downloads/content/allDownloadsViewOverlay.js b/application/palemoon/components/downloads/content/allDownloadsViewOverlay.js index 59da61c91..a895bd9f6 100644 --- a/application/palemoon/components/downloads/content/allDownloadsViewOverlay.js +++ b/application/palemoon/components/downloads/content/allDownloadsViewOverlay.js @@ -2,8 +2,6 @@ * 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/. */ -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsDataItem", - "resource:///modules/DownloadsCommon.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", "resource:///modules/RecentWindow.jsm"); @@ -127,21 +125,6 @@ HistoryDownload.prototype = { }, }; -/** - * Represents a download from the browser history. It uses the same interface as - * the DownloadsDataItem object. - * - * @param aPlacesNode - * The Places node for the history download. - */ -function DownloadsHistoryDataItem(aPlacesNode) { - this.download = new HistoryDownload(aPlacesNode); -} - -DownloadsHistoryDataItem.prototype = { - __proto__: DownloadsDataItem.prototype, -}; - /** * A download element shell is responsible for handling the commands and the * displayed data for a single download view element. @@ -157,23 +140,23 @@ DownloadsHistoryDataItem.prototype = { * The caller is also responsible for forwarding status notifications for * session downloads, calling the onStateChanged and onChanged methods. * - * @param [optional] aSessionDataItem - * The session download, required if aHistoryDataItem is not set. - * @param [optional] aHistoryDataItem - * The history download, required if aSessionDataItem is not set. + * @param [optional] aSessionDownload + * The session download, required if aHistoryDownload is not set. + * @param [optional] aHistoryDownload + * The history download, required if aSessionDownload is not set. */ -function HistoryDownloadElementShell(aSessionDataItem, aHistoryDataItem) { +function HistoryDownloadElementShell(aSessionDownload, aHistoryDownload) { this.element = document.createElement("richlistitem"); this.element._shell = this; this.element.classList.add("download"); this.element.classList.add("download-state"); - if (aSessionDataItem) { - this.sessionDataItem = aSessionDataItem; + if (aSessionDownload) { + this.sessionDownload = aSessionDownload; } - if (aHistoryDataItem) { - this.historyDataItem = aHistoryDataItem; + if (aHistoryDownload) { + this.historyDownload = aHistoryDownload; } } @@ -196,20 +179,20 @@ HistoryDownloadElementShell.prototype = { get active() !!this._active, /** - * DownloadsDataItem or DownloadsHistoryDataItem object to use for displaying - * information and for executing commands in the user interface. + * Overrides the base getter to return the Download or HistoryDownload object + * for displaying information and executing commands in the user interface. */ - get dataItem() this._sessionDataItem || this._historyDataItem, - - _sessionDataItem: null, - get sessionDataItem() this._sessionDataItem, - set sessionDataItem(aValue) { - if (this._sessionDataItem != aValue) { - if (!aValue && !this._historyDataItem) { - throw new Error("Should always have either a dataItem or a historyDataItem"); + get download() this._sessionDownload || this._historyDownload, + + _sessionDownload: null, + get sessionDownload() this._sessionDownload, + set sessionDownload(aValue) { + if (this._sessionDownload != aValue) { + if (!aValue && !this._historyDownload) { + throw new Error("Should always have either a Download or a HistoryDownload"); } - this._sessionDataItem = aValue; + this._sessionDownload = aValue; this.ensureActive(); this._updateUI(); @@ -217,19 +200,19 @@ HistoryDownloadElementShell.prototype = { return aValue; }, - _historyDataItem: null, - get historyDataItem() this._historyDataItem, - set historyDataItem(aValue) { - if (this._historyDataItem != aValue) { - if (!aValue && !this._sessionDataItem) { - throw new Error("Should always have either a dataItem or a historyDataItem"); + _historyDownload: null, + get historyDownload() this._historyDownload, + set historyDownload(aValue) { + if (this._historyDownload != aValue) { + if (!aValue && !this._sessionDownload) { + throw new Error("Should always have either a Download or a HistoryDownload"); } - this._historyDataItem = aValue; + this._historyDownload = aValue; // We don't need to update the UI if we had a session data item, because // the places information isn't used in this case. - if (!this._sessionDataItem) { + if (!this._sessionDownload) { this._updateUI(); } } @@ -286,7 +269,7 @@ HistoryDownloadElementShell.prototype = { // We cannot open a session download file unless it's succeeded. // If it's succeeded, we need to make sure the file was not removed, // as we do for past downloads. - if (this._sessionDataItem && !this.download.succeeded) { + if (this._sessionDownload && !this.download.succeeded) { return false; } @@ -299,7 +282,7 @@ HistoryDownloadElementShell.prototype = { return this.download.succeeded; case "downloadsCmd_show": // TODO: Bug 827010 - Handle part-file asynchronously. - if (this._sessionDataItem && this.download.target.partFilePath) { + if (this._sessionDownload && this.download.target.partFilePath) { let partFile = new FileUtils.File(this.download.target.partFilePath); if (partFile.exists()) { return true; @@ -323,7 +306,7 @@ HistoryDownloadElementShell.prototype = { // We don't want in-progress downloads to be removed accidentally. return this.download.stopped; case "downloadsCmd_cancel": - return !!this._sessionDataItem; + return !!this._sessionDownload; } return false; }, @@ -351,13 +334,13 @@ HistoryDownloadElementShell.prototype = { break; } case "cmd_delete": { - if (this._sessionDataItem) { + if (this._sessionDownload) { Downloads.getList(Downloads.ALL) .then(list => list.remove(this.download)) .then(() => this.download.finalize(true)) .catch(Cu.reportError); } - if (this._historyDataItem) { + if (this._historyDownload) { let uri = NetUtil.newURI(this.download.source.url); PlacesUtils.bhistory.removePage(uri); } @@ -480,7 +463,7 @@ function DownloadsPlacesView(aRichListBox, aActive = true) { this._downloadElementsShellsForURI = new Map(); // Map download data items to their element shells. - this._viewItemsForDataItems = new WeakMap(); + this._viewItemsForDownloads = new WeakMap(); // Points to the last session download element. We keep track of this // in order to keep all session downloads above past downloads. @@ -626,14 +609,12 @@ DownloadsPlacesView.prototype = { * alongside the other session downloads. If we don't, then we go ahead * and create a new element for the download. * - * @param aDataItem - * The data item of a session download. Set to null for history - * downloads data. + * @param [optional] sessionDownload + * A Download object, or null for history downloads. * @param [optional] aPlacesNode - * The places node for a history download. Required if there's no data - * item. + * The Places node for a history download, or null for session downloads. * @param [optional] aNewest - * @see onDataItemAdded. Ignored for history downloads. + * @see onDownloadAdded. Ignored for history downloads. * @param [optional] aDocumentFragment * To speed up the appending of multiple elements to the end of the * list which are coming in a single batch (i.e. invalidateContainer), @@ -641,10 +622,8 @@ DownloadsPlacesView.prototype = { * be appended. It's the caller's job to ensure the fragment is merged * to the richlistbox at the end. */ - _addDownloadData: - function DPV_addDownloadData(aDataItem, aPlacesNode, aNewest = false, + _addDownloadData(sessionDownload, aPlacesNode, aNewest = false, aDocumentFragment = null) { - let sessionDownload = aDataItem && aDataItem.download; let downloadURI = aPlacesNode ? aPlacesNode.uri : sessionDownload.source.url; let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI); @@ -670,21 +649,21 @@ DownloadsPlacesView.prototype = { // item). // // Note: If a cancelled session download is already in the list, and the - // download is retired, onDataItemAdded is called again for the same + // download is retried, onDownloadAdded is called again for the same // data item. Thus, we also check that we make sure we don't have a view item // already. if (!shouldCreateShell && - aDataItem && !this._viewItemsForDataItems.has(aDataItem)) { + sessionDownload && !this._viewItemsForDownloads.has(sessionDownload)) { // If there's a past-download-only shell for this download-uri with no // associated data item, use it for the new data item. Otherwise, go ahead // and create another shell. shouldCreateShell = true; for (let shell of shellsForURI) { - if (!shell.sessionDataItem) { + if (!shell.sessionDownload) { shouldCreateShell = false; - shell.sessionDataItem = aDataItem; + shell.sessionDownload = sessionDownload; newOrUpdatedShell = shell; - this._viewItemsForDataItems.set(aDataItem, shell); + this._viewItemsForDownloads.set(sessionDownload, shell); break; } } @@ -694,18 +673,20 @@ DownloadsPlacesView.prototype = { // If we are adding a new history download here, it means there is no // associated session download, thus we must read the Places metadata, // because it will not be obscured by the session download. - let historyDataItem = null; + let historyDownload = null; if (aPlacesNode) { let metaData = this._getCachedPlacesMetaDataFor(aPlacesNode.uri); - historyDataItem = new DownloadsHistoryDataItem(aPlacesNode); - historyDataItem.download.updateFromMetaData(metaData); + historyDownload = new HistoryDownload(aPlacesNode); + historyDownload.updateFromMetaData(metaData); } - let shell = new HistoryDownloadElementShell(aDataItem, historyDataItem); + let shell = new HistoryDownloadElementShell(sessionDownload, + historyDownload); shell.element._placesNode = aPlacesNode; newOrUpdatedShell = shell; shellsForURI.add(shell); - if (aDataItem) - this._viewItemsForDataItems.set(aDataItem, shell); + if (sessionDownload) { + this._viewItemsForDownloads.set(sessionDownload, shell); + } } else if (aPlacesNode) { // We are updating information for a history download for which we have @@ -720,9 +701,9 @@ DownloadsPlacesView.prototype = { // changed, just the reference to the Places node object is different. // So, we update all the node references and keep the metadata intact. for (let shell of shellsForURI) { - if (!shell.historyDataItem) { + if (!shell.historyDownload) { // Create the element to host the metadata when needed. - shell.historyDataItem = new DownloadsHistoryDataItem(aPlacesNode); + shell.historyDownload = new HistoryDownload(aPlacesNode); } shell.element._placesNode = aPlacesNode; } @@ -740,7 +721,7 @@ DownloadsPlacesView.prototype = { // More generally, if a new download is added, should be made visible. this._richlistbox.ensureElementIsVisible(newOrUpdatedShell.element); } - else if (aDataItem) { + } else if (sessionDownload) { let before = this._lastSessionDownloadElement ? this._lastSessionDownloadElement.nextSibling : this._richlistbox.firstChild; this._richlistbox.insertBefore(newOrUpdatedShell.element, before); @@ -791,8 +772,8 @@ DownloadsPlacesView.prototype = { let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI); if (shellsForURI) { for (let shell of shellsForURI) { - if (shell.sessionDataItem) { - shell.historyDataItem = null; + if (shell.sessionDownload) { + shell.historyDownload = null; } else { this._removeElement(shell.element); @@ -804,15 +785,13 @@ DownloadsPlacesView.prototype = { } }, - _removeSessionDownloadFromView: - function DPV__removeSessionDownloadFromView(aDataItem) { - let download = aDataItem.download; + _removeSessionDownloadFromView(download) { let shells = this._downloadElementsShellsForURI .get(download.source.url); if (shells.size == 0) throw new Error("Should have had at leaat one shell for this uri"); - let shell = this._viewItemsForDataItems.get(aDataItem); + let shell = this._viewItemsForDownloads.get(download); if (!shells.has(shell)) throw new Error("Missing download element shell in shells list for url"); @@ -820,7 +799,7 @@ DownloadsPlacesView.prototype = { // view item for this this particular data item go away. // If there's only one item for this download uri, we should only // keep it if it is associated with a history download. - if (shells.size > 1 || !shell.historyDataItem) { + if (shells.size > 1 || !shell.historyDownload) { this._removeElement(shell.element); shells.delete(shell); if (shells.size == 0) @@ -832,10 +811,10 @@ DownloadsPlacesView.prototype = { // Previously, we did not use the Places metadata because it was obscured // by the session download. Since this is no longer the case, we have to // read the latest metadata before removing the session download. - let url = shell.historyDataItem.download.source.url; + let url = shell.historyDownload.source.url; let metaData = this._getPlacesMetaDataFor(url); - shell.historyDataItem.download.updateFromMetaData(metaData); - shell.sessionDataItem = null; + shell.historyDownload.updateFromMetaData(metaData); + shell.sessionDownload = null; // Move it below the session-download items; if (this._lastSessionDownloadElement == shell.element) { this._lastSessionDownloadElement = shell.element.previousSibling; @@ -1108,22 +1087,20 @@ DownloadsPlacesView.prototype = { this._ensureInitialSelection(); }, - onDataItemAdded: function DPV_onDataItemAdded(aDataItem, aNewest) { - this._addDownloadData(aDataItem, null, aNewest); + onDownloadAdded(download, newest) { + this._addDownloadData(download, null, newest); }, - onDataItemRemoved: function DPV_onDataItemRemoved(aDataItem) { - this._removeSessionDownloadFromView(aDataItem); + onDownloadStateChanged(download) { + this._viewItemsForDownloads.get(download).onStateChanged(); }, - // DownloadsView - onDataItemStateChanged(aDataItem) { - this._viewItemsForDataItems.get(aDataItem).onStateChanged(); + onDownloadChanged(download) { + this._viewItemsForDownloads.get(download).onChanged(); }, - // DownloadsView - onDataItemChanged(aDataItem) { - this._viewItemsForDataItems.get(aDataItem).onChanged(); + onDownloadRemoved(download) { + this._removeSessionDownloadFromView(download); }, supportsCommand: function DPV_supportsCommand(aCommand) { diff --git a/application/palemoon/components/downloads/content/downloads.js b/application/palemoon/components/downloads/content/downloads.js index 05233e76d..9a6aa97e9 100644 --- a/application/palemoon/components/downloads/content/downloads.js +++ b/application/palemoon/components/downloads/content/downloads.js @@ -687,15 +687,15 @@ const DownloadsView = { loading: false, /** - * Ordered array of all DownloadsDataItem objects. We need to keep this array - * because only a limited number of items are shown at once, and if an item - * that is currently visible is removed from the list, we might need to take - * another item from the array and make it appear at the bottom. + * Ordered array of all Download objects. We need to keep this array because + * only a limited number of items are shown at once, and if an item that is + * currently visible is removed from the list, we might need to take another + * item from the array and make it appear at the bottom. */ - _dataItems: [], + _downloads: [], /** - * Associates the visible DownloadsDataItem objects with their corresponding + * Associates the visible Download objects with their corresponding * DownloadsViewItem object. There is a limited number of view items in the * panel at any given time. */ @@ -707,8 +707,8 @@ const DownloadsView = { _itemCountChanged: function DV_itemCountChanged() { DownloadsCommon.log("The downloads item count has changed - we are tracking", - this._dataItems.length, "downloads in total."); - let count = this._dataItems.length; + this._downloads.length, "downloads in total."); + let count = this._downloads.length; let hiddenCount = count - this.kItemCountLimit; if (count > 0) { @@ -797,8 +797,8 @@ const DownloadsView = { * Called when a new download data item is available, either during the * asynchronous data load or when a new download is started. * - * @param aDataItem - * DownloadsDataItem object that was just added. + * @param aDownload + * Download object that was just added. * @param aNewest * When true, indicates that this item is the most recent and should be * added in the topmost position. This happens when a new download is @@ -806,28 +806,28 @@ const DownloadsView = { * and should be appended. The latter generally happens during the * asynchronous data load. */ - onDataItemAdded: function DV_onDataItemAdded(aDataItem, aNewest) + onDownloadAdded(download, aNewest) { { DownloadsCommon.log("A new download data item was added - aNewest =", aNewest); if (aNewest) { - this._dataItems.unshift(aDataItem); + this._downloads.unshift(download); } else { - this._dataItems.push(aDataItem); + this._downloads.push(download); } - let itemsNowOverflow = this._dataItems.length > this.kItemCountLimit; + let itemsNowOverflow = this._downloads.length > this.kItemCountLimit; if (aNewest || !itemsNowOverflow) { // The newly added item is visible in the panel and we must add the // corresponding element. This is either because it is the first item, or // because it was added at the bottom but the list still doesn't overflow. - this._addViewItem(aDataItem, aNewest); + this._addViewItem(download, aNewest); } if (aNewest && itemsNowOverflow) { // If the list overflows, remove the last item from the panel to make room // for the new one that we just added at the top. - this._removeViewItem(this._dataItems[this.kItemCountLimit]); + this._removeViewItem(this._downloads[this.kItemCountLimit]); } // For better performance during batch loads, don't update the count for @@ -837,48 +837,45 @@ const DownloadsView = { } }, + onDownloadStateChanged(download) { + let viewItem = this._visibleViewItems.get(download); + if (viewItem) { + viewItem.onStateChanged(); + } + }, + + onDownloadChanged(download) { + let viewItem = this._visibleViewItems.get(download); + if (viewItem) { + viewItem.onChanged(); + } + }, + /** * Called when a data item is removed. Ensures that the widget associated * with the view item is removed from the user interface. * - * @param aDataItem - * DownloadsDataItem object that is being removed. + * @param download + * Download object that is being removed. */ - onDataItemRemoved: function DV_onDataItemRemoved(aDataItem) - { + onDownloadRemoved(download) { DownloadsCommon.log("A download data item was removed."); - let itemIndex = this._dataItems.indexOf(aDataItem); - this._dataItems.splice(itemIndex, 1); + let itemIndex = this._downloads.indexOf(download); + this._downloads.splice(itemIndex, 1); if (itemIndex < this.kItemCountLimit) { // The item to remove is visible in the panel. - this._removeViewItem(aDataItem); - if (this._dataItems.length >= this.kItemCountLimit) { + this._removeViewItem(download); + if (this._downloads.length >= this.kItemCountLimit) { // Reinsert the next item into the panel. - this._addViewItem(this._dataItems[this.kItemCountLimit - 1], false); + this._addViewItem(this._downloads[this.kItemCountLimit - 1], false); } } this._itemCountChanged(); }, - // DownloadsView - onDataItemStateChanged(aDataItem) { - let viewItem = this._visibleViewItems.get(aDataItem); - if (viewItem) { - viewItem.onStateChanged(); - } - }, - - // DownloadsView - onDataItemChanged(aDataItem) { - let viewItem = this._visibleViewItems.get(aDataItem); - if (viewItem) { - viewItem.onChanged(); - } - }, - /** * Associates each richlistitem for a download with its corresponding * DownloadsViewItemController object. @@ -893,15 +890,15 @@ const DownloadsView = { * Creates a new view item associated with the specified data item, and adds * it to the top or the bottom of the list. */ - _addViewItem: function DV_addViewItem(aDataItem, aNewest) + _addViewItem(download, aNewest) { DownloadsCommon.log("Adding a new DownloadsViewItem to the downloads list.", "aNewest =", aNewest); let element = document.createElement("richlistitem"); - let viewItem = new DownloadsViewItem(aDataItem, element); - this._visibleViewItems.set(aDataItem, viewItem); - let viewItemController = new DownloadsViewItemController(aDataItem); + let viewItem = new DownloadsViewItem(download, element); + this._visibleViewItems.set(download, viewItem); + let viewItemController = new DownloadsViewItemController(download); this._controllersForElements.set(element, viewItemController); if (aNewest) { this.richListBox.insertBefore(element, this.richListBox.firstChild); @@ -913,17 +910,17 @@ const DownloadsView = { /** * Removes the view item associated with the specified data item. */ - _removeViewItem: function DV_removeViewItem(aDataItem) + _removeViewItem(download) { { DownloadsCommon.log("Removing a DownloadsViewItem from the downloads list."); - let element = this._visibleViewItems.get(aDataItem).element; + let element = this._visibleViewItems.get(download).element; let previousSelectedIndex = this.richListBox.selectedIndex; this.richListBox.removeChild(element); if (previousSelectedIndex != -1) { this.richListBox.selectedIndex = Math.min(previousSelectedIndex, this.richListBox.itemCount - 1); } - this._visibleViewItems.delete(aDataItem); + this._visibleViewItems.delete(download); this._controllersForElements.delete(element); }, @@ -1053,14 +1050,13 @@ XPCOMUtils.defineConstant(this, "DownloadsView", DownloadsView); * Builds and updates a single item in the downloads list widget, responding to * changes in the download state and real-time data. * - * @param aDataItem - * DownloadsDataItem to be associated with the view item. + * @param download + * Download object to be associated with the view item. * @param aElement * XUL element corresponding to the single download item in the view. */ -function DownloadsViewItem(aDataItem, aElement) -{ - this.dataItem = aDataItem; +function DownloadsViewItem(download, aElement) { + this.download = download; this.element = aElement; this.element._shell = this; @@ -1075,11 +1071,6 @@ function DownloadsViewItem(aDataItem, aElement) DownloadsViewItem.prototype = { __proto__: DownloadElementShell.prototype, - /** - * The DownloadDataItem associated with this view item. - */ - dataItem: null, - /** * The XUL element corresponding to the associated richlistbox item. */ @@ -1233,21 +1224,11 @@ XPCOMUtils.defineConstant(this, "DownloadsViewController", DownloadsViewControll * Handles all the user interaction events, in particular the "commands", * related to a single item in the downloads list widgets. */ -function DownloadsViewItemController(aDataItem) { - this.dataItem = aDataItem; +function DownloadsViewItemController(download) { + this.download = download; } DownloadsViewItemController.prototype = { - ////////////////////////////////////////////////////////////////////////////// - //// Command dispatching - - /** - * The DownloadDataItem controlled by this object. - */ - dataItem: null, - - get download() this.dataItem.download, - isCommandEnabled: function DVIC_isCommandEnabled(aCommand) { switch (aCommand) { diff --git a/application/palemoon/components/downloads/content/downloadsViewCommon.js b/application/palemoon/components/downloads/content/downloadsViewCommon.js index 7d18eadc6..9ef492764 100644 --- a/application/palemoon/components/downloads/content/downloadsViewCommon.js +++ b/application/palemoon/components/downloads/content/downloadsViewCommon.js @@ -45,7 +45,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task", * from the JavaScript API for downloads, and commands are executed using a * combination of Download methods and DownloadsCommon.jsm helper functions. * - * Specialized versions of this shell must be defined, currently they are the + * Specialized versions of this shell must be defined, and they are required to + * implement the "download" property or getter. Currently these objects are the * HistoryDownloadElementShell and the DownloadsViewItem for the panel. The * history view may use a HistoryDownload object in place of a Download object. */ @@ -57,17 +58,6 @@ DownloadElementShell.prototype = { */ element: null, - /** - * The DownloadsDataItem for the download, overridden by the derived object. - */ - dataItem: null, - - /** - * Download or HistoryDownload object to use for displaying information and - * for executing commands in the user interface. - */ - get download() this.dataItem.download, - /** * URI string for the file type icon displayed in the download element. */ -- cgit v1.2.3