mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-28 10:07:30 +09:00
[PALEMOON] Bug 1117141 - Part 1 of 2 - Bypass all the DownloadsDataItem properties
This commit is contained in:
parent
5dabb446c5
commit
5705b9b36a
4 changed files with 321 additions and 306 deletions
|
|
@ -378,6 +378,26 @@ this.DownloadsCommon = {
|
||||||
return nsIDM.DOWNLOAD_NOTSTARTED;
|
return nsIDM.DOWNLOAD_NOTSTARTED;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the highest number of bytes transferred or the known size of the
|
||||||
|
* given Download object, or -1 if the size is not available. Callers should
|
||||||
|
* use Download properties directly when possible.
|
||||||
|
*/
|
||||||
|
maxBytesOfDownload(download) {
|
||||||
|
if (download.succeeded) {
|
||||||
|
// If the download succeeded, show the final size if available, otherwise
|
||||||
|
// use the last known number of bytes transferred. The final size on disk
|
||||||
|
// will be available when bug 941063 is resolved.
|
||||||
|
return download.hasProgress ? download.totalBytes : download.currentBytes;
|
||||||
|
} else if (download.hasProgress) {
|
||||||
|
// If the final size and progress are known, use them.
|
||||||
|
return download.totalBytes;
|
||||||
|
} else {
|
||||||
|
// The download final size and progress percentage is unknown.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an iterable collection of DownloadDataItems, generates and returns
|
* Given an iterable collection of DownloadDataItems, generates and returns
|
||||||
* statistics about that collection.
|
* statistics about that collection.
|
||||||
|
|
@ -418,8 +438,12 @@ this.DownloadsCommon = {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let dataItem of aDataItems) {
|
for (let dataItem of aDataItems) {
|
||||||
|
let download = dataItem.download;
|
||||||
|
let state = DownloadsCommon.stateOfDownload(download);
|
||||||
|
let maxBytes = DownloadsCommon.maxBytesOfDownload(download);
|
||||||
|
|
||||||
summary.numActive++;
|
summary.numActive++;
|
||||||
switch (dataItem.state) {
|
switch (state) {
|
||||||
case nsIDM.DOWNLOAD_PAUSED:
|
case nsIDM.DOWNLOAD_PAUSED:
|
||||||
summary.numPaused++;
|
summary.numPaused++;
|
||||||
break;
|
break;
|
||||||
|
|
@ -428,21 +452,20 @@ this.DownloadsCommon = {
|
||||||
break;
|
break;
|
||||||
case nsIDM.DOWNLOAD_DOWNLOADING:
|
case nsIDM.DOWNLOAD_DOWNLOADING:
|
||||||
summary.numDownloading++;
|
summary.numDownloading++;
|
||||||
if (dataItem.maxBytes > 0 && dataItem.download.speed > 0) {
|
if (maxBytes > 0 && download.speed > 0) {
|
||||||
let sizeLeft = dataItem.maxBytes - dataItem.download.currentBytes;
|
let sizeLeft = maxBytes - download.currentBytes;
|
||||||
summary.rawTimeLeft = Math.max(summary.rawTimeLeft,
|
summary.rawTimeLeft = Math.max(summary.rawTimeLeft,
|
||||||
sizeLeft / dataItem.download.speed);
|
sizeLeft / download.speed);
|
||||||
summary.slowestSpeed = Math.min(summary.slowestSpeed,
|
summary.slowestSpeed = Math.min(summary.slowestSpeed,
|
||||||
dataItem.download.speed);
|
download.speed);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Only add to total values if we actually know the download size.
|
// Only add to total values if we actually know the download size.
|
||||||
if (dataItem.maxBytes > 0 &&
|
if (maxBytes > 0 && state != nsIDM.DOWNLOAD_CANCELED &&
|
||||||
dataItem.state != nsIDM.DOWNLOAD_CANCELED &&
|
state != nsIDM.DOWNLOAD_FAILED) {
|
||||||
dataItem.state != nsIDM.DOWNLOAD_FAILED) {
|
summary.totalSize += maxBytes;
|
||||||
summary.totalSize += dataItem.maxBytes;
|
summary.totalTransferred += download.currentBytes;
|
||||||
summary.totalTransferred += dataItem.download.currentBytes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -498,7 +521,7 @@ this.DownloadsCommon = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a downloaded file.
|
* Opens a downloaded file.
|
||||||
* If you've a dataItem, you should call dataItem.openLocalFile.
|
*
|
||||||
* @param aFile
|
* @param aFile
|
||||||
* the downloaded file to be opened.
|
* the downloaded file to be opened.
|
||||||
* @param aMimeInfo
|
* @param aMimeInfo
|
||||||
|
|
@ -649,6 +672,7 @@ function DownloadsDataCtor(aPrivate) {
|
||||||
|
|
||||||
// Contains all the available DownloadsDataItem objects.
|
// Contains all the available DownloadsDataItem objects.
|
||||||
this.dataItems = new Set();
|
this.dataItems = new Set();
|
||||||
|
this.oldDownloadStates = new Map();
|
||||||
|
|
||||||
// Array of view objects that should be notified when the available download
|
// Array of view objects that should be notified when the available download
|
||||||
// data changes.
|
// data changes.
|
||||||
|
|
@ -687,7 +711,9 @@ DownloadsDataCtor.prototype = {
|
||||||
get canRemoveFinished()
|
get canRemoveFinished()
|
||||||
{
|
{
|
||||||
for (let dataItem of this.dataItems) {
|
for (let dataItem of this.dataItems) {
|
||||||
if (!dataItem.inProgress) {
|
let download = dataItem.download;
|
||||||
|
// Stopped, paused, and failed downloads with partial data are removed.
|
||||||
|
if (download.stopped && !(download.canceled && download.hasPartialData)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -713,23 +739,82 @@ DownloadsDataCtor.prototype = {
|
||||||
let dataItem = new DownloadsDataItem(aDownload);
|
let dataItem = new DownloadsDataItem(aDownload);
|
||||||
this._downloadToDataItemMap.set(aDownload, dataItem);
|
this._downloadToDataItemMap.set(aDownload, dataItem);
|
||||||
this.dataItems.add(dataItem);
|
this.dataItems.add(dataItem);
|
||||||
|
this.oldDownloadStates.set(aDownload,
|
||||||
|
DownloadsCommon.stateOfDownload(aDownload));
|
||||||
|
|
||||||
for (let view of this._views) {
|
for (let view of this._views) {
|
||||||
view.onDataItemAdded(dataItem, true);
|
view.onDataItemAdded(dataItem, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateDataItemState(dataItem);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onDownloadChanged: function (aDownload)
|
onDownloadChanged: function (aDownload)
|
||||||
{
|
{
|
||||||
let dataItem = this._downloadToDataItemMap.get(aDownload);
|
let aDataItem = this._downloadToDataItemMap.get(aDownload);
|
||||||
if (!dataItem) {
|
if (!aDataItem) {
|
||||||
Cu.reportError("Download doesn't exist.");
|
Cu.reportError("Download doesn't exist.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateDataItemState(dataItem);
|
let oldState = this.oldDownloadStates.get(aDownload);
|
||||||
|
let newState = DownloadsCommon.stateOfDownload(aDownload);
|
||||||
|
this.oldDownloadStates.set(aDownload, newState);
|
||||||
|
|
||||||
|
if (oldState != newState) {
|
||||||
|
if (aDownload.succeeded ||
|
||||||
|
(aDownload.canceled && !aDownload.hasPartialData) ||
|
||||||
|
aDownload.error) {
|
||||||
|
// Store the end time that may be displayed by the views.
|
||||||
|
aDownload.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
|
||||||
|
// annotations should be ideally hidden behind methods of
|
||||||
|
// nsIDownloadHistory (bug 830415).
|
||||||
|
if (!this._isPrivate) {
|
||||||
|
try {
|
||||||
|
let downloadMetaData = {
|
||||||
|
state: DownloadsCommon.stateOfDownload(aDownload),
|
||||||
|
endTime: aDownload.endTime,
|
||||||
|
};
|
||||||
|
if (aDownload.succeeded ||
|
||||||
|
(aDownload.error && aDownload.error.becauseBlocked)) {
|
||||||
|
downloadMetaData.fileSize =
|
||||||
|
DownloadsCommon.maxBytesOfDownload(aDataItem.download);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlacesUtils.annotations.setPageAnnotation(
|
||||||
|
NetUtil.newURI(aDownload.source.url),
|
||||||
|
"downloads/metaData",
|
||||||
|
JSON.stringify(downloadMetaData), 0,
|
||||||
|
PlacesUtils.annotations.EXPIRE_WITH_HISTORY);
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let view of this._views) {
|
||||||
|
try {
|
||||||
|
view.onDataItemStateChanged(aDataItem);
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aDownload.succeeded ||
|
||||||
|
(aDownload.error && aDownload.error.becauseBlocked)) {
|
||||||
|
this._notifyDownloadEvent("finish");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aDownload.newDownloadNotified) {
|
||||||
|
aDownload.newDownloadNotified = true;
|
||||||
|
this._notifyDownloadEvent("start");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let view of this._views) {
|
||||||
|
view.onDataItemChanged(aDataItem);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onDownloadRemoved: function (aDownload)
|
onDownloadRemoved: function (aDownload)
|
||||||
|
|
@ -742,73 +827,12 @@ DownloadsDataCtor.prototype = {
|
||||||
|
|
||||||
this._downloadToDataItemMap.delete(aDownload);
|
this._downloadToDataItemMap.delete(aDownload);
|
||||||
this.dataItems.delete(dataItem);
|
this.dataItems.delete(dataItem);
|
||||||
|
this.oldDownloadStates.delete(aDownload);
|
||||||
for (let view of this._views) {
|
for (let view of this._views) {
|
||||||
view.onDataItemRemoved(dataItem);
|
view.onDataItemRemoved(dataItem);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the given data item and sends related notifications.
|
|
||||||
*/
|
|
||||||
_updateDataItemState: function (aDataItem)
|
|
||||||
{
|
|
||||||
let oldState = aDataItem.state;
|
|
||||||
let wasInProgress = aDataItem.inProgress;
|
|
||||||
let wasDone = aDataItem.done;
|
|
||||||
|
|
||||||
aDataItem.updateFromJSDownload();
|
|
||||||
|
|
||||||
if (wasInProgress && !aDataItem.inProgress) {
|
|
||||||
aDataItem.endTime = Date.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldState != aDataItem.state) {
|
|
||||||
for (let view of this._views) {
|
|
||||||
try {
|
|
||||||
view.onDataItemStateChanged(aDataItem, oldState);
|
|
||||||
} catch (ex) {
|
|
||||||
Cu.reportError(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This state transition code should actually be located in a Downloads
|
|
||||||
// API module (bug 941009). Moreover, the fact that state is stored as
|
|
||||||
// annotations should be ideally hidden behind methods of
|
|
||||||
// nsIDownloadHistory (bug 830415).
|
|
||||||
if (!this._isPrivate && !aDataItem.inProgress) {
|
|
||||||
try {
|
|
||||||
let downloadMetaData = { state: aDataItem.state,
|
|
||||||
endTime: aDataItem.endTime };
|
|
||||||
if (aDataItem.done) {
|
|
||||||
downloadMetaData.fileSize = aDataItem.maxBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// RRR: Annotation service throws here. commented out for now.
|
|
||||||
/*PlacesUtils.annotations.setPageAnnotation(
|
|
||||||
NetUtil.newURI(aDataItem.download.source.url),
|
|
||||||
"downloads/metaData",
|
|
||||||
JSON.stringify(downloadMetaData), 0,
|
|
||||||
PlacesUtils.annotations.EXPIRE_WITH_HISTORY);*/
|
|
||||||
} catch (ex) {
|
|
||||||
Cu.reportError(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!aDataItem.newDownloadNotified) {
|
|
||||||
aDataItem.newDownloadNotified = true;
|
|
||||||
this._notifyDownloadEvent("start");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wasDone && aDataItem.done) {
|
|
||||||
this._notifyDownloadEvent("finish");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let view of this._views) {
|
|
||||||
view.onDataItemChanged(aDataItem);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//// Registration of views
|
//// Registration of views
|
||||||
|
|
||||||
|
|
@ -1333,6 +1357,8 @@ function DownloadsDataItem(aSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadsDataItem.prototype = {
|
DownloadsDataItem.prototype = {
|
||||||
|
get state() DownloadsCommon.stateOfDownload(this.download),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes this object from the JavaScript API for downloads.
|
* Initializes this object from the JavaScript API for downloads.
|
||||||
*
|
*
|
||||||
|
|
@ -1344,51 +1370,7 @@ DownloadsDataItem.prototype = {
|
||||||
_initFromJSDownload: function (aDownload)
|
_initFromJSDownload: function (aDownload)
|
||||||
{
|
{
|
||||||
this.download = aDownload;
|
this.download = aDownload;
|
||||||
this.endTime = Date.now();
|
this.download.endTime = Date.now();
|
||||||
|
|
||||||
this.updateFromJSDownload();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates this object from the JavaScript API for downloads.
|
|
||||||
*/
|
|
||||||
updateFromJSDownload: function ()
|
|
||||||
{
|
|
||||||
// Collapse state using the correct priority.
|
|
||||||
if (this.download.succeeded) {
|
|
||||||
this.state = nsIDM.DOWNLOAD_FINISHED;
|
|
||||||
} else if (this.download.error &&
|
|
||||||
this.download.error.becauseBlockedByParentalControls) {
|
|
||||||
this.state = nsIDM.DOWNLOAD_BLOCKED_PARENTAL;
|
|
||||||
} else if (this.download.error) {
|
|
||||||
this.state = nsIDM.DOWNLOAD_FAILED;
|
|
||||||
} else if (this.download.canceled && this.download.hasPartialData) {
|
|
||||||
this.state = nsIDM.DOWNLOAD_PAUSED;
|
|
||||||
} else if (this.download.canceled) {
|
|
||||||
this.state = nsIDM.DOWNLOAD_CANCELED;
|
|
||||||
} else if (this.download.stopped) {
|
|
||||||
this.state = nsIDM.DOWNLOAD_NOTSTARTED;
|
|
||||||
} else {
|
|
||||||
this.state = nsIDM.DOWNLOAD_DOWNLOADING;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.download.succeeded) {
|
|
||||||
// If the download succeeded, show the final size if available, otherwise
|
|
||||||
// use the last known number of bytes transferred. The final size on disk
|
|
||||||
// will be available when bug 941063 is resolved.
|
|
||||||
this.maxBytes = this.download.hasProgress ?
|
|
||||||
this.download.totalBytes :
|
|
||||||
this.download.currentBytes;
|
|
||||||
this.percentComplete = 100;
|
|
||||||
} else if (this.download.hasProgress) {
|
|
||||||
// If the final size and progress are known, use them.
|
|
||||||
this.maxBytes = this.download.totalBytes;
|
|
||||||
this.percentComplete = this.download.progress;
|
|
||||||
} else {
|
|
||||||
// The download final size and progress percentage is unknown.
|
|
||||||
this.maxBytes = -1;
|
|
||||||
this.percentComplete = -1;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1896,9 +1878,10 @@ DownloadsIndicatorDataCtor.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// DownloadsView
|
// DownloadsView
|
||||||
onDataItemStateChanged(aDataItem, aOldState) {
|
onDataItemStateChanged(aDataItem) {
|
||||||
if (aDataItem.state == nsIDM.DOWNLOAD_FINISHED ||
|
let download = aDataItem.download;
|
||||||
aDataItem.state == nsIDM.DOWNLOAD_FAILED) {
|
|
||||||
|
if (download.succeeded || download.error) {
|
||||||
this.attention = true;
|
this.attention = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2010,7 +1993,11 @@ DownloadsIndicatorDataCtor.prototype = {
|
||||||
let dataItems = this._isPrivate ? PrivateDownloadsData.dataItems
|
let dataItems = this._isPrivate ? PrivateDownloadsData.dataItems
|
||||||
: DownloadsData.dataItems;
|
: DownloadsData.dataItems;
|
||||||
for (let dataItem of dataItems) {
|
for (let dataItem of dataItems) {
|
||||||
if (dataItem && dataItem.inProgress) {
|
if (!dataItem) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let download = dataItem.download;
|
||||||
|
if (!download.stopped || (download.canceled && download.hasPartialData)) {
|
||||||
yield dataItem;
|
yield dataItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2164,7 +2151,7 @@ DownloadsSummaryData.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// DownloadsView
|
// DownloadsView
|
||||||
onDataItemStateChanged(aOldState) {
|
onDataItemStateChanged() {
|
||||||
// Since the state of a download changed, reset the estimated time left.
|
// Since the state of a download changed, reset the estimated time left.
|
||||||
this._lastRawTimeLeft = -1;
|
this._lastRawTimeLeft = -1;
|
||||||
this._lastTimeLeft = -1;
|
this._lastTimeLeft = -1;
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,90 @@ const DOWNLOAD_VIEW_SUPPORTED_COMMANDS =
|
||||||
*
|
*
|
||||||
* @param url
|
* @param url
|
||||||
* URI string for the download source.
|
* URI string for the download source.
|
||||||
|
* @param endTime
|
||||||
|
* Timestamp with the end time for the download, used if there is no
|
||||||
|
* additional metadata available.
|
||||||
*/
|
*/
|
||||||
function HistoryDownload(url) {
|
function HistoryDownload(aPlacesNode) {
|
||||||
// TODO (bug 829201): history downloads should get the referrer from Places.
|
// TODO (bug 829201): history downloads should get the referrer from Places.
|
||||||
this.source = { url };
|
this.source = { url: aPlacesNode.uri };
|
||||||
this.target = { path: undefined, size: undefined };
|
this.target = { path: undefined, size: undefined };
|
||||||
|
|
||||||
|
// In case this download cannot obtain its end time from the Places metadata,
|
||||||
|
// use the time from the Places node, that is the start time of the download.
|
||||||
|
this.endTime = aPlacesNode.time / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoryDownload.prototype = {
|
HistoryDownload.prototype = {
|
||||||
|
/**
|
||||||
|
* Pushes information from Places metadata into this object.
|
||||||
|
*/
|
||||||
|
updateFromMetaData(aPlacesMetaData) {
|
||||||
|
try {
|
||||||
|
this.target.path = Cc["@mozilla.org/network/protocol;1?name=file"]
|
||||||
|
.getService(Ci.nsIFileProtocolHandler)
|
||||||
|
.getFileFromURLSpec(aPlacesMetaData.
|
||||||
|
targetFileURISpec).path;
|
||||||
|
} catch (ex) {
|
||||||
|
this.target.path = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let metaData = JSON.parse(aPlacesMetaData.jsonDetails);
|
||||||
|
this.succeeded = metaData.state == nsIDM.DOWNLOAD_FINISHED;
|
||||||
|
this.error = metaData.state == nsIDM.DOWNLOAD_FAILED
|
||||||
|
? { message: "History download failed." }
|
||||||
|
: metaData.state == nsIDM.DOWNLOAD_BLOCKED_PARENTAL
|
||||||
|
? { becauseBlockedByParentalControls: true }
|
||||||
|
: metaData.state == nsIDM.DOWNLOAD_DIRTY
|
||||||
|
? { becauseBlockedByReputationCheck: true }
|
||||||
|
: null;
|
||||||
|
this.canceled = metaData.state == nsIDM.DOWNLOAD_CANCELED ||
|
||||||
|
metaData.state == nsIDM.DOWNLOAD_PAUSED;
|
||||||
|
this.endTime = metaData.endTime;
|
||||||
|
this.target.size = metaData.fileSize;
|
||||||
|
} catch (ex) {
|
||||||
|
// Metadata might be missing from a download that has started but hasn't
|
||||||
|
// stopped already. Normally, this state is overridden with the one from
|
||||||
|
// the corresponding in-progress session download. But if the browser is
|
||||||
|
// terminated abruptly and additionally the file with information about
|
||||||
|
// in-progress downloads is lost, we may end up using this state. We use
|
||||||
|
// the failed state to allow the download to be restarted.
|
||||||
|
//
|
||||||
|
// On the other hand, if the download is missing the target file
|
||||||
|
// annotation as well, it is just a very old one, and we can assume it
|
||||||
|
// succeeded.
|
||||||
|
this.succeeded = !this.target.path;
|
||||||
|
this.error = this.target.path ? { message: "Unstarted download." } : null;
|
||||||
|
this.canceled = false;
|
||||||
|
this.target.size = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This property is currently used to get the size of downloads, but will be
|
||||||
|
// replaced by download.target.size when available for session downloads.
|
||||||
|
this.totalBytes = this.target.size;
|
||||||
|
this.currentBytes = this.target.size;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* History downloads are never in progress.
|
||||||
|
*/
|
||||||
|
stopped: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No percentage indication is shown for history downloads.
|
||||||
|
*/
|
||||||
|
hasProgress: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* History downloads cannot be restarted using their partial data, even if
|
||||||
|
* they are indicated as paused in their Places metadata. The only way is to
|
||||||
|
* use the information from a persisted session download, that will be shown
|
||||||
|
* instead of the history download. In case this session download is not
|
||||||
|
* available, we show the history download as canceled, not paused.
|
||||||
|
*/
|
||||||
|
hasPartialData: false,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method mimicks the "start" method of session downloads, and is called
|
* This method mimicks the "start" method of session downloads, and is called
|
||||||
* when the user retries a history download.
|
* when the user retries a history download.
|
||||||
|
|
@ -59,57 +135,11 @@ HistoryDownload.prototype = {
|
||||||
* The Places node for the history download.
|
* The Places node for the history download.
|
||||||
*/
|
*/
|
||||||
function DownloadsHistoryDataItem(aPlacesNode) {
|
function DownloadsHistoryDataItem(aPlacesNode) {
|
||||||
this.download = new HistoryDownload(aPlacesNode.uri);
|
this.download = new HistoryDownload(aPlacesNode);
|
||||||
|
|
||||||
// In case this download cannot obtain its end time from the Places metadata,
|
|
||||||
// use the time from the Places node, that is the start time of the download.
|
|
||||||
this.endTime = aPlacesNode.time / 1000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadsHistoryDataItem.prototype = {
|
DownloadsHistoryDataItem.prototype = {
|
||||||
__proto__: DownloadsDataItem.prototype,
|
__proto__: DownloadsDataItem.prototype,
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes information from Places metadata into this object.
|
|
||||||
*/
|
|
||||||
updateFromMetaData(aPlacesMetaData) {
|
|
||||||
try {
|
|
||||||
let targetFile = Cc["@mozilla.org/network/protocol;1?name=file"]
|
|
||||||
.getService(Ci.nsIFileProtocolHandler)
|
|
||||||
.getFileFromURLSpec(aPlacesMetaData.targetFileURISpec);
|
|
||||||
this.download.target.path = targetFile.path;
|
|
||||||
} catch (ex) {
|
|
||||||
this.download.target.path = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let metaData = JSON.parse(aPlacesMetaData.jsonDetails);
|
|
||||||
this.state = metaData.state;
|
|
||||||
this.endTime = metaData.endTime;
|
|
||||||
this.download.target.size = metaData.fileSize;
|
|
||||||
} catch (ex) {
|
|
||||||
// Metadata might be missing from a download that has started but hasn't
|
|
||||||
// stopped already. Normally, this state is overridden with the one from
|
|
||||||
// the corresponding in-progress session download. But if the browser is
|
|
||||||
// terminated abruptly and additionally the file with information about
|
|
||||||
// in-progress downloads is lost, we may end up using this state. We use
|
|
||||||
// the failed state to allow the download to be restarted.
|
|
||||||
//
|
|
||||||
// On the other hand, if the download is missing the target file
|
|
||||||
// annotation as well, it is just a very old one, and we can assume it
|
|
||||||
// succeeded.
|
|
||||||
this.state = this.download.target.path ? nsIDM.DOWNLOAD_FAILED
|
|
||||||
: nsIDM.DOWNLOAD_FINISHED;
|
|
||||||
this.download.target.size = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This property is currently used to get the size of downloads, but will be
|
|
||||||
// replaced by download.target.size when available for session downloads.
|
|
||||||
this.maxBytes = this.download.target.size;
|
|
||||||
|
|
||||||
// This is not displayed for history downloads, that are never in progress.
|
|
||||||
this.percentComplete = 100;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -223,7 +253,7 @@ HistoryDownloadElementShell.prototype = {
|
||||||
|
|
||||||
// The base object would show extended progress information in the tooltip,
|
// The base object would show extended progress information in the tooltip,
|
||||||
// but we move this to the main view and never display a tooltip.
|
// but we move this to the main view and never display a tooltip.
|
||||||
if (this.dataItem.state == nsIDM.DOWNLOAD_DOWNLOADING) {
|
if (!this.download.stopped) {
|
||||||
status.text = status.tip;
|
status.text = status.tip;
|
||||||
}
|
}
|
||||||
status.tip = "";
|
status.tip = "";
|
||||||
|
|
@ -232,18 +262,9 @@ HistoryDownloadElementShell.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
onStateChanged() {
|
onStateChanged() {
|
||||||
// If a download just finished successfully, it means that the target file
|
this.element.setAttribute("image", this.image);
|
||||||
// now exists and we can extract its specific icon. To ensure that the icon
|
this.element.setAttribute("state",
|
||||||
// is reloaded, we must change the URI used by the XUL image element, for
|
DownloadsCommon.stateOfDownload(this.download));
|
||||||
// example by adding a query parameter. Since this URI has a "moz-icon"
|
|
||||||
// scheme, this only works if we add one of the parameters explicitly
|
|
||||||
// supported by the nsIMozIconURI interface.
|
|
||||||
if (this.dataItem.state == nsIDM.DOWNLOAD_FINISHED) {
|
|
||||||
this.element.setAttribute("image", this.image + "&state=normal");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the user interface after switching states.
|
|
||||||
this.element.setAttribute("state", this.dataItem.state);
|
|
||||||
|
|
||||||
if (this.element.selected) {
|
if (this.element.selected) {
|
||||||
goUpdateDownloadCommands();
|
goUpdateDownloadCommands();
|
||||||
|
|
@ -275,12 +296,14 @@ HistoryDownloadElementShell.prototype = {
|
||||||
|
|
||||||
// If the target file information is not yet fetched,
|
// If the target file information is not yet fetched,
|
||||||
// temporarily assume that the file is in place.
|
// temporarily assume that the file is in place.
|
||||||
return this.dataItem.state == nsIDM.DOWNLOAD_FINISHED;
|
return this.download.succeeded;
|
||||||
case "downloadsCmd_show":
|
case "downloadsCmd_show":
|
||||||
// TODO: Bug 827010 - Handle part-file asynchronously.
|
// TODO: Bug 827010 - Handle part-file asynchronously.
|
||||||
if (this._sessionDataItem &&
|
if (this._sessionDataItem && this.download.target.partFilePath) {
|
||||||
this.dataItem.partFile && this.dataItem.partFile.exists()) {
|
let partFile = new FileUtils.File(this.download.target.partFilePath);
|
||||||
return true;
|
if (partFile.exists()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._targetFileChecked) {
|
if (this._targetFileChecked) {
|
||||||
|
|
@ -289,17 +312,16 @@ HistoryDownloadElementShell.prototype = {
|
||||||
|
|
||||||
// If the target file information is not yet fetched,
|
// If the target file information is not yet fetched,
|
||||||
// temporarily assume that the file is in place.
|
// temporarily assume that the file is in place.
|
||||||
return this.dataItem.state == nsIDM.DOWNLOAD_FINISHED;
|
return this.download.succeeded;
|
||||||
case "downloadsCmd_pauseResume":
|
case "downloadsCmd_pauseResume":
|
||||||
return this._sessionDataItem && this.dataItem.inProgress &&
|
return this.download.hasPartialData && !this.download.error;
|
||||||
this.dataItem.download.hasPartialData;
|
|
||||||
case "downloadsCmd_retry":
|
case "downloadsCmd_retry":
|
||||||
return this.dataItem.canRetry;
|
return this.download.canceled || this.download.error;
|
||||||
case "downloadsCmd_openReferrer":
|
case "downloadsCmd_openReferrer":
|
||||||
return !!this.download.source.referrer;
|
return !!this.download.source.referrer;
|
||||||
case "cmd_delete":
|
case "cmd_delete":
|
||||||
// The behavior in this case is somewhat unexpected, so we disallow that.
|
// We don't want in-progress downloads to be removed accidentally.
|
||||||
return !this.dataItem.inProgress;
|
return this.download.stopped;
|
||||||
case "downloadsCmd_cancel":
|
case "downloadsCmd_cancel":
|
||||||
return !!this._sessionDataItem;
|
return !!this._sessionDataItem;
|
||||||
}
|
}
|
||||||
|
|
@ -393,7 +415,8 @@ HistoryDownloadElementShell.prototype = {
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
let command = getDefaultCommandForState(this.dataItem.state);
|
let command = getDefaultCommandForState(
|
||||||
|
DownloadsCommon.stateOfDownload(this.download));
|
||||||
if (command && this.isCommandEnabled(command))
|
if (command && this.isCommandEnabled(command))
|
||||||
this.doCommand(command);
|
this.doCommand(command);
|
||||||
},
|
},
|
||||||
|
|
@ -621,8 +644,9 @@ DownloadsPlacesView.prototype = {
|
||||||
_addDownloadData:
|
_addDownloadData:
|
||||||
function DPV_addDownloadData(aDataItem, aPlacesNode, aNewest = false,
|
function DPV_addDownloadData(aDataItem, aPlacesNode, aNewest = false,
|
||||||
aDocumentFragment = null) {
|
aDocumentFragment = null) {
|
||||||
|
let sessionDownload = aDataItem && aDataItem.download;
|
||||||
let downloadURI = aPlacesNode ? aPlacesNode.uri
|
let downloadURI = aPlacesNode ? aPlacesNode.uri
|
||||||
: aDataItem.download.source.url;
|
: sessionDownload.source.url;
|
||||||
let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI);
|
let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI);
|
||||||
if (!shellsForURI) {
|
if (!shellsForURI) {
|
||||||
shellsForURI = new Set();
|
shellsForURI = new Set();
|
||||||
|
|
@ -674,7 +698,7 @@ DownloadsPlacesView.prototype = {
|
||||||
if (aPlacesNode) {
|
if (aPlacesNode) {
|
||||||
let metaData = this._getCachedPlacesMetaDataFor(aPlacesNode.uri);
|
let metaData = this._getCachedPlacesMetaDataFor(aPlacesNode.uri);
|
||||||
historyDataItem = new DownloadsHistoryDataItem(aPlacesNode);
|
historyDataItem = new DownloadsHistoryDataItem(aPlacesNode);
|
||||||
historyDataItem.updateFromMetaData(metaData);
|
historyDataItem.download.updateFromMetaData(metaData);
|
||||||
}
|
}
|
||||||
let shell = new HistoryDownloadElementShell(aDataItem, historyDataItem);
|
let shell = new HistoryDownloadElementShell(aDataItem, historyDataItem);
|
||||||
shell.element._placesNode = aPlacesNode;
|
shell.element._placesNode = aPlacesNode;
|
||||||
|
|
@ -782,8 +806,9 @@ DownloadsPlacesView.prototype = {
|
||||||
|
|
||||||
_removeSessionDownloadFromView:
|
_removeSessionDownloadFromView:
|
||||||
function DPV__removeSessionDownloadFromView(aDataItem) {
|
function DPV__removeSessionDownloadFromView(aDataItem) {
|
||||||
|
let download = aDataItem.download;
|
||||||
let shells = this._downloadElementsShellsForURI
|
let shells = this._downloadElementsShellsForURI
|
||||||
.get(aDataItem.download.source.url);
|
.get(download.source.url);
|
||||||
if (shells.size == 0)
|
if (shells.size == 0)
|
||||||
throw new Error("Should have had at leaat one shell for this uri");
|
throw new Error("Should have had at leaat one shell for this uri");
|
||||||
|
|
||||||
|
|
@ -799,7 +824,7 @@ DownloadsPlacesView.prototype = {
|
||||||
this._removeElement(shell.element);
|
this._removeElement(shell.element);
|
||||||
shells.delete(shell);
|
shells.delete(shell);
|
||||||
if (shells.size == 0)
|
if (shells.size == 0)
|
||||||
this._downloadElementsShellsForURI.delete(aDataItem.download.source.url);
|
this._downloadElementsShellsForURI.delete(download.source.url);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We have one download element shell containing both a session download
|
// We have one download element shell containing both a session download
|
||||||
|
|
@ -809,7 +834,7 @@ DownloadsPlacesView.prototype = {
|
||||||
// read the latest metadata before removing the session download.
|
// read the latest metadata before removing the session download.
|
||||||
let url = shell.historyDataItem.download.source.url;
|
let url = shell.historyDataItem.download.source.url;
|
||||||
let metaData = this._getPlacesMetaDataFor(url);
|
let metaData = this._getPlacesMetaDataFor(url);
|
||||||
shell.historyDataItem.updateFromMetaData(metaData);
|
shell.historyDataItem.download.updateFromMetaData(metaData);
|
||||||
shell.sessionDataItem = null;
|
shell.sessionDataItem = null;
|
||||||
// Move it below the session-download items;
|
// Move it below the session-download items;
|
||||||
if (this._lastSessionDownloadElement == shell.element) {
|
if (this._lastSessionDownloadElement == shell.element) {
|
||||||
|
|
@ -1142,7 +1167,9 @@ DownloadsPlacesView.prototype = {
|
||||||
// Because history downloads are always removable and are listed after the
|
// Because history downloads are always removable and are listed after the
|
||||||
// session downloads, check from bottom to top.
|
// session downloads, check from bottom to top.
|
||||||
for (let elt = this._richlistbox.lastChild; elt; elt = elt.previousSibling) {
|
for (let elt = this._richlistbox.lastChild; elt; elt = elt.previousSibling) {
|
||||||
if (!elt._shell.dataItem.inProgress) {
|
// Stopped, paused, and failed downloads with partial data are removed.
|
||||||
|
let download = elt._shell.download;
|
||||||
|
if (download.stopped && !(download.canceled && download.hasPartialData)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1244,12 +1271,13 @@ DownloadsPlacesView.prototype = {
|
||||||
|
|
||||||
// Set the state attribute so that only the appropriate items are displayed.
|
// Set the state attribute so that only the appropriate items are displayed.
|
||||||
let contextMenu = document.getElementById("downloadsContextMenu");
|
let contextMenu = document.getElementById("downloadsContextMenu");
|
||||||
let state = element._shell.dataItem.state;
|
let download = element._shell.download;
|
||||||
contextMenu.setAttribute("state", state);
|
contextMenu.setAttribute("state",
|
||||||
|
DownloadsCommon.stateOfDownload(download));
|
||||||
|
|
||||||
if (state == nsIDM.DOWNLOAD_DOWNLOADING) {
|
if (!download.stopped) {
|
||||||
// The resumable property of a download may change at any time, so
|
// The hasPartialData property of a download may change at any time after
|
||||||
// ensure we update the related command now.
|
// it has started, so ensure we update the related command now.
|
||||||
goUpdateCommand("downloadsCmd_pauseResume");
|
goUpdateCommand("downloadsCmd_pauseResume");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -1025,9 +1025,10 @@ const DownloadsView = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let localFile = DownloadsView.controllerForElement(element)
|
// We must check for existence synchronously because this is a DOM event.
|
||||||
.dataItem.localFile;
|
let file = new FileUtils.File(DownloadsView.controllerForElement(element)
|
||||||
if (!localFile.exists()) {
|
.download.target.path);
|
||||||
|
if (!file.exists()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1085,24 +1086,17 @@ DownloadsViewItem.prototype = {
|
||||||
_element: null,
|
_element: null,
|
||||||
|
|
||||||
onStateChanged() {
|
onStateChanged() {
|
||||||
// If a download just finished successfully, it means that the target file
|
this.element.setAttribute("image", this.image);
|
||||||
// now exists and we can extract its specific icon. To ensure that the icon
|
this.element.setAttribute("state",
|
||||||
// is reloaded, we must change the URI used by the XUL image element, for
|
DownloadsCommon.stateOfDownload(this.download));
|
||||||
// example by adding a query parameter. Since this URI has a "moz-icon"
|
|
||||||
// scheme, this only works if we add one of the parameters explicitly
|
|
||||||
// supported by the nsIMozIconURI interface.
|
|
||||||
if (this.dataItem.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
|
|
||||||
this.element.setAttribute("image", this.image + "&state=normal");
|
|
||||||
|
|
||||||
|
if (this.download.succeeded) {
|
||||||
// We assume the existence of the target of a download that just completed
|
// We assume the existence of the target of a download that just completed
|
||||||
// successfully, without checking the condition in the background. If the
|
// successfully, without checking the condition in the background. If the
|
||||||
// panel is already open, this will take effect immediately. If the panel
|
// panel is already open, this will take effect immediately. If the panel
|
||||||
// is opened later, a new background existence check will be performed.
|
// is opened later, a new background existence check will be performed.
|
||||||
this.element.setAttribute("exists", "true");
|
this.element.setAttribute("exists", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the user interface after switching states.
|
|
||||||
this.element.setAttribute("state", this.dataItem.state);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onChanged() {
|
onChanged() {
|
||||||
|
|
@ -1252,24 +1246,38 @@ DownloadsViewItemController.prototype = {
|
||||||
*/
|
*/
|
||||||
dataItem: null,
|
dataItem: null,
|
||||||
|
|
||||||
|
get download() this.dataItem.download,
|
||||||
|
|
||||||
isCommandEnabled: function DVIC_isCommandEnabled(aCommand)
|
isCommandEnabled: function DVIC_isCommandEnabled(aCommand)
|
||||||
{
|
{
|
||||||
switch (aCommand) {
|
switch (aCommand) {
|
||||||
case "downloadsCmd_open": {
|
case "downloadsCmd_open": {
|
||||||
return this.dataItem.download.succeeded &&
|
if (!this.download.succeeded) {
|
||||||
this.dataItem.localFile.exists();
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = new FileUtils.File(this.download.target.path);
|
||||||
|
return file.exists();
|
||||||
}
|
}
|
||||||
case "downloadsCmd_show": {
|
case "downloadsCmd_show": {
|
||||||
return this.dataItem.localFile.exists() ||
|
let file = new FileUtils.File(this.download.target.path);
|
||||||
this.dataItem.partFile.exists();
|
if (file.exists()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.download.target.partFilePath) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let partFile = new FileUtils.File(this.download.target.partFilePath);
|
||||||
|
return partFile.exists();
|
||||||
}
|
}
|
||||||
case "downloadsCmd_pauseResume":
|
case "downloadsCmd_pauseResume":
|
||||||
return this.dataItem.inProgress &&
|
return this.download.hasPartialData && !this.download.error;
|
||||||
this.dataItem.download.hasPartialData;
|
|
||||||
case "downloadsCmd_retry":
|
case "downloadsCmd_retry":
|
||||||
return this.dataItem.canRetry;
|
return this.download.canceled || this.download.error;
|
||||||
case "downloadsCmd_openReferrer":
|
case "downloadsCmd_openReferrer":
|
||||||
return !!this.dataItem.download.source.referrer;
|
return !!this.download.source.referrer;
|
||||||
case "cmd_delete":
|
case "cmd_delete":
|
||||||
case "downloadsCmd_cancel":
|
case "downloadsCmd_cancel":
|
||||||
case "downloadsCmd_copyLocation":
|
case "downloadsCmd_copyLocation":
|
||||||
|
|
@ -1298,22 +1306,23 @@ DownloadsViewItemController.prototype = {
|
||||||
cmd_delete: function DVIC_cmd_delete()
|
cmd_delete: function DVIC_cmd_delete()
|
||||||
{
|
{
|
||||||
Downloads.getList(Downloads.ALL)
|
Downloads.getList(Downloads.ALL)
|
||||||
.then(list => list.remove(this.dataItem.download))
|
.then(list => list.remove(this.download))
|
||||||
.then(() => this.dataItem.download.finalize(true))
|
.then(() => this.download.finalize(true))
|
||||||
.catch(Cu.reportError);
|
.catch(Cu.reportError);
|
||||||
PlacesUtils.bhistory.removePage(
|
PlacesUtils.bhistory.removePage(
|
||||||
NetUtil.newURI(this.dataItem.download.source.url));
|
NetUtil.newURI(this.download.source.url));
|
||||||
},
|
},
|
||||||
|
|
||||||
downloadsCmd_cancel: function DVIC_downloadsCmd_cancel()
|
downloadsCmd_cancel: function DVIC_downloadsCmd_cancel()
|
||||||
{
|
{
|
||||||
this.dataItem.download.cancel().catch(() => {});
|
this.download.cancel().catch(() => {});
|
||||||
this.dataItem.download.removePartialData().catch(Cu.reportError);
|
this.download.removePartialData().catch(Cu.reportError);
|
||||||
},
|
},
|
||||||
|
|
||||||
downloadsCmd_open: function DVIC_downloadsCmd_open()
|
downloadsCmd_open: function DVIC_downloadsCmd_open()
|
||||||
{
|
{
|
||||||
this.dataItem.download.launch().catch(Cu.reportError);
|
this.download.launch().catch(Cu.reportError);
|
||||||
|
|
||||||
// We explicitly close the panel here to give the user the feedback that
|
// We explicitly close the panel here to give the user the feedback that
|
||||||
// their click has been received, and we're handling the action.
|
// their click has been received, and we're handling the action.
|
||||||
// Otherwise, we'd have to wait for the file-type handler to execute
|
// Otherwise, we'd have to wait for the file-type handler to execute
|
||||||
|
|
@ -1324,7 +1333,8 @@ DownloadsViewItemController.prototype = {
|
||||||
|
|
||||||
downloadsCmd_show: function DVIC_downloadsCmd_show()
|
downloadsCmd_show: function DVIC_downloadsCmd_show()
|
||||||
{
|
{
|
||||||
DownloadsCommon.showDownloadedFile(this.dataItem.localFile);
|
let file = new FileUtils.File(this.download.target.path);
|
||||||
|
DownloadsCommon.showDownloadedFile(file);
|
||||||
|
|
||||||
// We explicitly close the panel here to give the user the feedback that
|
// We explicitly close the panel here to give the user the feedback that
|
||||||
// their click has been received, and we're handling the action.
|
// their click has been received, and we're handling the action.
|
||||||
|
|
@ -1336,28 +1346,28 @@ DownloadsViewItemController.prototype = {
|
||||||
|
|
||||||
downloadsCmd_pauseResume: function DVIC_downloadsCmd_pauseResume()
|
downloadsCmd_pauseResume: function DVIC_downloadsCmd_pauseResume()
|
||||||
{
|
{
|
||||||
if (this.dataItem.download.stopped) {
|
if (this.download.stopped) {
|
||||||
this.dataItem.download.start();
|
this.download.start();
|
||||||
} else {
|
} else {
|
||||||
this.dataItem.download.cancel();
|
this.download.cancel();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
downloadsCmd_retry: function DVIC_downloadsCmd_retry()
|
downloadsCmd_retry: function DVIC_downloadsCmd_retry()
|
||||||
{
|
{
|
||||||
this.dataItem.download.start().catch(() => {});
|
this.download.start().catch(() => {});
|
||||||
},
|
},
|
||||||
|
|
||||||
downloadsCmd_openReferrer: function DVIC_downloadsCmd_openReferrer()
|
downloadsCmd_openReferrer: function DVIC_downloadsCmd_openReferrer()
|
||||||
{
|
{
|
||||||
openURL(this.dataItem.download.source.referrer);
|
openURL(this.download.source.referrer);
|
||||||
},
|
},
|
||||||
|
|
||||||
downloadsCmd_copyLocation: function DVIC_downloadsCmd_copyLocation()
|
downloadsCmd_copyLocation: function DVIC_downloadsCmd_copyLocation()
|
||||||
{
|
{
|
||||||
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
|
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
|
||||||
.getService(Ci.nsIClipboardHelper);
|
.getService(Ci.nsIClipboardHelper);
|
||||||
clipboard.copyString(this.dataItem.download.source.url, document);
|
clipboard.copyString(this.download.source.url, document);
|
||||||
},
|
},
|
||||||
|
|
||||||
downloadsCmd_doDefault: function DVIC_downloadsCmd_doDefault()
|
downloadsCmd_doDefault: function DVIC_downloadsCmd_doDefault()
|
||||||
|
|
@ -1366,7 +1376,7 @@ DownloadsViewItemController.prototype = {
|
||||||
|
|
||||||
// Determine the default command for the current item.
|
// Determine the default command for the current item.
|
||||||
let defaultCommand = function () {
|
let defaultCommand = function () {
|
||||||
switch (this.dataItem.state) {
|
switch (DownloadsCommon.stateOfDownload(this.download)) {
|
||||||
case nsIDM.DOWNLOAD_NOTSTARTED: return "downloadsCmd_cancel";
|
case nsIDM.DOWNLOAD_NOTSTARTED: return "downloadsCmd_cancel";
|
||||||
case nsIDM.DOWNLOAD_FINISHED: return "downloadsCmd_open";
|
case nsIDM.DOWNLOAD_FINISHED: return "downloadsCmd_open";
|
||||||
case nsIDM.DOWNLOAD_FAILED: return "downloadsCmd_retry";
|
case nsIDM.DOWNLOAD_FAILED: return "downloadsCmd_retry";
|
||||||
|
|
|
||||||
|
|
@ -72,12 +72,19 @@ DownloadElementShell.prototype = {
|
||||||
* URI string for the file type icon displayed in the download element.
|
* URI string for the file type icon displayed in the download element.
|
||||||
*/
|
*/
|
||||||
get image() {
|
get image() {
|
||||||
if (this.download.target.path) {
|
if (!this.download.target.path) {
|
||||||
return "moz-icon://" + this.download.target.path + "?size=32";
|
// Old history downloads may not have a target path.
|
||||||
|
return "moz-icon://.unknown?size=32";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Old history downloads may not have a target path.
|
// When a download that was previously in progress finishes successfully, it
|
||||||
return "moz-icon://.unknown?size=32";
|
// means that the target file now exists and we can extract its specific
|
||||||
|
// icon, for example from a Windows executable. To ensure that the icon is
|
||||||
|
// reloaded, however, we must change the URI used by the XUL image element,
|
||||||
|
// for example by adding a query parameter. This only works if we add one of
|
||||||
|
// the parameters explicitly supported by the nsIMozIconURI interface.
|
||||||
|
return "moz-icon://" + this.download.target.path + "?size=32" +
|
||||||
|
(this.download.succeeded ? "&state=normal" : "");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -127,11 +134,12 @@ DownloadElementShell.prototype = {
|
||||||
* update in order to improve performance.
|
* update in order to improve performance.
|
||||||
*/
|
*/
|
||||||
_updateState() {
|
_updateState() {
|
||||||
this.element.setAttribute("state", this.dataItem.state);
|
|
||||||
this.element.setAttribute("displayName", this.displayName);
|
this.element.setAttribute("displayName", this.displayName);
|
||||||
this.element.setAttribute("extendedDisplayName", this.extendedDisplayName);
|
this.element.setAttribute("extendedDisplayName", this.extendedDisplayName);
|
||||||
this.element.setAttribute("extendedDisplayNameTip", this.extendedDisplayNameTip);
|
this.element.setAttribute("extendedDisplayNameTip", this.extendedDisplayNameTip);
|
||||||
this.element.setAttribute("image", this.image);
|
this.element.setAttribute("image", this.image);
|
||||||
|
this.element.setAttribute("state",
|
||||||
|
DownloadsCommon.stateOfDownload(this.download));
|
||||||
|
|
||||||
// Since state changed, reset the time left estimation.
|
// Since state changed, reset the time left estimation.
|
||||||
this.lastEstimatedSecondsLeft = Infinity;
|
this.lastEstimatedSecondsLeft = Infinity;
|
||||||
|
|
@ -144,19 +152,12 @@ DownloadElementShell.prototype = {
|
||||||
* namely the progress bar and the status line.
|
* namely the progress bar and the status line.
|
||||||
*/
|
*/
|
||||||
_updateProgress() {
|
_updateProgress() {
|
||||||
if (this.dataItem.starting) {
|
// The progress bar is only displayed for in-progress downloads.
|
||||||
// Before the download starts, the progress meter has its initial value.
|
if (this.download.hasProgress) {
|
||||||
this.element.setAttribute("progressmode", "normal");
|
this.element.setAttribute("progressmode", "normal");
|
||||||
this.element.setAttribute("progress", "0");
|
this.element.setAttribute("progress", this.download.progress);
|
||||||
} else if (this.dataItem.state == Ci.nsIDownloadManager.DOWNLOAD_SCANNING ||
|
|
||||||
this.dataItem.percentComplete == -1) {
|
|
||||||
// We might not know the progress of a running download, and we don't know
|
|
||||||
// the remaining time during the malware scanning phase.
|
|
||||||
this.element.setAttribute("progressmode", "undetermined");
|
|
||||||
} else {
|
} else {
|
||||||
// This is a running download of which we know the progress.
|
this.element.setAttribute("progressmode", "undetermined");
|
||||||
this.element.setAttribute("progressmode", "normal");
|
|
||||||
this.element.setAttribute("progress", this.dataItem.percentComplete);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch the ValueChange event for accessibility, if possible.
|
// Dispatch the ValueChange event for accessibility, if possible.
|
||||||
|
|
@ -190,71 +191,60 @@ DownloadElementShell.prototype = {
|
||||||
let text = "";
|
let text = "";
|
||||||
let tip = "";
|
let tip = "";
|
||||||
|
|
||||||
if (this.dataItem.paused) {
|
if (!this.download.stopped) {
|
||||||
let transfer = DownloadUtils.getTransferTotal(this.download.currentBytes,
|
let maxBytes = DownloadsCommon.maxBytesOfDownload(this.download);
|
||||||
this.dataItem.maxBytes);
|
|
||||||
|
|
||||||
// We use the same XUL label to display both the state and the amount
|
|
||||||
// transferred, for example "Paused - 1.1 MB".
|
|
||||||
text = s.statusSeparatorBeforeNumber(s.statePaused, transfer);
|
|
||||||
} else if (this.dataItem.state == nsIDM.DOWNLOAD_DOWNLOADING) {
|
|
||||||
// By default, extended status information including the individual
|
// By default, extended status information including the individual
|
||||||
// download rate is displayed in the tooltip. The history view overrides
|
// download rate is displayed in the tooltip. The history view overrides
|
||||||
// the getter and displays the detials in the main area instead.
|
// the getter and displays the detials in the main area instead.
|
||||||
[text] = DownloadUtils.getDownloadStatusNoRate(
|
[text] = DownloadUtils.getDownloadStatusNoRate(
|
||||||
this.download.currentBytes,
|
this.download.currentBytes,
|
||||||
this.dataItem.maxBytes,
|
maxBytes,
|
||||||
this.download.speed,
|
this.download.speed,
|
||||||
this.lastEstimatedSecondsLeft);
|
this.lastEstimatedSecondsLeft);
|
||||||
let newEstimatedSecondsLeft;
|
let newEstimatedSecondsLeft;
|
||||||
[tip, newEstimatedSecondsLeft] = DownloadUtils.getDownloadStatus(
|
[tip, newEstimatedSecondsLeft] = DownloadUtils.getDownloadStatus(
|
||||||
this.download.currentBytes,
|
this.download.currentBytes,
|
||||||
this.dataItem.maxBytes,
|
maxBytes,
|
||||||
this.download.speed,
|
this.download.speed,
|
||||||
this.lastEstimatedSecondsLeft);
|
this.lastEstimatedSecondsLeft);
|
||||||
this.lastEstimatedSecondsLeft = newEstimatedSecondsLeft;
|
this.lastEstimatedSecondsLeft = newEstimatedSecondsLeft;
|
||||||
} else if (this.dataItem.starting) {
|
} else if (this.download.canceled && this.download.hasPartialData) {
|
||||||
|
let maxBytes = DownloadsCommon.maxBytesOfDownload(this.download);
|
||||||
|
let transfer = DownloadUtils.getTransferTotal(this.download.currentBytes,
|
||||||
|
maxBytes);
|
||||||
|
|
||||||
|
// We use the same XUL label to display both the state and the amount
|
||||||
|
// transferred, for example "Paused - 1.1 MB".
|
||||||
|
text = s.statusSeparatorBeforeNumber(s.statePaused, transfer);
|
||||||
|
} else if (!this.download.succeeded && !this.download.canceled &&
|
||||||
|
!this.download.error) {
|
||||||
text = s.stateStarting;
|
text = s.stateStarting;
|
||||||
} else if (this.dataItem.state == nsIDM.DOWNLOAD_SCANNING) {
|
|
||||||
text = s.stateScanning;
|
|
||||||
} else {
|
} else {
|
||||||
let stateLabel;
|
let stateLabel;
|
||||||
switch (this.dataItem.state) {
|
|
||||||
case nsIDM.DOWNLOAD_FAILED:
|
if (this.download.succeeded) {
|
||||||
stateLabel = s.stateFailed;
|
// For completed downloads, show the file size (e.g. "1.5 MB")
|
||||||
break;
|
let maxBytes = DownloadsCommon.maxBytesOfDownload(this.download);
|
||||||
case nsIDM.DOWNLOAD_CANCELED:
|
if (maxBytes >= 0) {
|
||||||
stateLabel = s.stateCanceled;
|
let [size, unit] = DownloadUtils.convertByteUnits(maxBytes);
|
||||||
break;
|
stateLabel = s.sizeWithUnits(size, unit);
|
||||||
case nsIDM.DOWNLOAD_BLOCKED_PARENTAL:
|
} else {
|
||||||
stateLabel = s.stateBlockedParentalControls;
|
|
||||||
break;
|
|
||||||
case nsIDM.DOWNLOAD_BLOCKED_POLICY:
|
|
||||||
stateLabel = s.stateBlockedPolicy;
|
|
||||||
break;
|
|
||||||
case nsIDM.DOWNLOAD_DIRTY:
|
|
||||||
stateLabel = s.stateDirty;
|
|
||||||
break;
|
|
||||||
case nsIDM.DOWNLOAD_FINISHED:
|
|
||||||
// For completed downloads, show the file size (e.g. "1.5 MB")
|
|
||||||
if (this.dataItem.maxBytes !== undefined &&
|
|
||||||
this.dataItem.maxBytes >= 0) {
|
|
||||||
let [size, unit] =
|
|
||||||
DownloadUtils.convertByteUnits(this.dataItem.maxBytes);
|
|
||||||
stateLabel = s.sizeWithUnits(size, unit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Fallback to default unknown state.
|
|
||||||
default:
|
|
||||||
stateLabel = s.sizeUnknown;
|
stateLabel = s.sizeUnknown;
|
||||||
break;
|
}
|
||||||
|
} else if (this.download.canceled) {
|
||||||
|
stateLabel = s.stateCanceled;
|
||||||
|
} else if (this.download.error.becauseBlockedByParentalControls) {
|
||||||
|
stateLabel = s.stateBlockedParentalControls;
|
||||||
|
} else if (this.download.error.becauseBlockedByReputationCheck) {
|
||||||
|
stateLabel = s.stateDirty;
|
||||||
|
} else {
|
||||||
|
stateLabel = s.stateFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
let referrer = this.download.source.referrer ||
|
let referrer = this.download.source.referrer || this.download.source.url;
|
||||||
this.download.source.url;
|
|
||||||
let [displayHost, fullHost] = DownloadUtils.getURIHost(referrer);
|
let [displayHost, fullHost] = DownloadUtils.getURIHost(referrer);
|
||||||
|
|
||||||
let date = new Date(this.dataItem.endTime);
|
let date = new Date(this.download.endTime);
|
||||||
let [displayDate, fullDate] = DownloadUtils.getReadableDates(date);
|
let [displayDate, fullDate] = DownloadUtils.getReadableDates(date);
|
||||||
|
|
||||||
let firstPart = s.statusSeparator(stateLabel, displayHost);
|
let firstPart = s.statusSeparator(stateLabel, displayHost);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue