mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 01:08:39 +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;
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
* statistics about that collection.
|
||||
|
|
@ -418,8 +438,12 @@ this.DownloadsCommon = {
|
|||
}
|
||||
|
||||
for (let dataItem of aDataItems) {
|
||||
let download = dataItem.download;
|
||||
let state = DownloadsCommon.stateOfDownload(download);
|
||||
let maxBytes = DownloadsCommon.maxBytesOfDownload(download);
|
||||
|
||||
summary.numActive++;
|
||||
switch (dataItem.state) {
|
||||
switch (state) {
|
||||
case nsIDM.DOWNLOAD_PAUSED:
|
||||
summary.numPaused++;
|
||||
break;
|
||||
|
|
@ -428,21 +452,20 @@ this.DownloadsCommon = {
|
|||
break;
|
||||
case nsIDM.DOWNLOAD_DOWNLOADING:
|
||||
summary.numDownloading++;
|
||||
if (dataItem.maxBytes > 0 && dataItem.download.speed > 0) {
|
||||
let sizeLeft = dataItem.maxBytes - dataItem.download.currentBytes;
|
||||
if (maxBytes > 0 && download.speed > 0) {
|
||||
let sizeLeft = maxBytes - download.currentBytes;
|
||||
summary.rawTimeLeft = Math.max(summary.rawTimeLeft,
|
||||
sizeLeft / dataItem.download.speed);
|
||||
sizeLeft / download.speed);
|
||||
summary.slowestSpeed = Math.min(summary.slowestSpeed,
|
||||
dataItem.download.speed);
|
||||
download.speed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Only add to total values if we actually know the download size.
|
||||
if (dataItem.maxBytes > 0 &&
|
||||
dataItem.state != nsIDM.DOWNLOAD_CANCELED &&
|
||||
dataItem.state != nsIDM.DOWNLOAD_FAILED) {
|
||||
summary.totalSize += dataItem.maxBytes;
|
||||
summary.totalTransferred += dataItem.download.currentBytes;
|
||||
if (maxBytes > 0 && state != nsIDM.DOWNLOAD_CANCELED &&
|
||||
state != nsIDM.DOWNLOAD_FAILED) {
|
||||
summary.totalSize += maxBytes;
|
||||
summary.totalTransferred += download.currentBytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -498,7 +521,7 @@ this.DownloadsCommon = {
|
|||
|
||||
/**
|
||||
* Opens a downloaded file.
|
||||
* If you've a dataItem, you should call dataItem.openLocalFile.
|
||||
*
|
||||
* @param aFile
|
||||
* the downloaded file to be opened.
|
||||
* @param aMimeInfo
|
||||
|
|
@ -649,6 +672,7 @@ function DownloadsDataCtor(aPrivate) {
|
|||
|
||||
// Contains all the available DownloadsDataItem objects.
|
||||
this.dataItems = new Set();
|
||||
this.oldDownloadStates = new Map();
|
||||
|
||||
// Array of view objects that should be notified when the available download
|
||||
// data changes.
|
||||
|
|
@ -687,7 +711,9 @@ DownloadsDataCtor.prototype = {
|
|||
get canRemoveFinished()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -713,23 +739,82 @@ DownloadsDataCtor.prototype = {
|
|||
let dataItem = new DownloadsDataItem(aDownload);
|
||||
this._downloadToDataItemMap.set(aDownload, dataItem);
|
||||
this.dataItems.add(dataItem);
|
||||
this.oldDownloadStates.set(aDownload,
|
||||
DownloadsCommon.stateOfDownload(aDownload));
|
||||
|
||||
for (let view of this._views) {
|
||||
view.onDataItemAdded(dataItem, true);
|
||||
}
|
||||
|
||||
this._updateDataItemState(dataItem);
|
||||
},
|
||||
|
||||
onDownloadChanged: function (aDownload)
|
||||
{
|
||||
let dataItem = this._downloadToDataItemMap.get(aDownload);
|
||||
if (!dataItem) {
|
||||
let aDataItem = this._downloadToDataItemMap.get(aDownload);
|
||||
if (!aDataItem) {
|
||||
Cu.reportError("Download doesn't exist.");
|
||||
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)
|
||||
|
|
@ -742,73 +827,12 @@ DownloadsDataCtor.prototype = {
|
|||
|
||||
this._downloadToDataItemMap.delete(aDownload);
|
||||
this.dataItems.delete(dataItem);
|
||||
this.oldDownloadStates.delete(aDownload);
|
||||
for (let view of this._views) {
|
||||
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
|
||||
|
||||
|
|
@ -1333,6 +1357,8 @@ function DownloadsDataItem(aSource)
|
|||
}
|
||||
|
||||
DownloadsDataItem.prototype = {
|
||||
get state() DownloadsCommon.stateOfDownload(this.download),
|
||||
|
||||
/**
|
||||
* Initializes this object from the JavaScript API for downloads.
|
||||
*
|
||||
|
|
@ -1344,51 +1370,7 @@ DownloadsDataItem.prototype = {
|
|||
_initFromJSDownload: function (aDownload)
|
||||
{
|
||||
this.download = aDownload;
|
||||
this.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;
|
||||
}
|
||||
this.download.endTime = Date.now();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -1896,9 +1878,10 @@ DownloadsIndicatorDataCtor.prototype = {
|
|||
},
|
||||
|
||||
// DownloadsView
|
||||
onDataItemStateChanged(aDataItem, aOldState) {
|
||||
if (aDataItem.state == nsIDM.DOWNLOAD_FINISHED ||
|
||||
aDataItem.state == nsIDM.DOWNLOAD_FAILED) {
|
||||
onDataItemStateChanged(aDataItem) {
|
||||
let download = aDataItem.download;
|
||||
|
||||
if (download.succeeded || download.error) {
|
||||
this.attention = true;
|
||||
}
|
||||
|
||||
|
|
@ -2010,7 +1993,11 @@ DownloadsIndicatorDataCtor.prototype = {
|
|||
let dataItems = this._isPrivate ? PrivateDownloadsData.dataItems
|
||||
: DownloadsData.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;
|
||||
}
|
||||
}
|
||||
|
|
@ -2164,7 +2151,7 @@ DownloadsSummaryData.prototype = {
|
|||
},
|
||||
|
||||
// DownloadsView
|
||||
onDataItemStateChanged(aOldState) {
|
||||
onDataItemStateChanged() {
|
||||
// Since the state of a download changed, reset the estimated time left.
|
||||
this._lastRawTimeLeft = -1;
|
||||
this._lastTimeLeft = -1;
|
||||
|
|
|
|||
|
|
@ -24,14 +24,90 @@ const DOWNLOAD_VIEW_SUPPORTED_COMMANDS =
|
|||
*
|
||||
* @param url
|
||||
* 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.
|
||||
this.source = { url };
|
||||
this.source = { url: aPlacesNode.uri };
|
||||
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 = {
|
||||
/**
|
||||
* 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
|
||||
* when the user retries a history download.
|
||||
|
|
@ -59,57 +135,11 @@ HistoryDownload.prototype = {
|
|||
* The Places node for the history download.
|
||||
*/
|
||||
function DownloadsHistoryDataItem(aPlacesNode) {
|
||||
this.download = new HistoryDownload(aPlacesNode.uri);
|
||||
|
||||
// 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;
|
||||
this.download = new HistoryDownload(aPlacesNode);
|
||||
}
|
||||
|
||||
DownloadsHistoryDataItem.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,
|
||||
// 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.tip = "";
|
||||
|
|
@ -232,18 +262,9 @@ HistoryDownloadElementShell.prototype = {
|
|||
},
|
||||
|
||||
onStateChanged() {
|
||||
// If a download just finished successfully, it means that the target file
|
||||
// now exists and we can extract its specific icon. To ensure that the icon
|
||||
// is reloaded, we must change the URI used by the XUL image element, for
|
||||
// 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);
|
||||
this.element.setAttribute("image", this.image);
|
||||
this.element.setAttribute("state",
|
||||
DownloadsCommon.stateOfDownload(this.download));
|
||||
|
||||
if (this.element.selected) {
|
||||
goUpdateDownloadCommands();
|
||||
|
|
@ -275,12 +296,14 @@ HistoryDownloadElementShell.prototype = {
|
|||
|
||||
// If the target file information is not yet fetched,
|
||||
// temporarily assume that the file is in place.
|
||||
return this.dataItem.state == nsIDM.DOWNLOAD_FINISHED;
|
||||
return this.download.succeeded;
|
||||
case "downloadsCmd_show":
|
||||
// TODO: Bug 827010 - Handle part-file asynchronously.
|
||||
if (this._sessionDataItem &&
|
||||
this.dataItem.partFile && this.dataItem.partFile.exists()) {
|
||||
return true;
|
||||
if (this._sessionDataItem && this.download.target.partFilePath) {
|
||||
let partFile = new FileUtils.File(this.download.target.partFilePath);
|
||||
if (partFile.exists()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._targetFileChecked) {
|
||||
|
|
@ -289,17 +312,16 @@ HistoryDownloadElementShell.prototype = {
|
|||
|
||||
// If the target file information is not yet fetched,
|
||||
// temporarily assume that the file is in place.
|
||||
return this.dataItem.state == nsIDM.DOWNLOAD_FINISHED;
|
||||
return this.download.succeeded;
|
||||
case "downloadsCmd_pauseResume":
|
||||
return this._sessionDataItem && this.dataItem.inProgress &&
|
||||
this.dataItem.download.hasPartialData;
|
||||
return this.download.hasPartialData && !this.download.error;
|
||||
case "downloadsCmd_retry":
|
||||
return this.dataItem.canRetry;
|
||||
return this.download.canceled || this.download.error;
|
||||
case "downloadsCmd_openReferrer":
|
||||
return !!this.download.source.referrer;
|
||||
case "cmd_delete":
|
||||
// The behavior in this case is somewhat unexpected, so we disallow that.
|
||||
return !this.dataItem.inProgress;
|
||||
// We don't want in-progress downloads to be removed accidentally.
|
||||
return this.download.stopped;
|
||||
case "downloadsCmd_cancel":
|
||||
return !!this._sessionDataItem;
|
||||
}
|
||||
|
|
@ -393,7 +415,8 @@ HistoryDownloadElementShell.prototype = {
|
|||
}
|
||||
return "";
|
||||
}
|
||||
let command = getDefaultCommandForState(this.dataItem.state);
|
||||
let command = getDefaultCommandForState(
|
||||
DownloadsCommon.stateOfDownload(this.download));
|
||||
if (command && this.isCommandEnabled(command))
|
||||
this.doCommand(command);
|
||||
},
|
||||
|
|
@ -621,8 +644,9 @@ DownloadsPlacesView.prototype = {
|
|||
_addDownloadData:
|
||||
function DPV_addDownloadData(aDataItem, aPlacesNode, aNewest = false,
|
||||
aDocumentFragment = null) {
|
||||
let sessionDownload = aDataItem && aDataItem.download;
|
||||
let downloadURI = aPlacesNode ? aPlacesNode.uri
|
||||
: aDataItem.download.source.url;
|
||||
: sessionDownload.source.url;
|
||||
let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI);
|
||||
if (!shellsForURI) {
|
||||
shellsForURI = new Set();
|
||||
|
|
@ -674,7 +698,7 @@ DownloadsPlacesView.prototype = {
|
|||
if (aPlacesNode) {
|
||||
let metaData = this._getCachedPlacesMetaDataFor(aPlacesNode.uri);
|
||||
historyDataItem = new DownloadsHistoryDataItem(aPlacesNode);
|
||||
historyDataItem.updateFromMetaData(metaData);
|
||||
historyDataItem.download.updateFromMetaData(metaData);
|
||||
}
|
||||
let shell = new HistoryDownloadElementShell(aDataItem, historyDataItem);
|
||||
shell.element._placesNode = aPlacesNode;
|
||||
|
|
@ -782,8 +806,9 @@ DownloadsPlacesView.prototype = {
|
|||
|
||||
_removeSessionDownloadFromView:
|
||||
function DPV__removeSessionDownloadFromView(aDataItem) {
|
||||
let download = aDataItem.download;
|
||||
let shells = this._downloadElementsShellsForURI
|
||||
.get(aDataItem.download.source.url);
|
||||
.get(download.source.url);
|
||||
if (shells.size == 0)
|
||||
throw new Error("Should have had at leaat one shell for this uri");
|
||||
|
||||
|
|
@ -799,7 +824,7 @@ DownloadsPlacesView.prototype = {
|
|||
this._removeElement(shell.element);
|
||||
shells.delete(shell);
|
||||
if (shells.size == 0)
|
||||
this._downloadElementsShellsForURI.delete(aDataItem.download.source.url);
|
||||
this._downloadElementsShellsForURI.delete(download.source.url);
|
||||
}
|
||||
else {
|
||||
// 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.
|
||||
let url = shell.historyDataItem.download.source.url;
|
||||
let metaData = this._getPlacesMetaDataFor(url);
|
||||
shell.historyDataItem.updateFromMetaData(metaData);
|
||||
shell.historyDataItem.download.updateFromMetaData(metaData);
|
||||
shell.sessionDataItem = null;
|
||||
// Move it below the session-download items;
|
||||
if (this._lastSessionDownloadElement == shell.element) {
|
||||
|
|
@ -1142,7 +1167,9 @@ DownloadsPlacesView.prototype = {
|
|||
// Because history downloads are always removable and are listed after the
|
||||
// session downloads, check from bottom to top.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1244,12 +1271,13 @@ DownloadsPlacesView.prototype = {
|
|||
|
||||
// Set the state attribute so that only the appropriate items are displayed.
|
||||
let contextMenu = document.getElementById("downloadsContextMenu");
|
||||
let state = element._shell.dataItem.state;
|
||||
contextMenu.setAttribute("state", state);
|
||||
let download = element._shell.download;
|
||||
contextMenu.setAttribute("state",
|
||||
DownloadsCommon.stateOfDownload(download));
|
||||
|
||||
if (state == nsIDM.DOWNLOAD_DOWNLOADING) {
|
||||
// The resumable property of a download may change at any time, so
|
||||
// ensure we update the related command now.
|
||||
if (!download.stopped) {
|
||||
// The hasPartialData property of a download may change at any time after
|
||||
// it has started, so ensure we update the related command now.
|
||||
goUpdateCommand("downloadsCmd_pauseResume");
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1025,9 +1025,10 @@ const DownloadsView = {
|
|||
return;
|
||||
}
|
||||
|
||||
let localFile = DownloadsView.controllerForElement(element)
|
||||
.dataItem.localFile;
|
||||
if (!localFile.exists()) {
|
||||
// We must check for existence synchronously because this is a DOM event.
|
||||
let file = new FileUtils.File(DownloadsView.controllerForElement(element)
|
||||
.download.target.path);
|
||||
if (!file.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1085,24 +1086,17 @@ DownloadsViewItem.prototype = {
|
|||
_element: null,
|
||||
|
||||
onStateChanged() {
|
||||
// If a download just finished successfully, it means that the target file
|
||||
// now exists and we can extract its specific icon. To ensure that the icon
|
||||
// is reloaded, we must change the URI used by the XUL image element, for
|
||||
// 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");
|
||||
this.element.setAttribute("image", this.image);
|
||||
this.element.setAttribute("state",
|
||||
DownloadsCommon.stateOfDownload(this.download));
|
||||
|
||||
if (this.download.succeeded) {
|
||||
// We assume the existence of the target of a download that just completed
|
||||
// successfully, without checking the condition in the background. If the
|
||||
// panel is already open, this will take effect immediately. If the panel
|
||||
// is opened later, a new background existence check will be performed.
|
||||
this.element.setAttribute("exists", "true");
|
||||
}
|
||||
|
||||
// Update the user interface after switching states.
|
||||
this.element.setAttribute("state", this.dataItem.state);
|
||||
},
|
||||
|
||||
onChanged() {
|
||||
|
|
@ -1252,24 +1246,38 @@ DownloadsViewItemController.prototype = {
|
|||
*/
|
||||
dataItem: null,
|
||||
|
||||
get download() this.dataItem.download,
|
||||
|
||||
isCommandEnabled: function DVIC_isCommandEnabled(aCommand)
|
||||
{
|
||||
switch (aCommand) {
|
||||
case "downloadsCmd_open": {
|
||||
return this.dataItem.download.succeeded &&
|
||||
this.dataItem.localFile.exists();
|
||||
if (!this.download.succeeded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let file = new FileUtils.File(this.download.target.path);
|
||||
return file.exists();
|
||||
}
|
||||
case "downloadsCmd_show": {
|
||||
return this.dataItem.localFile.exists() ||
|
||||
this.dataItem.partFile.exists();
|
||||
let file = new FileUtils.File(this.download.target.path);
|
||||
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":
|
||||
return this.dataItem.inProgress &&
|
||||
this.dataItem.download.hasPartialData;
|
||||
return this.download.hasPartialData && !this.download.error;
|
||||
case "downloadsCmd_retry":
|
||||
return this.dataItem.canRetry;
|
||||
return this.download.canceled || this.download.error;
|
||||
case "downloadsCmd_openReferrer":
|
||||
return !!this.dataItem.download.source.referrer;
|
||||
return !!this.download.source.referrer;
|
||||
case "cmd_delete":
|
||||
case "downloadsCmd_cancel":
|
||||
case "downloadsCmd_copyLocation":
|
||||
|
|
@ -1298,22 +1306,23 @@ DownloadsViewItemController.prototype = {
|
|||
cmd_delete: function DVIC_cmd_delete()
|
||||
{
|
||||
Downloads.getList(Downloads.ALL)
|
||||
.then(list => list.remove(this.dataItem.download))
|
||||
.then(() => this.dataItem.download.finalize(true))
|
||||
.then(list => list.remove(this.download))
|
||||
.then(() => this.download.finalize(true))
|
||||
.catch(Cu.reportError);
|
||||
PlacesUtils.bhistory.removePage(
|
||||
NetUtil.newURI(this.dataItem.download.source.url));
|
||||
NetUtil.newURI(this.download.source.url));
|
||||
},
|
||||
|
||||
downloadsCmd_cancel: function DVIC_downloadsCmd_cancel()
|
||||
{
|
||||
this.dataItem.download.cancel().catch(() => {});
|
||||
this.dataItem.download.removePartialData().catch(Cu.reportError);
|
||||
this.download.cancel().catch(() => {});
|
||||
this.download.removePartialData().catch(Cu.reportError);
|
||||
},
|
||||
|
||||
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
|
||||
// their click has been received, and we're handling the action.
|
||||
// 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()
|
||||
{
|
||||
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
|
||||
// their click has been received, and we're handling the action.
|
||||
|
|
@ -1336,28 +1346,28 @@ DownloadsViewItemController.prototype = {
|
|||
|
||||
downloadsCmd_pauseResume: function DVIC_downloadsCmd_pauseResume()
|
||||
{
|
||||
if (this.dataItem.download.stopped) {
|
||||
this.dataItem.download.start();
|
||||
if (this.download.stopped) {
|
||||
this.download.start();
|
||||
} else {
|
||||
this.dataItem.download.cancel();
|
||||
this.download.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
downloadsCmd_retry: function DVIC_downloadsCmd_retry()
|
||||
{
|
||||
this.dataItem.download.start().catch(() => {});
|
||||
this.download.start().catch(() => {});
|
||||
},
|
||||
|
||||
downloadsCmd_openReferrer: function DVIC_downloadsCmd_openReferrer()
|
||||
{
|
||||
openURL(this.dataItem.download.source.referrer);
|
||||
openURL(this.download.source.referrer);
|
||||
},
|
||||
|
||||
downloadsCmd_copyLocation: function DVIC_downloadsCmd_copyLocation()
|
||||
{
|
||||
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
|
||||
.getService(Ci.nsIClipboardHelper);
|
||||
clipboard.copyString(this.dataItem.download.source.url, document);
|
||||
clipboard.copyString(this.download.source.url, document);
|
||||
},
|
||||
|
||||
downloadsCmd_doDefault: function DVIC_downloadsCmd_doDefault()
|
||||
|
|
@ -1366,7 +1376,7 @@ DownloadsViewItemController.prototype = {
|
|||
|
||||
// Determine the default command for the current item.
|
||||
let defaultCommand = function () {
|
||||
switch (this.dataItem.state) {
|
||||
switch (DownloadsCommon.stateOfDownload(this.download)) {
|
||||
case nsIDM.DOWNLOAD_NOTSTARTED: return "downloadsCmd_cancel";
|
||||
case nsIDM.DOWNLOAD_FINISHED: return "downloadsCmd_open";
|
||||
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.
|
||||
*/
|
||||
get image() {
|
||||
if (this.download.target.path) {
|
||||
return "moz-icon://" + this.download.target.path + "?size=32";
|
||||
if (!this.download.target.path) {
|
||||
// Old history downloads may not have a target path.
|
||||
return "moz-icon://.unknown?size=32";
|
||||
}
|
||||
|
||||
// Old history downloads may not have a target path.
|
||||
return "moz-icon://.unknown?size=32";
|
||||
// When a download that was previously in progress finishes successfully, it
|
||||
// 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.
|
||||
*/
|
||||
_updateState() {
|
||||
this.element.setAttribute("state", this.dataItem.state);
|
||||
this.element.setAttribute("displayName", this.displayName);
|
||||
this.element.setAttribute("extendedDisplayName", this.extendedDisplayName);
|
||||
this.element.setAttribute("extendedDisplayNameTip", this.extendedDisplayNameTip);
|
||||
this.element.setAttribute("image", this.image);
|
||||
this.element.setAttribute("state",
|
||||
DownloadsCommon.stateOfDownload(this.download));
|
||||
|
||||
// Since state changed, reset the time left estimation.
|
||||
this.lastEstimatedSecondsLeft = Infinity;
|
||||
|
|
@ -144,19 +152,12 @@ DownloadElementShell.prototype = {
|
|||
* namely the progress bar and the status line.
|
||||
*/
|
||||
_updateProgress() {
|
||||
if (this.dataItem.starting) {
|
||||
// Before the download starts, the progress meter has its initial value.
|
||||
// The progress bar is only displayed for in-progress downloads.
|
||||
if (this.download.hasProgress) {
|
||||
this.element.setAttribute("progressmode", "normal");
|
||||
this.element.setAttribute("progress", "0");
|
||||
} 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");
|
||||
this.element.setAttribute("progress", this.download.progress);
|
||||
} else {
|
||||
// This is a running download of which we know the progress.
|
||||
this.element.setAttribute("progressmode", "normal");
|
||||
this.element.setAttribute("progress", this.dataItem.percentComplete);
|
||||
this.element.setAttribute("progressmode", "undetermined");
|
||||
}
|
||||
|
||||
// Dispatch the ValueChange event for accessibility, if possible.
|
||||
|
|
@ -190,71 +191,60 @@ DownloadElementShell.prototype = {
|
|||
let text = "";
|
||||
let tip = "";
|
||||
|
||||
if (this.dataItem.paused) {
|
||||
let transfer = DownloadUtils.getTransferTotal(this.download.currentBytes,
|
||||
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) {
|
||||
if (!this.download.stopped) {
|
||||
let maxBytes = DownloadsCommon.maxBytesOfDownload(this.download);
|
||||
// By default, extended status information including the individual
|
||||
// download rate is displayed in the tooltip. The history view overrides
|
||||
// the getter and displays the detials in the main area instead.
|
||||
[text] = DownloadUtils.getDownloadStatusNoRate(
|
||||
this.download.currentBytes,
|
||||
this.dataItem.maxBytes,
|
||||
maxBytes,
|
||||
this.download.speed,
|
||||
this.lastEstimatedSecondsLeft);
|
||||
let newEstimatedSecondsLeft;
|
||||
[tip, newEstimatedSecondsLeft] = DownloadUtils.getDownloadStatus(
|
||||
this.download.currentBytes,
|
||||
this.dataItem.maxBytes,
|
||||
maxBytes,
|
||||
this.download.speed,
|
||||
this.lastEstimatedSecondsLeft);
|
||||
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;
|
||||
} else if (this.dataItem.state == nsIDM.DOWNLOAD_SCANNING) {
|
||||
text = s.stateScanning;
|
||||
} else {
|
||||
let stateLabel;
|
||||
switch (this.dataItem.state) {
|
||||
case nsIDM.DOWNLOAD_FAILED:
|
||||
stateLabel = s.stateFailed;
|
||||
break;
|
||||
case nsIDM.DOWNLOAD_CANCELED:
|
||||
stateLabel = s.stateCanceled;
|
||||
break;
|
||||
case nsIDM.DOWNLOAD_BLOCKED_PARENTAL:
|
||||
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:
|
||||
|
||||
if (this.download.succeeded) {
|
||||
// For completed downloads, show the file size (e.g. "1.5 MB")
|
||||
let maxBytes = DownloadsCommon.maxBytesOfDownload(this.download);
|
||||
if (maxBytes >= 0) {
|
||||
let [size, unit] = DownloadUtils.convertByteUnits(maxBytes);
|
||||
stateLabel = s.sizeWithUnits(size, unit);
|
||||
} else {
|
||||
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 ||
|
||||
this.download.source.url;
|
||||
let referrer = this.download.source.referrer || this.download.source.url;
|
||||
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 firstPart = s.statusSeparator(stateLabel, displayHost);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue