summaryrefslogtreecommitdiffstats
path: root/comm/suite/components/downloads/content
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/suite/components/downloads/content
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/suite/components/downloads/content')
-rw-r--r--comm/suite/components/downloads/content/DownloadProgressListener.js30
-rw-r--r--comm/suite/components/downloads/content/downloadmanager.js634
-rw-r--r--comm/suite/components/downloads/content/downloadmanager.xul452
-rw-r--r--comm/suite/components/downloads/content/progressDialog.js240
-rw-r--r--comm/suite/components/downloads/content/progressDialog.xul108
-rw-r--r--comm/suite/components/downloads/content/treeView.js483
-rw-r--r--comm/suite/components/downloads/content/uploadProgress.js189
-rw-r--r--comm/suite/components/downloads/content/uploadProgress.xul33
8 files changed, 2169 insertions, 0 deletions
diff --git a/comm/suite/components/downloads/content/DownloadProgressListener.js b/comm/suite/components/downloads/content/DownloadProgressListener.js
new file mode 100644
index 0000000000..b5bab95727
--- /dev/null
+++ b/comm/suite/components/downloads/content/DownloadProgressListener.js
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * DownloadProgressListener "class" is used to help update download items shown
+ * in the Download Manager UI such as displaying amount transferred, transfer
+ * rate, and time left for each download.
+ */
+function DownloadProgressListener() {}
+
+DownloadProgressListener.prototype = {
+ onDownloadAdded: function(aDownload) {
+ gDownloadTreeView.addDownload(aDownload);
+
+ // Update window title in-case we don't get all progress notifications
+ onUpdateProgress();
+ },
+
+ onDownloadChanged: function(aDownload) {
+ gDownloadTreeView.updateDownload(aDownload);
+
+ // Update window title
+ onUpdateProgress();
+ },
+
+ onDownloadRemoved: function(aDownload) {
+ gDownloadTreeView.removeDownload(aDownload);
+ }
+};
diff --git a/comm/suite/components/downloads/content/downloadmanager.js b/comm/suite/components/downloads/content/downloadmanager.js
new file mode 100644
index 0000000000..d390e655dd
--- /dev/null
+++ b/comm/suite/components/downloads/content/downloadmanager.js
@@ -0,0 +1,634 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ PluralForm: "resource://gre/modules/PluralForm.jsm",
+ Downloads: "resource://gre/modules/Downloads.jsm",
+ DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+ PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
+ FileUtils: "resource://gre/modules/FileUtils.jsm",
+});
+
+var gDownloadTree;
+var gDownloadTreeView;
+var gDownloadList;
+var gDownloadStatus;
+var gDownloadListener;
+var gSearchBox;
+
+function dmStartup()
+{
+ Downloads.getList(Downloads.PUBLIC).then(dmAsyncStartup);
+}
+
+function dmAsyncStartup(aList)
+{
+ gDownloadList = aList;
+
+ gDownloadTree = document.getElementById("downloadTree");
+ gDownloadStatus = document.getElementById("statusbar-display");
+ gSearchBox = document.getElementById("search-box");
+
+ // Insert as first controller on the whole window
+ window.controllers.insertControllerAt(0, dlTreeController);
+
+ // We need to keep the view object around globally to access "local"
+ // non-nsITreeView methods
+ gDownloadTreeView = new DownloadTreeView();
+ gDownloadTree.view = gDownloadTreeView;
+
+ // The DownloadProgressListener (DownloadProgressListener.js) handles
+ // progress notifications.
+ gDownloadListener = new DownloadProgressListener();
+ gDownloadList.addView(gDownloadListener);
+
+ // correct keybinding command attributes which don't do our business yet
+ var key = document.getElementById("key_delete");
+ if (key.hasAttribute("command"))
+ key.setAttribute("command", "cmd_stop");
+ key = document.getElementById("key_delete2");
+ if (key.hasAttribute("command"))
+ key.setAttribute("command", "cmd_stop");
+
+ gDownloadTree.focus();
+
+ if (gDownloadTree.view.rowCount > 0)
+ gDownloadTree.view.selection.select(0);
+}
+
+function dmShutdown()
+{
+ gDownloadList.removeView(gDownloadListener);
+ window.controllers.removeController(dlTreeController);
+}
+
+function searchDownloads(aInput)
+{
+ gDownloadTreeView.searchView(aInput);
+}
+
+function sortDownloads(aEventTarget)
+{
+ var column = aEventTarget;
+ var colID = column.id;
+ var sortDirection = null;
+
+ // If the target is a menuitem, handle it and forward to a column
+ if (/^menu_SortBy/.test(colID)) {
+ colID = colID.replace(/^menu_SortBy/, "");
+ column = document.getElementById(colID);
+ var sortedColumn = gDownloadTree.columns.getSortedColumn();
+ if (sortedColumn && sortedColumn.id == colID)
+ sortDirection = sortedColumn.element.getAttribute("sortDirection");
+ else
+ sortDirection = "ascending";
+ }
+ else if (colID == "menu_Unsorted") {
+ // calling .sortView() with an "unsorted" colID returns us to original order
+ colID = "unsorted";
+ column = null;
+ sortDirection = "ascending";
+ }
+ else if (colID == "menu_SortAscending" || colID == "menu_SortDescending") {
+ sortDirection = colID.replace(/^menu_Sort/, "").toLowerCase();
+ var sortedColumn = gDownloadTree.columns.getSortedColumn();
+ if (sortedColumn) {
+ colID = sortedColumn.id;
+ column = sortedColumn.element;
+ }
+ }
+
+ // Abort if this is still no column
+ if (column && column.localName != "treecol")
+ return;
+
+ // Abort on cyler columns, we don't sort them
+ if (column && column.getAttribute("cycler") == "true")
+ return;
+
+ if (!sortDirection) {
+ // If not set above already, toggle the current direction
+ sortDirection = column.getAttribute("sortDirection") == "ascending" ?
+ "descending" : "ascending";
+ }
+
+ // Clear attributes on all columns, we're setting them again after sorting
+ for (let node = document.getElementById("Name"); node; node = node.nextSibling) {
+ node.removeAttribute("sortActive");
+ node.removeAttribute("sortDirection");
+ }
+
+ // Actually sort the tree view
+ gDownloadTreeView.sortView(colID, sortDirection);
+
+ if (column) {
+ // Set attributes to the sorting we did
+ column.setAttribute("sortActive", "true");
+ column.setAttribute("sortDirection", sortDirection);
+ }
+}
+
+async function removeDownload(aDownload)
+{
+ // Remove the associated history element first, if any, so that the views
+ // that combine history and session downloads won't resurrect the history
+ // download into the view just before it is deleted permanently.
+ try {
+ await PlacesUtils.history.remove(aDownload.source.url);
+ } catch (ex) {
+ Cu.reportError(ex);
+ }
+ let list = await Downloads.getList(Downloads.ALL);
+ await list.remove(aDownload);
+ await aDownload.finalize(true);
+}
+
+function cancelDownload(aDownload)
+{
+ // This is the correct way to avoid race conditions when cancelling.
+ aDownload.cancel().catch(() => {});
+ aDownload.removePartialData().catch(Cu.reportError);
+}
+
+function openDownload(aDownload)
+{
+ let file = new FileUtils.File(aDownload.target.path);
+ DownloadsCommon.openDownloadedFile(file, null, window);
+}
+
+function showDownload(aDownload)
+{
+ let file;
+
+ if (aDownload.succeeded &&
+ aDownload.target.exists) {
+ file = new FileUtils.File(aDownload.target.path);
+ } else {
+ file = new FileUtils.File(aDownload.target.partFilePath);
+ }
+ DownloadsCommon.showDownloadedFile(file);
+}
+
+function showProperties(aDownload)
+{
+ openDialog("chrome://communicator/content/downloads/progressDialog.xul",
+ null, "chrome,titlebar,centerscreen,minimizable=yes,dialog=no",
+ { wrappedJSObject: aDownload }, true);
+}
+
+function onTreeSelect(aEvent)
+{
+ var selectionCount = gDownloadTreeView.selection.count;
+ if (selectionCount == 1) {
+ var selItemData = gDownloadTreeView.getRowData(gDownloadTree.currentIndex);
+ gDownloadStatus.label = selItemData.target.path;
+ } else {
+ gDownloadStatus.label = "";
+ }
+
+ window.updateCommands("tree-select");
+}
+
+function onUpdateViewColumns(aMenuItem)
+{
+ while (aMenuItem) {
+ // Each menuitem should be checked if its column is not hidden.
+ var colID = aMenuItem.id.replace(/^menu_Toggle/, "");
+ var column = document.getElementById(colID);
+ aMenuItem.setAttribute("checked", !column.hidden);
+ aMenuItem = aMenuItem.nextSibling;
+ }
+}
+
+function toggleColumn(aMenuItem)
+{
+ var colID = aMenuItem.id.replace(/^menu_Toggle/, "");
+ var column = document.getElementById(colID);
+ column.setAttribute("hidden", !column.hidden);
+}
+
+function onUpdateViewSort(aMenuItem)
+{
+ var unsorted = true;
+ var ascending = true;
+ while (aMenuItem) {
+ switch (aMenuItem.id) {
+ case "": // separator
+ break;
+ case "menu_Unsorted":
+ if (unsorted) // this would work even if Unsorted was last
+ aMenuItem.setAttribute("checked", "true");
+ break;
+ case "menu_SortAscending":
+ aMenuItem.setAttribute("disabled", unsorted);
+ if (!unsorted && ascending)
+ aMenuItem.setAttribute("checked", "true");
+ break;
+ case "menu_SortDescending":
+ aMenuItem.setAttribute("disabled", unsorted);
+ if (!unsorted && !ascending)
+ aMenuItem.setAttribute("checked", "true");
+ break;
+ default:
+ var colID = aMenuItem.id.replace(/^menu_SortBy/, "");
+ var column = document.getElementById(colID);
+ var direction = column.getAttribute("sortDirection");
+ if (column.getAttribute("sortActive") == "true" && direction) {
+ // We've found a sorted column. Remember its direction.
+ ascending = direction == "ascending";
+ unsorted = false;
+ aMenuItem.setAttribute("checked", "true");
+ }
+ }
+ aMenuItem = aMenuItem.nextSibling;
+ }
+}
+
+// This is called by the progress listener.
+var gLastComputedMean = -1;
+var gLastActiveDownloads = 0;
+function onUpdateProgress()
+{
+ var dls = gDownloadTreeView.getActiveDownloads();
+ var numActiveDownloads = dls.length;
+
+ // Use the default title and reset "last" values if there's no downloads
+ if (numActiveDownloads == 0) {
+ document.title = document.documentElement.getAttribute("statictitle");
+ gLastComputedMean = -1;
+ gLastActiveDownloads = 0;
+
+ return;
+ }
+
+ // Establish the mean transfer speed and amount downloaded.
+ var mean = 0;
+ var base = 0;
+ for (var dl of dls) {
+ if (dl.totalBytes > 0) {
+ mean += dl.currentBytes;
+ base += dl.totalBytes;
+ }
+ }
+
+ // Calculate the percent transferred, unless we don't have a total file size
+ var dlbundle = document.getElementById("dmBundle");
+ if (base != 0)
+ mean = Math.floor((mean / base) * 100);
+
+ // Update title of window
+ if (mean != gLastComputedMean || gLastActiveDownloads != numActiveDownloads) {
+ gLastComputedMean = mean;
+ gLastActiveDownloads = numActiveDownloads;
+
+ var title;
+ if (base == 0)
+ title = dlbundle.getFormattedString("downloadsTitleFiles",
+ [numActiveDownloads]);
+ else
+ title = dlbundle.getFormattedString("downloadsTitlePercent",
+ [numActiveDownloads, mean]);
+
+ // Get the correct plural form and insert number of downloads and percent
+ title = PluralForm.get(numActiveDownloads, title);
+
+ document.title = title;
+ }
+}
+
+function handlePaste() {
+ let trans = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+ trans.init(null);
+
+ let flavors = ["text/x-moz-url", "text/unicode"];
+ flavors.forEach(trans.addDataFlavor);
+
+ Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard);
+
+ // Getting the data or creating the nsIURI might fail
+ try {
+ let data = {};
+ trans.getAnyTransferData({}, data, {});
+ let [url, name] = data.value.QueryInterface(Ci
+ .nsISupportsString).data.split("\n");
+
+ if (!url)
+ return;
+
+ DownloadURL(url, name || url, document);
+ } catch (ex) {}
+}
+
+var dlTreeController = {
+ supportsCommand: function(aCommand)
+ {
+ switch (aCommand) {
+ case "cmd_play":
+ case "cmd_pause":
+ case "cmd_resume":
+ case "cmd_retry":
+ case "cmd_cancel":
+ case "cmd_remove":
+ case "cmd_stop":
+ case "cmd_open":
+ case "cmd_show":
+ case "cmd_openReferrer":
+ case "cmd_copyLocation":
+ case "cmd_properties":
+ case "cmd_paste":
+ case "cmd_selectAll":
+ case "cmd_clearList":
+ return true;
+ }
+ return false;
+ },
+
+ isCommandEnabled: function(aCommand)
+ {
+ var selectionCount = 0;
+ if (gDownloadTreeView && gDownloadTreeView.selection)
+ selectionCount = gDownloadTreeView.selection.count;
+
+ var selItemData = [];
+ if (selectionCount) {
+ // walk all selected rows
+ let start = {};
+ let end = {};
+ let numRanges = gDownloadTreeView.selection.getRangeCount();
+ for (let rg = 0; rg < numRanges; rg++) {
+ gDownloadTreeView.selection.getRangeAt(rg, start, end);
+ for (let row = start.value; row <= end.value; row++)
+ selItemData.push(gDownloadTreeView.getRowData(row));
+ }
+ }
+
+ switch (aCommand) {
+ case "cmd_play":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (dldata.succeeded || (!dldata.stopped && !dldata.hasPartialData))
+ return false;
+ }
+ return true;
+ case "cmd_pause":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (dldata.stopped || !dldata.hasPartialData)
+ return false;
+ }
+ return true;
+ case "cmd_resume":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (!dldata.stopped || !dldata.hasPartialData)
+ return false;
+ }
+ return true;
+ case "cmd_open":
+ return selectionCount == 1 &&
+ selItemData[0].succeeded &&
+ selItemData[0].target.exists;
+ case "cmd_show":
+ // target.exists is only set if the download finished and the target
+ // is still located there.
+ // For simplicity we just assume the target is there if the download
+ // has not succeeded e.g. is still in progress. This might be wrong
+ // but showDownload will deal with it.
+ return selectionCount == 1 &&
+ ((selItemData[0].succeeded &&
+ selItemData[0].target.exists) ||
+ !selItemData[0].succeeded);
+ case "cmd_cancel":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (dldata.stopped && !dldata.hasPartialData)
+ return false;
+ }
+ return true;
+ case "cmd_retry":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (dldata.succeeded || !dldata.stopped || dldata.hasPartialData)
+ return false;
+ }
+ return true;
+ case "cmd_remove":
+ if (!selectionCount)
+ return false;
+ for (let dldata of selItemData) {
+ if (!dldata.stopped)
+ return false;
+ }
+ return true;
+ case "cmd_openReferrer":
+ return selectionCount == 1 && !!selItemData[0].source.referrer;
+ case "cmd_stop":
+ case "cmd_copyLocation":
+ return selectionCount > 0;
+ case "cmd_properties":
+ return selectionCount == 1;
+ case "cmd_selectAll":
+ return gDownloadTreeView.rowCount != selectionCount;
+ case "cmd_clearList":
+ // Since active downloads always sort before removable downloads,
+ // we only need to check that the last download has stopped.
+ return gDownloadTreeView.rowCount &&
+ !gDownloadTreeView.getRowData(gDownloadTreeView.rowCount - 1).isActive;
+ case "cmd_paste":
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function(aCommand) {
+ var selectionCount = 0;
+ if (gDownloadTreeView && gDownloadTreeView.selection)
+ selectionCount = gDownloadTreeView.selection.count;
+
+ var selItemData = [];
+ if (selectionCount) {
+ // walk all selected rows
+ let start = {};
+ let end = {};
+ let numRanges = gDownloadTreeView.selection.getRangeCount();
+ for (let rg = 0; rg < numRanges; rg++) {
+ gDownloadTreeView.selection.getRangeAt(rg, start, end);
+ for (let row = start.value; row <= end.value; row++)
+ selItemData.push(gDownloadTreeView.getRowData(row));
+ }
+ }
+
+ switch (aCommand) {
+ case "cmd_play":
+ for (let dldata of selItemData) {
+ if (!dldata.stopped)
+ dldata.cancel();
+ else if (!dldata.succeeded)
+ dldata.start();
+ }
+ break;
+ case "cmd_pause":
+ for (let dldata of selItemData)
+ dldata.cancel();
+ break;
+ case "cmd_resume":
+ case "cmd_retry":
+ for (let dldata of selItemData) {
+ // Errors when retrying are already reported as download failures.
+ dldata.start();
+ }
+ break;
+ case "cmd_cancel":
+ for (let dldata of selItemData)
+ cancelDownload(dldata);
+ break;
+ case "cmd_remove":
+ for (let dldata of selItemData)
+ removeDownload(dldata).catch(Cu.reportError);
+ break;
+ case "cmd_stop":
+ for (let dldata of selItemData) {
+ if (dldata.isActive)
+ cancelDownload(dldata);
+ else
+ gDownloadList.remove(dldata);
+ }
+ break;
+ case "cmd_open":
+ openDownload(selItemData[0]);
+ break;
+ case "cmd_show":
+ showDownload(selItemData[0]);
+ break;
+ case "cmd_openReferrer":
+ openUILink(selItemData[0].source.referrer);
+ break;
+ case "cmd_copyLocation":
+ var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper);
+ var uris = [];
+ for (let dldata of selItemData)
+ uris.push(dldata.source.url);
+ clipboard.copyString(uris.join("\n"), document);
+ break;
+ case "cmd_properties":
+ showProperties(selItemData[0]);
+ break;
+ case "cmd_selectAll":
+ gDownloadTreeView.selection.selectAll();
+ break;
+ case "cmd_clearList":
+ // Remove each download starting from the end until we hit a download
+ // that is in progress
+ for (let idx = gDownloadTreeView.rowCount - 1; idx >= 0; idx--) {
+ let dldata = gDownloadTreeView.getRowData(idx);
+ if (!dldata.isActive) {
+ gDownloadList.remove(dldata);
+ }
+ }
+
+ if (!gSearchBox.value)
+ break;
+
+ // Clear the input as if the user did it and move focus to the list
+ gSearchBox.value = "";
+ searchDownloads("");
+ gDownloadTree.focus();
+ break;
+ case "cmd_paste":
+ handlePaste();
+ break;
+ }
+ },
+
+ onEvent: function(aEvent){
+ switch (aEvent) {
+ case "tree-select":
+ this.onCommandUpdate();
+ }
+ },
+
+ onCommandUpdate: function() {
+ var cmds = ["cmd_play", "cmd_pause", "cmd_resume", "cmd_retry",
+ "cmd_cancel", "cmd_remove", "cmd_stop", "cmd_open", "cmd_show",
+ "cmd_openReferrer", "cmd_copyLocation", "cmd_properties",
+ "cmd_selectAll", "cmd_clearList"];
+ for (let command in cmds)
+ goUpdateCommand(cmds[command]);
+ }
+};
+
+var gDownloadDNDObserver = {
+ onDragStart: function (aEvent)
+ {
+ if (!gDownloadTreeView ||
+ !gDownloadTreeView.selection ||
+ !gDownloadTreeView.selection.count)
+ return;
+
+ var selItemData = gDownloadTreeView.getRowData(gDownloadTree.currentIndex);
+ var file = new FileUtils.File(selItemData.target.path);
+
+ if (!file.exists())
+ return;
+
+ var url = Services.io.newFileURI(file).spec;
+ var dt = aEvent.dataTransfer;
+ dt.mozSetDataAt("application/x-moz-file", file, 0);
+ dt.setData("text/uri-list", url + "\r\n");
+ dt.setData("text/plain", url + "\n");
+ dt.effectAllowed = "copyMove";
+ if (gDownloadTreeView.selection.count == 1)
+ dt.setDragImage(gDownloadStatus, 16, 16);
+ },
+
+ onDragOver: function (aEvent)
+ {
+ if (disallowDrop(aEvent))
+ return;
+
+ var types = aEvent.dataTransfer.types;
+ if (types.includes("text/uri-list") ||
+ types.includes("text/x-moz-url") ||
+ types.includes("text/plain"))
+ aEvent.preventDefault();
+ aEvent.stopPropagation();
+ },
+
+ onDrop: function(aEvent)
+ {
+ if (disallowDrop(aEvent))
+ return;
+
+ var dt = aEvent.dataTransfer;
+ var url = dt.getData("URL");
+ var name;
+ if (!url) {
+ url = dt.getData("text/x-moz-url") || dt.getData("text/plain");
+ [url, name] = url.split("\n");
+ }
+ if (url) {
+ let doc = dt.mozSourceNode ? dt.mozSourceNode.ownerDocument : document;
+ saveURL(url, name || url, null, true, true, null, doc);
+ }
+ }
+};
+
+function disallowDrop(aEvent)
+{
+ var dt = aEvent.dataTransfer;
+ var file = dt.mozGetDataAt("application/x-moz-file", 0);
+ // If this is a local file, Don't try to download it again.
+ return file && file instanceof Ci.nsIFile;
+}
diff --git a/comm/suite/components/downloads/content/downloadmanager.xul b/comm/suite/components/downloads/content/downloadmanager.xul
new file mode 100644
index 0000000000..5633d284b6
--- /dev/null
+++ b/comm/suite/components/downloads/content/downloadmanager.xul
@@ -0,0 +1,452 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/downloads/downloadmanager.css" type="text/css"?>
+
+<?xul-overlay href="chrome://communicator/content/tasksOverlay.xul"?>
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % downloadsDTD SYSTEM "chrome://communicator/locale/downloads/downloadmanager.dtd">
+%downloadsDTD;
+<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+%globalDTD;
+]>
+
+<window id="downloadManager"
+ title="&downloadManager.title;" statictitle="&downloadManager.title;"
+ onload="dmStartup();" onunload="dmShutdown();"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="500" height="400" screenX="10" screenY="10"
+ persist="width height screenX screenY sizemode"
+ toggletoolbar="true"
+ lightweightthemes="true"
+ lightweightthemesfooter="status-bar"
+ drawtitle="true"
+ windowtype="Download:Manager">
+
+ <script src="chrome://communicator/content/downloads/downloadmanager.js"/>
+ <script src="chrome://communicator/content/downloads/DownloadProgressListener.js"/>
+ <script src="chrome://communicator/content/downloads/treeView.js"/>
+ <script src="chrome://global/content/contentAreaUtils.js"/>
+ <script src="chrome://global/content/editMenuOverlay.js"/>
+
+ <broadcaster id="Communicator:WorkMode"/>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="dmBundle"
+ src="chrome://communicator/locale/downloads/downloadmanager.properties"/>
+ </stringbundleset>
+
+ <commandset id="dlWinCommands">
+ <commandset id="tasksCommands">
+ <!-- File Menu -->
+ <command id="cmd_close" oncommand="window.close()"/>
+ <!-- Search Box -->
+ <command id="cmd_search_focus"
+ oncommand="gSearchBox.focus();"/>
+ </commandset>
+ <commandset id="commandUpdate_Downloads"
+ commandupdater="true"
+ events="focus,tree-select"
+ oncommandupdate="dlTreeController.onCommandUpdate()"/>
+
+ <commandset id="downloadCommands">
+ <command id="cmd_play"
+ oncommand="goDoCommand('cmd_play');"/>
+ <command id="cmd_pause"
+ oncommand="goDoCommand('cmd_pause');"/>
+ <command id="cmd_resume"
+ oncommand="goDoCommand('cmd_resume');"/>
+ <command id="cmd_retry"
+ oncommand="goDoCommand('cmd_retry');"/>
+ <command id="cmd_cancel"
+ oncommand="goDoCommand('cmd_cancel');"/>
+ <command id="cmd_remove"
+ oncommand="goDoCommand('cmd_remove');"/>
+ <command id="cmd_stop"
+ oncommand="goDoCommand('cmd_stop');"/>
+ <command id="cmd_open"
+ oncommand="goDoCommand('cmd_open');"/>
+ <command id="cmd_show"
+ oncommand="goDoCommand('cmd_show');"/>
+ <command id="cmd_openReferrer"
+ oncommand="goDoCommand('cmd_openReferrer');"/>
+ <command id="cmd_copyLocation"
+ oncommand="goDoCommand('cmd_copyLocation');"/>
+ <command id="cmd_properties"
+ oncommand="goDoCommand('cmd_properties');"/>
+ <command id="cmd_clearList"
+ oncommand="goDoCommand('cmd_clearList');"/>
+ </commandset>
+ </commandset>
+
+ <keyset id="tasksKeys">
+ <!-- File Menu -->
+ <key id="key_open"
+ keycode="VK_RETURN"
+ command="cmd_open"/>
+ <key id="key_close"/>
+ <!-- Edit Menu -->
+ <key id="key_cut"/>
+ <key id="key_copy"/>
+ <key id="key_paste"
+ command="cmd_paste"/>
+ <key id="key_play"
+ key=" "
+ command="cmd_play"/>
+ <key id="key_delete"/>
+ <key id="key_delete2"/>
+ <key id="key_selectAll"/>
+ <!-- Search Box -->
+ <key id="key_search_focus"
+ command="cmd_search_focus"
+ key="&search.key;"
+ modifiers="accel"/>
+ </keyset>
+
+ <popupset id="downloadPopupset">
+ <menupopup id="downloadContext">
+ <menuitem id="dlContext-pause"
+ label="&cmd.pause.label;"
+ accesskey="&cmd.pause.accesskey;"
+ command="cmd_pause"/>
+ <menuitem id="dlContext-resume"
+ label="&cmd.resume.label;"
+ accesskey="&cmd.resume.accesskey;"
+ command="cmd_resume"/>
+ <menuitem id="dlContext-retry"
+ label="&cmd.retry.label;"
+ accesskey="&cmd.retry.accesskey;"
+ command="cmd_retry"/>
+ <menuitem id="dlContext-cancel"
+ label="&cmd.cancel.label;"
+ accesskey="&cmd.cancel.accesskey;"
+ command="cmd_cancel"/>
+ <menuitem id="dlContext-remove"
+ label="&cmd.remove.label;"
+ accesskey="&cmd.remove.accesskey;"
+ command="cmd_remove"/>
+ <menuseparator/>
+ <menuitem id="dlContext-open"
+ label="&cmd.open.label;"
+ accesskey="&cmd.open.accesskey;"
+ command="cmd_open"
+ default="true"/>
+ <menuitem id="dlContext-show"
+ label="&cmd.show.label;"
+ accesskey="&cmd.show.accesskey;"
+ command="cmd_show"/>
+ <menuitem id="dlContext-openReferrer"
+ label="&cmd.goToDownloadPage.label;"
+ accesskey="&cmd.goToDownloadPage.accesskey;"
+ command="cmd_openReferrer"/>
+ <menuitem id="dlContext-copyLocation"
+ label="&cmd.copyDownloadLink.label;"
+ accesskey="&cmd.copyDownloadLink.accesskey;"
+ command="cmd_copyLocation"/>
+ <menuitem id="dlContext-properties"
+ label="&cmd.properties.label;"
+ accesskey="&cmd.properties.accesskey;"
+ command="cmd_properties"/>
+ <menuseparator/>
+ <menuitem id="context-selectall"/>
+ </menupopup>
+ </popupset>
+
+ <vbox id="titlebar"/>
+
+ <toolbox id="download-toolbox">
+ <menubar id="download-menubar"
+ grippytooltiptext="&menuBar.tooltip;">
+ <menu id="menu_File">
+ <menupopup id="menu_FilePopup">
+ <menuitem id="dlMenu_open"
+ label="&cmd.open.label;"
+ accesskey="&cmd.open.accesskey;"
+ key="key_open"
+ command="cmd_open"/>
+ <menuitem id="dlMenu_show"
+ label="&cmd.show.label;"
+ accesskey="&cmd.show.accesskey;"
+ command="cmd_show"/>
+ <menuitem id="dlMenu_openReferrer"
+ label="&cmd.goToDownloadPage.label;"
+ accesskey="&cmd.goToDownloadPage.accesskey;"
+ command="cmd_openReferrer"/>
+ <menuitem id="dlMenu_properties"
+ label="&cmd.properties.label;"
+ accesskey="&cmd.properties.accesskey;"
+ command="cmd_properties"/>
+ <menuseparator/>
+ <menuitem id="menu_close"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_Edit">
+ <menupopup id="menu_EditPopup">
+ <menuitem id="dlMenu_pause"
+ label="&cmd.pause.label;"
+ accesskey="&cmd.pause.accesskey;"
+ command="cmd_pause"/>
+ <menuitem id="dlMenu_resume"
+ label="&cmd.resume.label;"
+ accesskey="&cmd.resume.accesskey;"
+ command="cmd_resume"/>
+ <menuitem id="dlMenu_retry"
+ label="&cmd.retry.label;"
+ accesskey="&cmd.retry.accesskey;"
+ command="cmd_retry"/>
+ <menuitem id="dlMenu_cancel"
+ label="&cmd.cancel.label;"
+ accesskey="&cmd.cancel.accesskey;"
+ command="cmd_cancel"/>
+ <menuseparator/>
+ <menuitem id="dlMenu_remove"
+ label="&cmd.remove.label;"
+ accesskey="&cmd.remove.accesskey;"
+ command="cmd_remove"/>
+ <menuitem id="dlMenu_copyLocation"
+ label="&cmd.copyDownloadLink.label;"
+ accesskey="&cmd.copyDownloadLink.accesskey;"
+ command="cmd_copyLocation"/>
+ <menuseparator/>
+ <menuitem id="dlMenu_clearList"
+ label="&cmd.clearList.label;"
+ accesskey="&cmd.clearList.accesskey;"
+ command="cmd_clearList"/>
+ <menuitem id="menu_selectAll"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_View">
+ <menupopup id="menu_ViewPopup">
+ <menu id="menu_ViewColumns"
+ label="&view.columns.label;"
+ accesskey="&view.columns.accesskey;">
+ <menupopup onpopupshowing="onUpdateViewColumns(this.firstChild);"
+ oncommand="toggleColumn(event.target);">
+ <menuitem id="menu_ToggleName" type="checkbox" disabled="true"
+ label="&col.name.label;"
+ accesskey="&col.name.accesskey;"/>
+ <menuitem id="menu_ToggleStatus" type="checkbox"
+ label="&col.status.label;"
+ accesskey="&col.status.accesskey;"/>
+ <menuitem id="menu_ToggleActionPlay" type="checkbox"
+ label="&col.actionPlay.label;"
+ accesskey="&col.actionPlay.accesskey;"/>
+ <menuitem id="menu_ToggleActionStop" type="checkbox"
+ label="&col.actionStop.label;"
+ accesskey="&col.actionStop.accesskey;"/>
+ <menuitem id="menu_ToggleProgress" type="checkbox"
+ label="&col.progress.label;"
+ accesskey="&col.progress.accesskey;"/>
+ <menuitem id="menu_ToggleTimeRemaining" type="checkbox"
+ label="&col.timeremaining.label;"
+ accesskey="&col.timeremaining.accesskey;"/>
+ <menuitem id="menu_ToggleTransferred" type="checkbox"
+ label="&col.transferred.label;"
+ accesskey="&col.transferred.accesskey;"/>
+ <menuitem id="menu_ToggleTransferRate" type="checkbox"
+ label="&col.transferrate.label;"
+ accesskey="&col.transferrate.accesskey;"/>
+ <menuitem id="menu_ToggleTimeElapsed" type="checkbox"
+ label="&col.timeelapsed.label;"
+ accesskey="&col.timeelapsed.accesskey;"/>
+ <menuitem id="menu_ToggleStartTime" type="checkbox"
+ label="&col.starttime.label;"
+ accesskey="&col.starttime.accesskey;"/>
+ <menuitem id="menu_ToggleEndTime" type="checkbox"
+ label="&col.endtime.label;"
+ accesskey="&col.endtime.accesskey;"/>
+ <menuitem id="menu_ToggleProgressPercent" type="checkbox"
+ label="&col.progresstext.label;"
+ accesskey="&col.progresstext.accesskey;"/>
+ <menuitem id="menu_ToggleSource" type="checkbox"
+ label="&col.source.label;"
+ accesskey="&col.source.accesskey;"/>
+ </menupopup>
+ </menu>
+ <menu id="menu_ViewSortBy" label="&view.sortBy.label;"
+ accesskey="&view.sortBy.accesskey;">
+ <menupopup onpopupshowing="onUpdateViewSort(this.firstChild);"
+ oncommand="sortDownloads(event.target);">
+ <menuitem id="menu_Unsorted" type="radio" name="columns"
+ label="&view.unsorted.label;"
+ accesskey="&view.unsorted.accesskey;"/>
+ <menuseparator/>
+ <menuitem id="menu_SortByName" type="radio" name="columns"
+ label="&col.name.label;"
+ accesskey="&col.name.accesskey;"/>
+ <menuitem id="menu_SortByStatus" type="radio" name="columns"
+ label="&col.status.label;"
+ accesskey="&col.status.accesskey;"/>
+ <menuitem id="menu_SortByProgress" type="radio" name="columns"
+ label="&col.progress.label;"
+ accesskey="&col.progress.accesskey;"/>
+ <menuitem id="menu_SortByTimeRemaining" type="radio" name="columns"
+ label="&col.timeremaining.label;"
+ accesskey="&col.timeremaining.accesskey;"/>
+ <menuitem id="menu_SortByTransferred" type="radio" name="columns"
+ label="&col.transferred.label;"
+ accesskey="&col.transferred.accesskey;"/>
+ <menuitem id="menu_SortByTransferRate" type="radio" name="columns"
+ label="&col.transferrate.label;"
+ accesskey="&col.transferrate.accesskey;"/>
+ <menuitem id="menu_SortByTimeElapsed" type="radio" name="columns"
+ label="&col.timeelapsed.label;"
+ accesskey="&col.timeelapsed.accesskey;"/>
+ <menuitem id="menu_SortByStartTime" type="radio" name="columns"
+ label="&col.starttime.label;"
+ accesskey="&col.starttime.accesskey;"/>
+ <menuitem id="menu_SortByEndTime" type="radio" name="columns"
+ label="&col.endtime.label;"
+ accesskey="&col.endtime.accesskey;"/>
+ <menuitem id="menu_SortByProgressPercent" type="radio" name="columns"
+ label="&col.progresstext.label;"
+ accesskey="&col.progresstext.accesskey;"/>
+ <menuitem id="menu_SortBySource" type="radio" name="columns"
+ label="&col.source.label;"
+ accesskey="&col.source.accesskey;"/>
+ <menuseparator/>
+ <menuitem id="menu_SortAscending" type="radio" name="direction"
+ label="&view.sortAscending.label;"
+ accesskey="&view.sortAscending.accesskey;"/>
+ <menuitem id="menu_SortDescending" type="radio" name="direction"
+ label="&view.sortDescending.label;"
+ accesskey="&view.sortDescending.accesskey;"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ <menu id="tasksMenu">
+ <menupopup id="taskPopup">
+ <menuitem id="dlMenu_find"
+ label="&search.label;"
+ accesskey="&search.accesskey;"
+ hidden="true"
+ command="cmd_search_focus"
+ key="key_search_focus"/>
+ <menuseparator hidden="true"/>
+ </menupopup>
+ </menu>
+ <menu id="windowMenu"/>
+ <menu id="menu_Help"/>
+ </menubar>
+ <toolbar class="chromeclass-toolbar"
+ id="downloadToolbar"
+ align="center"
+ grippytooltiptext="&searchBar.tooltip;">
+ <textbox id="search-box"
+ clickSelectsAll="true"
+ type="search"
+ hidden="true"
+ aria-controls="downloadTree"
+ class="compact"
+ placeholder="&search.placeholder;"
+ oncommand="searchDownloads(this.value);"/>
+ <spacer flex="1"/>
+ <button id="clearListButton" command="cmd_clearList"
+ label="&cmd.clearList.label;"
+ accesskey="&cmd.clearList.accesskey;"
+ tooltiptext="&cmd.clearList.tooltip;"/>
+ </toolbar>
+ </toolbox>
+
+ <tree id="downloadTree"
+ flex="1" type="downloads"
+ class="plain"
+ context="downloadContext"
+ enableColumnDrag="true"
+ onselect="onTreeSelect(event);">
+ <treecols context="" onclick="sortDownloads(event.target)">
+ <treecol id="Name"
+ label="&col.name.label;"
+ tooltiptext="&col.name.tooltip;"
+ flex="3"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Status" hidden="true"
+ label="&col.status.label;"
+ tooltiptext="&col.status.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="ActionPlay" cycler="true"
+ label="&col.actionPlay.label;"
+ tooltiptext="&col.actionPlay.tooltip;"
+ class="treecol-image" fixed="true"
+ persist="hidden ordinal"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="ActionStop" cycler="true"
+ label="&col.actionStop.label;"
+ tooltiptext="&col.actionStop.tooltip;"
+ class="treecol-image" fixed="true"
+ persist="hidden ordinal"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Progress" type="progressmeter"
+ label="&col.progress.label;"
+ tooltiptext="&col.progress.tooltip;"
+ flex="3"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="ProgressPercent" hidden="true"
+ label="&col.progresstext.label;"
+ tooltiptext="&col.progresstext.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="TimeRemaining"
+ label="&col.timeremaining.label;"
+ tooltiptext="&col.timeremaining.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Transferred"
+ label="&col.transferred.label;"
+ tooltiptext="&col.transferred.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="TransferRate"
+ label="&col.transferrate.label;"
+ tooltiptext="&col.transferrate.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="TimeElapsed" hidden="true"
+ label="&col.timeelapsed.label;"
+ tooltiptext="&col.timeelapsed.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="StartTime" hidden="true"
+ label="&col.starttime.label;"
+ tooltiptext="&col.starttime.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="EndTime" hidden="true"
+ label="&col.endtime.label;"
+ tooltiptext="&col.endtime.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="Source" hidden="true"
+ label="&col.source.label;"
+ tooltiptext="&col.source.tooltip;"
+ flex="1"
+ persist="width hidden ordinal sortActive sortDirection"/>
+ </treecols>
+ <treechildren ondblclick="goDoCommand('cmd_open');"
+ ondragstart="gDownloadDNDObserver.onDragStart(event);"
+ ondragover="gDownloadDNDObserver.onDragOver(event);"
+ ondrop="gDownloadDNDObserver.onDrop(event);"/>
+ </tree>
+ <statusbar id="status-bar" class="chromeclass-status">
+ <statusbarpanel id="statusbar-display" flex="1"/>
+ <statusbarpanel class="statusbarpanel-iconic" id="offline-status"/>
+ </statusbar>
+
+</window>
diff --git a/comm/suite/components/downloads/content/progressDialog.js b/comm/suite/components/downloads/content/progressDialog.js
new file mode 100644
index 0000000000..dae93f7fe9
--- /dev/null
+++ b/comm/suite/components/downloads/content/progressDialog.js
@@ -0,0 +1,240 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
+ DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+});
+
+var gDownload;
+var gDownloadBundle;
+
+var gDlList;
+var gDlStatus;
+var gDlListener;
+var gDlSize;
+var gTimeLeft;
+var gProgressMeter;
+var gProgressText;
+var gCloseWhenDone;
+
+function progressStartup() {
+ gDownload = window.arguments[0].wrappedJSObject;
+ Downloads.getList(gDownload.source.isPrivate ? Downloads.PRIVATE : Downloads.PUBLIC).then(progressAsyncStartup);
+}
+
+function progressAsyncStartup(aList) {
+ gDlList = aList;
+
+ // cache elements to save .getElementById() calls
+ gDownloadBundle = document.getElementById("dmBundle");
+ gDlStatus = document.getElementById("dlStatus");
+ gDlSize = document.getElementById("dlSize");
+ gTimeLeft = document.getElementById("timeLeft");
+ gProgressMeter = document.getElementById("progressMeter");
+ gProgressText = document.getElementById("progressText");
+ gCloseWhenDone = document.getElementById("closeWhenDone");
+
+ // Insert as first controller on the whole window
+ window.controllers.insertControllerAt(0, ProgressDlgController);
+
+ if (gDownload.isPrivate)
+ gCloseWhenDone.hidden = true;
+ else
+ gCloseWhenDone.checked = Services.prefs.getBoolPref("browser.download.progress.closeWhenDone");
+
+ if (gDownload.succeeded) {
+ if (gCloseWhenDone.checked && !window.arguments[1])
+ window.close();
+ }
+
+ var fName = document.getElementById("fileName");
+ var fSource = document.getElementById("fileSource");
+ fName.label = gDownload.displayName;
+ fName.tooltipText = gDownload.target.path;
+ var uri = Services.io.newURI(gDownload.source.url);
+ var fromString;
+ try {
+ fromString = uri.host;
+ }
+ catch (e) { }
+ if (!fromString)
+ fromString = uri.prePath;
+ fSource.label = gDownloadBundle.getFormattedString("fromSource", [fromString]);
+ fSource.tooltipText = gDownload.source.url;
+
+ // The DlProgressListener handles progress notifications.
+ gDlListener = new DlProgressListener();
+ gDlList.addView(gDlListener);
+
+ updateDownload();
+ updateButtons();
+ window.updateCommands("dlstate-change");
+}
+
+function progressShutdown() {
+ gDlList.removeView(gDlListener);
+ window.controllers.removeController(ProgressDlgController);
+ if (!gCloseWhenDone.hidden)
+ Services.prefs.setBoolPref("browser.download.progress.closeWhenDone",
+ gCloseWhenDone.checked);
+}
+
+function updateDownload() {
+ if (gDownload.hasProgress) {
+ gProgressText.value = gDownloadBundle.getFormattedString("percentFormat",
+ [gDownload.progress]);
+ gProgressText.hidden = false;
+ gProgressMeter.value = gDownload.progress;
+ gProgressMeter.mode = "determined";
+ } else {
+ gProgressText.hidden = true;
+ gProgressMeter.mode = "undetermined";
+ }
+ if (gDownload.stopped) {
+ gProgressMeter.style.opacity = 0.5;
+ } else {
+ gProgressMeter.style.opacity = 1;
+ }
+ // Update window title
+ let statusString = DownloadsCommon.stateOfDownloadText(gDownload);
+
+ if (gDownload.hasProgress) {
+ document.title = gDownloadBundle.getFormattedString("progressTitlePercent",
+ [gDownload.progress,
+ gDownload.displayName,
+ statusString]);
+ }
+ else {
+ document.title = gDownloadBundle.getFormattedString("progressTitle",
+ [gDownload.displayName,
+ statusString]);
+ }
+
+ // download size / transferred bytes
+ gDlSize.value = DownloadsCommon.getTransferredBytes(gDownload);
+
+ // time remaining
+ gTimeLeft.value = DownloadsCommon.getTimeRemaining(gDownload);
+
+ // download status
+ gDlStatus.value = statusString;
+
+}
+
+function updateButtons() {
+ document.getElementById("pauseButton").hidden = !ProgressDlgController.isCommandEnabled("cmd_pause");
+ document.getElementById("resumeButton").hidden = !ProgressDlgController.isCommandEnabled("cmd_resume");
+ document.getElementById("retryButton").hidden = !ProgressDlgController.isCommandEnabled("cmd_retry");
+ document.getElementById("cancelButton").hidden = !ProgressDlgController.isCommandEnabled("cmd_cancel");
+}
+
+/**
+ * DlProgressListener "class" is used to help update download items shown
+ * in the progress dialog such as displaying amount transferred, transfer
+ * rate, and time left for the download.
+ *
+ * This class implements the downloadProgressListener interface.
+ */
+function DlProgressListener() {}
+
+DlProgressListener.prototype = {
+ onDownloadChanged: function(aDownload) {
+ if (aDownload == gDownload) {
+ if (gCloseWhenDone.checked && aDownload.succeeded) {
+ window.close();
+ }
+ updateDownload();
+ updateButtons();
+ window.updateCommands("dlstate-change");
+ }
+ },
+
+ onDownloadRemoved: function(aDownload) {
+ if (aDownload == gDownload)
+ window.close();
+ }
+};
+
+var ProgressDlgController = {
+ supportsCommand: function(aCommand) {
+ switch (aCommand) {
+ case "cmd_pause":
+ case "cmd_resume":
+ case "cmd_retry":
+ case "cmd_cancel":
+ case "cmd_open":
+ case "cmd_show":
+ case "cmd_openReferrer":
+ case "cmd_copyLocation":
+ return true;
+ }
+ return false;
+ },
+
+ isCommandEnabled: function(aCommand) {
+ switch (aCommand) {
+ case "cmd_pause":
+ return !gDownload.stopped && gDownload.hasPartialData;
+ case "cmd_resume":
+ return gDownload.stopped && gDownload.hasPartialData;
+ case "cmd_open":
+ return gDownload.succeeded && gDownload.target.exists;
+ case "cmd_show":
+ return gDownload.target.exists;
+ case "cmd_cancel":
+ return !gDownload.stopped || gDownload.hasPartialData;
+ case "cmd_retry":
+ return !gDownload.succeeded && gDownload.stopped && !gDownload.hasPartialData;
+ case "cmd_openReferrer":
+ return !!gDownload.source.referrer;
+ case "cmd_copyLocation":
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ doCommand: function(aCommand) {
+ switch (aCommand) {
+ case "cmd_pause":
+ gDownload.cancel();
+ break;
+ case "cmd_resume":
+ case "cmd_retry":
+ gDownload.start();
+ break;
+ case "cmd_cancel":
+ cancelDownload(gDownload);
+ break;
+ case "cmd_open":
+ openDownload(gDownload);
+ break;
+ case "cmd_show":
+ showDownload(gDownload);
+ break;
+ case "cmd_openReferrer":
+ openUILink(gDownload.source.referrer);
+ break;
+ case "cmd_copyLocation":
+ var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper);
+ clipboard.copyString(gDownload.source.url);
+ break;
+ }
+ },
+
+ onEvent: function(aEvent) {
+ },
+
+ onCommandUpdate: function() {
+ var cmds = ["cmd_pause", "cmd_resume", "cmd_retry", "cmd_cancel",
+ "cmd_open", "cmd_show", "cmd_openReferrer", "cmd_copyLocation"];
+ for (let command in cmds)
+ goUpdateCommand(cmds[command]);
+ }
+};
diff --git a/comm/suite/components/downloads/content/progressDialog.xul b/comm/suite/components/downloads/content/progressDialog.xul
new file mode 100644
index 0000000000..cb8178f6fd
--- /dev/null
+++ b/comm/suite/components/downloads/content/progressDialog.xul
@@ -0,0 +1,108 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://communicator/skin/downloads/downloadmanager.css" type="text/css"?>
+
+<?xul-overlay href="chrome://communicator/content/utilityOverlay.xul"?>
+
+<!DOCTYPE window SYSTEM "chrome://communicator/locale/downloads/progressDialog.dtd">
+
+<window id="dlProgressWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ onload="progressStartup();" onunload="progressShutdown();"
+ title="&progress.title;"
+ persist="screenX screenY"
+ style="width:40em;">
+
+ <script src="chrome://communicator/content/downloads/downloadmanager.js"/>
+ <script src="chrome://communicator/content/downloads/progressDialog.js"/>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="dmBundle"
+ src="chrome://communicator/locale/downloads/downloadmanager.properties"/>
+ </stringbundleset>
+
+ <commandset id="dlProgressCommands">
+ <commandset id="commandUpdate_DlProgress"
+ commandupdater="true"
+ events="focus,dlstate-change"
+ oncommandupdate="ProgressDlgController.onCommandUpdate();"/>
+
+ <commandset id="downloadCommands">
+ <command id="cmd_pause"
+ oncommand="goDoCommand('cmd_pause');"/>
+ <command id="cmd_resume"
+ oncommand="goDoCommand('cmd_resume');"/>
+ <command id="cmd_retry"
+ oncommand="goDoCommand('cmd_retry');"/>
+ <command id="cmd_cancel"
+ oncommand="goDoCommand('cmd_cancel');"/>
+ <command id="cmd_open"
+ oncommand="goDoCommand('cmd_open');"/>
+ <command id="cmd_show"
+ oncommand="goDoCommand('cmd_show');"/>
+ <command id="cmd_openReferrer"
+ oncommand="goDoCommand('cmd_openReferrer');"/>
+ <command id="cmd_copyLocation"
+ oncommand="goDoCommand('cmd_copyLocation');"/>
+ <command id="cmd_close" oncommand="window.close();"/>
+ </commandset>
+ </commandset>
+
+ <keyset>
+ <key key="&closeWindow.key;" modifiers="accel" command="cmd_close"/>
+ <key keycode="VK_ESCAPE" command="cmd_close"/>
+ <key key="." modifiers="meta" command="cmd_close"/>
+ </keyset>
+
+ <hbox align="end">
+ <vbox flex="1" align="start">
+ <button id="fileName" crop="center" label="" type="menu">
+ <menupopup id="file-popup">
+ <menuitem id="dlContext-open"
+ label="&cmd.open.label;"
+ accesskey="&cmd.open.accesskey;"
+ command="cmd_open"/>
+ <menuitem id="dlContext-show"
+ label="&cmd.show.label;"
+ accesskey="&cmd.show.accesskey;"
+ command="cmd_show"/>
+ </menupopup>
+ </button>
+ <button id="fileSource" crop="center" label="" type="menu">
+ <menupopup id="source-popup">
+ <menuitem id="dlContext-openReferrer"
+ label="&cmd.goToDownloadPage.label;"
+ accesskey="&cmd.goToDownloadPage.accesskey;"
+ command="cmd_openReferrer"/>
+ <menuitem id="dlContext-copyLocation"
+ label="&cmd.copyDownloadLink.label;"
+ accesskey="&cmd.copyDownloadLink.accesskey;"
+ command="cmd_copyLocation"/>
+ </menupopup>
+ </button>
+ <label id="dlSize" value=""/>
+ <label id="timeLeft" value=""/>
+ <label id="dlStatus" value=""/>
+ </vbox>
+ <button id="pauseButton" class="mini-button"
+ command="cmd_pause" tooltiptext="&cmd.pause.tooltip;"/>
+ <button id="resumeButton" class="mini-button"
+ command="cmd_resume" tooltiptext="&cmd.resume.tooltip;"/>
+ <button id="retryButton" class="mini-button"
+ command="cmd_retry" tooltiptext="&cmd.retry.tooltip;"/>
+ <button id="cancelButton" class="mini-button"
+ command="cmd_cancel" tooltiptext="&cmd.cancel.tooltip;"/>
+ </hbox>
+ <hbox id="progressBox">
+ <progressmeter id="progressMeter" mode="determined" flex="1"/>
+ <label id="progressText" value=""/>
+ </hbox>
+ <checkbox id="closeWhenDone"
+ label="&closeWhenDone.label;"
+ accesskey="&closeWhenDone.accesskey;"/>
+</window>
diff --git a/comm/suite/components/downloads/content/treeView.js b/comm/suite/components/downloads/content/treeView.js
new file mode 100644
index 0000000000..03e1c48a11
--- /dev/null
+++ b/comm/suite/components/downloads/content/treeView.js
@@ -0,0 +1,483 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
+ DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+ DownloadHistory: "resource://gre/modules/DownloadHistory.jsm",
+});
+
+function DownloadTreeView() {
+ this._dlList = [];
+ this._searchTerms = [];
+ this.dateTimeFormatter =
+ new Services.intl.DateTimeFormat(undefined,
+ {dateStyle: "short",
+ timeStyle: "long"});
+}
+
+DownloadTreeView.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITreeView]),
+
+ // ***** nsITreeView attributes and methods *****
+ get rowCount() {
+ return this._dlList.length;
+ },
+
+ selection: null,
+
+ getRowProperties: function(aRow) {
+ let dl = this._dlList[aRow];
+ // (in)active
+ let properties = dl.isActive ? "active": "inactive";
+ // resumable
+ if (dl.hasPartialData)
+ properties += " resumable";
+
+ // Download states
+ let state = DownloadsCommon.stateOfDownload(dl);
+ switch (state) {
+ case DownloadsCommon.DOWNLOAD_PAUSED:
+ properties += " paused";
+ break;
+ case DownloadsCommon.DOWNLOAD_DOWNLOADING:
+ properties += " downloading";
+ break;
+ case DownloadsCommon.DOWNLOAD_FINISHED:
+ properties += " finished";
+ break;
+ case DownloadsCommon.DOWNLOAD_FAILED:
+ properties += " failed";
+ break;
+ case DownloadsCommon.DOWNLOAD_CANCELED:
+ properties += " canceled";
+ break;
+ case DownloadsCommon.DOWNLOAD_BLOCKED_PARENTAL: // Parental Controls
+ case DownloadsCommon.DOWNLOAD_BLOCKED_POLICY: // Security Zone Policy
+ case DownloadsCommon.DOWNLOAD_DIRTY: // possible virus/spyware
+ properties += " blocked";
+ break;
+ }
+
+ return properties;
+ },
+ getCellProperties: function(aRow, aColumn) {
+ // Append all row properties to the cell
+ return this.getRowProperties(aRow);
+ },
+ getColumnProperties: function(aColumn) { return ""; },
+ isContainer: function(aRow) { return false; },
+ isContainerOpen: function(aRow) { return false; },
+ isContainerEmpty: function(aRow) { return false; },
+ isSeparator: function(aRow) { return false; },
+ isSorted: function() { return false; },
+ canDrop: function(aIdx, aOrientation) { return false; },
+ drop: function(aIdx, aOrientation) { },
+ getParentIndex: function(aRow) { return -1; },
+ hasNextSibling: function(aRow, aAfterIdx) { return false; },
+ getLevel: function(aRow) { return 0; },
+
+ getImageSrc: function(aRow, aColumn) {
+ if (aColumn.id == "Name")
+ return "moz-icon://" + this._dlList[aRow].target.path + "?size=16";
+ return "";
+ },
+
+ getProgressMode: function(aRow, aColumn) {
+ if (aColumn.id == "Progress")
+ return this._dlList[aRow].progressMode;
+ return Ci.nsITreeView.PROGRESS_NONE;
+ },
+
+ getCellValue: function(aRow, aColumn) {
+ if (aColumn.id == "Progress")
+ return this._dlList[aRow].progress;
+ return "";
+ },
+
+ getCellText: function(aRow, aColumn) {
+ let dl = this._dlList[aRow];
+ switch (aColumn.id) {
+ case "Name":
+ return dl.displayName;
+ case "Status":
+ return DownloadsCommon.stateOfDownloadText(dl);
+ case "Progress":
+ if (dl.isActive)
+ return dl.progress;
+ return DownloadsCommon.stateOfDownloadText(dl);
+ case "ProgressPercent":
+ return dl.succeeded ? 100 : dl.progress;
+ case "TimeRemaining":
+ return DownloadsCommon.getTimeRemaining(dl);
+ case "Transferred":
+ return DownloadsCommon.getTransferredBytes(dl);
+ case "TransferRate":
+ let state = DownloadsCommon.stateOfDownload(dl);
+ switch (state) {
+ case DownloadsCommon.DOWNLOAD_DOWNLOADING:
+ let [rate, unit] = DownloadUtils.convertByteUnits(dl.speed);
+ return this._dlbundle.getFormattedString("speedFormat", [rate, unit]);
+ case DownloadsCommon.DOWNLOAD_PAUSED:
+ return this._dlbundle.getString("statePaused");
+ case DownloadsCommon.DOWNLOAD_NOTSTARTED:
+ return this._dlbundle.getString("stateNotStarted");
+ }
+ return "";
+ case "TimeElapsed":
+ // With no end time persisted in the downloads backend this is
+ // utterly useless unless the download is progressing.
+ if (DownloadsCommon.stateOfDownload(dl) ==
+ DownloadsCommon.DOWNLOAD_DOWNLOADING && dl.startTime) {
+ let seconds = (Date.now() - dl.startTime) / 1000;
+ let [time1, unit1, time2, unit2] =
+ DownloadUtils.convertTimeUnits(seconds);
+ if (seconds < 3600 || time2 == 0) {
+ return this._dlbundle.getFormattedString("timeSingle", [time1, unit1]);
+ }
+ return this._dlbundle.getFormattedString("timeDouble", [time1, unit1, time2, unit2]);
+ }
+ return "";
+ case "StartTime":
+ if (dl.startTime) {
+ return this.dateTimeFormatter.format(dl.startTime);
+ }
+ return "";
+ case "EndTime":
+ // This might end with an exception if it is an unsupported uri
+ // scheme.
+ let metaData = DownloadHistory.getPlacesMetaDataFor(dl.source.url);
+
+ if (metaData.endTime) {
+ return this.dateTimeFormatter.format(metaData.endTime);
+ }
+ return "";
+ case "Source":
+ return dl.source.url;
+ }
+ return "";
+ },
+
+ setTree: function(aTree) {
+ this._tree = aTree;
+ this._dlbundle = document.getElementById("dmBundle");
+ },
+
+ toggleOpenState: function(aRow) { },
+ cycleHeader: function(aColumn) { },
+ selectionChanged: function() { },
+ cycleCell: function(aRow, aColumn) {
+ var dl = this._dlList[aRow];
+ switch (aColumn.id) {
+ case "ActionPlay":
+ if (dl.stopped) {
+ if (!dl.succeeded)
+ dl.start();
+ } else {
+ if (dl.hasPartialData)
+ dl.cancel();
+ }
+ break;
+ case "ActionStop":
+ if (dl.isActive)
+ cancelDownload(dl);
+ else
+ removeDownload(dl);
+ break;
+ }
+ },
+ isEditable: function(aRow, aColumn) { return false; },
+ isSelectable: function(aRow, aColumn) { return false; },
+ setCellValue: function(aRow, aColumn, aText) { },
+ setCellText: function(aRow, aColumn, aText) { },
+
+ // ***** local public methods *****
+
+ addDownload: function(aDownload) {
+ aDownload.progressMode = Ci.nsITreeView.PROGRESS_NONE;
+ aDownload.lastSec = Infinity;
+ let state = DownloadsCommon.stateOfDownload(aDownload);
+ switch (state) {
+ case DownloadsCommon.DOWNLOAD_DOWNLOADING:
+ aDownload.endTime = Date.now();
+ // At this point, we know if we are an indeterminate download or not.
+ aDownload.progressMode = aDownload.hasProgress ?
+ Ci.nsITreeView.PROGRESS_UNDETERMINED :
+ Ci.nsITreeView.PROGRESS_NORMAL;
+ case DownloadsCommon.DOWNLOAD_NOTSTARTED:
+ case DownloadsCommon.DOWNLOAD_PAUSED:
+ aDownload.isActive = 1;
+ break;
+ default:
+ aDownload.isActive = 0;
+ break;
+ }
+
+ // prepend in natural sorting
+ aDownload.listIndex = this._lastListIndex--;
+
+ // Prepend data to the download list
+ this._dlList.unshift(aDownload);
+
+ // Tell the tree we added 1 row at index 0
+ this._tree.rowCountChanged(0, 1);
+
+ // Data has changed, so re-sorting might be needed
+ this.sortView("", "", aDownload, 0);
+
+ window.updateCommands("tree-select");
+ },
+
+ updateDownload: function(aDownload) {
+ var row = this._dlList.indexOf(aDownload);
+ if (row == -1) {
+ // No download row found to update, but as it's obviously going on,
+ // add it to the list now (can happen with very fast, e.g. local dls)
+ this.onDownloadAdded(aDownload);
+ return;
+ }
+ let state = DownloadsCommon.stateOfDownload(aDownload);
+ switch (state) {
+ case DownloadsCommon.DOWNLOAD_DOWNLOADING:
+ // At this point, we know if we are an indeterminate download or not.
+ aDownload.progressMode = aDownload.hasProgress ?
+ Ci.nsITreeView.PROGRESS_NORMAL : Ci.nsITreeView.PROGRESS_UNDETERMINED;
+ case DownloadsCommon.DOWNLOAD_NOTSTARTED:
+ case DownloadsCommon.DOWNLOAD_PAUSED:
+ aDownload.isActive = 1;
+ break;
+ default:
+ aDownload.isActive = 0;
+ aDownload.progressMode = Ci.nsITreeView.PROGRESS_NONE;
+ // This preference may not be set, so defaulting to two.
+ var flashCount = 2;
+ try {
+ flashCount = Services.prefs.getIntPref(PREF_FLASH_COUNT);
+ } catch (e) { }
+ getAttentionWithCycleCount(flashCount);
+ break;
+ }
+
+ // Repaint the tree row
+ this._tree.invalidateRow(row);
+
+ // Data has changed, so re-sorting might be needed
+ this.sortView("", "", aDownload, row);
+
+ window.updateCommands("tree-select");
+ },
+
+ removeDownload: function(aDownload) {
+ var row = this._dlList.indexOf(aDownload);
+ // Make sure we have an item to remove
+ if (row == -1)
+ return;
+
+ var index = this.selection.currentIndex;
+ var wasSingleSelection = this.selection.count == 1;
+
+ // Remove data from the download list
+ this._dlList.splice(row, 1);
+
+ // Tell the tree we removed 1 row at the given row index
+ this._tree.rowCountChanged(row, -1);
+
+ // Update selection if only removed download was selected
+ if (wasSingleSelection && this.selection.count == 0) {
+ index = Math.min(index, this.rowCount - 1);
+ if (index >= 0)
+ this.selection.select(index);
+ }
+
+ window.updateCommands("tree-select");
+ },
+
+ searchView: function(aInput) {
+ // Stringify the previous search
+ var prevSearch = this._searchTerms.join(" ");
+
+ // Array of space-separated lower-case search terms
+ this._searchTerms = aInput.trim().toLowerCase().split(/\s+/);
+
+ // Don't rebuild the download list if the search didn't change
+ if (this._searchTerms.join(" ") == prevSearch)
+ return;
+
+ // Cache the current selection
+ this._cacheSelection();
+
+ // Rebuild the tree with set search terms
+ //this.initTree();
+
+ // Restore the selection
+ this._restoreSelection();
+ },
+
+ sortView: function(aColumnID, aDirection, aDownload, aRow) {
+ var sortAscending = aDirection != "descending";
+
+ if (aColumnID == "" && aDirection == "") {
+ // Re-sort in already selected/cached order
+ var sortedColumn = this._tree.columns.getSortedColumn();
+ if (sortedColumn) {
+ aColumnID = sortedColumn.id;
+ sortAscending = sortedColumn.element.getAttribute("sortDirection") != "descending";
+ }
+ // no need for else, use default case of switch, sortAscending is true
+ }
+
+ // Compare function for two _dlList items
+ var compfunc = function(a, b) {
+ // Active downloads are always at the beginning
+ // i.e. 0 for .isActive is larger (!) than 1
+ if (a.isActive < b.isActive)
+ return 1;
+ if (a.isActive > b.isActive)
+ return -1;
+ // Same active/inactive state, sort normally
+ var comp_a = null;
+ var comp_b = null;
+ switch (aColumnID) {
+ case "Name":
+ comp_a = a.displayName.toLowerCase();
+ comp_b = b.displayName.toLowerCase();
+ break;
+ case "Status":
+ comp_a = DownloadsCommon.stateOfDownload(a);
+ comp_b = DownloadsCommon.stateOfDownload(b);
+ break;
+ case "Progress":
+ case "ProgressPercent":
+ // Use original sorting for inactive entries
+ // Use only one isActive to be sure we do the same
+ comp_a = a.isActive ? a.progress : a.listIndex;
+ comp_b = a.isActive ? b.progress : b.listIndex;
+ break;
+ case "TimeRemaining":
+ comp_a = a.isActive ? a.lastSec : a.listIndex;
+ comp_b = a.isActive ? b.lastSec : b.listIndex;
+ break;
+ case "Transferred":
+ comp_a = a.currentBytes;
+ comp_b = b.currentBytes;
+ break;
+ case "TransferRate":
+ comp_a = a.isActive ? a.speed : a.listIndex;
+ comp_b = a.isActive ? b.speed : b.listIndex;
+ break;
+ case "TimeElapsed":
+ comp_a = (a.endTime && a.startTime && (a.endTime > a.startTime))
+ ? a.endTime - a.startTime
+ : 0;
+ comp_b = (b.endTime && b.startTime && (b.endTime > b.startTime))
+ ? b.endTime - b.startTime
+ : 0;
+ break;
+ case "StartTime":
+ comp_a = a.startTime;
+ comp_b = b.startTime;
+ break;
+ case "EndTime":
+ comp_a = a.endTime;
+ comp_b = b.endTime;
+ break;
+ case "Source":
+ comp_a = a.source.url;
+ comp_b = b.source.url;
+ break;
+ case "unsorted": // Special case for reverting to original order
+ default:
+ comp_a = a.listIndex;
+ comp_b = b.listIndex;
+ }
+ if (comp_a > comp_b)
+ return sortAscending ? 1 : -1;
+ if (comp_a < comp_b)
+ return sortAscending ? -1 : 1;
+ return 0;
+ }
+
+ // Cache the current selection
+ this._cacheSelection();
+
+ // Do the actual sorting of the array
+ this._dlList.sort(compfunc);
+
+ var row = this._dlList.indexOf(aDownload);
+ if (row == -1)
+ // Repaint the tree
+ this._tree.invalidate();
+ else if (row == aRow)
+ // No effect
+ this._selectionCache = null;
+ else if (row < aRow)
+ // Download moved up from aRow to row
+ this._tree.invalidateRange(row, aRow);
+ else
+ // Download moved down from aRow to row
+ this._tree.invalidateRange(aRow, row)
+
+ // Restore the selection
+ this._restoreSelection();
+ },
+
+ getRowData: function(aRow) {
+ return this._dlList[aRow];
+ },
+
+ getActiveDownloads: function() {
+ return this._dlList.filter(dld => !dld.stopped);
+ },
+
+ // ***** local member vars *****
+
+ _tree: null,
+ _dlBundle: null,
+ _lastListIndex: 0,
+ _selectionCache: null,
+
+ // ***** local helper functions *****
+
+ // Cache IDs of selected downloads for later restoration
+ _cacheSelection: function() {
+ // Abort if there's already something cached
+ if (this._selectionCache)
+ return;
+
+ this._selectionCache = [];
+ if (this.selection.count < 1)
+ return;
+
+ // Walk all selected rows and cache their download IDs
+ var start = {};
+ var end = {};
+ var numRanges = this.selection.getRangeCount();
+ for (let rg = 0; rg < numRanges; rg++){
+ this.selection.getRangeAt(rg, start, end);
+ for (let row = start.value; row <= end.value; row++){
+ this._selectionCache.push(this._dlList[row]);
+ }
+ }
+ },
+
+ // Restore selection from cached IDs (as possible)
+ _restoreSelection: function() {
+ // Abort if the cache is empty
+ if (!this._selectionCache)
+ return;
+
+ this.selection.clearSelection();
+ for (let dl of this._selectionCache) {
+ // Find out what row this is now and if possible, add it to the selection
+ var row = this._dlList.indexOf(dl);
+ if (row != -1)
+ this.selection.rangedSelect(row, row, true);
+ }
+ // Work done, clear the cache
+ this._selectionCache = null;
+ },
+};
diff --git a/comm/suite/components/downloads/content/uploadProgress.js b/comm/suite/components/downloads/content/uploadProgress.js
new file mode 100644
index 0000000000..0cd4d27817
--- /dev/null
+++ b/comm/suite/components/downloads/content/uploadProgress.js
@@ -0,0 +1,189 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {DownloadUtils} = ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
+
+const kInterval = 750; // Default to .75 seconds.
+
+var gPersist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(Ci.nsIWebBrowserPersist);
+var gSource = window.arguments[0].QueryInterface(Ci.nsIFileURL);
+var gTarget = window.arguments[1].QueryInterface(Ci.nsIURL);
+var gFileName = gSource.file.leafName;
+var gFileSize = gSource.file.fileSize;
+var gPercent = -1;
+var gStartTime;
+var gLastUpdate;
+var gLastSeconds;
+var gBundle;
+var gStatus;
+var gTime;
+var gSize;
+var gProgress;
+var gMeter;
+
+function onLoad()
+{
+ gBundle = document.getElementById("dmBundle");
+ gStatus = document.getElementById("status");
+ gTime = document.getElementById("timeElapsed");
+ gSize = document.getElementById("size");
+ gProgress = document.getElementById("progressText");
+ gMeter = document.getElementById("progress");
+ var status = gBundle.getString("stateNotStarted");
+ document.title =
+ gBundle.getFormattedString("progressTitle", [gFileName, status]);
+ gStatus.value = status;
+ gTime.value = gBundle.getFormattedString("timeSingle",
+ DownloadUtils.convertTimeUnits(0));
+ gSize.value = DownloadUtils.getTransferTotal(0, gFileSize);
+ document.getElementById("target").value =
+ gBundle.getFormattedString("toTarget", [gTarget.resolve(".")]);
+ document.getElementById("source").value =
+ gBundle.getFormattedString("fromSource", [gSource.file.leafName]);
+ gPersist.progressListener = gProgressListener;
+ gPersist.saveURI(gSource, null, null, 0, null, null, gTarget, null);
+ document.documentElement.getButton("cancel").focus();
+}
+
+function onUnload()
+{
+ if (gPersist)
+ gPersist.cancel(Cr.NS_BINDING_ABORTED);
+ gPersist = null;
+}
+
+function setPercent(aPercent, aStatus)
+{
+ gPercent = aPercent;
+ document.title = gBundle.getFormattedString("progressTitlePercent",
+ [aPercent, gFileName, aStatus]);
+ gProgress.value = gBundle.getFormattedString("percentFormat", [aPercent]);
+ gMeter.mode = "normal";
+ gMeter.value = aPercent;
+}
+
+var gProgressListener = {
+ // ----- nsIWebProgressListener methods -----
+
+ // Look for STATE_STOP and close dialog to indicate completion when it happens.
+ onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
+ if (aRequest instanceof Ci.nsIChannel &&
+ aRequest.URI.equals(gTarget) &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+ gPersist = null;
+ var status = gBundle.getString("stateCompleted");
+ setPercent(100, status);
+ gStatus.value = status;
+ gSize.value = DownloadUtils.getTransferTotal(gFileSize, gFileSize);
+ setTimeout(window.close, kInterval);
+ }
+ },
+
+ // Handle progress notifications.
+ onProgressChange: function(aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress) {
+ return this.onProgressChange64(aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress);
+ },
+
+ onProgressChange64: function(aWebProgress, aRequest,
+ aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress) {
+ if (aRequest instanceof Ci.nsIChannel &&
+ aRequest.URI.equals(gTarget)) {
+ // Get current time.
+ var now = Date.now();
+
+ // If interval hasn't elapsed, ignore it.
+ if (!gStartTime)
+ gStartTime = now;
+ else if (now - gLastUpdate < kInterval && aCurTotalProgress < gFileSize)
+ return;
+
+ // Update this time.
+ gLastUpdate = now;
+
+ // Update elapsed time.
+ var elapsed = (now - gStartTime) / 1000;
+
+ // Calculate percentage.
+ var status = gBundle.getString("stateUploading");
+ var percent = -1;
+ if (gFileSize > 0)
+ percent = Math.floor(aCurTotalProgress * 100 / gFileSize);
+ if (percent != gPercent)
+ setPercent(percent, status);
+
+ // Update time remaining.
+ var rate = elapsed && aCurTotalProgress / elapsed;
+ if (rate && gFileSize) {
+ var timeLeft;
+ [timeLeft, gLastSeconds] =
+ DownloadUtils.getTimeLeft((gFileSize - aCurTotalProgress) / rate,
+ gLastSeconds);
+ status = gBundle.getFormattedString("statusActive", [status, timeLeft]);
+ }
+ gStatus.value = status;
+
+ // Update dialog's display of elapsed time.
+ var timeUnits = DownloadUtils.convertTimeUnits(elapsed);
+ var timeString = timeUnits[2] ? "timeDouble" : "timeSingle";
+ gTime.value = gBundle.getFormattedString(timeString, timeUnits);
+
+ // Update size (nn KB of mm KB at xx.x KB/sec)
+ var size = DownloadUtils.getTransferTotal(aCurTotalProgress, gFileSize);
+ if (elapsed)
+ size = gBundle.getFormattedString("sizeSpeed", [size,
+ gBundle.getFormattedString("speedFormat",
+ DownloadUtils.convertByteUnits(rate))]);
+ gSize.value = size;
+ }
+ },
+
+ // Look for error notifications and display alert to user.
+ onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) {
+ // Check for error condition (only if dialog is still open).
+ if (!Cr.isSuccessCode(aStatus)) {
+ // Display error alert (using text supplied by back-end).
+ Services.prompt.alert(window, document.title, aMessage);
+ // Close the dialog.
+ window.close();
+ }
+ },
+
+ // Ignore onLocationChange and onSecurityChange notifications.
+ onLocationChange: function( aWebProgress, aRequest, aLocation, aFlags ) {
+ },
+
+ onSecurityChange: function( aWebProgress, aRequest, aState ) {
+ },
+
+ // ---------- nsISupports methods ----------
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIWebProgressListener2,
+ Ci.nsIWebProgressListener,
+ Ci.nsIInterfaceRequestor]),
+
+ // ---------- nsIInterfaceRequestor methods ----------
+
+ getInterface: function(aIID) {
+ if (aIID.equals(Ci.nsIPrompt) ||
+ aIID.equals(Ci.nsIAuthPrompt)) {
+ var prompt;
+ if (aIID.equals(Ci.nsIPrompt))
+ prompt = Services.ww.getNewPrompter(window);
+ else
+ prompt = Services.ww.getNewAuthPrompter(window);
+ return prompt;
+ }
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
diff --git a/comm/suite/components/downloads/content/uploadProgress.xul b/comm/suite/components/downloads/content/uploadProgress.xul
new file mode 100644
index 0000000000..43e95d5432
--- /dev/null
+++ b/comm/suite/components/downloads/content/uploadProgress.xul
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<!DOCTYPE dialog>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="cancel"
+ onload="onLoad();"
+ onunload="onUnload();"
+ style="width: 40em;">
+
+ <script src="chrome://communicator/content/downloads/uploadProgress.js"/>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="dmBundle"
+ src="chrome://communicator/locale/downloads/downloadmanager.properties"/>
+ </stringbundleset>
+
+ <label id="source" value="" crop="center"/>
+ <label id="target" value="" crop="center"/>
+ <label id="size" value=""/>
+ <label id="timeElapsed" value=""/>
+ <label id="status" value=""/>
+ <hbox>
+ <progressmeter id="progress" mode="undetermined" value="0" flex="1"/>
+ <label id="progressText" value="" style="width: 4ch; text-align: right;"/>
+ </hbox>
+</dialog>