зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1117141
- Part 2 of 2 - Refactor notifications and remove the DownloadsDataItem object. r=mak
This commit is contained in:
Родитель
797528c9fe
Коммит
69d94e3fbd
|
@ -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
|
||||
|
@ -350,10 +344,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:
|
||||
|
@ -370,7 +364,7 @@ this.DownloadsCommon = {
|
|||
* complete.
|
||||
* percentComplete : The percentage of bytes successfully downloaded.
|
||||
*/
|
||||
summarizeDownloads(aDataItems) {
|
||||
summarizeDownloads(downloads) {
|
||||
let summary = {
|
||||
numActive: 0,
|
||||
numPaused: 0,
|
||||
|
@ -381,14 +375,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);
|
||||
|
||||
|
@ -659,16 +652,12 @@ XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", 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 = {
|
||||
|
@ -685,12 +674,17 @@ DownloadsDataCtor.prototype = {
|
|||
},
|
||||
_dataLinkInitialized: false,
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
@ -712,35 +706,32 @@ DownloadsDataCtor.prototype = {
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Integration with the asynchronous Downloads back-end
|
||||
|
||||
onDownloadAdded(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(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
|
||||
|
@ -749,17 +740,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);
|
||||
|
@ -771,40 +762,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(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);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -849,9 +833,9 @@ DownloadsDataCtor.prototype = {
|
|||
|
||||
// Sort backwards by start time, ensuring that the most recent
|
||||
// downloads are added first regardless of their state.
|
||||
let loadedItemsArray = [...this.dataItems];
|
||||
loadedItemsArray.sort((a, b) => b.download.startTime - a.download.startTime);
|
||||
loadedItemsArray.forEach(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.
|
||||
aView.onDataLoadCompleted();
|
||||
|
@ -913,106 +897,6 @@ XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() {
|
|||
return new DownloadsDataCtor(false);
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsDataItem
|
||||
|
||||
/**
|
||||
* Represents a single item in the list of downloads.
|
||||
*
|
||||
* The endTime property is initialized to the current date and time.
|
||||
*
|
||||
* @param aDownload
|
||||
* The Download object with the current state.
|
||||
*/
|
||||
function DownloadsDataItem(aDownload) {
|
||||
this.download = aDownload;
|
||||
this.download.endTime = Date.now();
|
||||
}
|
||||
|
||||
DownloadsDataItem.prototype = {
|
||||
get state() DownloadsCommon.stateOfDownload(this.download),
|
||||
|
||||
/**
|
||||
* 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);
|
||||
},
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsViewPrototype
|
||||
|
||||
|
@ -1123,9 +1007,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
|
||||
|
@ -1134,7 +1018,33 @@ const DownloadsViewPrototype = {
|
|||
*
|
||||
* @note Subclasses should override this.
|
||||
*/
|
||||
onDataItemAdded(aDataItem, aNewest) {
|
||||
onDownloadAdded(download, newest) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* The onDownloadChanged notification will always be sent afterwards.
|
||||
*
|
||||
* @note Subclasses should override this.
|
||||
*/
|
||||
onDownloadStateChanged(download) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called every time any state property of a Download may have changed,
|
||||
* including progress properties.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
onDownloadChanged(download) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
|
@ -1142,36 +1052,12 @@ const DownloadsViewPrototype = {
|
|||
* 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.
|
||||
*
|
||||
* @note Subclasses should override this.
|
||||
*/
|
||||
onDataItemRemoved(aDataItem) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the "state" property of a DownloadsDataItem has changed.
|
||||
*
|
||||
* The onDataItemChanged notification will be sent afterwards.
|
||||
*
|
||||
* @note Subclasses should override this.
|
||||
*/
|
||||
onDataItemStateChanged(aDataItem) {
|
||||
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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
onDataItemChanged(aDataItem) {
|
||||
onDownloadRemoved(download) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
|
@ -1232,48 +1118,17 @@ DownloadsIndicatorDataCtor.prototype = {
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Callback functions from DownloadsData
|
||||
|
||||
/**
|
||||
* Called after data loading finished.
|
||||
*/
|
||||
onDataLoadCompleted() {
|
||||
DownloadsViewPrototype.onDataLoadCompleted.call(this);
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
/**
|
||||
* 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(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(aDataItem) {
|
||||
this._itemCount--;
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
// DownloadsView
|
||||
onDataItemStateChanged(aDataItem) {
|
||||
let download = aDataItem.download;
|
||||
|
||||
onDownloadStateChanged(download) {
|
||||
if (download.succeeded || download.error) {
|
||||
this.attention = true;
|
||||
}
|
||||
|
@ -1283,8 +1138,12 @@ DownloadsIndicatorDataCtor.prototype = {
|
|||
this._lastTimeLeft = -1;
|
||||
},
|
||||
|
||||
// DownloadsView
|
||||
onDataItemChanged() {
|
||||
onDownloadChanged(download) {
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
onDownloadRemoved(download) {
|
||||
this._itemCount--;
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
|
@ -1372,21 +1231,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() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1396,7 +1251,7 @@ DownloadsIndicatorDataCtor.prototype = {
|
|||
*/
|
||||
_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);
|
||||
|
@ -1457,7 +1312,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
|
||||
|
@ -1496,9 +1351,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 = [];
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1512,31 +1367,29 @@ DownloadsSummaryData.prototype = {
|
|||
this._updateViews();
|
||||
},
|
||||
|
||||
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(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();
|
||||
},
|
||||
|
||||
|
@ -1573,16 +1426,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() {
|
||||
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];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1593,7 +1446,7 @@ DownloadsSummaryData.prototype = {
|
|||
_refreshProperties() {
|
||||
// Pre-load summary with default values.
|
||||
let summary =
|
||||
DownloadsCommon.summarizeDownloads(this._dataItemsForSummary());
|
||||
DownloadsCommon.summarizeDownloads(this._downloadsForSummary());
|
||||
|
||||
this._description = DownloadsCommon.strings
|
||||
.otherDownloads2(summary.numActive);
|
||||
|
|
|
@ -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,
|
||||
get download() this._sessionDownload || this._historyDownload,
|
||||
|
||||
_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");
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +271,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;
|
||||
}
|
||||
|
||||
|
@ -301,7 +284,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;
|
||||
|
@ -325,7 +308,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;
|
||||
},
|
||||
|
@ -353,13 +336,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);
|
||||
}
|
||||
|
@ -485,7 +468,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.
|
||||
|
@ -631,14 +614,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),
|
||||
|
@ -646,9 +627,8 @@ DownloadsPlacesView.prototype = {
|
|||
* be appended. It's the caller's job to ensure the fragment is merged
|
||||
* to the richlistbox at the end.
|
||||
*/
|
||||
_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);
|
||||
|
@ -674,21 +654,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;
|
||||
}
|
||||
}
|
||||
|
@ -698,18 +678,19 @@ 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
|
||||
|
@ -724,9 +705,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;
|
||||
}
|
||||
|
@ -743,7 +724,7 @@ DownloadsPlacesView.prototype = {
|
|||
// the top of the richlistbox, along with other session downloads.
|
||||
// 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);
|
||||
|
@ -793,8 +774,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);
|
||||
shellsForURI.delete(shell);
|
||||
|
@ -805,15 +786,14 @@ DownloadsPlacesView.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_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");
|
||||
}
|
||||
|
@ -822,7 +802,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) {
|
||||
|
@ -834,10 +814,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;
|
||||
|
@ -1113,22 +1093,20 @@ DownloadsPlacesView.prototype = {
|
|||
this._ensureInitialSelection();
|
||||
},
|
||||
|
||||
onDataItemAdded(aDataItem, aNewest) {
|
||||
this._addDownloadData(aDataItem, null, aNewest);
|
||||
onDownloadAdded(download, newest) {
|
||||
this._addDownloadData(download, null, newest);
|
||||
},
|
||||
|
||||
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(aCommand) {
|
||||
|
|
|
@ -649,15 +649,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.
|
||||
*/
|
||||
|
@ -668,8 +668,8 @@ const DownloadsView = {
|
|||
*/
|
||||
_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) {
|
||||
|
@ -734,8 +734,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
|
||||
|
@ -743,27 +743,27 @@ const DownloadsView = {
|
|||
* and should be appended. The latter generally happens during the
|
||||
* asynchronous data load.
|
||||
*/
|
||||
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
|
||||
|
@ -773,47 +773,45 @@ const DownloadsView = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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(aDataItem) {
|
||||
DownloadsCommon.log("A download data item was removed.");
|
||||
|
||||
let itemIndex = this._dataItems.indexOf(aDataItem);
|
||||
this._dataItems.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) {
|
||||
// Reinsert the next item into the panel.
|
||||
this._addViewItem(this._dataItems[this.kItemCountLimit - 1], false);
|
||||
}
|
||||
}
|
||||
|
||||
this._itemCountChanged();
|
||||
},
|
||||
|
||||
// DownloadsView
|
||||
onDataItemStateChanged(aDataItem) {
|
||||
let viewItem = this._visibleViewItems.get(aDataItem);
|
||||
onDownloadStateChanged(download) {
|
||||
let viewItem = this._visibleViewItems.get(download);
|
||||
if (viewItem) {
|
||||
viewItem.onStateChanged();
|
||||
}
|
||||
},
|
||||
|
||||
// DownloadsView
|
||||
onDataItemChanged(aDataItem) {
|
||||
let viewItem = this._visibleViewItems.get(aDataItem);
|
||||
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 download
|
||||
* Download object that is being removed.
|
||||
*/
|
||||
onDownloadRemoved(download) {
|
||||
DownloadsCommon.log("A download data item was removed.");
|
||||
|
||||
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(download);
|
||||
if (this._downloads.length >= this.kItemCountLimit) {
|
||||
// Reinsert the next item into the panel.
|
||||
this._addViewItem(this._downloads[this.kItemCountLimit - 1], false);
|
||||
}
|
||||
}
|
||||
|
||||
this._itemCountChanged();
|
||||
},
|
||||
|
||||
/**
|
||||
* Associates each richlistitem for a download with its corresponding
|
||||
* DownloadsViewItemController object.
|
||||
|
@ -828,15 +826,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(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);
|
||||
|
@ -848,16 +846,16 @@ const DownloadsView = {
|
|||
/**
|
||||
* Removes the view item associated with the specified data item.
|
||||
*/
|
||||
_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);
|
||||
},
|
||||
|
||||
|
@ -979,13 +977,13 @@ const 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;
|
||||
|
||||
|
@ -999,11 +997,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.
|
||||
*/
|
||||
|
@ -1148,21 +1141,11 @@ const DownloadsViewController = {
|
|||
* 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(aCommand) {
|
||||
switch (aCommand) {
|
||||
case "downloadsCmd_open": {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
Загрузка…
Ссылка в новой задаче