Index: chrome/browser/resources/downloads/downloads.js |
diff --git a/chrome/browser/resources/downloads/downloads.js b/chrome/browser/resources/downloads/downloads.js |
deleted file mode 100644 |
index 0a074b7bf28f290c4615c3195488864ef9ee92f1..0000000000000000000000000000000000000000 |
--- a/chrome/browser/resources/downloads/downloads.js |
+++ /dev/null |
@@ -1,1078 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-// TODO(hcarmona): This file is big: it may be good to split it up. |
- |
-/** |
- * The type of the download object. The definition is based on |
- * chrome/browser/ui/webui/downloads_dom_handler.cc:CreateDownloadItemValue() |
- * @typedef {{by_ext_id: (string|undefined), |
- * by_ext_name: (string|undefined), |
- * danger_type: (string|undefined), |
- * date_string: string, |
- * file_externally_removed: boolean, |
- * file_name: string, |
- * file_path: string, |
- * file_url: string, |
- * id: string, |
- * last_reason_text: (string|undefined), |
- * otr: boolean, |
- * percent: (number|undefined), |
- * progress_status_text: (string|undefined), |
- * received: (number|undefined), |
- * resume: boolean, |
- * retry: boolean, |
- * since_string: string, |
- * started: number, |
- * state: string, |
- * total: number, |
- * url: string}} |
- */ |
-var DownloadItem; |
- |
-/** |
- * Creates a link with a specified onclick handler and content. |
- * @param {function()} onclick The onclick handler. |
- * @param {string=} opt_text The link text. |
- * @return {!Element} The created link element. |
- */ |
-function createActionLink(onclick, opt_text) { |
- var link = new ActionLink; |
- link.onclick = onclick; |
- if (opt_text) link.textContent = opt_text; |
- return link; |
-} |
- |
-/** |
- * Creates a button with a specified onclick handler and content. |
- * @param {function()} onclick The onclick handler. |
- * @param {string} value The button text. |
- * @return {Element} The created button. |
- */ |
-function createButton(onclick, value) { |
- var button = document.createElement('input'); |
- button.type = 'button'; |
- button.value = value; |
- button.onclick = onclick; |
- return button; |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-// DownloadFocusRow: |
- |
-/** |
- * Provides an implementation for a single column grid. |
- * @constructor |
- * @extends {cr.ui.FocusRow} |
- */ |
-function DownloadFocusRow() {} |
- |
-/** |
- * Decorates |focusRow| so that it can be treated as a DownloadFocusRow. |
- * @param {Element} focusRow The element that has all the columns represented |
- * by |download|. |
- * @param {Download} download The Download representing this row. |
- * @param {Node} boundary Focus events are ignored outside of this node. |
- */ |
-DownloadFocusRow.decorate = function(focusRow, download, boundary) { |
- focusRow.__proto__ = DownloadFocusRow.prototype; |
- focusRow.decorate(boundary); |
- |
- // Add all clickable elements as a row into the grid. |
- focusRow.addElementIfFocusable_(download.nodeFileLink_, 'name'); |
- focusRow.addElementIfFocusable_(download.nodeURL_, 'url'); |
- focusRow.addElementIfFocusable_(download.controlShow_, 'show'); |
- focusRow.addElementIfFocusable_(download.controlRetry_, 'retry'); |
- focusRow.addElementIfFocusable_(download.controlPause_, 'pause'); |
- focusRow.addElementIfFocusable_(download.controlResume_, 'resume'); |
- focusRow.addElementIfFocusable_(download.controlRemove_, 'remove'); |
- focusRow.addElementIfFocusable_(download.controlCancel_, 'cancel'); |
- focusRow.addElementIfFocusable_(download.malwareSave_, 'save'); |
- focusRow.addElementIfFocusable_(download.dangerSave_, 'save'); |
- focusRow.addElementIfFocusable_(download.malwareDiscard_, 'discard'); |
- focusRow.addElementIfFocusable_(download.dangerDiscard_, 'discard'); |
- focusRow.addElementIfFocusable_(download.controlByExtensionLink_, |
- 'extension'); |
-}; |
- |
-DownloadFocusRow.prototype = { |
- __proto__: cr.ui.FocusRow.prototype, |
- |
- /** @override */ |
- getEquivalentElement: function(element) { |
- if (this.focusableElements.indexOf(element) > -1) |
- return element; |
- |
- // All elements default to another element with the same type. |
- var columnType = element.getAttribute('column-type'); |
- var equivalent = this.querySelector('[column-type=' + columnType + ']'); |
- |
- if (this.focusableElements.indexOf(equivalent) < 0) { |
- var equivalentTypes = |
- ['show', 'retry', 'pause', 'resume', 'remove', 'cancel']; |
- if (equivalentTypes.indexOf(columnType) != -1) { |
- var allTypes = equivalentTypes.map(function(type) { |
- return '[column-type=' + type + ']:not([hidden])'; |
- }).join(', '); |
- equivalent = this.querySelector(allTypes); |
- } |
- } |
- |
- // Return the first focusable element if no equivalent element is found. |
- return equivalent || this.focusableElements[0]; |
- }, |
- |
- /** |
- * @param {Element} element The element that should be added. |
- * @param {string} type The column type to use for the element. |
- * @private |
- */ |
- addElementIfFocusable_: function(element, type) { |
- if (this.shouldFocus_(element)) { |
- this.addFocusableElement(element); |
- element.setAttribute('column-type', type); |
- } |
- }, |
- |
- /** |
- * Determines if element should be focusable. |
- * @param {Element} element |
- * @return {boolean} |
- * @private |
- */ |
- shouldFocus_: function(element) { |
- if (!element) |
- return false; |
- |
- // Hidden elements are not focusable. |
- var style = window.getComputedStyle(element); |
- if (style.visibility == 'hidden' || style.display == 'none') |
- return false; |
- |
- // Verify all ancestors are focusable. |
- return !element.parentElement || this.shouldFocus_(element.parentElement); |
- }, |
-}; |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-// Downloads |
-/** |
- * Class to hold all the information about the visible downloads. |
- * @constructor |
- */ |
-function Downloads() { |
- /** |
- * @type {!Object<string, Download>} |
- * @private |
- */ |
- this.downloads_ = {}; |
- this.node_ = $('downloads-display'); |
- this.summary_ = $('downloads-summary-text'); |
- this.searchText_ = ''; |
- this.focusGrid_ = new cr.ui.FocusGrid(); |
- |
- // Keep track of the dates of the newest and oldest downloads so that we |
- // know where to insert them. |
- this.newestTime_ = -1; |
- |
- // Icon load request queue. |
- this.iconLoadQueue_ = []; |
- this.isIconLoading_ = false; |
- |
- this.progressForeground1_ = new Image(); |
- this.progressForeground1_.src = |
- 'chrome://theme/IDR_DOWNLOAD_PROGRESS_FOREGROUND_32@1x'; |
- this.progressForeground2_ = new Image(); |
- this.progressForeground2_.src = |
- 'chrome://theme/IDR_DOWNLOAD_PROGRESS_FOREGROUND_32@2x'; |
- |
- cr.ui.decorate('command', cr.ui.Command); |
- document.addEventListener('canExecute', this.onCanExecute_.bind(this)); |
- document.addEventListener('command', this.onCommand_.bind(this)); |
-} |
- |
-/** |
- * Called when a download has been updated or added. |
- * @param {DownloadItem} download Information about a download. |
- */ |
-Downloads.prototype.updated = function(download) { |
- var id = download.id; |
- if (this.downloads_[id]) { |
- this.downloads_[id].update(download); |
- } else { |
- this.downloads_[id] = new Download(download); |
- // We get downloads in display order, so we don't have to worry about |
- // maintaining correct order - we can assume that any downloads not in |
- // display order are new ones and so we can add them to the top of the |
- // list. |
- if (download.started > this.newestTime_) { |
- this.node_.insertBefore(this.downloads_[id].node, this.node_.firstChild); |
- this.newestTime_ = download.started; |
- } else { |
- this.node_.appendChild(this.downloads_[id].node); |
- } |
- } |
- // Download.prototype.update may change its nodeSince_ and nodeDate_, so |
- // update all the date displays. |
- // TODO(benjhayden) Only do this if its nodeSince_ or nodeDate_ actually did |
- // change since this may touch 150 elements and Downloads.prototype.updated |
- // may be called 150 times. |
- this.onDownloadListChanged_(); |
-}; |
- |
-/** |
- * Set our display search text. |
- * @param {string} searchText The string we're searching for. |
- */ |
-Downloads.prototype.setSearchText = function(searchText) { |
- this.searchText_ = searchText; |
-}; |
- |
-/** Update the summary block above the results. */ |
-Downloads.prototype.updateSummary = function() { |
- if (this.searchText_) { |
- this.summary_.textContent = loadTimeData.getStringF('searchresultsfor', |
- this.searchText_); |
- } else { |
- this.summary_.textContent = ''; |
- } |
-}; |
- |
-/** |
- * Called when either a search or load completes to update whether there are |
- * results or not. |
- */ |
-Downloads.prototype.updateResults = function() { |
- var noDownloadsOrResults = $('no-downloads-or-results'); |
- noDownloadsOrResults.textContent = loadTimeData.getString( |
- this.searchText_ ? 'no_search_results' : 'no_downloads'); |
- |
- var hasDownloads = this.size() > 0; |
- this.node_.hidden = !hasDownloads; |
- noDownloadsOrResults.hidden = hasDownloads; |
- |
- if (loadTimeData.getBoolean('allow_deleting_history')) |
- $('clear-all').hidden = !hasDownloads || this.searchText_.length > 0; |
- |
- this.rebuildFocusGrid_(); |
-}; |
- |
-/** |
- * Rebuild the focusGrid_ using the elements that each download will have. |
- * @private |
- */ |
-Downloads.prototype.rebuildFocusGrid_ = function() { |
- var activeElement = document.activeElement; |
- this.focusGrid_.destroy(); |
- |
- var keys = Object.keys(this.downloads_); |
- for (var i = 0; i < keys.length; ++i) { |
- var download = this.downloads_[keys[i]]; |
- DownloadFocusRow.decorate(download.node, download, this.node_); |
- } |
- |
- // The ordering of the keys is not guaranteed, and downloads should be added |
- // to the FocusGrid in the order they will be in the UI. |
- var downloads = document.querySelectorAll('.download'); |
- for (var i = 0; i < downloads.length; ++i) { |
- var focusRow = downloads[i]; |
- this.focusGrid_.addRow(focusRow); |
- |
- // Focus the equivalent element in the focusRow because the active element |
- // may no longer be visible. |
- if (focusRow.contains(activeElement)) |
- focusRow.getEquivalentElement(activeElement).focus(); |
- } |
-}; |
- |
-/** |
- * Returns the number of downloads in the model. Used by tests. |
- * @return {number} Returns the number of downloads shown on the page. |
- */ |
-Downloads.prototype.size = function() { |
- return Object.keys(this.downloads_).length; |
-}; |
- |
-/** |
- * Called whenever the downloads lists items have changed (either by being |
- * updated, added, or removed). |
- * @private |
- */ |
-Downloads.prototype.onDownloadListChanged_ = function() { |
- // Update the date visibility in our nodes so that no date is repeated. |
- var dateContainers = document.getElementsByClassName('date-container'); |
- var displayed = {}; |
- for (var i = 0, container; container = dateContainers[i]; i++) { |
- var dateString = container.getElementsByClassName('date')[0].innerHTML; |
- if (displayed[dateString]) { |
- container.style.display = 'none'; |
- } else { |
- displayed[dateString] = true; |
- container.style.display = 'block'; |
- } |
- } |
- |
- this.updateResults(); |
-}; |
- |
-/** |
- * Remove a download. |
- * @param {string} id The id of the download to remove. |
- */ |
-Downloads.prototype.remove = function(id) { |
- this.node_.removeChild(this.downloads_[id].node); |
- delete this.downloads_[id]; |
- this.onDownloadListChanged_(); |
-}; |
- |
-/** Clear all downloads and reset us back to a null state. */ |
-Downloads.prototype.clear = function() { |
- for (var id in this.downloads_) { |
- this.downloads_[id].clear(); |
- this.remove(id); |
- } |
-}; |
- |
-/** |
- * Schedule icon load. |
- * @param {HTMLImageElement} elem Image element that should contain the icon. |
- * @param {string} iconURL URL to the icon. |
- */ |
-Downloads.prototype.scheduleIconLoad = function(elem, iconURL) { |
- var self = this; |
- |
- // Sends request to the next icon in the queue and schedules |
- // call to itself when the icon is loaded. |
- function loadNext() { |
- self.isIconLoading_ = true; |
- while (self.iconLoadQueue_.length > 0) { |
- var request = self.iconLoadQueue_.shift(); |
- var oldSrc = request.element.src; |
- request.element.onabort = request.element.onerror = |
- request.element.onload = loadNext; |
- request.element.src = request.url; |
- if (oldSrc != request.element.src) |
- return; |
- } |
- self.isIconLoading_ = false; |
- } |
- |
- // Create new request |
- var loadRequest = {element: elem, url: iconURL}; |
- this.iconLoadQueue_.push(loadRequest); |
- |
- // Start loading if none scheduled yet |
- if (!this.isIconLoading_) |
- loadNext(); |
-}; |
- |
-/** |
- * Returns whether the displayed list needs to be updated or not. |
- * @param {Array} downloads Array of download nodes. |
- * @return {boolean} Returns true if the displayed list is to be updated. |
- */ |
-Downloads.prototype.isUpdateNeeded = function(downloads) { |
- var size = 0; |
- for (var i in this.downloads_) |
- size++; |
- if (size != downloads.length) |
- return true; |
- // Since there are the same number of items in the incoming list as |
- // |this.downloads_|, there won't be any removed downloads without some |
- // downloads having been inserted. So check only for new downloads in |
- // deciding whether to update. |
- for (var i = 0; i < downloads.length; i++) { |
- if (!this.downloads_[downloads[i].id]) |
- return true; |
- } |
- return false; |
-}; |
- |
-/** |
- * @param {Event} e |
- * @private |
- */ |
-Downloads.prototype.onCanExecute_ = function(e) { |
- e = /** @type {cr.ui.CanExecuteEvent} */(e); |
- e.canExecute = document.activeElement != $('term'); |
-}; |
- |
-/** |
- * @param {Event} e |
- * @private |
- */ |
-Downloads.prototype.onCommand_ = function(e) { |
- if (e.command.id == 'undo-command') |
- chrome.send('undo'); |
- else if (e.command.id == 'clear-all-command') |
- clearAll(); |
-}; |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-// Download |
-/** |
- * A download and the DOM representation for that download. |
- * @param {DownloadItem} download Info about the download. |
- * @constructor |
- */ |
-function Download(download) { |
- // Create DOM |
- this.node = createElementWithClassName( |
- 'div', 'download' + (download.otr ? ' otr' : '')); |
- |
- // Dates |
- this.dateContainer_ = createElementWithClassName('div', 'date-container'); |
- this.node.appendChild(this.dateContainer_); |
- |
- this.nodeSince_ = createElementWithClassName('div', 'since'); |
- this.nodeDate_ = createElementWithClassName('div', 'date'); |
- this.dateContainer_.appendChild(this.nodeSince_); |
- this.dateContainer_.appendChild(this.nodeDate_); |
- |
- // Container for all 'safe download' UI. |
- this.safe_ = createElementWithClassName('div', 'safe'); |
- this.safe_.ondragstart = this.drag_.bind(this); |
- this.node.appendChild(this.safe_); |
- |
- if (download.state != Download.States.COMPLETE) { |
- this.nodeProgressBackground_ = |
- createElementWithClassName('div', 'progress background'); |
- this.safe_.appendChild(this.nodeProgressBackground_); |
- |
- this.nodeProgressForeground_ = |
- createElementWithClassName('canvas', 'progress'); |
- this.nodeProgressForeground_.width = Download.Progress.width; |
- this.nodeProgressForeground_.height = Download.Progress.height; |
- this.canvasProgress_ = this.nodeProgressForeground_.getContext('2d'); |
- |
- this.safe_.appendChild(this.nodeProgressForeground_); |
- } |
- |
- this.nodeImg_ = createElementWithClassName('img', 'icon'); |
- this.nodeImg_.alt = ''; |
- this.safe_.appendChild(this.nodeImg_); |
- |
- // FileLink is used for completed downloads, otherwise we show FileName. |
- this.nodeTitleArea_ = createElementWithClassName('div', 'title-area'); |
- this.safe_.appendChild(this.nodeTitleArea_); |
- |
- this.nodeFileLink_ = createActionLink(this.openFile_.bind(this)); |
- this.nodeFileLink_.className = 'name'; |
- this.nodeTitleArea_.appendChild(this.nodeFileLink_); |
- |
- this.nodeFileName_ = createElementWithClassName('span', 'name'); |
- this.nodeTitleArea_.appendChild(this.nodeFileName_); |
- |
- this.nodeStatus_ = createElementWithClassName('span', 'status'); |
- this.nodeTitleArea_.appendChild(this.nodeStatus_); |
- |
- var nodeURLDiv = createElementWithClassName('div', 'url-container'); |
- this.safe_.appendChild(nodeURLDiv); |
- |
- this.nodeURL_ = createElementWithClassName('a', 'src-url'); |
- this.nodeURL_.target = '_blank'; |
- nodeURLDiv.appendChild(this.nodeURL_); |
- |
- // Controls. |
- this.nodeControls_ = createElementWithClassName('div', 'controls'); |
- this.safe_.appendChild(this.nodeControls_); |
- |
- // We don't need 'show in folder' in chromium os. See download_ui.cc and |
- // http://code.google.com/p/chromium-os/issues/detail?id=916. |
- if (loadTimeData.valueExists('control_showinfolder')) { |
- this.controlShow_ = createActionLink(this.show_.bind(this), |
- loadTimeData.getString('control_showinfolder')); |
- this.nodeControls_.appendChild(this.controlShow_); |
- } else { |
- this.controlShow_ = null; |
- } |
- |
- this.controlRetry_ = document.createElement('a'); |
- this.controlRetry_.download = ''; |
- this.controlRetry_.textContent = loadTimeData.getString('control_retry'); |
- this.nodeControls_.appendChild(this.controlRetry_); |
- |
- // Pause/Resume are a toggle. |
- this.controlPause_ = createActionLink(this.pause_.bind(this), |
- loadTimeData.getString('control_pause')); |
- this.nodeControls_.appendChild(this.controlPause_); |
- |
- this.controlResume_ = createActionLink(this.resume_.bind(this), |
- loadTimeData.getString('control_resume')); |
- this.nodeControls_.appendChild(this.controlResume_); |
- |
- if (loadTimeData.getBoolean('allow_deleting_history')) { |
- this.controlRemove_ = createActionLink(this.remove_.bind(this), |
- loadTimeData.getString('control_removefromlist')); |
- this.controlRemove_.classList.add('control-remove-link'); |
- this.nodeControls_.appendChild(this.controlRemove_); |
- } |
- |
- this.controlCancel_ = createActionLink(this.cancel_.bind(this), |
- loadTimeData.getString('control_cancel')); |
- this.nodeControls_.appendChild(this.controlCancel_); |
- |
- this.controlByExtension_ = document.createElement('span'); |
- this.nodeControls_.appendChild(this.controlByExtension_); |
- |
- // Container for 'unsafe download' UI. |
- this.danger_ = createElementWithClassName('div', 'show-dangerous'); |
- this.node.appendChild(this.danger_); |
- |
- this.dangerNodeImg_ = createElementWithClassName('img', 'icon'); |
- this.dangerNodeImg_.alt = ''; |
- this.danger_.appendChild(this.dangerNodeImg_); |
- |
- this.dangerDesc_ = document.createElement('div'); |
- this.danger_.appendChild(this.dangerDesc_); |
- |
- // Buttons for the malicious case. |
- this.malwareNodeControls_ = createElementWithClassName('div', 'controls'); |
- this.malwareSave_ = createActionLink( |
- this.saveDangerous_.bind(this), |
- loadTimeData.getString('danger_restore')); |
- this.malwareNodeControls_.appendChild(this.malwareSave_); |
- this.malwareDiscard_ = createActionLink( |
- this.discardDangerous_.bind(this), |
- loadTimeData.getString('control_removefromlist')); |
- this.malwareNodeControls_.appendChild(this.malwareDiscard_); |
- this.danger_.appendChild(this.malwareNodeControls_); |
- |
- // Buttons for the dangerous but not malicious case. |
- this.dangerSave_ = createButton( |
- this.saveDangerous_.bind(this), |
- loadTimeData.getString('danger_save')); |
- this.danger_.appendChild(this.dangerSave_); |
- |
- this.dangerDiscard_ = createButton( |
- this.discardDangerous_.bind(this), |
- loadTimeData.getString('danger_discard')); |
- this.danger_.appendChild(this.dangerDiscard_); |
- |
- // Update member vars. |
- this.update(download); |
-} |
- |
-/** |
- * The states a download can be in. These correspond to states defined in |
- * DownloadsDOMHandler::CreateDownloadItemValue |
- * @enum {string} |
- */ |
-Download.States = { |
- IN_PROGRESS: 'IN_PROGRESS', |
- CANCELLED: 'CANCELLED', |
- COMPLETE: 'COMPLETE', |
- PAUSED: 'PAUSED', |
- DANGEROUS: 'DANGEROUS', |
- INTERRUPTED: 'INTERRUPTED', |
-}; |
- |
-/** |
- * Explains why a download is in DANGEROUS state. |
- * @enum {string} |
- */ |
-Download.DangerType = { |
- NOT_DANGEROUS: 'NOT_DANGEROUS', |
- DANGEROUS_FILE: 'DANGEROUS_FILE', |
- DANGEROUS_URL: 'DANGEROUS_URL', |
- DANGEROUS_CONTENT: 'DANGEROUS_CONTENT', |
- UNCOMMON_CONTENT: 'UNCOMMON_CONTENT', |
- DANGEROUS_HOST: 'DANGEROUS_HOST', |
- POTENTIALLY_UNWANTED: 'POTENTIALLY_UNWANTED', |
-}; |
- |
-/** |
- * @param {number} a Some float. |
- * @param {number} b Some float. |
- * @param {number=} opt_pct Percent of min(a,b). |
- * @return {boolean} true if a is within opt_pct percent of b. |
- */ |
-function floatEq(a, b, opt_pct) { |
- return Math.abs(a - b) < (Math.min(a, b) * (opt_pct || 1.0) / 100.0); |
-} |
- |
-/** Constants and "constants" for the progress meter. */ |
-Download.Progress = { |
- START_ANGLE: -0.5 * Math.PI, |
- SIDE: 48, |
-}; |
- |
-/***/ |
-Download.Progress.HALF = Download.Progress.SIDE / 2; |
- |
-function computeDownloadProgress() { |
- if (floatEq(Download.Progress.scale, window.devicePixelRatio)) { |
- // Zooming in or out multiple times then typing Ctrl+0 resets the zoom level |
- // directly to 1x, which fires the matchMedia event multiple times. |
- return; |
- } |
- Download.Progress.scale = window.devicePixelRatio; |
- Download.Progress.width = Download.Progress.SIDE * Download.Progress.scale; |
- Download.Progress.height = Download.Progress.SIDE * Download.Progress.scale; |
- Download.Progress.radius = Download.Progress.HALF * Download.Progress.scale; |
- Download.Progress.centerX = Download.Progress.HALF * Download.Progress.scale; |
- Download.Progress.centerY = Download.Progress.HALF * Download.Progress.scale; |
-} |
-computeDownloadProgress(); |
- |
-// Listens for when device-pixel-ratio changes between any zoom level. |
-[0.3, 0.4, 0.6, 0.7, 0.8, 0.95, 1.05, 1.2, 1.4, 1.6, 1.9, 2.2, 2.7, 3.5, 4.5 |
-].forEach(function(scale) { |
- var media = '(-webkit-min-device-pixel-ratio:' + scale + ')'; |
- window.matchMedia(media).addListener(computeDownloadProgress); |
-}); |
- |
-/** |
- * Updates the download to reflect new data. |
- * @param {DownloadItem} download Updated info about this download. |
- */ |
-Download.prototype.update = function(download) { |
- this.id_ = download.id; |
- this.filePath_ = download.file_path; |
- this.fileUrl_ = download.file_url; |
- this.fileName_ = download.file_name; |
- this.url_ = download.url; |
- this.state_ = download.state; |
- this.fileExternallyRemoved_ = download.file_externally_removed; |
- this.dangerType_ = download.danger_type; |
- this.lastReasonDescription_ = download.last_reason_text; |
- this.byExtensionId_ = download.by_ext_id; |
- this.byExtensionName_ = download.by_ext_name; |
- |
- this.since_ = download.since_string; |
- this.date_ = download.date_string; |
- |
- // See DownloadItem::PercentComplete |
- this.percent_ = Math.max(download.percent, 0); |
- this.progressStatusText_ = download.progress_status_text; |
- this.received_ = download.received; |
- |
- if (this.state_ == Download.States.DANGEROUS) { |
- this.updateDangerousFile(); |
- } else { |
- downloads.scheduleIconLoad(this.nodeImg_, |
- 'chrome://fileicon/' + |
- encodeURIComponent(this.filePath_) + |
- '?scale=' + window.devicePixelRatio + 'x'); |
- |
- if (this.state_ == Download.States.COMPLETE && |
- !this.fileExternallyRemoved_) { |
- this.nodeFileLink_.textContent = this.fileName_; |
- this.nodeFileLink_.href = this.fileUrl_; |
- this.nodeFileLink_.oncontextmenu = null; |
- } else if (this.nodeFileName_.textContent != this.fileName_) { |
- this.nodeFileName_.textContent = this.fileName_; |
- } |
- if (this.state_ == Download.States.INTERRUPTED) { |
- this.nodeFileName_.classList.add('interrupted'); |
- } else if (this.nodeFileName_.classList.contains('interrupted')) { |
- this.nodeFileName_.classList.remove('interrupted'); |
- } |
- |
- var completelyOnDisk = this.state_ == Download.States.COMPLETE && |
- !this.fileExternallyRemoved_; |
- this.nodeFileName_.hidden = completelyOnDisk; |
- this.nodeFileLink_.hidden = !completelyOnDisk; |
- |
- if (this.state_ == Download.States.IN_PROGRESS) { |
- this.nodeProgressForeground_.style.display = 'block'; |
- this.nodeProgressBackground_.style.display = 'block'; |
- this.nodeProgressForeground_.width = Download.Progress.width; |
- this.nodeProgressForeground_.height = Download.Progress.height; |
- |
- var foregroundImage = (window.devicePixelRatio < 2) ? |
- downloads.progressForeground1_ : downloads.progressForeground2_; |
- |
- // Draw a pie-slice for the progress. |
- this.canvasProgress_.globalCompositeOperation = 'copy'; |
- this.canvasProgress_.drawImage( |
- foregroundImage, |
- 0, 0, // sx, sy |
- foregroundImage.width, |
- foregroundImage.height, |
- 0, 0, // x, y |
- Download.Progress.width, Download.Progress.height); |
- this.canvasProgress_.globalCompositeOperation = 'destination-in'; |
- this.canvasProgress_.beginPath(); |
- this.canvasProgress_.moveTo(Download.Progress.centerX, |
- Download.Progress.centerY); |
- |
- // Draw an arc CW for both RTL and LTR. http://crbug.com/13215 |
- this.canvasProgress_.arc(Download.Progress.centerX, |
- Download.Progress.centerY, |
- Download.Progress.radius, |
- Download.Progress.START_ANGLE, |
- Download.Progress.START_ANGLE + Math.PI * 0.02 * |
- Number(this.percent_), |
- false); |
- |
- this.canvasProgress_.lineTo(Download.Progress.centerX, |
- Download.Progress.centerY); |
- this.canvasProgress_.fill(); |
- this.canvasProgress_.closePath(); |
- } else if (this.nodeProgressBackground_) { |
- this.nodeProgressForeground_.style.display = 'none'; |
- this.nodeProgressBackground_.style.display = 'none'; |
- } |
- |
- if (this.controlShow_) |
- this.controlShow_.hidden = !completelyOnDisk; |
- this.controlRetry_.hidden = !download.retry; |
- this.controlRetry_.href = this.url_; |
- this.controlPause_.hidden = this.state_ != Download.States.IN_PROGRESS; |
- this.controlResume_.hidden = !download.resume; |
- var showCancel = this.state_ == Download.States.IN_PROGRESS || |
- this.state_ == Download.States.PAUSED; |
- this.controlCancel_.hidden = !showCancel; |
- if (this.controlRemove_) |
- this.controlRemove_.hidden = showCancel; |
- |
- if (this.byExtensionId_ && this.byExtensionName_) { |
- // Format 'control_by_extension' with a link instead of plain text by |
- // splitting the formatted string into pieces. |
- var slug = 'XXXXX'; |
- var formatted = loadTimeData.getStringF('control_by_extension', slug); |
- var slugIndex = formatted.indexOf(slug); |
- this.controlByExtension_.textContent = formatted.substr(0, slugIndex); |
- this.controlByExtensionLink_ = document.createElement('a'); |
- this.controlByExtensionLink_.href = |
- 'chrome://extensions#' + this.byExtensionId_; |
- this.controlByExtensionLink_.textContent = this.byExtensionName_; |
- this.controlByExtension_.appendChild(this.controlByExtensionLink_); |
- if (slugIndex < (formatted.length - slug.length)) |
- this.controlByExtension_.appendChild(document.createTextNode( |
- formatted.substr(slugIndex + 1))); |
- } |
- |
- this.nodeSince_.textContent = this.since_; |
- this.nodeDate_.textContent = this.date_; |
- // Don't unnecessarily update the url, as doing so will remove any |
- // text selection the user has started (http://crbug.com/44982). |
- if (this.nodeURL_.textContent != this.url_) { |
- this.nodeURL_.textContent = this.url_; |
- this.nodeURL_.href = this.url_; |
- } |
- this.nodeStatus_.textContent = this.getStatusText_(); |
- |
- this.danger_.style.display = 'none'; |
- this.safe_.style.display = 'block'; |
- } |
-}; |
- |
-/** |
- * Decorates the icons, strings, and buttons for a download to reflect the |
- * danger level of a file. Dangerous & malicious files are treated differently. |
- */ |
-Download.prototype.updateDangerousFile = function() { |
- switch (this.dangerType_) { |
- case Download.DangerType.DANGEROUS_FILE: { |
- this.dangerDesc_.textContent = loadTimeData.getStringF( |
- 'danger_file_desc', this.fileName_); |
- break; |
- } |
- case Download.DangerType.DANGEROUS_URL: { |
- this.dangerDesc_.textContent = loadTimeData.getString('danger_url_desc'); |
- break; |
- } |
- case Download.DangerType.DANGEROUS_CONTENT: // Fall through. |
- case Download.DangerType.DANGEROUS_HOST: { |
- this.dangerDesc_.textContent = loadTimeData.getStringF( |
- 'danger_content_desc', this.fileName_); |
- break; |
- } |
- case Download.DangerType.UNCOMMON_CONTENT: { |
- this.dangerDesc_.textContent = loadTimeData.getStringF( |
- 'danger_uncommon_desc', this.fileName_); |
- break; |
- } |
- case Download.DangerType.POTENTIALLY_UNWANTED: { |
- this.dangerDesc_.textContent = loadTimeData.getStringF( |
- 'danger_settings_desc', this.fileName_); |
- break; |
- } |
- } |
- |
- if (this.dangerType_ == Download.DangerType.DANGEROUS_FILE) { |
- downloads.scheduleIconLoad( |
- this.dangerNodeImg_, |
- 'chrome://theme/IDR_WARNING?scale=' + window.devicePixelRatio + 'x'); |
- } else { |
- downloads.scheduleIconLoad( |
- this.dangerNodeImg_, |
- 'chrome://theme/IDR_SAFEBROWSING_WARNING?scale=' + |
- window.devicePixelRatio + 'x'); |
- this.dangerDesc_.className = 'malware-description'; |
- } |
- |
- if (this.dangerType_ == Download.DangerType.DANGEROUS_CONTENT || |
- this.dangerType_ == Download.DangerType.DANGEROUS_HOST || |
- this.dangerType_ == Download.DangerType.DANGEROUS_URL || |
- this.dangerType_ == Download.DangerType.POTENTIALLY_UNWANTED) { |
- this.malwareNodeControls_.style.display = 'block'; |
- this.dangerDiscard_.style.display = 'none'; |
- this.dangerSave_.style.display = 'none'; |
- } else { |
- this.malwareNodeControls_.style.display = 'none'; |
- this.dangerDiscard_.style.display = 'inline'; |
- this.dangerSave_.style.display = 'inline'; |
- } |
- |
- this.danger_.style.display = 'block'; |
- this.safe_.style.display = 'none'; |
-}; |
- |
-/** Removes applicable bits from the DOM in preparation for deletion. */ |
-Download.prototype.clear = function() { |
- this.safe_.ondragstart = null; |
- this.nodeFileLink_.onclick = null; |
- if (this.controlShow_) { |
- this.controlShow_.onclick = null; |
- } |
- this.controlCancel_.onclick = null; |
- this.controlPause_.onclick = null; |
- this.controlResume_.onclick = null; |
- this.dangerDiscard_.onclick = null; |
- this.dangerSave_.onclick = null; |
- this.malwareDiscard_.onclick = null; |
- this.malwareSave_.onclick = null; |
- |
- this.node.innerHTML = ''; |
-}; |
- |
-/** |
- * @private |
- * @return {string} User-visible status update text. |
- */ |
-Download.prototype.getStatusText_ = function() { |
- switch (this.state_) { |
- case Download.States.IN_PROGRESS: |
- return this.progressStatusText_; |
- case Download.States.CANCELLED: |
- return loadTimeData.getString('status_cancelled'); |
- case Download.States.PAUSED: |
- return loadTimeData.getString('status_paused'); |
- case Download.States.DANGEROUS: |
- // danger_url_desc is also used by DANGEROUS_CONTENT. |
- var desc = this.dangerType_ == Download.DangerType.DANGEROUS_FILE ? |
- 'danger_file_desc' : 'danger_url_desc'; |
- return loadTimeData.getString(desc); |
- case Download.States.INTERRUPTED: |
- return this.lastReasonDescription_; |
- case Download.States.COMPLETE: |
- return this.fileExternallyRemoved_ ? |
- loadTimeData.getString('status_removed') : ''; |
- } |
- assertNotReached(); |
- return ''; |
-}; |
- |
-/** |
- * Tells the backend to initiate a drag, allowing users to drag |
- * files from the download page and have them appear as native file |
- * drags. |
- * @return {boolean} Returns false to prevent the default action. |
- * @private |
- */ |
-Download.prototype.drag_ = function() { |
- chrome.send('drag', [this.id_]); |
- return false; |
-}; |
- |
-/** |
- * Tells the backend to open this file. |
- * @return {boolean} Returns false to prevent the default action. |
- * @private |
- */ |
-Download.prototype.openFile_ = function() { |
- chrome.send('openFile', [this.id_]); |
- return false; |
-}; |
- |
-/** |
- * Tells the backend that the user chose to save a dangerous file. |
- * @return {boolean} Returns false to prevent the default action. |
- * @private |
- */ |
-Download.prototype.saveDangerous_ = function() { |
- chrome.send('saveDangerous', [this.id_]); |
- return false; |
-}; |
- |
-/** |
- * Tells the backend that the user chose to discard a dangerous file. |
- * @return {boolean} Returns false to prevent the default action. |
- * @private |
- */ |
-Download.prototype.discardDangerous_ = function() { |
- chrome.send('discardDangerous', [this.id_]); |
- downloads.remove(this.id_); |
- return false; |
-}; |
- |
-/** |
- * Tells the backend to show the file in explorer. |
- * @return {boolean} Returns false to prevent the default action. |
- * @private |
- */ |
-Download.prototype.show_ = function() { |
- chrome.send('show', [this.id_]); |
- return false; |
-}; |
- |
-/** |
- * Tells the backend to pause this download. |
- * @return {boolean} Returns false to prevent the default action. |
- * @private |
- */ |
-Download.prototype.pause_ = function() { |
- chrome.send('pause', [this.id_]); |
- return false; |
-}; |
- |
-/** |
- * Tells the backend to resume this download. |
- * @return {boolean} Returns false to prevent the default action. |
- * @private |
- */ |
-Download.prototype.resume_ = function() { |
- chrome.send('resume', [this.id_]); |
- return false; |
-}; |
- |
-/** |
- * Tells the backend to remove this download from history and download shelf. |
- * @return {boolean} Returns false to prevent the default action. |
- * @private |
- */ |
-Download.prototype.remove_ = function() { |
- assert(loadTimeData.getBoolean('allow_deleting_history')); |
- chrome.send('remove', [this.id_]); |
- return false; |
-}; |
- |
-/** |
- * Tells the backend to cancel this download. |
- * @return {boolean} Returns false to prevent the default action. |
- * @private |
- */ |
-Download.prototype.cancel_ = function() { |
- chrome.send('cancel', [this.id_]); |
- return false; |
-}; |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-// Page: |
-var downloads, resultsTimeout; |
- |
-// TODO(benjhayden): Rename Downloads to DownloadManager, downloads to |
-// downloadManager or theDownloadManager or DownloadManager.get() to prevent |
-// confusing Downloads with Download. |
- |
-/** |
- * The FIFO array that stores updates of download files to be appeared |
- * on the download page. It is guaranteed that the updates in this array |
- * are reflected to the download page in a FIFO order. |
-*/ |
-var fifoResults; |
- |
-function load() { |
- chrome.send('onPageLoaded'); |
- fifoResults = []; |
- downloads = new Downloads(); |
- $('term').focus(); |
- setSearch(''); |
- |
- $('clear-all').onclick = function() { |
- chrome.send('clearAll'); |
- }; |
- |
- $('open-downloads-folder').onclick = function() { |
- chrome.send('openDownloadsFolder'); |
- }; |
- |
- $('term').onsearch = function(e) { |
- setSearch($('term').value); |
- }; |
-} |
- |
-function setSearch(searchText) { |
- fifoResults.length = 0; |
- downloads.setSearchText(searchText); |
- searchText = searchText.toString().match(/(?:[^\s"]+|"[^"]*")+/g); |
- if (searchText) { |
- searchText = searchText.map(function(term) { |
- // strip quotes |
- return (term.match(/\s/) && |
- term[0].match(/["']/) && |
- term[term.length - 1] == term[0]) ? |
- term.substr(1, term.length - 2) : term; |
- }); |
- } else { |
- searchText = []; |
- } |
- chrome.send('getDownloads', searchText); |
-} |
- |
-function clearAll() { |
- if (!loadTimeData.getBoolean('allow_deleting_history')) |
- return; |
- |
- fifoResults.length = 0; |
- downloads.clear(); |
- downloads.setSearchText(''); |
- chrome.send('clearAll'); |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-// Chrome callbacks: |
-/** |
- * Our history system calls this function with results from searches or when |
- * downloads are added or removed. |
- * @param {Array<Object>} results List of updates. |
- */ |
-function downloadsList(results) { |
- if (downloads && downloads.isUpdateNeeded(results)) { |
- if (resultsTimeout) |
- clearTimeout(resultsTimeout); |
- fifoResults.length = 0; |
- downloads.clear(); |
- downloadUpdated(results); |
- } |
- downloads.updateResults(); |
- downloads.updateSummary(); |
-} |
- |
-/** |
- * When a download is updated (progress, state change), this is called. |
- * @param {Array<Object>} results List of updates for the download process. |
- */ |
-function downloadUpdated(results) { |
- // Sometimes this can get called too early. |
- if (!downloads) |
- return; |
- |
- fifoResults = fifoResults.concat(results); |
- tryDownloadUpdatedPeriodically(); |
-} |
- |
-/** |
- * Try to reflect as much updates as possible within 50ms. |
- * This function is scheduled again and again until all updates are reflected. |
- */ |
-function tryDownloadUpdatedPeriodically() { |
- var start = Date.now(); |
- while (fifoResults.length) { |
- var result = fifoResults.shift(); |
- downloads.updated(result); |
- // Do as much as we can in 50ms. |
- if (Date.now() - start > 50) { |
- clearTimeout(resultsTimeout); |
- resultsTimeout = setTimeout(tryDownloadUpdatedPeriodically, 5); |
- break; |
- } |
- } |
-} |
- |
-// Add handlers to HTML elements. |
-window.addEventListener('DOMContentLoaded', load); |