summaryrefslogtreecommitdiffstats
path: root/comm/suite/components/downloads
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--comm/suite/components/downloads/DownloadsCommon.jsm800
-rw-r--r--comm/suite/components/downloads/DownloadsTaskbar.jsm182
-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
-rw-r--r--comm/suite/components/downloads/jar.mn14
-rw-r--r--comm/suite/components/downloads/moz.build17
-rw-r--r--comm/suite/components/downloads/tests/chrome/chrome.ini21
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_action_keys_respect_focus.xul376
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_basic_functionality.xul281
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_cleanup_search.xul172
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_clear_button_disabled.xul201
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_close_download_manager.xul117
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_delete_key_cancels.xul200
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_delete_key_removes.xul198
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_drag.xul201
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_enter_dblclick_opens.xul243
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_multi_select.xul204
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_multiword_search.xul173
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_open_properties.xul197
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_removeDownload_updates_ui.xul150
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_search_clearlist.xul168
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_search_keys.xul128
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_select_all.xul145
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_space_key_pauses_resumes.xul221
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_space_key_retries.xul198
-rw-r--r--comm/suite/components/downloads/tests/chrome/test_ui_stays_open_on_alert_clickback.xul115
32 files changed, 6891 insertions, 0 deletions
diff --git a/comm/suite/components/downloads/DownloadsCommon.jsm b/comm/suite/components/downloads/DownloadsCommon.jsm
new file mode 100644
index 0000000000..81fdcccfa3
--- /dev/null
+++ b/comm/suite/components/downloads/DownloadsCommon.jsm
@@ -0,0 +1,800 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+ "DownloadsCommon",
+];
+
+/**
+ * Handles the Downloads panel shared methods and data access.
+ *
+ * This file includes the following constructors and global objects:
+ *
+ * DownloadsCommon
+ * This object is exposed directly to the consumers of this JavaScript module,
+ * and provides shared methods for all the instances of the user interface.
+ *
+ * DownloadsData
+ * Retrieves the list of past and completed downloads from the underlying
+ * Downloads API data, and provides asynchronous notifications allowing
+ * to build a consistent view of the available data.
+ */
+
+// Globals
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } =
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const { AppConstants } =
+ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ NetUtil: "resource://gre/modules/NetUtil.jsm",
+ PluralForm: "resource://gre/modules/PluralForm.jsm",
+ DownloadHistory: "resource://gre/modules/DownloadHistory.jsm",
+ Downloads: "resource://gre/modules/Downloads.jsm",
+ DownloadUIHelper: "resource://gre/modules/DownloadUIHelper.jsm",
+ DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
+ OS: "resource://gre/modules/osfile.jsm",
+});
+
+XPCOMUtils.defineLazyGetter(this, "DownloadsLogger", () => {
+ let { ConsoleAPI } =
+ ChromeUtils.import("resource://gre/modules/Console.jsm", {});
+ let consoleOptions = {
+ maxLogLevelPref: "browser.download.loglevel",
+ prefix: "Downloads"
+ };
+ return new ConsoleAPI(consoleOptions);
+});
+
+const kDownloadsStringBundleUrl =
+ "chrome://communicator/locale/downloads/downloadmanager.properties";
+
+// Currently not used. Keep for future updates.
+const kDownloadsStringsRequiringFormatting = {
+ fileExecutableSecurityWarning: true
+};
+
+// Currently not used. Keep for future updates.
+const kDownloadsStringsRequiringPluralForm = {
+ otherDownloads3: true
+};
+
+const kPartialDownloadSuffix = ".part";
+
+const kPrefBranch = Services.prefs.getBranch("browser.download.");
+
+const PREF_DM_BEHAVIOR = "browser.download.manager.behavior";
+const PROGRESS_DIALOG_URL = "chrome://communicator/content/downloads/progressDialog.xul";
+
+var PrefObserver = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+ Ci.nsISupportsWeakReference]),
+ getPref(name) {
+ try {
+ switch (typeof this.prefs[name]) {
+ case "boolean":
+ return kPrefBranch.getBoolPref(name);
+ }
+ } catch (ex) { }
+ return this.prefs[name];
+ },
+ observe(aSubject, aTopic, aData) {
+ if (this.prefs.hasOwnProperty(aData)) {
+ delete this[aData];
+ this[aData] = this.getPref(aData);
+ }
+ },
+ register(prefs) {
+ this.prefs = prefs;
+ kPrefBranch.addObserver("", this, true);
+ for (let key in prefs) {
+ let name = key;
+ XPCOMUtils.defineLazyGetter(this, name, function() {
+ return PrefObserver.getPref(name);
+ });
+ }
+ },
+};
+
+// PrefObserver.register({
+ // prefName: defaultValue
+// });
+
+
+// DownloadsCommon
+
+/**
+ * This object is exposed directly to the consumers of this JavaScript module,
+ * and provides shared methods for all the instances of the user interface.
+ */
+var DownloadsCommon = {
+ // The following legacy constants are still returned by stateOfDownload, but
+ // individual properties of the Download object should normally be used.
+ DOWNLOAD_NOTSTARTED: -1,
+ DOWNLOAD_DOWNLOADING: 0,
+ DOWNLOAD_FINISHED: 1,
+ DOWNLOAD_FAILED: 2,
+ DOWNLOAD_CANCELED: 3,
+ DOWNLOAD_PAUSED: 4,
+ DOWNLOAD_BLOCKED_PARENTAL: 6,
+ DOWNLOAD_DIRTY: 8,
+ DOWNLOAD_BLOCKED_POLICY: 9,
+
+ // The following are the possible values of the "attention" property.
+ ATTENTION_NONE: "",
+ ATTENTION_SUCCESS: "success",
+ ATTENTION_WARNING: "warning",
+ ATTENTION_SEVERE: "severe",
+
+ /**
+ * Returns an object whose keys are the string names from the downloads string
+ * bundle, and whose values are either the translated strings or functions
+ * returning formatted strings.
+ */
+ get strings() {
+ let strings = {};
+ let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
+ let enumerator = sb.getSimpleEnumeration();
+ while (enumerator.hasMoreElements()) {
+ let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
+ let stringName = string.key;
+ if (stringName in kDownloadsStringsRequiringFormatting) {
+ strings[stringName] = function() {
+ // Convert "arguments" to a real array before calling into XPCOM.
+ return sb.formatStringFromName(stringName,
+ Array.slice(arguments, 0),
+ arguments.length);
+ };
+ } else if (stringName in kDownloadsStringsRequiringPluralForm) {
+ strings[stringName] = function(aCount) {
+ // Convert "arguments" to a real array before calling into XPCOM.
+ let formattedString = sb.formatStringFromName(stringName,
+ Array.slice(arguments, 0),
+ arguments.length);
+ return PluralForm.get(aCount, formattedString);
+ };
+ } else {
+ strings[stringName] = string.value;
+ }
+ }
+ delete this.strings;
+ return this.strings = strings;
+ },
+
+ /**
+ * Get access to one of the DownloadsData or HistoryDownloadsData objects
+ * depending on whether history downloads should be included.
+ *
+ * @param window
+ * The browser window which owns the download button.
+ * @param [optional] history
+ * True to include history downloads when the window is public.
+ */
+ // does not apply in SM
+ getData(window, history = false) {
+ if (history) {
+ return HistoryDownloadsData;
+ }
+ return DownloadsData;
+ },
+
+ /**
+ * Initializes the Downloads Manager common code.
+ */
+ init() {
+ const { DownloadsData } =
+ ChromeUtils.import("resource://gre/modules/Downloads.jsm");
+ const { DownloadIntegration } =
+ ChromeUtils.import("resource://gre/modules/DownloadIntegration.jsm");
+ DownloadIntegration.shouldPersistDownload = function() { return true; };
+ DownloadsData.initializeDataLink();
+ },
+
+ /**
+ * Returns the legacy state integer value for the provided Download object.
+ */
+ stateOfDownload(download) {
+ // Collapse state using the correct priority.
+ if (!download.stopped) {
+ return DownloadsCommon.DOWNLOAD_DOWNLOADING;
+ }
+ if (download.succeeded) {
+ return DownloadsCommon.DOWNLOAD_FINISHED;
+ }
+ if (download.error) {
+ if (download.error.becauseBlockedByParentalControls) {
+ return DownloadsCommon.DOWNLOAD_BLOCKED_PARENTAL;
+ }
+ if (download.error.becauseBlockedByReputationCheck) {
+ return DownloadsCommon.DOWNLOAD_DIRTY;
+ }
+ return DownloadsCommon.DOWNLOAD_FAILED;
+ }
+ if (download.canceled) {
+ if (download.hasPartialData) {
+ return DownloadsCommon.DOWNLOAD_PAUSED;
+ }
+ return DownloadsCommon.DOWNLOAD_CANCELED;
+ }
+ return DownloadsCommon.DOWNLOAD_NOTSTARTED;
+ },
+
+ /**
+ * Returns the state as a string for the provided Download object.
+ */
+ stateOfDownloadText(download) {
+ // Don't duplicate the logic so just call stateOfDownload.
+ let state = this.stateOfDownload(download);
+ let s = DownloadsCommon.strings;
+ let title = s.unblockHeaderUnblock;
+ let verboseState;
+
+ switch (state) {
+ case DownloadsCommon.DOWNLOAD_PAUSED:
+ verboseState = s.statePaused;
+ break;
+ case DownloadsCommon.DOWNLOAD_DOWNLOADING:
+ verboseState = s.stateDownloading;
+ break;
+ case DownloadsCommon.DOWNLOAD_FINISHED:
+ verboseState = s.stateCompleted;
+ break;
+ case DownloadsCommon.DOWNLOAD_FAILED:
+ verboseState = s.stateFailed;
+ break;
+ case DownloadsCommon.DOWNLOAD_CANCELED:
+ verboseState = s.stateCanceled;
+ break;
+ // Security Zone Policy
+ case DownloadsCommon.DOWNLOAD_BLOCKED_PARENTAL:
+ // Security Zone Policy
+ verboseState = s.stateBlockedParentalControls;
+ break;
+ // Security Zone Policy
+ case DownloadsCommon.DOWNLOAD_BLOCKED_POLICY:
+ verboseState = s.stateBlockedPolicy;
+ break;
+ // possible virus/spyware
+ case DownloadsCommon.DOWNLOAD_DIRTY:
+ verboseState = s.stateDirty;
+ break;
+ // Currently not returned.
+ case DownloadsCommon.DOWNLOAD_UPLOADING:
+ verboseState = s.stateNotStarted;
+ break;
+ case DownloadsCommon.DOWNLOAD_NOTSTARTED:
+ verboseState = s.stateNotStarted;
+ break;
+ // Whoops!
+ default:
+ verboseState = s.stateUnknown;
+ break;
+ }
+
+ return verboseState;
+ },
+
+ /**
+ * Returns the transfer progress text for the provided Download object.
+ */
+ getTransferredBytes(download) {
+ let currentBytes;
+ let totalBytes;
+ // Download in progress.
+ // Download paused / canceled and has partial data.
+ if (!download.stopped ||
+ (download.canceled && download.hasPartialData)) {
+ currentBytes = download.currentBytes,
+ totalBytes = download.hasProgress ? download.totalBytes : -1;
+ // Download done but file missing.
+ } else if (download.succeeded && !download.exists) {
+ currentBytes = download.totalBytes ? download.totalBytes : -1;
+ totalBytes = -1;
+ // For completed downloads, show the file size
+ } else if (download.succeeded && download.target.size !== undefined) {
+ currentBytes = download.target.size;
+ totalBytes = -1;
+ // Some local files saves e.g. from attachments also have no size.
+ // They only have a target in downloads.json but no target.path.
+ // FIX ME later.
+ } else {
+ currentBytes = -1;
+ totalBytes = -1;
+ }
+
+ // We do not want to show 0 of xxx bytes.
+ if (currentBytes == 0) {
+ currentBytes = -1;
+ }
+
+ if (totalBytes == 0) {
+ totalBytes = -1;
+ }
+
+ // We tried everything.
+ if (currentBytes == -1 && totalBytes == -1) {
+ return "";
+ }
+
+ return DownloadUtils.getTransferTotal(currentBytes, totalBytes);
+ },
+
+ /**
+ * Returns the time remaining text for the provided Download object.
+ * For calculation a variable is stored in it.
+ */
+ getTimeRemaining(download) {
+ // If you do changes here please check progressDialog.js.
+ if (!download.stopped) {
+ let lastSec = (download.lastSec == null) ? Infinity : download.lastSec;
+ // Calculate the time remaining if we have valid values
+ let seconds = (download.speed > 0) && (download.totalBytes > 0)
+ ? (download.totalBytes - download.currentBytes) / download.speed
+ : -1;
+ let [timeLeft, newLast] = DownloadUtils.getTimeLeft(seconds, lastSec);
+ // Store it back for next calculation.
+ download.lastSec = newLast;
+ return timeLeft;
+ }
+ return "";
+ },
+
+ /**
+ * Opens a downloaded file.
+ *
+ * @param aFile
+ * the downloaded file to be opened.
+ * @param aMimeInfo
+ * the mime type info object. May be null.
+ * @param aOwnerWindow
+ * the window with which this action is associated.
+ */
+ openDownloadedFile(aFile, aMimeInfo, aOwnerWindow) {
+ if (!(aFile instanceof Ci.nsIFile)) {
+ throw new Error("aFile must be a nsIFile object");
+ }
+ if (aMimeInfo && !(aMimeInfo instanceof Ci.nsIMIMEInfo)) {
+ throw new Error("Invalid value passed for aMimeInfo");
+ }
+ if (!(aOwnerWindow instanceof Ci.nsIDOMWindow)) {
+ throw new Error("aOwnerWindow must be a dom-window object");
+ }
+
+ let isWindowsExe = AppConstants.platform == "win" &&
+ aFile.leafName.toLowerCase().endsWith(".exe");
+
+ let promiseShouldLaunch;
+ // Don't prompt on Windows for .exe since there will be a native prompt.
+ if (aFile.isExecutable() && !isWindowsExe) {
+ // We get a prompter for the provided window here, even though anchoring
+ // to the most recently active window should work as well.
+ promiseShouldLaunch =
+ DownloadUIHelper.getPrompter(aOwnerWindow)
+ .confirmLaunchExecutable(aFile.path);
+ } else {
+ promiseShouldLaunch = Promise.resolve(true);
+ }
+
+ promiseShouldLaunch.then(shouldLaunch => {
+ if (!shouldLaunch) {
+ return;
+ }
+
+ // Actually open the file.
+ try {
+ if (aMimeInfo && aMimeInfo.preferredAction == aMimeInfo.useHelperApp) {
+ aMimeInfo.launchWithFile(aFile);
+ return;
+ }
+ } catch (ex) { }
+
+ // If either we don't have the mime info, or the preferred action failed,
+ // attempt to launch the file directly.
+ try {
+ aFile.launch();
+ } catch (ex) {
+ // If launch fails, try sending it through the system's external "file:"
+ // URL handler.
+ Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService)
+ .loadUrl(NetUtil.newURI(aFile));
+ }
+ }).catch(Cu.reportError);
+ },
+
+ /**
+ * Show a downloaded file in the system file manager.
+ *
+ * @param aFile
+ * a downloaded file.
+ */
+ showDownloadedFile(aFile) {
+ if (!(aFile instanceof Ci.nsIFile)) {
+ throw new Error("aFile must be a nsIFile object");
+ }
+ try {
+ // Show the directory containing the file and select the file.
+ aFile.reveal();
+ } catch (ex) {
+ // If reveal fails for some reason (e.g., it's not implemented on unix
+ // or the file doesn't exist), try using the parent if we have it.
+ let parent = aFile.parent;
+ if (parent) {
+ this.showDirectory(parent);
+ }
+ }
+ },
+
+ /**
+ * Show the specified folder in the system file manager.
+ *
+ * @param aDirectory
+ * a directory to be opened with system file manager.
+ */
+ showDirectory(aDirectory) {
+ if (!(aDirectory instanceof Ci.nsIFile)) {
+ throw new Error("aDirectory must be a nsIFile object");
+ }
+ try {
+ aDirectory.launch();
+ } catch (ex) {
+ // If launch fails (probably because it's not implemented), let
+ // the OS handler try to open the directory.
+ Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+ .getService(Ci.nsIExternalProtocolService)
+ .loadUrl(NetUtil.newURI(aDirectory));
+ }
+ },
+
+ /**
+ * Displays an alert message box which asks the user if they want to
+ * unblock the downloaded file or not.
+ *
+ * @param options
+ * An object with the following properties:
+ * {
+ * verdict:
+ * The detailed reason why the download was blocked, according to
+ * the "Downloads.Error.BLOCK_VERDICT_" constants. If an unknown
+ * reason is specified, "Downloads.Error.BLOCK_VERDICT_MALWARE" is
+ * assumed.
+ * window:
+ * The window with which this action is associated.
+ * dialogType:
+ * String that determines which actions are available:
+ * - "unblock" to offer just "unblock".
+ * - "chooseUnblock" to offer "unblock" and "confirmBlock".
+ * - "chooseOpen" to offer "open" and "confirmBlock".
+ * }
+ *
+ * @return {Promise}
+ * @resolves String representing the action that should be executed:
+ * - "open" to allow the download and open the file.
+ * - "unblock" to allow the download without opening the file.
+ * - "confirmBlock" to delete the blocked data permanently.
+ * - "cancel" to do nothing and cancel the operation.
+ */
+ async confirmUnblockDownload({ verdict, window,
+ dialogType }) {
+ let s = DownloadsCommon.strings;
+
+ // All the dialogs have an action button and a cancel button, while only
+ // some of them have an additonal button to remove the file. The cancel
+ // button must always be the one at BUTTON_POS_1 because this is the value
+ // returned by confirmEx when using ESC or closing the dialog (bug 345067).
+ let title = s.unblockHeaderUnblock;
+ let firstButtonText = s.unblockButtonUnblock;
+ let firstButtonAction = "unblock";
+ let buttonFlags =
+ (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
+ (Ci.nsIPrompt.BUTTON_TITLE_CANCEL * Ci.nsIPrompt.BUTTON_POS_1);
+
+ switch (dialogType) {
+ case "unblock":
+ // Use only the unblock action. The default is to cancel.
+ buttonFlags += Ci.nsIPrompt.BUTTON_POS_1_DEFAULT;
+ break;
+ case "chooseUnblock":
+ // Use the unblock and remove file actions. The default is remove file.
+ buttonFlags +=
+ (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2) +
+ Ci.nsIPrompt.BUTTON_POS_2_DEFAULT;
+ break;
+ case "chooseOpen":
+ // Use the unblock and open file actions. The default is open file.
+ title = s.unblockHeaderOpen;
+ firstButtonText = s.unblockButtonOpen;
+ firstButtonAction = "open";
+ buttonFlags +=
+ (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2) +
+ Ci.nsIPrompt.BUTTON_POS_0_DEFAULT;
+ break;
+ default:
+ Cu.reportError("Unexpected dialog type: " + dialogType);
+ return "cancel";
+ }
+
+ let message;
+ switch (verdict) {
+ case Downloads.Error.BLOCK_VERDICT_UNCOMMON:
+ message = s.unblockTypeUncommon2;
+ break;
+ case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
+ message = s.unblockTypePotentiallyUnwanted2;
+ break;
+ default: // Assume Downloads.Error.BLOCK_VERDICT_MALWARE
+ message = s.unblockTypeMalware;
+ break;
+ }
+ message += "\n\n" + s.unblockTip2;
+
+ Services.ww.registerNotification(function onOpen(subj, topic) {
+ if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
+ // Make sure to listen for "DOMContentLoaded" because it is fired
+ // before the "load" event.
+ subj.addEventListener("DOMContentLoaded", function() {
+ if (subj.document.documentURI ==
+ "chrome://global/content/commonDialog.xul") {
+ Services.ww.unregisterNotification(onOpen);
+ let dialog = subj.document.getElementById("commonDialog");
+ if (dialog) {
+ // Change the dialog to use a warning icon.
+ dialog.classList.add("alert-dialog");
+ }
+ }
+ }, {once: true});
+ }
+ });
+
+ let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
+ firstButtonText, null,
+ s.unblockButtonConfirmBlock, null, {});
+ return [firstButtonAction, "cancel", "confirmBlock"][rv];
+ },
+};
+
+XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "log", () => {
+ return DownloadsLogger.log.bind(DownloadsLogger);
+});
+XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "error", () => {
+ return DownloadsLogger.error.bind(DownloadsLogger);
+});
+
+// DownloadsData
+
+/**
+ * Retrieves the list of past and completed downloads from the underlying
+ * Downloads API data, and provides asynchronous notifications allowing to
+ * build a consistent view of the available data.
+ *
+ * Note that using this object does not automatically initialize the list of
+ * downloads. This is useful to display a neutral progress indicator in
+ * the main browser window until the autostart timeout elapses.
+ *
+ * This powers the DownloadsData and HistoryDownloadsData singleton objects.
+ */
+ function DownloadsDataCtor({ isHistory } = {}) {
+
+ // Contains all the available Download objects and their integer state.
+ this.oldDownloadStates = new Map();
+
+ // For the history downloads list we don't need to register this as a view,
+ // but we have to ensure that the DownloadsData object is initialized before
+ // we register more views. This ensures that the view methods of DownloadsData
+ // are invoked before those of views registered on HistoryDownloadsData,
+ // allowing the endTime property to be set correctly.
+ if (isHistory) {
+ DownloadsData.initializeDataLink();
+ this._promiseList = DownloadsData._promiseList
+ .then(() => DownloadHistory.getList());
+ return;
+ }
+
+ // This defines "initializeDataLink" and "_promiseList" synchronously, then
+ // continues execution only when "initializeDataLink" is called, allowing the
+ // underlying data to be loaded only when actually needed.
+ this._promiseList = (async () => {
+ await new Promise(resolve => this.initializeDataLink = resolve);
+ let list = await Downloads.getList(Downloads.ALL);
+
+ await list.addView(this);
+ this._downloadsLoaded = true;
+
+ return list;
+ })();
+}
+
+DownloadsDataCtor.prototype = {
+ /**
+ * Starts receiving events for current downloads.
+ */
+ initializeDataLink() {},
+
+ /**
+ * Used by sound logic when download ends.
+ */
+ _sound: null,
+ /**
+ * Promise resolved with the underlying DownloadList object once we started
+ * receiving events for current downloads.
+ */
+ _promiseList: null,
+
+ _downloadsLoaded: null,
+
+ /**
+ * Iterator for all the available Download objects. This is empty until the
+ * data has been loaded using the JavaScript API for downloads.
+ */
+ get downloads() {
+ return this.oldDownloadStates.keys();
+ },
+
+ /**
+ * True if there are finished downloads that can be removed from the list.
+ */
+ get canRemoveFinished() {
+ for (let download of this.downloads) {
+ // Stopped, paused, and failed downloads with partial data are removed.
+ if (download.stopped && !(download.canceled && download.hasPartialData)) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /**
+ * Asks the back-end to remove finished downloads from the list. This method
+ * is only called after the data link has been initialized.
+ */
+ removeFinished() {
+ Downloads.getList(Downloads.ALL)
+ .then(list => list.removeFinished())
+ .catch(Cu.reportError);
+ },
+
+ // Integration with the asynchronous Downloads back-end
+
+ // Download view
+ onDownloadAdded: function(download)
+ {
+ // Download objects do not store the end time of downloads, as the Downloads
+ // API does not need to persist this information for all platforms. Once a
+ // download terminates on a Desktop browser, it becomes a history download,
+ // for which the end time is stored differently, as a Places annotation.
+ download.endTime = Date.now();
+ this.oldDownloadStates.set(download,
+ DownloadsCommon.stateOfDownload(download));
+
+ download.displayName =
+ download.target.path ? OS.Path.basename(download.target.path)
+ : download.source.url;
+ this.onDownloadChanged(download);
+ if (!this._downloadsLoaded)
+ return;
+
+ var behavior = download.source.isPrivate ? 1 :
+ Services.prefs.getIntPref(PREF_DM_BEHAVIOR);
+ switch (behavior) {
+ case 0:
+ Cc["@mozilla.org/suite/suiteglue;1"]
+ .getService(Ci.nsISuiteGlue)
+ .showDownloadManager(true);
+ break;
+ case 1:
+ Services.ww.openWindow(null, PROGRESS_DIALOG_URL, null,
+ "chrome,titlebar,centerscreen,minimizable=yes,dialog=no",
+ { wrappedJSObject: download });
+ break;
+ }
+
+ return; // No UI for behavior >= 2
+ },
+
+ onDownloadChanged(download) {
+ let oldState = this.oldDownloadStates.get(download);
+ let newState = DownloadsCommon.stateOfDownload(download);
+ this.oldDownloadStates.set(download, newState);
+
+ if (oldState != newState &&
+ (download.succeeded ||
+ (download.canceled && !download.hasPartialData) ||
+ download.error)) {
+ // Store the end time that may be displayed by the views.
+ download.endTime = Date.now();
+
+ // This state transition code should actually be located in a Downloads
+ // API module (bug 941009).
+ // This might end with an exception if it is an unsupported uri scheme.
+ DownloadHistory.updateMetaData(download);
+
+ if (download.succeeded) {
+ this.playDownloadSound();
+ }
+ }
+ },
+
+ onDownloadRemoved(download) {
+ this.oldDownloadStates.delete(download);
+ },
+
+ // Download summary
+ onSummaryChanged: function() {
+
+ if (!gTaskbarProgress)
+ return;
+
+ const nsITaskbarProgress = Ci.nsITaskbarProgress;
+ var currentBytes = gDownloadsSummary.progressCurrentBytes;
+ var totalBytes = gDownloadsSummary.progressTotalBytes;
+ var state = gDownloadsSummary.allHaveStopped ?
+ currentBytes ? nsITaskbarProgress.STATE_PAUSED :
+ nsITaskbarProgress.STATE_NO_PROGRESS :
+ currentBytes < totalBytes ? nsITaskbarProgress.STATE_NORMAL :
+ nsITaskbarProgress.STATE_INDETERMINATE;
+ switch (state) {
+ case nsITaskbarProgress.STATE_NO_PROGRESS:
+ case nsITaskbarProgress.STATE_INDETERMINATE:
+ gTaskbarProgress.setProgressState(state, 0, 0);
+ break;
+ default:
+ gTaskbarProgress.setProgressState(state, currentBytes, totalBytes);
+ break;
+ }
+ },
+
+ // Play a download sound.
+ playDownloadSound: function()
+ {
+ if (Services.prefs.getBoolPref("browser.download.finished_download_sound")) {
+ if (!this._sound)
+ this._sound = Cc["@mozilla.org/sound;1"].createInstance(Ci.nsISound);
+ try {
+ let url = Services.prefs.getStringPref("browser.download.finished_sound_url");
+ this._sound.play(Services.io.newURI(url));
+ } catch (e) {
+ this._sound.beep();
+ }
+ }
+ },
+
+ // Registration of views
+
+ /**
+ * Adds an object to be notified when the available download data changes.
+ * The specified object is initialized with the currently available downloads.
+ *
+ * @param aView
+ * DownloadsView object to be added. This reference must be passed to
+ * removeView before termination.
+ */
+ addView(aView) {
+ this._promiseList.then(list => list.addView(aView))
+ .catch(Cu.reportError);
+ },
+
+ /**
+ * Removes an object previously added using addView.
+ *
+ * @param aView
+ * DownloadsView object to be removed.
+ */
+ removeView(aView) {
+ this._promiseList.then(list => list.removeView(aView))
+ .catch(Cu.reportError);
+ },
+};
+
+XPCOMUtils.defineLazyGetter(this, "HistoryDownloadsData", function() {
+ return new DownloadsDataCtor({ isHistory: true });
+});
+
+XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() {
+ return new DownloadsDataCtor();
+});
diff --git a/comm/suite/components/downloads/DownloadsTaskbar.jsm b/comm/suite/components/downloads/DownloadsTaskbar.jsm
new file mode 100644
index 0000000000..6cefeedcca
--- /dev/null
+++ b/comm/suite/components/downloads/DownloadsTaskbar.jsm
@@ -0,0 +1,182 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim: sw=2 ts=2 sts=2 et filetype=javascript
+ * 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 EXPORTED_SYMBOLS = [
+ "DownloadsTaskbar",
+];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } =
+ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ChromeUtils.defineModuleGetter(this, "Downloads",
+ "resource://gre/modules/Downloads.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function() {
+ if (!("@mozilla.org/windows-taskbar;1" in Cc)) {
+ return null;
+ }
+ let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"]
+ .getService(Ci.nsIWinTaskbar);
+ return winTaskbar.available && winTaskbar;
+});
+
+XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function() {
+ return ("@mozilla.org/widget/macdocksupport;1" in Cc) &&
+ Cc["@mozilla.org/widget/macdocksupport;1"]
+ .getService(Ci.nsITaskbarProgress);
+});
+
+XPCOMUtils.defineLazyGetter(this, "gGtkTaskbarProgress", function() {
+ return ("@mozilla.org/widget/taskbarprogress/gtk;1" in Cc) &&
+ Cc["@mozilla.org/widget/taskbarprogress/gtk;1"]
+ .getService(Ci.nsIGtkTaskbarProgress);
+});
+
+// DownloadsTaskbar
+
+/**
+ * Handles the download progress indicator in the taskbar.
+ */
+var DownloadsTaskbar = {
+ /**
+ * Underlying DownloadSummary providing the aggregate download information, or
+ * null if the indicator has never been initialized.
+ */
+ _summary: null,
+
+ /**
+ * nsITaskbarProgress object to which download information is dispatched.
+ * This can be null if the indicator has never been initialized or if the
+ * indicator is currently hidden on Windows.
+ */
+ _taskbarProgress: null,
+
+ /**
+ * This method is called after a new browser window is opened, and ensures
+ * that the download progress indicator is displayed in the taskbar.
+ *
+ * On Windows, the indicator is attached to the first browser window that
+ * calls this method. When the window is closed, the indicator is moved to
+ * another browser window, if available, in no particular order. When there
+ * are no browser windows visible, the indicator is hidden.
+ *
+ * On macOS, the indicator is initialized globally when this method is
+ * called for the first time. Subsequent calls have no effect.
+ *
+ * @param aBrowserWindow
+ * nsIDOMWindow object of the newly opened browser window to which the
+ * indicator may be attached.
+ */
+ registerIndicator(aWindow) {
+ if (!this._taskbarProgress) {
+ if (gMacTaskbarProgress) {
+ // On macOS, we have to register the global indicator only once.
+ this._taskbarProgress = gMacTaskbarProgress;
+ // Free the XPCOM reference on shutdown, to prevent detecting a leak.
+ Services.obs.addObserver(() => {
+ this._taskbarProgress = null;
+ gMacTaskbarProgress = null;
+ }, "quit-application-granted");
+ } else {
+ // On Windows, the indicator is currently hidden because we have no
+ // previous window, thus we should attach the indicator now.
+ // In gtk3, the window itself implements the progress interface.
+ this.attachIndicator(aWindow);
+ }
+ }
+
+ // Ensure that the DownloadSummary object will be created asynchronously.
+ if (!this._summary) {
+ Downloads.getSummary(Downloads.ALL).then(summary => {
+ // In case the method is re-entered, we simply ignore redundant
+ // invocations of the callback, instead of keeping separate state.
+ if (this._summary) {
+ return undefined;
+ }
+ this._summary = summary;
+ return this._summary.addView(this);
+ }).catch(Cu.reportError);
+ }
+ },
+
+ /**
+ * On Windows and linux attach the taskbar indicator to the specified window.
+ */
+ attachIndicator(aWindow) {
+ // If there is already a taskbarProgress this usually means the download
+ // manager became active. So clear the taskbar state first.
+ if (this._taskbarProgress) {
+ this._taskbarProgress.setProgressState(Ci.nsITaskbarProgress.STATE_NO_PROGRESS);
+ }
+
+ if (gWinTaskbar) {
+ // Activate the indicator on the specified window.
+ let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIXULWindow).docShell;
+ this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell);
+ } else if (gGtkTaskbarProgress) {
+ // In gtk3, the window itself implements the progress interface.
+ if (!this._taskbarProgress) {
+ this._taskbarProgress = gGtkTaskbarProgress;
+ }
+
+ this._taskbarProgress.setPrimaryWindow(aWindow);
+ } else {
+ // macOS, not gtk3 or unsupported OS.
+ return;
+ }
+
+ // If the DownloadSummary object has already been created, we should update
+ // the state of the new indicator, otherwise it will be updated as soon as
+ // the DownloadSummary view is registered.
+ if (this._summary) {
+ this.onSummaryChanged();
+ }
+
+ aWindow.addEventListener("unload", () => {
+ let windows = Services.wm.getEnumerator(null);
+ let newActiveWindow = null;
+ if (windows.hasMoreElements()) {
+ newActiveWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
+ }
+ if (newActiveWindow) {
+ // Move the progress indicator to the other window.
+ this.attachIndicator(newActiveWindow);
+ } else {
+ // The last window has been closed. We remove the reference to
+ // the taskbar progress object.
+ this._taskbarProgress = null;
+ }
+ });
+ },
+
+ // DownloadSummary view
+ onSummaryChanged() {
+ // If the last window has been closed, we have no indicator any more.
+ if (!this._taskbarProgress) {
+ return;
+ }
+
+ if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
+ this._taskbarProgress.setProgressState(
+ Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0);
+ } else {
+ // For a brief moment before completion, some download components may
+ // report more transferred bytes than the total number of bytes. Thus,
+ // ensure that we never break the expectations of the progress indicator.
+ let progressCurrentBytes = Math.min(this._summary.progressTotalBytes,
+ this._summary.progressCurrentBytes);
+ this._taskbarProgress.setProgressState(
+ Ci.nsITaskbarProgress.STATE_NORMAL,
+ progressCurrentBytes,
+ this._summary.progressTotalBytes);
+ }
+ },
+};
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>
diff --git a/comm/suite/components/downloads/jar.mn b/comm/suite/components/downloads/jar.mn
new file mode 100644
index 0000000000..9abbb0cd7b
--- /dev/null
+++ b/comm/suite/components/downloads/jar.mn
@@ -0,0 +1,14 @@
+# 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/.
+
+comm.jar:
+% content communicator %content/communicator/ contentaccessible=yes
+ content/communicator/downloads/downloadmanager.js (content/downloadmanager.js)
+ content/communicator/downloads/downloadmanager.xul (content/downloadmanager.xul)
+ content/communicator/downloads/DownloadProgressListener.js (content/DownloadProgressListener.js)
+ content/communicator/downloads/progressDialog.xul (content/progressDialog.xul)
+ content/communicator/downloads/progressDialog.js (content/progressDialog.js)
+ content/communicator/downloads/uploadProgress.xul (content/uploadProgress.xul)
+ content/communicator/downloads/uploadProgress.js (content/uploadProgress.js)
+ content/communicator/downloads/treeView.js (content/treeView.js)
diff --git a/comm/suite/components/downloads/moz.build b/comm/suite/components/downloads/moz.build
new file mode 100644
index 0000000000..cc6cc0f1d6
--- /dev/null
+++ b/comm/suite/components/downloads/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+MOCHITEST_CHROME_MANIFESTS += ["tests/chrome/chrome.ini"]
+
+JAR_MANIFESTS += ["jar.mn"]
+
+EXTRA_JS_MODULES += [
+ "DownloadsCommon.jsm",
+ "DownloadsTaskbar.jsm",
+]
+
+with Files("**"):
+ BUG_COMPONENT = ("SeaMonkey", "Downloads")
diff --git a/comm/suite/components/downloads/tests/chrome/chrome.ini b/comm/suite/components/downloads/tests/chrome/chrome.ini
new file mode 100644
index 0000000000..dd5f9fc31f
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/chrome.ini
@@ -0,0 +1,21 @@
+[DEFAULT]
+
+[test_action_keys_respect_focus.xul]
+[test_basic_functionality.xul]
+[test_cleanup_search.xul]
+[test_clear_button_disabled.xul]
+[test_close_download_manager.xul]
+[test_delete_key_cancels.xul]
+[test_delete_key_removes.xul]
+[test_drag.xul]
+[test_enter_dblclick_opens.xul]
+[test_multi_select.xul]
+[test_multiword_search.xul]
+[test_open_properties.xul]
+[test_removeDownload_updates_ui.xul]
+[test_search_clearlist.xul]
+[test_search_keys.xul]
+[test_select_all.xul]
+[test_space_key_pauses_resumes.xul]
+[test_space_key_retries.xul]
+[test_ui_stays_open_on_alert_clickback.xul]
diff --git a/comm/suite/components/downloads/tests/chrome/test_action_keys_respect_focus.xul b/comm/suite/components/downloads/tests/chrome/test_action_keys_respect_focus.xul
new file mode 100644
index 0000000000..765e0a3a9c
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_action_keys_respect_focus.xul
@@ -0,0 +1,376 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jens Hatlak <jh@junetz.de> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test for bug 474622 to check that action keys (Del, Backspace, Return)
+ * respect focus, i.e. work as expected in the Search field, Clear List button
+ * and download list.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+var openInvokeCount = 0;
+var removeInvokeCount = 0;
+var resumeInvokeCount = 0;
+var testedFunctions = {
+ openDownload : null,
+ removeDownload : null,
+ resumeDownload : null
+};
+
+function getCounter(aFn)
+{
+ switch (aFn) {
+ case "openDownload":
+ return () => openInvokeCount++;
+ case "removeDownload":
+ return () => removeInvokeCount++;
+ case "resumeDownload":
+ return () => resumeInvokeCount++;
+ }
+}
+
+function backupTestedFunction(aFn, aWin)
+{
+ ok(true, "(info) backupTestedFunction('" + aFn + "')");
+
+ [testedFunctions[aFn], aWin[aFn]] = [aWin[aFn], getCounter(aFn)];
+}
+function restoreTestedFunction(aFn, aWin)
+{
+ aWin[aFn] = testedFunctions[aFn];
+ testedFunctions[aFn] = null;
+
+ ok(true, "(info) restoreTestedFunction('" + aFn + "')");
+}
+
+function keyPressObs(aWin, aKey)
+{
+ this.mWin = aWin;
+ this.mKey = aKey;
+}
+keyPressObs.prototype = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if ("timer-callback" == aTopic)
+ synthesizeKey(this.mKey, {}, this.mWin);
+ }
+};
+var searchAndPressKey = function(aKey, aWin, aValue) {
+ var searchbox = aWin.document.getElementById("search-box");
+ searchbox.focus();
+ if (aValue != null)
+ searchbox.value = aValue;
+
+ // Press given key after a short delay to allow focus() to complete
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(new keyPressObs(aWin, aKey), 500,
+ Ci.nsITimer.TYPE_ONE_SHOT);
+}
+
+function dlObs(aWin)
+{
+ this.mWin = aWin;
+ this.wasPaused = false;
+ this.wasResumed = false;
+ this.wasFinished = false;
+}
+dlObs.prototype = {
+ onDownloadStateChange: function(aState, aDownload)
+ {
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING &&
+ !this.wasPaused)
+ {
+ this.wasPaused = true;
+ this.mWin.pauseDownload(aDownload.id);
+ return;
+ }
+
+ var searchbox = this.mWin.document.getElementById("search-box");
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_PAUSED &&
+ !this.wasResumed)
+ {
+ this.wasResumed = true;
+
+ // Fill Search with an added space (test continues in testObs)
+ backupTestedFunction("resumeDownload", this.mWin);
+ searchAndPressKey(" ", this.mWin, "paused");
+ } else
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED &&
+ !this.wasFinished)
+ {
+ this.wasFinished = true;
+
+ // The formerly paused download was resumed successfully, is now complete
+ // and still selected. Since it is a real download it can be opened.
+
+ // Init Search (test continues in testObs)
+ backupTestedFunction("openDownload", this.mWin);
+ searchAndPressKey("VK_RETURN", this.mWin, "delete me");
+
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ dm.removeListener(this);
+ }
+ },
+ onStateChange: function(a, b, c, d, e) { },
+ onProgressChange: function(a, b, c, d, e, f, g) { },
+ onSecurityChange: function(a, b, c, d) { }
+};
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ function addDownload()
+ {
+ function createURI(aObj)
+ {
+ return (aObj instanceof Ci.nsIFile) ? Services.io.newFileURI(aObj) :
+ Services.io.newURI(aObj);
+ }
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ var destFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ // The "paused" part of this filename will be searched for later.
+ destFile.append("download.paused");
+ if (destFile.exists())
+ destFile.remove(false);
+
+ var dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+ createURI("http://example.com/httpd.js"),
+ createURI(destFile), null, null,
+ Math.round(Date.now() * 1000), null, persist, false);
+
+ persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+ persist.saveURI(dl.source, null, null, 0, null, null, dl.targetFile, null);
+
+ return dl;
+ }
+
+ // Empty any old downloads
+ dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // Make a file name for the downloads
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("cleanUp");
+ var filePath = Services.io.newFileURI(file).spec;
+
+ var stmt = dm.DBConnection.createStatement(
+ "INSERT INTO moz_downloads (name, target, source, state) " +
+ "VALUES (?1, ?2, ?3, ?4)");
+
+ try {
+ for (let site of ["delete.me", "i.live"]) {
+ stmt.bindByIndex(0, "Finished Download");
+ stmt.bindByIndex(1, filePath);
+ stmt.bindByIndex(2, "http://" + site + "/file");
+ stmt.bindByIndex(3, dm.DOWNLOAD_FINISHED);
+
+ // Add it!
+ stmt.execute();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+ const IS_MAC = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime)
+ .OS == "Darwin";
+
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ var downloadTree = win.document.getElementById("downloadTree");
+ var searchbox = win.document.getElementById("search-box");
+ var clearList = win.document.getElementById("clearListButton");
+
+ // The list must have built, so figure out what test to do
+ switch (testPhase++) {
+ case 0:
+ // Init Search
+ searchbox.value = "delete me";
+ searchbox.doCommand();
+
+ break;
+ case 1:
+ // Clear Search
+ backupTestedFunction("removeDownload", win);
+ searchAndPressKey("VK_DELETE", win);
+
+ break;
+ case 2:
+ is(removeInvokeCount, 0, "Search box: Del didn't remove download");
+
+ // Search has been cleared, init again
+ searchbox.value = "live";
+ searchbox.doCommand();
+
+ break;
+ case 3:
+ // Clear Search
+ searchAndPressKey("VK_BACK_SPACE", win);
+
+ break;
+ case 4:
+ is(removeInvokeCount, 0, "Search box: Backspace didn't remove download");
+ restoreTestedFunction("removeDownload", win);
+
+ // Add paused download (test continues in dlObs)
+ dm.addListener(new dlObs(win));
+ addDownload();
+
+ break;
+ case 5:
+ // Back from dlObs
+ is(resumeInvokeCount, 0, "Search box: Space didn't resume download");
+
+ // Focus download tree and select first (paused) download
+ downloadTree.focus();
+ downloadTree.view.selection.select(0);
+
+ // Simulate Resume download
+ synthesizeKey(" ", {}, win);
+ is(resumeInvokeCount, 1, "Download list: Space resumed download");
+
+ // Resume download for real (test continues in dlObs)
+ restoreTestedFunction("resumeDownload", win);
+ synthesizeKey(" ", {}, win);
+
+ break;
+ case 6:
+ // Back from dlObs
+ is(openInvokeCount, 0, "Search box: Return didn't open download");
+
+ // Search has been changed, init again to get formerly paused download
+ searchbox.value = "paused";
+ searchbox.doCommand();
+
+ break;
+ case 7:
+ // Focus download tree and select first (formerly paused) download
+ downloadTree.focus();
+ downloadTree.view.selection.select(0);
+
+ // Simulate Open download
+ synthesizeKey("VK_RETURN", {}, win);
+ is(openInvokeCount, 1, "Download list: Return opened download");
+
+ // Clear List: Return (execute Clear List)
+ // MacOSX: VK_RETURN doesn't work on this button (See bug 506850).
+ if (IS_MAC)
+ // Workaround not to time out.
+ clearList.doCommand();
+ else {
+ clearList.focus();
+ synthesizeKey("VK_RETURN", {}, win);
+ }
+
+ break;
+ case 8:
+ if (IS_MAC)
+ is(openInvokeCount, 1, "Clear List: doCommand() didn't open download (MacOSX)");
+ else
+ is(openInvokeCount, 1, "Clear List: Enter didn't open download (Linux, Windows)");
+ restoreTestedFunction("openDownload", win);
+
+ // We're done here
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ win.close();
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_basic_functionality.xul b/comm/suite/components/downloads/tests/chrome/test_basic_functionality.xul
new file mode 100644
index 0000000000..ffedc65227
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_basic_functionality.xul
@@ -0,0 +1,281 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Make sure the download manager can display downloads in the right order and
+ * contains the expected data. The list has one of each download state ordered
+ * by the start/end times.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+var dmFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+dmFile.append("dm-ui-test.file");
+dmFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var gTestPath = Services.io.newFileURI(dmFile).spec;
+
+// Downloads are sorted by endTime, so make sure the end times are distinct
+const DownloadData = [
+ /* Active states first */
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859238,
+ state: Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859237,
+ state: Ci.nsIDownloadManager.DOWNLOAD_PAUSED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859236,
+ state: Ci.nsIDownloadManager.DOWNLOAD_SCANNING,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859235,
+ state: Ci.nsIDownloadManager.DOWNLOAD_QUEUED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ /* Finished states */
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859234,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859233,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FAILED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859232,
+ state: Ci.nsIDownloadManager.DOWNLOAD_CANCELED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859231,
+ state: Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859230,
+ state: Ci.nsIDownloadManager.DOWNLOAD_DIRTY,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859229,
+ endTime: 1180493839859229,
+ state: Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_POLICY,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+function test_numberOfTreeItems(aWin)
+{
+ var doc = aWin.document;
+ var dlTree = doc.getElementById("downloadTree");
+ is(dlTree.view.rowCount, DownloadData.length,
+ "There is the correct number of tree items");
+}
+
+function test_properDownloadData(aWin)
+{
+ // This also tests the ordering of the display
+ var doc = aWin.document;
+ var dlTree = doc.getElementById("downloadTree");
+ var view = dlTree.view;
+ var colName = dlTree.columns.getNamedColumn("Name");
+ var colState = dlTree.columns.getNamedColumn("Status");
+ var colTarget = dlTree.columns.getNamedColumn("Name");
+ var colSource = dlTree.columns.getNamedColumn("Source");
+ var stateString;
+ var statusBar = doc.getElementById("statusbar-display");
+
+ for (var i = 0; i < view.rowCount; i++) {
+ view.selection.select(i);
+ is(view.getCellText(i, colName), DownloadData[i].name,
+ "Download names match up");
+ switch (DownloadData[i].state) {
+ case Ci.nsIDownloadManager.DOWNLOAD_PAUSED:
+ stateString = "Paused";
+ break;
+ case Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING:
+ stateString = "Downloading";
+ break;
+ case Ci.nsIDownloadManager.DOWNLOAD_FINISHED:
+ stateString = "Finished";
+ break;
+ case Ci.nsIDownloadManager.DOWNLOAD_FAILED:
+ stateString = "Failed";
+ break;
+ case Ci.nsIDownloadManager.DOWNLOAD_CANCELED:
+ stateString = "Canceled";
+ break;
+ case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL: // Parental Controls
+ case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_POLICY: // Security Zone Policy
+ case Ci.nsIDownloadManager.DOWNLOAD_DIRTY: // possible virus/spyware
+ stateString = "Blocked";
+ break;
+ default:
+ stateString = "Not Started";
+ break;
+ }
+ is(view.getCellText(i, colState), stateString,
+ "Download states match up");
+
+ var filePath = Services.io.newURI(DownloadData[i].target)
+ .QueryInterface(Ci.nsIFileURL)
+ .file.clone()
+ .QueryInterface(Ci.nsIFile)
+ .path;
+ is(statusBar.label, filePath,
+ "Download targets match up");
+ is(view.getCellText(i, colSource), DownloadData[i].source,
+ "Download sources match up");
+ }
+}
+
+var testFuncs = [
+ test_numberOfTreeItems
+ , test_properDownloadData
+];
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // First, we populate the database with some fake data
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
+ "state, currBytes, maxBytes, preferredAction, autoResume) " +
+ "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
+ ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+ for (let dl of DownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ stmt.finalize();
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+
+ // Now we can run our tests
+ for (let t of testFuncs)
+ t(win);
+
+ win.close();
+ dmFile.remove(false);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+ };
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_cleanup_search.xul b/comm/suite/components/downloads/tests/chrome/test_cleanup_search.xul
new file mode 100644
index 0000000000..37394168dc
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_cleanup_search.xul
@@ -0,0 +1,172 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test for bug 414850 to make sure only downloads that are shown when
+ * searching are cleared and afterwards, the default list is shown.
+ *
+ * Test bug 430486 to make sure the Clear list button is disabled only when
+ * there are no download items visible.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // Make a file name for the downloads
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("cleanUp");
+ var filePath = Services.io.newFileURI(file).spec;
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, target, source, state) " +
+ "VALUES (?1, ?2, ?3, ?4)");
+
+ try {
+ for (let site of ["delete.me", "i.live"]) {
+ stmt.bindByIndex(0, "Super Pimped Download");
+ stmt.bindByIndex(1, filePath);
+ stmt.bindByIndex(2, "http://" + site + "/file");
+ stmt.bindByIndex(3, dm.DOWNLOAD_FINISHED);
+
+ // Add it!
+ stmt.execute();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+ var downloadView = win.document.getElementById("downloadTree").view;
+ var searchbox = win.document.getElementById("search-box");
+ var clearList = win.document.getElementById("clearListButton");
+
+ // The list must have built, so figure out what test to do
+ switch (testPhase++) {
+ case 0:
+ // Make sure the button is initially enabled
+ is(clearList.disabled, false, "Clear list is enabled for default 2 item view");
+
+ // Search for multiple words in any order in all places
+ searchbox.value = "delete me";
+ searchbox.doCommand();
+
+ break;
+ case 1:
+ // Search came back with 1 item
+ is(downloadView.rowCount, 1, "Search found the item to delete");
+ is(clearList.disabled, false, "Clear list is enabled for search matching 1 item");
+
+ // Clear the list that has the single matched item
+ clearList.doCommand();
+
+ break;
+ case 2:
+ // Done rebuilding with one item left
+ is(downloadView.rowCount, 1, "Clear list rebuilt the list with one");
+ is(clearList.disabled, false, "Clear list still enabled for 1 item in default view");
+
+ // Clear the whole list
+ clearList.doCommand();
+
+ break;
+ case 3:
+ // There's nothing left
+ is(downloadView.rowCount, 0, "Clear list killed everything");
+ is(clearList.disabled, true, "Clear list is disabled for no items");
+
+ // We're done!
+ win.close();
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+ };
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_clear_button_disabled.xul b/comm/suite/components/downloads/tests/chrome/test_clear_button_disabled.xul
new file mode 100644
index 0000000000..0b713a31c3
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_clear_button_disabled.xul
@@ -0,0 +1,201 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Anoop Saldanha <poonaatsoc@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests 437422. This test basically intends to checks if the clear list
+ * button is disabled when:
+ * 1. an invalid search string has been entered into the search box.
+ * 2. active downloads are present in the dm ui
+ * 3. we have both case (1) and (2)
+ */
+-->
+
+<window title="Download Manager Test"
+ onload="runTest();"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+const nsIDownloadManager = Ci.nsIDownloadManager;
+var dmFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+dmFile.append("dm-ui-test.file");
+dmFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var gTestPath = Services.io.newFileURI(dmFile).spec;
+
+const DoneDownloadData = [
+ { name: "Dead",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: nsIDownloadManager.DOWNLOAD_CANCELED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+const ActiveDownloadData = [
+ { name: "Patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: nsIDownloadManager.DOWNLOAD_DOWNLOADING,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+function runTest()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
+ "state, currBytes, maxBytes, preferredAction, autoResume) " +
+ "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
+ ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+ for (let dl of DoneDownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ //stmt.finalize();
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ var doc = aSubject.document;
+ var searchbox = doc.getElementById("search-box");
+ var clearButton = doc.getElementById("clearListButton");
+
+ switch (testPhase++) {
+ case 0:
+ // Ensure that the clear list button is enabled at first
+ ok(!clearButton.disabled,
+ "The clear list button is not disabled initially.");
+
+ // Now, insert an nonsensical search string - nothing should show up,
+ // and the button should be disabled in the next test phase
+ searchbox.value = "Nonsensical";
+ searchbox.doCommand();
+
+ break;
+ case 1:
+ ok(clearButton.disabled,
+ "The clear list button is disabled with a nonsensical search " +
+ "term entered");
+
+ // Clear the search box
+ searchbox.value = "";
+ searchbox.doCommand();
+ break;
+
+ case 2:
+ // Populate the download manager with an active download now, and
+ // rebuild the list
+ stmt.reset();
+ for (let dl of ActiveDownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ stmt.finalize();
+ dm.cleanUp();
+
+ break;
+ case 3:
+ ok(clearButton.disabled,
+ "The clear list button is disabled when we only have an active " +
+ "download");
+
+ // Now, insert an nonsensical search string - only the active download
+ // should show up, and the button should be disabled in the next test
+ // phase
+ searchbox.value = "Nonsensical";
+ searchbox.doCommand();
+ break;
+ case 4:
+ ok(clearButton.disabled,
+ "The clear list button is disabled with a nonsensical search " +
+ "term entered and one active download");
+
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+ };
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_close_download_manager.xul b/comm/suite/components/downloads/tests/chrome/test_close_download_manager.xul
new file mode 100644
index 0000000000..94251a3771
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_close_download_manager.xul
@@ -0,0 +1,117 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Anoop Saldanha <poonaatsoc@gmail.com>
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This test basically checks if the download manager
+ * closes when you press the esc key and accel + w.
+ */
+-->
+
+<window title="Download Manager Test"
+ onload="runTest();"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+const dmui = Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsIDownloadManagerUI);
+
+function testCloseDMWithAccelKey(aWin)
+{
+ function dmWindowClosedListener() {
+ aWin.removeEventListener("unload", dmWindowClosedListener, false);
+ ok(!dmui.visible, "DMUI closes with accel + w");
+ SimpleTest.finish();
+ }
+ aWin.addEventListener("unload", dmWindowClosedListener, false);
+
+ synthesizeKey("w", { accelKey: true }, aWin);
+}
+
+function runTest()
+{
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var testPhase = 0;
+ // Specify an observer that will be notified when the dm has been rendered on screen
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ SimpleTest.waitForFocus(function () { closeDM(aSubject) }, aSubject);
+ }
+ };
+
+ function closeDM(win) {
+ // if we add more ways to close DM with keys, add more cases here
+ switch(testPhase++) {
+ case 0:
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ testCloseDMWithAccelKey(win);
+ }
+ }
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_delete_key_cancels.xul b/comm/suite/components/downloads/tests/chrome/test_delete_key_cancels.xul
new file mode 100644
index 0000000000..f02459dd6f
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_delete_key_cancels.xul
@@ -0,0 +1,200 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jens Hatlak <jh@junetz.de> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests that the delete key will cancel a download in the UI.
+ * This test was added in bug 474622.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+var invokeCount = 0;
+var cancelDownload = null;
+
+function dlObs(aWin)
+{
+ this.mWin = aWin;
+ this.wasPaused = false;
+ this.wasCanceled = false;
+}
+dlObs.prototype = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if ("timer-callback" == aTopic) {
+ // We're done!
+ this.mWin.close();
+ SimpleTest.finish();
+ }
+ },
+
+ onDownloadStateChange: function(aState, aDownload)
+ {
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING &&
+ !this.wasPaused) {
+ // Make a copy of the cancelDownload function and replace it with a test
+ var counter = () => invokeCount++;
+ [cancelDownload, this.mWin["cancelDownload"]] = [this.mWin["cancelDownload"], counter];
+
+ synthesizeKey("VK_DELETE", {}, this.mWin);
+ is(invokeCount, 1, "Delete canceled the active download");
+
+ this.wasPaused = true;
+ this.mWin.pauseDownload(aDownload.id);
+ }
+
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_PAUSED &&
+ !this.wasCanceled) {
+ synthesizeKey("VK_DELETE", {}, this.mWin);
+ is(invokeCount, 2, "Delete canceled the paused download");
+
+ // After all tests, restore original function
+ this.mWin["cancelDownload"] = cancelDownload;
+
+ this.wasCanceled = true;
+ this.mWin.cancelDownload(aDownload);
+
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ dm.removeListener(this);
+
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+ },
+ onStateChange: function(a, b, c, d, e) { },
+ onProgressChange: function(a, b, c, d, e, f, g) { },
+ onSecurityChange: function(a, b, c, d) { }
+};
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ function addDownload() {
+ function createURI(aObj) {
+ return (aObj instanceof Ci.nsIFile) ? Services.io.newFileURI(aObj) :
+ Services.io.newURI(aObj);
+ }
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ var destFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ destFile.append("download.result");
+ if (destFile.exists())
+ destFile.remove(false);
+
+ var dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+ createURI("http://example.com/httpd.js"),
+ createURI(destFile), null, null,
+ Math.round(Date.now() * 1000), null, persist, false);
+
+ persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+ persist.saveURI(dl.source, null, null, 0, null, null, dl.targetFile, null);
+
+ return dl;
+ }
+
+ // First, we clear out the database
+ dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { cancelDL(aSubject) }, aSubject);
+ }
+ };
+
+ function cancelDL(win) {
+ var doc = win.document;
+ dm.addListener(new dlObs(win));
+
+ addDownload();
+ // we need to focus the download as well
+ doc.getElementById("downloadTree").view.selection.select(0);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_delete_key_removes.xul b/comm/suite/components/downloads/tests/chrome/test_delete_key_removes.xul
new file mode 100644
index 0000000000..8462c3323b
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_delete_key_removes.xul
@@ -0,0 +1,198 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This test ensures that the delete key removes a download. This was added by
+ * bug 411172.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+var dmFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+dmFile.append("dm-ui-test.file");
+dmFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var gTestPath = Services.io.newFileURI(dmFile).spec;
+
+// Downloads are sorted by endTime, so make sure the end times are distinct
+const DownloadData = [
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859236,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FAILED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859234,
+ state: Ci.nsIDownloadManager.DOWNLOAD_CANCELED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859232,
+ state: Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859230,
+ state: Ci.nsIDownloadManager.DOWNLOAD_DIRTY,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 },
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859229,
+ endTime: 1180493839859229,
+ state: Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_POLICY,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // First, we populate the database with some fake data
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
+ "state, currBytes, maxBytes, preferredAction, autoResume) " +
+ "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
+ ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+ for (let dl of DownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ stmt.finalize();
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { deleteDL(aSubject) }, aSubject);
+ }
+ };
+
+ function deleteDL(win) {
+ var doc = win.document;
+
+ var stmt = db.createStatement("SELECT COUNT(*) FROM moz_downloads");
+ try {
+ stmt.executeStep();
+ let dlTree = doc.getElementById("downloadTree");
+ is(stmt.getInt32(0), dlTree.view.rowCount,
+ "The database and the number of downloads display matches");
+ stmt.reset();
+
+ let len = DownloadData.length;
+ for (let i = 0; i < len; i++) {
+ synthesizeKey("VK_DELETE", {}, win);
+
+ stmt.executeStep();
+ is(stmt.getInt32(0), len - (i + 1),
+ "The download was properly removed");
+ stmt.reset();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ win.close();
+ dmFile.remove(false);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_drag.xul b/comm/suite/components/downloads/tests/chrome/test_drag.xul
new file mode 100644
index 0000000000..133e633c39
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_drag.xul
@@ -0,0 +1,201 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Phil Lacy <philbaseless-firefox@yahoo.com> (Original Author)
+ * Jens Hatlak <jh@junetz.de>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Assure download manager can load valid list item as
+ * "application/moz-x-file", "text/uri-list" and "text/plain"
+ */
+
+based on toolkit/mozapps/downloads/tests/chrome/test_bug_462172.xul
+https://bugzilla.mozilla.org/show_bug.cgi?id=462172
+
+create a file with unique name
+create another file with unique name and delete it
+load into downloads database
+open download manager
+synthesize drag on both files
+missing file should not init drag
+real file should return transferdata with application/x-moz-file,
+ text/uri-list (CRLF-terminated) and text/plain (LF-terminated)
+close window
+-->
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
+
+ <script>
+ <![CDATA[
+var missingFileElid;
+var realFileElid;
+const kFiller = "notApplicable";
+const kFillerURL = "https://bugzilla.mozilla.org/show_bug.cgi?id=462172"
+var realFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+var missingFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
+
+realFile.append(kFiller);
+realFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var realFilePath = Services.io.newFileURI(realFile).spec;
+
+missingFile.append(kFiller);
+missingFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var missingFilePath = Services.io.newFileURI(missingFile).spec;
+missingFile.remove(false);
+
+// Dummy data for our files.
+// 'source' field must be in form of a URL.
+const DownloadData = [
+ { name: kFiller,
+ source: kFillerURL,
+ target: realFilePath,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED },
+ { name: kFiller,
+ source: kFillerURL,
+ target: missingFilePath,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED }
+];
+
+function mouseDragStartOnCell(aTree, aRow, aColumn, aWin, aFile)
+{
+ // get cell coordinates
+ if (typeof aTree.columns != "undefined")
+ aColumn = aTree.columns[aColumn];
+ var rect = aTree.treeBoxObject.getCoordsForCellItem(aRow, aColumn, "text");
+ return synthesizeDragStart(aTree.body, aFile, aWin, rect.x, rect.y);
+}
+
+function compareFunc(actualData, expectedData)
+{
+ return expectedData.equals(actualData);
+}
+
+var dragRealFile = [[
+ { type: "application/x-moz-file",
+ data: realFile,
+ eqTest: compareFunc },
+ { type: "text/uri-list",
+ data: realFilePath + "\r\n" },
+ { type: "text/plain",
+ data: realFilePath + "\n" }
+]];
+var dragMissingFile = [[
+ { type: "application/x-moz-file",
+ data: missingFile,
+ eqTest: compareFunc },
+ { type: "text/uri-list",
+ data: missingFilePath + "\r\n" },
+ { type: "text/plain",
+ data: missingFilePath + "\n" }
+]];
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ // load files into db
+ var db = dm.DBConnection;
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads ( name, source, target, state)" +
+ "VALUES (:name, :source, :target, :state)");
+ for (let dl of DownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+ stmt.execute();
+ }
+ stmt.finalize();
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+ win.focus();
+
+ var downloadTree = win.document.getElementById("downloadTree");
+
+ // Now we can run our tests
+ // Unordered sorting -> DownloadData/insert order: realFile, missingFile
+ // Column 4 is "Progress" (column 1, "Status", is hidden by default)
+ var result = mouseDragStartOnCell(downloadTree, 0, 4, win, dragRealFile);
+ is(result, null, "Checking for Real file match");
+ result = mouseDragStartOnCell(downloadTree, 1, 4, win, dragMissingFile);
+ isnot(result, null, "Drag start did not return item for missing file");
+
+ // Done.
+ win.close();
+ realFile.remove(false);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+ };
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_enter_dblclick_opens.xul b/comm/suite/components/downloads/tests/chrome/test_enter_dblclick_opens.xul
new file mode 100644
index 0000000000..59c105ceaa
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_enter_dblclick_opens.xul
@@ -0,0 +1,243 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jens Hatlak <jh@junetz.de> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test bug 495545 (implemented by bug 474622) to make sure the enter key
+ * or a double click actually calls opening the downloaded file.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+// similar, but not equal to the one in tree_shared.js
+function mouseDblClickOnCell(aTree, aRow, aColumn, aWin)
+{
+ // get cell coordinates
+ if (typeof aTree.columns != "undefined")
+ aColumn = aTree.columns[aColumn];
+ var rect = aTree.treeBoxObject.getCoordsForCellItem(aRow, aColumn, "text");
+ synthesizeMouse(aTree.body, rect.x, rect.y, { clickCount: 2 }, aWin);
+}
+
+function dlObs(aWin)
+{
+ this.mWin = aWin;
+ this.currDownload = null;
+}
+dlObs.prototype = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if ("timer-callback" == aTopic) {
+ var downloadTree = this.mWin.document.getElementById("downloadTree");
+ var downloadView = downloadTree.view;
+
+ // Default test/check for invocations
+ var invokeCount = 0;
+ var counter = () => invokeCount++;
+
+ // Run tests
+
+ // Make a copy of the openDownload function and replace it with a test
+ let copy;
+ [copy, this.mWin["openDownload"]] = [this.mWin["openDownload"], counter];
+
+ // Select the first (paused) download for not calling openDownload
+ downloadView.selection.select(0);
+
+ synthesizeKey("VK_RETURN", {}, this.mWin);
+ is(invokeCount, 0, "Enter didn't do anything");
+
+ mouseDblClickOnCell(downloadTree, 0, 3, this.mWin);
+ is(invokeCount, 0, "Double click didn't do anything");
+
+ // Select the second (finished) download for calling openDownload
+ downloadView.selection.select(1);
+
+ synthesizeKey("VK_RETURN", {}, this.mWin);
+ is(invokeCount, 1, "Enter opened download");
+
+ mouseDblClickOnCell(downloadTree, 1, 3, this.mWin);
+ is(invokeCount, 2, "Double click opened download");
+
+ // After all tests, restore original function
+ this.mWin["openDownload"] = copy;
+
+ // We're done!
+ this.mWin.close();
+ this.currDownload.targetFile.remove(false);
+ SimpleTest.finish();
+ }
+ },
+
+ onDownloadStateChange: function(aState, aDownload)
+ {
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
+ this.currDownload = aDownload;
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ dm.removeListener(this);
+ }
+ },
+ onStateChange: function(a, b, c, d, e) { },
+ onProgressChange: function(a, b, c, d, e, f, g) { },
+ onSecurityChange: function(a, b, c, d) { }
+};
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ function addDownload() {
+ function createURI(aObj) {
+ return (aObj instanceof Ci.nsIFile) ? Services.io.newFileURI(aObj) :
+ Services.io.newURI(aObj);
+ }
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ var destFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ destFile.append("download.result");
+ if (destFile.exists())
+ destFile.remove(false);
+
+ var dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+ createURI("http://example.com/httpd.js"),
+ createURI(destFile), null, null,
+ Math.round(Date.now() * 1000), null, persist, false);
+
+ persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+ persist.saveURI(dl.source, null, null, 0, null, null, dl.targetFile, null);
+
+ return dl;
+ }
+
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (source, state, target, referrer) " +
+ "VALUES (?1, ?2, ?3, ?4)");
+
+ // add first download: PAUSED state
+ try {
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("dltest-paused");
+ var fileSpec = Services.io.newFileURI(file).spec;
+ stmt.bindByIndex(0, "http://example.com/file");
+ stmt.bindByIndex(1, dm.DOWNLOAD_PAUSED);
+ stmt.bindByIndex(2, fileSpec);
+ stmt.bindByIndex(3, "http://referrer/");
+
+ // Add it!
+ stmt.execute();
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ dm.addListener(new dlObs(win));
+
+ // add second download: FINISHED state, actually created
+ // (checked by cmd_open)
+ addDownload();
+
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ }
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_multi_select.xul b/comm/suite/components/downloads/tests/chrome/test_multi_select.xul
new file mode 100644
index 0000000000..0c0a05cf4b
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_multi_select.xul
@@ -0,0 +1,204 @@
+<?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/. -->
+<!--
+ * Test bug 228842 to make sure multiple selections work in the download
+ * manager by making sure commands work as expected for both single and doubly
+ * selected items.
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (source, state, target, referrer) " +
+ "VALUES (?1, ?2, ?3, ?4)");
+
+ try {
+ for (let site of ["ed.agadak.net", "mozilla.org", "mozilla.com", "mozilla.net"]) {
+ let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append(site);
+ let fileSpec = Services.io.newFileURI(file).spec;
+
+ stmt.bindByIndex(0, "http://" + site + "/file");
+ stmt.bindByIndex(1, dm.DOWNLOAD_FINISHED);
+ stmt.bindByIndex(2, fileSpec);
+ stmt.bindByIndex(3, "http://referrer/");
+
+ // Add it!
+ stmt.execute();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ var downloadView = win.document.getElementById("downloadTree").view;
+
+ // Default test/check for invocations
+ var invokeCount = 0;
+ var counter = () => invokeCount++;
+
+ // Accessors for returning a value for various properties
+ var getItems = () => downloadView.rowCount;
+ var getSelected = () => downloadView.selection.count;
+ var getClipboard = function() {
+ var clip = Cc["@mozilla.org/widget/clipboard;1"]
+ .getService(Ci.nsIClipboard);
+ var trans = Cc["@mozilla.org/widget/transferable;1"]
+ .createInstance(Ci.nsITransferable);
+
+ trans.init(null);
+ trans.addDataFlavor("text/unicode");
+ clip.getData(trans, clip.kGlobalClipboard);
+ var str = {};
+ trans.getTransferData("text/unicode", str, {});
+ return str.value.QueryInterface(Ci.nsISupportsString)
+ .data;
+ };
+
+ // Array of tests that consist of the command name, download manager
+ // function to temporarily replace, method to use in its place, value to
+ // use when checking correctness
+ var commandTests = [
+ ["pause", "pauseDownload", counter, counter],
+ ["resume", "resumeDownload", counter, counter],
+ ["cancel", "cancelDownload", counter, counter],
+ ["open", "openDownload", counter, counter],
+ ["show", "showDownload", counter, counter],
+ ["properties", "showProperties", counter, counter],
+ ["retry", "retryDownload", counter, counter],
+ ["openReferrer", "openUILink", counter, counter],
+ ["copyLocation", null, null, getClipboard],
+ ["remove", null, null, getItems],
+ ["selectAll", null, null, getSelected],
+ ];
+
+ // All the expected results for both single and double selections
+ var allExpected = {
+ single: {
+ pause: [0, "Paused no downloads"],
+ resume: [0, "Resumed no downloads"],
+ cancel: [0, "Canceled no downloads"],
+ open: [0, "Opened no downloads"],
+ show: [0, "Showed no downloads"],
+ properties: [1, "Called properties for one download"],
+ retry: [0, "Retried no downloads"],
+ openReferrer: [1, "Opened one referrer"],
+ copyLocation: ["http://ed.agadak.net/file", "Copied one location"],
+ remove: [3, "Removed one download, remaining 3"],
+ selectAll: [3, "Selected all 3 remaining downloads"],
+ },
+ double: {
+ pause: [0, "Paused neither download"],
+ resume: [0, "Resumed neither download"],
+ cancel: [0, "Canceled neither download"],
+ open: [0, "Opened neither download"],
+ show: [0, "Showed neither download"],
+ properties: [0, "Called properties for neither download"],
+ retry: [0, "Retried neither download"],
+ openReferrer: [0, "Opened neither referrer"],
+ copyLocation: ["http://mozilla.org/file\nhttp://mozilla.com/file", "Copied both locations"],
+ remove: [1, "Removed both downloads, remaining 1"],
+ selectAll: [1, "Selected the 1 remaining download"],
+ },
+ };
+
+ var cmdName;
+
+ // Run two tests: single selected, double selected
+ for (let whichTest of ["single", "double"]) {
+ let expected = allExpected[whichTest];
+
+ if (whichTest == "double")
+ // Select the first 2 downloads for double
+ downloadView.selection.rangedSelect(0, 1, false);
+ else
+ // Select the first download for single
+ downloadView.selection.select(0);
+
+ for (let [command, func, test, value] of commandTests) {
+ // Make a copy of the original function and replace it with a test
+ let copy;
+ [copy, win[func]] = [win[func], test];
+
+ // Run the command from the menu
+ if (command == "selectAll")
+ cmdName = "menu_" + command;
+ else
+ cmdName = "dlMenu_" + command;
+
+ win.document.getElementById(cmdName).doCommand();
+
+ // Make sure the value is as expected
+ let [correct, message] = expected[command];
+ is(value(), correct, message);
+
+ // Restore original values
+ invokeCount = 0;
+ win[func] = copy;
+ }
+ }
+
+ // We're done!
+ win.close();
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_multiword_search.xul b/comm/suite/components/downloads/tests/chrome/test_multiword_search.xul
new file mode 100644
index 0000000000..ae2817c3fa
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_multiword_search.xul
@@ -0,0 +1,173 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test for bug 419403 that lets the download manager support multiple word
+ * search against the download name, source/referrer, date/time, file size,
+ * etc.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // Make a file name for the downloads
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("multiWord");
+ var filePath = Services.io.newFileURI(file).spec;
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, target, source, state, endTime, maxBytes) " +
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6)");
+
+ try {
+ for (let site of ["ed.agadak.net", "mozilla.org"]) {
+ stmt.bindByIndex(0, "Super Pimped Download");
+ stmt.bindByIndex(1, filePath);
+ stmt.bindByIndex(2, "http://" + site + "/file");
+ stmt.bindByIndex(3, dm.DOWNLOAD_FINISHED);
+ stmt.bindByIndex(4, new Date(1985, 7, 2) * 1000);
+ stmt.bindByIndex(5, 111222333444);
+
+ // Add it!
+ stmt.execute();
+ }
+ } finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testPhase = -1;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+ var downloadView = win.document.getElementById("downloadTree").view;
+ var searchbox = win.document.getElementById("search-box");
+
+ var search = function(aTerms) {
+ searchbox.value = aTerms;
+ searchbox.doCommand();
+ };
+
+ let testResults = function(aExpected) {
+ is(downloadView.rowCount, aExpected,
+ "Phase " + testPhase + ": search matched " + aExpected + " download(s)");
+ };
+
+ // The list must have built, so figure out what test to do
+ switch (++testPhase) {
+ case 0:
+ // Search for multiple words in any order in all places
+ search("download super pimped multiWord");
+
+ break;
+ case 1:
+ // Done populating the two items
+ testResults(2);
+
+ // Do partial word matches including the site
+ search("Agadak.net downl pimp multi");
+
+ break;
+ case 2:
+ // Done populating the one result
+ testResults(1);
+
+ // The search term shouldn't be treated like a regular expression,
+ // e.g. "D.wnload" shouldn't match "Download".
+ search("D.wnload");
+
+ break;
+ case 3:
+ testResults(0);
+
+ // We're done!
+ win.close();
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+ };
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_open_properties.xul b/comm/suite/components/downloads/tests/chrome/test_open_properties.xul
new file mode 100644
index 0000000000..2f319d6766
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_open_properties.xul
@@ -0,0 +1,197 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Anoop Saldanha <poonaatsoc@gmail.com> (Original Author)
+ * Robert Kaiser <kairo@kairo.at>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests bug 474620 - opening a progress dialog with the "properties"
+ * item in the download manager.
+ */
+-->
+
+<window title="Download Manager Test"
+ onload="runTest();"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+const nsIDownloadManager = Ci.nsIDownloadManager;
+var dmFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+dmFile.append("dm-ui-test.file");
+dmFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var gTestPath = Services.io.newFileURI(dmFile).spec;
+
+const DoneDownloadData = [
+ { name: "Dead",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: nsIDownloadManager.DOWNLOAD_CANCELED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+const ActiveDownloadData = [
+ { name: "Patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: nsIDownloadManager.DOWNLOAD_DOWNLOADING,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+function runTest()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
+ "state, currBytes, maxBytes, preferredAction, autoResume) " +
+ "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
+ ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+ for (let dl of DoneDownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ //stmt.finalize();
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var dmview;
+ var testStartTime = Date.now();
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ switch (testPhase++) {
+ case 0:
+ ok(!!aSubject.document.getElementById("dlWinCommands"),
+ "The download manager window is active");
+ // the download manager is started, select the first download
+ dmview = aSubject.document.getElementById("downloadTree").view;
+ dmview.selection.select(0);
+ // call "properties"
+ aSubject.document.getElementById("dlMenu_properties").doCommand();
+
+ break;
+ case 1:
+ ok(!!aSubject.document.getElementById("dlProgressCommands"),
+ "The progress dialog was called for a canceled download");
+ var endTimeSeconds = Math.round(DoneDownloadData[0].endTime / 1000);
+ is(aSubject.gEndTime, endTimeSeconds, "End time matches data");
+
+ // Close the progress window
+ aSubject.close();
+
+ // Populate the download manager with an active download now, and
+ // rebuild the list
+ stmt.reset();
+ for (let dl of ActiveDownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ stmt.finalize();
+ dm.cleanUp();
+
+ break;
+ case 2:
+ ok(!!aSubject.document.getElementById("dlWinCommands"),
+ "The download manager window got an event");
+ // the download manager UI is rebuilt, select the first download
+ dmview.selection.select(0);
+ // call "properties"
+ aSubject.document.getElementById("dlMenu_properties").doCommand();
+
+ break;
+ case 3:
+ ok(!!aSubject.document.getElementById("dlProgressCommands"),
+ "The progress dialog was called for an active download");
+ // active downloads updated to current time,
+ // just check if it's set to later than start of the test
+ ok(aSubject.gEndTime >= testStartTime, "End time within test run");
+
+ // Close the progress window
+ aSubject.close();
+
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+ };
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_removeDownload_updates_ui.xul b/comm/suite/components/downloads/tests/chrome/test_removeDownload_updates_ui.xul
new file mode 100644
index 0000000000..d5b5d6c657
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_removeDownload_updates_ui.xul
@@ -0,0 +1,150 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test bug 394039 to make sure calling removeDownload of the
+ * nsIDownloadManager service correctly updates the download window.
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+var dmFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+dmFile.append("dm-ui-test.file");
+dmFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+var gTestPath = Services.io.newFileURI(dmFile).spec;
+
+const DownloadData = [
+ { name: "381603.patch",
+ source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
+ target: gTestPath,
+ startTime: 1180493839859230,
+ endTime: 1180493839859239,
+ state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
+ currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 }
+];
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // First, we populate the database with some fake data
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
+ "state, currBytes, maxBytes, preferredAction, autoResume) " +
+ "VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
+ ":currBytes, :maxBytes, :preferredAction, :autoResume)");
+ for (let dl of DownloadData) {
+ for (let [prop, value] of Object.entries(dl))
+ stmt.params[prop] = value;
+
+ stmt.execute();
+ }
+ stmt.finalize();
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+ var doc = win.document;
+
+ // Note: This also tests the ordering of the display
+ var stmt = db.createStatement("SELECT id FROM moz_downloads");
+ var id = -1;
+ try {
+ stmt.executeStep();
+ id = stmt.getInt64(0);
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ dm.removeDownload(id);
+ var dlTreeView = doc.getElementById("downloadTree").view;
+ is(dlTreeView.rowCount, 0,
+ "The download was properly removed");
+
+ win.close();
+ dmFile.remove(false);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_search_clearlist.xul b/comm/suite/components/downloads/tests/chrome/test_search_clearlist.xul
new file mode 100644
index 0000000000..862291cd5d
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_search_clearlist.xul
@@ -0,0 +1,168 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test bug 431188 to make sure the Clear list button is enabled after doing a
+ * search and finding results.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // Make a file name for the downloads
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("cleanUp");
+ var filePath = Services.io.newFileURI(file).spec;
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (target, source, state, endTime) " +
+ "VALUES (?1, ?2, ?3, ?4)");
+
+ // Add a bunch of downloads that don't match the search
+ var sites = [];
+ for (let i = 0; i < 50; i++)
+ sites.push("i-hate.clear-list-" + i);
+
+ // Add one download that matches the search
+ var searchTerm = "one-download.match-search";
+ sites.push(searchTerm);
+
+ try {
+ for (let site of sites) {
+ stmt.bindByIndex(0, filePath);
+ stmt.bindByIndex(1, "http://" + site + "/file");
+ stmt.bindByIndex(2, dm.DOWNLOAD_FINISHED);
+ // Make the one that matches slightly older so it appears last
+ stmt.bindByIndex(3, 1112223334445556 - (site == searchTerm));
+
+ // Add it!
+ stmt.execute();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testPhase = 0;
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+ var downloadView = win.document.getElementById("downloadTree").view;
+ var searchbox = win.document.getElementById("search-box");
+ var clearList = win.document.getElementById("clearListButton");
+
+ // The list must have built, so figure out what test to do
+ switch (testPhase++) {
+ case 0:
+ case 2:
+ // Search for the one download
+ searchbox.value = searchTerm;
+ searchbox.doCommand();
+
+ break;
+ case 1:
+ // Search came back with 1 item
+ is(downloadView.rowCount, 1, "Search found the item to delete");
+ is(clearList.disabled, false, "Clear list is enabled for search matching 1 item");
+
+ // Clear the list that has the single matched item
+ clearList.doCommand();
+
+ break;
+ case 3:
+ // There's nothing that matches the search
+ is(downloadView.rowCount, 0, "Clear list killed the one matching download");
+ is(clearList.disabled, true, "Clear list is disabled for no items");
+
+ // We're done!
+ win.close();
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+
+ break;
+ }
+ }
+ };
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_search_keys.xul b/comm/suite/components/downloads/tests/chrome/test_search_keys.xul
new file mode 100644
index 0000000000..e651e341b5
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_search_keys.xul
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Make sure the download manager can display downloads in the right order and
+ * contains the expected data. The list has one of each download state ordered
+ * by the start/end times.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+function test_meta_f(aWin)
+{
+ var doc = aWin.document;
+ var searchbox = doc.getElementById("search-box");
+ var dlTree = doc.getElementById("downloadTree");
+
+ // Enusre the searchbox is not focused
+ dlTree.focus();
+
+ // Dispatch the right key combination
+ synthesizeKey("f", {accelKey: true}, aWin);
+
+ ok(searchbox.hasAttribute("focused"), "Searchbox is focused");
+}
+
+var testFuncs = [
+ test_meta_f,
+]
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ // Now we can run our tests
+ for (let t of testFuncs)
+ t(win);
+
+ win.close();
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_select_all.xul b/comm/suite/components/downloads/tests/chrome/test_select_all.xul
new file mode 100644
index 0000000000..85dadcb168
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_select_all.xul
@@ -0,0 +1,145 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test bug 429614 to make sure ctrl/cmd-a work to select all downloads and
+ * hitting delete removes them all.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ var db = dm.DBConnection;
+
+ // Empty any old downloads
+ db.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // Make a file name for the downloads
+ var file = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("selectAll");
+ var filePath = Services.io.newFileURI(file).spec;
+
+ var stmt = db.createStatement(
+ "INSERT INTO moz_downloads (target, source, state) " +
+ "VALUES (?1, ?2, ?3)");
+
+ var sites = ["mozilla.org", "mozilla.com", "select.all"];
+ try {
+ for (let site of sites) {
+ stmt.bindByIndex(0, filePath);
+ stmt.bindByIndex(1, "http://" + site + "/file");
+ stmt.bindByIndex(2, dm.DOWNLOAD_FINISHED);
+
+ // Add it!
+ stmt.execute();
+ }
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ var obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ var downloadView = win.document.getElementById("downloadTree").view;
+
+ is(downloadView.rowCount, sites.length, "All downloads displayed");
+
+ // Select all downloads
+ var isMac = AppConstants.platform == "macosx";
+ synthesizeKey("a", { metaKey: isMac, ctrlKey: !isMac }, win);
+ is(downloadView.selection.count, sites.length, "All downloads selected");
+
+ // Delete all downloads
+ synthesizeKey("VK_DELETE", {}, win);
+ is(downloadView.rowCount, 0, "All downloads removed");
+
+ // We're done!
+ win.close();
+ obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+
+ obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_space_key_pauses_resumes.xul b/comm/suite/components/downloads/tests/chrome/test_space_key_pauses_resumes.xul
new file mode 100644
index 0000000000..d22624be62
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_space_key_pauses_resumes.xul
@@ -0,0 +1,221 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests that the space key will pause and resume a download in the UI.
+ * This test was added in bug 413985.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+function bug413985obs(aWin)
+{
+ this.mWin = aWin;
+ this.wasPaused = false;
+ this.wasResumed = false;
+ this.wasFinished = false;
+}
+bug413985obs.prototype = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if ("timer-callback" == aTopic) {
+ if (this.wasFinished) {
+ // We're done!
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ dm.removeListener(this);
+ this.mWin.close();
+ SimpleTest.finish();
+ } else {
+ if (!this.wasPaused)
+ this.wasPaused = true;
+ else if (!this.wasResumed)
+ this.wasResumed = true;
+
+ // dispatch a space keypress to pause/resume the download
+ synthesizeKey(" ", {}, this.mWin);
+ }
+ }
+ },
+
+ onDownloadStateChange: function(aState, aDownload)
+ {
+ ok(true, "State value = " + aDownload.state);
+ switch (aDownload.state) {
+ case Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING:
+ if (!this.wasPaused) {
+ ok(true, "The download was started successfully");
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ } else {
+ ok(this.wasResumed, "The download was resumed successfully");
+ }
+ break;
+
+ case Ci.nsIDownloadManager.DOWNLOAD_PAUSED:
+ ok(this.wasPaused && !this.wasResumed,
+ "The download was paused successfully");
+ if (!this.wasResumed) {
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+ break;
+
+ case Ci.nsIDownloadManager.DOWNLOAD_FINISHED:
+ ok(this.wasPaused && this.wasResumed,
+ "The download was paused, and then resumed to completion");
+ this.wasFinished = true;
+ aDownload.targetFile.remove(false);
+
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ break;
+
+ default:
+ break;
+ }
+ },
+ onStateChange: function(a, b, c, d, e) { },
+ onProgressChange: function(a, b, c, d, e, f, g) { },
+ onSecurityChange: function(a, b, c, d) { }
+};
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ function addDownload() {
+ function createURI(aObj) {
+ return (aObj instanceof Ci.nsIFile) ? Services.io.newFileURI(aObj) :
+ Services.io.newURI(aObj);
+ }
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ var destFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ destFile.append("download.result");
+ if (destFile.exists())
+ destFile.remove(false);
+
+ // SeaMonkey: Use a bigger file than "http://example.com/httpd.js". (Bug 595685)
+ var dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+ createURI("http://example.com/tests/fonts/mplus/mplus-1p-regular.ttf"),
+ createURI(destFile), null, null,
+ Math.round(Date.now() * 1000), null, persist, false);
+
+ persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+ persist.saveURI(dl.source, null, null, 0, null, null, dl.targetFile, null);
+
+ return dl;
+ }
+
+ // First, we clear out the database
+ dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ var doc = win.document;
+ dm.addListener(new bug413985obs(win));
+
+ addDownload();
+ // we need to focus the download as well
+ doc.getElementById("downloadTree").view.selection.select(0);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_space_key_retries.xul b/comm/suite/components/downloads/tests/chrome/test_space_key_retries.xul
new file mode 100644
index 0000000000..5255f9caea
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_space_key_retries.xul
@@ -0,0 +1,198 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Jens Hatlak <jh@junetz.de> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests that the space key will retry a download in the UI.
+ * This test was added in bug 474622.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script>
+ <![CDATA[
+
+function dlObs(aWin)
+{
+ this.mWin = aWin;
+ this.wasCanceled = false;
+ this.wasFinished = false;
+}
+dlObs.prototype = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if ("timer-callback" == aTopic) {
+ if (this.wasFinished) {
+ // We're done!
+ this.mWin.close();
+ SimpleTest.finish();
+ } else {
+ // dispatch a space keypress to retry the download
+ synthesizeKey(" ", {}, this.mWin);
+ }
+ }
+ },
+
+ onDownloadStateChange: function(aState, aDownload)
+ {
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING &&
+ !this.wasCanceled) {
+ this.wasCanceled = true;
+ this.mWin.cancelDownload(aDownload);
+ }
+
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_CANCELED) {
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+
+ if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
+ ok(this.wasCanceled,
+ "The download was canceled, retried and then ran to completion");
+ this.wasFinished = true;
+ aDownload.targetFile.remove(false);
+
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+ dm.removeListener(this);
+
+ // We have to do this on a timer so other JS stuff that handles the UI
+ // can actually catch up to us...
+ var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+ timer.init(this, 0, Ci.nsITimer.TYPE_ONE_SHOT);
+ }
+ },
+ onStateChange: function(a, b, c, d, e) { },
+ onProgressChange: function(a, b, c, d, e, f, g) { },
+ onSecurityChange: function(a, b, c, d) { }
+};
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ function addDownload() {
+ function createURI(aObj) {
+ return (aObj instanceof Ci.nsIFile) ? Services.io.newFileURI(aObj) :
+ Services.io.newURI(aObj);
+ }
+
+ const nsIWBP = Ci.nsIWebBrowserPersist;
+ var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+ .createInstance(nsIWBP);
+ persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+ nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+ nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+ var destFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ destFile.append("download.result");
+ if (destFile.exists())
+ destFile.remove(false);
+
+ var dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+ createURI("http://example.com/httpd.js"),
+ createURI(destFile), null, null,
+ Math.round(Date.now() * 1000), null, persist, false);
+
+ persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+ persist.saveURI(dl.source, null, null, 0, null, null, dl.targetFile, null);
+
+ return dl;
+ }
+
+ // First, we clear out the database
+ dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ // See if the DM is already open, and if it is, close it!
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win)
+ win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ SimpleTest.waitForFocus(function () { continueTest(aSubject) }, aSubject);
+ }
+ };
+
+ function continueTest(win) {
+ var doc = win.document;
+ dm.addListener(new dlObs(win));
+
+ addDownload();
+ // we need to focus the download as well
+ doc.getElementById("downloadTree").view.selection.select(0);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ }
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Show the Download Manager UI
+ Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsISuiteDownloadManagerUI)
+ .showManager();
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>
diff --git a/comm/suite/components/downloads/tests/chrome/test_ui_stays_open_on_alert_clickback.xul b/comm/suite/components/downloads/tests/chrome/test_ui_stays_open_on_alert_clickback.xul
new file mode 100644
index 0000000000..f54fb2dc21
--- /dev/null
+++ b/comm/suite/components/downloads/tests/chrome/test_ui_stays_open_on_alert_clickback.xul
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<!--
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Download Manager UI Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Edward Lee <edward.lee@engineering.uiuc.edu>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Test bug 397935 to make sure the download manager ui window stays open when
+ * it's opened by the user clicking the alert and has the close-when-done pref
+ * set.
+ */
+-->
+
+<window title="Download Manager Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/MochiKit/packed.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script>
+ <![CDATA[
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+function test()
+{
+ var dm = Cc["@mozilla.org/download-manager;1"]
+ .getService(Ci.nsIDownloadManager);
+
+ // Empty any old downloads
+ dm.DBConnection.executeSimpleSQL("DELETE FROM moz_downloads");
+
+ var setClose = aVal =>
+ Services.prefs.setBoolPref("browser.download.manager.closeWhenDone", aVal);
+
+ // Close the UI if necessary
+ var win = Services.wm.getMostRecentWindow("Download:Manager");
+ if (win) win.close();
+
+ const DLMGR_UI_DONE = "download-manager-ui-done";
+
+ var testObs = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic != DLMGR_UI_DONE)
+ return;
+
+ var win = aSubject;
+
+ // Note: This test will not be valid if the download list is built
+ // synchronously in Startup in downloads.js
+ // Make sure the window stays open
+ var dmui = Cc["@mozilla.org/download-manager-ui;1"]
+ .getService(Ci.nsIDownloadManagerUI);
+ ok(dmui.visible, "Download Manager stays open on alert click");
+
+ win.close();
+ setClose(false);
+ Services.obs.removeObserver(testObs, DLMGR_UI_DONE);
+ SimpleTest.finish();
+ }
+ };
+
+ // Register with the observer service
+ Services.obs.addObserver(testObs, DLMGR_UI_DONE);
+
+ // Simulate an alert click with pref set to true
+ setClose(true);
+ dm.QueryInterface(Ci.nsIObserver)
+ .observe(null, "alertclickcallback", null);
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>