1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/chrome/content/aboutDownloads.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,617 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils; 1.9 + 1.10 +Cu.import("resource://gre/modules/Services.jsm"); 1.11 +Cu.import("resource://gre/modules/DownloadUtils.jsm"); 1.12 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.13 +Cu.import("resource://gre/modules/PluralForm.jsm"); 1.14 +Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); 1.15 + 1.16 +XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); 1.17 + 1.18 +let gStrings = Services.strings.createBundle("chrome://browser/locale/aboutDownloads.properties"); 1.19 + 1.20 +let downloadTemplate = 1.21 +"<li downloadGUID='{guid}' class='list-item' role='button' state='{state}' contextmenu='downloadmenu'>" + 1.22 + "<img class='icon' src='{icon}'/>" + 1.23 + "<div class='details'>" + 1.24 + "<div class='row'>" + 1.25 + // This is a hack so that we can crop this label in its center 1.26 + "<xul:label class='title' crop='center' value='{target}'/>" + 1.27 + "<div class='date'>{date}</div>" + 1.28 + "</div>" + 1.29 + "<div class='size'>{size}</div>" + 1.30 + "<div class='domain'>{domain}</div>" + 1.31 + "<div class='displayState'>{displayState}</div>" + 1.32 + "</div>" + 1.33 +"</li>"; 1.34 + 1.35 +XPCOMUtils.defineLazyGetter(window, "gChromeWin", function () 1.36 + window.QueryInterface(Ci.nsIInterfaceRequestor) 1.37 + .getInterface(Ci.nsIWebNavigation) 1.38 + .QueryInterface(Ci.nsIDocShellTreeItem) 1.39 + .rootTreeItem 1.40 + .QueryInterface(Ci.nsIInterfaceRequestor) 1.41 + .getInterface(Ci.nsIDOMWindow) 1.42 + .QueryInterface(Ci.nsIDOMChromeWindow)); 1.43 + 1.44 + 1.45 +var ContextMenus = { 1.46 + target: null, 1.47 + 1.48 + init: function() { 1.49 + document.addEventListener("contextmenu", this, false); 1.50 + document.getElementById("contextmenu-open").addEventListener("click", this.open.bind(this), false); 1.51 + document.getElementById("contextmenu-retry").addEventListener("click", this.retry.bind(this), false); 1.52 + document.getElementById("contextmenu-remove").addEventListener("click", this.remove.bind(this), false); 1.53 + document.getElementById("contextmenu-pause").addEventListener("click", this.pause.bind(this), false); 1.54 + document.getElementById("contextmenu-resume").addEventListener("click", this.resume.bind(this), false); 1.55 + document.getElementById("contextmenu-cancel").addEventListener("click", this.cancel.bind(this), false); 1.56 + document.getElementById("contextmenu-removeall").addEventListener("click", this.removeAll.bind(this), false); 1.57 + this.items = [ 1.58 + { name: "open", states: [Downloads._dlmgr.DOWNLOAD_FINISHED] }, 1.59 + { name: "retry", states: [Downloads._dlmgr.DOWNLOAD_FAILED, Downloads._dlmgr.DOWNLOAD_CANCELED] }, 1.60 + { name: "remove", states: [Downloads._dlmgr.DOWNLOAD_FINISHED,Downloads._dlmgr.DOWNLOAD_FAILED, Downloads._dlmgr.DOWNLOAD_CANCELED] }, 1.61 + { name: "removeall", states: [Downloads._dlmgr.DOWNLOAD_FINISHED,Downloads._dlmgr.DOWNLOAD_FAILED, Downloads._dlmgr.DOWNLOAD_CANCELED] }, 1.62 + { name: "pause", states: [Downloads._dlmgr.DOWNLOAD_DOWNLOADING] }, 1.63 + { name: "resume", states: [Downloads._dlmgr.DOWNLOAD_PAUSED] }, 1.64 + { name: "cancel", states: [Downloads._dlmgr.DOWNLOAD_DOWNLOADING, Downloads._dlmgr.DOWNLOAD_NOTSTARTED, Downloads._dlmgr.DOWNLOAD_QUEUED, Downloads._dlmgr.DOWNLOAD_PAUSED] }, 1.65 + ]; 1.66 + }, 1.67 + 1.68 + handleEvent: function(event) { 1.69 + // store the target of context menu events so that we know which app to act on 1.70 + this.target = event.target; 1.71 + while (!this.target.hasAttribute("contextmenu")) { 1.72 + this.target = this.target.parentNode; 1.73 + } 1.74 + if (!this.target) 1.75 + return; 1.76 + 1.77 + let state = parseInt(this.target.getAttribute("state")); 1.78 + for (let i = 0; i < this.items.length; i++) { 1.79 + var item = this.items[i]; 1.80 + let enabled = (item.states.indexOf(state) > -1); 1.81 + if (enabled) 1.82 + document.getElementById("contextmenu-" + item.name).removeAttribute("hidden"); 1.83 + else 1.84 + document.getElementById("contextmenu-" + item.name).setAttribute("hidden", "true"); 1.85 + } 1.86 + }, 1.87 + 1.88 + // Open shown only for downloads that completed successfully 1.89 + open: function(event) { 1.90 + Downloads.openDownload(this.target); 1.91 + this.target = null; 1.92 + }, 1.93 + 1.94 + // Retry shown when its failed, canceled, blocked(covered in failed, see _getState()) 1.95 + retry: function (event) { 1.96 + Downloads.retryDownload(this.target); 1.97 + this.target = null; 1.98 + }, 1.99 + 1.100 + // Remove shown when its canceled, finished, failed(failed includes blocked and dirty, see _getState()) 1.101 + remove: function (event) { 1.102 + Downloads.removeDownload(this.target); 1.103 + this.target = null; 1.104 + }, 1.105 + 1.106 + // Pause shown when item is currently downloading 1.107 + pause: function (event) { 1.108 + Downloads.pauseDownload(this.target); 1.109 + this.target = null; 1.110 + }, 1.111 + 1.112 + // Resume shown for paused items only 1.113 + resume: function (event) { 1.114 + Downloads.resumeDownload(this.target); 1.115 + this.target = null; 1.116 + }, 1.117 + 1.118 + // Cancel shown when its downloading, notstarted, queued or paused 1.119 + cancel: function (event) { 1.120 + Downloads.cancelDownload(this.target); 1.121 + this.target = null; 1.122 + }, 1.123 + 1.124 + removeAll: function(event) { 1.125 + Downloads.removeAll(); 1.126 + this.target = null; 1.127 + } 1.128 +} 1.129 + 1.130 + 1.131 +let Downloads = { 1.132 + init: function dl_init() { 1.133 + function onClick(evt) { 1.134 + let target = evt.target; 1.135 + while (target.nodeName != "li") { 1.136 + target = target.parentNode; 1.137 + if (!target) 1.138 + return; 1.139 + } 1.140 + 1.141 + Downloads.openDownload(target); 1.142 + } 1.143 + 1.144 + this._normalList = document.getElementById("normal-downloads-list"); 1.145 + this._privateList = document.getElementById("private-downloads-list"); 1.146 + 1.147 + this._normalList.addEventListener("click", onClick, false); 1.148 + this._privateList.addEventListener("click", onClick, false); 1.149 + 1.150 + this._dlmgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); 1.151 + this._dlmgr.addPrivacyAwareListener(this); 1.152 + 1.153 + Services.obs.addObserver(this, "last-pb-context-exited", false); 1.154 + Services.obs.addObserver(this, "download-manager-remove-download-guid", false); 1.155 + 1.156 + // If we have private downloads, show them all immediately. If we were to 1.157 + // add them asynchronously, there's a small chance we could get a 1.158 + // "last-pb-context-exited" notification before downloads are added to the 1.159 + // list, meaning we'd show private downloads without any private tabs open. 1.160 + let privateEntries = this.getDownloads({ isPrivate: true }); 1.161 + this._stepAddEntries(privateEntries, this._privateList, privateEntries.length); 1.162 + 1.163 + // Add non-private downloads 1.164 + let normalEntries = this.getDownloads({ isPrivate: false }); 1.165 + this._stepAddEntries(normalEntries, this._normalList, 1, this._scrollToSelectedDownload.bind(this)); 1.166 + ContextMenus.init(); 1.167 + }, 1.168 + 1.169 + uninit: function dl_uninit() { 1.170 + let contextmenus = gChromeWin.NativeWindow.contextmenus; 1.171 + contextmenus.remove(this.openMenuItem); 1.172 + contextmenus.remove(this.removeMenuItem); 1.173 + contextmenus.remove(this.pauseMenuItem); 1.174 + contextmenus.remove(this.resumeMenuItem); 1.175 + contextmenus.remove(this.retryMenuItem); 1.176 + contextmenus.remove(this.cancelMenuItem); 1.177 + contextmenus.remove(this.deleteAllMenuItem); 1.178 + 1.179 + this._dlmgr.removeListener(this); 1.180 + Services.obs.removeObserver(this, "last-pb-context-exited"); 1.181 + Services.obs.removeObserver(this, "download-manager-remove-download-guid"); 1.182 + }, 1.183 + 1.184 + onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, 1.185 + aCurTotalProgress, aMaxTotalProgress, aDownload) { }, 1.186 + onDownloadStateChange: function(aState, aDownload) { 1.187 + switch (aDownload.state) { 1.188 + case Ci.nsIDownloadManager.DOWNLOAD_FAILED: 1.189 + case Ci.nsIDownloadManager.DOWNLOAD_CANCELED: 1.190 + case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL: 1.191 + case Ci.nsIDownloadManager.DOWNLOAD_DIRTY: 1.192 + case Ci.nsIDownloadManager.DOWNLOAD_FINISHED: 1.193 + // For all "completed" states, move them after active downloads 1.194 + this._moveDownloadAfterActive(this._getElementForDownload(aDownload.guid)); 1.195 + 1.196 + // Fall-through the rest 1.197 + case Ci.nsIDownloadManager.DOWNLOAD_SCANNING: 1.198 + case Ci.nsIDownloadManager.DOWNLOAD_QUEUED: 1.199 + case Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING: 1.200 + let item = this._getElementForDownload(aDownload.guid); 1.201 + if (item) 1.202 + this._updateDownloadRow(item, aDownload); 1.203 + else 1.204 + this._insertDownloadRow(aDownload); 1.205 + break; 1.206 + } 1.207 + }, 1.208 + onStateChange: function(aWebProgress, aRequest, aState, aStatus, aDownload) { }, 1.209 + onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) { }, 1.210 + 1.211 + observe: function (aSubject, aTopic, aData) { 1.212 + switch (aTopic) { 1.213 + case "last-pb-context-exited": 1.214 + this._privateList.innerHTML = ""; 1.215 + break; 1.216 + case "download-manager-remove-download-guid": { 1.217 + let guid = aSubject.QueryInterface(Ci.nsISupportsCString).data; 1.218 + this._removeItem(this._getElementForDownload(guid)); 1.219 + break; 1.220 + } 1.221 + } 1.222 + }, 1.223 + 1.224 + _moveDownloadAfterActive: function dl_moveDownloadAfterActive(aItem) { 1.225 + // Move downloads that just reached a "completed" state below any active 1.226 + try { 1.227 + // Iterate down until we find a non-active download 1.228 + let next = aItem.nextElementSibling; 1.229 + while (next && this._inProgress(next.getAttribute("state"))) 1.230 + next = next.nextElementSibling; 1.231 + // Move the item 1.232 + aItem.parentNode.insertBefore(aItem, next); 1.233 + } catch (ex) { 1.234 + this.logError("_moveDownloadAfterActive() " + ex); 1.235 + } 1.236 + }, 1.237 + 1.238 + _inProgress: function dl_inProgress(aState) { 1.239 + return [ 1.240 + this._dlmgr.DOWNLOAD_NOTSTARTED, 1.241 + this._dlmgr.DOWNLOAD_QUEUED, 1.242 + this._dlmgr.DOWNLOAD_DOWNLOADING, 1.243 + this._dlmgr.DOWNLOAD_PAUSED, 1.244 + this._dlmgr.DOWNLOAD_SCANNING, 1.245 + ].indexOf(parseInt(aState)) != -1; 1.246 + }, 1.247 + 1.248 + _insertDownloadRow: function dl_insertDownloadRow(aDownload) { 1.249 + let updatedState = this._getState(aDownload.state); 1.250 + let item = this._createItem(downloadTemplate, { 1.251 + guid: aDownload.guid, 1.252 + target: aDownload.displayName, 1.253 + icon: "moz-icon://" + aDownload.displayName + "?size=64", 1.254 + date: DownloadUtils.getReadableDates(new Date())[0], 1.255 + domain: DownloadUtils.getURIHost(aDownload.source.spec)[0], 1.256 + size: this._getDownloadSize(aDownload.size), 1.257 + displayState: this._getStateString(updatedState), 1.258 + state: updatedState 1.259 + }); 1.260 + list = aDownload.isPrivate ? this._privateList : this._normalList; 1.261 + list.insertAdjacentHTML("afterbegin", item); 1.262 + }, 1.263 + 1.264 + _getDownloadSize: function dl_getDownloadSize(aSize) { 1.265 + if (aSize > 0) { 1.266 + let displaySize = DownloadUtils.convertByteUnits(aSize); 1.267 + return displaySize.join(""); // [0] is size, [1] is units 1.268 + } 1.269 + return gStrings.GetStringFromName("downloadState.unknownSize"); 1.270 + }, 1.271 + 1.272 + // Not all states are displayed as-is on mobile, some are translated to a generic state 1.273 + _getState: function dl_getState(aState) { 1.274 + let str; 1.275 + switch (aState) { 1.276 + // Downloading and Scanning states show up as "Downloading" 1.277 + case this._dlmgr.DOWNLOAD_DOWNLOADING: 1.278 + case this._dlmgr.DOWNLOAD_SCANNING: 1.279 + str = this._dlmgr.DOWNLOAD_DOWNLOADING; 1.280 + break; 1.281 + 1.282 + // Failed, Dirty and Blocked states show up as "Failed" 1.283 + case this._dlmgr.DOWNLOAD_FAILED: 1.284 + case this._dlmgr.DOWNLOAD_DIRTY: 1.285 + case this._dlmgr.DOWNLOAD_BLOCKED_POLICY: 1.286 + case this._dlmgr.DOWNLOAD_BLOCKED_PARENTAL: 1.287 + str = this._dlmgr.DOWNLOAD_FAILED; 1.288 + break; 1.289 + 1.290 + /* QUEUED and NOTSTARTED are not translated as they 1.291 + dont fall under a common state but we still need 1.292 + to display a common "status" on the UI */ 1.293 + 1.294 + default: 1.295 + str = aState; 1.296 + } 1.297 + return str; 1.298 + }, 1.299 + 1.300 + // Note: This doesn't cover all states as some of the states are translated in _getState() 1.301 + _getStateString: function dl_getStateString(aState) { 1.302 + let str; 1.303 + switch (aState) { 1.304 + case this._dlmgr.DOWNLOAD_DOWNLOADING: 1.305 + str = "downloadState.downloading"; 1.306 + break; 1.307 + case this._dlmgr.DOWNLOAD_CANCELED: 1.308 + str = "downloadState.canceled"; 1.309 + break; 1.310 + case this._dlmgr.DOWNLOAD_FAILED: 1.311 + str = "downloadState.failed"; 1.312 + break; 1.313 + case this._dlmgr.DOWNLOAD_PAUSED: 1.314 + str = "downloadState.paused"; 1.315 + break; 1.316 + 1.317 + // Queued and Notstarted show up as "Starting..." 1.318 + case this._dlmgr.DOWNLOAD_QUEUED: 1.319 + case this._dlmgr.DOWNLOAD_NOTSTARTED: 1.320 + str = "downloadState.starting"; 1.321 + break; 1.322 + 1.323 + default: 1.324 + return ""; 1.325 + } 1.326 + return gStrings.GetStringFromName(str); 1.327 + }, 1.328 + 1.329 + _updateItem: function dl_updateItem(aItem, aValues) { 1.330 + for (let i in aValues) { 1.331 + aItem.querySelector("." + i).textContent = aValues[i]; 1.332 + } 1.333 + }, 1.334 + 1.335 + _initStatement: function dv__initStatement(aIsPrivate) { 1.336 + let dbConn = aIsPrivate ? this._dlmgr.privateDBConnection : this._dlmgr.DBConnection; 1.337 + return dbConn.createStatement( 1.338 + "SELECT guid, name, source, state, startTime, endTime, referrer, " + 1.339 + "currBytes, maxBytes, state IN (?1, ?2, ?3, ?4, ?5) isActive " + 1.340 + "FROM moz_downloads " + 1.341 + "ORDER BY isActive DESC, endTime DESC, startTime DESC"); 1.342 + }, 1.343 + 1.344 + _createItem: function _createItem(aTemplate, aValues) { 1.345 + function htmlEscape(s) { 1.346 + s = s.replace(/&/g, "&"); 1.347 + s = s.replace(/>/g, ">"); 1.348 + s = s.replace(/</g, "<"); 1.349 + s = s.replace(/"/g, """); 1.350 + s = s.replace(/'/g, "'"); 1.351 + return s; 1.352 + } 1.353 + 1.354 + let t = aTemplate; 1.355 + for (let key in aValues) { 1.356 + if (aValues.hasOwnProperty(key)) { 1.357 + let regEx = new RegExp("{" + key + "}", "g"); 1.358 + let value = htmlEscape(aValues[key].toString()); 1.359 + t = t.replace(regEx, value); 1.360 + } 1.361 + } 1.362 + return t; 1.363 + }, 1.364 + 1.365 + _getEntry: function dv__getEntry(aStmt) { 1.366 + try { 1.367 + if (!aStmt.executeStep()) { 1.368 + return null; 1.369 + } 1.370 + 1.371 + let updatedState = this._getState(aStmt.row.state); 1.372 + // Try to get the attribute values from the statement 1.373 + 1.374 + return { 1.375 + guid: aStmt.row.guid, 1.376 + target: aStmt.row.name, 1.377 + icon: "moz-icon://" + aStmt.row.name + "?size=64", 1.378 + date: DownloadUtils.getReadableDates(new Date(aStmt.row.endTime / 1000))[0], 1.379 + domain: DownloadUtils.getURIHost(aStmt.row.source)[0], 1.380 + size: this._getDownloadSize(aStmt.row.maxBytes), 1.381 + displayState: this._getStateString(updatedState), 1.382 + state: updatedState 1.383 + }; 1.384 + 1.385 + } catch (e) { 1.386 + // Something went wrong when stepping or getting values, so clear and quit 1.387 + this.logError("_getEntry() " + e); 1.388 + aStmt.reset(); 1.389 + return null; 1.390 + } 1.391 + }, 1.392 + 1.393 + _stepAddEntries: function dv__stepAddEntries(aEntries, aList, aNumItems, aCallback) { 1.394 + 1.395 + if (aEntries.length == 0){ 1.396 + if (aCallback) 1.397 + aCallback(); 1.398 + 1.399 + return; 1.400 + } 1.401 + 1.402 + let attrs = aEntries.shift(); 1.403 + let item = this._createItem(downloadTemplate, attrs); 1.404 + aList.insertAdjacentHTML("beforeend", item); 1.405 + 1.406 + // Add another item to the list if we should; otherwise, let the UI update 1.407 + // and continue later 1.408 + if (aNumItems > 1) { 1.409 + this._stepAddEntries(aEntries, aList, aNumItems - 1, aCallback); 1.410 + } else { 1.411 + // Use a shorter delay for earlier downloads to display them faster 1.412 + let delay = Math.min(aList.itemCount * 10, 300); 1.413 + setTimeout(function () { 1.414 + this._stepAddEntries(aEntries, aList, 5, aCallback); 1.415 + }.bind(this), delay); 1.416 + } 1.417 + }, 1.418 + 1.419 + getDownloads: function dl_getDownloads(aParams) { 1.420 + aParams = aParams || {}; 1.421 + let stmt = this._initStatement(aParams.isPrivate); 1.422 + 1.423 + stmt.reset(); 1.424 + stmt.bindInt32Parameter(0, Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED); 1.425 + stmt.bindInt32Parameter(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING); 1.426 + stmt.bindInt32Parameter(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED); 1.427 + stmt.bindInt32Parameter(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED); 1.428 + stmt.bindInt32Parameter(4, Ci.nsIDownloadManager.DOWNLOAD_SCANNING); 1.429 + 1.430 + let entries = []; 1.431 + while (entry = this._getEntry(stmt)) { 1.432 + entries.push(entry); 1.433 + } 1.434 + 1.435 + stmt.finalize(); 1.436 + 1.437 + return entries; 1.438 + }, 1.439 + 1.440 + _getElementForDownload: function dl_getElementForDownload(aKey) { 1.441 + return document.body.querySelector("li[downloadGUID='" + aKey + "']"); 1.442 + }, 1.443 + 1.444 + _getDownloadForElement: function dl_getDownloadForElement(aElement, aCallback) { 1.445 + let guid = aElement.getAttribute("downloadGUID"); 1.446 + this._dlmgr.getDownloadByGUID(guid, function(status, download) { 1.447 + if (!Components.isSuccessCode(status)) { 1.448 + return; 1.449 + } 1.450 + aCallback(download); 1.451 + }); 1.452 + }, 1.453 + 1.454 + _removeItem: function dl_removeItem(aItem) { 1.455 + // Make sure we have an item to remove 1.456 + if (!aItem) 1.457 + return; 1.458 + 1.459 + aItem.parentNode.removeChild(aItem); 1.460 + }, 1.461 + 1.462 + openDownload: function dl_openDownload(aItem) { 1.463 + this._getDownloadForElement(aItem, function(aDownload) { 1.464 + if (aDownload.state !== Ci.nsIDownloadManager.DOWNLOAD_FINISHED) { 1.465 + // Do not open unfinished downloads. 1.466 + return; 1.467 + } 1.468 + try { 1.469 + let f = aDownload.targetFile; 1.470 + if (f) f.launch(); 1.471 + } catch (ex) { 1.472 + this.logError("openDownload() " + ex, aDownload); 1.473 + } 1.474 + }.bind(this)); 1.475 + }, 1.476 + 1.477 + removeDownload: function dl_removeDownload(aItem) { 1.478 + this._getDownloadForElement(aItem, function(aDownload) { 1.479 + if (aDownload.targetFile) { 1.480 + OS.File.remove(aDownload.targetFile.path).then(null, function onError(reason) { 1.481 + if (!(reason instanceof OS.File.Error && reason.becauseNoSuchFile)) { 1.482 + this.logError("removeDownload() " + reason, aDownload); 1.483 + } 1.484 + }.bind(this)); 1.485 + } 1.486 + 1.487 + aDownload.remove(); 1.488 + }.bind(this)); 1.489 + }, 1.490 + 1.491 + removeAll: function dl_removeAll() { 1.492 + let title = gStrings.GetStringFromName("downloadAction.deleteAll"); 1.493 + let messageForm = gStrings.GetStringFromName("downloadMessage.deleteAll"); 1.494 + let elements = document.body.querySelectorAll("li[state='" + this._dlmgr.DOWNLOAD_FINISHED + "']," + 1.495 + "li[state='" + this._dlmgr.DOWNLOAD_CANCELED + "']," + 1.496 + "li[state='" + this._dlmgr.DOWNLOAD_FAILED + "']"); 1.497 + let message = PluralForm.get(elements.length, messageForm) 1.498 + .replace("#1", elements.length); 1.499 + let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_OK + 1.500 + Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL; 1.501 + let choice = Services.prompt.confirmEx(null, title, message, flags, 1.502 + null, null, null, null, {}); 1.503 + if (choice == 0) { 1.504 + for (let i = 0; i < elements.length; i++) { 1.505 + this.removeDownload(elements[i]); 1.506 + } 1.507 + } 1.508 + }, 1.509 + 1.510 + pauseDownload: function dl_pauseDownload(aItem) { 1.511 + this._getDownloadForElement(aItem, function(aDownload) { 1.512 + try { 1.513 + aDownload.pause(); 1.514 + this._updateDownloadRow(aItem, aDownload); 1.515 + } catch (ex) { 1.516 + this.logError("Error: pauseDownload() " + ex, aDownload); 1.517 + } 1.518 + }.bind(this)); 1.519 + }, 1.520 + 1.521 + resumeDownload: function dl_resumeDownload(aItem) { 1.522 + this._getDownloadForElement(aItem, function(aDownload) { 1.523 + try { 1.524 + aDownload.resume(); 1.525 + this._updateDownloadRow(aItem, aDownload); 1.526 + } catch (ex) { 1.527 + this.logError("resumeDownload() " + ex, aDownload); 1.528 + } 1.529 + }.bind(this)); 1.530 + }, 1.531 + 1.532 + retryDownload: function dl_retryDownload(aItem) { 1.533 + this._getDownloadForElement(aItem, function(aDownload) { 1.534 + try { 1.535 + this._removeItem(aItem); 1.536 + aDownload.retry(); 1.537 + } catch (ex) { 1.538 + this.logError("retryDownload() " + ex, aDownload); 1.539 + } 1.540 + }.bind(this)); 1.541 + }, 1.542 + 1.543 + cancelDownload: function dl_cancelDownload(aItem) { 1.544 + this._getDownloadForElement(aItem, function(aDownload) { 1.545 + OS.File.remove(aDownload.targetFile.path).then(null, function onError(reason) { 1.546 + if (!(reason instanceof OS.File.Error && reason.becauseNoSuchFile)) { 1.547 + this.logError("cancelDownload() " + reason, aDownload); 1.548 + } 1.549 + }.bind(this)); 1.550 + 1.551 + aDownload.cancel(); 1.552 + 1.553 + this._updateDownloadRow(aItem, aDownload); 1.554 + }.bind(this)); 1.555 + }, 1.556 + 1.557 + _updateDownloadRow: function dl_updateDownloadRow(aItem, aDownload) { 1.558 + try { 1.559 + let updatedState = this._getState(aDownload.state); 1.560 + aItem.setAttribute("state", updatedState); 1.561 + this._updateItem(aItem, { 1.562 + size: this._getDownloadSize(aDownload.size), 1.563 + displayState: this._getStateString(updatedState), 1.564 + date: DownloadUtils.getReadableDates(new Date())[0] 1.565 + }); 1.566 + } catch (ex) { 1.567 + this.logError("_updateDownloadRow() " + ex, aDownload); 1.568 + } 1.569 + }, 1.570 + 1.571 + /** 1.572 + * In case a specific downloadId was passed while opening, scrolls the list to 1.573 + * the given elemenet 1.574 + */ 1.575 + 1.576 + _scrollToSelectedDownload : function dl_scrollToSelected() { 1.577 + let spec = document.location.href; 1.578 + let pos = spec.indexOf("?"); 1.579 + let query = ""; 1.580 + if (pos >= 0) 1.581 + query = spec.substring(pos + 1); 1.582 + 1.583 + // Just assume the query is "id=<id>" 1.584 + let id = query.substring(3); 1.585 + if (!id) { 1.586 + return; 1.587 + } 1.588 + downloadElement = this._getElementForDownload(id); 1.589 + if (!downloadElement) { 1.590 + return; 1.591 + } 1.592 + 1.593 + downloadElement.scrollIntoView(); 1.594 + }, 1.595 + 1.596 + /** 1.597 + * Logs the error to the console. 1.598 + * 1.599 + * @param aMessage error message to log 1.600 + * @param aDownload (optional) if given, and if the download is private, the 1.601 + * log message is suppressed 1.602 + */ 1.603 + logError: function dl_logError(aMessage, aDownload) { 1.604 + if (!aDownload || !aDownload.isPrivate) { 1.605 + console.log("Error: " + aMessage); 1.606 + } 1.607 + }, 1.608 + 1.609 + QueryInterface: function (aIID) { 1.610 + if (!aIID.equals(Ci.nsIDownloadProgressListener) && 1.611 + !aIID.equals(Ci.nsISupports)) 1.612 + throw Components.results.NS_ERROR_NO_INTERFACE; 1.613 + return this; 1.614 + } 1.615 +} 1.616 + 1.617 +document.addEventListener("DOMContentLoaded", Downloads.init.bind(Downloads), true); 1.618 +window.addEventListener("unload", Downloads.uninit.bind(Downloads), false); 1.619 + 1.620 +