/* 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/. */ /* * This module is imported by code that uses the "download.xml" binding, and * provides prototypes for objects that handle input and display information. */ "use strict"; var EXPORTED_SYMBOLS = ["DownloadsViewUI"]; const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); XPCOMUtils.defineLazyModuleGetters(this, { BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm", Downloads: "resource://gre/modules/Downloads.jsm", DownloadUtils: "resource://gre/modules/DownloadUtils.jsm", DownloadsCommon: "resource:///modules/DownloadsCommon.jsm", FileUtils: "resource://gre/modules/FileUtils.jsm", OS: "resource://gre/modules/osfile.jsm", }); XPCOMUtils.defineLazyServiceGetter( this, "handlerSvc", "@mozilla.org/uriloader/handler-service;1", "nsIHandlerService" ); const { Integration } = ChromeUtils.import( "resource://gre/modules/Integration.jsm" ); /* global DownloadIntegration */ Integration.downloads.defineModuleGetter( this, "DownloadIntegration", "resource://gre/modules/DownloadIntegration.jsm" ); const HTML_NS = "http://www.w3.org/1999/xhtml"; var gDownloadElementButtons = { cancel: { commandName: "downloadsCmd_cancel", l10nId: "downloads-cmd-cancel", descriptionL10nId: "downloads-cancel-download", panelL10nId: "downloads-cmd-cancel-panel", iconClass: "downloadIconCancel", }, retry: { commandName: "downloadsCmd_retry", l10nId: "downloads-cmd-retry", descriptionL10nId: "downloads-retry-download", panelL10nId: "downloads-cmd-retry-panel", iconClass: "downloadIconRetry", }, show: { commandName: "downloadsCmd_show", l10nId: "downloads-cmd-show-button", descriptionL10nId: "downloads-cmd-show-description", panelL10nId: "downloads-cmd-show-panel", iconClass: "downloadIconShow", }, subviewOpenOrRemoveFile: { commandName: "downloadsCmd_showBlockedInfo", l10nId: "downloads-cmd-choose-open", descriptionL10nId: "downloads-show-more-information", panelL10nId: "downloads-cmd-choose-open-panel", iconClass: "downloadIconSubviewArrow", }, askOpenOrRemoveFile: { commandName: "downloadsCmd_chooseOpen", l10nId: "downloads-cmd-choose-open", panelL10nId: "downloads-cmd-choose-open-panel", iconClass: "downloadIconShow", }, askRemoveFileOrAllow: { commandName: "downloadsCmd_chooseUnblock", l10nId: "downloads-cmd-choose-unblock", panelL10nId: "downloads-cmd-choose-unblock-panel", iconClass: "downloadIconShow", }, removeFile: { commandName: "downloadsCmd_confirmBlock", l10nId: "downloads-cmd-remove-file", panelL10nId: "downloads-cmd-remove-file-panel", iconClass: "downloadIconCancel", }, }; /** * Associates each document with a pre-built DOM fragment representing the * download list item. This is then cloned to create each individual list item. * This is stored on the document to prevent leaks that would occur if a single * instance created by one document's DOMParser was stored globally. */ var gDownloadListItemFragments = new WeakMap(); var DownloadsViewUI = { /** * Returns true if the given string is the name of a command that can be * handled by the Downloads user interface, including standard commands. */ isCommandName(name) { return name.startsWith("cmd_") || name.startsWith("downloadsCmd_"); }, /** * Returns the user-facing label for the given Download object. This is * normally the leaf name of the download target file. In case this is a very * old history download for which the target file is unknown, the download * source URI is displayed. */ getDisplayName(download) { return download.target.path ? OS.Path.basename(download.target.path) : download.source.url; }, /** * Given a Download object, returns a string representing its file size with * an appropriate measurement unit, for example "1.5 MB", or an empty string * if the size is unknown. */ getSizeWithUnits(download) { if (download.target.size === undefined) { return ""; } let [size, unit] = DownloadUtils.convertByteUnits(download.target.size); return DownloadsCommon.strings.sizeWithUnits(size, unit); }, }; DownloadsViewUI.BaseView = class { canClearDownloads(nodeContainer) { // Downloads can be cleared if there's at least one removable download in // the list (either a history download or a completed session download). // Because history downloads are always removable and are listed after the // session downloads, check from bottom to top. for (let elt = nodeContainer.lastChild; elt; elt = elt.previousSibling) { // Stopped, paused, and failed downloads with partial data are removed. let download = elt._shell.download; if (download.stopped && !(download.canceled && download.hasPartialData)) { return true; } } return false; } }; /** * A download element shell is responsible for handling the commands and the * displayed data for a single element that uses the "download.xml" binding. * * The information to display is obtained through the associated Download object * from the JavaScript API for downloads, and commands are executed using a * combination of Download methods and DownloadsCommon.jsm helper functions. * * Specialized versions of this shell must be defined, and they are required to * implement the "download" property or getter. Currently these objects are the * HistoryDownloadElementShell and the DownloadsViewItem for the panel. The * history view may use a HistoryDownload object in place of a Download object. */ DownloadsViewUI.DownloadElementShell = function() {}; DownloadsViewUI.DownloadElementShell.prototype = { /** * The richlistitem for the download, initialized by the derived object. */ element: null, /** * Manages the "active" state of the shell. By default all the shells are * inactive, thus their UI is not updated. They must be activated when * entering the visible area. */ ensureActive() { if (!this._active) { this._active = true; this.connect(); this.onChanged(); } }, get active() { return !!this._active; }, connect() { let document = this.element.ownerDocument; let downloadListItemFragment = gDownloadListItemFragments.get(document); // When changing the markup within the fragment, please ensure that // the functions within DownloadsView still operate correctly. // E.g. onDownloadClick() relies on brittle logic and performs/prevents // actions based on the check if originaltarget was not a button. if (!downloadListItemFragment) { let MozXULElement = document.defaultView.MozXULElement; downloadListItemFragment = MozXULElement.parseXULToFragment(`