From f4ade12bdbb4bfefd4e722ac9711937e5370b538 Mon Sep 17 00:00:00 2001 From: dirkf Date: Sat, 25 Sep 2021 13:05:45 +0100 Subject: [PATCH 1/3] Update to released code from 3.0.9 --- LICENSE | 2 +- chrome.manifest | 12 +- chrome/content/common/bindings.xml | 8 +- chrome/content/common/internalFunctions.js | 36 +- chrome/content/dta/manager/conflicts.js | 5 +- chrome/content/dta/manager/info.js | 3 + chrome/content/dta/manager/manager.js | 1601 ++++++++++-------- chrome/content/dta/manager/manager.xul | 8 +- chrome/content/dta/manager/tree.js | 1047 +++++------- chrome/content/dta/manager/utils.js | 10 +- chrome/content/dta/mirrors.js | 105 +- chrome/content/dta/select.js | 6 +- chrome/content/integration/toolbarinstall.js | 5 +- chrome/content/preferences/bindings.xml | 32 - chrome/content/preferences/interfacePane.xul | 2 - chrome/content/preferences/prefs.js | 11 +- chrome/locale/de/landingpage.dtd | 10 +- chrome/locale/en-US/landingpage.dtd | 10 +- chrome/locale/en-US/manager.dtd | 1 - chrome/locale/en-US/manager.properties | 4 - chrome/locale/en-US/prefpanes.dtd | 4 - chrome/locale/es-ES/landingpage.dtd | 10 +- chrome/locale/et/landingpage.dtd | 10 +- chrome/locale/fr/landingpage.dtd | 10 +- chrome/locale/gl-ES/landingpage.dtd | 10 +- chrome/locale/gl/landingpage.dtd | 10 +- chrome/locale/id/landingpage.dtd | 10 +- chrome/locale/it/landingpage.dtd | 10 +- chrome/locale/ja/landingpage.dtd | 10 +- chrome/locale/nl/landingpage.dtd | 10 +- chrome/locale/pl/landingpage.dtd | 10 +- chrome/locale/pt-BR/landingpage.dtd | 10 +- chrome/locale/pt-PT/landingpage.dtd | 10 +- chrome/locale/ro/landingpage.dtd | 10 +- chrome/locale/ru/landingpage.dtd | 10 +- chrome/locale/sl-SI/landingpage.dtd | 10 +- chrome/locale/sv-SE/landingpage.dtd | 10 +- chrome/locale/tr/landingpage.dtd | 10 +- chrome/locale/zh-CN/landingpage.dtd | 10 +- chrome/locale/zh-TW/landingpage.dtd | 10 +- chrome/skin/toolbarbuttons/buttons.css | 10 + install.rdf | 36 +- modules/api.js | 188 +- modules/api.jsm | 15 +- modules/glue.jsm | 165 +- modules/loaders/integration-content.js | 10 +- modules/loaders/integration.js | 572 ++++--- modules/loaders/saveas.js | 13 +- modules/loaders/selector.js | 239 ++- modules/logging.js | 9 +- modules/main.js | 36 +- modules/manager/chunk.js | 536 +++--- modules/manager/connection.js | 332 ++-- modules/manager/decompressor.js | 163 +- modules/manager/globalprogress.js | 111 +- modules/manager/imex.js | 46 +- modules/manager/matcher.js | 89 +- modules/manager/memoryreporter.js | 89 +- modules/manager/queuestore.js | 67 +- modules/manager/renamer.js | 1 + modules/manager/speedstats.js | 40 +- modules/manager/visitormanager.js | 135 +- modules/preferences.js | 7 +- modules/support/alertservice.js | 12 +- modules/support/atoms.js | 14 +- modules/support/batchgen.js | 248 ++- modules/support/contenthandling.js | 273 ++- modules/support/cothreads.js | 124 +- modules/support/defer.js | 4 +- modules/support/domainprefs.js | 192 +-- modules/support/fileextsheet.js | 38 +- modules/support/filtermanager.js | 453 ++--- modules/support/historymanager.js | 45 +- modules/support/iconcheat.js | 26 +- modules/support/icons.js | 23 +- modules/support/memoize.js | 6 +- modules/support/metalinker.js | 106 +- modules/support/movefile.js | 4 +- modules/support/movefile_worker.js | 2 +- modules/support/observers.js | 37 +- modules/support/overlays.js | 9 +- modules/support/requestmanipulation.js | 46 +- modules/support/scheduleautostart.js | 12 +- modules/support/serverlimits.js | 457 +++-- modules/support/stringfuncs.js | 1 + modules/support/textlinks.js | 33 +- modules/support/urlmanager.js | 96 +- modules/utils.js | 114 +- modules/version.js | 2 +- 89 files changed, 3939 insertions(+), 4459 deletions(-) diff --git a/LICENSE b/LICENSE index 4072ac263..da2e7b6ab 100644 --- a/LICENSE +++ b/LICENSE @@ -7,7 +7,7 @@ DownThemAll! as a whole is licensed under GPL 2.0. See the GPL file. Individual files may have additional and/or different licenses. See the corresponding repository files available from - + Most files are licensed under the GPL/LGPL-compatible MPL 2.0, or are tri-licensed under a MPL 1.1/GPL 2.0/LGPL 2.1 license. See the MPL, GPL, LGPL files respectively. diff --git a/chrome.manifest b/chrome.manifest index 564906922..f1e384bfe 100644 --- a/chrome.manifest +++ b/chrome.manifest @@ -8,7 +8,6 @@ content dta-public chrome/public/ contentaccessible skin dta-public classic/1.0 chrome/public/ ## aux packages ## -content dta-tests tests/ content dta-modules modules/ ## additional locales @@ -34,15 +33,14 @@ locale dta zh-CN chrome/locale/zh-CN/ locale dta zh-TW chrome/locale/zh-TW/ ## platform package ## -content dta-platform chrome/content/unix/ -content dta-platform chrome/content/mac/ os=Darwin -content dta-platform chrome/content/win/ os=WINNT -skin dta-platform classic/1.0 chrome/skin/unix/ -skin dta-platform classic/1.0 chrome/skin/mac/ os=Darwin -skin dta-platform classic/1.0 chrome/skin/win/ os=WINNT +content dta-platform chrome/content/ platform +skin dta-platform classic/1.0 chrome/skin/ override chrome://dta/content/dta/manager.xul chrome://dta/content/dta/manager-aero.xul os=WINNT osversion=6 override chrome://dta/content/dta/manager.xul chrome://dta/content/dta/manager-newer.xul os=WINNT osversion>6 override chrome://dta/content/dta/manager.xul chrome://dta/content/dta/manager-aero.xul os=WINNT osversion=6.1 override chrome://dta-platform/skin/common.css chrome://dta-platform/skin/common-aero.css os=WINNT osversion>=6 override chrome://dta/skin/manager/netstatus.png chrome://dta-platform/skin/netstatus-aero.png os=WINNT osversion>=6 + +# Hack to prevent .Net Framework Assistant from messing up the browser, courtesy of AdBlock Plus +override chrome://dotnetassistant/content/bootstrap.xul data:text/xml, diff --git a/chrome/content/common/bindings.xml b/chrome/content/common/bindings.xml index a474d45b3..7222f6f11 100644 --- a/chrome/content/common/bindings.xml +++ b/chrome/content/common/bindings.xml @@ -703,9 +703,9 @@ 4) { return ref; @@ -805,7 +805,7 @@ if (l && poorMansSimilarity(newRef, ref) < 5) { return newRef; } - return reccolor(newRef, ++l); + return arguments.callee(newRef, ++l); })(); } diff --git a/chrome/content/common/internalFunctions.js b/chrome/content/common/internalFunctions.js index 0c0ea65af..3aaf70ae0 100644 --- a/chrome/content/common/internalFunctions.js +++ b/chrome/content/common/internalFunctions.js @@ -4,8 +4,8 @@ /* dTa-only code! - DO NOT include in overlays or such! */ "use strict"; -/* jshint browser:true */ -/* global _, Cc:true, Ci:true, Cu:true, Cr:true, ctor:true, Exception:true */ +/* jshint strict:true, globalstrict:true, browser:true */ +/* global _, Cc:true, Ci:true, Cr:true, Cu:true, ctor:true, Exception:true */ var Cc = Components.classes; var Ci = Components.interfaces; var Cr = Components.results; @@ -48,8 +48,7 @@ var getLargeIcon = (function() { var getFavIcon = (function() { const RE_HTML = /html?$|aspx?$|php\d?$|py$|\/[^.]*$/i; return function getFavIcon(uri, cb, tp) { - const path = uri.path || uri.pathQueryRef; - if (!RE_HTML.test(path)) { + if (!RE_HTML.test(uri.path)) { cb.call(tp, getIcon(uri), false); return; } @@ -65,18 +64,18 @@ var getFavIcon = (function() { * @return Either the element when there was just one parameter, or an array of * elements. */ -function $(...args) { - if (args.length === 1) { - return document.getElementById(args[0]); +function $() { + if (arguments.length === 1) { + return document.getElementById(arguments[0]); } let elements = []; - for (let i = 0, e = args.length; i < e; ++i) { - let element = document.getElementById(args[i]); + for (let i = 0, e = arguments.length; i < e; ++i) { + let element = document.getElementById(arguments[i]); if (element) { elements.push(element); } else { - log(LOG_ERROR, "requested a non-existing element: " + args[i]); + log(LOG_ERROR, "requested a non-existing element: " + arguments[i]); } } return elements; @@ -207,7 +206,7 @@ var Utils = { if (!isFinite(aNumber)) { return 'NaN'; } - return _('sizeKB', [aNumber.toFixed(decimalPlace || 1)]); + return _('sizeKB', [aNumber.toFixed(arguments.length > 1 ? decimalPlace : 1)]); }, formatConflictName: function(basename, conflicts) { @@ -228,8 +227,7 @@ var Utils = { const sunits = units; const nunits = sunits.length; const s = scale; - const {memoize} = require("support/memoize"); - return memoize(function(val, decimalPlace) { + return function(val, decimalPlace) { var rv = val; if (!isFinite(rv)) { return 'NaN'; @@ -239,9 +237,9 @@ var Utils = { rv /= 1024; } const unit = sunits[i]; - decimalPlace = isFinite(decimalPlace) ? decimalPlace : unit[1]; + decimalPlace = arguments.length > 1 ? decimalPlace : unit[1]; return _(unit[0], [rv.toFixed(decimalPlace)], unit[2] && Math.floor(rv)); - }, 50); + }; } Utils.formatBytes = createFormatter( [['sizeB.2', 0, true], ['sizeKB', 1], ['sizeMB', 2], ['sizeGB', 2], ['sizeTB', 3]], @@ -268,11 +266,11 @@ requireJoined(Utils, "support/stringfuncs"); */ XPCOMUtils.defineLazyGetter(window, "_", function() { let bundles = new Utils.StringBundles(document); - return function(...args) { - if (args.length === 1) { - return bundles.getString(args[0]); + return function() { + if (arguments.length === 1) { + return bundles.getString(arguments[0]); } - return bundles.getFormattedString(...args); + return bundles.getFormattedString.apply(bundles, arguments); }; }); diff --git a/chrome/content/dta/manager/conflicts.js b/chrome/content/dta/manager/conflicts.js index 08e9a2053..07d5c2c53 100644 --- a/chrome/content/dta/manager/conflicts.js +++ b/chrome/content/dta/manager/conflicts.js @@ -2,7 +2,7 @@ * 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"; -/* jshint browser:true */ +/* jshint browser:true, globalstrict:true, strict:false */ /* global _, $ */ function load() { @@ -24,3 +24,6 @@ function accept() { } return false; } +opener.addEventListener("unload", function unloadOpener() { + opener.removeEventListener("unload", unloadOpener, false); +}, false); diff --git a/chrome/content/dta/manager/info.js b/chrome/content/dta/manager/info.js index 82a944ff0..c4de51b80 100644 --- a/chrome/content/dta/manager/info.js +++ b/chrome/content/dta/manager/info.js @@ -7,6 +7,8 @@ /* jshint strict:true, globalstrict:true, browser:true */ var {defer} = require("support/defer"); +var {TimerManager} = require("support/timers"); +var Timers = new TimerManager(); function discard() { if (opener) { @@ -184,6 +186,7 @@ var Dialog = { }, unload: function() { Tooltip.stop(); + Timers.killAllTimers(); return true; }, browseDir: function() { diff --git a/chrome/content/dta/manager/manager.js b/chrome/content/dta/manager/manager.js index 8e7d9962d..07c04dec7 100644 --- a/chrome/content/dta/manager/manager.js +++ b/chrome/content/dta/manager/manager.js @@ -5,9 +5,10 @@ /* global _, DTA, $, $$, Utils, Preferences, getDefaultDownloadsDirectory, unloadWindow */ /* global $e, mapInSitu, filterMapInSitu, filterInSitu, mapFilterInSitu, setTimeoutOnlyFun */ /* global toURI, toURL, showPreferences, openUrl, getLargeIcon */ -/* global TreeManager, Prefs, ConflictManager */ +/* global Tree, Prefs */ /* global QUEUED, PAUSED, CANCELED, FINISHING, COMPLETE, RUNNING, SPEED_COUNT, REFRESH_FREQ, MIN_CHUNK_SIZE */ -/* jshint strict:true, globalstrict:true, browser:true, latedef:false */ +/* jshint strict:false, globalstrict:true, browser:true, latedef:false */ +/* jshint -W083, -W030 */ var {CoThreadListWalker} = require("support/cothreads"); var Prompts = require("prompts"); @@ -15,6 +16,7 @@ var {ByteBucket} = require("support/bytebucket"); var {GlobalBucket} = require("manager/globalbucket"); var {defer} = require("support/defer"); var PrivateBrowsing = require("support/pbm"); +var {TimerManager} = require("support/timers"); var {ContentHandling} = require("support/contenthandling"); var GlobalProgress = new (require("manager/globalprogress").GlobalProgress)(window); var RequestManipulation = require("support/requestmanipulation"); @@ -30,6 +32,7 @@ var {Connection} = require("manager/connection"); var {createRenamer} = require("manager/renamer"); var {memoize, identity} = require("support/memoize"); var {moveFile} = require("support/movefile"); +var {Task} = requireJSM("resource://gre/modules/Task.jsm"); // Use the main OS.File here! var {OS} = requireJSM("resource://gre/modules/osfile.jsm"); @@ -40,23 +43,14 @@ XPCOMUtils.defineLazyGetter(window, "AlertService", () => require("support/alert XPCOMUtils.defineLazyGetter(window, "Decompressor", () => require("manager/decompressor").Decompressor); XPCOMUtils.defineLazyGetter(window, "Verificator", () => require("manager/verificator")); XPCOMUtils.defineLazyGetter(window, "FileExts", () => new FileExtensionSheet(window, Tree)); -XPCOMUtils.defineLazyGetter(window, "ConflictManager", () => { - return new (require("manager/conflicts"))( - window, Utils.formatConflictName.bind(Utils), Prefs, _); -}); - -let speedElems; /* global TextCache_PAUSED, TextCache_QUEUED, TextCache_COMPLETE, TextCache_CANCELED, TextCache_NAS */ /* global TextCache_UNKNOWN, TextCache_OFFLINE, TextCache_TIMEOUT, TextCache_STARTING, TextCache_DECOMPRESSING */ -/* global TextCache_VERIFYING, TextCache_MOVING, TextCache_FINISHING */ +/* global TextCache_VERIFYING, TextCache_MOVING */ addEventListener("load", function load_textCache() { removeEventListener("load", load_textCache, false); - speedElems = $('listSpeeds', 'perDownloadSpeedLimitList'); const texts = ['paused', 'queued', 'complete', 'canceled', 'nas', 'unknown', - 'offline', 'timeout', 'starting', 'decompressing', 'verifying', 'moving', - 'finishing', - ]; + 'offline', 'timeout', 'starting', 'decompressing', 'verifying', 'moving']; for (let i = 0, text; i < texts.length; ++i) { text = texts[i]; window["TextCache_" + text.toUpperCase()] = _(text); @@ -73,29 +67,22 @@ function isOSError(ex, unix, win) { return false; } -function timeout(secs) { - return new Promise(function(resolve) { - setTimeoutOnlyFun(() => resolve(), secs); - }); -} - function _moveFile(destination, self) { let remakeDir = false; - let move = async function() { + let move = function*() { for (let x = 0; x < 10; ++x) { if (remakeDir) { - await Utils.makeDir(destination, Prefs.dirPermissions, true); + yield Utils.makeDir(destination, Prefs.dirPermissions, true); } let df = destination.clone(); df.append(self.destinationName); try { - await moveFile(self.tmpFile.path, df.path, self.shouldOverwrite); + yield moveFile(self.tmpFile.path, df.path, self.shouldOverwrite); return; } catch (ex) { if (isOSError(ex, "EEXIST", "ERROR_ALREADY_EXISTS") && !self.shouldOverwrite) { self.conflicts += 1; - x--; continue; } if (isOSError(ex, "ENAMETOOLONG", "ERROR_PATH_NOT_FOUND")) { @@ -114,13 +101,15 @@ function _moveFile(destination, self) { remakeDir = true; } log(LOG_ERROR, ex); - await timeout(x * 500); + yield new Promise(function(resolve) { + setTimeoutOnlyFun(() => resolve(), x * 500); + }); } } log(LOG_ERROR, "shit hit the fan!"); throw new Exception("Failed to move file"); }; - return move(); + return Task.spawn(move); }; function dieEarly() { @@ -131,35 +120,7 @@ function dieEarly() { } window.addEventListener("unload", dieEarly, false); -function _downloadChunk(download, chunk, header) { - chunk.download = new Connection(download, chunk, header || download.mustGetInfo); - chunk.running = true; - download.mustGetInfo = false; - download.setState(RUNNING); - log(LOG_DEBUG, "started: " + chunk); - ++download.activeChunks; - ++download.sessionConnections; -} -function downloadNewChunk(download, start, end, header) { - let chunk = new Chunk(download, start, end); - download.chunks.push(chunk); - download.chunks.sort(function(a,b) { return a.start - b.start; }); - _downloadChunk(download, chunk, header); -} -function downloadOldChunk(download, chunk, header) { - if (chunk.wasOpened) { - let idx = download.chunks.indexOf(chunk); - if (idx < 0) { - throw Error("Invalid chunk"); - } - let newChunk = chunk.clone(); - download.chunks[idx] = newChunk; - _downloadChunk(download, newChunk, header); - } - else { - _downloadChunk(download, chunk, header); - } -} +var Timers = new TimerManager(); var Dialog_loadDownloads_props = ['contentType', 'conflicts', 'postData', 'destinationName', 'resumable', 'compression', @@ -172,7 +133,6 @@ var Dialog_serialize_props = ['fileName', 'fileNameFromUser', 'postData', 'description', 'title', 'resumable', 'mask', 'pathName', 'compression', 'contentType', 'conflicts', 'fromMetalink', 'speedLimit', "relaxSize", "cleanRequest"]; -var Tree; var Dialog = { _observes: [ 'quit-application-requested', @@ -216,7 +176,7 @@ var Dialog = { _wasRunning: false, _sum: 0, _speeds: new SpeedStats(10), - _running: new Set(), + _running: [], _autoClears: [], completed: 0, finishing: 0, @@ -307,7 +267,7 @@ var Dialog = { }, false); let tree = $("downloads"); - Tree = new TreeManager(tree); + Tree.init(tree); addEventListener("unload", function unloadUnlink() { removeEventListener("unload", unloadUnlink, false); Tree.unlink(); @@ -488,7 +448,7 @@ var Dialog = { $('listSpeeds').limit -= 102400; this.changeSpeedLimit(); }, - _loadDownloads: async function() { + _loadDownloads: function() { this._loading = $('loading'); if (!this._loading) { this._loading = {}; @@ -499,53 +459,25 @@ var Dialog = { log(LOG_INFO, "loading of the queue started!"); GlobalProgress.reset(); GlobalProgress.pause(); - try { - let result = await QueueStore.loadItems(); - if (result && result.length) { - log(LOG_INFO, "Result has arrived: " + result.length); - await new Promise((resolve, reject) => { - let loader = new CoThreadListWalker( - this._loadDownloads_item, - result, - -1, - this - ); - loader.start(resolve); - }); - } - log(LOG_INFO, "Result was processed"); - } - catch (ex) { - log(LOG_ERROR, "Failed to load QueueStore items", ex); - } - - Tree.savePositions(); - Tree.invalidate(); - Tree.doFilter(); - Tree.endUpdate(); - - if (this._brokenDownloads.length) { - QueueStore.beginUpdate(); - try { - for (let id of this._brokenDownloads) { - QueueStore.deleteDownload(id); - log(LOG_ERROR, "Removed broken download #" + id); - } - } - catch (ex) { - log(LOG_ERROR, "failed to remove broken downloads", ex); + QueueStore.loadItems(function(result) { + if (!result || !result.length) { + log(LOG_DEBUG, "The cake is a lie"); + this._loadDownloads_finish(); + return; } - QueueStore.endUpdate(); - } - delete this._brokenDownloads; - delete this._loading; - - GlobalProgress.reset(); - this.statusText.hidden = false; - - this._timerProcess = setInterval(() => this.process(), REFRESH_FREQ); - this.refresh(); - this.start(); + log(LOG_INFO, "Result has arrived: " + result.length); + this._loader = new CoThreadListWalker( + this._loadDownloads_item, + result, + -1, + this + ); + let self = this; + this._loader.start(function() { + result = null; + self._loadDownloads_finish(); + }); + }, this); }, _loadDownloads_item: function(dbItem, idx) { if (!idx) { @@ -560,7 +492,7 @@ var Dialog = { try { let down = dbItem.item; - let d = new QueueItem(Dialog); + let d = new QueueItem(); d.dbId = dbItem.id; let state = Dialog_loadDownloads_get(down, "state"); if (state) { @@ -581,7 +513,7 @@ var Dialog = { } // only access the setter of the last so that we don't generate stuff trice. - d._pathName = identity(Utils.addFinalSlash(Dialog_loadDownloads_get(down, "pathName"))); + d._pathName = identity(Dialog_loadDownloads_get(down, "pathName")); d._description = identity(Dialog_loadDownloads_get(down, "description")); d._title = identity(Dialog_loadDownloads_get(down, "title")); d._mask = identity(Dialog_loadDownloads_get(down, "mask")); @@ -674,6 +606,38 @@ var Dialog = { } return true; }, + _loadDownloads_finish: function() { + log(LOG_INFO, "Result was processed"); + delete this._loader; + Tree.savePositions(); + Tree.invalidate(); + Tree.doFilter(); + Tree.endUpdate(); + + if (this._brokenDownloads.length) { + QueueStore.beginUpdate(); + try { + for (let id of this._brokenDownloads) { + QueueStore.deleteDownload(id); + log(LOG_ERROR, "Removed broken download #" + id); + } + } + catch (ex) { + log(LOG_ERROR, "failed to remove broken downloads", ex); + } + QueueStore.endUpdate(); + } + delete this._brokenDownloads; + delete this._loading; + + GlobalProgress.reset(); + this.statusText.hidden = false; + + this._updTimer = Timers.createRepeating(REFRESH_FREQ, this.process, this, true); + this.refresh(); + this.start(); + }, + openAdd: function() { window.openDialog( 'chrome://dta/content/dta/addurl.xul', @@ -684,7 +648,7 @@ var Dialog = { openDonate: function() { try { - openUrl('http://www.downthemall.net/howto/donate/'); + openUrl('https://www.downthemall.org/howto/donate/'); } catch(ex) { window.alert(ex); @@ -710,8 +674,8 @@ var Dialog = { this.run(d); } } - this._timerWritten = setInterval(() => this.refreshWritten(), 200); - this._timerSave = setInterval(() => this.saveRunning(), 10000); + Timers.createRepeating(200, this.refreshWritten, this, true); + Timers.createRepeating(10000, this.saveRunning, this); $('loadingbox').parentNode.removeChild($('loadingbox')); window.removeEventListener("unload", dieEarly, false); @@ -740,7 +704,7 @@ var Dialog = { } }, _continueReinit: function() { - this._running = new Set(); + this._running = []; delete this._forceQuit; this._speeds.clear(); this.offlineForced = false; @@ -802,7 +766,8 @@ var Dialog = { refresh: function() { try { const now = Utils.getTimestamp(); - for (let d of this._running) { + for (let i = 0, e = this._running.length; i < e; ++i) { + let d = this._running[i]; if (!d) { continue; } @@ -830,7 +795,7 @@ var Dialog = { this._speeds.add(this._sum, now); let speed = Utils.formatSpeed(this._speeds.avg); this._maxObservedSpeed = Math.max(this._speeds.avg || this._maxObservedSpeed, this._maxObservedSpeed); - for (let e of speedElems) { + for (let e of $('listSpeeds', 'perDownloadSpeedLimitList')) { try { e.hint = this._maxObservedSpeed; hintChunkBufferSize(this._maxObservedSpeed); @@ -842,8 +807,8 @@ var Dialog = { // Refresh status bar this.statusText.label = _("currentdownloadstats", - [this.completed, Tree.downloadCount, Tree.rowCount, this._running.size]); - if (!this._running.size) { + [this.completed, Tree.downloadCount, Tree.rowCount, this._running.length]); + if (!this._running.length) { this.statusSpeed.hidden = true; } else { @@ -852,11 +817,10 @@ var Dialog = { } // Refresh window title - let fr = this._running.values().next().value || null; - if (this._running.size === 1 && fr.totalSize > 0) { + if (this._running.length === 1 && this._running[0].totalSize > 0) { if (Tree.filtered) { document.title = _('titlespeedfiltered', [ - fr.percent, + this._running[0].percent, this.statusSpeed.label, this.completed, Tree.downloadCount, @@ -865,20 +829,20 @@ var Dialog = { } else { document.title = _('titlespeed', [ - fr.percent, + this._running[0].percent, this.statusSpeed.label, this.completed, Tree.downloadCount, ]); } - if (fr.totalSize) { - GlobalProgress.activate(fr.progress * 10, 1000); + if (this._running[0].totalSize) { + GlobalProgress.activate(this._running[0].progress * 10, 1000); } else { GlobalProgress.unknown(); } } - else if (this._running.size > 0) { + else if (this._running.length > 0) { let p = Math.floor(this.completed * 1000 / Tree.downloadCount); let pt = Math.floor(this.completed * 100 / Tree.downloadCount) + '%'; if (Tree.filtered) { @@ -950,7 +914,8 @@ var Dialog = { } }, refreshWritten: function() { - for (let d of this._running) { + for (let i = 0, e = this._running.length; i < e; ++i) { + let d = this._running[i]; if (!d) { continue; } @@ -959,11 +924,11 @@ var Dialog = { } }, saveRunning: function() { - if (!this._running.size) { + if (!this._running.length) { return; } - for (let d of this._running) { - d.save(); + for (let i = 0, e = this._running.length; i < e; ++i) { + this._running[i].save(); } }, @@ -990,17 +955,14 @@ var Dialog = { Tree.box.invalidate(); }, - _filterAutoRetrying(d) { - return !d.autoRetry(); - }, - process: function() { try { Prefs.refreshConnPrefs(this._running); this.refresh(); let ts = Utils.getTimestamp(); - for (let d of this._running) { + for (let i = 0, e = this._running.length; i < e; ++i) { + let d = this._running[i]; if (!d || d.isCritical) { continue; } @@ -1021,7 +983,7 @@ var Dialog = { if (!this.offline && !this._mustReload) { if (Prefs.autoRetryInterval) { - filterInSitu(this._autoRetrying, this._filterAutoRetrying); + filterInSitu(this._autoRetrying, d => !d.autoRetry()); } this.startNext(); } @@ -1049,7 +1011,7 @@ var Dialog = { try { var rv = false; // pre-condition, do check prior to loop, or else we'll have the generator cost. - if (this._running.size >= Prefs.maxInProgress) { + if (this._running.length >= Prefs.maxInProgress) { return false; } if (Prefs.schedEnabled) { @@ -1082,7 +1044,7 @@ var Dialog = { log(LOG_DEBUG, "rebuild scheduler"); } let finishingPenality = Math.ceil(this.finishing / 10); - while (this._running.size < Prefs.maxInProgress - finishingPenality) { + while (this._running.length < Prefs.maxInProgress - finishingPenality) { let d = this.scheduler.next(this._running); if (!d) { break; @@ -1123,8 +1085,7 @@ var Dialog = { if (download.totalSize) { download.partialSize = download.totalSize; } - log(LOG_INFO, - "Download seems to be complete; likely a left-over from a crash, finish it:" + download); + log(LOG_INFO, "Download seems to be complete; likely a left-over from a crash, finish it:" + download); download.finishDownload(); return true; } @@ -1140,13 +1101,16 @@ var Dialog = { else { log(LOG_INFO, "Let's resume " + download + " at " + download.partialSize); } - this._running.add(download); + this._running.push(download); download.prealloc(); download.resumeDownload(); return true; }, wasStopped: function(download) { - this._running.delete(download); + let idx = this._running.indexOf(download); + if (idx > -1) { + this._running.splice(idx, 1); + } }, wasFinished: function() { --this.finishing; @@ -1223,8 +1187,11 @@ var Dialog = { } }, wasRemoved: function(download) { - this._running.delete(download); - let idx = this._autoRetrying.indexOf(download); + let idx = this._running.indexOf(download); + if (idx > -1) { + this._running.splice(idx, 1); + } + idx = this._autoRetrying.indexOf(download); if (idx > -1) { this._autoRetrying.splice(idx, 1); } @@ -1282,9 +1249,9 @@ var Dialog = { // stop everything! // enumerate everything we'll have to wait for! - if (this._timerProcess) { - clearInterval(this._timerProcess); - delete this._timerProcess; + if (this._updTimer) { + Timers.killTimer(this._updTimer); + delete this._updTimer; } let chunks = 0; @@ -1317,7 +1284,7 @@ var Dialog = { if (chunks || finishing) { if (!this._forceClose && this._safeCloseAttempts < 20) { ++this._safeCloseAttempts; - setTimeoutOnlyFun(() => this.shutdown(callback), 250); + Timers.createOneshot(250, () => this.shutdown(callback), this); return false; } log(LOG_ERROR, "Going down even if queue was not probably closed yet!"); @@ -1360,9 +1327,10 @@ var Dialog = { unload: function() { Limits.killServerBuckets(); - clearInterval(this._timerRunning); - clearInterval(this._timerSave); - clearInterval(this._timerProcess); + Timers.killAllTimers(); + if (this._loader) { + this._loader.cancel(); + } Prefs.shutdown(); try { this._cleanTmpDir(); @@ -1469,47 +1437,62 @@ var Metalinker = { }; requireJoined(Metalinker, "support/metalinker"); -var QueueItem = class QueueItem { - constructor(dialog) { - this.dialog = dialog; - - this.visitors = new VisitorManager(); - this.chunks = []; - this.speeds = new SpeedStats(SPEED_COUNT); - this.rebuildDestination_renamer = createRenamer(this); - } - - get maskURL() { - return this.urlManager.usableURL; - } - - get maskCURL() { - return Utils.getCURL(this.maskURL); - } - - get maskURLPath() { - return this.urlManager.usableURLPath; - } - - get maskReferrerURL() { - return this.referrerUrlManager.usableURL; - } - - get maskReferrerURLPath() { - return this.referrerUrlManager.usableURLPath; - } +function QueueItem() { + this.visitors = new VisitorManager(); - get maskReferrerCURL() { - return Utils.getCURL(this.maskReferrerURL); - } + this.chunks = []; + this.speeds = new SpeedStats(SPEED_COUNT); + this.rebuildDestination_renamer = createRenamer(this); +} - get autoRetrying() { - return !!this._autoRetryTime; - } +QueueItem.prototype = { + state: QUEUED, + _setStateInternal: function(nv) { + Object.defineProperty(this, "state", {value: nv, configurable: true, enumerable: true}); + }, + setState: function(nv) { + if (this.state === nv) { + return nv; + } + if (this.state === RUNNING) { + // remove ourself from inprogresslist + Dialog.wasStopped(this); + // kill the bucket via it's setter + this.bucket = null; + } + else if (this.state === COMPLETE) { + --Dialog.completed; + } + else if (this.state === FINISHING) { + --Dialog.finishing; + } + this.speed = ''; + this._setStateInternal(nv); + if (this.state === RUNNING) { + // set up the bucket + this._bucket = new ByteBucket(this.speedLimit, 1.7, "download"); + } + else if (this.state === FINISHING) { + ++Dialog.finishing; + if (!this.totalSize) { + // We are done now, just set indeterminate size downloads to what we actually downloaded + this.refreshPartialSize(); + this.totalSize = this.partialSize; + } + } + else if (this.state === COMPLETE) { + ++Dialog.completed; + } + Dialog.signal(this); + this.invalidate(); + Tree.refreshTools(); + return nv; + }, + _bucket: null, get bucket() { return this._bucket; - } + }, set bucket(nv) { if (nv !== null) { throw new Exception("Bucket is only nullable"); @@ -1517,11 +1500,12 @@ var QueueItem = class QueueItem { if (this._bucket) { this._bucket = null; } - } + }, + _speedLimit: -1, get speedLimit() { return this._speedLimit; - } + }, set speedLimit(nv) { nv = Math.max(nv, -1); if (this._speedLimit === nv) { @@ -1532,11 +1516,20 @@ var QueueItem = class QueueItem { this._bucket.byteRate = this.speedLimit; } this.save(); - } + }, + otherBytes: 0, + + postData: null, + + fromMetalink: false, + bNum: 0, + iNum: 0, + _fileName: null, + fileNameFromUser: false, get fileName() { return this._fileName; - } + }, set fileName(nv) { if (this._fileName === nv || this.fileNameFromUser) { return nv; @@ -1547,8 +1540,36 @@ var QueueItem = class QueueItem { this.rebuildDestination(); this.invalidate(0); return nv; - } - + }, + setUserFileName: function(name) { + try { + Tree.beginUpdate(); + this.fileNameFromUser = false; + this.fileName = name; + this.fileNameFromUser = true; + this.save(); + } + finally { + this.iconProp; // set up initial icon to avoid display problems + Tree.invalidate(); + Tree.endUpdate(); + } + }, + shortenName: function() { + let fn = this.destinationName; + let ext = Utils.getExtension(fn); + if (ext) { + fn = fn.substring(0, fn.length - ext.length - 1); + } + let nn = fn.substr(0, Math.min(200, Math.max(fn.length - 25, 10))); + if (nn === fn) { + return; + } + if (ext) { + nn += "." + ext; + } + this.destinationName = nn; + }, get fileNameAndExtension() { if (!this._fileNameAndExtension) { let fn = this.fileName; @@ -1577,15 +1598,13 @@ var QueueItem = class QueueItem { this._fileNameAndExtension = {name: fn, extension: ext }; } return this._fileNameAndExtension; - } - + }, get referrerUrlManager() { if (this.referrer && !this._referrerUrlManager) { this._referrerUrlManager = new UrlManager([this.referrer]); } return this._referrerUrlManager; - } - + }, get referrerFileNameAndExtension() { if (!this.referrerUrlManager) { return null; @@ -1602,11 +1621,11 @@ var QueueItem = class QueueItem { this._referrerFileNameAndExtension = {name: fn, extension: ext}; } return this._referrerFileNameAndExtension; - } - + }, + _description: null, get description() { return this._description; - } + }, set description(nv) { if (nv === this._description) { return nv; @@ -1615,11 +1634,11 @@ var QueueItem = class QueueItem { this.rebuildDestination(); this.invalidate(0); return nv; - } - + }, + _title: '', get title() { return this._title; - } + }, set title(nv) { if (nv === this._title) { return this._title; @@ -1628,25 +1647,26 @@ var QueueItem = class QueueItem { this.rebuildDestination(); this.invalidate(0); return this._title; - } - + }, + _pathName: null, get pathName() { return this._pathName; - } + }, set pathName(nv) { nv = nv.toString(); if (this._pathName === nv) { return nv; } - this._pathName = identity(Utils.addFinalSlash(nv)); + this._pathName = identity(nv); this.rebuildDestination(); this.invalidate(0); return nv; - } + }, + _mask: null, get mask() { return this._mask; - } + }, set mask(nv) { if (this._mask === nv) { return nv; @@ -1655,11 +1675,14 @@ var QueueItem = class QueueItem { this.rebuildDestination(); this.invalidate(7); return nv; - } + }, + _destinationName: null, + destinationNameOverride: null, + _destinationNameFull: null, get destinationName() { return this._destinationNameFull; - } + }, set destinationName(nv) { if (this.destinationNameOverride === nv) { return this._destinationNameFull; @@ -1668,25 +1691,28 @@ var QueueItem = class QueueItem { this.rebuildDestination(); this.invalidate(0); return this._destinationNameFull; - } + }, + _destinationFile: null, get destinationFile() { if (!this._destinationFile) { this.rebuildDestination(); } return this._destinationFile; - } + }, + _destinationLocalFile: null, get destinationLocalFile() { if (!this._destinationLocalFile) { this.rebuildDestination(); } return this._destinationLocalFile; - } + }, + _conflicts: 0, get conflicts() { return this._conflicts; - } + }, set conflicts(nv) { if (this._conflicts === nv) { return nv; @@ -1695,8 +1721,8 @@ var QueueItem = class QueueItem { this.rebuildDestination(); this.invalidate(0); return nv; - } - + }, + _tmpFile: null, get tmpFile() { if (!this._tmpFile) { var dest = Prefs.tempLocation ? @@ -1710,11 +1736,11 @@ var QueueItem = class QueueItem { this._tmpFile = dest; } return this._tmpFile; - } - + }, + _hashCollection: null, get hashCollection() { return this._hashCollection; - } + }, set hashCollection(nv) { if (nv && !(nv instanceof DTA.HashCollection)) { throw new Exception("Not a hash collection"); @@ -1723,58 +1749,99 @@ var QueueItem = class QueueItem { this._prettyHash = this._hashCollection ? _('prettyhash', [this._hashCollection.full.type, this._hashCollection.full.sum]) : TextCache_NAS; - } - + }, + _prettyHash: null, get prettyHash() { return this._prettyHash; - } + }, + is: function(state) { + return this.state === state; + }, + isOf: function(states) { + return (this.state & states) !== 0; + }, + save: function() { + if (this.deleting) { + return false; + } + const state = this.state; + if ((Prefs.removeCompleted && state === COMPLETE) || + (Prefs.removeCanceled && state === CANCELED) || + (Prefs.removeAborted && state === PAUSED)) { + if (this.dbId) { + this.remove(); + } + return false; + } + if (this.isPrivate) { + return false; + } + if (this.dbId) { + QueueStore.saveDownload(this.dbId, JSON.stringify(this)); + return true; + } + this.dbId = QueueStore.queueDownload(JSON.stringify(this), this.position); + return true; + }, + remove: function() { + QueueStore.deleteDownload(this.dbId); + delete this.dbId; + }, + position: -1, + _contentType: "", get contentType() { return this._contentType; - } + }, set contentType(nv) { if (nv === this._contentType) { return; } this._contentType = nv; delete this._fileNameAndExtension; - } - - get totalSize() { - return this._totalSize; - } + }, + visitors: null, + relaxSize: false, + _totalSize: 0, + get totalSize() { return this._totalSize; }, set totalSize(nv) { if (nv >= 0 && !isNaN(nv)) { this._totalSize = Math.floor(nv); } this.invalidate(3); this.prealloc(); - } + }, + partialSize: 0, + progress: 0, + mustGetInfo: false, get startDate() { return this._startDate || (this.startDate = new Date()); - } + }, set startDate(nv) { this._startDate = nv; - } + }, + + compression: null, + + resumable: true, + started: false, get canResumeLater() { return this.resumable && !this.isPrivate; - } + }, + _activeChunks: 0, get activeChunks() { return this._activeChunks; - } + }, set activeChunks(nv) { nv = Math.max(0, nv); - if (!nv && this.state === RUNNING) { - log(LOG_INFO, `active chunks set to zero while running: ${nv} ${this._activeChunks}`); - } this._activeChunks = nv; this.invalidate(6); return this._activeChunks; - } - + }, + _maxChunks: 0, get maxChunks() { if (!this.urlManager) { return Prefs.maxChunks; @@ -1784,7 +1851,7 @@ var QueueItem = class QueueItem { this._maxChunks = (limit ? limit.segments : 0) || Prefs.maxChunks; } return this._maxChunks; - } + }, set maxChunks(nv) { this._maxChunks = nv; if (this._maxChunks < this._activeChunks) { @@ -1804,20 +1871,21 @@ var QueueItem = class QueueItem { this.invalidate(6); log(LOG_DEBUG, "mc set to " + nv); return this._maxChunks; - } + }, + timeLastProgress: 0, + timeStart: 0, + _icon: null, get iconProp() { if (!this._icon) { let icon = FileExts.getAtom(this.destinationName, 'metalink' in this).toString(); this._icon = identity((this.isPrivate ? "iconic private file " : "iconic file ") + icon); } return this._icon; - } - + }, get largeIcon() { return getLargeIcon(this.destinationName, 'metalink' in this); - } - + }, get dimensionString() { if (this.partialSize <= 0) { return TextCache_UNKNOWN; @@ -1829,30 +1897,27 @@ var QueueItem = class QueueItem { return Utils.formatBytes(this.totalSize); } return _('transfered', [Utils.formatBytes(this.partialSize), Utils.formatBytes(this.totalSize)]); - } - + }, + _status : '', get status() { - if (this.dialog.offline && this.isOf(QUEUED | PAUSED)) { + if (Dialog.offline && this.isOf(QUEUED | PAUSED)) { return TextCache_OFFLINE; } return this._status + (this.autoRetrying ? ' *' : ''); - } - + }, set status(nv) { if (nv !== this._status) { this._status = nv; this.invalidate(); } return this._status; - } - + }, get parts() { if (this.maxChunks) { return (this.activeChunks) + '/' + this.maxChunks; } return ''; - } - + }, get percent() { const state = this.state; if (!this.totalSize && state === RUNNING) { @@ -1865,132 +1930,17 @@ var QueueItem = class QueueItem { return "100%"; } return this.progress + "%"; - } - + }, + _destinationPath: '', get destinationPath() { return this._destinationPath; - } - - get isCritical() { - return this._criticals !== 0; - } - - _setStateInternal(nv) { - Object.defineProperty(this, "state", {value: nv, configurable: true, enumerable: true}); - } - - setState(nv) { - if (this.state === nv) { - return nv; - } - if (this.state === RUNNING) { - // remove ourself from inprogresslist - this.dialog.wasStopped(this); - // kill the bucket via it's setter - this.bucket = null; - } - else if (this.state === COMPLETE) { - --this.dialog.completed; - } - else if (this.state === FINISHING) { - --this.dialog.finishing; - } - this.speed = ''; - this._setStateInternal(nv); - if (this.state === RUNNING) { - // set up the bucket - this._bucket = new ByteBucket(this.speedLimit, 1.2, "download"); - } - else if (this.state === FINISHING) { - ++this.dialog.finishing; - if (!this.totalSize) { - // We are done now, just set indeterminate size downloads to what we actually downloaded - this.refreshPartialSize(); - this.totalSize = this.partialSize; - } - } - else if (this.state === COMPLETE) { - ++this.dialog.completed; - } - this.dialog.signal(this); - this.invalidate(); - Tree.refreshTools(); - return nv; - } - - setUserFileName(name) { - try { - Tree.beginUpdate(); - this.fileNameFromUser = false; - this.fileName = name; - this.fileNameFromUser = true; - this.save(); - let dummy = this.iconProp; // set up initial icon to avoid display problems - } - finally { - Tree.invalidate(); - Tree.endUpdate(); - } - } - - shortenName() { - let fn = this.destinationName; - let ext = Utils.getExtension(fn); - if (ext) { - fn = fn.substring(0, fn.length - ext.length - 1); - } - let nn = fn.substr(0, Math.min(200, Math.max(fn.length - 25, 10))); - if (nn === fn) { - return; - } - if (ext) { - nn += "." + ext; - } - this.destinationName = nn; - } - - is(state) { - return this.state === state; - } - - isOf(states) { - return (this.state & states) !== 0; - } - - save() { - if (this.deleting) { - return false; - } - const state = this.state; - if ((Prefs.removeCompleted && state === COMPLETE) || - (Prefs.removeCanceled && state === CANCELED) || - (Prefs.removeAborted && state === PAUSED)) { - if (this.dbId) { - this.remove(); - } - return false; - } - if (this.isPrivate) { - return false; - } - if (this.dbId) { - QueueStore.saveDownload(this.dbId, JSON.stringify(this)); - return true; - } - this.dbId = QueueStore.queueDownload(JSON.stringify(this), this.position); - return true; - } - - remove() { - QueueStore.deleteDownload(this.dbId); - delete this.dbId; - } + }, - invalidate(cell) { + invalidate: function(cell) { Tree.invalidate(this, cell); - } + }, - safeRetry(resumable) { + safeRetry: function(resumable) { this.cancel().then(() => { // reset flags this.progress = this.totalSize = this.partialSize = 0; @@ -2005,11 +1955,11 @@ var QueueItem = class QueueItem { this.visitors = new VisitorManager(); this.resumable = resumable !== false; this.setState(QUEUED); - this.dialog.run(this); + Dialog.run(this); }); - } + }, - refreshPartialSize() { + refreshPartialSize: function(){ let size = 0; for (let i = 0, e = this.chunks.length; i < e; ++i) { size += this.chunks[i].written; @@ -2029,40 +1979,39 @@ var QueueItem = class QueueItem { this.progress = 100; } } - } + }, - pause() { + pause: function(){ this.setState(PAUSED); if (this.chunks) { for (let c of this.chunks) { if (c.running) { - c.cancelChunk(); + c.pauseChunk(); } } } this.activeChunks = 0; this.speeds.clear(); this.otherBytes = 0; - } - - async moveCompleted() { + }, + moveCompleted: function*() { if (this.state === CANCELED) { throw Error("Cannot move incomplete file"); } this.status = TextCache_MOVING; - let pinned = (await this.resolveConflicts()); + let pinned = (yield this.resolveConflicts()); if (!pinned) { return; } try { let destination = new Instances.LocalFile(this.destinationPath); - await Utils.makeDir(destination, Prefs.dirPermissions); + yield Utils.makeDir(destination, Prefs.dirPermissions); log(LOG_INFO, this.fileName + ": Move " + this.tmpFile.path + " to " + this.destinationFile); // move file if (this.compression) { this.status = TextCache_DECOMPRESSING; - await new Promise(function(resolve, reject) { + yield new Promise(function(resolve, reject) { new Decompressor(this, function(ex) { if (ex) { reject(ex); @@ -2074,132 +2023,132 @@ var QueueItem = class QueueItem { }.bind(this)); return true; } - await _moveFile(destination, this); + yield _moveFile(destination, this); return true; } finally { ConflictManager.unpin(pinned); } return false; - } - - handleMetalink() { + }, + handleMetalink: function() { try { Metalinker.handleDownload(this); } catch (ex) { log(LOG_ERROR, "handleMetalink", ex); } - } - - async verifyHash() { + }, + verifyHash: function() { let oldStatus = this.status; this.status = TextCache_VERIFYING; - let mismatches = await Verificator.verify( - (await OS.File.exists(this.tmpFile.path)) ? this.tmpFile.path : this.destinationFile, - this.hashCollection, - progress => { - this.partialSize = progress; - this.invalidate(); - }); - if (!mismatches) { - log(LOG_ERROR, "hash not computed"); - Prompts.alert(window, _('error', ["Metalink"]), _('verificationfailed', [this.destinationFile])); - return true; - } - else if (mismatches.length) { - log(LOG_ERROR, "Mismatches: " + mismatches.toSource()); - return (await this.verifyHashError(mismatches)); - } - this.status = oldStatus; - return true; - } - - async verifyHashError(mismatches) { - async function deleteFile(file) { - try { - await OS.File.remove(file.path); + return Task.spawn((function*() { + let mismatches = yield Verificator.verify( + (yield OS.File.exists(this.tmpFile.path)) ? this.tmpFile.path : this.destinationFile, + this.hashCollection, + (function(progress) { + this.partialSize = progress; + this.invalidate(); + }).bind(this)); + if (!mismatches) { + log(LOG_ERROR, "hash not computed"); + Prompts.alert(window, _('error', ["Metalink"]), _('verificationfailed', [this.destinationFile])); + return true; } - catch (ex if ex.becauseNoSuchFile) { - // no op + else if (mismatches.length) { + log(LOG_ERROR, "Mismatches: " + mismatches.toSource()); + return (yield this.verifyHashError(mismatches)); } - } + this.status = oldStatus; + return true; + }).bind(this)); + }, + verifyHashError: function(mismatches) { + let file = this.destinationLocalFile; - function recoverPartials(download) { - // merge - for (let i = mismatches.length - 1; i > 0; --i) { - if (mismatches[i].start === mismatches[i-1].end + 1) { - mismatches[i-1].end = mismatches[i].end; - mismatches.splice(i, 1); + return Task.spawn((function*() { + function* deleteFile() { + try { + yield OS.File.remove(file.path); } - } - let chunks = []; - let next = 0; - for (let mismatch of mismatches) { - if (next !== mismatch.start) { - chunks.push(new Chunk(download, next, mismatch.start - 1, mismatch.start - next)); + catch (ex if ex.becauseNoSuchFile) { + // no op } - chunks.push(new Chunk(download, mismatch.start, mismatch.end)); - next = mismatch.end + 1; } - if (next !== download.totalSize) { - log(LOG_DEBUG, "Inserting last"); - chunks.push(new Chunk(download, next, download.totalSize - 1, download.totalSize - next)); - } - download.chunks = chunks; - download.refreshPartialSize(); - download.queue(); - } - let file = this.destinationLocalFile; - filterInSitu(mismatches, e => e.start !== e.end); - - if (mismatches.length && (await OS.File.exists(this.tmpFile.path))) { - // partials + function recoverPartials(download) { + // merge + for (let i = mismatches.length - 1; i > 0; --i) { + if (mismatches[i].start === mismatches[i-1].end + 1) { + mismatches[i-1].end = mismatches[i].end; + mismatches.splice(i, 1); + } + } + let chunks = []; + let next = 0; + for (let mismatch of mismatches) { + if (next !== mismatch.start) { + chunks.push(new Chunk(download, next, mismatch.start - 1, mismatch.start - next)); + } + chunks.push(new Chunk(download, mismatch.start, mismatch.end)); + next = mismatch.end + 1; + } + if (next !== download.totalSize) { + log(LOG_DEBUG, "Inserting last"); + chunks.push(new Chunk(download, next, download.totalSize - 1, download.totalSize - next)); + } + download.chunks = chunks; + download.refreshPartialSize(); + download.queue(); + } + + filterInSitu(mismatches, e => e.start !== e.end); + + if (mismatches.length && (yield OS.File.exists(this.tmpFile.path))) { + // partials + let act = Prompts.confirm( + window, + _('verifyerror.title'), + _('verifyerror.partialstext'), + _('recover'), + _('delete'), + _('keep')); + switch (act) { + case 0: + yield deleteFile(); + recoverPartials(this, mismatches); + return false; + case 1: + yield deleteFile(); + this.cancel(); + return false; + } + return true; + } let act = Prompts.confirm( window, _('verifyerror.title'), - _('verifyerror.partialstext'), - _('recover'), + _('verifyerror.text'), + _('retry'), _('delete'), _('keep')); switch (act) { case 0: - await deleteFile(file); - recoverPartials(this, mismatches); + yield deleteFile(); + this.safeRetry(); return false; case 1: - await deleteFile(file); + yield deleteFile(); this.cancel(); return false; } return true; - } - let act = Prompts.confirm( - window, - _('verifyerror.title'), - _('verifyerror.text'), - _('retry'), - _('delete'), - _('keep')); - switch (act) { - case 0: - await deleteFile(); - this.safeRetry(); - return false; - case 1: - await deleteFile(); - this.cancel(); - return false; - } - return true; - } - - customFinishEvent() { + }).bind(this)); + }, + customFinishEvent: function() { new CustomAction(this, Prefs.finishEvent); - } - - async setAttributes() { + }, + setAttributes: function*() { if (Prefs.setTime) { // XXX: async API try { @@ -2229,107 +2178,107 @@ var QueueItem = class QueueItem { file = this.destinationLocalFile; } try { - this.totalSize = this.partialSize = (await OS.File.stat(file.path)).size; + this.totalSize = this.partialSize = (yield OS.File.stat(file.path)).size; } catch (ex) { log(LOG_ERROR, "failed to get filesize for " + file.path, ex); this.totalSize = this.partialSize = 0; } return true; - } - - async closeChunks() { + }, + closeChunks: function*() { if (!this.chunks) { return; } - for (let i = 0; i < this.chunks.length; ++i) { - let c = this.chunks[i]; - await c.close(); - this.chunks[i] = c.clone(); + for (let c of this.chunks) { + yield c.close(); } - } - - critical() { + }, + _criticals: 0, + get isCritical() { + return this._criticals !== 0; + }, + critical: function() { this._criticals++; - } - - uncritical() { + }, + uncritical: function() { this._criticals = Math.max(0, this._criticals - 1); - } - - finishDownload(exception) { + }, + finishDownload: function(exception) { if (this._finishDownloadTask) { - return this._finishDownloadTask; + return; } log(LOG_DEBUG, "finishDownload, connections: " + this.sessionConnections); // Last speed update this.refreshPartialSize(); - this.dialog._sum += this.speeds.add(this.partialSize + this.otherBytes, Utils.getTimestamp()); + Dialog._sum += this.speeds.add(this.partialSize + this.otherBytes, Utils.getTimestamp()); if (!this.partialSize) { log(LOG_ERROR, "INVALID SIZE!!!!!"); this.fail(_("accesserror"), _("accesserror.long"), _("accesserror")); return; } - return this._finishDownloadTask = this._runFinishDownloadTask(); - } - - async _runFinishDownloadTask() { - try { - this.setState(FINISHING); - this.status = TextCache_FINISHING; - await this.closeChunks(); - if (this.hashCollection && !(await this.verifyHash())) { - return; - } - if ("isMetalink" in this) { - this.handleMetalink(); - return; - } + this._finishDownloadTask = Task.spawn(function* finishDownloadTask() { try { - if (!(await this.moveCompleted())) { + this.setState(FINISHING); + yield this.closeChunks(); + if (this.hashCollection && !(yield this.verifyHash())) { + return; + } + if ("isMetalink" in this) { + this.handleMetalink(); + return; + } + if (!(yield this.moveCompleted())) { log(LOG_DEBUG, "moveCompleted scheduled!"); return; } - } - catch (iex) { - log(LOG_ERROR, "move failed", iex); - this.fail( - _("moveerror"), - _("moveerror.long"), - _("moveerror.status", iex.message || iex) - ); - return; - } - - await this.setAttributes(); - if (Prefs.finishEvent) { - this.customFinishEvent(); - } - this.chunks.length = 0; - this.speeds.clear(); - this.activeChunks = 0; - this.setState(COMPLETE); - this.status = TextCache_COMPLETE; - this.visitors = new VisitorManager(); - this.compression = null; - } - catch (ex) { - log(LOG_ERROR, "complete: ", ex); - this.fail(_("accesserror"), _("accesserror.long"), _("accesserror")); - } - finally { - delete this._finishDownloadTask; - } - } - - rebuildDestination() { + yield this.setAttributes(); + if (Prefs.finishEvent) { + this.customFinishEvent(); + } + this.chunks.length = 0; + this.speeds.clear(); + this.activeChunks = 0; + this.setState(COMPLETE); + this.status = TextCache_COMPLETE; + this.visitors = new VisitorManager(); + this.compression = null; + } + catch (ex) { + log(LOG_ERROR, "complete: ", ex); + this.fail(_("accesserror"), _("accesserror.long"), _("accesserror")); + } + finally { + delete this._finishDownloadTask; + } + }.bind(this)); + }, + get maskURL() { + return this.urlManager.usableURL; + }, + get maskCURL() { + return Utils.getCURL(this.maskURL); + }, + get maskURLPath() { + return this.urlManager.usableURLPath; + }, + get maskReferrerURL() { + return this.referrerUrlManager.usableURL; + }, + get maskReferrerURLPath() { + return this.referrerUrlManager.usableURLPath; + }, + get maskReferrerCURL() { + return Utils.getCURL(this.maskReferrerURL); + }, + rebuildDestination: function() { try { let mask = Utils.removeFinalSlash(Utils.normalizeSlashes(Utils.removeFinalChar( this.rebuildDestination_renamer(this.mask), "." ))); - let file = new Instances.LocalFile(this.pathName); + let file = new Instances.LocalFile(Utils.addFinalSlash(this.pathName)); if (!~mask.indexOf(Utils.SYSTEMSLASH)) { file.append(Utils.removeBadChars(mask).trim()); } @@ -2365,21 +2314,17 @@ var QueueItem = class QueueItem { } finally { this._icon = null; - let dummy = this.iconProp; // set up initial icon to avoid display problems + this.iconProp; // set up initial icon to avoid display problems FileExts.add(); } - } - - checkConflicts() { + }, + checkConflicts: function() { return ConflictManager.check(this); - } - - resolveConflicts() { + }, + resolveConflicts: function() { return ConflictManager.resolve(this); - - } - - fail(title, msg, state) { + }, + fail: function(title, msg, state) { log(LOG_INFO, "failDownload invoked"); this.cancel(state); @@ -2396,9 +2341,9 @@ var QueueItem = class QueueItem { window.alert(msg); break; } - } + }, - cancel(message) { + cancel: function(message) { try { const state = this.state; if (state === RUNNING) { @@ -2414,66 +2359,65 @@ var QueueItem = class QueueItem { this.activeChunks = 0; } this.setState(CANCELED); - return this._cancelClose(message); - } - catch(ex) { - log(LOG_ERROR, "cancel():", ex); - } - } - - async _cancelClose(message) { - try { - await this.closeChunks(); - if (this._preallocTask) { - await this._preallocTask; - } - log(LOG_INFO, this.fileName + ": canceled"); + return Task.spawn(function*() { + try { + yield this.closeChunks(); + if (this._preallocTask) { + yield this._preallocTask; + } + log(LOG_INFO, this.fileName + ": canceled"); - this.shutdown(); - await this.removeTmpFile(); + this.shutdown(); + this.removeTmpFile(); - // gc - if (this.deleting) { - return; - } - if (!message) { - message = _("canceled"); - } + // gc + if (this.deleting) { + return; + } + if (!message) { + message = _("canceled"); + } - this.status = message; - this.visitors = new VisitorManager(); - this.chunks.length = 0; - this.progress = this.totalSize = this.partialSize = 0; - this.conflicts = 0; - this.resumable = true; - this._maxChunks = this._activeChunks = 0; - this._autoRetries = 0; - delete this._autoRetryTime; - this.speeds.clear(); - this.otherBytes = 0; - this.save(); + this.status = message; + this.visitors = new VisitorManager(); + this.chunks.length = 0; + this.progress = this.totalSize = this.partialSize = 0; + this.conflicts = 0; + this.resumable = true; + this._maxChunks = this._activeChunks = 0; + this._autoRetries = 0; + delete this._autoRetryTime; + this.speeds.clear(); + this.otherBytes = 0; + this.save(); + } + catch (ex) { + log(LOG_ERROR, "cancel() Task", ex); + } + }.bind(this)); } - catch (ex) { - log(LOG_ERROR, "cancel() Task", ex); + catch(ex) { + log(LOG_ERROR, "cancel():", ex); } - } - - async cleanup() { - if (this.chunks) { - await this.closeChunks(); - } - delete this.visitors; - delete this.chunks; - delete this.speeds; - delete this.urlManager; - delete this.referrer; - delete this._referrerUrlManager; - delete this._destinationLocalFile; - delete this._tmpFile; - delete this.rebuildDestination_renamer; - } + }, - prealloc() { + cleanup: function() { + Task.spawn(function*() { + if (this.chunks) { + yield this.closeChunks(); + } + delete this.visitors; + delete this.chunks; + delete this.speeds; + delete this.urlManager; + delete this.referrer; + delete this._referrerUrlManager; + delete this._destinationLocalFile; + delete this._tmpFile; + delete this.rebuildDestination_renamer; + }.bind(this)); + }, + prealloc: function() { let file = this.tmpFile; if (this.state !== RUNNING) { @@ -2489,82 +2433,86 @@ var QueueItem = class QueueItem { return; } - this._preallocTask = this._preallocInternal(file); - } - - async _preallocInternal(file) { - try { - try { - await Utils.makeDir(file.parent, Prefs.dirPermissions); - } - catch (ex if ex.becauseExists) { - // no op - } + this._preallocTask = Task.spawn(function*() { try { - if (this.totalSize === (await OS.File.stat(file.path)).size) { - log(LOG_INFO, "pa: already allocated"); - return; + try { + yield Utils.makeDir(file.parent, Prefs.dirPermissions); + } + catch (ex if ex.becauseExists) { + // no op + } + try { + if (this.totalSize === (yield OS.File.stat(file.path)).size) { + log(LOG_INFO, "pa: already allocated"); + return; + } + } + catch (ex if ex.becauseNoSuchFile) { + // no op + } + let pa = Preallocator.prealloc( + file, + this.totalSize, + Prefs.permissions, + Prefs.sparseFiles + ); + if (pa) { + yield pa; + log(LOG_INFO, "pa: done"); + } + else { + log(LOG_INFO, "pa: not preallocating"); } } - catch (ex if ex.becauseNoSuchFile) { - // no op - } - let pa = Preallocator.prealloc( - file, - this.totalSize, - Prefs.permissions, - Prefs.sparseFiles - ); - if (pa) { - await pa; - log(LOG_INFO, "pa: done"); + catch(ex) { + log(LOG_ERROR, "pa: failed", ex); } - else { - log(LOG_INFO, "pa: not preallocating"); + finally { + this._preallocTask = null; + this.maybeResumeDownload(); } - } - catch(ex) { - log(LOG_ERROR, "pa: failed", ex); - } - finally { - this._preallocTask = null; - this.maybeResumeDownload(); - } - } + }.bind(this)); + }, - shutdown() { } + shutdown: function() { + }, - async removeTmpFile() { + removeTmpFile: function() { let tmpFile = this._tmpFile; delete this._tmpFile; if (!tmpFile) { return; } - try { - await OS.File.remove(tmpFile.path); - } - catch (ex if ex.becauseNoSuchFile) { - // no op - } - catch (ex) { + Task.spawn(function*() { + try { + yield OS.File.remove(tmpFile.path); + } catch (ex if ex.becauseNoSuchFile) { + // no op + } + }).then(null, function(ex) { log(LOG_ERROR, "failed to remove tmpfile: " + tmpFile.path, ex); - } - } + }); + }, - pauseAndRetry() { + sessionConnections: 0, + _autoRetries: 0, + _autoRetryTime: 0, + get autoRetrying() { + return !!this._autoRetryTime; + }, + pauseAndRetry: function() { let retry = this.state === RUNNING; this.pause(); this.resumable = true; if (retry && Prefs.autoRetryInterval && !(Prefs.maxAutoRetries && Prefs.maxAutoRetries <= this._autoRetries)) { - this.dialog.markAutoRetry(this); + Dialog.markAutoRetry(this); this._autoRetryTime = Utils.getTimestamp(); log(LOG_INFO, "marked auto-retry: " + this); } this.save(); - } - - autoRetry() { + }, + autoRetry: function() { if (!this.autoRetrying || Utils.getTimestamp() - (Prefs.autoRetryInterval * 1000) < this._autoRetryTime) { return false; } @@ -2574,40 +2522,55 @@ var QueueItem = class QueueItem { this.queue(); log(LOG_DEBUG, "Requeued due to auto-retry: " + this); return true; - } - clearAutoRetry() { + }, + clearAutoRetry: function() { this._autoRetryTime = 0; this._autoRetries = 0; - } - - queue() { + }, + queue: function() { this._autoRetryTime = 0; this.setState(QUEUED); this.status = TextCache_QUEUED; - } - - maybeResumeDownload() { + }, + maybeResumeDownload: function() { if (this.state !== RUNNING) { return; } this.resumeDownload(); - } - - resumeDownload() { + }, + resumeDownload: function() { log(LOG_DEBUG, "resumeDownload: " + this); - - // merge finished chunks together, so that the scoreboard does not bloat - // that much - for (let i = this.chunks.length - 2; i > -1; --i) { - let c1 = this.chunks[i], c2 = this.chunks[i + 1]; - if (c1.complete && c2.complete && !c1.buffered && !c2.buffered) { - c1.merge(c2); - this.chunks.splice(i + 1, 1); + function cleanChunks(d) { + // merge finished chunks together, so that the scoreboard does not bloat + // that much + for (let i = d.chunks.length - 2; i > -1; --i) { + let c1 = d.chunks[i], c2 = d.chunks[i + 1]; + if (c1.complete && c2.complete && !c1.buffered && !c2.buffered) { + c1.merge(c2); + d.chunks.splice(i + 1, 1); + } } } + function downloadNewChunk(download, start, end, header) { + let chunk = new Chunk(download, start, end); + download.chunks.push(chunk); + download.chunks.sort(function(a,b) { return a.start - b.start; }); + downloadChunk(download, chunk, header); + } + function downloadChunk(download, chunk, header) { + chunk.download = new Connection(download, chunk, header || download.mustGetInfo); + chunk.running = true; + download.mustGetInfo = false; + download.setState(RUNNING); + log(LOG_DEBUG, "started: " + chunk); + ++download.activeChunks; + ++download.sessionConnections; + } + + cleanChunks(this); try { - if (this.dialog.offline || this.maxChunks <= this.activeChunks) { + if (Dialog.offline || this.maxChunks <= this.activeChunks) { return false; } @@ -2634,7 +2597,7 @@ var QueueItem = class QueueItem { // restart paused chunks if (paused.length) { let p = paused.shift(); - downloadOldChunk(this, p, p.end === 0); + downloadChunk(this, p, p.end === 0); rv = true; continue; } @@ -2670,21 +2633,15 @@ var QueueItem = class QueueItem { downloadNewChunk(this, biggest.end + 1, end); rv = true; } - if (this.activeChunks < 1 && - this.chunks.some(chunk => !(chunk.running || chunk.complete))) { - throw new Error("Nothing started but no actives, yet paused"); - } return rv; } catch(ex) { - this.dumpScoreboard(); - log(LOG_ERROR, "resumeDownload():", ex, true); + log(LOG_ERROR, "resumeDownload():", ex); } return false; - } - - replaceMirrors(mirrors) { + }, + replaceMirrors: function(mirrors) { let restart = this.urlManager.length < 3; this.urlManager.initByArray(mirrors); if (restart && this.resumable && this.state === RUNNING && this.maxChunks > 2) { @@ -2696,9 +2653,8 @@ var QueueItem = class QueueItem { } this.invalidate(); this.save(); - } - - dumpScoreboard() { + }, + dumpScoreboard: function() { if (!log.enabled) { return; } @@ -2708,13 +2664,11 @@ var QueueItem = class QueueItem { scoreboard += i + ": " + c + "\n"; } log(LOG_DEBUG, "scoreboard\n" + scoreboard); - } - - toString() { + }, + toString: function() { return this.urlManager.usable; - } - - toJSON() { + }, + toJSON: function() { let rv = Object.create(null); let p = Object.getPrototypeOf(this); for (let u of Dialog_serialize_props) { @@ -2764,64 +2718,205 @@ var QueueItem = class QueueItem { return rv; } }; -Object.assign(QueueItem.prototype, { - state: QUEUED, - position: -1, - _contentType: "", - _description: null, - _hashCollection: null, - _mask: null, - _pathName: null, - _prettyHash: null, - _status : '', - _title: '', - bNum: 0, - compression: null, - fromMetalink: false, - iNum: 0, - postData: null, - visitors: null, +XPCOMUtils.defineLazyGetter(QueueItem.prototype, 'AuthPrompts', function() { + const {LoggedPrompter} = require("support/loggedprompter"); + return new LoggedPrompter(window); +}); - _destinationFile: null, - _destinationLocalFile: null, - _destinationName: null, - _destinationNameFull: null, - _destinationPath: '', - _fileName: null, - _tmpFile: null, - destinationNameOverride: null, - fileNameFromUser: false, +var ConflictManager = { + _items: new Map(), + _queue: [], + _pinned: new Map(), + resolve: function(download) { + return this._resolve(download, true); + }, + check: function(download) { + return this._resolve(download, false); + }, + _resolve: function(download, pinned) { + log(LOG_DEBUG, "ConflictManager: Resolving " + download); + let data = this._items.get(download); + if (data) { + // Make sure pinning request is carried over + data.pinned |= pinned; + log(LOG_DEBUG, "ConflictManager: Resolving already " + data); + return data.promise; + } + data = {pinned: pinned}; + data.promise = new Promise(function(resolve, reject) { + data.reject = reject; + data.resolve = resolve; + }); + this._items.set(download,data); + this._queue.push(download); + log(LOG_DEBUG, "ConflictManager: Resolving new " + data); + this._processNext(); + return data.promise; + }, + pin: function(name, unique) { + let count = (this._pinned.get(name) || 0) + 1; + if (unique && count > 1) { + throw new Error("Invalid pin; not unique"); + } + this._pinned.set(name, count); + }, + unpin: function(name) { + let count = this._pinned.get(name); + if (!isFinite(count)) { + log(LOG_ERROR, "ConflictManager: trying to unpin a name that does not exist"); + this._pinned.delete(name); + return; + } + if (--count <= 0) { + this._pinned.delete(name); + return; + } + this._pinned.set(name, count); // store new count + }, + _processNext: function() { + log(LOG_DEBUG, "ConflictManager: Resolving next"); + if (this._processing) { + log(LOG_DEBUG, "ConflictManager: Resolving rescheduling"); + return; + } + let download = this._queue.shift(); + if (!download) { + return; + } + let data = this._items.get(download); + this._items.delete(download); - _totalSize: 0, - otherBytes: 0, - partialSize: 0, - progress: 0, - relaxSize: false, + this._processing = true; + Task.spawn(function*() { + try { + data.resolve(yield this._processOne(download, data)); + } + catch (ex) { + log(LOG_ERROR, "ConflictManager: Failed to resolve", ex); + data.reject(null); + } + finally { + this._processing = false; + setTimeoutOnlyFun(this._processNext.bind(this), 0); + } + }.bind(this)); + }, + _findUnique: function*(newDest, basename, conflicts) { + for (;; ++conflicts) { + newDest.leafName = Utils.formatConflictName(basename, conflicts); + let exists = this._pinned.has(newDest.path); + if (!exists) { + exists = yield OS.File.exists(newDest.path); + // recheck + exists = exists || this._pinned.has(newDest.path); + } + if (!exists) { + return conflicts; + } + } + }, + _processOne: function*(download, data) { + log(LOG_DEBUG, "ConflictManager: Starting conflict resolution for " + download); + let dest = download.destinationLocalFile; + let exists = this._pinned.has(dest.path); + if (!exists) { + exists = yield OS.File.exists(dest.path); + // recheck + exists = exists || this._pinned.has(dest.path); + } + if (!exists) { + log(LOG_DEBUG, "ConflictManager: Does not exist " + download); + if (data.pinned) { + this.pin(dest.path, true); + } + return dest.path; + } - _activeChunks: 0, - _maxChunks: 0, - timeLastProgress: 0, - timeStart: 0, + let cr = -1; - _bucket: null, - _icon: null, + let conflicts = 0; + const basename = download.destinationName; + let newDest = download.destinationLocalFile.clone(); - _autoRetries: 0, - _autoRetryTime: 0, - _conflicts: 0, - _criticals: 0, - _speedLimit: -1, - mustGetInfo: false, - resumable: true, - sessionConnections: 0, - started: false, -}); + if (Prefs.conflictResolution !== 3) { + cr = Prefs.conflictResolution; + } + else if (download.shouldOverwrite) { + cr = 1; + } + else if ('_sessionSetting' in this) { + cr = this._sessionSetting; + } + else if ('_conflictSetting' in download) { + cr = download._conflictSetting; + } -XPCOMUtils.defineLazyGetter(QueueItem.prototype, 'AuthPrompts', function() { - const {LoggedPrompter} = require("support/loggedprompter"); - return new LoggedPrompter(window); -}); + if (cr < 0) { + let dialog = {}; + dialog.promise = new Promise(function(resolve, reject) { + dialog.resolve = resolve; + dialog.reject = reject; + }); + conflicts = yield this._findUnique(newDest, basename, conflicts); + let options = { + url: Utils.cropCenter(download.urlManager.usable, 45), + fn: Utils.cropCenter(download.destinationLocalFile.leafName, 45), + newDest: Utils.cropCenter(newDest.leafName, 45) + }; + window.openDialog( + "chrome://dta/content/dta/manager/conflicts.xul", + "_blank", + "chrome,centerscreen,resizable=no,dialog,close=no,dependent", + options, dialog + ); + let ctype = 0; + [cr, ctype] = yield dialog.promise; + + if (ctype === 1) { + this._sessionSetting = cr; + } + else if (ctype === 2) { + Preferences.setExt('conflictresolution', cr); + } + else { + download._conflictSetting = cr; + } + } + + switch (cr) { + case 0: { + if (!data.pinned) { + // No need to actually check here... + // Check will be performed once we pin + return; + } + conflicts = yield this._findUnique(newDest, basename, conflicts); + let pinned = null; + if (data.pinned) { + download.conflicts = conflicts; + pinned = download.destinationFile; + download.shouldOverwrite = false; + this.pin(pinned, true); + } + log(LOG_DEBUG, "ConflictManager: resolved setting conflicts for " + download); + return pinned; + } + case 1: { + let pinned = null; + if (data.pinned) { + pinned = download.destinationFile; + download.shouldOverwrite = true; + this.pin(pinned, false); + } + return pinned; + } + default: + download.cancel(_('skipped')); + return false; + } + } +}; function CustomAction(download, command) { try { @@ -2887,7 +2982,7 @@ var startDownloads = (function() { let addItem = function(e) { try { - let qi = new QueueItem(Dialog); + let qi = new QueueItem(); let lnk = e.url; if (typeof lnk === 'string') { qi.urlManager = new UrlManager([new DTA.URL(Services.io.newURI(lnk, null, null))]); diff --git a/chrome/content/dta/manager/manager.xul b/chrome/content/dta/manager/manager.xul index d0fe2bdaf..5a636c58e 100644 --- a/chrome/content/dta/manager/manager.xul +++ b/chrome/content/dta/manager/manager.xul @@ -54,9 +54,9 @@ - - - + + + @@ -293,7 +293,7 @@ - + diff --git a/chrome/content/dta/manager/tree.js b/chrome/content/dta/manager/tree.js index c16794d0e..dbce0c224 100644 --- a/chrome/content/dta/manager/tree.js +++ b/chrome/content/dta/manager/tree.js @@ -2,64 +2,64 @@ * 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"; -/* global $, $e, $$, _, Utils, FilterManager, getIcon, Preferences, OS */ +/* global $, $e, $$, _, Utils, Timers, FilterManager, getIcon, Preferences, Task, OS */ /* global mapInSitu, filterInSitu, mapFilterInSitu, filterMapInSitu */ -/* global DTA, Dialog, QueueItem, Prefs, QueueStore, Prompts, ImportExport, Metalinker */ +/* global DTA, Dialog, QueueItem, Prefs, QueueStore, Prompts, ImportExport, Metalinker */ /* global asyncMoveFile, showPreferences, Tooltip, CoThreadListWalker */ /* global COMPLETE, CANCELED, RUNNING, PAUSED, QUEUED, FINISHING */ /* global TextCache_PAUSED */ -/* global FileExts, setTimeoutOnlyFun */ +/* global FileExts */ /* jshint strict:true, globalstrict:true, browser:true, latedef:false */ XPCOMUtils.defineLazyGetter(window, "ImportExport", () => require("manager/imex")); -class FileDataProvider { - constructor(tree, download, file) { - this._tree = tree; - this._download = download; - this._file = file; - this._checks = 0; - this.QueryInterface = QI([Ci.nsIFlavorDataProvider]); - } +function FileDataProvider(download, file) { + this._download = download; + this._file = file; + this._checkFile = this._checkFile.bind(this); +}; +FileDataProvider.prototype = { + _checks: 0, + QueryInterface: QI([Ci.nsIFlavorDataProvider]), get file() { if (this._timer) { - clearTimeout(this._timer); + Timers.killTimer(this._timer); delete this._timer; } this._checks = 0; - this._timer = setTimeoutOnlyFun(() => this.checkFile(), 500); + this._timer = Timers.createOneshot(500, this.checkFile.bind(this)); return this._file; - } - async checkFile() { + }, + checkFile: function() { + Task.spawn(this._checkFile); + }, + _checkFile: function*() { delete this._timer; - let exists = await OS.File.exists(this._file.path); + let exists = yield OS.File.exists(this._file.path); if (!exists) { - this._tree.remove(this._download); + Tree.remove(this._download); return; } if (++this._checks < 10) { - this._timer = setTimeoutOnlyFun(() => this.checkFile(), 5000); + this._timer = Timers.createOneshot(5000, this.checkFile.bind(this)); } - } - getFlavorData(dataTransfer, flavor, data, dataLen) { + }, + getFlavorData: function(dataTransfer, flavor, data, dataLen) { data.value = this.file; dataLen.value = 1; } -} +}; -class TreeManager { - constructor(elem) { +var Tree = { + init: function(elem) { this.elem = elem; this._downloads = []; - this._updating = 0; - this._filter = ''; - this._mustFilter = false; this._filtered = this._downloads; this._speedLimitList = $('perDownloadSpeedLimitList'); this._matcher = new this.Matcher(); - addEventListener('blur', () => this.stopTip(), false); + addEventListener('blur', () => Tree.stopTip(), false); this.elem.addEventListener('select', () => this.selectionChanged(), false); this.elem.addEventListener('click', evt => { @@ -70,10 +70,10 @@ class TreeManager { let dtree = $('downloadList'); dtree.addEventListener('dragstart', event => this.onDragStart(event), false); - dtree.addEventListener('dblclick', (event) => { + dtree.addEventListener('dblclick', function(event) { event.preventDefault(); event.stopPropagation(); - this.openFile(); + FileHandling.openFile(); }, false); $("matcher").addEventListener("command", event => this.handleMatcherPopup(event), true); @@ -97,53 +97,12 @@ class TreeManager { this.assembleMenus(); this._refreshTools_init(); this.refreshTools(); - } - - get downloadCount() { - return this._downloads.length; - } - get rowCount() { - return this._filtered.length; - } - get filtered() { - return this._matcher.filtering; - } - get box() { - return this._box; - } - get all() { - return this._downloads; - } - // get the first selected item, NOT the item which has the input focus. - get current() { - let select = this.selection; - try { - let ci = {value: -1}; - this.selection.getRangeAt(0, ci, {}); - if (ci.value > -1 && ci.value < this.rowCount) { - return this._filtered[ci.value]; - } - } - catch (ex) { - // fall-through - } - return null; - } - // get the currently focused item. - get focused() { - let ci = this.selection.currentIndex; - if (ci > -1 && ci < this.rowCount) { - return this._filtered[ci]; - } - return null; - } - - unlink() { + }, + unlink: function() { this.elem.view = null; delete this.elem; - } - - assembleMenus() { + }, + assembleMenus: function() { // jshint globalstrict:true, strict:true, loopfunc:true for (let popup of $('removeCompletedPopup', 'removePopup')) { while (popup.lastChild) { @@ -168,13 +127,12 @@ class TreeManager { else { mi.setAttribute('class', 'menuitem-iconic menuitem-filter'); } - mi.addEventListener('command', () => this.removeByFilter(filter, id), true); + mi.addEventListener('command', () => Tree.removeByFilter(filter, id), true); popup.appendChild(mi); } } - } - - handleMatcherPopupshowing(col) { + }, + handleMatcherPopupshowing: function(col) { let processor = col.getAttribute('matcher'); if (!processor) { return; @@ -218,9 +176,8 @@ class TreeManager { } popup.col = col; popup.openPopup(col, "after_start", -1, -1, true, false, null); - } - - handleMatcherPopup(event) { + }, + handleMatcherPopup: function(event) { let target = event.target; let popup = target.parentNode; let element = popup.col; @@ -329,9 +286,8 @@ class TreeManager { } return; } - } - - clear() { + }, + clear: function() { log(LOG_INFO, "Tree: clearing"); this.beginUpdate(); delete this._downloads; @@ -341,9 +297,15 @@ class TreeManager { $('search').clear(); this.elem.view = this; this.endUpdate(); - } + }, - setTree(box) { + get downloadCount() { + return this._downloads.length; + }, + get rowCount() { + return this._filtered.length; + }, + setTree: function(box) { if (!box) { return; } @@ -352,9 +314,8 @@ class TreeManager { for (let i = 0; i < box.columns.count; ++i) { this._cols.push(box.columns.getColumnAt(i)); } - } - - sort(id, descending) { + }, + sort: function(id, descending) { if (Prompts.confirm( window, _('sortqueue.title'), @@ -408,9 +369,13 @@ class TreeManager { this.invalidate(); this.endUpdate(); } - } - - doFilter() { + }, + _filter: '', + _mustFilter: false, + get filtered() { + return this._matcher.filtering; + }, + doFilter: function() { if (this._updating) { this._mustFilter = true; return; @@ -454,9 +419,8 @@ class TreeManager { finally { this.endUpdate(); } - } - - doFilterOne(d) { + }, + doFilterOne: function(d) { const display = !this.filtered || this._matcher.shouldDisplay(d); if (display === !!~d.filteredPosition) { return false; @@ -502,9 +466,8 @@ class TreeManager { this.doFilter(); } return true; - } - - setFilter(nv) { + }, + setFilter: function(nv) { if (nv === this._filter) { return; } @@ -517,19 +480,16 @@ class TreeManager { } // apply filters this.doFilter(); - } - - getParentIndex(idx) { + }, + getParentIndex: function(idx) { // no parents, as we are actually a list return -1; - } - - getLevel(idx) { + }, + getLevel: function(idx) { // ... and being a list all nodes are on the same level return 0; - } - - getCellText(idx, col) { + }, + getCellText: function(idx, col) { const d = this._filtered[idx]; if (!d) { return ''; @@ -548,9 +508,8 @@ class TreeManager { case 10: return d.prettyHash; } return ''; - } - - setCellText(idx, col, text) { + }, + setCellText: function(idx, col, text) { text = Utils.getUsableFileName(text); if (col.index || !text) { return; @@ -568,39 +527,39 @@ class TreeManager { log(LOG_DEBUG, "reset"); return; // complete logic will do this } + Task.spawn(this._setCellText.bind(this, d, from, to)).then(null, function(ex) { + log(LOG_DEBUG, "move failed " + from.path + " to " + to.path, ex); + Prompts.alert(window, _("rename.title"), _("rename.failedtomove", [from.path, to.path])); + }); + }, + _setCellText: function*(d, from, to) { + if (!(yield OS.File.exists(from.path))) { + d.setUserFileName(to.leafName); + log(LOG_DEBUG, "gone"); + return; // gone already + } + if ((yield OS.File.exists(to.path))) { + Prompts.alert(window, _("rename.title"), _("rename.alreadythere", [from.leafName, to.path])); + log(LOG_DEBUG, "exists"); + return; + } - this._moveToNewLocation(d, from, to); - } - - isSorted() { - return true; - } - - isContainer(idx) { - return false; - } - - isContainerOpen(idx) { - return false; - } - - isContainerEmpty(idx) { - return false; - } - - isSeparator(idx) { - return false; - } - - isEditable(row, col) { - return !col.index; - } - + log(LOG_DEBUG, "move " + from.path + " to " + to.path); + // need to move + yield OS.File.move(from.path, to.path); + log(LOG_DEBUG, "move complete " + from.path + " to " + to.path); + d.setUserFileName(to.leafName); + }, + isSorted: function() { return true; }, + isContainer: function(idx) { return false; }, + isContainerOpen: function(idx) { return false; }, + isContainerEmpty: function(idx) { return false; }, + isSeparator: function(idx) { return false; }, + isEditable: function(row, col) { return !col.index; }, // will grab the "icon" for a cell. - getImageSrc(idx, col) {} - - getProgressMode(idx, col) { + getImageSrc: function(idx, col) {}, + getProgressMode : function(idx, col) { if (col.index === 2) { const d = this._filtered[idx]; if (!d) { @@ -616,10 +575,9 @@ class TreeManager { return 1; // PROGRESS_NORMAL; } return 3; // PROGRESS_NONE; - } - + }, // will be called for cells other than textcells - getCellValue(idx, col) { + getCellValue: function(idx, col) { if (col.index === 2) { const d = this._filtered[idx]; if (!d) { @@ -631,9 +589,18 @@ class TreeManager { return d.progress || 0; } return null; - } - - getCellProperties(idx, col) { + }, + _cpprop_iconic: "iconic progress", + _cpprop_iconiccomplete: "iconic progress completed", + _cpprop_iconicfinishing: "iconic progress finishing", + _cpprop_iconicverified: "iconic progress completed verified", + _cpprop_iconicpaused: "iconic progress paused", + _cpprop_iconicpausedundetermined: "iconic progress paused pausedUndetermined", + _cpprop_iconicpausedretrying: "iconic progress paused pausedAutoretrying", + _cpprop_iconicpausedundeterminedretrying: "iconic progress paused pausedUndetermined pausedAutoretrying", + _cpprop_iconicinprogress: "iconic progress inprogress", + _cpprop_iconicicanceled: "iconic progress canceled", + getCellProperties: function(idx, col) { const cidx = col.index; if (cidx !== 2 && cidx !== 0) { return ""; @@ -678,48 +645,37 @@ class TreeManager { return d.iconProp; } return ""; - } - - cycleHeader(col) { + }, + cycleHeader: function(col) { if (!col.element.hasAttribute("matcher")) { return; } this.handleMatcherPopupshowing(col.element); - } - + }, // just some stubs we need to provide anyway to implement a full nsITreeView - cycleCell(idx, column) {} - - performAction(action) {} - - performActionOnRow(action, index, column) {} - - performActionOnCell(action, index, column) {} - - getColumnProperties(column, element) { - return ""; - } - - getRowProperties(idx) { - return ""; - } - - setCellValue(idx, col, value) {} - - selectionChanged() { + cycleCell: function(idx, column) {}, + performAction: function(action) {}, + performActionOnRow: function(action, index, column) {}, + performActionOnCell: function(action, index, column) {}, + getColumnProperties: function(column, element) { return ""; }, + getRowProperties: function(idx) { return ""; }, + setCellValue: function(idx, col, value) {}, + + _changeTimer: null, + selectionChanged: function() { if (this._updating) { return; } if (this._changeTimer) { - clearTimeout(this._changeTimer); + Timers.killTimer(this._changeTimer); } - this._changeTimer = setTimeoutOnlyFun(() => { + this._changeTimer = Timers.createOneshot(100, function() { this._changeTimer = null; this.refreshTools(); - }, 100); - } + }, this); + }, - onDragStart(event) { + onDragStart: function(event) { let transfer = event.dataTransfer; let i = 0; transfer.effectAllowed = "copymove"; @@ -728,31 +684,25 @@ class TreeManager { if (qi.state === COMPLETE) { let file = qi.destinationLocalFile; if (file.exists()) { - transfer.mozSetDataAt( - "application/x-moz-file", - new FileDataProvider(this, qi, file), - i++); + transfer.mozSetDataAt("application/x-moz-file", new FileDataProvider(qi, file), i++); } } - transfer.setData("application/x-dta-position", qi.position); - i++; + transfer.setData("application/x-dta-position", qi.position); i++; } catch (ex) { log(LOG_ERROR, "dnd failure", ex); } return; } - } - - canDrop(index, orient, dt) { + }, + canDrop: function(index, orient, dt) { let rv = dt.types.contains("application/x-dta-position"); if (rv) { dt.dropEffect = "move"; } return rv; - } - - drop(row, orient, dt) { + }, + drop: function(row, orient, dt) { log(LOG_DEBUG, "drop"); if (!this.canDrop(row, orient, dt)) { return; @@ -802,15 +752,15 @@ class TreeManager { catch (ex) { log(LOG_ERROR, "_dropSelection", ex); } - } + }, - beginUpdate() { + _updating: 0, + beginUpdate: function() { if (++this._updating === 1) { this._box.beginUpdateBatch(); } - } - - endUpdate() { + }, + endUpdate: function() { if (--this._updating === 0) { this._box.endUpdateBatch(); this.refreshTools(); @@ -823,17 +773,15 @@ class TreeManager { this.fireChangeEvent(); } } - } - - fastLoad(download) { + }, + fastLoad: function(download) { if (download.state === COMPLETE) { ++Dialog.completed; } let dummy = download.iconProp; // set up initial icon to avoid display problems return this._downloads.push(download) - 1; - } - - add(download) { + }, + add: function(download) { let pos = download.position = this.fastLoad(download); if (this.filtered) { download.filteredPosition = -1; @@ -844,9 +792,8 @@ class TreeManager { } this.fireChangeEvent(); return pos; - } - - scrollToNearest(download) { + }, + scrollToNearest: function(download) { if (!download || download.position < 0) { // Cannot scroll to a deleted download return; @@ -868,9 +815,8 @@ class TreeManager { return; } // nothing found; do not scroll - } - - removeWithConfirmation() { + }, + removeWithConfirmation: function() { if (Prefs.confirmRemove) { let res = Prompts.confirm( window, @@ -890,17 +836,15 @@ class TreeManager { } } this.remove(null, true); - } - - removeAllWithConfirmation() { + }, + removeAllWithConfirmation: function() { let res = Prompts.confirm(window, _('remove.title'), _('removeallquestion'), Prompts.YES, Prompts.NO); if (res) { return; } this.remove(this._downloads.map(e => e), true); - } - - removeHostWithConfirmation() { + }, + removeHostWithConfirmation: function() { let domain = this.current.urlManager.domain; let res = Prompts.confirm( window, @@ -912,9 +856,8 @@ class TreeManager { return; } this.remove(this._downloads.filter(e => e.urlManager.domain === domain), true); - } - - removeBatchWithConfirmation() { + }, + removeBatchWithConfirmation: function() { let bid = this.current.bNum; if (Prefs.confirmRemove) { let res = Prompts.confirm( @@ -928,9 +871,8 @@ class TreeManager { } } this.remove(this._downloads.filter(e => e.bNum === bid), true); - } - - removeByFilter(filter, id) { + }, + removeByFilter: function(filter, id) { let pref = null; let mask = -1; let msg = null; @@ -975,11 +917,11 @@ class TreeManager { downloads.push(d); } if (downloads.length) { - this.remove(downloads); + Tree.remove(downloads); } - } - - fireChangeEvent() { + }, + _mustFireChangeEvent: false, + fireChangeEvent: function() { if (this._updating) { this._mustFireChangeEvent = true; return; @@ -987,9 +929,8 @@ class TreeManager { let evt = document.createEvent("UIEvents"); evt.initUIEvent("change", true, true, null, 0); return this.elem.dispatchEvent(evt); - } - - remove(downloads, performJump) { + }, + remove: function(downloads, performJump) { if (downloads && !(downloads instanceof Array)) { downloads = [downloads]; } @@ -1038,13 +979,39 @@ class TreeManager { this.invalidate(); this.doFilter(); this.endUpdate(); + this.fireChangeEvent(); } if (performJump) { this._removeJump(filterInSitu(downloads, e => e.filteredPosition >= 0).length, last); } - } - - removeCompleted() { + }, + _removeByState: function(state, onlyGone) { + this.beginUpdate(); + Task.spawn((function*() { + try { + QueueStore.beginUpdate(); + var removing = []; + for (let d of this._downloads) { + if (d.state !== state) { + continue; + } + if (onlyGone && (yield OS.File.exists(d.destinationLocalFile.path))) { + continue; + } + removing.push(d); + } + if (removing.length) { + this.remove(removing); + } + QueueStore.endUpdate(); + } + finally { + this.invalidate(); + this.endUpdate(); + } + }).bind(this)); + }, + removeCompleted: function() { if (Prefs.confirmRemoveCompleted) { let res = Prompts.confirm( window, @@ -1064,9 +1031,8 @@ class TreeManager { } } this._removeByState(COMPLETE, false); - } - - removeFailed() { + }, + removeFailed: function() { if (Prefs.confirmRemoveFailed) { let res = Prompts.confirm( window, @@ -1086,9 +1052,8 @@ class TreeManager { } } this._removeByState(CANCELED, false); - } - - removePaused() { + }, + removePaused: function() { if (Prefs.confirmRemovePaused) { let res = Prompts.confirm( window, @@ -1108,9 +1073,8 @@ class TreeManager { } } this._removeByState(PAUSED, false); - } - - removeDupes() { + }, + removeDupes: function() { let known = {}; let dupes = []; for (let d of this.all) { @@ -1130,13 +1094,11 @@ class TreeManager { return true; } return false; - } - - removeGone() { + }, + removeGone: function() { this._removeByState(COMPLETE, true); - } - - _removeJump(delta, last) { + }, + _removeJump: function(delta, last) { if (!this.rowCount) { this._box.ensureRowIsVisible(0); } @@ -1147,39 +1109,33 @@ class TreeManager { } this.selection.currentIndex = np; } - } - - _pause_item(d) { - if (d.isOf(QUEUED | PAUSED | CANCELED) || (d.state === RUNNING && d.resumable)) { + }, + _pause_item: function(d) { + if (d.isOf(QUEUED | PAUSED) || (d.state === RUNNING && d.resumable)) { d.pause(); d.clearAutoRetry(); d.status = TextCache_PAUSED; d.setState(PAUSED); } return true; - } - - pause() { + }, + pause: function() { this.updateSelected(this._pause_item); - } - - _resume_item(d) { + }, + _resume_item: function(d) { if (d.isOf(PAUSED | CANCELED)) { d.liftLoginRestriction = true; d.queue(); } return true; - } - - resume(d) { + }, + resume: function(d) { this.updateSelected(this._resume_item); - } - - _cancel_item(d) { + }, + _cancel_item: function(d) { return d.cancel() || true; - } - - cancel() { + }, + cancel: function() { if (Prefs.confirmCancel) { let many = this.selection.count > 1; let res = Prompts.confirm( @@ -1197,48 +1153,41 @@ class TreeManager { } } this.updateSelected(this._cancel_item); - } - - selectAll() { + }, + selectAll: function() { this.selection.selectAll(); this.selectionChanged(); - } - - selectInv() { + }, + selectInv: function() { for (let d of this.all) { this.selection.toggleSelect(d.position); } this.selectionChanged(); - } - - _changeChunks_inc(d) { + }, + _changeChunks_inc: function(d) { if (d.maxChunks < 10 && d.resumable) { ++d.maxChunks; } return true; - } - - _changeChunks_dec(d) { + }, + _changeChunks_dec: function(d) { if (d.maxChunks > 1) { --d.maxChunks; } return true; - } - - changeChunks(increase) { - this.updateSelected(increase ? this._changeChunks_inc : this._changeChunks_dec); - } - - force() { - for (let d of this.getSelected()) { + }, + changeChunks: function(increase) { + Tree.updateSelected(increase ? this._changeChunks_inc : this._changeChunks_dec); + }, + force: function() { + for (let d of Tree.getSelected()) { if (d.isOf(QUEUED | PAUSED | CANCELED)) { d.queue(); Dialog.run(d, true); } } - } - - manageMirrors() { + }, + manageMirrors: function() { if (!this.current) { return; } @@ -1253,9 +1202,8 @@ class TreeManager { this.current.replaceMirrors(mirrors); log(LOG_INFO, "New mirrors set " + mirrors); } - } - - export() { + }, + export: function() { function processResponse(fp, rv) { if (rv !== Ci.nsIFilePicker.returnOK && rv !== Ci.nsIFilePicker.returnReplace) { return; @@ -1314,10 +1262,9 @@ class TreeManager { log(LOG_ERROR, "Cannot export downloads", ex); Prompts.alert(window, _('export.title'), _('exportfailed')); } - } - - import() { - const processResponse = async function(fp, rv) { + }, + import: function() { + function processResponse(fp, rv) { if (rv !== Ci.nsIFilePicker.returnOK) { return; } @@ -1326,16 +1273,17 @@ class TreeManager { Metalinker.handleFile(fp.file); return; } - let lnks = await ImportExport.parseTextFile(fp.file); - if (lnks.length) { - DTA.saveLinkArray(window, lnks, []); - } + ImportExport.parseTextFile(fp.file, function importcb(lnks) { + if (lnks.length) { + DTA.saveLinkArray(window, lnks, []); + } + }); } catch (ex) { log(LOG_ERROR, "Cannot import downloads (processResponse)", ex); Prompts.alert(window, _('import.title'), _('importfailed')); } - }; + } try { let fp = new Instances.FilePicker(window, _('import.title'), Ci.nsIFilePicker.modeOpen); fp.appendFilters(Ci.nsIFilePicker.filterText); @@ -1355,9 +1303,8 @@ class TreeManager { log(LOG_ERROR, "Cannot import downloads", ex); Prompts.alert(window, _('import.title'), _('importfailed')); } - } - - addLimits() { + }, + addLimits: function() { showPreferences( "paneServers", { @@ -1365,13 +1312,12 @@ class TreeManager { url: this.current.urlManager.spec } ); - } - - showInfo() { + }, + showInfo: function() { this.beginUpdate(); try { let downloads = []; - for (let d of this.getSelected()) { + for (let d of Tree.getSelected()) { downloads.push(d); } if (downloads.length) { @@ -1381,9 +1327,8 @@ class TreeManager { finally { this.endUpdate(); } - } - - showTip(event) { + }, + showTip: function(event) { if (!Prefs.showTooltip || Services.ww.activeWindow !== window) { return false; } @@ -1404,29 +1349,40 @@ class TreeManager { Tooltip.start(d, true); return true; - } - - stopTip() { + }, + stopTip: function() { Tooltip.stop(); - } - - _refreshTools_init() { - this._refreshTools_item.forEach(function(e) { - e.item = $(e.item); - }); - this._refreshTools_items.forEach(function(e) { - e.items = $(...e.items); - }); - this._refreshTools_items_deferred.forEach(function(e) { - e.items = $(...e.items); - }); - } - - _stateIs(s) { - return this.state & s; - } + }, + _refreshTools_item: [ + {item: 'cmdResume', f: function(d) { return d.isOf(PAUSED | QUEUED | CANCELED); }}, + {item: 'cmdPause', f: function(d) { return (d.isOf(RUNNING) && d.resumable) || d.isOf(QUEUED | PAUSED); }}, + {item: 'cmdCancel', f: function(d) { return d.isOf(PAUSED | RUNNING | QUEUED | COMPLETE); }}, + + {item: 'cmdMoveUp', f: function(d) { return !Tree.filtered && d.min > 0; }}, + {item: 'cmdMoveTop', f: function(d) { return d.minId > 0; }}, + {item: 'cmdMoveDown', f: function(d) { return !Tree.filtered && d.max !== d.rows - 1; }}, + {item: 'cmdMoveBottom', f: function(d) { return d.maxId !== Tree._downloads.length - 1; }} + ], + _refreshTools_items: [ + {items: ["cmdDelete", "delete"], f: function(d) { return d.state === COMPLETE; }}, - refreshTools(d) { + {items: ['cmdRemoveSelected', 'cmdExport', 'cmdGetInfo', 'perDownloadSpeedLimit'], + f: function(d) { return !!d.count; }}, + {items: ['cmdMirrors', 'cmdAddLimits', 'cmdRename'], + f: function(d) { return d.count === 1; }}, + {items: ['cmdAddChunk', 'cmdRemoveChunk', 'cmdForceStart'], + f: function(d) { return d.isOf(QUEUED | RUNNING | PAUSED | CANCELED); }}, + ], + _refreshTools_items_deferred: [ + {items: ['cmdLaunch', "launch"], f: function(d) { return !!d.curFile; }}, + {items: ["cmdOpenFolder", "folder"], f: function(d) { return !!d.curFolder; }}, + ], + _refreshTools_init: function() { + this._refreshTools_item.forEach(function(e) { e.item = $(e.item); }); + this._refreshTools_items.forEach(function(e) { e.items = $.apply(null, e.items); }); + this._refreshTools_items_deferred.forEach(function(e) { e.items = $.apply(null, e.items); }); + }, + refreshTools: function(d) { if (this._updating || (d && ('position' in d) && !this.selection.isSelected(d.position))) { return; } @@ -1442,19 +1398,13 @@ class TreeManager { items[ii].setAttribute("disabled", "true"); } } - for (let i = 0, e = this._refreshTools_items_deferred.length; i < e; ++i) { - let items = this._refreshTools_items[i].items; - for (let ii = 0, ee = items.length; ii < ee; ++ii) { - items[ii].setAttribute("disabled", "true"); - } - } return; } let states = { state: 0, resumable: false, - is: this._stateIs, + is: function(s) { return this.state & s; }, isOf: QueueItem.prototype.isOf, count: this.selection.count, rows: this.rowCount, @@ -1474,25 +1424,42 @@ class TreeManager { let cur = this.current; for (let i = 0, e = this._refreshTools_item.length; i < e; ++i) { let item = this._refreshTools_item[i]; - let disabled = item.f.call(this, states) ? "false" : "true"; + let disabled = item.f(states) ? "false" : "true"; item.item.setAttribute("disabled", disabled); } for (let i = 0, e = this._refreshTools_items.length; i < e; ++i) { let items = this._refreshTools_items[i]; - let disabled = items.f.call(this, states) ? "false" : "true"; + let disabled = items.f(states) ? "false" : "true"; items = items.items; for (let ii = 0, ee = items.length; ii < ee; ++ii) { items[ii].setAttribute("disabled", disabled); } } - this._refreshToolsAsync(states, cur); + Task.spawn((function*() { + try { + states.curFile = (cur && cur.state === COMPLETE && + (yield OS.File.exists(cur.destinationLocalFile.path))); + states.curFolder = (cur && (yield OS.File.exists( + new Instances.LocalFile(cur.destinationPath).path))); + for (let i = 0, e = this._refreshTools_items_deferred.length; i < e; ++i) { + let items = this._refreshTools_items_deferred[i]; + let disabled = items.f(states) ? "false" : "true"; + items = items.items; + for (let ii = 0, ee = items.length; ii < ee; ++ii) { + items[ii].setAttribute("disabled", disabled); + } + } + } + catch (tex) { + log(LOG_ERROR, "rt (task)", tex); + } + }).bind(this)); } catch (ex) { log(LOG_ERROR, "rt", ex); } - } - - savePositions() { + }, + savePositions: function() { let saveArray = []; for (let i = 0, e = this._downloads.length; i < e; ++i) { let d = this._downloads[i]; @@ -1505,9 +1472,8 @@ class TreeManager { QueueStore.savePositions(saveArray); this.fireChangeEvent(); } - } - - savePositionsByOffsets() { + }, + savePositionsByOffsets: function() { // Special case: When deleting we know that we will only reduce .position. // This allows for DB updates based on offsets instead of absolute positions, // reducing the number of queries (param bindings) a lot, thus avoiding @@ -1531,9 +1497,8 @@ class TreeManager { sp.finalize(); this.fireChangeEvent(); } - } - - _invalidate_item(d, cell) { + }, + _invalidate_item: function(d, cell) { if (d.position >= 0 && !this.doFilterOne(d) && ~d.filteredPosition) { if (cell !== undefined) { this._box.invalidateCell(d.filteredPosition, this._cols[cell]); @@ -1542,13 +1507,12 @@ class TreeManager { this._box.invalidateRow(d.filteredPosition); } } - } - - invalidate(d, cell) { + }, + invalidate: function(d, cell) { if (!d) { FileExts.add(); this._box.invalidate(); - this.fireChangeEvent(); + this.refreshTools(this); return; } @@ -1559,9 +1523,15 @@ class TreeManager { return; } this._invalidate_item(d, cell); - } + }, + get box() { + return this._box; + }, + get all() { + return this._downloads; + }, - getSelected() { + getSelected: function() { if (!this.selection.count) { return []; } @@ -1575,10 +1545,10 @@ class TreeManager { } } return rv; - } + }, // returns an ASC sorted array of IDs that are currently selected. - _getSelectedIds(getReversed) { + _getSelectedIds: function(getReversed) { let select = this.selection; if (!select.count) { return []; @@ -1601,37 +1571,47 @@ class TreeManager { Array.sort(rv, this._getSelectedIds_asc); } return rv; - } - - _getSelectedIds_asc(a, b) { - return a - b; - } - - _getSelectedIds_desc(a, b) { - return b - a; - } - - _getSelectedFilteredIds_map(id) { - return this._filtered[id].position; - } - - _getSelectedFilteredIds(reverse) { + }, + _getSelectedIds_asc: function(a, b) { return a - b; }, + _getSelectedIds_desc: function(a, b) { return b - a; }, + _getSelectedFilteredIds_map: function(id) { return this._filtered[id].position; }, + _getSelectedFilteredIds: function(reverse) { return mapInSitu(this._getSelectedIds(reverse), this._getSelectedFilteredIds_map, this); - } + }, - at(idx) { + // get the first selected item, NOT the item which has the input focus. + get current() { + let select = this.selection; + try { + let ci = {value: -1}; + this.selection.getRangeAt(0, ci, {}); + if (ci.value > -1 && ci.value < this.rowCount) { + return this._filtered[ci.value]; + } + } + catch (ex) { + // fall-through + } + return null; + }, + // get the currently focused item. + get focused() { + let ci = this.selection.currentIndex; + if (ci > -1 && ci < this.rowCount) { + return this._filtered[ci]; + } + return null; + }, + at: function(idx) { return this._filtered[idx]; - } - - some(f, t) { + }, + some: function(f, t) { return this._downloads.some(f, t); - } - - every(f, t) { + }, + every: function(f, t) { return this._downloads.every(f, t); - } - - update(f, t) { + }, + update: function(f, t) { try { this.beginUpdate(); try { @@ -1645,33 +1625,28 @@ class TreeManager { log(LOG_ERROR, "function threw during update", ex); throw ex; } - } - - updateSelected(fn, ctx) { - this.beginUpdate(); - QueueStore.beginUpdate(); + }, + updateSelected: function(fn, ctx) { try { - for (let i of this.getSelected()) { - try { - fn.call(ctx, i); - } - catch (ex) { - log(LOG_ERROR, "Updating an item failed!"); - } - } + this.beginUpdate(); + QueueStore.beginUpdate(); + new CoThreadListWalker( + fn, + this.getSelected(), + 0, + ctx + ).start((function() { + QueueStore.endUpdate(); + this.invalidate(); + this.endUpdate(); + }).bind(this)); } catch (ex) { log(LOG_ERROR, "function threw during _gen", ex); throw ex; } - finally { - QueueStore.endUpdate(); - this.invalidate(); - this.endUpdate(); - } - } - - updateAll(fn, ctx) { + }, + updateAll: function(fn, ctx) { try { this.beginUpdate(); QueueStore.beginUpdate(); @@ -1691,9 +1666,8 @@ class TreeManager { log(LOG_ERROR, "function threw during updateAll", ex); throw ex; } - } - - moveTop() { + }, + moveTop: function() { try { this.beginUpdate(); let ids; @@ -1716,9 +1690,8 @@ class TreeManager { catch (ex) { log(LOG_ERROR, "Mover::top", ex); } - } - - moveBottom() { + }, + moveBottom: function() { try { this.beginUpdate(); let ids; @@ -1741,9 +1714,8 @@ class TreeManager { catch (ex) { log(LOG_ERROR, "Mover::bottom", ex); } - } - - moveUp() { + }, + moveUp: function() { try { if (this.filtered) { throw Error("not implemented"); @@ -1776,9 +1748,8 @@ class TreeManager { catch (ex) { log(LOG_ERROR, "Mover::up", ex); } - } - - moveDown() { + }, + moveDown: function() { try { if (this.filtered) { throw Error("not implemented"); @@ -1814,9 +1785,8 @@ class TreeManager { catch (ex) { log(LOG_ERROR, "Mover::down", ex); } - } - - showSpeedLimitList(event) { + }, + showSpeedLimitList: function(event) { if (!this.selection.count) { return false; } @@ -1829,18 +1799,15 @@ class TreeManager { } this._speedLimitList.limit = limit; return true; - } - - _changePerDownloadSpeedLimit_item(limit, d) { + }, + _changePerDownloadSpeedLimit_item: function(limit, d) { return (d.speedLimit = limit) || true; - } - - changePerDownloadSpeedLimit() { + }, + changePerDownloadSpeedLimit: function() { let limit = $('perDownloadSpeedLimitList').limit; this.updateSelected(this._changePerDownloadSpeedLimit_item.bind(null, limit)); - } - - startRename() { + }, + startRename: function() { try { let ci = {value: -1}; this.selection.getRangeAt(0, ci, {}); @@ -1859,96 +1826,14 @@ class TreeManager { log(LOG_ERROR, "Cannot rename", ex); } } +}; +requireJoined(Tree, "manager/matcher"); +requireJoined(Tree, "support/atoms"); - async _refreshToolsAsync(states, cur) { - try { - if (!cur || cur.state !== COMPLETE) { - states.curFile = states.curFolder = false; - this._refreshLastDest = null; - } - else if (this._refreshLastDest === cur.destinationLocalFile.path) { - states.curFile = this._refreshLastDestExists; - states.curFolder = this._refreshLastDestPathExists; - } - else { - this._refreshLastDest = cur.destinationLocalFile.path; - states.curFile = this._refreshLastDestExists = await OS.File.exists( - this._refreshLastDest); - if (states.curFile) { - states.curFolder = this._refreshLastDestPathExists = true; - } - else { - states.curFolder = this._refreshLastDestPathExists = await OS.File.exists( - new Instances.LocalFile(cur.destinationPath).path); - } - } - for (let items of this._refreshTools_items_deferred) { - let disabled = items.f.call(this, states) ? "false" : "true"; - items = items.items; - for (let item of items) { - item.setAttribute("disabled", disabled); - } - } - } - catch (tex) { - log(LOG_ERROR, "rt (task)", tex); - } - } - - async _removeByState(state, onlyGone) { - this.beginUpdate(); - try { - QueueStore.beginUpdate(); - var removing = []; - for (let d of this._downloads) { - if (d.state !== state) { - continue; - } - if (onlyGone && (await OS.File.exists(d.destinationLocalFile.path))) { - continue; - } - removing.push(d); - } - if (removing.length) { - this.remove(removing); - } - QueueStore.endUpdate(); - } - finally { - this.invalidate(); - this.endUpdate(); - } - } - - async _moveToNewLocation(download, from, to) { - try { - if (!(await OS.File.exists(from.path))) { - download.setUserFileName(to.leafName); - log(LOG_DEBUG, "gone"); - return; // gone already - } - if ((await OS.File.exists(to.path))) { - Prompts.alert(window, _("rename.title"), _("rename.alreadythere", [from.leafName, to.path])); - log(LOG_DEBUG, "exists"); - return; - } - - log(LOG_DEBUG, "move " + from.path + " to " + to.path); - // need to move - await OS.File.move(from.path, to.path); - log(LOG_DEBUG, "move complete " + from.path + " to " + to.path); - - download.setUserFileName(to.leafName); - } - catch (ex) { - log(LOG_DEBUG, "move failed " + from.path + " to " + to.path, ex); - Prompts.alert(window, _("rename.title"), _("rename.failedtomove", [from.path, to.path])); - } - } - - *_uniqueList() { +var FileHandling = { + _uniqueList: function*() { let u = {}; - for (let d of this.getSelected()) { + for (let d of Tree.getSelected()) { if (d.state !== COMPLETE) { continue; } @@ -1961,10 +1846,9 @@ class TreeManager { yield d; } } - } - - openFolder() { - for (let d of this.getSelected()) { + }, + openFolder: function() { + for (let d of Tree.getSelected()) { try { if (new Instances.LocalFile(d.destinationPath).exists()) { Utils.reveal(d.destinationFile); @@ -1974,10 +1858,9 @@ class TreeManager { log(LOG_ERROR, 'reveal', ex); } } - } - - openFile() { - let cur = this.current; + }, + openFile: function() { + let cur = Tree.current; if (cur && cur.state === COMPLETE) { try { Utils.launch(cur.destinationFile); @@ -1986,99 +1869,41 @@ class TreeManager { log(LOG_INFO, 'launch', ex); } } - } - - async deleteFile() { - try { - let list = []; - for (let d of this._uniqueList()) { - list.push(d); - } - let msg = ''; - if (list.length < 25) { - msg = _('deletetexts'); - for (let d of list) { - msg += "\n" + d.destinationLocalFile.leafName; + }, + deleteFile: function() { + Task.spawn(function*() { + try { + let list = []; + for (let d of this._uniqueList()) { + list.push(d); } - } - else { - msg = _('deletetextl.2', [list.length], list.length); - } - if (list.length && Prompts.confirm( - window, _('deletecaption'), msg, _('delete'), Prompts.CANCEL, null, 1)) { - return; - } - for (let d of list) { - try { - await OS.File.remove(d.destinationLocalFile.path); + let msg = ''; + if (list.length < 25) { + msg = _('deletetexts'); + for (let d of list) { + msg += "\n" + d.destinationLocalFile.leafName; + } + } + else { + msg = _('deletetextl.2', [list.length], list.length); } - catch (ex) { - // no-op + if (list.length && Prompts.confirm( + window, _('deletecaption'), msg, _('delete'), Prompts.CANCEL, null, 1)) { + return; + } + for (let d of list) { + try { + yield OS.File.remove(d.destinationLocalFile.path); + } + catch (ex) { + // no-op + } } + Tree.remove(list, true); } - this.remove(list, true); - } - catch (ex) { - log(LOG_ERROR, "deleteFile", ex); - } + catch (ex) { + log(LOG_ERROR, "deleteFile", ex); + } + }.bind(this)); } -} - -Object.assign(TreeManager.prototype, { - _cpprop_iconic: "iconic progress", - _cpprop_iconiccomplete: "iconic progress completed", - _cpprop_iconicfinishing: "iconic progress finishing", - _cpprop_iconicverified: "iconic progress completed verified", - _cpprop_iconicpaused: "iconic progress paused", - _cpprop_iconicpausedundetermined: "iconic progress paused pausedUndetermined", - _cpprop_iconicpausedretrying: "iconic progress paused pausedAutoretrying", - _cpprop_iconicpausedundeterminedretrying: "iconic progress paused pausedUndetermined pausedAutoretrying", - _cpprop_iconicinprogress: "iconic progress inprogress", - _cpprop_iconicicanceled: "iconic progress canceled", - _refreshTools_item: [ - {item: 'cmdResume', f: function(d) { - return d.isOf(PAUSED | QUEUED | CANCELED); - }}, - {item: 'cmdPause', f: function(d) { - return (d.isOf(RUNNING) && d.resumable) || d.isOf(QUEUED | PAUSED | CANCELED); - }}, - {item: 'cmdCancel', f: function(d) { - return d.isOf(PAUSED | RUNNING | QUEUED | COMPLETE); - }}, - - {item: 'cmdMoveUp', f: function(d) { - return !this.filtered && d.min > 0; - }}, - {item: 'cmdMoveTop', f: function(d) { - return true; - }}, - {item: 'cmdMoveDown', f: function(d) { - return !this.filtered && d.max !== d.rows - 1; - }}, - {item: 'cmdMoveBottom', f: function(d) { - return true; - }} - ], - _refreshTools_items: [ - {items: ["cmdDelete", "delete"], f: function(d) { - return d.state === COMPLETE; - }}, - - {items: ['cmdRemoveSelected', 'cmdExport', 'cmdGetInfo', 'perDownloadSpeedLimit'], - f: function(d) { return !!d.count; }}, - {items: ['cmdMirrors', 'cmdAddLimits', 'cmdRename'], - f: function(d) { return d.count === 1; }}, - {items: ['cmdAddChunk', 'cmdRemoveChunk', 'cmdForceStart'], - f: function(d) { return d.isOf(QUEUED | RUNNING | PAUSED | CANCELED); }}, - ], - _refreshTools_items_deferred: [ - {items: ['cmdLaunch', "launch"], f: function(d) { - return !!d.curFile; - }}, - {items: ["cmdOpenFolder", "folder"], f: function(d) { - return !!d.curFolder; - }}, - ], -}); -requireJoined(TreeManager.prototype, "manager/matcher"); -requireJoined(TreeManager.prototype, "support/atoms"); +}; diff --git a/chrome/content/dta/manager/utils.js b/chrome/content/dta/manager/utils.js index 1cd845407..fd7cb3838 100644 --- a/chrome/content/dta/manager/utils.js +++ b/chrome/content/dta/manager/utils.js @@ -2,7 +2,7 @@ * 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"; -/* global $, $$, _, Preferences, Dialog, Utils, mapInSitu, RequestManipulation, hash, showPreferences, defer */ +/* global $, $$, _, Preferences, Dialog, Utils, mapInSitu, RequestManipulation, hash, showPreferences, Timers, defer */ /* global COMPLETE, PAUSED, CANCELED, RUNNING, SPEED_COUNT, TOOLTIP_FREQ */ /* jshint globalstrict: true, strict:true, browser:true */ @@ -209,8 +209,8 @@ var Prefs = { _currentConns: 0, refreshConnPrefs: function(downloads) { let conns = 0; - for (let d of downloads) { - conns += d.activeChunks; + for (let i = 0, e = downloads.length; i < e; i++) { + conns += downloads[i].activeChunks; } conns = Math.max(this._baselineConns, Math.min(50, conns)); if (this._currentConns !== conns) { @@ -347,7 +347,7 @@ var Tooltip = { this.speedCanvas.hidden = false; this.speedRow.collapsed = false; } - this._timer = setInterval(() => this.update(), TOOLTIP_FREQ); + this._timer = Timers.createRepeating(TOOLTIP_FREQ, this.update, this, true); this._initUpdateRetries = 0; this.initUpdate(); }, @@ -396,7 +396,7 @@ var Tooltip = { stop: function() { this._current = null; if (this._timer) { - clearInterval(this._timer); + Timers.killTimer(this._timer); delete this._timer; } }, diff --git a/chrome/content/dta/mirrors.js b/chrome/content/dta/mirrors.js index 4db7b3f68..d553a9d5b 100644 --- a/chrome/content/dta/mirrors.js +++ b/chrome/content/dta/mirrors.js @@ -1,10 +1,10 @@ /* 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/. */ -/* jshint browser:true */ "use strict"; /* global _, DTA, $, $$, Utils, Preferences, getDefaultDownloadsDirectory, unloadWindow */ /* global toURI, toURL, setTimeoutOnlyFun */ +/* jshint strict:true, globalstrict:true, browser:true */ var Prompts = require("prompts"); var {LoggedPrompter} = require("support/loggedprompter"); @@ -163,45 +163,26 @@ function checkMirrors() { } }; - /* jshint -W003 */ - function finish() { - if (numGoodLengths > 1) { - let max; - let maxCL; - for (let cl in good) { - if (!max || good[cl].length > max) { - max = good[cl].length; - maxCL = cl; - } - } - for (let cl in good) { - if (cl === maxCL) { - continue; - } - for (let m of good[cl]) { - log(LOG_INFO, m.mirror + " has a cl of " + cl + " but the majority of mirrors uses " + maxCL); - m.setAttribute('state', 'bad'); - m.setAttribute('error', _('sizecheckerror')); - bad.push(m); - } - } + function makeRequest(m) { + let req = new XMLHttpRequest(); + req.mirror = m; + req.addEventListener("load", function() { + finishRequest(req); + }, false); + req.addEventListener("error", function() { + finishRequest(req); + }, false); + requests.add(req); + try { + req.open('HEAD', m.mirror); + req._callbacks = new Callbacks(req); + req.send(null); } - if (bad.length && (mirrors.itemCount - bad.length) > 0 && - Prompts.confirm( - window, - _('removebadmirrors.caption'), - _('removebadmirrors.message', [bad.length]), - _('removebadmirrors.keep'), // XXX swap - _('removebadmirrors.remove') - )) { - for (let b of bad) { - b.parentNode.removeChild(b); - } + catch (ex) { + finishRequest(req); } - button.disabled = false; } - function finishRequest(req, error) { let m = req.mirror; let state = 'bad'; @@ -243,28 +224,42 @@ function checkMirrors() { makeRequest(pending.shift()); } } - - function makeRequest(m) { - let req = new XMLHttpRequest(); - req.mirror = m; - req.addEventListener("load", function() { - finishRequest(req); - }, false); - req.addEventListener("error", function() { - finishRequest(req); - }, false); - requests.add(req); - try { - req.open('HEAD', m.mirror); - req._callbacks = new Callbacks(req); - req.send(null); + function finish() { + if (numGoodLengths > 1) { + let max; + let maxCL; + for (let cl in good) { + if (!max || good[cl].length > max) { + max = good[cl].length; + maxCL = cl; + } + } + for (let cl in good) { + if (cl === maxCL) { + continue; + } + for (let m of good[cl]) { + log(LOG_INFO, m.mirror + " has a cl of " + cl + " but the majority of mirrors uses " + maxCL); + m.setAttribute('state', 'bad'); + m.setAttribute('error', _('sizecheckerror')); + bad.push(m); + } + } } - catch (ex) { - finishRequest(req); + if (bad.length && (mirrors.itemCount - bad.length) > 0 && + Prompts.confirm( + window, + _('removebadmirrors.caption'), + _('removebadmirrors.message', [bad.length]), + _('removebadmirrors.keep'), // XXX swap + _('removebadmirrors.remove') + )) { + for (let b of bad) { + b.parentNode.removeChild(b); + } } + button.disabled = false; } - /* jshint +W003 */ - function timeout() { for (let req of requests) { log(LOG_INFO, req.mirror.mirror + " is a timeout"); diff --git a/chrome/content/dta/select.js b/chrome/content/dta/select.js index 6d00d26c6..8d6302af5 100644 --- a/chrome/content/dta/select.js +++ b/chrome/content/dta/select.js @@ -635,12 +635,12 @@ Dialog = { }, // will check/uncheck/invert the currently selected links - toggleSelection: function (...args) { + toggleSelection: function () { // modes: 1 = check, 2 = uncheck, other = invert let mode = 0; - if (args && args.length) { - mode = args[0] ? 1 : 2; + if (arguments && arguments.length) { + mode = arguments[0] ? 1 : 2; } let tree = this.current; diff --git a/chrome/content/integration/toolbarinstall.js b/chrome/content/integration/toolbarinstall.js index 1fd48c217..47bd0eea2 100644 --- a/chrome/content/integration/toolbarinstall.js +++ b/chrome/content/integration/toolbarinstall.js @@ -2,7 +2,7 @@ * 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"; -/* jshint browser:true */ +/*global window, close, document, opener, removeEventListener, addEventListener */ var all = ['dta-button', 'dta-turbo-button', 'dta-turboselect-button', 'dta-manager-button']; @@ -96,12 +96,11 @@ addEventListener("dialogaccept", function accept() { // Remove a button again // Note that the toolbar is not necessarily nav-bar let tbb = btn.parentNode; - // jshint -W083 + /* jshint -W083 */ tbb.currentSet = tbb.currentSet .split(',') .filter(id => id !== b) .join(","); - // jshint +W083 tbb.setAttribute("currentset", tbb.currentSet); opener.document.persist(tbb.id, "currentset"); } diff --git a/chrome/content/preferences/bindings.xml b/chrome/content/preferences/bindings.xml index c3ff88211..2d74dfda6 100644 --- a/chrome/content/preferences/bindings.xml +++ b/chrome/content/preferences/bindings.xml @@ -16,7 +16,6 @@ document.getAnonymousElementByAttribute(this, 'anonid', 'connectionsLabel'); document.getAnonymousElementByAttribute(this, 'anonid', 'speedLabel'); document.getAnonymousElementByAttribute(this, 'anonid', 'segmentsLabel'); - document.getAnonymousElementByAttribute(this, 'anonid', 'cleanLabel'); @@ -36,7 +35,6 @@ this._connectionsLabel.value = (this.connections > 0) ? this.connections : _('unlimited'); this._speedLabel.value = (this.speed > 0) ? Utils.formatSpeed(this.speed * 1024) : _('unlimited'); this._segmentsLabel.value = (this.segments > 0) ? this.segments : _('unlimited'); - this._cleanLabel.value = this.clean; ]]> @@ -71,10 +69,6 @@ - - - - @@ -82,7 +76,6 @@ - @@ -110,10 +103,6 @@ - - - - @@ -127,8 +116,6 @@ - - - - - - - - - @@ -227,9 +207,6 @@ document.getAnonymousElementByAttribute(this, 'anonid', 'segmentsGroup'); document.getAnonymousElementByAttribute(this, 'anonid', 'segments'); - document.getAnonymousElementByAttribute(this, 'anonid', 'cleanGroup'); - document.getAnonymousElementByAttribute(this, 'anonid', 'clean'); - document.getAnonymousElementByAttribute(this, 'anonid', 'saveButton'); @@ -259,17 +236,10 @@ ]]> - - - - diff --git a/chrome/content/preferences/interfacePane.xul b/chrome/content/preferences/interfacePane.xul index 123e165e7..dd3631f3f 100644 --- a/chrome/content/preferences/interfacePane.xul +++ b/chrome/content/preferences/interfacePane.xul @@ -28,7 +28,6 @@ - @@ -38,7 +37,6 @@ diff --git a/chrome/content/preferences/prefs.js b/chrome/content/preferences/prefs.js index 35d2156ea..c93a1a5e4 100644 --- a/chrome/content/preferences/prefs.js +++ b/chrome/content/preferences/prefs.js @@ -11,13 +11,6 @@ var IMAGE_FILTER = FilterManager.IMAGE_FILTER; var Prompts = require("prompts"); -function showBool(bool) { - if (bool === true || bool === "true") { - return "✓"; - } - return "✘"; -} - var Main = { load: function() { $('alert2').hidden = !('nsIAlertsService' in Ci); @@ -481,7 +474,7 @@ var Servers = { while (this._list.firstChild){ this._list.removeChild(this._list.firstChild); } - for (let [,limit] of this.listLimits()) { + for (let [,limit] in Iterator(this.listLimits())) { let e = document.createElement('richlistitem'); e.setAttribute('class', 'serverlimit'); e.setAttribute('id', "host" + limit.host); @@ -490,7 +483,6 @@ var Servers = { e.setAttribute('connections', limit.connections); e.setAttribute('speed', limit.speed); e.setAttribute('segments', limit.segments); - e.setAttribute('clean', limit.clean ? "true" : "false"); e.limit = limit; this._list.appendChild(e); } @@ -516,7 +508,6 @@ var Servers = { e.setAttribute('searchlabel', limit.host); e.setAttribute('connections', limit.connections); e.setAttribute('speed', limit.speed); - e.setAttribute('clean', limit.clean); e.limit = limit; this._list.appendChild(e); this._list.selectedItem = e; diff --git a/chrome/locale/de/landingpage.dtd b/chrome/locale/de/landingpage.dtd index 63f3c51e4..72bd3ac3a 100644 --- a/chrome/locale/de/landingpage.dtd +++ b/chrome/locale/de/landingpage.dtd @@ -11,11 +11,11 @@ -Features Seite."> +Features Seite."> -Features Seite."> +Features Seite."> @@ -86,7 +86,7 @@ babelzilla.org beizutreten um dabei zu helfen diese und viele weitere tolle Erweiterungen zu übersetzen."> GPL 2.0 lizenziert. Siehe GPL Datei. Dies trifft auf alle Quelltext-Dateien zu."> -http://code.downthemall.net/repos/. Die meisten Dateien sind unter der GPL/LGPL-kompatiblen MPL 2.0 lizensiert, oder sind dreifach-lizensiert als MPL 1.1/GPL 2.0/LGPL 2.1. Siehe MPL, GPL, LGPL Dateien."> +https://code.downthemall.org/repos/. Die meisten Dateien sind unter der GPL/LGPL-kompatiblen MPL 2.0 lizensiert, oder sind dreifach-lizensiert als MPL 1.1/GPL 2.0/LGPL 2.1. Siehe MPL, GPL, LGPL Dateien."> @@ -95,7 +95,7 @@ -Bugtracker einstellen."> +Bugtracker einstellen."> -Kontaktseite online."> +Kontaktseite online."> diff --git a/chrome/locale/en-US/landingpage.dtd b/chrome/locale/en-US/landingpage.dtd index 6e85db147..d4ec89efb 100644 --- a/chrome/locale/en-US/landingpage.dtd +++ b/chrome/locale/en-US/landingpage.dtd @@ -15,12 +15,12 @@ -Features page."> +Features page."> -features page."> +features page."> @@ -101,7 +101,7 @@ GPL 2.0. See the GPL file. This applies to all source code files."> -http://code.downthemall.net/repos/. Most files are licensed under the GPL/LGPL-compatible MPL 2.0, or are tri-licensed under a MPL 1.1/GPL 2.0/LGPL 2.1 license. See the MPL, GPL, LGPL files respectively."> +https://code.downthemall.org/repos/. Most files are licensed under the GPL/LGPL-compatible MPL 2.0, or are tri-licensed under a MPL 1.1/GPL 2.0/LGPL 2.1 license. See the MPL, GPL, LGPL files respectively."> @@ -111,8 +111,8 @@ -bug tracker."> +bug tracker."> -contact page."> +contact page."> diff --git a/chrome/locale/en-US/manager.dtd b/chrome/locale/en-US/manager.dtd index 55eb46200..9345269bf 100644 --- a/chrome/locale/en-US/manager.dtd +++ b/chrome/locale/en-US/manager.dtd @@ -67,7 +67,6 @@ - diff --git a/chrome/locale/en-US/manager.properties b/chrome/locale/en-US/manager.properties index f3e1c000b..c85c7170b 100644 --- a/chrome/locale/en-US/manager.properties +++ b/chrome/locale/en-US/manager.properties @@ -1,7 +1,6 @@ complete=Complete canceled=Canceled moving=Moving -finishing=Finishing… # LOCALIZATION NOTE(currentdownloadstats): number complete, number total, number running currentdownloadstats=%S of %S (%S), %S running # LOCALIZATION NOTE(titlespeed): Percent complete, speed, number complete, number total @@ -58,9 +57,6 @@ freespace.title=Not Enough Free Space! freespace.temp=There is not enough free space available in the temporary directory. Please disable this option from Advanced preferences. freespace=No free space freespace.dir=There is not enough free space available. Please make more free space or change the download directory. -moveerror=Failed to move file -moveerror.status=Failed to move file (%s) -moveerror.long=Failed to move file. Could not create a directory or file in the download directory. Please check your file permissions or change the download directory. accesserror=File access error accesserror.long=File access was denied. Could not create a directory or file in the download directory. Please check your file permissions or change the download directory. temp.notdir=The temporary location is not a directory. Using the download directory to store files for now. Please fix this in the preferences. diff --git a/chrome/locale/en-US/prefpanes.dtd b/chrome/locale/en-US/prefpanes.dtd index 8a35528b4..b535280fd 100644 --- a/chrome/locale/en-US/prefpanes.dtd +++ b/chrome/locale/en-US/prefpanes.dtd @@ -65,11 +65,8 @@ - - - @@ -122,7 +119,6 @@ - diff --git a/chrome/locale/es-ES/landingpage.dtd b/chrome/locale/es-ES/landingpage.dtd index 0991b8c50..486771f92 100644 --- a/chrome/locale/es-ES/landingpage.dtd +++ b/chrome/locale/es-ES/landingpage.dtd @@ -11,11 +11,11 @@ -página de características."> +página de características."> -página de características."> +página de características."> @@ -86,7 +86,7 @@ babelzilla.org para ayudar a traducir este y otras muchas grandes extensiones."> GPL 2.0. Consulte el archivo GPL. Esto se aplica a todos los códigos fuente."> -http://code.downthemall.net/repos/. La mayoría de los archivos usan licencia GPL/LGPL compatible con MPL 2.0, o están trilicenciados con una licencia MPL 1.1/GPL 2.0/LGPL 2.1. Consulte los archivos MPL, GPL y LGPL correspondientes."> +https://code.downthemall.org/repos/. La mayoría de los archivos usan licencia GPL/LGPL compatible con MPL 2.0, o están trilicenciados con una licencia MPL 1.1/GPL 2.0/LGPL 2.1. Consulte los archivos MPL, GPL y LGPL correspondientes."> @@ -95,7 +95,7 @@ -seguidor de errores."> +seguidor de errores."> -página de contacto en línea."> +página de contacto en línea."> diff --git a/chrome/locale/et/landingpage.dtd b/chrome/locale/et/landingpage.dtd index 66930d86c..575f541a2 100644 --- a/chrome/locale/et/landingpage.dtd +++ b/chrome/locale/et/landingpage.dtd @@ -11,11 +11,11 @@ -Võimaluste lehte."> +Võimaluste lehte."> -võimaluste lehte."> +võimaluste lehte."> @@ -86,7 +86,7 @@ babelzilla.org, et aidata tõlkida seda ja palju muid suurepäraseid lisasid."> GPL 2.0 alusel. Vaata GPL faili. See käib kõigi lähtekoodifailide kohta."> -http://code.downthemall.net/repos/. Enamus faile on litsentsitud litsentsi GPL/LGPL-ühilduv MPL 2.0 või on kolmik-litsentsitud litsentside MPL 1.1/GPL 2.0/LGPL 2.1 alusel. Vaata vastavaid faile MPL, GPL, LGPL."> +https://code.downthemall.org/repos/. Enamus faile on litsentsitud litsentsi GPL/LGPL-ühilduv MPL 2.0 või on kolmik-litsentsitud litsentside MPL 1.1/GPL 2.0/LGPL 2.1 alusel. Vaata vastavaid faile MPL, GPL, LGPL."> @@ -95,7 +95,7 @@ -vigade jälitaja kaudu."> +vigade jälitaja kaudu."> -kontaktide lehte veebis."> +kontaktide lehte veebis."> diff --git a/chrome/locale/fr/landingpage.dtd b/chrome/locale/fr/landingpage.dtd index 794ddb637..35f0ad1c6 100644 --- a/chrome/locale/fr/landingpage.dtd +++ b/chrome/locale/fr/landingpage.dtd @@ -11,11 +11,11 @@ -page des fonctionnalités."> +page des fonctionnalités."> -page des fonctionnalités."> +page des fonctionnalités."> @@ -86,7 +86,7 @@ babelzilla.org pour aider à la traduction de cette extension et beaucoup d'autres."> GPL 2.0. Voyez le fichier GPL. Ceci s'applique à tous les fichiers contenant du code source."> -http://code.downthemall.net/repos/. La plupart des fichiers sont sous licence GPL/LGPL-compatible MPL 2.0 ou sous tri-licence MPL 1.1/GPL 2.0/LGPL 2.1. Voyez respectivement les fichiers MPL, GPL, LGPL."> +https://code.downthemall.org/repos/. La plupart des fichiers sont sous licence GPL/LGPL-compatible MPL 2.0 ou sous tri-licence MPL 1.1/GPL 2.0/LGPL 2.1. Voyez respectivement les fichiers MPL, GPL, LGPL."> @@ -95,7 +95,7 @@ -bug tracker."> +bug tracker."> -la page de contact en ligne."> +la page de contact en ligne."> diff --git a/chrome/locale/gl-ES/landingpage.dtd b/chrome/locale/gl-ES/landingpage.dtd index 3385f2cfe..f4ceb8a25 100644 --- a/chrome/locale/gl-ES/landingpage.dtd +++ b/chrome/locale/gl-ES/landingpage.dtd @@ -11,11 +11,11 @@ -Páxina de características."> +Páxina de características."> -páxina de características."> +páxina de características."> @@ -86,7 +86,7 @@ babelzilla.org para axudar a traducir este e moitos outros grandes complemetos."> GPL 2.0. Vexa o ficheiro GPL. Isto aplícase a todos os ficheiros do código fonte."> -http://code.downthemall.net/repos/. A maioría dos ficheiros están publicados baixo a licenza GPL/LGPL-compatible MPL 2.0, ou tri-licenciados baixo a licenza MPL 1.1/GPL 2.0/LGPL 2.1. Vexa os ficheiros MPL, GPL e LGPL respectivamente."> +https://code.downthemall.org/repos/. A maioría dos ficheiros están publicados baixo a licenza GPL/LGPL-compatible MPL 2.0, ou tri-licenciados baixo a licenza MPL 1.1/GPL 2.0/LGPL 2.1. Vexa os ficheiros MPL, GPL e LGPL respectivamente."> @@ -95,7 +95,7 @@ -monitor de erros."> +monitor de erros."> -páxina de contacto en liña."> +páxina de contacto en liña."> diff --git a/chrome/locale/gl/landingpage.dtd b/chrome/locale/gl/landingpage.dtd index 3385f2cfe..f4ceb8a25 100644 --- a/chrome/locale/gl/landingpage.dtd +++ b/chrome/locale/gl/landingpage.dtd @@ -11,11 +11,11 @@ -Páxina de características."> +Páxina de características."> -páxina de características."> +páxina de características."> @@ -86,7 +86,7 @@ babelzilla.org para axudar a traducir este e moitos outros grandes complemetos."> GPL 2.0. Vexa o ficheiro GPL. Isto aplícase a todos os ficheiros do código fonte."> -http://code.downthemall.net/repos/. A maioría dos ficheiros están publicados baixo a licenza GPL/LGPL-compatible MPL 2.0, ou tri-licenciados baixo a licenza MPL 1.1/GPL 2.0/LGPL 2.1. Vexa os ficheiros MPL, GPL e LGPL respectivamente."> +https://code.downthemall.org/repos/. A maioría dos ficheiros están publicados baixo a licenza GPL/LGPL-compatible MPL 2.0, ou tri-licenciados baixo a licenza MPL 1.1/GPL 2.0/LGPL 2.1. Vexa os ficheiros MPL, GPL e LGPL respectivamente."> @@ -95,7 +95,7 @@ -monitor de erros."> +monitor de erros."> -páxina de contacto en liña."> +páxina de contacto en liña."> diff --git a/chrome/locale/id/landingpage.dtd b/chrome/locale/id/landingpage.dtd index ac66262ba..a5a3c73e8 100644 --- a/chrome/locale/id/landingpage.dtd +++ b/chrome/locale/id/landingpage.dtd @@ -11,11 +11,11 @@ -halaman Fitur."> +halaman Fitur."> -halaman fitur."> +halaman fitur."> @@ -86,7 +86,7 @@ babelzilla.org untuk membantu menerjemahkan DownThemAll! dan pengaya keren lainnya."> GPL 2.0. Lihat berkas GPL. Ini belaku untuk semua kode sumber."> -http://code.downthemall.net/repos/. Sebagian besar berkas menggunakan lisensi GPL/LGPL yang kompatibel dengan MPL 2.0, atau menggunakan tiga lisensi sekaligus MPL 1.1/GPL 2.0/LGPL 2.1. Lihat berkas MPL, GPL, LGPL terkait."> +https://code.downthemall.org/repos/. Sebagian besar berkas menggunakan lisensi GPL/LGPL yang kompatibel dengan MPL 2.0, atau menggunakan tiga lisensi sekaligus MPL 1.1/GPL 2.0/LGPL 2.1. Lihat berkas MPL, GPL, LGPL terkait."> @@ -95,7 +95,7 @@ -pelacak bug kami."> +pelacak bug kami."> -halaman kontak kami."> +halaman kontak kami."> diff --git a/chrome/locale/it/landingpage.dtd b/chrome/locale/it/landingpage.dtd index 72279c0cd..f3b42d665 100644 --- a/chrome/locale/it/landingpage.dtd +++ b/chrome/locale/it/landingpage.dtd @@ -11,11 +11,11 @@ -pagina delle caratteristiche."> +pagina delle caratteristiche."> -pagina delle caratteristiche."> +pagina delle caratteristiche."> @@ -86,7 +86,7 @@ babelzilla.org per aiutare a tradurre questo e molti altri ottimi componenti aggiuntivi."> GPL 2.0. Vedi il file GPL. Ciò si applica a tutti i file sorgente."> -http://code.downthemall.net/repos/. La maggior parte dei file è concessa in licenza in base alla MPL 2.0, compatibile con la GPL/LGPL, o con tre licenze in base alle MPL 1.1/GPL 2.0/LGPL 2.1. Vedi rispettivamente i file MPL, GPL, LGPL."> +https://code.downthemall.org/repos/. La maggior parte dei file è concessa in licenza in base alla MPL 2.0, compatibile con la GPL/LGPL, o con tre licenze in base alle MPL 1.1/GPL 2.0/LGPL 2.1. Vedi rispettivamente i file MPL, GPL, LGPL."> @@ -95,7 +95,7 @@ -bug tracker."> +bug tracker."> -pagina dei contatti."> +pagina dei contatti."> diff --git a/chrome/locale/ja/landingpage.dtd b/chrome/locale/ja/landingpage.dtd index ef816464e..e8e4627f2 100644 --- a/chrome/locale/ja/landingpage.dtd +++ b/chrome/locale/ja/landingpage.dtd @@ -11,11 +11,11 @@ -ホームページ をご覧ください。"> +ホームページ をご覧ください。"> -機能紹介のページ をご覧ください。"> +機能紹介のページ をご覧ください。"> @@ -86,7 +86,7 @@ babelzilla.org に参加して DownThemAll!! の他の言語への翻訳にご協力ください。"> GPL 2.0 の下でライセンスされています。ライセンスに関する詳細は GPL ファイルをご覧ください。 このライセンスはすべてのソースコードファイルに対して適用されます。"> -http://code.downthemall.net/repos/ リポジトリから個々のファイルを確認してください。多くのファイルが GPL/LGPL と互換性のある MPL 2.0 か MPL 1.1/GPL 2.0/LGPL 2.1 のトリプルライセンスで提供されいます。各ライセンスについては MPL、GPL、LGPL のファイルをご覧ください。"> +https://code.downthemall.org/repos/ リポジトリから個々のファイルを確認してください。多くのファイルが GPL/LGPL と互換性のある MPL 2.0 か MPL 1.1/GPL 2.0/LGPL 2.1 のトリプルライセンスで提供されいます。各ライセンスについては MPL、GPL、LGPL のファイルをご覧ください。"> @@ -95,7 +95,7 @@ -bug tracker に報告してください。"> +bug tracker に報告してください。"> -連絡先ページ をご覧ください。"> +連絡先ページ をご覧ください。"> diff --git a/chrome/locale/nl/landingpage.dtd b/chrome/locale/nl/landingpage.dtd index 35b7acc0b..dbf154cf1 100644 --- a/chrome/locale/nl/landingpage.dtd +++ b/chrome/locale/nl/landingpage.dtd @@ -11,11 +11,11 @@ -pagina Features."> +pagina Features."> -pagina Features."> +pagina Features."> @@ -86,7 +86,7 @@ babelzilla.org om deze en vele andere geweldige add-ons te helpen vertalen."> GPL 2.0. Zie hiervoor het GPL-bestand. Dit is op alle broncodebestanden van toepassing."> -http://code.downthemall.net/repos/. De meeste bestanden zijn gelicentieerd onder de GPL/LGPL-compatibele MPL 2.0, of ze zijn ‘tri-licensed’ onder een MPL 1.1/GPL 2.0/LGPL 2.1-licentie. Zie hiervoor respectievelijk de MPL-, GPL- en LGPL-bestanden."> +https://code.downthemall.org/repos/. De meeste bestanden zijn gelicentieerd onder de GPL/LGPL-compatibele MPL 2.0, of ze zijn ‘tri-licensed’ onder een MPL 1.1/GPL 2.0/LGPL 2.1-licentie. Zie hiervoor respectievelijk de MPL-, GPL- en LGPL-bestanden."> @@ -95,7 +95,7 @@ -bugtracker."> +bugtracker."> -contactpagina."> +contactpagina."> diff --git a/chrome/locale/pl/landingpage.dtd b/chrome/locale/pl/landingpage.dtd index 11b4d0ff6..dbb7a98fa 100644 --- a/chrome/locale/pl/landingpage.dtd +++ b/chrome/locale/pl/landingpage.dtd @@ -11,11 +11,11 @@ -opisem funkcji."> +opisem funkcji."> -opisem funkcji."> +opisem funkcji."> @@ -86,7 +86,7 @@ babelzilla.org, by pomóc w tłumaczeniu tego i innych wspaniałych dodatków."> GPL 2.0. Zobacz plik GPL. Dotyczy to wszystkich plików zawierających kod źródłowy."> -http://code.downthemall.net/repos/. Wiele plików ma trzy licencje: MPL 1.1/GPL 2.0/LGPL 2.1. Zobacz odpowiednio pliki MPL, GPL, LGPL."> +https://code.downthemall.org/repos/. Wiele plików ma trzy licencje: MPL 1.1/GPL 2.0/LGPL 2.1. Zobacz odpowiednio pliki MPL, GPL, LGPL."> @@ -95,7 +95,7 @@ -system śledzenia błędów."> +system śledzenia błędów."> -stronę kontaktową."> +stronę kontaktową."> diff --git a/chrome/locale/pt-BR/landingpage.dtd b/chrome/locale/pt-BR/landingpage.dtd index eff869008..94ecbe873 100644 --- a/chrome/locale/pt-BR/landingpage.dtd +++ b/chrome/locale/pt-BR/landingpage.dtd @@ -11,11 +11,11 @@ -Página de Funcionalidades."> +Página de Funcionalidades."> -Página de Funcionalidades."> +Página de Funcionalidades."> @@ -86,7 +86,7 @@ babelzilla.org para ajudar a traduzir este e outros bons complementos."> GPL 2.0. Veja o arquivo da GPL. Isso é aplicado a todos os arquivos de códigos-fonte."> -http://code.downthemall.net/repos/. Os arquivos são, em sua maioria, licenciados sob o regime GPL/LGPL-compatible MPL 2.0, ou são tri-licenciados sob uma licença MPL 1.1/GPL 2.0/LGPL 2.1. Veja os arquivos MPL, GPL e LGPL, respectivamente."> +https://code.downthemall.org/repos/. Os arquivos são, em sua maioria, licenciados sob o regime GPL/LGPL-compatible MPL 2.0, ou são tri-licenciados sob uma licença MPL 1.1/GPL 2.0/LGPL 2.1. Veja os arquivos MPL, GPL e LGPL, respectivamente."> @@ -95,7 +95,7 @@ -monitor de bug."> +monitor de bug."> -página de contato."> +página de contato."> diff --git a/chrome/locale/pt-PT/landingpage.dtd b/chrome/locale/pt-PT/landingpage.dtd index c8fdb8d8a..0cd08c53a 100644 --- a/chrome/locale/pt-PT/landingpage.dtd +++ b/chrome/locale/pt-PT/landingpage.dtd @@ -11,11 +11,11 @@ -de funcionalidades."> +de funcionalidades."> -página de funcionalidades."> +página de funcionalidades."> @@ -86,7 +86,7 @@ babelzilla.org para ajudar a traduzir este e muitos outros grandiosos extras."> GPL 2.0. Veja o ficheiro GPL. Isto aplica-se a todo os ficheiros de código-fonte."> -http://code.downthemall.net/repos/. A maioria dos ficheiros são licenciados sob GPL/LGPL-compatível MPL 2.0, ou são tri-licenciados sob licenças MPL 1.1/GPL 2.0/LGPL 2.1. Veja os ficheiros MPL, GPL, LGPL respetivos."> +https://code.downthemall.org/repos/. A maioria dos ficheiros são licenciados sob GPL/LGPL-compatível MPL 2.0, ou são tri-licenciados sob licenças MPL 1.1/GPL 2.0/LGPL 2.1. Veja os ficheiros MPL, GPL, LGPL respetivos."> @@ -95,7 +95,7 @@ -bug tracker."> +bug tracker."> -página de contactos online."> +página de contactos online."> diff --git a/chrome/locale/ro/landingpage.dtd b/chrome/locale/ro/landingpage.dtd index 0c553f1b1..86d7f2502 100644 --- a/chrome/locale/ro/landingpage.dtd +++ b/chrome/locale/ro/landingpage.dtd @@ -11,11 +11,11 @@ -Caracteristici (în limba engleză)."> +Caracteristici (în limba engleză)."> -Caracteristici."> +Caracteristici."> @@ -86,7 +86,7 @@ babelzilla.org to help translate this and many other great add-ons."> Drepturi de autor și licență">"> GPL 2.0. Vedeți fișierul GPL. Aceasta este valabilă pentru toate fișierele codului sursă."> -http://code.downthemall.net/repos/. Most files are licensed under the GPL/LGPL-compatible MPL 2.0, or are tri-licensed under a MPL 1.1/GPL 2.0/LGPL 2.1 license. See the MPL, GPL, LGPL files respectively."> +https://code.downthemall.org/repos/. Most files are licensed under the GPL/LGPL-compatible MPL 2.0, or are tri-licensed under a MPL 1.1/GPL 2.0/LGPL 2.1 license. See the MPL, GPL, LGPL files respectively."> @@ -95,7 +95,7 @@ -bug tracker."> +bug tracker."> -pagina de contact (în limba engleză)."> +pagina de contact (în limba engleză)."> diff --git a/chrome/locale/ru/landingpage.dtd b/chrome/locale/ru/landingpage.dtd index 2edaf5099..f43f8ec84 100644 --- a/chrome/locale/ru/landingpage.dtd +++ b/chrome/locale/ru/landingpage.dtd @@ -11,11 +11,11 @@ -Возможности."> +Возможности."> -возможности."> +возможности."> @@ -86,7 +86,7 @@ babelzilla.org."> GPL 2.0. См. файл GPL. Это относится ко всем файлам исходного кода."> -http://code.downthemall.net/repos/. В основном файлы лицензированы под совместимой с GPL/LGPL MPL 2.0, или имеют три лицензии MPL 1.1/GPL 2.0/LGPL 2.1. Смотрите соответственно файлы MPL, GPL, LGPL."> +https://code.downthemall.org/repos/. В основном файлы лицензированы под совместимой с GPL/LGPL MPL 2.0, или имеют три лицензии MPL 1.1/GPL 2.0/LGPL 2.1. Смотрите соответственно файлы MPL, GPL, LGPL."> @@ -95,7 +95,7 @@ -системе отслеживания ошибок."> +системе отслеживания ошибок."> -контакты."> +контакты."> diff --git a/chrome/locale/sl-SI/landingpage.dtd b/chrome/locale/sl-SI/landingpage.dtd index 3c5955963..2e62f7b04 100644 --- a/chrome/locale/sl-SI/landingpage.dtd +++ b/chrome/locale/sl-SI/landingpage.dtd @@ -11,11 +11,11 @@ -stran značilnosti."> +stran značilnosti."> -stran značilnosti."> +stran značilnosti."> @@ -86,7 +86,7 @@ Babelzilli, da nam pomagate prevesti ta in veliko drugih dodatkov."> GPL 2.0. Glejte datoteko GPL. To velja za vse datoteke izvorne kode."> -http://code.downthemall.net/repos/. Večina datotek je pod licenco GPL/LGPL-compatible MPL 2.0 ali pod trojno licenco MPL 1.1/GPL 2.0/LGPL 2.1. Glejte datoteke MPL, GPL in LGPL."> +https://code.downthemall.org/repos/. Večina datotek je pod licenco GPL/LGPL-compatible MPL 2.0 ali pod trojno licenco MPL 1.1/GPL 2.0/LGPL 2.1. Glejte datoteke MPL, GPL in LGPL."> @@ -95,7 +95,7 @@ -sledilniku hroščev."> +sledilniku hroščev."> -stran za stike."> +stran za stike."> diff --git a/chrome/locale/sv-SE/landingpage.dtd b/chrome/locale/sv-SE/landingpage.dtd index c3a2d06ed..9b7eb5f9b 100644 --- a/chrome/locale/sv-SE/landingpage.dtd +++ b/chrome/locale/sv-SE/landingpage.dtd @@ -11,11 +11,11 @@ -webbsida."> +webbsida."> -funktionsidan."> +funktionsidan."> @@ -86,7 +86,7 @@ babelzilla.org för att hjälpa till att översätta detta och många andra bra tillägg."> GPL 2.0. Se GPL-filen. Detta gäller alla källkodsfiler."> -http://code.downthemall.net/repos/. De flesta filer är licenserade under GPL/LGPL-compatible MPL 2.0, eller har de tre licenserna MPL 1.1/GPL 2.0/LGPL 2.1. Se de respektive MPL-, GPL- och LGPL-filerna."> +https://code.downthemall.org/repos/. De flesta filer är licenserade under GPL/LGPL-compatible MPL 2.0, eller har de tre licenserna MPL 1.1/GPL 2.0/LGPL 2.1. Se de respektive MPL-, GPL- och LGPL-filerna."> @@ -95,7 +95,7 @@ -buggspårningslista."> +buggspårningslista."> -kontaktsida."> +kontaktsida."> diff --git a/chrome/locale/tr/landingpage.dtd b/chrome/locale/tr/landingpage.dtd index 84148f0d1..aa7fcb70b 100644 --- a/chrome/locale/tr/landingpage.dtd +++ b/chrome/locale/tr/landingpage.dtd @@ -11,11 +11,11 @@ -Özellikler sayfasına uğrayın."> +Özellikler sayfasına uğrayın."> -özellikler sayfasına uğrayın."> +özellikler sayfasına uğrayın."> @@ -86,7 +86,7 @@ babelzilla.org'a katılıp bu ve diğer güzel eklentileri çevirmede yardımcı olabilirsiniz."> GPL 2.0 altında lisanslanmıştır. Bu lisans tüm kaynak kodları için geçerlidir."> -http://code.downthemall.net/repos/ adresindeki ilgili depolanmış dosyalara bakın. Çoğu dosya GPL/LGPL-uyumlu MPL 2.0 lisanslanmış, ya da MPL1.1/GPL 2.0/LGPL 2.1 lisansları altında üçlü-lisanslanmıştır. Sırasıyla MPL, GPL, LGPL dosyalarına bakın."> +https://code.downthemall.org/repos/ adresindeki ilgili depolanmış dosyalara bakın. Çoğu dosya GPL/LGPL-uyumlu MPL 2.0 lisanslanmış, ya da MPL1.1/GPL 2.0/LGPL 2.1 lisansları altında üçlü-lisanslanmıştır. Sırasıyla MPL, GPL, LGPL dosyalarına bakın."> @@ -95,7 +95,7 @@ -hata takipçimize gönderin."> +hata takipçimize gönderin."> -iletişim sayfamıza uğrayın."> +iletişim sayfamıza uğrayın."> diff --git a/chrome/locale/zh-CN/landingpage.dtd b/chrome/locale/zh-CN/landingpage.dtd index 4a2f70a84..b3ff68978 100644 --- a/chrome/locale/zh-CN/landingpage.dtd +++ b/chrome/locale/zh-CN/landingpage.dtd @@ -11,11 +11,11 @@ -产品特点页面。"> +产品特点页面。"> -产品特点页面。"> +产品特点页面。"> @@ -86,7 +86,7 @@ babelzilla.org 来翻译本扩展以及其它更多好用的扩展。"> GPL 2.0。查看GPL文件。应用到所有源代码文件。"> -http://code.downthemall.net/repos/。大多数文件基于 GPL/LGPL-compatible MPL 2.0,或是MPL 1.1/GPL 2.0/LGPL 2.1 license。请分别查看MPL,GPL,LGPL文件。"> +https://code.downthemall.org/repos/。大多数文件基于 GPL/LGPL-compatible MPL 2.0,或是MPL 1.1/GPL 2.0/LGPL 2.1 license。请分别查看MPL,GPL,LGPL文件。"> @@ -95,7 +95,7 @@ -bug tracker报告Bug和功能请求。"> +bug tracker报告Bug和功能请求。"> -联系方式页面。"> +联系方式页面。"> diff --git a/chrome/locale/zh-TW/landingpage.dtd b/chrome/locale/zh-TW/landingpage.dtd index fbca95937..d7bb50b64 100644 --- a/chrome/locale/zh-TW/landingpage.dtd +++ b/chrome/locale/zh-TW/landingpage.dtd @@ -11,11 +11,11 @@ -功能說明頁面取得更多資訊。"> +功能說明頁面取得更多資訊。"> -我們的網頁取得更多資訊。"> +我們的網頁取得更多資訊。"> @@ -86,7 +86,7 @@ babelzilla.org 幫忙翻譯這個擴充套件或其他許多優秀的附加元件。"> GPL 2.0 。參見 GNU GPL 的文件。這適用於所有的原始碼檔案。"> -http://code.downthemall.net/repos/ 查看可用的對應檔案倉儲。大多數檔案以 GPL/LGPL-compatible MPL 2.0 授權,或 MPL 1.1/GPL 2.0/LGPL 2.1 三重授權。詳情請參閱個別的 MPL, GPL, LGPL 授權條款文件。"> +https://code.downthemall.org/repos/ 查看可用的對應檔案倉儲。大多數檔案以 GPL/LGPL-compatible MPL 2.0 授權,或 MPL 1.1/GPL 2.0/LGPL 2.1 三重授權。詳情請參閱個別的 MPL, GPL, LGPL 授權條款文件。"> @@ -95,7 +95,7 @@ -Bug 追蹤系統 回報軟體缺陷(Bug)與新功能要求。"> +Bug 追蹤系統 回報軟體缺陷(Bug)與新功能要求。"> -聯絡網頁 ."> +聯絡網頁 ."> diff --git a/chrome/skin/toolbarbuttons/buttons.css b/chrome/skin/toolbarbuttons/buttons.css index 728d75430..d5fa7a147 100644 --- a/chrome/skin/toolbarbuttons/buttons.css +++ b/chrome/skin/toolbarbuttons/buttons.css @@ -97,13 +97,23 @@ toolbar[iconsize="large"] #dta-manager-button { .dta-toolbarbutton { list-style-image: url(buttons32.png) !important; } + .dta-toolbarbutton .toolbarbutton-icon { + width: 16px; + } toolbar[iconsize="large"] .dta-toolbarbutton { list-style-image: url(buttons24.png) !important; } + toolbar[iconsize="large"] .dta-toolbarbutton .toolbarbutton-icon { + width: 24px; + } .dta-toolbarbutton[cui-areatype="menu-panel"], #customization-container .dta-toolbarbutton { list-style-image: url(buttons64.png) !important; } + .dta-toolbarbutton[cui-areatype="menu-panel"] .toolbarbutton-icon, + #customization-container .dta-toolbarbutton .toolbarbutton-icon { + width: 32px; + } #dta-button { -moz-image-region: rect(0px,64px,32px,32px); diff --git a/install.rdf b/install.rdf index 4dcdfbff1..c41689daa 100644 --- a/install.rdf +++ b/install.rdf @@ -1,11 +1,10 @@ - - + - dta@downthemall.net + {DDC359D1-844A-42a7-9AA1-88A850A938A8} DownThemAll! The mass downloader for Firefox. - 3.1.1pre + 3.0.9 true true 2 @@ -19,7 +18,7 @@ chrome://dta/content/about/about.xul chrome://dtaicon/content/icon.png chrome://dtaicon/content/icon64.png - http://downthemall.net/ + https://downthemall.org/ chrome://dta/content/preferences/prefs.xul @@ -27,7 +26,7 @@ {ec8030f7-c20a-464f-9b0e-13a3a9e97384} 45.0 - 51.* + 56.* @@ -36,11 +35,30 @@ {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} 2.40 - 2.48.* + 2.49.* - The mass downloader for Firefox.Federico Parodi, Stefano Verna, Nils Maieren-USDownThemAll!The dTa Teamhttp://downthemall.net/Federico ParodiStefano VernaNils MaierDer Massen-Download-Manager für FirefoxFederico Parodi, Stefano Verna, Nils MaierdeDownThemAll!Nils Maierhttp://downthemall.net/Federico ParodiStefano VernaNils MaierEl descargador masivo para FirefoxFederico Parodi, Stefano Verna, Nils Maieres-ESDownThemAll!Urko - Babelzilla.orghttp://downthemall.net/Federico ParodiStefano VernaNils MaierFailide massallalaadija Firefoxile.Federico Parodi, Stefano Verna, Nils MaieretDownThemAll!Maidurhttp://downthemall.net/Federico ParodiStefano VernaNils MaierL'extension de téléchargement par lot pour Firefox.Federico Parodi, Stefano Verna, Nils MaierfrDownThemAll!L'équipe dTahttp://downthemall.net/Federico ParodiStefano VernaNils MaierDescargador masivo para Firefox.Federico Parodi, Stefano Verna, Nils MaierglDownThemAll!O equipo de dTahttp://downthemall.net/Federico ParodiStefano VernaNils MaierDescargador masivo para Firefox.Federico Parodi, Stefano Verna, Nils Maiergl-ESDownThemAll!O equipo de dTahttp://downthemall.net/Federico ParodiStefano VernaNils MaierLo scaricatore di massa per FirefoxFederico Parodi, Stefano Verna, Nils MaieritDownThemAll!Il team dTa, Alessandro Mentihttp://downthemall.net/Federico ParodiStefano VernaNils MaierDe bulkdownloader voor Firefox.Federico Parodi, Stefano Verna, Nils MaiernlDownThemAll!Het dTa-teamhttp://downthemall.net/Federico ParodiStefano VernaNils MaierMenedżer pobierania plików dla Firefoksa.Federico Parodi, Stefano Verna, Nils MaierplDownThemAll!Leszek(teo)Życzkowskihttp://downthemall.net/Federico ParodiStefano VernaNils MaierO transferidor em massa para o Firefox.Federico Parodi, Stefano Verna, Nils Maierpt-PTDownThemAll!A Equipa dTahttp://downthemall.net/Federico ParodiStefano VernaNils MaierМенеджер загрузок для Firefox.Federico Parodi, Stefano Verna, Nils MaierruDownThemAll!Sergeys - Russian Mozilla Teamhttp://downthemall.net/Federico ParodiStefano VernaNils MaierMnožični prenašalnik za Firefox.Federico Parodi, Stefano Verna, Nils Maiersl-SIDownThemAll!Ekipa dTahttp://downthemall.net/Federico ParodiStefano VernaNils MaierFirefox için toplu indirme aracı.Federico Parodi, Stefano Verna, Nils MaiertrDownThemAll!S.K.Yerli ve dTA takımıhttp://downthemall.net/Federico ParodiStefano VernaNils MaierFirefox 的批量下载工具。Federico Parodi, Stefano Verna, Nils Maierzh-CNDownThemAll!Cye3s & Blackohttp://downthemall.net/Federico ParodiStefano VernaNils MaierFirefox 的大量下載工具Federico Parodi, Stefano Verna, Nils Maierzh-TWDownThemAll!rayx000, scsi, tiffblue, Sakura3814, Goldie Lin.http://downthemall.net/Federico ParodiStefano VernaNils Maier - + en-USDownThemAll!The mass downloader for Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierThe dTa Team + deDownThemAll!Der Massen-Download-Manager für FirefoxFederico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierNils Maier + es-ESDownThemAll!El descargador masivo para FirefoxFederico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierUrko - Babelzilla.org + etDownThemAll!Failide massallalaadija Firefoxile.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierMaidur + frDownThemAll!L'extension de téléchargement par lot pour Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierL'équipe dTa + glDownThemAll!Descargador masivo para Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierO equipo de dTa + gl-ESDownThemAll!Descargador masivo para Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierO equipo de dTa + idDownThemAll!Pengunduh banyak berkas untuk Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierTim dTa + itDownThemAll!Lo scaricatore di massa per FirefoxFederico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierIl team dTa, Alessandro Menti + jaDownThemAll!The mass downloader for Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierThe dTa Team + nlDownThemAll!De bulkdownloader voor Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierHet dTa-team + plDownThemAll!Menedżer pobierania plików dla Firefoksa.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierLeszek(teo)Życzkowski + pt-BRDownThemAll!O gerenciador de downloads em massa do Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierA Equipe dTa + pt-PTDownThemAll!O transferidor em massa para o Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierA Equipa dTa + roDownThemAll!Gestionează descărcarea în masă pentru Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierCătălin Zamfirescu (x10firefox) + ruDownThemAll!Менеджер загрузок для Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierSergeys - Russian Mozilla Team + sl-SIDownThemAll!Množični prenašalnik za Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierEkipa dTa + sv-SEDownThemAll!Massfilhämtaren för Firefox.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierdTa-teamet + trDownThemAll!Firefox için toplu indirme aracı.Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierS.K.Yerli ve dTA takımı + zh-CNDownThemAll!Firefox 的批量下载工具。Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils MaierCye3s & Blacko + zh-TWDownThemAll!Firefox 的大量下載工具Federico Parodi, Stefano Verna, Nils Maierhttps://downthemall.org/Federico ParodiStefano VernaNils Maierrayx000, scsi, tiffblue, Sakura3814, Goldie Lin. diff --git a/modules/api.js b/modules/api.js index b0f205221..0e962cc6f 100644 --- a/modules/api.js +++ b/modules/api.js @@ -30,56 +30,54 @@ function _decodeCharset(text, charset) { } return rv; } -class URL { - constructor(url, preference, _fast) { - this.preference = preference || 100; +function URL(url, preference, _fast) { + this.preference = preference || 100; - if (!(url instanceof Ci.nsIURL)) { - throw new Exception("You must pass a nsIURL"); - } - if (!_fast && URL.schemes.indexOf(url.scheme) === -1) { - throw new Exception("Not a supported URL"); - } + if (!(url instanceof Ci.nsIURL)) { + throw new Exception("You must pass a nsIURL"); + } + if (!_fast && URL.schemes.indexOf(url.scheme) === -1) { + throw new Exception("Not a supported URL"); + } - this._url = url.clone(); - if (!_fast) { - let hash = exports.getLinkPrintHash(this._url); - this._url.ref = ''; - if (hash) { - this.hash = hash; - } + this._url = url.clone(); + if (!_fast) { + let hash = exports.getLinkPrintHash(this._url); + this._url.ref = ''; + if (hash) { + this.hash = hash; } - lazy(this, "urlCharset", this._getCharset); - lazy(this, "spec", this._getSpec); - lazy(this, "usable", this._getUsable); } - - _getCharset() { + lazy(this, "urlCharset", this._getCharset); + lazy(this, "spec", this._getSpec); + lazy(this, "usable", this._getUsable); +} +URL.schemes = ['http', 'https', 'ftp']; +URL.prototype = Object.freeze({ + _getCharset: function() { return this._url.originCharset; - } - _getSpec() { + }, + _getSpec: function() { return this._url.spec; - } - _getUsable() { + }, + _getUsable: function() { return _decodeCharset(this.spec, this._urlCharset); - } + }, get url() { return this._url; - } - toJSON() { + }, + toJSON: function() { return { url: this.spec, charset: this.urlCharset, preference: this.preference }; - } - - toString() { + }, + toString: function() { return this.usable; } -}; -URL.schemes = ['http', 'https', 'ftp']; +}); exports.URL = Object.freeze(URL); /** @@ -121,109 +119,95 @@ exports.WANT_DIGEST_STRING = (function() { return rv.toString(); })(); -class Hash { - constructor(hash, type) { - if (typeof(hash) !== 'string' && !(hash instanceof String)) { - throw new Error("hash is invalid"); - } - if (typeof(type) !== 'string' && !(type instanceof String)) { - throw new Error("hashtype is invalid"); - } - - type = type.toUpperCase().replace(/[\s-]/g, ''); - if (!(type in SUPPORTED_HASHES_ALIASES)) { - throw new Error("hashtype is invalid: " + type); - } - this.type = SUPPORTED_HASHES_ALIASES[type]; - this.sum = hash.toLowerCase().replace(/\s/g, ''); - let h = SUPPORTED_HASHES[this.type]; - if (h.l !== this.sum.length || isNaN(parseInt(this.sum, 16))) { - throw new Error("hash is invalid"); - } - this._q = h.q; +function Hash(hash, type) { + if (typeof(hash) !== 'string' && !(hash instanceof String)) { + throw new Exception("hash is invalid"); } - - get q() { - return this._q; + if (typeof(type) !== 'string' && !(type instanceof String)) { + throw new Exception("hashtype is invalid"); } - toString() { - return `[Hash(${this.type}, ${this.sum})]`; + type = type.toUpperCase().replace(/[\s-]/g, ''); + if (!(type in SUPPORTED_HASHES_ALIASES)) { + throw new Exception("hashtype is invalid: " + type); } - - toJSON() { + this.type = SUPPORTED_HASHES_ALIASES[type]; + this.sum = hash.toLowerCase().replace(/\s/g, ''); + let h = SUPPORTED_HASHES[this.type]; + if (h.l !== this.sum.length || isNaN(parseInt(this.sum, 16))) { + throw new Exception("hash is invalid"); + } + this._q = h.q; +} +Hash.prototype = Object.freeze({ + get q() { + return this._q; + }, + toString: function() { + return this.type + " [" + this.sum + "]"; + }, + toJSON: function() { return { type: this.type, sum: this.sum }; } -} +}); exports.Hash = Object.freeze(Hash); /** * Collection of hashes (checksums) about a single download * @param fullHash Full hash covering the whole download */ -class HashCollection { - constructor(fullHash) { - if (!(fullHash instanceof Hash)) { - throw new Error("Cannot init empty HashCollection"); - } - this.full = fullHash; - this.parLength = 0; - this.partials = []; - this._serialize(); - } - - /** - * Load HashCollection from a serialized object - * @see serialize - * @param obj (object) Serialized object - */ - static load(obj) { - let rv = new HashCollection(new Hash(obj.full.sum, obj.full.type)); - rv.parLength = obj.parLength || 0; - for (let e of obj.partials) { - rv.add(new Hash(e.sum, e.type)); - } - rv._serialize(); - return rv; +function HashCollection(fullHash) { + if (!(fullHash instanceof Hash)) { + throw new Exception("Cannot init empty HashCollection"); } + this.full = fullHash; + this.partials = []; + this._serialize(); +} +/** + * Load HashCollection from a serialized object + * (Static) + * @see serialize + * @param obj (object) Serialized object + */ +HashCollection.load = function(obj) { + let rv = new HashCollection(new Hash(obj.full.sum, obj.full.type)); + rv.parLength = obj.parLength ? obj.parLength : 0; + rv.partials = obj.partials.map(e => new Hash(e.sum, e.type)); + rv._serialize(); + return rv; +}; +HashCollection.prototype = Object.freeze({ /** * HashCollection has parital hashes */ - get hasPartials() { - return !!this.partials.length; - } - - add(hash) { + get hasPartials() { return !!this.partials.length; }, + add: function(hash) { if (!(hash instanceof Hash)) { throw new Exception("Must supply hash"); } this.partials.push(hash); this._serialize(); - } - + }, /** * Serializes HashCollection * @return (object) Serialized HashCollection */ - toJSON() { + toJSON: function() { return this._serialized; - } - _serialize() { + }, + _serialize: function() { this._serialized = { full: this.full, parLength: this.parLength, partials: this.partials }; } - - toString() { - return `[HashCollection(${this.toJSON()})]`; - } -} +}); exports.HashCollection = Object.freeze(HashCollection); const _rglph = /^hash\((md5|sha(?:-?(?:1|256|384|512))?):([\da-f]+)\)$/i; @@ -463,8 +447,6 @@ exports.turboSaveLinkArray = function turboSaveLinkArray(window, urls, images, c var isManagerPending = false; var managerRequests = []; - -// jshint -W003 function openManagerCallback(event) { log(LOG_DEBUG, "manager ready; pushing queued items"); event.target.removeEventListener("DTA:dieEarly", openManagerDiedCallback, true); @@ -475,7 +457,6 @@ function openManagerCallback(event) { managerRequests.length = 0; isManagerPending = false; } - function openManagerDiedCallback(event) { event.target.removeEventListener("DTA:dieEarly", openManagerDiedCallback, true); event.target.removeEventListener("DTA:ready", openManagerCallback, true); @@ -486,7 +467,6 @@ function openManagerDiedCallback(event) { exports.openManager(); } } -// jshint +W003 exports.openManager = function openManager(window, quiet, cb) { try { diff --git a/modules/api.jsm b/modules/api.jsm index 6d0a32d33..4b7742c95 100644 --- a/modules/api.jsm +++ b/modules/api.jsm @@ -2,25 +2,22 @@ * 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"; -/* global Cu:true */ +/*globals Cu:true */ const Cu = Components.utils; const EXPORTED_SYMBOLS = []; Cu.import("chrome://dta-modules/content/glue.jsm"); let api = require("api"); -let wrap = (_k, _v) => { - return function() { - Cu.reportError("deprecated DownThemAll! API access; see glue.jsm/require(). Symbol was " + _k); - return _v; - }; -}; - for (let [k,v] in Iterator(api)) { let [_k,_v] = [k,v]; + /* jshint -W083 */ Object.defineProperty(this, _k, { - get: wrap(_k, _v), + get: function() { + Cu.reportError("deprecated DownThemAll! API access; see glue.jsm/require(). Symbol was " + _k); + return _v; + }, enumerable: true }); EXPORTED_SYMBOLS.push(_k); diff --git a/modules/glue.jsm b/modules/glue.jsm index 5747caa01..04bcdba0c 100644 --- a/modules/glue.jsm +++ b/modules/glue.jsm @@ -2,9 +2,10 @@ * 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"; -/* globals Services:true, Instances: true, Cr:true, Cc:true, Ci:true, Cu:true, Cm:true, ctor:true, Exception:true */ -/* globals requireJoined:true, weak:true, lazy:true, reportError:true, QI:true, lazyProto:true, LRUMap:true */ -/* globals log:true, LOG_DEBUG:true, LOG_INFO:true, LOG_ERROR:true */ +/* jshint strict:false */ +/* jshint globalstrict:true */ +/* jshint -W079, -W003, -W083, -W116 */ +/* global Services:true */ var EXPORTED_SYMBOLS = [ "require", @@ -37,34 +38,12 @@ Cm.QueryInterface(Ci.nsIComponentRegistrar); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.importGlobalProperties([ - "atob", - "btoa", - "Blob", - "crypto", - "fetch", - "File", - "TextDecoder", - "TextEncoder", - "URL", - "URLSearchParams", - "XMLHttpRequest", -]); var weak = Cu.getWeakReference.bind(Cu); var reportError = Cu.reportError.bind(Cu); var lazy = XPCOMUtils.defineLazyGetter; // bind? var QI = XPCOMUtils.generateQI.bind(XPCOMUtils); -var log = function logStub(...args) { - Cu.reportError(Array.join(args, ", ")); -}; - -var LOG_DEBUG = 0, LOG_INFO = 0, LOG_ERROR = 0; -var Instances; - -const DEAD = Symbol(); - function canUnload() { let cancel = new Instances.SupportsBool(); cancel.data = false; @@ -95,46 +74,59 @@ var lazyProto = (function() { }; })(); -class LRUMap extends Map { - constructor(limit, values) { - if (!(limit > 1) || (limit !== (limit | 0))) { - throw new Error("Invalid limit"); - } - super(values); - Object.defineProperty(this, "_limit", {value: limit}); - } - get limit() { - return this._limit; - } - get capacity() { - return this._limit; - } - get free() { - return this._limit - this.size; +var log = function logStub() { + Cu.reportError(Array.join(arguments, ", ")); +}; +var LOG_DEBUG = 0, LOG_INFO = 0, LOG_ERROR = 0; + +const DEAD = Symbol(); + +var LRUMap = function LRUMap(limit, values) { + if (!(limit > 1) || (limit !== (limit | 0))) { + throw new Error("Invalid limit"); } + this._limit = limit; + this.clear(); + Object.preventExtensions(this); - "set"(key, val) { + for (let i = (values || DEAD).length - 1; i >= 0; --i) { + Cu.reportError(i); + this.set(values[i][0], values[i][1]); + } +}; +LRUMap.prototype = Object.freeze({ + "get": function(key) { return this._dict.get(key); }, + "has": function(key) { return this._dict.has(key); }, + "set": function(key, val) { if (this.has(key)) { - super.delete(key); - return super.set(key, val); + this._dict.set(key, val); + return; } - if (this.size === this._limit) { - this.delete(this.keys().next().value); + if (this._arr.length === this._limit) { + this._dict.delete(this._arr.shift()); } - return super.set(key, val); - } - /** - * Serialize to JSON (via JSON.stringify) - * Please not that it is serialized to an array containing key/value pairs. - * Please note that therefore keys and values need to be serializable with - * JSON. - * Please note that the limit is not imcluded! - */ - toJSON() { - return Array.from(this.entries()); + this._dict.set(key, val); + this._arr.push(key); + }, + "delete": function(key) { + if (!this._dict.has(key)) { + return; + } + this._dict.delete(key); + this._arr.splice(this._arr.indexOf(key), 1); + }, + "clear": function() { + this._dict = new Map(); + this._arr = []; + }, + toJSON: function() { + let rv = []; + for (let i of this._arr) { + rv.push([i, this._dict.get(i)]); + } + return rv; } -}; -this.LRUMap = LRUMap; +}); //hide our internals //Since require() uses .scriptloader, the loaded require scopes will have @@ -222,9 +214,10 @@ this.LRUMap = LRUMap; dlsg("pps", "@mozilla.org/network/protocol-proxy-service;1", "nsIProtocolProxyService"); dlsg("sysprincipal", "@mozilla.org/systemprincipal;1", "nsIPrincipal"); - Instances = exports.Instances = {}; + const Instances = exports.Instances = {}; // non-init + itor("XHR", "@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest"); itor("DOMSerializer", "@mozilla.org/xmlextras/xmlserializer;1", "nsIDOMSerializer"); itor("MimeInputStream", "@mozilla.org/network/mime-input-stream;1", "nsIMIMEInputStream"); itor("SupportsArray","@mozilla.org/supports-array;1", "nsISupportsArray"); @@ -275,8 +268,7 @@ this.LRUMap = LRUMap; var _unloaders = []; let _runUnloader = function _runUnloader(fn, args) { try { - args = args || []; - fn(...args); + fn.apply(null, args); } catch (ex) { try { @@ -307,27 +299,25 @@ this.LRUMap = LRUMap; _runUnloader(_unloaders[i]); } _unloaders.length = 0; - let re = Cu.reportError.bind(Cu); - let ns = Cu.nukeSandbox.bind(Cu); - let nukeDelayed = (p, scope) => { - p.then(function() { - ns(scope); - }, function(ex) { - re(ex); - ns(scope); - }); - }; for (let r of _registry.keys()) { try { let scope = _registry.get(r); if (scope.asyncShutdown) { let p = scope.asyncShutdown(); if (p && p.then) { - nukeDelayed(p, scope); + let re = Cu.reportError.bind(Cu); + let ns = Cu.nukeSandbox.bind(Cu); + p.then(function() { + ns(scope); + }, function(ex) { + re(ex); + ns(scope); + }); continue; } } - ns(scope); + + Cu.nukeSandbox(scope); } catch (ex) {} } @@ -344,22 +334,22 @@ this.LRUMap = LRUMap; return; }; exports.unload = function unload(fn, ...args) { - if (fn === "shutdown") { + if (fn == "shutdown") { return shutdown(args.unshift()); } - if (fn === "eventual-shutdown") { + if (fn == "eventual-shutdown") { _upgrade = args.shift(); return; } // add an unloader - if (typeof(fn) !== "function") { + if (typeof(fn) != "function") { throw new Error("unloader is not a function"); } _unloaders.push(fn); - return function(...args) { - _runUnloader(fn, args); - _unloaders = _unloaders.filter(c => c !== fn); + return function() { + _runUnloader(fn, arguments); + _unloaders = _unloaders.filter(c => c != fn); }; }; @@ -384,30 +374,29 @@ this.LRUMap = LRUMap; }; })(); - let requireJoined; const require = function require(base, module) { let path = module.split("/").filter(e => !!e); if (!path || !path.length) { throw new Error("Invalid module path"); } - if (path[0] === "." || path[0] === "..") { + if (path[0] == "." || path[0] == "..") { path = base.split("/").filter(e => !!e).concat(path); } for (let i = path.length - 2; i >= 0; --i) { - if (path[i] === ".") { + if (path[i] == ".") { path.splice(i, 1); continue; } - if (path[i] !== "..") { + if (path[i] != "..") { continue; } - if (i === 0) { + if (i == 0) { throw new Error("Invalid traversal"); } path.splice(i - 1, 2); } let file = path.pop(); - if (file === ".." || file === ".") { + if (file == ".." || file == ".") { throw new Error("Invalid traversal"); } base = path.join("/"); @@ -494,7 +483,7 @@ this.LRUMap = LRUMap; return (scope.module && scope.module.exports) || scope.exports; }; - requireJoined = function requireJoined(base, where, module) { + const requireJoined = function requireJoined(base, where, module) { module = require(base, module); for (let k of Object.getOwnPropertyNames(module)) { Object.defineProperty(where, k, Object.getOwnPropertyDescriptor(module, k)); @@ -528,7 +517,7 @@ this.LRUMap = LRUMap; exports.require("version").getInfo(function setupVersion(v) { log( LOG_INFO, - `${v.NAME}/${v.VERSION} on ${v.APP_NAME}/${v.APP_VERSION} (${v.LOCALE}/${v.OS}) ready` + v.NAME + "/" + v.VERSION + " on " + v.APP_NAME + "/" + v.APP_VERSION + " (" + v.LOCALE + " / " + v.OS + ") ready" ); }); try { diff --git a/modules/loaders/integration-content.js b/modules/loaders/integration-content.js index 0ae148113..77d986e35 100644 --- a/modules/loaders/integration-content.js +++ b/modules/loaders/integration-content.js @@ -14,7 +14,6 @@ if (!("setTimeout" in this)) { const {classes: Cc, interfaces: Ci, utils: Cu} = Components; let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); -let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); let isWindowPrivate = () => false; try { let {PrivateBrowsingUtils} = Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm", {}); @@ -53,22 +52,15 @@ const require = function require_mini(m) { LOG_INFO: LOG_INFO, LOG_ERROR: LOG_ERROR, Services: Services, - XPCOMUtils: XPCOMUtils, - require: require, unload: function() { log(LOG_DEBUG, "unload stub called"); }, exports: {} }; - scope.module = { - exports: scope.exports, - loaded: false, - require: scope.require - }; let module = "chrome://dta-modules/content/" + m + ".js"; Services.scriptloader.loadSubScript(module, scope); - return (scope.module && scope.module.exports) || scope.exports; + return scope.exports; }; const TextLinks = require("support/textlinks"); diff --git a/modules/loaders/integration.js b/modules/loaders/integration.js index 8b060da6f..818139cae 100644 --- a/modules/loaders/integration.js +++ b/modules/loaders/integration.js @@ -27,6 +27,8 @@ const {unique} = require("support/uniquelinks"); const {unloadWindow} = require("support/overlays"); const strfn = require("support/stringfuncs"); +const {Task} = requireJSM("resource://gre/modules/Task.jsm"); + var findLinksJob = 0; const MENU_ITEMS = [ @@ -122,28 +124,28 @@ function getSniffedInfoFromLocation(l) { */ exports.load = function load(window, outerEvent) { let document = window.document; - let setTimeoutOnlyFun = function(c, ...args) { + let setTimeoutOnlyFun = function(c) { if (typeof(c) !== "function") { throw new Error("do not call me with a string!"); } - return window.setTimeout.call(window, c, ...args); + return window.setTimeout.apply(window, arguments); }; - let setIntervalOnlyFun = function(c, ...args) { + let setIntervalOnlyFun = function(c) { if (typeof(c) !== "function") { throw new Error("do not call me with a string!"); } - return window.setInterval(c, ...args); + return window.setInterval.apply(window, arguments); }; let clearInterval = window.clearInterval; let gBrowser = window.gBrowser; - function $(...args) { - if (args.length === 1) { - return document.getElementById(args[0]); + function $() { + if (arguments.length === 1) { + return document.getElementById(arguments[0]); } let elements = []; - for (let i = 0, e = args.length; i < e; ++i) { - let id = args[i]; + for (let i = 0, e = arguments.length; i < e; ++i) { + let id = arguments[i]; let element = document.getElementById(id); if (element) { elements.push(element); @@ -250,7 +252,7 @@ exports.load = function load(window, outerEvent) { return browsers; } - async function findLinks(turbo, all) { + function findLinks(turbo, all) { try { if (!all && turbo && Preferences.getExt('rememberoneclick', false)) { all = Preferences.getExt('lastalltabs', false); @@ -287,123 +289,125 @@ exports.load = function load(window, outerEvent) { } }), 1750, true); - try { - let promises = []; - let job = findLinksJob++; - for (let b of findBrowsers(all)) { - let browser = b; - promises.push(new Promise((resolve, reject) => { - let progress = m => { - urlsLength += m.data.urls; - imagesLength += m.data.images; - }; - let result = m => { - browser.messageManager.removeMessageListener("DTA:findLinks:progress:" + job, progress); - browser.messageManager.removeMessageListener("DTA:findLinks:" + job, result); - resolve(m.data); - }; - browser.messageManager.addMessageListener("DTA:findLinks:progress:" + job, progress); - browser.messageManager.addMessageListener("DTA:findLinks:" + job, result); - browser.messageManager.sendAsyncMessage("DTA:findLinks", { - job: job, - honorSelection: !all, - recognizeTextLinks: recognizeTextLinks - }); - })); - } + new Task.spawn(function*() { + try { + let promises = []; + let job = findLinksJob++; + for (let b of findBrowsers(all)) { + let browser = b; + promises.push(new Promise((resolve, reject) => { + let progress = m => { + urlsLength += m.data.urls; + imagesLength += m.data.images; + }; + let result = m => { + browser.messageManager.removeMessageListener("DTA:findLinks:progress:" + job, progress); + browser.messageManager.removeMessageListener("DTA:findLinks:" + job, result); + resolve(m.data); + }; + browser.messageManager.addMessageListener("DTA:findLinks:progress:" + job, progress); + browser.messageManager.addMessageListener("DTA:findLinks:" + job, result); + browser.messageManager.sendAsyncMessage("DTA:findLinks", { + job: job, + honorSelection: !all, + recognizeTextLinks: recognizeTextLinks + }); + })); + } - let nonnull = function(e) { - return !!e; - }; - let transposeURIs = function(e) { - try { - e.url = makeURI(e.url); - if (!e.url) { + let nonnull = function(e) { + return !!e; + }; + let transposeURIs = function(e) { + try { + e.url = makeURI(e.url); + if (!e.url) { + return null; + } + e.referrer = makeURI(e.referrer); + return e; + } + catch (ex) { return null; } - e.referrer = makeURI(e.referrer); - return e; - } - catch (ex) { - return null; - } - }; - for (let p of promises) { - let {urls, images, locations} = await p; - if (!urls.length && !images.length && !locations.length) { - continue; - } - collectedUrls = collectedUrls.concat(mapFilterInSitu(urls, transposeURIs, nonnull)); - collectedImages = collectedImages.concat(mapFilterInSitu(images, transposeURIs, nonnull)); - for (let l of locations) { - let sniffed = getSniffedInfoFromLocation(l); - for (let s of sniffed) { - let o = { - "url": new DTA.URL(s.url), - "fileName": s.name, - "referrer": s.ref, - "description": bundle.getString('sniffedvideo') - }; - collectedUrls.push(o); - collectedImages.push(o); + }; + for (let p of promises) { + let {urls, images, locations} = yield p; + if (!urls.length && !images.length && !locations.length) { + continue; + } + collectedUrls = collectedUrls.concat(mapFilterInSitu(urls, transposeURIs, nonnull)); + collectedImages = collectedImages.concat(mapFilterInSitu(images, transposeURIs, nonnull)); + for (let l of locations) { + let sniffed = getSniffedInfoFromLocation(l); + for (let s of sniffed) { + let o = { + "url": new DTA.URL(s.url), + "fileName": s.name, + "referrer": s.ref, + "description": bundle.getString('sniffedvideo') + }; + collectedUrls.push(o); + collectedImages.push(o); + } } } - } - unique(collectedUrls); - for (let e of collectedUrls) { - if (!e.description) { - e.description = e.defaultDescription || ""; + unique(collectedUrls); + for (let e of collectedUrls) { + if (!e.description) { + e.description = e.defaultDescription || ""; + } + delete e.defaultDescription; + e.description = identity(e.description); } - delete e.defaultDescription; - e.description = identity(e.description); - } - unique(collectedImages); - for (let e of collectedImages) { - if (!e.description) { - e.description = e.defaultDescription || ""; + unique(collectedImages); + for (let e of collectedImages) { + if (!e.description) { + e.description = e.defaultDescription || ""; + } + delete e.defaultDescription; + e.description = identity(e.description); } - delete e.defaultDescription; - e.description = identity(e.description); - } - // clean up the "hint" notification from above - clearInterval(_updateInterval); - notifyProgress(); - - log(LOG_DEBUG, "findLinks(): finishing..."); - if (!collectedUrls.length && !collectedImages.length) { - notifyError(bundle.getString('error'), bundle.getString('error.nolinks')); - return; - } + // clean up the "hint" notification from above + clearInterval(_updateInterval); + notifyProgress(); - DTA.setPrivateMode(window, collectedUrls); - DTA.setPrivateMode(window, collectedImages); + log(LOG_DEBUG, "findLinks(): finishing..."); + if (!collectedUrls.length && !collectedImages.length) { + notifyError(bundle.getString('error'), bundle.getString('error.nolinks')); + return; + } - if (turbo) { - DTA.turboSaveLinkArray(window, collectedUrls, collectedImages, function(queued) { - if (!queued) { - DTA.saveLinkArray( - window, - collectedUrls, - collectedImages, - bundle.getString('error.information') - ); - } - else if (typeof queued === 'number') { - notifyInfo(bundle.getFormattedString('queuedn', [queued])); - } - else { - notifyInfo(bundle.getFormattedString('queued', [queued.url])); - } - }); - return; + DTA.setPrivateMode(window, collectedUrls); + DTA.setPrivateMode(window, collectedImages); + + if (turbo) { + DTA.turboSaveLinkArray(window, collectedUrls, collectedImages, function(queued) { + if (!queued) { + DTA.saveLinkArray( + window, + collectedUrls, + collectedImages, + bundle.getString('error.information') + ); + } + else if (typeof queued === 'number') { + notifyInfo(bundle.getFormattedString('queuedn', [queued])); + } + else { + notifyInfo(bundle.getFormattedString('queued', [queued.url])); + } + }); + return; + } + DTA.saveLinkArray(window, collectedUrls, collectedImages); } - DTA.saveLinkArray(window, collectedUrls, collectedImages); - } - catch (ex) { - log(LOG_ERROR, "findLinksTask", ex); - } + catch (ex) { + log(LOG_ERROR, "findLinksTask", ex); + } + }.bind(this)); } catch(ex) { log(LOG_ERROR, 'findLinks', ex); @@ -438,105 +442,109 @@ exports.load = function load(window, outerEvent) { } } - async function saveSingleLinkAsync(turbo, what, target, linkhint) { - try { - let data = await getMethod("saveTarget", {what:what, linkhint: linkhint}, target); - let url = makeURI(data, true); - if (!url) { - throw new Error("invalid URL"); - } - let ref = makeURI(data.ref); + function saveSingleLinkAsync(turbo, what, target, linkhint) { + Task.spawn(function*() { + try { + let data = yield getMethod("saveTarget", {what:what, linkhint: linkhint}, target); + let url = makeURI(data, true); + if (!url) { + throw new Error("invalid URL"); + } + let ref = makeURI(data.ref); - const item = { - url: url, - description: data.desc || trimMore(data.title || ""), - referrer: ref, - isPrivate: isWindowPrivate(window) - }; - if (data.download) { - data.download = data.download.trim(); + const item = { + url: url, + description: data.desc || trimMore(data.title || ""), + referrer: ref, + isPrivate: isWindowPrivate(window) + }; if (data.download) { - item.fileName = data.download; - } - } - if (turbo) { - try { - DTA.saveSingleItem(window, true, item); - notifyInfo(bundle.getFormattedString('queued', [url])); - return; + data.download = data.download.trim(); + if (data.download) { + item.fileName = data.download; + } } - catch (ex) { - log(LOG_ERROR, 'saveSingleLink', ex); - notifyError(bundle.getString('error'), bundle.getString('error.information')); + if (turbo) { + try { + DTA.saveSingleItem(window, true, item); + notifyInfo(bundle.getFormattedString('queued', [url])); + return; + } + catch (ex) { + log(LOG_ERROR, 'saveSingleLink', ex); + notifyError(bundle.getString('error'), bundle.getString('error.information')); + } } + DTA.saveSingleItem(window, false, item); } - DTA.saveSingleItem(window, false, item); - } - catch (ex) { - log(LOG_ERROR, "Failed to process single link", ex); - notifyError(bundle.getString('error'), bundle.getString('errorcannotdownload')); - } + catch (ex) { + log(LOG_ERROR, "Failed to process single link", ex); + notifyError(bundle.getString('error'), bundle.getString('errorcannotdownload')); + } + }); } - async function findForm(turbo) { - try { - let data = await getFormData(window.gContextMenu.target); + function findForm(turbo) { + Task.spawn(function*() { + try { + let data = yield getFormData(window.gContextMenu.target); - let action = makeURI(data); - if (!action) { - throw new Error("Invalid Form URL"); - } + let action = makeURI(data); + if (!action) { + throw new Error("Invalid Form URL"); + } - if (data.method === 'post') { - let ss = new Instances.StringInputStream(data.values, -1); - let ms = new Instances.MimeInputStream(); - ms.addContentLength = true; - ms.addHeader('Content-Type', 'application/x-www-form-urlencoded'); - ms.setData(ss); - - let sis = new Instances.ScriptableInputStream(ms); - let postData = ''; - let avail = 0; - while ((avail = sis.available())) { - postData += sis.read(avail); + if (data.method === 'post') { + let ss = new Instances.StringInputStream(data.values, -1); + let ms = new Instances.MimeInputStream(); + ms.addContentLength = true; + ms.addHeader('Content-Type', 'application/x-www-form-urlencoded'); + ms.setData(ss); + + let sis = new Instances.ScriptableInputStream(ms); + let postData = ''; + let avail = 0; + while ((avail = sis.available())) { + postData += sis.read(avail); + } + sis.close(); + ms.close(); + ss.close(); + + action.postData = postData; + } + else { + action.url.query = data.values; + action.url.ref = ''; } - sis.close(); - ms.close(); - ss.close(); - action.postData = postData; - } - else { - action.url.query = data.values; - action.url.ref = ''; - } + let ref = makeURI(data.ref); + let defaultDescription = trimMore(data.title || ""); + let desc = data.desc || defaultDescription; - let ref = makeURI(data.ref); - let defaultDescription = trimMore(data.title || ""); - let desc = data.desc || defaultDescription; - - let item = { - "url": action, - "referrer": ref, - "description": desc, - "isPrivate": isWindowPrivate(window) - }; + let item = { + "url": action, + "referrer": ref, + "description": desc, + "isPrivate": isWindowPrivate(window) + }; - if (turbo) { - try { - DTA.saveSingleItem(window, true, item); - return; - } - catch (ex) { - log(LOG_ERROR, 'findSingleLink', ex); - notifyError(bundle.getString('error'), bundle.getString('error.information')); + if (turbo) { + try { + DTA.saveSingleItem(window, true, item); + return; + } + catch (ex) { + log(LOG_ERROR, 'findSingleLink', ex); + notifyError(bundle.getString('error'), bundle.getString('error.information')); + } } + DTA.saveSingleItem(window, false, item); } - DTA.saveSingleItem(window, false, item); - } - catch (ex) { - log(LOG_ERROR, 'findForm', ex); - } + catch (ex) { + log(LOG_ERROR, 'findForm', ex); + } + }); } let notifyProgress = function(message) { @@ -819,7 +827,7 @@ exports.load = function load(window, outerEvent) { } } - async function onDTAShowing(evt) { + function onDTAShowing(evt) { let menu = evt.target; for (let n of menu.querySelectorAll(".dta-sniff-element")) { n.parentNode.removeChild(n); @@ -827,79 +835,83 @@ exports.load = function load(window, outerEvent) { if (!Preferences.getExt('listsniffedvideos', false)) { return; } - const locations = await getCurrentLocations(); - let sniffed = []; - for (let l of locations) { - sniffed = sniffed.concat(getSniffedInfoFromLocation(l)); - } - if (!sniffed.length) { - return; - } + Task.spawn(function*() { + const locations = yield getCurrentLocations(); + let sniffed = []; + for (let l of locations) { + sniffed = sniffed.concat(getSniffedInfoFromLocation(l)); + } + if (!sniffed.length) { + return; + } - let sep = document.createElement("menuseparator"); - sep.className = "dta-sniff-element"; - menu.appendChild(sep); - - let cmd = menu.parentNode.getAttribute("buttoncommand") + "-sniff"; - for (let s of sniffed) { - let o = { - "url": new DTA.URL(s.url), - "referrer": s.ref, - "fileName": s.name, - "description": bundle.getString("sniffedvideo"), - "isPrivate": isWindowPrivate(window) - }; - let mi = document.createElement("menuitem"); - mi.setAttribute("label", strfn.cropCenter(s.name, 60)); - mi.setAttribute("tooltiptext", o.url.spec); - mi.setAttribute("image", getIcon(s.name)); - mi.setAttribute("command", cmd); - mi.info = o; - mi.className = "dta-sniff-element menuitem-iconic"; - menu.appendChild(mi); - } + let sep = document.createElement("menuseparator"); + sep.className = "dta-sniff-element"; + menu.appendChild(sep); + + let cmd = menu.parentNode.getAttribute("buttoncommand") + "-sniff"; + for (let s of sniffed) { + let o = { + "url": new DTA.URL(s.url), + "referrer": s.ref, + "fileName": s.name, + "description": bundle.getString("sniffedvideo"), + "isPrivate": isWindowPrivate(window) + }; + let mi = document.createElement("menuitem"); + mi.setAttribute("label", strfn.cropCenter(s.name, 60)); + mi.setAttribute("tooltiptext", o.url.spec); + mi.setAttribute("image", getIcon(s.name)); + mi.setAttribute("command", cmd); + mi.info = o; + mi.className = "dta-sniff-element menuitem-iconic"; + menu.appendChild(mi); + } + }); } - async function onDTAViewShowing(button, view) { + function onDTAViewShowing(button, view) { for (let n of view.querySelectorAll(".dta-sniff-element")) { n.parentNode.removeChild(n); } if (!Preferences.getExt('listsniffedvideos', false)) { return; } - const locations = await getCurrentLocations(); - let sniffed = []; - for (let l of locations) { - sniffed = sniffed.concat(getSniffedInfoFromLocation(l)); - } - if (!sniffed.length) { - return; - } + Task.spawn(function*() { + const locations = yield getCurrentLocations(); + let sniffed = []; + for (let l of locations) { + sniffed = sniffed.concat(getSniffedInfoFromLocation(l)); + } + if (!sniffed.length) { + return; + } - let menu = view.querySelector(".panel-subview-body"); + let menu = view.querySelector(".panel-subview-body"); - let sep = document.createElement("menuseparator"); - sep.className = "dta-sniff-element"; - menu.appendChild(sep); + let sep = document.createElement("menuseparator"); + sep.className = "dta-sniff-element"; + menu.appendChild(sep); - let cmd = button.getAttribute("buttoncommand") + "-sniff"; - for (let s of sniffed) { - let o = { - "url": new DTA.URL(s.url), - "referrer": s.ref, - "fileName": s.name, - "description": bundle.getString("sniffedvideo"), - "isPrivate": isWindowPrivate(window) - }; - let mi = document.createElement("toolbarbutton"); - mi.setAttribute("label", strfn.cropCenter(s.name, 60)); - mi.setAttribute("tooltiptext", o.url.spec); - mi.setAttribute("image", getIcon(s.name)); - mi.setAttribute("command", cmd); - mi.info = o; - mi.className = "dta-sniff-element subviewbutton cui-withicon"; - menu.appendChild(mi); - } + let cmd = button.getAttribute("buttoncommand") + "-sniff"; + for (let s of sniffed) { + let o = { + "url": new DTA.URL(s.url), + "referrer": s.ref, + "fileName": s.name, + "description": bundle.getString("sniffedvideo"), + "isPrivate": isWindowPrivate(window) + }; + let mi = document.createElement("toolbarbutton"); + mi.setAttribute("label", strfn.cropCenter(s.name, 60)); + mi.setAttribute("tooltiptext", o.url.spec); + mi.setAttribute("image", getIcon(s.name)); + mi.setAttribute("command", cmd); + mi.info = o; + mi.className = "dta-sniff-element subviewbutton cui-withicon"; + menu.appendChild(mi); + } + }); } function attachOneClick() { @@ -942,7 +954,7 @@ exports.load = function load(window, outerEvent) { // The remote site does not get special privileges! try { if (!/^about:downthemall/.test(event.target.location) && - event.target.location.host !== "about.downthemall.net") { + event.target.location.host !== "about.downthemall.org") { return; } } @@ -997,21 +1009,23 @@ exports.load = function load(window, outerEvent) { log(LOG_ERROR, "failed to process ondragover", ex); } } - async function ondrop(event) { - try { - let url = event.dataTransfer.getData("URL"); - if (!url) { - return; + function ondrop(event) { + Task.spawn(function*() { + try { + let url = event.dataTransfer.getData("URL"); + if (!url) { + return; + } + url = Services.io.newURI(url, null, null); + url = new DTA.URL(DTA.getLinkPrintMetalink(url) || url); + let {ref, title} = yield getFocusedDetails(); + ref = makeURI(ref); + func(url, ref); } - url = Services.io.newURI(url, null, null); - url = new DTA.URL(DTA.getLinkPrintMetalink(url) || url); - let {ref, title} = await getFocusedDetails(); - ref = makeURI(ref); - func(url, ref); - } - catch (ex) { - log(LOG_ERROR, "failed to process ondrop", ex); - } + catch (ex) { + log(LOG_ERROR, "failed to process ondrop", ex); + } + }); } elem.addEventListener("dragover", ondragover, true); elem.addEventListener("drop", ondrop, true); @@ -1021,10 +1035,10 @@ exports.load = function load(window, outerEvent) { }); } - function $t(...args) { + function $t() { let rv = []; - for (let i = 0, e = args.length; i < e; ++i) { - let id = args[i]; + for (let i = 0, e = arguments.length; i < e; ++i) { + let id = arguments[i]; let element = document.getElementById(id); if (element) { rv.push(element); diff --git a/modules/loaders/saveas.js b/modules/loaders/saveas.js index 593b6fac7..a37b86f17 100644 --- a/modules/loaders/saveas.js +++ b/modules/loaders/saveas.js @@ -16,13 +16,13 @@ lazy(this, "isWindowPrivate", () => require("support/pbm").isWindowPrivate); * Loader */ function load(window, document) { - function $(...args) { - if (args.length === 1) { - return document.getElementById(args[0]); + function $() { + if (arguments.length === 1) { + return document.getElementById(arguments[0]); } let elements = []; - for (let i = 0, e = args.length; i < e; ++i) { - let id = args[i]; + for (let i = 0, e = arguments.length; i < e; ++i) { + let id = arguments[i]; let element = document.getElementById(id); if (element) { elements.push(element); @@ -87,6 +87,7 @@ function load(window, document) { log(LOG_DEBUG, "save-as reverted!"); }; + let url, referrer, mask, isPrivate; let download = turbo => { @@ -142,7 +143,7 @@ function load(window, document) { value: function(...args) { log(LOG_DEBUG, "initDialog called"); try { - return dialog.initDialog(...args); + return dialog.initDialog.apply(dialog, args); } finally { revertUI(); diff --git a/modules/loaders/selector.js b/modules/loaders/selector.js index f5309e1f1..bf80174b5 100644 --- a/modules/loaders/selector.js +++ b/modules/loaders/selector.js @@ -5,6 +5,125 @@ /* globals addEventListener, removeEventListener, setTimeout, content */ +function Selector(bgimages, handler) { + this._callback = evt => { + return this.onClickOneClick(evt); + }; + this._handler = handler; + + addEventListener('click', this._callback, true); + addEventListener('mouseup', this._callback, false); + addEventListener('mousemove', this._callback, false); + this.observe(bgimages); +} + +Selector.prototype = { + dispose: function() { + removeEventListener('click', this._callback, true); + removeEventListener('mouseup', this._callback, false); + removeEventListener('mousemove', this._callback, false); + this.detachHilight(); + }, + detachHilight: function () { + if (this._hilight) { + this._hilight.hide(); + delete this._hilight; + } + }, + getBgImage: function(e) { + if (!e || !e.ownerDocument) { + return null; + } + let url = e.ownerDocument.defaultView.getComputedStyle(e, "").getPropertyCSSValue('background-image'); + if (url && url.primitiveType === content.CSSPrimitiveValue.CSS_URI) { + return {elem: e, url: url.getStringValue()}; + } + return this.getBgImage(e.parentNode); + }, + findElemUnderCursor: function (e, n, a) { + if (n === 'bgimg') { + return this.getBgImage(e); + } + if (!e || !e.localName) { + return null; + } + if (e.localName.toLowerCase() === n && e[a]) { + if (n === "a") { + return {elem: e, url: e[a], download: e.getAttribute("download")}; + } + return {elem: e, url: e[a] }; + } + return this.findElemUnderCursor(e.parentNode, n, a); + }, + cancelEvent: function (evt) { + if (!evt.cancelable) { + return; + } + evt.preventDefault(); + evt.stopPropagation(); + }, + onClickOneClick: function(evt) { + let target = evt.target; + let doc = target.ownerDocument; + + function processRegular(e) { + let m = this.findElemUnderCursor(target, e[0], e[1]); + if (!m) { + return false; + } + try { + if (!this._handler(doc, m)) { + return false; + } + this.detachHilight(); + new Flasher(m.elem).hide(); + return true; + } + catch (ex) { + log(LOG_ERROR, "processRegular", ex); + } + return false; + } + function highlightElement(e) { + let m = this.findElemUnderCursor(target, e[0], e[1]); + if (!m) { + return false; + } + if (this._hilight && this._hilight.elem === m.elem) { + return true; + } + this.detachHilight(); + this._hilight = new Highlighter(m.elem); + return true; + } + + if (evt.type === 'click') { + if (evt.button === 0 && !!target && + target.nodeType === 1 && + (!target.namespaceURI || target.namespaceURI === 'http://www.w3.org/1999/xhtml')) { + if (this._searchee.some(processRegular, this)) { + this.cancelEvent(evt); + } + } + } + else if (evt.type === 'mousemove') { + if (!this._searchee.some(highlightElement, this)) { + this.detachHilight(); + } + } + }, + observe: function(bgimgs) { + let searchee = [ + ['a', 'href'], + ['img', 'src'] + ]; + if (bgimgs) { + searchee.push(['bgimg', 'bgimg']); + } + this._searchee = searchee; + } +}; + function Flasher(elem) { this.elem = elem; this.doc = elem.ownerDocument; @@ -169,124 +288,4 @@ Highlighter.prototype = { } }; - -function Selector(bgimages, handler) { - this._callback = evt => { - return this.onClickOneClick(evt); - }; - this._handler = handler; - - addEventListener('click', this._callback, true); - addEventListener('mouseup', this._callback, false); - addEventListener('mousemove', this._callback, false); - this.observe(bgimages); -} - -Selector.prototype = { - dispose: function() { - removeEventListener('click', this._callback, true); - removeEventListener('mouseup', this._callback, false); - removeEventListener('mousemove', this._callback, false); - this.detachHilight(); - }, - detachHilight: function () { - if (this._hilight) { - this._hilight.hide(); - delete this._hilight; - } - }, - getBgImage: function(e) { - if (!e || !e.ownerDocument) { - return null; - } - let url = e.ownerDocument.defaultView.getComputedStyle(e, "").getPropertyCSSValue('background-image'); - if (url && url.primitiveType === content.CSSPrimitiveValue.CSS_URI) { - return {elem: e, url: url.getStringValue()}; - } - return this.getBgImage(e.parentNode); - }, - findElemUnderCursor: function (e, n, a) { - if (n === 'bgimg') { - return this.getBgImage(e); - } - if (!e || !e.localName) { - return null; - } - if (e.localName.toLowerCase() === n && e[a]) { - if (n === "a") { - return {elem: e, url: e[a], download: e.getAttribute("download")}; - } - return {elem: e, url: e[a] }; - } - return this.findElemUnderCursor(e.parentNode, n, a); - }, - cancelEvent: function (evt) { - if (!evt.cancelable) { - return; - } - evt.preventDefault(); - evt.stopPropagation(); - }, - onClickOneClick: function(evt) { - let target = evt.target; - let doc = target.ownerDocument; - - function processRegular(e) { - let m = this.findElemUnderCursor(target, e[0], e[1]); - if (!m) { - return false; - } - try { - if (!this._handler(doc, m)) { - return false; - } - this.detachHilight(); - new Flasher(m.elem).hide(); - return true; - } - catch (ex) { - log(LOG_ERROR, "processRegular", ex); - } - return false; - } - function highlightElement(e) { - let m = this.findElemUnderCursor(target, e[0], e[1]); - if (!m) { - return false; - } - if (this._hilight && this._hilight.elem === m.elem) { - return true; - } - this.detachHilight(); - this._hilight = new Highlighter(m.elem); - return true; - } - - if (evt.type === 'click') { - if (evt.button === 0 && !!target && - target.nodeType === 1 && - (!target.namespaceURI || target.namespaceURI === 'http://www.w3.org/1999/xhtml')) { - if (this._searchee.some(processRegular, this)) { - this.cancelEvent(evt); - } - } - } - else if (evt.type === 'mousemove') { - if (!this._searchee.some(highlightElement, this)) { - this.detachHilight(); - } - } - }, - observe: function(bgimgs) { - let searchee = [ - ['a', 'href'], - ['img', 'src'] - ]; - if (bgimgs) { - searchee.push(['bgimg', 'bgimg']); - } - this._searchee = searchee; - } -}; - exports.Selector = Selector; diff --git a/modules/logging.js b/modules/logging.js index 2e4e3b101..94e040ba7 100644 --- a/modules/logging.js +++ b/modules/logging.js @@ -42,8 +42,7 @@ function prepareStack(stack) { message = []; for (let i = 0; stack && i < 60; ++i, stack = stack.caller) { if (stack.lineNumber) { - let loc = (stack.filename || "unknown").replace(GLUE, ""); - message.push(`\t${stack.name || "[anonymous]"}() @ ${loc}:${stack.lineNumber}`); + message.push(`\t${stack.name || "[anonymous]"}() @ ${(stack.filename || "unknown").replace(GLUE, "")}:${stack.lineNumber}`); } else { message.push(`\t[native @ ${stack.languageName || "???"}]`); @@ -112,8 +111,8 @@ function getTimeString() { "::" + fmt(time.getMilliseconds()); } -exports.log = function(level, message, exception, force) { - if (!force && global.level > level && level < exports.LOG_ERROR) { +exports.log = function(level, message, exception) { + if (global.level > level && level < exports.LOG_ERROR) { return; } try { @@ -187,7 +186,7 @@ exports.log = function(level, message, exception, force) { Services.console.logMessage(scriptError); message = `${getTimeString()}\n${message}\n--> ${sourceName}:${lineNumber}:${columnNumber}\n`; - if (force || global.level <= level) { + if (global.level <= level) { let f = new Instances.FileOutputStream(global.file, 0x04 | 0x08 | 0x10, parseInt("664", 8), 0); f.write(message, message.length); f.close(); diff --git a/modules/main.js b/modules/main.js index d13062e87..7ba8f6015 100644 --- a/modules/main.js +++ b/modules/main.js @@ -14,7 +14,6 @@ const obs = require("support/observers"); const {registerOverlay, watchWindows, unloadWindow} = require("support/overlays"); // Tests will only be available in dev mode. See make.py -exports.hasTests = "dta-tests"; if (!("hasTests" in exports)) { exports.hasTests = false; } @@ -23,7 +22,7 @@ if (!("hasTests" in exports)) { * AboutModule */ const ABOUT_URI = - 'https://about.downthemall.net/%BASE_VERSION%/?locale=%LOCALE%&app=%APP_ID%&version=%APP_VERSION%&os=%OS%'; + 'https://about.downthemall.org/%BASE_VERSION%/?locale=%LOCALE%&app=%APP_ID%&version=%APP_VERSION%&os=%OS%'; function AboutModule() { } @@ -73,34 +72,8 @@ AboutModule.prototype = Object.freeze({ } }); -function AboutTestsModule() { // dta-tests -} // dta-tests -AboutTestsModule.prototype = Object.freeze({ // dta-tests - classDescription: "DownThemAll! Tests about module", // dta-tests - classID: Components.ID('{6b5f6ca0-6a19-11e4-9803-0800200c9a66}'), // dta-tests - contractID: '@mozilla.org/network/protocol/about;1?what=dta-tests', // dta-tests - - QueryInterface: QI([Ci.nsIAboutModule]), // dta-tests - - newChannel: function(aURI, aLoadInfo) { // dta-tests - try { // dta-tests - log(LOG_ERROR, "aURI " + aURI.spec); // dta-tests - return Services.oldio.newChannelFromURI( // dta-tests - Services.io.newURI("chrome://dta-tests/content/dta-tests.xul", null, null), // dta-tests - aLoadInfo); // dta-tests - } // dta-tests - catch (ex) { // dta-tests - log(LOG_ERROR, "failed to create about channel", ex); // dta-tests - throw ex; // dta-tests - } // dta-tests - }, // dta-tests - getURIFlags: function(aURI) { // dta-tests - return Ci.nsIAboutModule.ALLOW_SCRIPT; // dta-tests - }, // dta-tests - getIndexedDBOriginPostfix: function(uri) { // dta-tests - return null; // dta-tests - } // dta-tests -}); // dta-tests + + function MetalinkInterceptModule() {} MetalinkInterceptModule.prototype = Object.freeze({ @@ -627,9 +600,6 @@ exports.main = function main() { const {registerComponents} = require("components"); const components = [AboutModule, MetalinkInterceptModule]; - if (exports.hasTests) { // dta-tests - components.push(AboutTestsModule); // dta-tests - } // dta-tests registerComponents(components); migrate(); diff --git a/modules/manager/chunk.js b/modules/manager/chunk.js index 9cee9ebff..0707c12ec 100644 --- a/modules/manager/chunk.js +++ b/modules/manager/chunk.js @@ -3,15 +3,18 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -/* global PIPE_SEGMENT_SIZE, MAX_PIPE_SEGMENTS */ +/* global BUFFER_SIZE, PIPE_SEGMENT_SIZE, MAX_PIPE_SEGMENTS */ requireJoined(this, "constants"); const Prefs = require("preferences"); const {ByteBucketTee} = require("support/bytebucket"); const {GlobalBucket} = require("./globalbucket"); +const {TimerManager} = require("support/timers"); const Limits = require("support/serverlimits"); const {getTimestamp, formatNumber, makeDir, randint} = require("utils"); +const {Task} = requireJSM("resource://gre/modules/Task.jsm"); const {memoryReporter} = require("./memoryreporter"); -const {setTimeout} = require("support/defer"); + +const Timers = new TimerManager(); const _thread = (function() { // Use a dedicated thread, so that we have serialized writes. @@ -49,18 +52,14 @@ unload(() => { try { _thread.shutdown(); } - catch (ex) { - // ignored - } + catch (ex) {} }); -exports.hintChunkBufferSize = function() { +exports.hintChunkBufferSize = function(bs) { }; const Observer = { - /* eslint-disable no-unused-vars */ observe: function(s, topic, data) { - /* eslint-enable no-unused-vars */ let perms = Prefs.permissions = Prefs.getExt("permissions", 384); if (perms & 384) { perms |= 64; @@ -87,9 +86,7 @@ function asyncCopy(instream, outstream, close) { ); return new Promise(function(resolve, reject) { copier.asyncCopy({ - /* eslint-disable no-unused-vars */ onStartRequest: function(req, context) {}, - /* eslint-enable no-unused-vars */ onStopRequest: function(req, context, status) { if (!Components.isSuccessCode(status)) { reject(status); @@ -102,46 +99,44 @@ function asyncCopy(instream, outstream, close) { }); } -class Chunk { - constructor(download, start, end, written) { - // safeguard against null or strings and such - this._parent = download; - this._start = start; - this._written = written > 0 ? written : 0; - this._sessionBytes = 0; - this.errored = false; - this.running = false; +function Chunk(download, start, end, written) { + // safeguard against null or strings and such + this._parent = download; + this._start = start; + this._written = written > 0 ? written : 0; + this._sessionBytes = 0; - this.end = end; - this.startBytes = this.safeBytes = this._written; - this.wasOpened = false; + this.end = end; + this.safeBytes = this._written; - log(LOG_INFO, "chunk created: " + this); - } + log(LOG_INFO, "chunk created: " + this); +} - clone() { - return new Chunk(this._parent, this.start, this.end, this.safeBytes); - } +Chunk.prototype = { + _written: 0, + + running: false, + safeBytes: 0, get starter() { return this.end <= 0; - } + }, get start() { return this._start; - } + }, get end() { return this._end; - } + }, set end(nv) { this._end = nv; this._total = this.end && (this._end - this._start + 1); - } + }, get total() { return this._total; - } + }, get written() { return this._written; - } + }, get buffered() { let rv = 0; try { @@ -165,25 +160,26 @@ class Chunk { rv += PIPE_SEGMENT_SIZE; } return rv; - } + }, get currentPosition() { return this.start + this.written; - } + }, get remainder() { return this._total - this._written; - } + }, get complete() { if (this._end <= 0) { return this.written !== 0; } return this._total === this.written; - } + }, get parent() { return this._parent; - } + }, get sessionBytes() { return this._sessionBytes; - } + }, + get buckets() { if (this._buckets) { return this._buckets; @@ -194,69 +190,190 @@ class Chunk { GlobalBucket ); return this._buckets; - } + }, - open() { - if (this.wasOpened) { - throw new Error("No recylcing"); - } + open: function() { if (this._openPromise) { - log(LOG_DEBUG, `opening ${this}: already pending`); return this._openPromise; } const file = this.parent.tmpFile; let pos = this.start + this.safeBytes; - log(LOG_DEBUG, `opening ${this}: ${file.path} at ${pos}`); - this.wasOpened = true; - return this._openPromise = this._openAsync(file, pos); - } + log(LOG_DEBUG, "opening " + file.path + " at: " + pos); + return this._openPromise = Task.spawn(function*() { + if (this._closing) { + // still pending? + yield this._closing; + } - _noteBytesWritten(bytes) { + this.errored = false; + this._sessionBytes = 0; + memoryReporter.registerChunk(this); + + try { + try { + yield makeDir(file.parent, Prefs.dirPermissions, true); + } + catch (ex if ex.becauseExists) { + // no op + } + let outStream = this._fileOutputStream = new Instances.FileOutputStream( + file, + 0x02 | 0x08, + Prefs.permissions, + Ci.nsIFileOutputStream.DEFER_OPEN + ); + if (pos) { + let seekable = outStream.QueryInterface(Ci.nsISeekableStream); + seekable.seek(0x00, pos); + } + this._pipe = new Instances.Pipe( + true, + true, + PIPE_SEGMENT_SIZE, + MAX_PIPE_SEGMENTS); + this._inStream = this._pipe.inputStream; + this._outStream = this._pipe.outputStream; + this._copier = asyncCopy(this._inStream, outStream, true); + this._copier.catch(status => { + this.errored = true; + this.download.writeFailed(status); + }); + } + finally { + delete this._openPromise; + } + }.bind(this)); + }, + _noteBytesWritten: function(bytes) { this._written += bytes; this._sessionBytes += bytes; - this.safeBytes = Math.max(this.startBytes, this._written - this.buffered); + this.safeBytes = Math.max(this.safeBytes, this._written - this.buffered); memoryReporter.noteBytesWritten(bytes); this.parent.timeLastProgress = getTimestamp(); - } - - close() { - log(LOG_DEBUG, `closing ${this}`); + }, + close: function() { if (this._closing) { return this._closing; } this.running = false; - this._closing = this._closeAsync(); - } + return (this._closing = Task.spawn(function*() { + try { + if (this._openPromise) { + yield this._openPromise; + } + // drain the overflowPipe, if any + if (this._overflowPipe && !this.errored) { + log(LOG_DEBUG, "draining overflow"); + try { + yield asyncCopy(this._overflowPipe.inputStream, this._outStream, false); + } + catch (status) { + this.download.writeFailed(status); + } + } + if (this._overflowPipe) { + // Still got an overflow pipe, meaning we failed a write + // Since we kill the pipe now, we need to adjust written sizes + // beforehand accordingly so saveBytes and rollback() are still + // correct + try { + let pending = this._overflowPipe.inputStream.available(); + this._written -= pending; + this._sessionBytes -= pending; + } + catch (ex) { + log(LOG_DEBUG, "failed to substract overflow, which is probably OK", ex); + } + this._overflowPipe.outputStream.close(); + this._overflowPipe.inputStream.close(); + delete this._overflowPipe; + } + // we are done writing to the pipe + if (this._outStream) { + this._outStream.close(); + delete this._pipe; + delete this._outStream; + } + // but still need to wait for the copy into the file + if (this._copier) { + try { + yield this._copier; + } + catch (ex) { + // ignore here! + } + delete this._copier; + } + + // upate the counters one last time + this._noteBytesWritten(0); + // and close the input stream end of the pipe + if (this._inStream) { + try { + this._inStream.close(); + } + catch (ex) { + // no op + } + delete this._inStream; // ... before deleting the instream + } - merge(ch) { + // and do some cleanup + if (this._buckets) { + delete this._buckets; + } + delete this._req; + memoryReporter.unregisterChunk(this); + + this._sessionBytes = 0; + this._written = this.safeBytes; + } + catch (ex) { + log(LOG_ERROR, "Damn!", ex); + } + finally { + if (this._fileOutputStream) { + try { + this._fileOutputStream.close(); + } + catch (ex) { + // might have been already closed + } + } + delete this._fileOutputStream; + delete this.download; + delete this._closing; + } + }.bind(this))); + }, + merge: function(ch) { if (!this.complete && !ch.complete) { throw new Error("Cannot merge incomplete chunks this way!"); } this.end = ch.end; - this.safeBytes += ch.safeBytes; this._written += ch._written; this.safeBytes += ch.safeBytes; - } - - rollback() { + }, + rollback: function() { if (!this._sessionBytes || this._sessionBytes > this._written) { return; } this._written -= this._sessionBytes; this._sessionBytes = 0; - } - - cancelChunk() { + }, + cancelChunk: function() { + this.pauseChunk(); + }, + pauseChunk: function() { this.running = false; this.close(); if (this.download) { this.download.cancel(); } - } - - suspend(aRequest, pending) { + }, + suspend: function(aRequest, pending) { if (this._req) { this._reqPending += pending; } @@ -266,13 +383,11 @@ class Chunk { this._reqPending = pending; } this.schedule(); - } - - write(aRequest, aInputStream, aCount) { + }, + write: function(aRequest, aInputStream, aCount) { try { // not running: do not write anything - if (!this.running || !this._inStream || !this._outStream) { - log(LOG_ERROR, "trying to write on a closed chunk"); + if (!this.running) { return -1; } let bytes = this.remainder; @@ -283,15 +398,14 @@ class Chunk { // we got what we wanted return -1; } - if (bytes < 0) { - throw new Error(`bytes negative: ${bytes} ${this.remainder} ${aCount}`); - } let got = this.requestBytes(bytes); - // didn't get enough if (got < bytes) { this.suspend(aRequest, bytes - got); } + if (bytes < 0) { + throw new Error("bytes negative: " + bytes + " " + this.remainder + " " + aCount); + } // per e10n contract we must consume all bytes // or in our case all remainder bytes @@ -304,19 +418,14 @@ class Chunk { } else { let written = 0; - // jshint -W116 + /* jshint -W116 */ try { written = this._outStream.writeFrom(aInputStream, bytes); } - catch (ex) { - if (ex.result == Cr.NS_BASE_STREAM_WOULD_BLOCK || ex == Cr.NS_BASE_STREAM_WOULD_BLOCK) { - // aka still nothing written - } - else { - throw ex; - } + catch (ex if ex.result == Cr.NS_BASE_STREAM_WOULD_BLOCK || ex == Cr.NS_BASE_STREAM_WOULD_BLOCK) { + // aka still nothing written } - // jshint +W116 + /* jshint +W116 */ let remain = bytes - written; if (remain > 0) { if (!this._overflowPipe) { @@ -341,55 +450,44 @@ class Chunk { log(LOG_ERROR, 'write: ' + this.parent.tmpFile.path, ex); throw ex; } - } - - observe() { + return 0; + }, + observe: function() { this.run(); - } - - _substractOverflow(requested) { - let instream = this._overflowPipe.inputStream; - let avail = instream.available(); - if (!avail) { - this._overflowPipe.outputStream.close(); - this._overflowPipe.inputStream.close(); - delete this._overflowPipe; - return requested; - } - - log(LOG_DEBUG, `still overflow: ${avail}`); - // decreasing requested will put the stream into suspended mode, need - // to schedule - requested = Math.max(requested - avail, 0); - let written = 0; - // jshint -W116 - try { - written = this._outStream.writeFrom(instream, avail); - } - catch (ex) { - if (ex.result == Cr.NS_BASE_STREAM_WOULD_BLOCK || ex == Cr.NS_BASE_STREAM_WOULD_BLOCK) { - // nothing written + }, + requestBytes: function(requested) { + if (this._overflowPipe) { + let instream = this._overflowPipe.inputStream; + let avail = instream.available(); + if (!avail) { + this._overflowPipe.outputStream.close(); + this._overflowPipe.inputStream.close(); + delete this._overflowPipe; } else { - throw ex; + log(LOG_DEBUG, `still overflow: ${avail}`); + // decreasing requested will put the stream into suspended mode, need + // to schedule + requested = Math.max(requested - avail, 0); + let written = 0; + /* jshint -W116 */ + try { + written = this._outStream.writeFrom(instream, avail); + } + catch (ex if ex.result == Cr.NS_BASE_STREAM_WOULD_BLOCK || ex == Cr.NS_BASE_STREAM_WOULD_BLOCK) { + // nothing written + } + /* jshint +W116 */ + avail -= written; + log(LOG_DEBUG, `overflow written: ${written} ${avail}`); + if (!avail) { + log(LOG_DEBUG, "overflow cleared"); + this._overflowPipe.outputStream.close(); + this._overflowPipe.inputStream.close(); + delete this._overflowPipe; + } } } - // jshint +W116 - avail -= written; - log(LOG_DEBUG, `overflow written: ${written} ${avail}`); - if (!avail) { - log(LOG_DEBUG, "overflow cleared"); - this._overflowPipe.outputStream.close(); - this._overflowPipe.inputStream.close(); - delete this._overflowPipe; - } - return requested; - } - - requestBytes(requested) { - if (this._overflowPipe) { - requested = this._substractOverflow(requested); - } if (memoryReporter.memoryPressure > 0) { log(LOG_INFO, "Under some pressure: " + memoryReporter.pendingBytes + @@ -398,25 +496,24 @@ class Chunk { log(LOG_INFO, "Using instead: " + requested); } return this.buckets.requestBytes(requested); - } - - schedule() { + }, + schedule: function() { if (this._schedTimer) { return; } - this._schedTimer = setTimeout(() => { + this._schedTimer = Timers.createOneshot(randint(10, 150), function() { delete this._schedTimer; this.run(); - }, randint(0, 150)); - } - - run() { + }, this); + }, + run: function() { if (!this._req) { return; } if (this._reqPending > 0) { // Still have pending bytes? - let got = this.requestBytes(this._reqPending); + let requested = this._reqPending; + let got = this.requestBytes(requested); if (!got) { this.schedule(); return; @@ -437,9 +534,8 @@ class Chunk { this.parent.timeLastProgress = getTimestamp(); this.schedule(); } - } - - toString() { + }, + toString: function() { let len = this.parent.totalSize ? String(this.parent.totalSize).length : 10; return formatNumber(this.start, len) + "/" + formatNumber(this.end, len) + @@ -448,164 +544,14 @@ class Chunk { " written/remain/sb:" + formatNumber(this.written, len) + "/" + formatNumber(this.remainder, len) + "/" + formatNumber(this._sessionBytes, len); - } - - toJSON() { + }, + toJSON: function() { return { start: this.start, end: this.end, written: this.safeBytes }; } - async _openAsync(file, pos) { - try { - try { - await makeDir(file.parent, Prefs.dirPermissions, true); - } - catch (ex) { - if (ex.becauseExists) { - // no op - } - else { - throw ex; - } - } - let outStream = new Instances.FileOutputStream( - file, - 0x02 | 0x08, - Prefs.permissions, - Ci.nsIFileOutputStream.DEFER_OPEN - ); - let closeStream = () => { - if (outStream) { - try { - outStream.close(); - } - catch (ex) { - // might have been already closed - } - } - outStream = null; - this.close(); - }; - if (pos) { - let seekable = outStream.QueryInterface(Ci.nsISeekableStream); - seekable.seek(0x00, pos); - } - this._pipe = new Instances.Pipe( - true, - true, - PIPE_SEGMENT_SIZE, - MAX_PIPE_SEGMENTS); - this._inStream = this._pipe.inputStream; - this._outStream = this._pipe.outputStream; - this._copier = asyncCopy(this._inStream, outStream, true); - this._copier.then(closeStream); - this._copier.catch(status => { - this.errored = true; - try { - closeStream(); - } - catch (ex) { - this.download.writeFailed(ex); - return; - } - this.download.writeFailed(status); - }); - } - finally { - memoryReporter.registerChunk(this); - delete this._openPromise; - } - } - async _closeAsync() { - try { - if (this._openPromise) { - await this._openPromise; - } - // drain the overflowPipe, if any - if (this._overflowPipe && !this.errored) { - log(LOG_DEBUG, "draining overflow"); - try { - await asyncCopy(this._overflowPipe.inputStream, this._outStream, false); - } - catch (status) { - this.errored = true; - this.download.writeFailed(status); - } - } - - if (this._overflowPipe) { - // Still got an overflow pipe, meaning we failed a write - // Since we kill the pipe now, we need to adjust written sizes - // beforehand accordingly so saveBytes and rollback() are still - // correct - try { - let pending = this._overflowPipe.inputStream.available(); - this._written -= pending; - this._sessionBytes -= pending; - } - catch (ex) { - log(LOG_DEBUG, "failed to substract overflow, which is probably OK", ex); - } - this._overflowPipe.outputStream.close(); - this._overflowPipe.inputStream.close(); - delete this._overflowPipe; - } - // we are done writing to the pipe - if (this._outStream) { - this._outStream.close(); - delete this._pipe; - delete this._outStream; - } - // but still need to wait for the copy into the file - if (this._copier) { - try { - await this._copier; - } - catch (ex) { - this.errored = true; - // ignore here otherwise! - } - delete this._copier; - } - - // upate the counters one last time - this._noteBytesWritten(0); - // and close the input stream end of the pipe - if (this._inStream) { - try { - this._inStream.close(); - } - catch (ex) { - // no op - } - delete this._inStream; // ... before deleting the instream - } - - // and do some cleanup - if (this._buckets) { - delete this._buckets; - } - delete this._req; - - this._sessionBytes = 0; - if (this.errored || !this.complete) { - this.startBytes = this._written = Math.max(this.startBytes, this.safeBytes - 2048); - } - else { - this.startBytes = this._written = this.safeBytes; - } - } - catch (ex) { - log(LOG_ERROR, "Damn!", ex); - } - finally { - memoryReporter.unregisterChunk(this); - delete this.download; - delete this._closing; - } - } -} +}; exports.Chunk = Chunk; diff --git a/modules/manager/connection.js b/modules/manager/connection.js index e213db3be..02ad8a2ce 100644 --- a/modules/manager/connection.js +++ b/modules/manager/connection.js @@ -18,7 +18,7 @@ const {formatNumber, StringBundles, getTimestamp} = require("utils"); const {modifyURL, modifyHttp} = require("support/requestmanipulation"); const Preferences = require("preferences"); const DomainPrefs = require("support/domainprefs"); -const {ProxyManager} = require("support/proxyprovider"); +const {Task} = requireJSM("resource://gre/modules/Task.jsm"); const { getUsableFileName, @@ -33,30 +33,54 @@ const DISCONNECTION_CODES = [ NS_ERROR_NET_RESET ]; -function canPrivate(chan) { - return ("nsIPrivateBrowsingChannel" in Ci) && (chan instanceof Ci.nsIPrivateBrowsingChannel); -} - -const _ = (function() { +const _ = (function(global) { let bundles = new StringBundles(["chrome://dta/locale/manager.properties"]); - return function(...args) { - if (args.length === 1) { - return bundles.getString(args[0]); + return function() { + if (arguments.length === 1) { + return bundles.getString(arguments[0]); } - return bundles.getFormattedString(...args); + return bundles.getFormattedString.apply(bundles, arguments); }; })(this); const cleanRequest = Symbol.for("cleanRequest"); +let proxyInfo = null; +const proxyObserver = { + observe: function() { + let type = Preferences.getExt("proxy.type", ""); + let host = Preferences.getExt("proxy.host", ""); + let port = Preferences.getExt("proxy.port", 0); + let resolve = Preferences.getExt("proxy.resolve", true); + if (!type || !host || !port) { + log(LOG_DEBUG, "no proxy info"); + proxyInfo = null; + return; + } + try { + let flags = 0; + if (resolve) { + flags |= Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST; + } + proxyInfo = Services.pps.newProxyInfo(type, host, port, flags, 0xffffffff, null); + log(LOG_DEBUG, "created proxy info"); + } + catch (ex) { + log(LOG_ERROR, "Failed to create proxy info", ex); + proxyInfo = null; + } + } +}; +Preferences.addObserver("extensions.dta.proxy", proxyObserver); +proxyObserver.observe(); + function maybeTempBlacklisted(conn, item, httpchan) { try { let server = httpchan.getResponseHeader("Server"); if (server.includes("cloudflare")) { item.cleanRequest = true; - let priv = canPrivate(httpchan) && httpchan.isChannelPrivate; - DomainPrefs.setTLD(conn.origURL, cleanRequest, true, priv); - DomainPrefs.setTLD(httpchan.URI, cleanRequest, true, priv); + DomainPrefs.setTLD(conn.origURL, cleanRequest, true); + DomainPrefs.setTLD(httpchan.URI, cleanRequest, true); return true; } return false; @@ -80,9 +104,9 @@ function Connection(d, c, isInfoGetter) { let referrer = d.referrer; log(LOG_INFO, "starting: " + url.spec); - this._chan = Services.oldio.newProxiedChannel(url, ProxyManager.getFor(url)); + this._chan = Services.oldio.newProxiedChannel(url, proxyInfo); let r = Ci.nsIRequest; - let loadFlags = r.LOAD_NORMAL | Ci.nsIChannel.LOAD_EXPLICIT_CREDENTIALS; + let loadFlags = r.LOAD_NORMAL; if (!Preferences.getExt('useCache', false)) { loadFlags = loadFlags | r.LOAD_BYPASS_CACHE; } @@ -93,7 +117,7 @@ function Connection(d, c, isInfoGetter) { this._chan.notificationCallbacks = this; if (d.isPrivate) { - if (canPrivate(this._chan)) { + if (("nsIPrivateBrowsingChannel" in Ci) && (this._chan instanceof Ci.nsIPrivateBrowsingChannel)) { try { this._chan.setPrivate(d.isPrivate); log(LOG_DEBUG, url.spec + ": setPrivate"); @@ -138,7 +162,12 @@ function Connection(d, c, isInfoGetter) { this.prepareChannel(this._chan); c.running = true; - this.open(); + c.open().then(() => { + this._chan.asyncOpen(this, null); + log(LOG_INFO, `chunk ${c} ${isInfoGetter ? "InfoGetter" : "Regular"} is now open!`); + }).catch(ex => { + this.writeFailed(ex); + }); } Connection.prototype = { @@ -157,17 +186,6 @@ Connection.prototype = { cantCount: false, - open: async function() { - try { - await this.c.open(); - this._chan.asyncOpen(this, null); - log(LOG_INFO, `chunk ${this.c} ${this.isInfoGetter ? "InfoGetter" : "Regular"} is now open!`); - } - catch (ex) { - this.writeoFailed(ex); - } - }, - prepareChannel: function(chan) { let d = this.d; try { @@ -190,7 +208,7 @@ Connection.prototype = { if (!d.cleanRequest && !Preferences.getExt("usecleanrequests", false) && - !DomainPrefs.getTLD(chan.URI, cleanRequest, canPrivate(chan) && chan.isChannelPrivate)) { + !DomainPrefs.getTLD(chan.URI, cleanRequest)) { log(LOG_DEBUG, `setting up ${chan.URI.spec}`); // Cannot hash when compressed chan.setRequestHeader("Accept-Encoding", "", false); @@ -295,6 +313,7 @@ Connection.prototype = { contractID: null, classDescription: "DownThemAll! connection", classID: null, + implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT, flags: Ci.nsIClassInfo.MAIN_THREAD_ONLY, // nsIChannelEventSink @@ -472,23 +491,15 @@ Connection.prototype = { this.discard(aInputStream, aCount - written); } } - catch (ex) { - if (ex !== NS_ERROR_BINDING_ABORTED && ex.result !== NS_ERROR_BINDING_ABORTED) { - log(LOG_ERROR, 'onDataAvailable', ex); - this.writeFailed(ex); - return; - } - throw ex; + catch (ex if (ex !== NS_ERROR_BINDING_ABORTED && ex.result !== NS_ERROR_BINDING_ABORTED)) { + log(LOG_ERROR, 'onDataAvailable', ex); + this.writeFailed(ex); } }, writeFailed: function(ex) { + log(LOG_DEBUG, "write failed invoked!", ex); let d = this.d; - if (!d || !d.chunks || d.chunks.indexOf(this.c) < 0) { - log(LOG_DEBUG, "write failed invoked, but ignored (not registered chunk)!", ex, true); - return; - } - log(LOG_DEBUG, "write failed invoked!", ex, true); if ((ex.result || ex) === Cr.NS_ERROR_FILE_NO_DEVICE_SPACE) { d.pauseAndRetry(); d.status = _("freespace"); @@ -803,9 +814,7 @@ Connection.prototype = { let file = d.fileName.length > 50 ? d.fileName.substring(0, 50) + "..." : d.fileName; if (~[401, 402, 407, 500, 502, 503, 504].indexOf(code) || (Preferences.getExt('recoverallhttperrors', false) && code !== 404) || - (code === 403 && - aChannel instanceof Ci.nsIHttpChannel && - maybeTempBlacklisted(this, d, aChannel))) { + (code === 403 && aChannel instanceof Ci.nsIHttpChannel && maybeTempBlacklisted(this, d, aChannel))) { log(LOG_DEBUG, "we got temp failure!", code); d.pauseAndRetry(); d.status = code >= 500 ? _('temperror') : _("error", [formatNumber(code, 3)]); @@ -985,7 +994,7 @@ Connection.prototype = { } try { - d.visitors.visit(aChannel.QueryInterface(Ci.nsIChannel)); + let visitor = d.visitors.visit(aChannel.QueryInterface(Ci.nsIChannel)); } catch (ex) { log(LOG_ERROR, "header failed! " + d, ex); @@ -1050,13 +1059,7 @@ Connection.prototype = { return; } if (!~d.chunks.indexOf(c)) { - log(LOG_DEBUG, - "invalid connection state (chunk index): " + - d.chunks.indexOf(c) + - " / " + - JSON.stringify(c) + - " / " + - JSON.stringify(d.chunks)); + log(LOG_DEBUG, "invalid connection state (chunk index): " + d.chunks.indexOf(c) + " / " + JSON.stringify(c) + " / " + JSON.stringify(d.chunks)); return; } @@ -1112,7 +1115,7 @@ Connection.prototype = { return; } }, - onStopRequest: async function(aRequest, aContext, aStatusCode) { + onStopRequest: function(aRequest, aContext, aStatusCode) { try { log(LOG_INFO, 'StopRequest'); } @@ -1123,161 +1126,146 @@ Connection.prototype = { let c = this.c; let d = this.d; - if (d) { - d.critical(); - } - try { - try { - await c.close(); + log(LOG_DEBUG, "closing"); + Task.spawn(function*() { + if (d) { + d.critical(); } - finally { - if (d) { - d.uncritical(); + try { + yield c.close(); + log(LOG_DEBUG, "closed"); + if (!d || !d.chunks || !~d.chunks.indexOf(c)) { + log(LOG_INFO, "chunk unknown"); + return; + } + if (c.errored || d.state === CANCELED) { + return; // already handled } - } - log(LOG_DEBUG, "closed"); - if (!d || !d.chunks || !~d.chunks.indexOf(c)) { - log(LOG_INFO, "chunk unknown"); - return; - } - if (c.errored || d.state === CANCELED) { - return; // already handled - } - // update flags and counters - d.refreshPartialSize(); - --d.activeChunks; + // update flags and counters + d.refreshPartialSize(); + --d.activeChunks; - // If automatic creation of new chunks is disabled we reduce maxChunks for - // every complete chunk so that no new chunk is generated. - // Manual addidition of new chunks is not affected by this. - if (!Preferences.getExt('autosegments', true) && d.maxChunks > 1) { - --d.maxChunks; - } + // If automatic creation of new chunks is disabled we reduce maxChunks for + // every complete chunk so that no new chunk is generated. + // Manual addidition of new chunks is not affected by this. + if (!Preferences.getExt('autosegments', true) && d.maxChunks > 1) { + --d.maxChunks; + } - const isRunning = d.state === RUNNING; + const isRunning = d.state === RUNNING; - if (c.starter && ~DISCONNECTION_CODES.indexOf(aStatusCode)) { - if (!d.urlManager.markBad(this.url)) { - log(LOG_ERROR, d + ": Server error or disconnection", "(type 3)"); - d.pauseAndRetry(); - d.status = _("servererror"); - } - else { - log(LOG_ERROR, "caught bad server", d.toString()); - d.safeRetry(); + if (c.starter && ~DISCONNECTION_CODES.indexOf(aStatusCode)) { + if (!d.urlManager.markBad(this.url)) { + log(LOG_ERROR, d + ": Server error or disconnection", "(type 3)"); + d.pauseAndRetry(); + d.status = _("servererror"); + } + else { + log(LOG_ERROR, "caught bad server", d.toString()); + d.safeRetry(); + } + return; } - return; - } - // work-around for ftp crap - // nsiftpchan for some reason assumes that if RETR fails it is a directory - // and tries to advance into said directory - if (aStatusCode === NS_ERROR_FTP_CWD) { - log(LOG_DEBUG, "Cannot change to directory :p", aStatusCode); - if (!this.handleError()) { - d.fail(_('servererror'), _('ftperrortext'), _('servererror')); + // work-around for ftp crap + // nsiftpchan for some reason assumes that if RETR fails it is a directory + // and tries to advance into said directory + if (aStatusCode === NS_ERROR_FTP_CWD) { + log(LOG_DEBUG, "Cannot change to directory :p", aStatusCode); + if (!this.handleError()) { + d.fail(_('servererror'), _('ftperrortext'), _('servererror')); + } + return; } - return; - } - // routine for normal chunk - log(LOG_INFO, this.url + ": Chunk " + c.start + "-" + c.end + " finished."); + // routine for normal chunk + log(LOG_INFO, this.url + ": Chunk " + c.start + "-" + c.end + " finished."); - // rude way to determine disconnection: if connection is closed before - // download is started we assume a server error/disconnection - if (c.starter && isRunning && !c.written) { - if (!d.urlManager.markBad(this.url)) { - log(LOG_ERROR, d + ": Server error or disconnection", "(type 2)"); - d.pauseAndRetry(); - d.status = _("servererror"); - } - else { - log(LOG_ERROR, "caught bad server", d.toString()); - d.safeRetry(); - } - return; - } - - // Server did not return any data. - // Try to mark the URL bad - // else pause + autoretry - if (!c.written && !!c.remainder) { - if (!d.urlManager.markBad(this.url)) { - log(LOG_ERROR, d + ": Server error or disconnection", "(type 1)"); - d.pauseAndRetry(); - d.status = _("servererror"); + // rude way to determine disconnection: if connection is closed before + // download is started we assume a server error/disconnection + if (c.starter && isRunning && !c.written) { + if (!d.urlManager.markBad(this.url)) { + log(LOG_ERROR, d + ": Server error or disconnection", "(type 2)"); + d.pauseAndRetry(); + d.status = _("servererror"); + } + else { + log(LOG_ERROR, "caught bad server", d.toString()); + d.safeRetry(); + } + return; } - return; - } - // check if we're complete now - if (isRunning && d.chunks.every(e => e.complete)) { - if (!d.resumeDownload()) { - if (d.chunks.some(e => e.errored)) { + // Server did not return any data. + // Try to mark the URL bad + // else pause + autoretry + if (!c.written && !!c.remainder) { + if (!d.urlManager.markBad(this.url)) { log(LOG_ERROR, d + ": Server error or disconnection", "(type 1)"); d.pauseAndRetry(); d.status = _("servererror"); - return; } - log(LOG_INFO, d + ": Download is complete!"); - d.finishDownload(); return; } - } - // size mismatch - if (!d.isOf(PAUSED | CANCELED | FINISHING) && d.chunks.length === 1 && d.chunks[0] === c) { - if (d.relaxSize && c.remainder < 250) { - log(LOG_INFO, d + ": Download is complete!"); - d.setState(FINISHING); - d.finishDownload(); - return; + // check if we're complete now + if (isRunning && d.chunks.every(e => e.complete)) { + if (!d.resumeDownload()) { + log(LOG_INFO, d + ": Download is complete!"); + d.finishDownload(); + return; + } } - if (d.resumable && c.sessionBytes > 0) { - // fast retry unless we didn't actually receive something - if (d.resumeDownload()) { + // size mismatch + if (!d.isOf(PAUSED | CANCELED | FINISHING) && d.chunks.length === 1 && d.chunks[0] === c) { + if (d.relaxSize && c.remainder < 250) { + log(LOG_INFO, d + ": Download is complete!"); + d.setState(FINISHING); + d.finishDownload(); return; } + if (d.resumable && c.sessionBytes > 0) { + // fast retry unless we didn't actually receive something + d.resumeDownload(); + } + else if (d.resumable || Preferences.getExt('resumeonerror', false)) { + d.pauseAndRetry(); + d.status = _('errmismatchtitle'); + } + else { + d.fail( + _('errmismatchtitle'), + _('errmismatchtext', [d.partialSize, d.totalSize]), + _('errmismatchtitle') + ); + } + return; } - if (d.resumable || Preferences.getExt('resumeonerror', false)) { - d.pauseAndRetry(); - d.status = _('errmismatchtitle'); - } - else { - d.fail( - _('errmismatchtitle'), - _('errmismatchtext', [d.partialSize, d.totalSize]), - _('errmismatchtitle') - ); + if (!d.isOf(PAUSED | CANCELED)) { + d.resumeDownload(); } - return; } - - if (!d.isOf(PAUSED | CANCELED)) { - if (!d.resumeDownload() && !d.chunks.every(e => e.complete || e.running)) { - d.dumpScoreBoard(); - log(LOG_ERROR, "Failed to resume although not complete yet", null, true); - d.pauseAndRetry(); - d.status = _('errmismatchtitle'); + catch (ex) { + log(LOG_ERROR, "Failed onStopRequest", ex); + } + finally { + delete this.c; + delete this._chan; + if (d) { + d.uncritical(); } } - } - catch (ex) { - log(LOG_ERROR, "Failed onStopRequest", ex, true); - } - finally { - delete this.c; - delete this._chan; - } + }.bind(this)); }, // nsIProgressEventSink onProgress: function(aRequest, aContext, aProgress, aProgressMax) { try { // shortcuts + let c = this.c; let d = this.d; if (this.reexamine) { diff --git a/modules/manager/decompressor.js b/modules/manager/decompressor.js index 528c57020..f137a129a 100644 --- a/modules/manager/decompressor.js +++ b/modules/manager/decompressor.js @@ -7,132 +7,113 @@ const BUFFER_SIZE = 5 * 1024 * 1024; const FREQ = 250; const DTA = require("api"); -const {setInterval, clearInterval} = require("support/defer"); +const {TimerManager} = require("support/timers"); const Prefs = require("preferences"); -class Decompressor { - constructor(download, callback) { - this.download = download; - this.callback = callback; - this.to = download.destinationLocalFile.clone(); - this.from = download.tmpFile.clone(); - this.exception = null; +const Timers = new TimerManager(); - try { - this._outStream = new Instances.FileOutputStream(this.to, 0x04 | 0x08, Prefs.getExt('permissions', 384), 0); - this.outStream = new Instances.BinaryOutputStream( - new Instances.BufferedOutputStream(this._outStream, BUFFER_SIZE)); +function Decompressor(download, callback) { + this.download = download; + this.callback = callback; + this.to = download.destinationLocalFile.clone(); + this.from = download.tmpFile.clone(); - const converter = Cc["@mozilla.org/streamconv;1?from=" + download.compression + "&to=uncompressed"] - .createInstance(Ci.nsIStreamConverter); + try { + this._outStream = new Instances.FileOutputStream(this.to, 0x04 | 0x08, Prefs.getExt('permissions', 384), 0); + this.outStream = new Instances.BinaryOutputStream( + new Instances.BufferedOutputStream(this._outStream, BUFFER_SIZE)); - converter.asyncConvertData( - download.compression, - "uncompressed", - this, - null - ); + let converter = Cc["@mozilla.org/streamconv;1?from=" + download.compression + "&to=uncompressed"] + .createInstance(Ci.nsIStreamConverter); - const chan = Services.oldio.newChannelFromURI(Services.io.newFileURI(this.from)); - chan.asyncOpen(converter, null); - } - catch (ex) { - try { - if (this.outStream) { - this.outStream.close(); - } - if (this.to.exists()) { - this.to.remove(false); - } - if (this.from.exists()) { - this.from.remove(false); - } - } - catch (exx) { - // XXX: what now? - } - log(LOG_ERROR, "err. :p", ex); - callback.call(download, ex); - } - } + converter.asyncConvertData( + download.compression, + "uncompressed", + this, + null + ); - setException(ex) { - if (this.exception) { - return; - } - this.exception = ex; + let chan = Services.oldio.newChannelFromURI(Services.io.newFileURI(this.from)); + chan.asyncOpen(converter, null); } - - close() { + catch (ex) { try { - this.outStream.flush(); - this._outStream.QueryInterface(Ci.nsISeekableStream).setEOF(); - } - catch (ex) { - this.setException(ex); - } - finally { - try { + if (this.outStream) { this.outStream.close(); - this._outStream.close(); } - catch (ex) { - // huh? - log(LOG_ERROR, "Decompressor: close streams", ex); + if (this.to.exists()) { + this.to.remove(false); } + if (this.from.exists()) { + this.from.remove(false); + } + } + catch (exx) { + // XXX: what now? } + log(LOG_ERROR, "err. :p", ex); + callback.call(download, ex); } - - QueryInterface(iid) { +} +Decompressor.prototype = { + exception: null, + QueryInterface: function(iid) { if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIStreamListener) || iid.equals(Ci.nsIRequestObserver)) { return this; } throw Cr.NS_ERROR_NO_INTERFACE; - } - onStartRequest(r, c) { - this._timer = setInterval(() => this.download.invalidate(), FREQ); - } - onStopRequest(request, c) { - clearInterval(this._timer); + }, + onStartRequest: function(r, c) { + this._timer = Timers.createRepeating(FREQ, this.download.invalidate, this.download); + }, + onStopRequest: function(request, c) { + Timers.killTimer(this._timer); // important, or else we don't write out the last buffer and truncate too early. :p + this.outStream.flush(); + try { + this._outStream.QueryInterface(Ci.nsISeekableStream).setEOF(); + } + catch (ex) { + this.exception = ex; + } try { - this.close(); - if (this.exception) { - try { - this.to.remove(false); - } - catch (ex) { - // no-op: we're already bad :p - } + this.outStream.close(); + this._outStream.close(); + } + catch (ex) { + // huh? + log(LOG_ERROR, "Decompressor: close streams", ex); + } + if (this.exception) { + try { + this.to.remove(false); } - else { - try { - this.from.remove(false); - } - catch (ex) { - log(LOG_ERROR, "Failed to remove tmpFile", ex); - } + catch (ex) { + // no-op: we're already bad :p } } + try { + this.from.remove(false); + } catch (ex) { - this.setException(ex); + log(LOG_ERROR, "Failed to remove tmpFile", ex); } this.callback.call(this.download, this.exception); - } - onDataAvailable(request, c, stream, offset, count) { + }, + onDataAvailable: function(request, c, stream, offset, count) { try { - const binStream = new Instances.BinaryInputStream(stream); + var binStream = new Instances.BinaryInputStream(stream); if (count !== this.outStream.write(binStream.readBytes(count), count)) { throw new Exception("Failed to write!"); } this.download.partialSize = offset; } catch (ex) { - this.setException(ex); - const reason = 0x804b0002; // NS_BINDING_ABORTED; + this.exception = ex; + var reason = 0x804b0002; // NS_BINDING_ABORTED; request.cancel(reason); } } -} +}; exports.Decompressor = Decompressor; diff --git a/modules/manager/globalprogress.js b/modules/manager/globalprogress.js index 6d7549e13..5c28f65d9 100644 --- a/modules/manager/globalprogress.js +++ b/modules/manager/globalprogress.js @@ -3,41 +3,36 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -class GlobalProgressStub { - construct(window) { - this._total = 0; - this._value = 0; - this.init(window); - } - init() {} - exit() {} - reset() {} - - hide() {} - unknown() {} - pause() {} - activate() {} - error() {} - get value() { - return this._value; - } - set value(nv) { - this._value = nv; - } - get total() { - return this._total; - } - set total(nv) { - this._total = nv; - } +function GlobalProgress(window) { + this.init(window); } -exports.GlobalProgress = GlobalProgressStub; + +/** + * Stub implementation, furthermore showing the interface + * Right now it is fairly similar to Win7 capabilities, + * but that may change in the future (UNFROZEN) + */ +GlobalProgress.prototype = { + + init: function() {}, + exit: function() {}, + reset: function() {}, + + hide: function() {}, + unknown: function() {}, + pause: function() {}, + activate: function() {}, + error: function() {}, + + total: 0, + value: 0 +}; try { // Windows7 if (!Services.wintaskbar.available) { // Service is present but not supported - throw new Error("not available"); + throw new Exception("not available"); } /* global NO_PROGRESS, INDETERMINATE, PAUSED, NORMAL, ERROR */ for (let s in Ci.nsITaskbarProgress) { @@ -45,73 +40,72 @@ try { this[s.slice(6)] = Ci.nsITaskbarProgress[s]; } } - class GlobalProgress extends GlobalProgressStub { - constructor(window) { - super(); - this._state = NO_PROGRESS; + GlobalProgress.prototype = { + _state: NO_PROGRESS, + init: function(window) { let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor). getInterface(Ci.nsIWebNavigation). QueryInterface(Ci.nsIDocShellTreeItem).treeOwner. QueryInterface(Ci.nsIInterfaceRequestor). getInterface(Ci.nsIXULWindow).docShell; this._progress = Services.wintaskbar.getTaskbarProgress(docShell); - } - exit() { + }, + exit: function() { this.hide(); delete this._progress; - } - reset() { + }, + reset: function() { this._total = 1; this._value = 0; this.hide(); - } - hide() { + }, + hide: function() { this._state = NO_PROGRESS; this._setState(); - } - unknown() { + }, + unknown: function() { this._state = INDETERMINATE; this._setState(); - } - pause(value, total) { + }, + pause: function(value, total) { if (value && total) { this._value = value; this._total = total; } this._state = PAUSED; this._setState(); - } - activate(value, total) { + }, + activate: function(value, total) { if (value && total) { this._value = value; this._total = total; } this._state = NORMAL; this._setState(); - } - error(value, total) { + }, + error: function(value, total) { if (value && total) { this._value = value; this._total = total; } this._state = ERROR; this._setState(); - } + }, get value() { return this._value; - } + }, set value(nv) { - this.__value = nv.toFixed(0); + this._value = nv.toFixed(0); this._setState(); - } + }, get total() { return this._total; - } + }, set total(nv) { this._total = nv.toFixed(0); this._setState(); - } - _setState() { + }, + _setState: function() { if (this._state <= INDETERMINATE) { this._progress.setProgressState(this._state); } @@ -122,11 +116,14 @@ try { this._total ); } - } - } - exports.GlobalProgress = GlobalProgress; + }, + _total: 1, + _value: 0 + }; } catch (ex) { // not available or failed to init // Stub will be used! } + +exports.GlobalProgress = GlobalProgress; diff --git a/modules/manager/imex.js b/modules/manager/imex.js index 0b50da390..430804e92 100644 --- a/modules/manager/imex.js +++ b/modules/manager/imex.js @@ -4,32 +4,36 @@ "use strict"; const DTA = require("api"); +const Preferences = require("preferences"); const {getTextLinks} = require("support/textlinks"); const Version = require("version"); const {NS_DTA, NS_METALINKER3, NS_METALINK_RFC5854} = require("support/metalinker"); const {filterInSitu} = require("utils"); -exports.parseTextFile = async function parseTextFile(aFile) { - log(LOG_INFO, "Parsing text file: " + aFile.spec); +const XPathResult = Ci.nsIDOMXPathResult; - let req = await fetch(Services.io.newFileURI(aFile).spec); - req = await req.text(); - log(LOG_ERROR, req); - - let links = []; - for (let l of getTextLinks(req, false)) { - l = Services.io.newURI(l, null, null); - links.push({ - url: new DTA.URL(l), - referrer: null, - description: 'imported from ' + aFile.leafName, - isPrivate: false - }); - } - log(LOG_INFO, "parsed text file, links: " + links.length); - return filterInSitu(links, function(e) { - return (e = e.url.spec) && !((e in this) || (this[e] = null)); - }, {}); +exports.parseTextFile = function parseTextFile(aFile, cb) { + log(LOG_INFO, "Parsing text file: " + aFile.spec); + let req = new Instances.XHR(); + req.onload = function() { + let links = []; + for (let l of getTextLinks(req.responseText, false)) { + l = Services.io.newURI(l, null, null); + links.push({ + url: new DTA.URL(l), + referrer: null, + description: 'imported from ' + aFile.leafName, + isPrivate: false + }); + } + log(LOG_INFO, "parsed text file, links: " + links.length); + cb(filterInSitu(links, function(e) { + return (e = e.url.spec) && !((e in this) || (this[e] = null)); + }, {})); + }; + req.overrideMimeType("text/plain"); + req.open("GET", Services.io.newFileURI(aFile).spec); + req.send(); }; exports.exportToTextFile = function exportToTextFile(aDownloads, aFile, aPermissions) { @@ -129,7 +133,7 @@ exports.exportToHtmlFile = function exportToHtmlFile(aDownloads, aDocument, aFil let foot = document.createElement('p'); foot.appendChild(document.createTextNode('Exported by ')); n = document.createElement('a'); - n.setAttribute('href', 'http://www.downthemall.net/'); + n.setAttribute('href', 'https://www.downthemall.org/'); n.textContent = 'DownThemAll! ' + Version.VERSION; foot.appendChild(n); body.appendChild(foot); diff --git a/modules/manager/matcher.js b/modules/manager/matcher.js index 61d265b28..577bfb0cc 100644 --- a/modules/manager/matcher.js +++ b/modules/manager/matcher.js @@ -17,11 +17,11 @@ const _ = (function(global) { "chrome://dta/locale/common.properties", "chrome://dta/locale/manager.properties" ]); - return function(...args) { - if (args.length === 1) { - return bundles.getString(args[0]); + return function() { + if (arguments.length === 1) { + return bundles.getString(arguments[0]); } - return bundles.getFormattedString(...args); + return bundles.getFormattedString.apply(bundles, arguments); }; })(this); @@ -141,6 +141,7 @@ const RemainderMatch = { }; }, getMatcher: function(params) { + let state = 0; let est = 0; for (let p of params) { let n = parseInt(p, 10); @@ -262,17 +263,15 @@ const DomainMatch = { } }; -class MatcherTee { - constructor(a, b) { - this.a = a; - this.b = b; - } - +function MatcherTee(a, b) { + this.a = a; + this.b = b; +} +MatcherTee.prototype = { get name() { return this.a + ";" + this.b; - } - - *getItems(downloads) { + }, + getItems: function*(downloads) { for (let a of this.a.getItems(downloads)) { a.param = "a:" + a.param; yield a; @@ -282,9 +281,8 @@ class MatcherTee { b.param = "b:" + b.param; yield b; } - } - - getMatcher(params) { + }, + getMatcher: function(params) { let a = [], b = []; params.forEach(function(p) { return this[p[0]].push(p.substr(2)); @@ -302,21 +300,27 @@ class MatcherTee { b = this.b.getMatcher(b); return function(d) { return a(d) && b(d); }; } -} - -class Matcher { - constructor() { - this._matchers = []; - this._matchersLength = 0; - } +}; - *getItems(name, downloads) { +function Matcher() { + this._matchers = []; + this._matchersLength = 0; +} +Matcher.prototype = { + _available: { + 'textmatch': TextMatch, + 'downloadmatch': new MatcherTee(FilterMatch, DomainMatch), + 'pathmatch': PathMatch, + 'statusmatch': new MatcherTee(StatusMatch, RemainderMatch), + 'sizematch': SizeMatch, + 'domainmatch': DomainMatch + }, + getItems: function*(name, downloads) { for (let i of this._available[name].getItems(downloads)) { yield i; } - } - - addMatcher(name, params) { + }, + addMatcher: function(name, params) { if (!(name in this._available)) { log(LOG_ERROR, "trying to add a matcher that does not exist"); return; @@ -328,17 +332,14 @@ class Matcher { this._matchers.push({name: name, isMatch: m}); this._matchersLength = this._matchers.length; } - } - - removeMatcher(name) { + }, + removeMatcher: function(name) { this._matchersLength = filterInSitu(this._matchers, m => m.name !== name).length; - } - + }, get filtering() { return !!this._matchersLength; - } - - filter(array) { + }, + filter: function(array) { // jshint strict:true, globalstrict:true, loopfunc:true let rv; for (let i = 0, e = this._matchers.length; i < e; ++i) { @@ -357,9 +358,8 @@ class Matcher { } } return rv; - } - - shouldDisplay(d) { + }, + shouldDisplay: function(d) { for (let i = 0, e = this._matchers.length; i < e; ++i) { if (!this._matchers[i].isMatch(d)) { return false; @@ -367,17 +367,6 @@ class Matcher { } return true; } -} - -Object.assign(Matcher.prototype, { - _available: { - 'textmatch': TextMatch, - 'downloadmatch': new MatcherTee(FilterMatch, DomainMatch), - 'pathmatch': PathMatch, - 'statusmatch': new MatcherTee(StatusMatch, RemainderMatch), - 'sizematch': SizeMatch, - 'domainmatch': DomainMatch - }, -}); +}; exports.Matcher = Matcher; diff --git a/modules/manager/memoryreporter.js b/modules/manager/memoryreporter.js index d55199550..aa80dc65f 100644 --- a/modules/manager/memoryreporter.js +++ b/modules/manager/memoryreporter.js @@ -3,26 +3,26 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; +/* global BUFFER_SIZE, MAX_PENDING_SIZE */ requireJoined(this, "constants"); -const {setTimeout, clearTimeout} = require("support/defer"); +const {TimerManager} = require("support/timers"); const pressure = require("support/memorypressure"); -class MemoryReporter { - constructor() { - this.explicitNonHeap = 0; - this.process = ""; - this.chunks = new Set(); - this.session = { - chunks: 0, - written: 0 - }; - this._calc(); - this.memoryPressure = 0; - this.timer = null; - return Object.seal(this); - } +const Timers = new TimerManager(); - _calc(force) { +function MemoryReporter() { + this.chunks = new Set(); + this.session = { + chunks: 0, + written: 0 + }; + this._calc(); + this.memoryPressure = 0; + return Object.seal(this); +} +MemoryReporter.prototype = { + process: "", + _calc: function(force) { if (!this._generation) { this._generation = 10; } @@ -37,6 +37,7 @@ class MemoryReporter { this._chunksActive = 0; for (let c of this.chunks) { + let bs = c.buffer_size; this._pendingBytes += c.buffered; if (c._req) { ++this._chunksScheduled; @@ -45,16 +46,16 @@ class MemoryReporter { ++this._chunksActive; } } - } + }, get pendingBytes() { this._calc(); return this._pendingBytes; - } + }, get cachedBytes() { this._calc(); return this._cachedBytes; - } - collectReports(callback, closure) { + }, + collectReports: function(callback, closure) { this._calc(true); // As per :njn, add-ons should not use anything other than @@ -113,18 +114,19 @@ class MemoryReporter { "Total bytes received during this session.", closure ); - } - noteBytesWritten(bytes) { + }, + explicitNonHeap: 0, + noteBytesWritten: function(bytes) { this.session.written += bytes; - } - registerChunk(chunk) { + }, + registerChunk: function(chunk) { this.chunks.add(chunk); ++this.session.chunks; - } - unregisterChunk(chunk) { + }, + unregisterChunk: function(chunk) { this.chunks.delete(chunk); - } - unload() { + }, + unload: function() { pressure.remove(this); try { if ("unregisterStrongReporter" in Services.memrm) { @@ -133,15 +135,10 @@ class MemoryReporter { else { Services.memrm.unregisterReporter(this); } - } catch (ex) { - // ignored - } - if (this.timer) { - clearTimeout(this.timer); - this.timer = null; - } - } - observe(s, topic, data) { + } catch (ex) {} + Timers.killAllTimers(); + }, + observe: function(s, topic, data) { if (topic === "memory-pressure") { if (data === "low-memory") { this.memoryPressure += 25; @@ -152,8 +149,8 @@ class MemoryReporter { this.schedulePressureDecrement(); return; } - } - decrementPressure() { + }, + decrementPressure: function() { --this.memoryPressure; if (this.memoryPressure <= 0) { this.memoryPressure = 0; @@ -161,17 +158,11 @@ class MemoryReporter { return; } this.schedulePressureDecrement(); + }, + schedulePressureDecrement: function() { + Timers.createOneshot(100, this.decrementPressure, this); } - schedulePressureDecrement() { - if (this.timer) { - return; - } - this.timer = setTimeout(() => { - this.timer = null; - this.decrementPressure(); - }, 100); - } -} +}; Object.seal(MemoryReporter.prototype); const memoryReporter = exports.memoryReporter = new MemoryReporter(); diff --git a/modules/manager/queuestore.js b/modules/manager/queuestore.js index 022c7c2f2..a09ecc633 100644 --- a/modules/manager/queuestore.js +++ b/modules/manager/queuestore.js @@ -11,7 +11,7 @@ const DB_VERSION = 2; const STMT_SELECT = 'SELECT uuid, item, pos FROM queue ORDER BY pos'; -const {setTimeout} = require("support/defer"); +const Timers = new (require("support/timers").TimerManager)(); const obs = require("support/observers"); let _connection = null; @@ -193,7 +193,7 @@ const QueueStore = { if (!_timer) { // delay up to 5000 msecs - _timer = setTimeout(() => this._saveDownloadQueue(), 5000); + _timer = Timers.createOneshot(5000, this._saveDownloadQueue, this); } }, _saveDownloadQueue: function() { @@ -285,37 +285,40 @@ const QueueStore = { stmt.finalize(); } }, - loadItems: function() { - return new Promise((resolve, reject) => { - let stmt = _connection.createAsyncStatement(STMT_SELECT); - let rows = []; - stmt.executeAsync({ - handleResult: function(aResult) { - for (let row = aResult.getNextRow(); row; row = aResult.getNextRow()) { - rows.push({ - id: row.getResultByIndex(0), - item: JSON.parse(row.getResultByIndex(1)), - pos: row.getResultByIndex(2), - }); - } - }, - handleError: function(aError) { - log(LOG_ERROR, 'failed load queue file', aError); - reject(aError); - }, - handleCompletion: function(aReason) { - try { - stmt.finalize(); - let count = rows.length; - rows.forEach(e => e.count = count); - log(LOG_DEBUG, "All your callback are belong to us"); - resolve(rows); - } - catch (ex) { - reject(ex); - } + loadItems: function(callback, ctx) { + function Item(row) { + this.id = row.getResultByIndex(0); + this.item = JSON.parse(row.getResultByIndex(1)); + this.pos = row.getResultByIndex(2); + } + + ctx = ctx || null; + let stmt; + try { + stmt = _connection.createAsyncStatement(STMT_SELECT); + } + catch (ex) { + log(LOG_ERROR, "SQLite", _connection.lastErrorString); + callback.call(ctx, null); + } + let rows = []; + stmt.executeAsync({ + handleResult: function(aResult) { + for (let row = aResult.getNextRow(); row; row = aResult.getNextRow()) { + rows.push(new Item(row)); } - }); + }, + handleError: function(aError) { + log(LOG_ERROR, 'failed load queue file', aError); + callback.call(ctx, null); + }, + handleCompletion: function(aReason) { + stmt.finalize(); + let count = rows.length; + rows.forEach(e => e.count = count); + log(LOG_DEBUG, "All your callback are belong to us"); + callback.call(ctx, rows); + } }); }, flush: function() { diff --git a/modules/manager/renamer.js b/modules/manager/renamer.js index c92b8e4cf..047818139 100644 --- a/modules/manager/renamer.js +++ b/modules/manager/renamer.js @@ -8,6 +8,7 @@ const Prefs = require("preferences"); const { replaceSlashes, getUsablePath, + getUsableFileName, getUsableFileNameWithFlatten } = require("support/stringfuncs"); diff --git a/modules/manager/speedstats.js b/modules/manager/speedstats.js index 1d49c926d..8653b315c 100644 --- a/modules/manager/speedstats.js +++ b/modules/manager/speedstats.js @@ -7,45 +7,45 @@ * Speed Statistics * @param maxSpeeds (unsigned) Maximum number of speeds to count */ -class SpeedStats { - constructor(maxSpeeds) { - this._maxSpeeds = maxSpeeds; - this._speeds = []; - this._aspeeds = []; - this._lastTime = this._lastBytes = this._avg = 0; - } +function SpeedStats(maxSpeeds) { + this._maxSpeeds = maxSpeeds; + this._speeds = []; + this._aspeeds = []; + this._lastTime = this._lastBytes = this._avg = 0; +} +SpeedStats.prototype = Object.freeze({ /** * Maximum number of speeds to store * Oldest will be dropped if buffer runs full */ get maxSpeeds() { return this._maxSpeeds; - } + }, /** * Average speed (at the moment) */ get avg() { return this._avg; - } + }, /** * First (oldest) speed recorded */ get first() { return this._speeds[0]; - } + }, /** * Last (most recent) speed recorded */ get last() { return this._speeds[this._speeds.length - 1]; - } + }, /** * Number of speed statistics currently recorded */ get length() { return this._speeds.length; - } + }, /** * Generator over all recorded speeds */ @@ -55,7 +55,7 @@ class SpeedStats { yield x; } }).call(this); - } + }, /** * Generator over all avg speeds */ @@ -65,25 +65,25 @@ class SpeedStats { yield x; } }).call(this); - } + }, /** * Time of last update */ get lastUpdate() { return this._lastTime; - } + }, /** * Bytes in last period */ get lastBytes() { return this._lastBytes; - } + }, /** * Adds a new data point based on given downloaded bytes and time * @param bytes (int) Bytes in the period * @param time (int) Time bytes was recorded */ - add(bytes, time) { + add: function(bytes, time) { let received = 0; if (this._lastTime) { let elapsed = (time - this._lastTime) / 1000; @@ -113,14 +113,14 @@ class SpeedStats { this._lastTime = time; this._lastBytes = bytes; return received; - } + }, /** * Clears all statistics */ - clear() { + clear: function() { this._speeds.length = 0; this._aspeeds.length = 0; this._lastTime = this._lastBytes = this._avg = 0; } -} +}); exports.SpeedStats = Object.freeze(SpeedStats); diff --git a/modules/manager/visitormanager.js b/modules/manager/visitormanager.js index 0e89928f3..eb51b9e47 100644 --- a/modules/manager/visitormanager.js +++ b/modules/manager/visitormanager.js @@ -8,19 +8,18 @@ const {LOCALE} = require("version"); const {getTimestamp, normalizeMetaPrefs} = require("utils"); const {identity} = require("support/memoize"); -class Visitor { - constructor(nodes) { - if (nodes) { - for (let x in nodes) { - if (!(x in this.cmpKeys)) { - continue; - } - this[x] = nodes[x]; - } +function Visitor() { + let nodes = arguments[0]; + for (let x in nodes) { + if (!(x in this.cmpKeys)) { + continue; } + this[x] = nodes[x]; } +} - compare(v) { +Visitor.prototype = { + compare: function vi_compare(v) { if (!(v instanceof Visitor)) { return; } @@ -45,9 +44,8 @@ class Visitor { throw new Exception("Header " + x + " doesn't match"); } } - } - - save(node) { + }, + save: function vi_save(node) { var rv = {}; for (let x in this.cmpKeys) { if (!(x in this)) { @@ -57,30 +55,39 @@ class Visitor { } return rv; } -} +}; -class HttpVisitor extends Visitor { - constructor(chan) { - if ((chan instanceof Ci.nsIHttpChannel) || ("_stub" in chan)) { - super(null); - this.acceptRanges = true; - this._charset = chan.URI.originCharset; - this.visit(chan); - } - else { - super(chan); - } +function HttpVisitor(chan) { + if ((chan instanceof Ci.nsIHttpChannel) || ("_stub" in chan)) { + this._charset = chan.URI.originCharset; + this.visit(chan); } + else { + Visitor.apply(this, arguments); + } +} - QueryInterface(aIID) { - if (aIID.equals(Ci.nsISupports) || - aIID.equals(Ci.nsIHttpHeaderVisitor)) { +HttpVisitor.prototype = { + __proto__: Visitor.prototype, + acceptRanges: true, + cmpKeys: { + 'etag': true, // must not be modified from 200 to 206: + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7 + //'content-length': false, + 'last-modified': true, // may get omitted later, but should not change + 'content-encoding': true // must not change, or download will become + // corrupt. + }, + QueryInterface: function(aIID) { + if ( + aIID.equals(Ci.nsISupports) || + aIID.equals(Ci.nsIHttpHeaderVisitor) + ) { return this; } throw Components.results.NS_ERROR_NO_INTERFACE; - } - - visit(chan) { + }, + visit: function vmh_visit(chan) { if (log.enabled) { let msg = chan.URI.spec + "\nRequest:\n"; let visitor = { @@ -246,9 +253,8 @@ class HttpVisitor extends Visitor { if (!("fileName" in this) && ("type" in this)) { this._checkFileName(this.type); } - - } - _checkFileName(aValue) { + }, + _checkFileName: function(aValue) { let fn; try { fn = Services.mimeheader.getParameter(aValue, 'filename', this._charset, true, {}); @@ -270,24 +276,18 @@ class HttpVisitor extends Visitor { } } }; -Object.assign(HttpVisitor.prototype, { - cmpKeys: { - 'etag': true, // must not be modified from 200 to 206: - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7 - //'content-length': false, - 'last-modified': true, // may get omitted later, but should not change - 'content-encoding': true // must not change, or download will become - // corrupt. - }, -}); -class FtpVisitor extends Visitor { - constructor(nodes) { - super(nodes); - this.time = null; - } +function FtpVisitor(chan) { + Visitor.apply(this, arguments); +} - visitChan(chan) { +FtpVisitor.prototype = { + __proto__: Visitor.prototype, + cmpKeys: { + 'etag': true, + }, + time: null, + visitChan: function fv_visitChan(chan) { try { this.etag = chan.QueryInterface(Ci.nsIResumableChannel).entityID; let m = this.etag.match(/\/(\d{4})(\d{2})(\d{2})(?:(\d{2})(\d{2})(?:(\d{2})))?$/); @@ -307,22 +307,21 @@ class FtpVisitor extends Visitor { log(LOG_ERROR, "visitChan:", ex); } } -} -Object.assign(FtpVisitor.prototype, { - cmpKeys: { - 'etag': true, - }, -}); - -class VisitorManager { - constructor() { - this._visitors = {}; - } +}; +/** + * Visitor Manager c'tor + * + * @author Nils + */ +function VisitorManager() { + this._visitors = {}; +} +VisitorManager.prototype = { /** * Loads a ::save'd JS Array Will silently bypass failed items! */ - load(nodes) { + load: function vm_init(nodes) { for (let n of nodes) { try { let uri = Services.io.newURI(n.url, null, null); @@ -340,13 +339,13 @@ class VisitorManager { log(LOG_ERROR, "failed to read one visitor", ex); } } - } + }, /** * Saves/serializes the Manager and associated Visitors to an JS Array * * @return A ::load compatible Array */ - toJSON() { + toJSON: function vm_toJSON() { let rv = []; for (let x in this._visitors) { try { @@ -360,7 +359,7 @@ class VisitorManager { } } return rv; - } + }, /** * Visit and compare a channel * @@ -369,7 +368,7 @@ class VisitorManager { * if comparision yield a difference (i.e. channels are not * "compatible") */ - visit(chan) { + visit: function vm_visit(chan) { let url = chan.URI.spec; let visitor; @@ -392,7 +391,7 @@ class VisitorManager { this._visitors[url].compare(visitor); } return (this._visitors[url] = visitor); - } + }, /** * return the first timestamp registered with a visitor * @@ -407,6 +406,6 @@ class VisitorManager { } throw new Exception("No Date registered"); } -} +}; exports.VisitorManager = VisitorManager; diff --git a/modules/preferences.js b/modules/preferences.js index 277476b3c..00d633c89 100644 --- a/modules/preferences.js +++ b/modules/preferences.js @@ -12,12 +12,9 @@ const PREF_INT = Ci.nsIPrefBranch.PREF_INT; const PREF_BOOL = Ci.nsIPrefBranch.PREF_BOOL; const prefs = Services.prefs; -try { - if (!(prefs instanceof Ci.nsIPrefBranch2)) { - log(LOG_DEBUG, "simple prefs"); - } +if (!(prefs instanceof Ci.nsIPrefBranch2) || !(prefs instanceof Ci.nsIPrefBranch2)) { + log(LOG_DEBUG, "simple prefs"); } -catch (ex) { /* ignore */ } //Helper: get a (multi-byte) string function getMultiByte(key, defaultValue){ diff --git a/modules/support/alertservice.js b/modules/support/alertservice.js index 3803c6a2c..08329cf3b 100644 --- a/modules/support/alertservice.js +++ b/modules/support/alertservice.js @@ -36,23 +36,23 @@ XULAlertsService.prototype = Object.freeze({ QueryInterface: QI([Ci.nsIAlertsService]), - showAlertNotification: function showAlertNotification(image, title, text, click, c, ctx, name, dir, lang, prin) { + showAlertNotification: function showAlertNotification(imageUrl, title, text, textClickable, cookie, listener, name, dir, lang, principal) { try { let args = new Instances.SupportsArray(); - args.AppendElement(str(image)); + args.AppendElement(str(imageUrl)); args.AppendElement(str(title)); args.AppendElement(str(text)); - args.AppendElement(bool(click|| false)); - args.AppendElement(str(c|| "")); + args.AppendElement(bool(textClickable || false)); + args.AppendElement(str(cookie || "")); args.AppendElement(int(4)); // NS_ALERT_TOP; args.AppendElement(str(dir || "")); args.AppendElement(str(lang || "")); args.AppendElement(int(0)); // XXX implement replacement window if necessary - if (ctx) { + if (listener) { try { let ptr = new Instances.SupportsInterfacePointer(); - ptr.data = ctx.QueryInterface(Ci.nsIObserver); + ptr.data = listener.QueryInterface(Ci.nsIObserver); ptr.dataIID = Ci.nsIObserver; args.AppendElement(ptr); } diff --git a/modules/support/atoms.js b/modules/support/atoms.js index 80e582b8a..8bae757ec 100644 --- a/modules/support/atoms.js +++ b/modules/support/atoms.js @@ -11,16 +11,16 @@ const COMMON_ATOMS = [ const _as = Cc["@mozilla.org/atom-service;1"].getService(Ci.nsIAtomService); -class Atoms { - constructor(...args) { - for (let i = 0; i < args.length; ++i) { - this.getAtom(args[i]); - } +function Atoms() { + for (let i = 0; i < arguments.length; ++i) { + this.getAtom(arguments[i]); } - getAtom(atom) { +} +Atoms.prototype = { + getAtom: function(atom) { return this[atom] || (this[atom] = _as.getAtom(atom)); } -} +}; exports.Atoms = Atoms; for (let atom of COMMON_ATOMS) { diff --git a/modules/support/batchgen.js b/modules/support/batchgen.js index 5d1761959..c51c7e217 100644 --- a/modules/support/batchgen.js +++ b/modules/support/batchgen.js @@ -9,25 +9,26 @@ const {range} = require("utils"); * Simple literal * @param str (string) Literal */ -class Literal { - constructor(str) { - this.str = str; - this.first = this.last = this.str; - this.length = 1; - } - *join(str) { +function Literal(str) { + this.str = str; + this.first = this.last = this.str; + this.length = 1; +} +Literal.prototype = { + join: function*(str) { yield str + this.str; - } - toString() { + }, + toString: function() { return this.str; } -} +}; /** * Abstract base class for Ranges (Numeric, Alpha, ...) */ -class Range { - constructor(name, start, stop, step) { +function Range() {} +Range.prototype = { + init: function(name, start, stop, step) { stop += -Math.abs(step)/step; stop += step - ((stop - start) % step); @@ -38,13 +39,13 @@ class Range { this.length = Math.floor((stop - start) / step); this.first = this.format(this.start); this.last = this.format(this.stop - this.step); - } - *join(str) { + }, + join: function*(str) { for (let i of range(this.start, this.stop, this.step)) { - yield `${str}${this.format(i)}`; + yield (str + this.format(i)); } } -} +}; /** * Numeric range @@ -54,12 +55,13 @@ class Range { * @param step (int) Range step * @param strl (int) Minimal length of the numeric literals to produce */ -class NumericRange extends Range { - constructor(name, start, stop, step, strl) { - super(name, start, stop + (step > 0 ? 1 : -1), step); - this.strl = strl; - } - format(val) { +function NumericRange(name, start, stop, step, strl) { + this.strl = strl; + this.init(name, start, stop + (step > 0 ? 1 : -1), step); +} +NumericRange.prototype = { + __proto__: Range.prototype, + format: function(val) { let rv = Math.abs(val).toString(); while (rv.length < this.strl) { rv = '0' + rv; @@ -69,7 +71,7 @@ class NumericRange extends Range { } return rv; } -} +}; /** * Alpha (Character) Range @@ -78,14 +80,13 @@ class NumericRange extends Range { * @param stop (int) Range stop/end * @param step (int) Range step */ -class CharRange extends Range { - constructor(name, start, stop, step) { - super(name, start, stop + (step > 0 ? 1 : -1), step); - } - format(val) { - return String.fromCharCode(val); - } +function CharRange(name, start, stop, step) { + this.init(name, start, stop + (step > 0 ? 1 : -1), step); } +CharRange.prototype = { + __proto__: Range.prototype, + format: String.fromCharCode +}; /** * Batch generator. @@ -94,113 +95,112 @@ class CharRange extends Range { * * @param link URL to parse */ -class BatchGenerator { - constructor(link) { - this.url = link.url; - let url = link.usable; - this._length = 1; - this._pats = []; - let i; +function BatchGenerator(link) { + this.url = link.url; + let url = link.usable; + this._length = 1; + this._pats = []; + let i; - // search all batchdescriptors - while ((i = url.search(/\[.*?]/)) !== -1) { - // Heading string is a simple Literal - if (i !== 0) { - this._pats.push(new Literal(url.substring(0, i))); - url = url.slice(i); - } + // search all batchdescriptors + while ((i = url.search(/\[.*?]/)) !== -1) { + // Heading string is a simple Literal + if (i !== 0) { + this._pats.push(new Literal(url.substring(0, i))); + url = url.slice(i); + } - let m; - // Numeric range syntax - if ((m = url.match(/^\[(-?\d+):(-?\d+)(?::(-?\d+))?\]/))) { - url = url.slice(m[0].length); - try { - let start = parseInt(m[1], 10); - let stop = parseInt(m[2], 10); - let step = stop > start ? 1 : -1; - if (m.length > 3 && typeof(m[3]) !== 'undefined') { - step = parseInt(m[3], 10); - } - this._checkRange(start, stop, step); - if (start === stop) { - this._pats.push(new Literal(m[1])); - continue; - } - var x = m[Math.abs(start) > Math.abs(stop) ? 2 : 1]; - var sl = x.length; - if (x.slice(0,1) === '-') { - --sl; - } - this._pats.push(new NumericRange(m[0], start, stop, step, sl)); + let m; + // Numeric range syntax + if ((m = url.match(/^\[(-?\d+):(-?\d+)(?::(-?\d+))?\]/))) { + url = url.slice(m[0].length); + try { + let start = parseInt(m[1], 10); + let stop = parseInt(m[2], 10); + let step = stop > start ? 1 : -1; + if (m.length > 3 && typeof(m[3]) !== 'undefined') { + step = parseInt(m[3], 10); + } + this._checkRange(start, stop, step); + if (start === stop) { + this._pats.push(new Literal(m[1])); + continue; } - catch (ex) { - log(LOG_ERROR, "Bad Numeric Range", ex); - this._pats.push(new Literal(m[0])); + var x = m[Math.abs(start) > Math.abs(stop) ? 2 : 1]; + var sl = x.length; + if (x.slice(0,1) === '-') { + --sl; } - continue; + this._pats.push(new NumericRange(m[0], start, stop, step, sl)); } + catch (ex) { + log(LOG_ERROR, "Bad Numeric Range", ex); + this._pats.push(new Literal(m[0])); + } + continue; + } - // Alpha range syntax - if ((m = url.match(/^\[([a-z]):([a-z])(?::(-?\d))?\]/)) || - (m = url.match(/\[([A-Z]):([A-Z])(?::(-?\d))?\]/))) { - url = url.slice(m[0].length); - try { - let start = m[1].charCodeAt(0); - let stop = m[2].charCodeAt(0); - let step = stop > start ? 1 : -1; - if (m.length > 3 && typeof(m[3]) !== 'undefined') { - step = parseInt(m[3], 10); - } - this._checkRange(start, stop, step); - if (start === stop) { - this._pats.push(new Literal(m[1])); - continue; - } - this._pats.push(new CharRange(m[0], start, stop, step)); + // Alpha range syntax + if ((m = url.match(/^\[([a-z]):([a-z])(?::(-?\d))?\]/)) || (m = url.match(/\[([A-Z]):([A-Z])(?::(-?\d))?\]/))) { + url = url.slice(m[0].length); + try { + let start = m[1].charCodeAt(0); + let stop = m[2].charCodeAt(0); + let step = stop > start ? 1 : -1; + if (m.length > 3 && typeof(m[3]) !== 'undefined') { + step = parseInt(m[3], 10); } - catch (ex) { - log(LOG_ERROR, "Bad Char Range", ex); - this._pats.push(new Literal(m[0])); + this._checkRange(start, stop, step); + if (start === stop) { + this._pats.push(new Literal(m[1])); + continue; } - continue; + this._pats.push(new CharRange(m[0], start, stop, step)); } - - // Unknown/invalid descriptor - // Insert as Literal - if ((m = url.match(/^\[.*?]/))) { - url = url.slice(m[0].length); + catch (ex) { + log(LOG_ERROR, "Bad Char Range", ex); this._pats.push(new Literal(m[0])); - continue; } - - // Something very bad happened. Should never get here. - throw new Exception("Failed to parse the expression"); - } - // URL got a literal tail. Insert. - if (url.length) { - this._pats.push(new Literal(url)); + continue; } - // Join successive Literals. This will produce a faster generation later. - for (i = this._pats.length - 2; i >= 0; --i) { - if ((this._pats[i] instanceof Literal) && (this._pats[i + 1] instanceof Literal)) { - this._pats[i] = new Literal(this._pats[i].str + this._pats[i + 1].str); - this._pats.splice(i + 1, 1); - } + // Unknown/invalid descriptor + // Insert as Literal + if ((m = url.match(/^\[.*?]/))) { + url = url.slice(m[0].length); + this._pats.push(new Literal(m[0])); + continue; } - // Calculate the total length of the batch - for (let i of this._pats) { - this._length *= i.length; + // Something very bad happened. Should never get here. + throw new Exception("Failed to parse the expression"); + } + // URL got a literal tail. Insert. + if (url.length) { + this._pats.push(new Literal(url)); + } + + // Join successive Literals. This will produce a faster generation later. + for (i = this._pats.length - 2; i >= 0; --i) { + if ((this._pats[i] instanceof Literal) && (this._pats[i + 1] instanceof Literal)) { + this._pats[i] = new Literal(this._pats[i].str + this._pats[i + 1].str); + this._pats.splice(i + 1, 1); } } - _checkRange(start, stop, step) { + + // Calculate the total length of the batch + for (let i of this._pats) { + this._length *= i.length; + } +} +BatchGenerator.prototype = { + _checkRange: function(start, stop, step) { // validate the range if (!step || (stop - start) / step < 0) { throw new Exception("step invalid!"); } - } - *_process(pats) { + }, + _process: function*(pats) { // Recursively called ;) // Keep this "static" @@ -214,24 +214,24 @@ class BatchGenerator { yield j; } } - } + }, /** * Generates all URLs * @return (generator) All URLs according to any batch descriptors */ - *getURLs() { + getURLs: function*() { for (let i of this._process(this._pats)) { yield i; } - } + }, /** * Expected number of generated Links */ get length() { return this._length; - } + }, /** * All matched batch descriptors @@ -242,7 +242,7 @@ class BatchGenerator { .filter(function(e) { return !(e instanceof Literal); }) .map(function(e) { return e.name; }) .join(", "); - } + }, /** * First URL that will be generated @@ -253,8 +253,7 @@ class BatchGenerator { return p.first; } ).join(''); - } - + }, /** * Last URL that will be generated */ @@ -265,6 +264,5 @@ class BatchGenerator { } ).join(''); } -} - +}; exports.BatchGenerator = BatchGenerator; diff --git a/modules/support/contenthandling.js b/modules/support/contenthandling.js index 747778338..02a14bcdc 100644 --- a/modules/support/contenthandling.js +++ b/modules/support/contenthandling.js @@ -12,6 +12,8 @@ const REGEXP_SWF = /\.swf\b/i; const REGEXP_CT = /\b(flv|ogg|ogm|avi|divx|mp4v|webm)\b/i; const REGEXP_STARTPARAM = /start=\d+&?/; +const {Task} = requireJSM("resource://gre/modules/Task.jsm"); + const { registerPrivatePurger, unregisterPrivatePurger, @@ -23,51 +25,40 @@ const obs = require("./observers"); const {modifyURL} = require("./requestmanipulation"); -class ContextLRUMap { - constructor (num) { - this._normal = new LRUMap(num); - this._private = new LRUMap(num); - } - _m(isPrivate) { - return isPrivate ? this._private : this._normal; - } - "get"(key, isPrivate) { - return this._m(isPrivate).get(key); - } - "set"(key, val, isPrivate) { - return this._m(isPrivate).set(key, val); - } - has(key, isPrivate) { - return this._m(isPrivate).has(key); - } - "delete"(key, isPrivate) { - return this._m(isPrivate).delete(key); - } - clear() { +function ContextLRUMap(num) { + this._normal = new LRUMap(num); + this._private = new LRUMap(num); +} +ContextLRUMap.prototype = { + _m: function(isPrivate) { return isPrivate ? this._private : this._normal; }, + get: function(key, isPrivate) { return this._m(isPrivate).get(key); }, + has: function(key, isPrivate) { return this._m(isPrivate).has(key); }, + set: function(key, val, isPrivate) { return this._m(isPrivate).set(key, val); }, + "delete": function(key, isPrivate) { return this._m(isPrivate).delete(key); }, + clear: function() { this._normal.clear(); this._private.clear(); - } - clearPrivate() { + }, + clearPrivate: function() { this._private.clear(); } -} +}; /** * ContentHandling */ -exports.ContentHandling = new class { - constructor() { - this.classDescription = "DownThemAll! ContentHandling"; - this.classID = Components.ID("366982b8-9db9-4383-aae7-dbc2f40ba6f6"); - this.contractID = "@downthemall.net/content/redirects;1"; - this.xpcom_categories = ["net-channel-event-sinks"]; - this.QueryInterface = QI([ - Ci.nsIObserver, - Ci.nsIURIContentListener, - Ci.nsIFactory, - Ci.nsIChannelEventSink] - ); +function ContentHandlingImpl() { + this._init(); +} +ContentHandlingImpl.prototype = { + classDescription: "DownThemAll! ContentHandling", + classID: Components.ID("366982b8-9db9-4383-aae7-dbc2f40ba6f6"), + contractID: "@downthemall.net/content/redirects;1", + xpcom_categories: ["net-channel-event-sinks"], + QueryInterface: QI([Ci.nsIObserver, Ci.nsIURIContentListener, Ci.nsIFactory, Ci.nsIChannelEventSink]), + + _init: function ct__init() { obs.add(this, "http-on-modify-request"); require("components").registerComponents([this], true); @@ -92,9 +83,9 @@ exports.ContentHandling = new class { this.globalMM.removeDelayedFrameScript(fs); }); unload(this._uninit.bind(this)); - } + }, - _uninit() { + _uninit: function ct__uninit() { Services.prefs.removeObserver('extensions.dta.listsniffedvideos', this); if (this.sniffVideos) { this.sniffVideos = false; @@ -102,16 +93,16 @@ exports.ContentHandling = new class { } unregisterPrivatePurger(this.boundPurge); obs.remove(this, 'http-on-modify-request'); - } - registerHttpObservers() { + }, + registerHttpObservers: function ct_registerHttpObservers() { obs.add(this, 'http-on-examine-response'); obs.add(this, 'http-on-examine-cached-response'); - } - unregisterHttpObservers() { + }, + unregisterHttpObservers: function ct_unregisterHttpObservers() { obs.remove(this, 'http-on-examine-response'); obs.remove(this, 'http-on-examine-cached-response'); - } - observe(subject, topic, data) { + }, + observe: function ct_observe(subject, topic, data) { switch(topic) { case 'http-on-modify-request': this.observeRequest(subject, topic, data); @@ -139,8 +130,8 @@ exports.ContentHandling = new class { } break; } - } - observeRequest(channel, topic, data) { + }, + observeRequest: function ct_observeRequest(channel, topic, data) { if ( !(channel instanceof Ci.nsIHttpChannel) || !(channel instanceof Ci.nsIUploadChannel)) { @@ -184,8 +175,8 @@ exports.ContentHandling = new class { catch (ex) { log(LOG_ERROR, "observe request", ex); } - } - observeResponse(channel, topic, data) { + }, + observeResponse: function ct_observeResponse(channel, topic, data) { if (!this.sniffVideos || !(channel instanceof Ci.nsIHttpChannel)) { return; } @@ -206,110 +197,107 @@ exports.ContentHandling = new class { if (!(REGEXP_MEDIA.test(spec) && !REGEXP_SWF.test(spec)) && !REGEXP_CT.test(ct)) { return; } - this._observeResponseAsync(channel); - } - catch (ex) { - log(LOG_ERROR, "observe response", ex); - } - } - async _observeResponseAsync(channel) { - let uri = null; - let lc = null; - if (channel instanceof Ci.nsIInterfaceRequestor) { - try { - lc = channel.getInterface(Ci.nsILoadContext); - } - catch (ex) { - // ignored - } - } - if (!lc) { - try { - lc = channel.notificationCallbacks.getInterface(Ci.nsILoadContext); - } - catch (ex) { - // ignored - } - } - if (lc) { - try { - log(LOG_DEBUG, "got load context"); + let uri = null; + let lc = null; + if (channel instanceof Ci.nsIInterfaceRequestor) { try { - let wnd = lc.topWindow; - uri = Services.io.newURI(wnd.location.href, wnd.document.characterSet, null); - log(LOG_DEBUG, "got uri from lctw " + uri.spec); + lc = channel.getInterface(Ci.nsILoadContext); } catch (ex) { + + } + } + Task.spawn(function*() { + if (!lc) { try { - let tfe = lc.topFrameElement; - let mm = tfe.messageManager; - let wnd = await new Promise((resolve, reject) => { - let topic = `DTA::getURI:${this.getUriJob++}`; - mm.addMessageListener(topic, function load(m) { - mm.removeMessageListener(topic, load); - resolve(m.data); - }); - mm.sendAsyncMessage("DTA:ch:getURI", { - topic: topic - }); - }); - uri = Services.io.newURI(wnd.location, wnd.characterSet, null); - log(LOG_DEBUG, "got uri from lctfe " + uri.spec); + lc = channel.notificationCallbacks.getInterface(Ci.nsILoadContext); } catch (ex) { - log(LOG_DEBUG, "Cannot get from lc", ex); + } } - } - catch (ex) { - // no op - } - } - if (!uri) { - try { - let wp; - if (!uri && channel.loadGroup && channel.loadGroup.groupObserver) { - wp = channel.loadGroup.groupObserver.QueryInterface(Ci.nsIWebProgress); + if (lc) { + try { + log(LOG_DEBUG, "got load context"); + try { + let wnd = lc.topWindow; + uri = Services.io.newURI(wnd.location.href, wnd.document.characterSet, null); + log(LOG_DEBUG, "got uri from lctw " + uri.spec); + } + catch (ex) { + try { + let tfe = lc.topFrameElement; + let mm = tfe.messageManager; + let wnd = yield new Promise((resolve, reject) => { + let topic = `DTA::getURI:${this.getUriJob++}`; + mm.addMessageListener(topic, function load(m) { + mm.removeMessageListener(topic, load); + resolve(m.data); + }); + mm.sendAsyncMessage("DTA:ch:getURI", { + topic: topic + }); + }); + uri = Services.io.newURI(wnd.location, wnd.characterSet, null); + log(LOG_DEBUG, "got uri from lctfe " + uri.spec); + } + catch (ex) { + log(LOG_DEBUG, "Cannot get from lc", ex); + } + } + } + catch (ex) { + // no op + } } - if (!uri && !wp) { - wp = channel.notificationCallbacks.getInterface(Ci.nsIWebProgress); + if (!uri) { + try { + let wp; + if (!uri && channel.loadGroup && channel.loadGroup.groupObserver) { + wp = channel.loadGroup.groupObserver.QueryInterface(Ci.nsIWebProgress); + } + if (!uri && !wp) { + wp = channel.notificationCallbacks.getInterface(Ci.nsIWebProgress); + } + if (!wp || !wp.DOMWindow) { + return; + } + let wn = wp.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation); + if (!wn || !wn.currentURI) { + return; + } + uri = wn.currentURI; + } + catch (ex) {} } - if (!wp || !wp.DOMWindow) { + if (!uri) { + log(LOG_DEBUG, "Failed to get video doc uri"); return; } - let wn = wp.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation); - if (!wn || !wn.currentURI) { + log(LOG_DEBUG, channel.URI.spec + " -> " + uri.spec); + if (!uri.schemeIs('http') && !uri.schemeIs('https') && !uri.schemeIs('ftp') && !uri.schemeIs("data")) { return; } - uri = wn.currentURI; - } - catch (ex) { - // ignored - } - } - if (!uri) { - log(LOG_DEBUG, "Failed to get video doc uri"); - return; + this._registerVideo(uri, channel.URI, isChannelPrivate(channel)); + }.bind(this)); } - log(LOG_DEBUG, channel.URI.spec + " -> " + uri.spec); - if (!uri.schemeIs('http') && !uri.schemeIs('https') && !uri.schemeIs('ftp') && !uri.schemeIs("data")) { - return; + catch (ex) { + log(LOG_ERROR, "observe response", ex); } - this._registerVideo(uri, channel.URI, isChannelPrivate(channel)); - } + }, + _sniffVideos: false, get sniffVideos() { return this._sniffVideos; - } + }, set sniffVideos(nv) { this._sniffVideos = nv; if (!nv) { this._videos.clear(); } return nv; - } - - _registerVideo(uri, vid, isPrivate) { + }, + _registerVideo: function(uri, vid, isPrivate) { // sanitize vid and remove the start param vid = vid.clone(); if (vid instanceof Ci.nsIURL) { @@ -324,23 +312,23 @@ exports.ContentHandling = new class { nv.push(vid); this._videos.set(uri, nv, isPrivate); } - } + }, - getPostDataFor(uri, isPrivate) { + getPostDataFor: function(uri, isPrivate) { if (uri instanceof Ci.nsIURI) { uri = uri.spec; } return this._data.get(uri, isPrivate) || ""; - } - getSniffedVideosFor(uri, isPrivate) { + }, + getSniffedVideosFor: function(uri, isPrivate) { if (uri instanceof Ci.nsIURI) { uri = uri.spec; } return (this._videos.get(uri, isPrivate) || []).map(a => a.clone()); - } + }, // nsIChannelEventSink - asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) { + asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) { try { this.onChannelRedirect(oldChannel, newChannel, flags); } @@ -348,16 +336,16 @@ exports.ContentHandling = new class { log(LOG_ERROR, "asyncOnChannelRedirect", ex); } callback.onRedirectVerifyCallback(0); - } - onChannelRedirect(oldChannel, newChannel, flags) { + }, + onChannelRedirect: function(oldChannel, newChannel, flags) { let oldURI = oldChannel.URI.spec; let newURI = newChannel.URI.spec; let isPrivate = isChannelPrivate(oldChannel); oldURI = this._revRedirects.get(oldURI, isPrivate) || oldURI; this._redirects.set(oldURI, newURI, isPrivate); this._revRedirects.set(newURI, oldURI, isPrivate); - } - getRedirect(uri, isPrivate) { + }, + getRedirect: function(uri, isPrivate) { let rv = this._revRedirects.get(uri.spec, isPrivate); if (!rv) { return uri; @@ -368,20 +356,21 @@ exports.ContentHandling = new class { catch (ex) { return uri; } - } - clear() { + }, + clear: function() { this._data = new ContextLRUMap(5); this._videos = new ContextLRUMap(20); this._redirects = new ContextLRUMap(20); this._revRedirects = new ContextLRUMap(100); - } - purge() { + }, + purge: function() { this._data.clearPrivate(); this._videos.clearPrivate(); this._redirects.clearPrivate(); this._revRedirects.clearPrivate(); log(LOG_DEBUG, "purged private data"); } -}(); +}; +exports.ContentHandling = new ContentHandlingImpl(); Object.freeze(exports); diff --git a/modules/support/cothreads.js b/modules/support/cothreads.js index d287a3a62..a98a9267a 100644 --- a/modules/support/cothreads.js +++ b/modules/support/cothreads.js @@ -5,14 +5,13 @@ const {defer} = require("./defer"); -class CoThreadBase { - constructor(func, yieldEvery, thisCtx) { - this._idx = 0; - this._ran = false; - this._finishFunc = null; - this._thisCtx = thisCtx ? thisCtx : this; - this._terminated = false; +const CoThreadBase = { + _idx: 0, + _ran: false, + _finishFunc: null, + init: function CoThreadBase_init(func, yieldEvery, thisCtx) { + this._thisCtx = thisCtx ? thisCtx : this; // default to 0 (adjust) this._yieldEvery = typeof yieldEvery === 'number' ? Math.floor(yieldEvery) : 0; @@ -22,18 +21,22 @@ class CoThreadBase { } this._func = func; this.init = function() {}; - } + }, - start(finishFunc) { + start: function CoThreadBase_run(finishFunc) { if (this._ran) { throw new Error("You cannot run a CoThread/CoThreadListWalker instance more than once."); } this._finishFunc = finishFunc; this._ran = true; defer(this, 0); - } + }, - run() { + QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsICancelable, Ci.nsIRunnable]), + + _terminated: false, + + run: function CoThreadBase_run() { if (this._terminated) { return; } @@ -89,9 +92,9 @@ class CoThreadBase { catch (ex) { this.cancel(); } - } + }, - cancel() { + cancel: function CoThreadBase_cancel() { if (this._terminated) { return; } @@ -100,11 +103,7 @@ class CoThreadBase { this._finishFunc.call(this._thisCtx, this._yieldEvery); } } -} -Object.assign(CoThreadBase.prototype, { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsICancelable, Ci.nsIRunnable]), -}); - +}; /** * Constructs a new CoThread (aka. pseudo-thread). @@ -132,20 +131,23 @@ Object.assign(CoThreadBase.prototype, { * Optional. The function will be called in the scope of this object * (or if omitted in the scope of the CoThread instance) */ -exports.CoThread = class CoThread extends CoThreadBase { - constructor(func, yieldEvery, thisCtx) { - super(func, yieldEvery, thisCtx); - // fake generator so we may use a common implementation. ;) - this._generator = (function*() { - for(;;) { - yield null; - } - })(); - } - _callf(ctx, i, idx, fn) { - return fn.call(ctx, idx); - } +exports.CoThread = function CoThread(func, yieldEvery, thisCtx) { + this.init(func, yieldEvery, thisCtx); + // fake generator so we may use a common implementation. ;) + this._generator = (function*() { + for(;;) { + yield null; + } + })(); }; +exports.CoThread.prototype = Object.create(CoThreadBase, { + _callf: { + value: function CoThread__callf(ctx, i, idx, fn) { + return fn.call(ctx, idx); + }, + enumerable: true + } +}); /** * Constructs a new CoThreadInterleaved (aka. pseudo-thread). @@ -177,15 +179,16 @@ exports.CoThread = class CoThread extends CoThreadBase { * Optional. The function will be called in the scope of this object * (or if omitted in the scope of the CoThread instance) */ -exports.CoThreadInterleaved = class CoThreadInterleaved extends CoThreadBase { - constructor (generator, yieldEvery, thisCtx) { - super(() => true, yieldEvery, thisCtx); - this._generator = typeof(generator) === "function" ? generator() : generator; - } - _callf() { - return true; - } +exports.CoThreadInterleaved = function CoThreadInterleaved(generator, yieldEvery, thisCtx) { + this.init(() => true, yieldEvery, thisCtx); + this._generator = typeof(generator) === "function" ? generator() : generator; }; +exports.CoThreadInterleaved.prototype = Object.create(CoThreadBase, { + _callf: { + value: function() { return true; }, + enumerable: true + } +}); /** * Constructs a new CoThreadListWalker (aka. pseudo-thread). @@ -219,27 +222,30 @@ exports.CoThreadInterleaved = class CoThreadInterleaved extends CoThreadBase { * Optional. The function will be called in the scope of this object * (or if omitted in the scope of the CoThread instance) */ -exports.CoThreadListWalker = class CoThreadListWalker extends CoThreadBase { - constructor(func, arrayOrGenerator, yieldEvery, thisCtx) { - super(func, yieldEvery, thisCtx); - - if (Array.isArray(arrayOrGenerator)) { - // make a generator - this._generator = (function*() { - for (let i of arrayOrGenerator) { - yield i; - } - })(); - } - else { - this._generator = arrayOrGenerator; - } +exports.CoThreadListWalker = function CoThreadListWalker(func, arrayOrGenerator, yieldEvery, thisCtx) { + this.init(func, yieldEvery, thisCtx); - if (this._lastFunc && (typeof func !== 'function' && !(func instanceof Function))) { - throw Cr.NS_ERROR_INVALID_ARG; - } + if (Array.isArray(arrayOrGenerator)) { + // make a generator + this._generator = (function*() { + for (let i of arrayOrGenerator) { + yield i; + } + })(); } - _callf(ctx, item, idx, fn) { - return fn.call(ctx, item, idx); + else { + this._generator = arrayOrGenerator; + } + + if (this._lastFunc && (typeof func !== 'function' && !(func instanceof Function))) { + throw Cr.NS_ERROR_INVALID_ARG; } }; +exports.CoThreadListWalker.prototype = Object.create(CoThreadBase, { + _callf: { + value: function CoThreadListWalker__callf(ctx, item, idx, fn) { + return fn.call(ctx, item, idx); + }, + enumerable: true + } +}); diff --git a/modules/support/defer.js b/modules/support/defer.js index 001276ccd..e303c1516 100644 --- a/modules/support/defer.js +++ b/modules/support/defer.js @@ -3,7 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -Cu.import("resource://gre/modules/Timer.jsm", exports); +// Only used to dispatch runnables to the main thread, as a cheap alternative to setTimeout/nsITimer Object.defineProperty(exports, "defer", { value: (function setup() { @@ -16,8 +16,6 @@ Object.defineProperty(exports, "defer", { }; } else { - // Only used to dispatch runnables to the main thread, as a cheap - // alternative to setTimeout/nsITimer const MainThread = Services.tm.mainThread; return function defer(fn, ctx) { if (ctx) { diff --git a/modules/support/domainprefs.js b/modules/support/domainprefs.js index 649e1b768..1c1c0ba43 100644 --- a/modules/support/domainprefs.js +++ b/modules/support/domainprefs.js @@ -3,83 +3,12 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -const DOMAINS_FILE = "domain-prefs.json"; +// for now domain prefs are non-persistent +// XXX Make persistent and migrate limits -const {notifyLocal} = require("./observers"); const {symbolize} = require("./stringfuncs"); -const {DeferredSave} = requireJSM("resource://gre/modules/DeferredSave.jsm"); -const storedDomains = new Map(); -const privDomains = new LRUMap(200); - -const PENDING = Symbol(); - -class Saver extends DeferredSave { - constructor() { - let file = require("api").getProfileFile(DOMAINS_FILE, true).path; - super(file, () => this.serialize(), 1000); - this.load(); - } - get file() { - return this._path || this._file; - } - load() { - if (!this[PENDING]) { - this[PENDING] = this._loadAsync(); - } - return this[PENDING]; - } - _deferredSave() { - super._deferredSave(); - notifyLocal("DTA:domain-prefs", null, null); - } - serialize() { - let rv = []; - for (let [domain, prefs] of storedDomains.entries()) { - domain = Symbol.keyFor(domain); - if (!domain) { - continue; - } - let cur = []; - for (let [pref, value] of prefs.entries()) { - pref = Symbol.keyFor(pref); - cur.push([pref, value]); - } - if (cur.length) { - rv.push([domain, cur]); - } - } - return JSON.stringify(rv); - } - async _loadAsync() { - try { - let req = await fetch(Services.io.newFileURI(new Instances.LocalFile(this.file)).spec); - let json = await req.json(); - for (let [domain, prefs] of json) { - domain = Symbol.for(domain); - for (let [pref, value] of prefs) { - let prefs = storedDomains.get(domain); - if (!prefs) { - prefs = new Map(); - storedDomains.set(domain, prefs); - } - prefs.set(symbolize(pref), value); - } - } - } - catch (ex) { - this.saveChanges(); - } - } -} - -let saver = new Saver(); -unload(function() { - if (saver) { - saver.flush(); - } - saver = null; -}); +const domains = new LRUMap(500); function domain(url, tld) { try { @@ -87,7 +16,7 @@ function domain(url, tld) { } catch (ex) { try { - log(LOG_DEBUG, "Failed to get tld for " + (url.spec || url)); + log(LOG_ERROR, "Failed to get tld for " + (url.spec || url)); return url.host; } catch (ex) { @@ -96,60 +25,41 @@ function domain(url, tld) { } } -function _getPref(dom, pref, defaultValue, options) { - let prefs = null; - if (options && options.isPrivate) { - prefs = privDomains.get(dom); - } - if (!prefs) { - prefs = storedDomains.get(dom); +function getPref(url, pref, defaultValue, tld) { + let dom = domain(url, tld); + if (!dom) { + return defaultValue; } + let prefs = domains.get(Symbol.for(dom)); if (!prefs) { return defaultValue; } return prefs.get(symbolize(pref)) || defaultValue; } -function getPref(url, pref, defaultValue, options) { - let dom = domain(url, options && options.tld); +function setPref(url, pref, value, tld) { + let dom = domain(url, tld); if (!dom) { - return defaultValue; + // We cannot store for stuff we cannot get a domain from + // then again, no big deal, since the prefs are not persistent anyway at the moment + // XXX this may change + return; } - return _getPref(Symbol.for(dom), pref, defaultValue, options); -} - -function getHost(host, pref, defaultValue) { - return _getPref(symbolize(host), pref, defaultValue); -} - -function _setPref(dom, pref, value, options) { - let domains = (options && options.isPrivate) ? privDomains : storedDomains; + dom = Symbol.for(dom); let prefs = domains.get(dom); if (!prefs) { prefs = new Map(); domains.set(dom, prefs); } prefs.set(symbolize(pref), value); - saver.saveChanges(); } -function setPref(url, pref, value, options) { - let dom = domain(url, options && options.tld); +function deletePref(url, pref, tld) { + let dom = domain(url, tld); if (!dom) { - // We cannot store for stuff we cannot get a domain from - // then again, no big deal, since the prefs are not persistent anyway at the moment - // XXX this may change return; } - return _setPref(Symbol.for(dom), pref, value, options); -} - -function setHost(host, pref, value) { - return _setPref(symbolize(host), pref, value); -} - -function _deletePref(dom, pref, options) { - let domains = (options && options.isPrivate) ? privDomains : storedDomains; + dom = Symbol.for(dom); let prefs = domains.get(dom); if (!prefs) { return; @@ -158,89 +68,37 @@ function _deletePref(dom, pref, options) { if (!prefs.size) { domains.delete(dom); } - saver.saveChanges(); -} - -function deletePref(url, pref, options) { - let dom = domain(url, options && options.tld); - if (!dom) { - return; - } - return _deletePref(Symbol.for(dom), pref, options); -} - -function deleteHost(host, pref) { - return _deletePref(symbolize(host), pref); -} - - -function* enumHosts() { - for (let domain of storedDomains.keys()) { - domain = Symbol.keyFor(domain); - if (domain) { - yield domain; - } - } } Object.defineProperties(exports, { - "load": { - value: () => saver.load(), - enumerable: true - }, "get": { value: getPref, enumerable: true }, - "getHost": { - value: getHost, - enumerable: true - }, "set": { value: setPref, enumerable: true }, - "setHost": { - value: setHost, - enumerable: true - }, "delete": { value: deletePref, enumerable: true }, - "deleteHost": { - value: deleteHost, - enumerable: true - }, "getTLD": { - value: function(url, pref, defaultValue, isPrivate) { - return getPref(url, pref, defaultValue, { - tld: true, - isPrivate - }); + value: function(url, pref, defaultValue) { + return getPref(url, pref, defaultValue, true); }, enumerable: true }, "setTLD": { - value: function(url, pref, value, isPrivate) { - return setPref(url, pref, value, { - tld: true, - isPrivate - }); + value: function(url, pref, value) { + return setPref(url, pref, value, true); }, enumerable: true }, "deleteTLD": { - value: function(url, pref, isPrivate) { - return deletePref(url, pref, { - tld: true, - isPrivate - }); + value: function(url, pref, value) { + return deletePref(url, pref, true); }, enumerable: true - }, - "enumHosts": { - value: enumHosts, - enumerable: true } }); diff --git a/modules/support/fileextsheet.js b/modules/support/fileextsheet.js index c85109afc..a30ddecfb 100644 --- a/modules/support/fileextsheet.js +++ b/modules/support/fileextsheet.js @@ -4,19 +4,21 @@ "use strict"; const {Atoms} = require("./atoms"); -const {setTimeout} = new require("./defer"); +const Timers = new (require("./timers").TimerManager)(); const {getIcon} = require("./icons"); const {getExtension} = require("./stringfuncs"); const {identity} = require("./memoize"); -class FileExtensionSheet { - constructor(window, tree) { - this._tree = tree; - this._windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - this._entries = new Map(); - this._toadd = []; - } - getAtom(fileName, metalink, invalidate) { +function FileExtensionSheet(window, tree) { + this._tree = tree; + this._windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + this._entries = new Map(); + this._toadd = []; +} + +FileExtensionSheet.prototype = Object.freeze({ + _atoms: new Atoms(), + getAtom: function(fileName, metalink, invalidate) { let ext = getExtension(fileName); if (!ext || ext.length > 10 || ext.indexOf(" ") > -1) { ext = 'unknown'; @@ -43,22 +45,18 @@ treechildren::-moz-tree-image(iconic,${entry.toString()}) { }`; this._toadd.push(rule); if (!this._timer) { - this._timer = setTimeout(() => { - delete this._timer; - this.add(true); - }, 0); + this._timer = Timers.createOneshot(0, () => this.add(true)); } this._entries.set(ext, entry); } return this._atoms.getAtom(entry); - } - add(invalidate) { + }, + add: function(invalidate) { + this._timer = null; if (!this._toadd.length) { return; } - let rule = - 'data:text/css,@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");\n' + - this._toadd.join("\n"); + let rule = `data:text/css,@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");\n${this._toadd.join("\n")}`; this._toadd = []; try { log(LOG_DEBUG, "new sheet: " + rule); @@ -71,6 +69,6 @@ treechildren::-moz-tree-image(iconic,${entry.toString()}) { log(LOG_ERROR, "sheet: " + rule, ex); } } -} -FileExtensionSheet.prototype._atoms = new Atoms(); +}); + exports.FileExtensionSheet = Object.freeze(FileExtensionSheet); diff --git a/modules/support/filtermanager.js b/modules/support/filtermanager.js index d0cd81c4a..e27706227 100644 --- a/modules/support/filtermanager.js +++ b/modules/support/filtermanager.js @@ -2,7 +2,7 @@ * 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"; -/* global TextDecoder */ +/*globals TextDecoder */ const PREF_FILTERS_BASE = 'extensions.dta.filters.'; const LINK_FILTER = (1<<0); @@ -20,6 +20,7 @@ const Preferences = require("preferences"); const RegExpMerger = require("./regexpmerger"); const {mapInSitu} = require("utils"); const {OS} = requireJSM("resource://gre/modules/osfile.jsm"); +const {Task} = requireJSM("resource://gre/modules/Task.jsm"); const {DeferredSave} = requireJSM("resource://gre/modules/DeferredSave.jsm"); const nsITimer = Ci.nsITimer; @@ -87,34 +88,37 @@ function consolidateRegs(regs) { * FilterManager */ // no not create DTA_Filter yourself, managed by FilterManager -class Filter { - constructor(name) { - this._id = name; - this._expr = null; - } - +function Filter(name) { + this._id = name; + this._expr = null; +} +Filter.prototype = { + // exported get id() { return this._id; - } + }, + // exported get defFilter() { return this._defFilter; - } + }, + // exported get label() { return this._label; - } + }, set label(value) { if (this._label === value) { return; } this._label = value; this._modified = true; - } + }, + // exported get expression() { return this._expr; - } + }, set expression(value) { if (this._expr === value) { return; @@ -136,8 +140,8 @@ class Filter { delete this.match; } this._modified = true; - } - _makeRegs(str) { + }, + _makeRegs: function(str) { str = str.trim(); // first of all: check if we are are a regexp. if (str.length > 2 && str[0] === '/') { @@ -176,46 +180,48 @@ class Filter { if (str.length) { this._regs.push(new RegExp(str, 'i')); } - } + }, + // exported get active() { return this._active; - } + }, set active(value) { if (this.active === !!value) { return; } this._active = !!value; this._modified = true; - } + }, + // exported get type() { return this._type; - } + }, set type(t) { if (this._type === t) { return; } this._type = t; this._modified = true; - } + }, - pref(str) { + pref: function(str) { return this._id + "." + str; - } + }, - match(str) { + match: function(str) { if (!str) { return false; } str = str.toString(); return this._regs.some(r => r.test(str)); - } + }, /** * @throws Exception in case loading failed */ - load(obj) { + load: function(obj) { this._label = obj.label; if (!this._label || !this._label.length) { throw new Exception("Empty filter!"); @@ -229,43 +235,46 @@ class Filter { this.expression = obj.expr; this._modified = false; - } + }, - save() { + // exported + save: function() { if (!this._modified) { return; } exports.FilterManager.save(); this._modified = false; - } + }, - _reset() { + _reset: function() { exports.FilterManager.remove(this._id); - } + }, - restore() { + // exported + restore: function() { if (!this._defFilter) { throw new Exception("only default filters can be restored!"); } this._reset(); - } + }, - remove() { + // exported + remove: function() { if (this._defFilter) { throw new Exception("default filters cannot be deleted!"); } this._reset(); - } + }, - toString() { + toString: function() { return this._label + " (" + this._id + ")"; - } + }, - toSource() { + toSource: function() { return this.toString() + ": " + this._regs.toSource(); - } + }, - toJSON() { + toJSON: function() { return { id: this.id, label: this._label, @@ -274,33 +283,38 @@ class Filter { active: this._active, }; } -} +}; -class FilterEnumerator { - constructor(filters) { - this._filters = filters; - this._idx = 0; - } - hasMoreElements() { +function FilterEnumerator(filters) { + this._filters = filters; + this._idx = 0; +} +FilterEnumerator.prototype = { + QueryInterface: QI([Ci.nsISimpleEnumerator]), + hasMoreElements: function() { return this._idx < this._filters.length; - } - getNext() { + }, + getNext: function() { if (!this.hasMoreElements()) { throw Cr.NS_ERROR_FAILURE; } return this._filters[this._idx++]; } - - *[Symbol.iterator]() { - for (let f of this._filters) { - yield f; - } +}; +FilterEnumerator.prototype[Symbol.iterator] = function*() { + for (let f of this._filters) { + yield f; } -} +}; +function FilterManagerImpl() { + this.init(); +}; +FilterManagerImpl.prototype = { + LINK_FILTER: LINK_FILTER, + IMAGE_FILTER: IMAGE_FILTER, -class FilterManagerImpl { - constructor() { + init: function() { log(LOG_DEBUG, "initializing filter manager"); // load those localized labels for default filters. this._localizedLabels = {}; @@ -316,23 +330,111 @@ class FilterManagerImpl { this._file.path, JSON.stringify.bind(JSON, this, null, 2), 100); - this.reload(); - } + this._reload(); + }, get count() { return this._count; - } - - reload() { + }, + reload: function() { + this._reload(); + }, + _reload: function() { if (this._pending) { log(LOG_DEBUG, "reload pending"); return this._pending; } log(LOG_DEBUG, "reload spawning"); - return this._pending = this._reloadAsync(); - } + this._pending = Task.spawn((function*() { + log(LOG_DEBUG, "reload commencing"); + try { + let decoder = new TextDecoder(); + if (!this.defFilters) { + yield new Promise(function(resolve, reject) { + let x = new Instances.XHR(); + this._filters = {}; + this._all = []; + x.overrideMimeType("application/json"); + x.open("GET", BASE_PATH + "support/filters.json"); + x.onloadend = (function() { + try { + this.defFilters = JSON.parse(x.responseText); + for (let id in this.defFilters) { + if (id in this._localizedLabels) { + this.defFilters[id].label = this._localizedLabels[id]; + } + } + Object.freeze(this.defFilters); + } + catch (ex) { + log(LOG_ERROR, "Failed to load default filters", ex); + this.defFilters = {}; + } + resolve(); + }).bind(this); + x.send(); + }.bind(this)); + } + let filters = {}; + try { + filters = JSON.parse(decoder.decode(yield OS.File.read(this._file.path))); + if (!filters) { + throw new Error ("No filters where loaded"); + } + for (let f of Object.keys(filters)) { + if (!(f in this.defFilters)) { + continue; + } + let filter = filters[f]; + let no = Object.create(this.defFilters[f]); + for (let i of Object.getOwnPropertyNames(filter)) { + Object.defineProperty(no, i, Object.getOwnPropertyDescriptor(filter, i)); + } + filters[f] = no; + } + } + catch (lex) { + log(LOG_DEBUG, "Couldn't load filters file", lex); + filters = this._migrateFromPrefs(this._pending); + } + // merge with defFilters + for (let f in this.defFilters) { + if (!(f in filters) || !filters[f].expr) { + filters[f] = Object.create(this.defFilters[f]); + } + } - rebuild() { + // Load all + let all = []; + for (let [id, obj] in Iterator(filters)) { + try { + let f = new Filter(id); + f.load(obj); + filters[f.id] = f; + all.push(f); + } + catch (ex) { + log(LOG_DEBUG, "failed to load filter: " + id, ex); + delete filters[id]; + } + } + this._filters = filters; + this._all = all; + log(LOG_DEBUG, "loaded all filters"); + } + catch (ex) { + log(LOG_ERROR, "failed to load filters", ex); + } + + this._rebuild(); + delete this._pending; + }).bind(this)).then(null, function(ex) { + log(LOG_ERROR, "Task did not finish", ex); + }); + return this._pending; + }, + + _rebuild: function() { this._count = this._all.length; this._all.sort(function(a,b) { if (a.defFilter && !b.defFilter) { @@ -359,9 +461,9 @@ class FilterManagerImpl { // notify all observers require("./observers").notify(this, TOPIC_FILTERSCHANGED, null); - } + }, - async _migrateFromPrefs(pending) { + _migrateFromPrefs: function(pending) { log(LOG_DEBUG, "migrating from prefs"); let rv = {}; @@ -397,26 +499,28 @@ class FilterManagerImpl { } } if (pending && kill.size) { - try { - await this._save(); - for (let i of kill) { - log(LOG_DEBUG, "killing " + i); - Preferences.resetBranch(i); + Task.spawn((function*() { + try { + yield this._save(); + for (let i of kill) { + log(LOG_DEBUG, "killing " + i); + Preferences.resetBranch(i); + } } - } - catch (ex) { - log(LOG_ERROR, "failed to reset prefs", ex); - } + catch (ex) { + log(LOG_ERROR, "failed to reset prefs", ex); + } + }).bind(this)); } this._save(); return rv; - } + }, - enumAll() { + enumAll: function() { return new FilterEnumerator(this._all); - } + }, - getFilter(id) { + getFilter: function(id) { if (id in this._filters) { return this._filters[id]; } @@ -432,7 +536,7 @@ class FilterManagerImpl { f.load(filters[id]); this._filters[f.id] = f; this._all.push(f); - this.rebuild(); + this._rebuild(); this._save(); return f; } @@ -443,9 +547,8 @@ class FilterManagerImpl { } } throw new Exception("invalid filter specified: " + id); - } - - getMatcherFor(filters) { + }, + getMatcherFor: function(filters) { let regs = consolidateRegs(flatten( filters.map(f => f._regs) )); @@ -466,13 +569,10 @@ class FilterManagerImpl { } return regs.some(r => r.test(test)); }; - } + }, + matchActive: function(test, type) { return this._activeRegs[type](test); }, - matchActive(test, type) { - return this._activeRegs[type](test); - } - - create(label, expression, active, type) { + create: function(label, expression, active, type) { // we will use unique ids for user-supplied filters. // no need to keep track of the actual number of filters or an index. @@ -489,21 +589,50 @@ class FilterManagerImpl { // will call our observer so we re-init... no need to do more work here :p this._filters[filter.id] = filter; this._all.push(filter); - this.rebuild(); + this._rebuild(); this._save(); return filter.id; - } + }, - remove(id) { - if (!(id in this._filters)) { - throw new Exception('filter not defined!'); + remove: function(id) { + if (id in this._filters) { + delete this._filters[id]; + this.save(); + this._saver.flush(); + return; } - delete this._filters[id]; - this.save(); - this._saver.flush(); - } + throw new Exception('filter not defined!'); + }, - getTmpFromString(expression) { + _save: function() { + return Task.spawn((function*() { + try { + try { + yield OS.File.makeDir(this._file.parent.path, {unixMode: 0o775, ignoreExisting: true}); + } + catch (ex if ex.becauseExists) { + // no op; + } + yield this._saver.saveChanges(); + } + catch (ex) { + log(LOG_ERROR, "failed to save filters", ex); + } + }).bind(this)); + }, + save: function() { + Task.spawn((function*() { + try { + yield this._save(); + this._reload(); + } + catch (ex) { + log(LOG_ERROR, "failed to save filters", ex); + } + }).bind(this)); + }, + + getTmpFromString: function(expression) { if (!expression.length) { throw Cr.NS_ERROR_INVALID_ARG; } @@ -513,139 +642,19 @@ class FilterManagerImpl { filter._modified = false; filter.expression = expression; return filter; - } + }, - ready(callback) { + ready: function(callback) { if (this._pending) { log(LOG_DEBUG, "waiting for ready"); this._pending.then(callback); return; } callback(); - } - - async _save() { - try { - try { - await OS.File.makeDir(this._file.parent.path, {unixMode: 0o775, ignoreExisting: true}); - } - catch (ex if ex.becauseExists) { - // no op; - } - await this._saver.saveChanges(); - } - catch (ex) { - log(LOG_ERROR, "failed to save filters", ex); - } - } - - async save() { - try { - await this._save(); - this.reload(); - } - catch (ex) { - log(LOG_ERROR, "failed to save filters", ex); - } - } - - async _reloadAsync() { - log(LOG_DEBUG, "reload commencing"); - try { - try { - let decoder = new TextDecoder(); - if (!this.defFilters) { - await new Promise(function(resolve, reject) { - let x = new XMLHttpRequest(); - this._filters = {}; - this._all = []; - x.overrideMimeType("application/json"); - x.open("GET", BASE_PATH + "support/filters.json"); - x.onloadend = (function() { - try { - this.defFilters = JSON.parse(x.responseText); - for (let id in this.defFilters) { - if (id in this._localizedLabels) { - this.defFilters[id].label = this._localizedLabels[id]; - } - } - Object.freeze(this.defFilters); - } - catch (ex) { - log(LOG_ERROR, "Failed to load default filters", ex); - this.defFilters = {}; - } - resolve(); - }).bind(this); - x.send(); - }.bind(this)); - } - - let filters = {}; - try { - filters = JSON.parse(decoder.decode(await OS.File.read(this._file.path))); - if (!filters) { - throw new Error ("No filters where loaded"); - } - for (let f of Object.keys(filters)) { - if (!(f in this.defFilters)) { - continue; - } - let filter = filters[f]; - let no = Object.create(this.defFilters[f]); - for (let i of Object.getOwnPropertyNames(filter)) { - Object.defineProperty(no, i, Object.getOwnPropertyDescriptor(filter, i)); - } - filters[f] = no; - } - } - catch (lex) { - log(LOG_DEBUG, "Couldn't load filters file", lex); - filters = this._migrateFromPrefs(this._pending); - } - // merge with defFilters - for (let f in this.defFilters) { - if (!(f in filters) || !filters[f].expr) { - filters[f] = Object.create(this.defFilters[f]); - } - } + }, - // Load all - let all = []; - for (let [id, obj] in Iterator(filters)) { - try { - let f = new Filter(id); - f.load(obj); - filters[f.id] = f; - all.push(f); - } - catch (ex) { - log(LOG_DEBUG, "failed to load filter: " + id, ex); - delete filters[id]; - } - } - this._filters = filters; - this._all = all; - log(LOG_DEBUG, "loaded all filters"); - } - catch (ex) { - log(LOG_ERROR, "failed to load filters", ex); - } - - this.rebuild(); - } - finally { - delete this._pending; - } - } - - toJSON() { + toJSON: function() { return this._filters; } }; -Object.assign(FilterManagerImpl.prototype, { - LINK_FILTER: LINK_FILTER, - IMAGE_FILTER: IMAGE_FILTER, -}); - exports.FilterManager = new FilterManagerImpl(); diff --git a/modules/support/historymanager.js b/modules/support/historymanager.js index ccb634c63..907e9849d 100644 --- a/modules/support/historymanager.js +++ b/modules/support/historymanager.js @@ -18,8 +18,8 @@ const validators = { } }; -class BaseHistory { - constructor(key) { +const BaseHistory = { + init: function(key) { this._key = key; if (key in validators) { this._validator = validators[key]; @@ -27,14 +27,14 @@ class BaseHistory { else { this._validator = function() { return true; }; } - } + }, get key() { return this._key; - } + }, get values() { return this._values.filter(this._validator); - } - push(value, once) { + }, + push: function(value, once) { try { value = value.toString(); let values = this._values; @@ -64,14 +64,18 @@ class BaseHistory { catch (ex) { log(LOG_ERROR, "Histories: Push failed!", ex); } - } - reset(value) { + }, + reset: function(value) { log(LOG_INFO, "Histories: Reset called"); this._setValues([]); } -} +}; -class PrefHistory extends BaseHistory { +function PrefHistory(key) { + this.init(key); +} +PrefHistory.prototype = { + __proto__: BaseHistory, get _values() { let json = prefs.getExt(this._key, '[]'); let rv; @@ -93,8 +97,8 @@ class PrefHistory extends BaseHistory { rv = []; } return rv; - } - _setValues(values) { + }, + _setValues: function(values) { log(LOG_DEBUG, "PrefHistory save of " + values); try { prefs.setExt(this._key, JSON.stringify(values)); @@ -104,18 +108,19 @@ class PrefHistory extends BaseHistory { throw ex; } } -} +}; -class MemHistory extends BaseHistory { - constructor(key) { - super(key); - this._setValues((new PrefHistory(this.key)).values); - } - _setValues(values) { +function MemHistory(key) { + this.init(key); + this._setValues((new PrefHistory(this.key)).values); +} +MemHistory.prototype = { + __proto__: BaseHistory, + _setValues: function(values) { log(LOG_DEBUG, "MemHistory save of " + values); this._values = values; } -} +}; const _normalHistories = {}; var _privateHistories = {}; diff --git a/modules/support/iconcheat.js b/modules/support/iconcheat.js index b8fda5606..6295098f8 100644 --- a/modules/support/iconcheat.js +++ b/modules/support/iconcheat.js @@ -57,33 +57,29 @@ exports.loadWindow = (function() { } // Directory Provider we use to check the system :p - class CheatDirProvider { - construxtor() { - this.hasMore = false; - } - getFile(prop, persist) { + function CheatDirProvider() { + this.hasMore = false; + } + CheatDirProvider.prototype = Object.freeze({ + QueryInterface: QI([Ci.nsIDirectoryServiceProvider, Ci.nsIDirectoryServiceProvider2, Ci.nsISimpleEnumerator]), + getFile: function(prop, persist) { throw Cr.NS_ERROR_FAILURE; - } - getFiles(prop, persist) { + }, + getFiles: function(prop, persist) { if (prop === "AChromDL") { this.hasMore = true; return this; } throw Cr.NS_ERROR_FAILURE; - } - hasMoreElements() { - return this.hasMore; - } - getNext() { + }, + hasMoreElements: function() { return this.hasMore; }, + getNext: function() { if (!this.hasMore) { throw Cr.NS_ERROR_FAILURE; } this.hasMore = false; return profileDir.clone(); } - } - Object.assign(CheatDirProvider.prototype, { - QueryInterface: QI([Ci.nsIDirectoryServiceProvider, Ci.nsIDirectoryServiceProvider2, Ci.nsISimpleEnumerator]), }); // Create icons if not there yet, or if we got a major version update diff --git a/modules/support/icons.js b/modules/support/icons.js index 98a8823dd..e0aaa947c 100644 --- a/modules/support/icons.js +++ b/modules/support/icons.js @@ -5,7 +5,7 @@ const {URL} = require("api"); const {memoize} = require("./memoize"); -const {getExtension} = require("./stringfuncs"); +const {getExtension, toURL} = require("./stringfuncs"); const favCache = new LRUMap(200); @@ -72,16 +72,9 @@ if ("mozIAsyncFavicons" in Ci && Services.favicons instanceof Ci.mozIAsyncFavico const ficb = function(aFavURI) { if (!aFavURI) { log(LOG_DEBUG, "getFavIconAsync: failed " + spec + " " + uri.spec); - let path = uri.path || uri.pathQueryRef; - if (path !== "/") { + if (uri.path !== "/") { uri = uri.clone(); - if ("pathQueryRef" in uri) { - uri.pathQueryRef = "/"; - } - else { - uri.path = "/"; - } - path = "/"; + uri.path = "/"; let hostSpec = uri.spec; if (favCache.has(hostSpec)) { let rv = favCache.get(hostSpec); @@ -98,7 +91,7 @@ if ("mozIAsyncFavicons" in Ci && Services.favicons instanceof Ci.mozIAsyncFavico return; } let rv = fis.getFaviconLinkForIcon(aFavURI).spec; - if (path !== "/") { + if (uri.path !== "/") { favCache.set(uri.spec, rv); } callback.call(tp, rv, true); @@ -118,12 +111,7 @@ else if ("nsIFaviconService" in Ci) { let fi = fis.getFaviconImageForPage(uri); if (!fi || fi.equals(defIcon)) { uri = uri.clone(); - if ("pathQueryRef" in uri) { - uri.pathQueryRef = "/"; - } - else { - uri.path = "/"; - } + uri.path = ""; if (favCache.has(uri.spec)) { callback.call(tp, favCache.get(uri.spec)); return; @@ -151,6 +139,7 @@ else { }; } + // The Windows icon loader does not support icons > 32px at the moment exports.getLargeIcon = (function() { let _s = 32, _sh = 32; diff --git a/modules/support/memoize.js b/modules/support/memoize.js index e821572b7..633b41662 100644 --- a/modules/support/memoize.js +++ b/modules/support/memoize.js @@ -118,14 +118,14 @@ exports.memoize = function memoize(func, limit, num_args) { }; default: - return function(...args) { - var key = JSON.stringify(args); + return function() { + var key = JSON.stringify(arguments); if (cache.has(key)) { return cache.get(key); } - var result = func(...args); + var result = func.apply(null, arguments); cache.set(key, result); if (keylist.push(key) > limit) { cache.delete(keylist.shift()); diff --git a/modules/support/metalinker.js b/modules/support/metalinker.js index e4994a093..30f8d086a 100644 --- a/modules/support/metalinker.js +++ b/modules/support/metalinker.js @@ -13,6 +13,7 @@ const NS_METALINKER3 = 'http://www.metalinker.org/'; const NS_METALINK_RFC5854 = 'urn:ietf:params:xml:ns:metalink'; const DTA = require("api"); +const Preferences = require("preferences"); const {LOCALE} = require("version"); const {UrlManager} = require("./urlmanager"); const {NS_DTA, NS_HTML, normalizeMetaPrefs} = require("utils"); @@ -23,20 +24,32 @@ const XPathResult = Ci.nsIDOMXPathResult; * Parsed Metalink representation * (Do not construct yourself unless you know what you're doing) */ -class Metalink { - constructor(downloads, info, parser) { - this.downloads = downloads; - this.info = info; - this.parser = parser; - } +function Metalink(downloads, info, parser) { + this.downloads = downloads; + this.info = info; + this.parser = parser; } +Metalink.prototype = { + /** + * Array of downloads + */ + downloads: [], + /** + * Dict of general information + */ + info: {}, + /** + * Parser identifaction + */ + parser: "" +}; -class Base { - constructor(doc, NS) { - this._doc = doc; - this._NS = NS; - } - lookupNamespaceURI(prefix) { +function Base(doc, NS) { + this._doc = doc; + this._NS = NS; +} +Base.prototype = { + lookupNamespaceURI: function Base_lookupNamespaceURI(prefix) { switch (prefix) { case 'html': return NS_HTML; @@ -44,8 +57,8 @@ class Base { return NS_DTA; } return this._NS; - } - getNodes(elem, query) { + }, + getNodes: function (elem, query) { let rv = []; let iterator = this._doc.evaluate( query, @@ -58,19 +71,19 @@ class Base { rv.push(n); } return rv; - } - getNode(elem, query) { + }, + getNode: function Base_getNode(elem, query) { let r = this.getNodes(elem, query); if (r.length) { return r.shift(); } return null; - } - getSingle(elem, query) { + }, + getSingle: function BasegetSingle(elem, query) { let rv = this.getNode(elem, 'ml:' + query); return rv ? rv.textContent.trim() : ''; - } - getLinkRes(elem, query) { + }, + getLinkRes: function BasegetLinkRes(elem, query) { let rv = this.getNode(elem, 'ml:' + query); if (rv) { let n = this.getSingle(rv, 'name'), l = this.checkURL(this.getSingle(rv, 'url')); @@ -79,8 +92,8 @@ class Base { } } return null; - } - checkURL(url, allowed) { + }, + checkURL: function Base_checkURL(url, allowed) { if (!url) { return null; } @@ -106,23 +119,23 @@ class Base { } return null; } -} +}; /** * Metalink3 Parser * @param doc document to parse * @return Metalink */ -class Metalinker3 extends Base { - constructor(doc) { - let root = doc.documentElement; - if (root.nodeName !== 'metalink' || root.getAttribute('version') !== '3.0') { - throw new Error('mlinvalid'); - } - super(doc, NS_METALINKER3); +function Metalinker3(doc) { + let root = doc.documentElement; + if (root.nodeName !== 'metalink' || root.getAttribute('version') !== '3.0') { + throw new Exception('mlinvalid'); } - - parse(aReferrer) { + Base.call(this, doc, NS_METALINKER3); +} +Metalinker3.prototype = { + __proto__: Base.prototype, + parse: function ML3_parse(aReferrer) { if (aReferrer && 'spec' in aReferrer) { aReferrer = aReferrer.spec; } @@ -321,25 +334,26 @@ class Metalinker3 extends Base { }; return new Metalink(downloads, info, "Metalinker Version 3.0"); } -} +}; /** * Metalink RFC5854 (IETF) Parser * @param doc document to parse * @return Metalink */ -class MetalinkerRFC5854 extends Base { - constructor(doc) { - let root = doc.documentElement; - if (root.nodeName !== 'metalink' || root.namespaceURI !== NS_METALINK_RFC5854 ) { - if (log.enabled) { - log(LOG_DEBUG, root.nodeName + "\nns:" + root.namespaceURI); - } - throw new Error('mlinvalid'); +function MetalinkerRFC5854(doc) { + let root = doc.documentElement; + if (root.nodeName !== 'metalink' || root.namespaceURI !== NS_METALINK_RFC5854 ) { + if (log.enabled) { + log(LOG_DEBUG, root.nodeName + "\nns:" + root.namespaceURI); } - super(doc, NS_METALINK_RFC5854); + throw new Exception('mlinvalid'); } - parse(aReferrer) { + Base.call(this, doc, NS_METALINK_RFC5854); +} +MetalinkerRFC5854.prototype = { + __proto__: Base.prototype, + parse: function ML4_parse(aReferrer) { if (aReferrer && 'spec' in aReferrer) { aReferrer = aReferrer.spec; } @@ -446,7 +460,6 @@ class MetalinkerRFC5854 extends Base { } } if (hash) { - Cu.reportError(hash); hash = new DTA.HashCollection(hash); let pieces = this.getNodes(file, 'ml:pieces'); if (pieces.length) { @@ -523,7 +536,7 @@ class MetalinkerRFC5854 extends Base { }; return new Metalink(downloads, info, "Metalinker Version 4.0 (RFC5854/IETF)"); } -} +}; const __parsers__ = [ Metalinker3, @@ -538,7 +551,8 @@ const __parsers__ = [ * @return async (Metalink) Parsed metalink data */ function parse(aURI, aReferrer, aCallback) { - let xhr = new XMLHttpRequest(); + let xhrLoad, xhrError; + let xhr = new Instances.XHR(); xhr.open("GET", aURI.spec); log(LOG_DEBUG, "parsing metalink at " + aURI.spec); xhr.overrideMimeType("application/xml"); diff --git a/modules/support/movefile.js b/modules/support/movefile.js index 19f55052c..4659e6adf 100644 --- a/modules/support/movefile.js +++ b/modules/support/movefile.js @@ -16,7 +16,6 @@ let _killWorker = new Promise((resolve, reject) => { }); let _worker = new ChromeWorker(BASE_PATH + "support/movefile_worker.js"); - function onmessage({data}) { if (data.log) { log(LOG_DEBUG, "movefile_worker said" + data.log); @@ -45,7 +44,7 @@ function onmessage({data}) { } function onerror(e) { - log(LOG_ERROR, `moveFile worker died ${e.message} ${e.filename}:${e.linenumber} ${Object.keys(e)}`, e); + log(LOG_ERROR, "moveFile worker died " + e.message + " " + e.filename + " " + e.linenumber + " " + Object.keys(e), e); _worker = null; _kill(); } @@ -53,6 +52,7 @@ function onerror(e) { _worker.onmessage = onmessage; _worker.onerror = onerror; + const asyncShutdown = function() { obs.removeExit(asyncShutdown); const dead = () => { diff --git a/modules/support/movefile_worker.js b/modules/support/movefile_worker.js index e8fa65984..3c9b08a50 100644 --- a/modules/support/movefile_worker.js +++ b/modules/support/movefile_worker.js @@ -2,7 +2,7 @@ * 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"; -/* global log:true, importScripts, postMessage, onmessage:true, self, OS */ +/* globals log:true, importScripts, postMessage, self, onmessage:true, OS */ importScripts("resource://gre/modules/osfile.jsm"); diff --git a/modules/support/observers.js b/modules/support/observers.js index 55f4e7d29..9be78752b 100644 --- a/modules/support/observers.js +++ b/modules/support/observers.js @@ -5,13 +5,13 @@ const TOPIC_SHUTDOWN = "profile-change-teardown"; -class Observer { - constructor() { - this.observers = new Map(); - Services.obs.addObserver(this, TOPIC_SHUTDOWN, true); - } +function Observer() { + Services.obs.addObserver(this, TOPIC_SHUTDOWN, true); +} +Observer.prototype = Object.freeze({ + observers: new Map(), - unload() { + unload: function() { log(LOG_DEBUG, "DYING"); for (let [t, o] of this.observers) { try { @@ -30,10 +30,11 @@ class Observer { catch (ex) { // no op } - } + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]), - add(obs, topic) { + add: function(obs, topic) { if (!obs || !topic) { throw new Error("Invalid arguments"); } @@ -46,9 +47,8 @@ class Observer { this.observers.set(topic, observers); } observers.add(obs); - } - - remove(obs, topic) { + }, + remove: function(obs, topic) { if (!obs || !topic) { throw new Error("Invalid arguments"); } @@ -64,17 +64,15 @@ class Observer { } this.observers.delete(topic); } - } - + }, get topics() { let topics = []; - for (let [t, _] of this.observers) { + for (let [t,o] of this.observers) { topics.push(t); } return topics; - } - - observe(subject, topic, data) { + }, + observe: function(subject, topic, data) { var observers = this.observers.get(topic); if (!observers) { return; @@ -96,10 +94,7 @@ class Observer { this.unload(); } } -} -Observer.prototype.QueryInterface = - XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]); - +}); const observer = new Observer(); unload(observer.unload.bind(observer)); diff --git a/modules/support/overlays.js b/modules/support/overlays.js index d69cd990b..2b4ce20f3 100644 --- a/modules/support/overlays.js +++ b/modules/support/overlays.js @@ -2,6 +2,7 @@ * 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"; +/* jshint -W083 */ const {defer} = require("./defer"); const obs = require("./observers"); @@ -10,11 +11,12 @@ const obs = require("./observers"); * Specialized unloader that will trigger whenever either the window gets * unloaded or the add-on is shut down */ -exports.unloadWindow = function unloadWindow(window, fn, ...args) { +exports.unloadWindow = function unloadWindow(window, fn) { + let args = arguments; let handler = unload(function() { window.removeEventListener('unload', handler, false); try { - fn(...args); + fn.apply(null, args); } catch (ex) { log(LOG_ERROR, "failed to run window unloader", ex); @@ -136,7 +138,6 @@ exports.registerOverlay = function registerOverlay(src, location, callback) { try { let uri = Services.io.newURI(data, null, null); winUtils.loadSheet(uri, Ci.nsIDOMWindowUtils.AUTHOR_SHEET); - // jshint -W083 unloaders.push(function() { winUtils.removeSheet(uri, Ci.nsIDOMWindowUtils.AUTHOR_SHEET); }); @@ -207,7 +208,7 @@ exports.registerOverlay = function registerOverlay(src, location, callback) { return; } - let _r = new XMLHttpRequest(); + let _r = new Instances.XHR(); _r.onload = function() { let doc = _r.responseXML; diff --git a/modules/support/requestmanipulation.js b/modules/support/requestmanipulation.js index d2523888f..f80fd7a1c 100644 --- a/modules/support/requestmanipulation.js +++ b/modules/support/requestmanipulation.js @@ -3,24 +3,23 @@ * You can obtain one at http://mozilla.org/MPL/2.0/ */ "use strict"; -class Manipulator { - constructor() { - this._m = new Map(); - } - - register(id, matcher, ...args) { - this._m.set(id, { +function Manipulator() { + this._m = {}; +} +Manipulator.prototype = { + register: function(id, matcher) { + this._m[id] = { matcher: matcher, - funcs: args, - }); - } - - unregister(id) { - this._m.delete(id); - } - - modify(context, spec) { - for (let [id, m] of this._m.entries()) { + funcs: Array.slice(arguments, 2) + }; + }, + unregister: function(id) { + if (id in this._m) { + delete this._m[id]; + } + }, + modify: function(context, spec) { + for (let [,m] in new Iterator(this._m)) { if (m.matcher.test(spec)) { try { for (let func of m.funcs) { @@ -28,17 +27,17 @@ class Manipulator { } } catch (ex) { - log(LOG_ERROR, `Failed to apply request manipulator id ${id}`, ex); + Cu.reportError(ex); } } } return context; } -} +}; function defineManipulator(m, sp) { const _m = new Manipulator(); - exports['register' + m] = function(...args) { _m.register(...args); }; + exports['register' + m] = function() { _m.register.apply(_m, arguments); }; exports['unregister' + m] = function(id) { _m.unregister(id); }; exports['modify' + m] = function(context) { return _m.modify(context, sp(context)); }; } @@ -73,12 +72,7 @@ exports.makeAnonymous = function makeAnonymous() { this.setRequestHeader('Referer', '', false); this.setRequestHeader('Cookie', '', false); if (("nsIPrivateBrowsingChannel" in Ci) && (this instanceof Ci.nsIPrivateBrowsingChannel)) { - try { - this.setPrivate(true); - } - catch (ex) { - // ignored - } + try { this.setPrivate(true); } catch (ex) {} } }; diff --git a/modules/support/scheduleautostart.js b/modules/support/scheduleautostart.js index 681d0d9fc..94c8f8493 100644 --- a/modules/support/scheduleautostart.js +++ b/modules/support/scheduleautostart.js @@ -5,7 +5,7 @@ const Prefs = require("preferences"); const {QUEUED} = require("constants"); -const {setTimeout, clearTimeout} = require("./defer"); +const {TimerManager} = require("./timers"); //Add some helpers to Date //Note to reviewers: Our scope, our rules ;) @@ -23,6 +23,8 @@ Date.__defineGetter__("today", function() { return rv; }); +const Timers = new TimerManager(); + /* global DTA */ lazy(this, "DTA", () => require("api")); /* global QueueStore */ @@ -36,7 +38,7 @@ const Observer = { log(LOG_DEBUG, "scheduler running"); }, unload: function() { - this.cancelTimer(); + Timers.killAllTimers(); }, observe: function(s, topic, d) { if (!this.immediatelyOpened) { @@ -49,7 +51,7 @@ const Observer = { openManager: function() { let wnd = require("./mediator").getMostRecent(); if (!wnd) { - setTimeout(() => this.openManager(), 1000); + Timers.createOneshot(1000, this.openManager.bind(this)); return; } DTA.openManager(wnd); @@ -99,7 +101,7 @@ const Observer = { if (!this.timer) { return; } - clearTimeout(this.timer); + Timers.killTimer(this.timer); this.timer = null; }, scheduleNext: function() { @@ -112,7 +114,7 @@ const Observer = { current.addDays(1); } current = current.valueOf() - now.valueOf() + 1000; - this.timer = setTimeout(() => this.openIfInRange(), current); + this.timer = Timers.createOneshot(current, this.openIfInRange, this); } }; Observer.init(); diff --git a/modules/support/serverlimits.js b/modules/support/serverlimits.js index 5a8c43c42..0c832081d 100644 --- a/modules/support/serverlimits.js +++ b/modules/support/serverlimits.js @@ -7,9 +7,8 @@ const Prefs = require("preferences"); requireJoined(this, "constants"); const {ByteBucket} = require("./bytebucket"); -const {filterInSitu, shuffle} = require("utils"); +const {filterInSitu} = require("utils"); const obs = require("./observers"); -const domainprefs = require("./domainprefs"); const TOPIC = 'DTA:serverlimits-changed'; const PREFS = 'extensions.dta.serverlimit.'; @@ -21,130 +20,85 @@ const SCHEDULER_FAIR = 'fair'; const SCHEDULER_RND = 'rnd'; const SCHEDULER_LEGACY = 'legacy'; -let limits = new Map(); - -const CONNECTIONS = Symbol.for("conns"); -const SPEED = Symbol.for("spd"); -const SEGMENTS = Symbol.for("seg"); -const CLEAN = Symbol.for("cleanRequest"); +let limits = {}; const LIMIT_PROTO = { - c: Prefs.getExt("ntask", 2), + c: 2, s: -1, - seg: 0, - cr: false, + seg: 0 }; Object.freeze(LIMIT_PROTO); -class Limit { - constructor(host, isNew) { - this._host = host; - this._isNew = isNew; - try { - this.connections = domainprefs.getHost(this._host, CONNECTIONS, null); - if (!this.connections) { - throw new Error("domain pref not available"); - } - this.speed = domainprefs.getHost(this._host, SPEED, LIMIT_PROTO.s); - this.segments = domainprefs.getHost(this._host, SEGMENTS, LIMIT_PROTO.seg); - this.clean = domainprefs.getHost(this._host, CLEAN, LIMIT_PROTO.cr); - } - catch (oex) { - try { - let branch = LIMITS_PREF + this._host; - let o = JSON.parse(Prefs.get(branch, "")); - for (let p in LIMIT_PROTO) { - if (!o.hasOwnProperty(p)) { - o[p] = LIMIT_PROTO[p]; - } - } - this.connections = o.c; - this.speed = o.s; - this.segments = o.seg; - this.clean = o.cr; - this.save(); - Prefs.resetBranch(branch); - } - catch (ex) { - this.connections = LIMIT_PROTO.c; - this.speed = LIMIT_PROTO.s; - this.segments = LIMIT_PROTO.seg; - this.clean = LIMIT_PROTO.cr; +function Limit(host, isNew) { + this._host = host; + this._isNew = isNew; + let o = LIMIT_PROTO; + try { + o = JSON.parse(Prefs.get(LIMITS_PREF + this._host, "")); + for (let p in LIMIT_PROTO) { + if (!o.hasOwnProperty(p)) { + o[p] = LIMIT_PROTO[p]; } } } - - get host() { - return this._host; - } - get isNew() { - return this._isNew; - } - get connections() { - return this._connections; + catch (ex) { + // no op; } + this.connections = o.c; + this.speed = o.s; + this.segments = o.seg; +} +Limit.prototype = Object.freeze({ + get host() { return this._host; }, + get isNew() { return this._isNew; }, + get connections() { return this._connections; }, set connections(value) { if (!isFinite(value)) { - throw new Error("Invalid Limit"); + throw new Exception("Invalid Limit"); } this._connections = value; - } - get speed() { - return this._speed; - } + }, + get speed() { return this._speed; }, set speed(value) { if (!isFinite(value)) { - throw new Error("Invalid Limit"); + throw new Exception("Invalid Limit"); } this._speed = value; - } - get segments() { - return this._segments; - } + }, + get segments() { return this._segments; }, set segments(value) { if (!isFinite(value)) { - throw new Error("Invalid Limit"); + throw new Exception("Invalid Limit"); } this._segments = value; - } - - save() { - limits.set(this._host, this); - domainprefs.setHost(this._host, CONNECTIONS, this.connections); - domainprefs.setHost(this._host, SPEED, this.speed); - domainprefs.setHost(this._host, SEGMENTS, this.segments); - domainprefs.setHost(this._host, CLEAN, this.clean); + }, + save: function() { + Prefs.set( + LIMITS_PREF + this._host, + JSON.stringify({c: this._connections, s: this._speed, seg: this._segments}) + ); this._isNew = false; - } - - remove() { - limits.delete(this._host); - domainprefs.deleteHost(this._host, CONNECTIONS); - domainprefs.deleteHost(this._host, SPEED); - domainprefs.deleteHost(this._host, SEGMENTS); - domainprefs.deleteHost(this._host, CLEAN, this.clean); + }, + remove: function() { Prefs.reset(LIMITS_PREF + this._host); + }, + toString: function() { + return this._host + + " conn: " + this._connections + + " speed: " + this._speed + + " segments:" + this._segments; } +}); - toString() { - return `[Limit(conn: ${this._connections}, spd: ${this._speed}, seg: ${this._segments}, cr:${this.clean})]`; - } -} - -const loadLimits = async function loadLimits() { - await domainprefs.load(); - limits = new Map(); - let dp = Array.from(domainprefs.enumHosts()).filter(h => domainprefs.getHost(h, CONNECTIONS)); +function loadLimits() { + limits = Object.create(null); let hosts = Prefs.getChildren(LIMITS_PREF).map(e => e.substr(LIMITS_PREF.length)); - log(LOG_DEBUG, "dp " + dp.join(", ")); - log(LOG_DEBUG, "hosts " + hosts.join(", ")); - hosts = Array.from((new Set(dp.concat(hosts))).values()); hosts.sort(); for (let host of hosts) { try { let limit = new Limit(host); - limits.set(limit.host, limit); + limits[limit.host] = limit; log(LOG_DEBUG, "loaded limit: " + limit); } catch (ex) { @@ -152,7 +106,7 @@ const loadLimits = async function loadLimits() { } } obs.notify(null, TOPIC, null); -}; +} function getEffectiveHost(url) { try { @@ -165,28 +119,31 @@ function getEffectiveHost(url) { function addLimit(host) { host = getEffectiveHost(Services.fixups.createFixupURI(host, 0x0)); - if (limits.has(host)) { - return limits.get(host); + if (host in limits) { + return limits[host]; } return new Limit(host, true); } function listLimits() { - return limits.entries(); + return limits; } function getLimitFor(d) { let host = d.urlManager.domain; - return limits.get(host); + if (host in limits) { + return limits[host]; + } + return null; } + let globalConnections = -1; -class BaseScheduler { - static _queuedFilter(e) { - return e.state === QUEUED; - } - next() { +function BaseScheduler() {} +BaseScheduler.prototype = Object.freeze({ + _queuedFilter: function(e) { return e.state === QUEUED; }, + next: function() { for (let d; this._schedule.length;) { d = this._schedule.shift(); if (d.state !== QUEUED) { @@ -195,129 +152,126 @@ class BaseScheduler { return d; } return null; - } - - destroy() { + }, + destroy: function() { this._schedule.length = 0; delete this._schedule; } -} +}); +Object.freeze(BaseScheduler); // Legacy scheduler. Does not respect limits // Basically Olegacy(1) -class LegacyScheduler extends BaseScheduler { - constructor(downloads) { - super(); - this._schedule = downloads.filter(BaseScheduler._queuedFilter); - } +function LegacyScheduler(downloads) { + this._schedule = downloads.filter(this._queuedFilter); } -exports.LegacyScheduler = LegacyScheduler; +LegacyScheduler.prototype = BaseScheduler.prototype; +Object.freeze(LegacyScheduler); // Fast generator: Start downloads as in queue -class FastScheduler extends BaseScheduler { - constructor(downloads, running) { - super(); - this._downloads = []; - for (let i = 0, e = downloads.length; i < e; ++i) { - let d = downloads[i]; - if (d.state === QUEUED) { - this._downloads.push(d); - } +function FastScheduler(downloads, running) { + this._downloads = []; + for (let i = 0, e = downloads.length; i < e; ++i) { + let d = downloads[i]; + if (d.state === QUEUED) { + this._downloads.push(d); } - this._runCount = 0; } - - next(running) { + this._runCount = 0; + //this._downloads = downloads.filter(this._queuedFilter); +} +FastScheduler.prototype = Object.freeze({ + __proto__: BaseScheduler.prototype, + next: function(running) { if (!this._downloads.length) { return null; } - let downloading = new Map(); - let i, e, d; + let downloadSet = Object.create(null); + let i, e, d, host; if (this._runCount > 50) { - filterInSitu(this._downloads, BaseScheduler._queuedFilter); + filterInSitu(this._downloads, this._queuedFilter); this._runCount = 0; } // count running downloads per host - for (let r of running) { - if (r.totalSize && r.totalSize < 1024*1024) { + for (i = 0, e = running.length; i < e; ++i) { + if (running[i].totalSize && running[i].totalSize < 1024*1024) { continue; } - let host = r.urlManager.domain; - downloading.set(host, (downloading.get(host) || 0) + 1); + host = running[i].urlManager.domain; + downloadSet[host] = ++downloadSet[host] || 1; } // calculate available slots // negative means: available, else not available; - for (let [host, count] of downloading.entries()) { - let limit = limits.get(host); - if (limit) { - i = limit.connections; + for (host in downloadSet) { + if (host in limits) { + i = limits[host].connections; } else { i = globalConnections; } if (i <= 0) { // no limit - i = -1; + downloadSet[host] = -1; + } + else { + downloadSet[host] -= i; } - downloading.set(host, count - i); } for (i = 0, e = this._downloads.length; i < e; ++i) { - let d = this._downloads[i]; + d = this._downloads[i]; if (!d || d.state !== QUEUED) { continue; } - let host = d.urlManager.domain; - //log(LOG_ERROR, "fair check: " + host + " downloading:" + downloading.get(host, -1)); + host = d.urlManager.domain; // no running downloads for this host yet - if (!downloading.has(host)) { + if (!(host in downloadSet)) { this._runCount++; return d; } - // free slot - if (downloading.get(host) < 0) { + + if (downloadSet[host] < 0) { this._runCount++; return d; } } return null; - } - - destroy() { + }, + destroy: function() { this._downloads.length = 0; delete this._downloads; } -} -exports.FastScheduler = FastScheduler; +}); +Object.freeze(FastScheduler); // Fair Scheduler: evenly distribute slots // Performs worse than FastScheduler but is more precise. -class FairScheduler extends BaseScheduler { - constructor(downloads) { - super(); - this._downloadSet = Object.create(null); - - // set up our internal state - for (let i = 0, e = downloads.length, d, host; i < e; ++i) { - d = downloads[i]; - if (d.state !== QUEUED) { - continue; - } - host = d.urlManager.domain; - if (!(host in this._downloadSet)) { - this._downloadSet[host] = new FairScheduler.SchedItem(host); - } - this._downloadSet[host].push(d); +function FairScheduler(downloads) { + this._downloadSet = Object.create(null); + + // set up our internal state + for (let i = 0, e = downloads.length, d, host; i < e; ++i) { + d = downloads[i]; + if (d.state !== QUEUED) { + continue; + } + host = d.urlManager.domain; + if (!(host in this._downloadSet)) { + this._downloadSet[host] = new FairScheduler.SchedItem(host); } + this._downloadSet[host].push(d); } +} +FairScheduler.prototype = Object.freeze({ + __proto__: BaseScheduler.prototype, - next(running) { + next: function(running) { let i, e, d, host; // reset all counters @@ -326,11 +280,12 @@ class FairScheduler extends BaseScheduler { } // Count the running tasks - for (let r of running) { - if (r.totalSize && r.totalSize < 1024*1024) { + for (i = 0, e = running.length; i < e; ++i) { + if (running[i].totalSize && running[i].totalSize < 1024*1024) { continue; } - host = r.urlManager.domain; + d = running[i]; + host = d.urlManager.domain; if (!(host in this._downloadSet)) { // we don't care, because we don't have any more queued downloads for this host continue; @@ -363,91 +318,70 @@ class FairScheduler extends BaseScheduler { return d; } return null; - } - - destroy() { + }, + destroy: function() { for (let k in this._downloadSet) { this._downloadSet[k].destroy(); delete this._downloadSet[k]; } this._downloadSet = null; } -} -FairScheduler.SchedItem = class { - constructor(host) { - this.host = host; - this.limit = 0; - let limit = limits.get(host); - if (limit) { - this.limit = limit.connections; - } - else { - this.limit = globalConnections; - } - this.downloads = []; - this.resetCounter(); - } - - get available() { - return this.limit <= 0 || this.n < this.limit; - } - - inc() { - this.n++; - } - resetCounter() { - return this.n = 0; - } - - toString() { - return this.host; +}); +FairScheduler.SchedItem = function(host) { + this.host = host; + this.limit = 0; + if (host in limits) { + this.limit = limits[host].connections; } - - get length() { - return this.downloads.length; + else { + this.limit = globalConnections; } - - shift() { + this.downloads = []; + this.resetCounter(); +}; +FairScheduler.SchedItem.prototype = Object.freeze({ + get available() { return this.limit <= 0 || this.n < this.limit; }, + inc: function() { this.n++; }, + resetCounter: function() { return this.n = 0; }, + toString: function() { return this.host; }, + get length() { return this.downloads.length; }, + shift: function() { ++this.n; return this.downloads.shift(); - } - - pop() { + }, + pop: function() { ++this.n; return this.downloads.pop(); - } - - push(d) { - return this.downloads.push(d); - } - destroy() { + }, + push: function(d) { return this.downloads.push(d); }, + destroy: function() { this.downloads.length = 0; delete this.downloads; } -}; -exports.FairScheduler = FairScheduler; +}); +Object.freeze(FairScheduler); // Fair Dir Scheduler: evenly distribute slots -class DirScheduler extends BaseScheduler { - constructor(downloads) { - super(); - this._downloadSet = Object.create(null); - - // set up our internal state - for (let i = 0, e = downloads.length, d, dir; i < e; ++i) { - d = downloads[i]; - if (d.state !== QUEUED) { - continue; - } - dir = d.destinationPath; - if (!(dir in this._downloadSet)) { - this._downloadSet[dir] = new FairScheduler.SchedItem(dir); - } - this._downloadSet[dir].push(d); +function DirScheduler(downloads) { + this._downloadSet = Object.create(null); + + // set up our internal state + for (let i = 0, e = downloads.length, d, dir; i < e; ++i) { + d = downloads[i]; + if (d.state !== QUEUED) { + continue; } + dir = d.destinationPath; + if (!(dir in this._downloadSet)) { + this._downloadSet[dir] = new FairScheduler.SchedItem(dir); + } + this._downloadSet[dir].push(d); } +} +DirScheduler.prototype = Object.freeze({ + __proto__: BaseScheduler.prototype, - next(running) { + next: function(running) { let i, e, d, dir; // reset all counters @@ -456,7 +390,8 @@ class DirScheduler extends BaseScheduler { } // Count the running tasks - for (let d of running) { + for (i = 0, e = running.length; i < e; ++i) { + d = running[i]; dir = d.destinationPath; if (!(dir in this._downloadSet)) { // we don't care, because we don't have any more queued downloads for this directory @@ -490,26 +425,33 @@ class DirScheduler extends BaseScheduler { return d; } return null; - } - destroy() { - for (let k in this._downloadSet) { - this._downloadSet[k].destroy(); - delete this._downloadSet[k]; - } - this._downloadSet = null; - } -} -exports.DirScheduler = DirScheduler; + }, + destroy: FairScheduler.prototype.destroy, +}); +Object.freeze(DirScheduler); //Random scheduler. Does not respect limits -class RndScheduler extends BaseScheduler { - constructor(downloads, running) { - super(); - this._schedule = downloads.filter(BaseScheduler._queuedFilter); - shuffle(this._schedule); - } +function RndScheduler(downloads, running) { + this._schedule = downloads.filter(this._queuedFilter); + this.shuffle(this._schedule); } -exports.RndScheduler = RndScheduler; +// Fisher-Yates based shuffle +RndScheduler.prototype = Object.freeze({ + __proto__: BaseScheduler.prototype, + shuffle: function shuffle(a) { + let c, e = a.length; + if (e < 4) { + // no need to shuffle for such small sets + return; + } + while (e > 1) { + c = Math.floor(Math.random() * (e--)); + // swap + [a[e], a[c]] = [a[c], a[e]]; + } + } +}); +Object.freeze(RndScheduler); let Scheduler; function loadScheduler() { @@ -540,8 +482,8 @@ var buckets = Object.create(null); var unlimitedBucket = new ByteBucket(-1, 1.0, "unlimited"); function loadServerBuckets() { for (let b in buckets) { - if (limits.has(b)) { - buckets[b].byteRate = limits.get(b).speed * 1024; + if (b in limits) { + buckets[b].byteRate = limits[b].speed * 1024; } else { buckets[b].byteRate = -1; @@ -556,8 +498,8 @@ function getServerBucket(d) { if (host in buckets) { return buckets[host]; } - if (limits.has(host)) { - return (buckets[host] = new ByteBucket(limits.get(host).speed * 1024, 1.2, host)); + if (host in limits) { + return (buckets[host] = new ByteBucket(limits[host].speed * 1024, 1.2, host)); } return unlimitedBucket; } @@ -576,7 +518,6 @@ const Observer = { } }; Prefs.addObserver(PREFS, Observer); -require("./observers").add(Observer, "DTA:domain-prefs"); unload(() => Observer.unload()); Observer.observe(); diff --git a/modules/support/stringfuncs.js b/modules/support/stringfuncs.js index 396fdcd33..c00da13f5 100644 --- a/modules/support/stringfuncs.js +++ b/modules/support/stringfuncs.js @@ -8,6 +8,7 @@ const {memoize} = require("./memoize"); const rbc_u = /[\n\r\v?:<>*|"]/g; const rbc_w = /%(?:25)?20/g; const rsl_r = /[\/\\]/g; +const gufn_u = /\?.*$/; const SYSTEMSLASH = (function() { let f = Services.dirsvc.get("TmpD", Ci.nsIFile); diff --git a/modules/support/textlinks.js b/modules/support/textlinks.js index 279e59b36..445b8f228 100644 --- a/modules/support/textlinks.js +++ b/modules/support/textlinks.js @@ -3,13 +3,8 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -const XRegExp = require("thirdparty/xregexp"); - // Link matcher -const regLinks = new XRegExp( - "\\b(?:(?:h(?:x+|tt)?ps?|f(?:x+|t)p):\\/\\/(?:[\\pL\\pN\\pS]+?:[\\pL\\pN\\pS]+?@)?|www\\d?\\.)" + - "[\\d\\w.-]+\\.?(?:\\/[\\p{N}\\p{L}\\pP\\pS]*)?", - "giu"); +const regLinks = /\b(?:(?:h(?:x+|tt)?ps?|f(?:x+|t)p):\/\/|www\d?\.)[\d\w.-]+\.?(?:\/[\d\w+&@#\/%?=~_|!:,.;\(\)$-]*)?/ig; // Match more exactly or more than 3 dots. Links are then assumed "cropped" and will be ignored. const regShortened = /\.{3,}/; // http cleanup @@ -51,24 +46,24 @@ function mapper(e) { * @param title (string) Optional. Title/description * @see DOMElement */ -class FakeLink { - constructor (url, title) { - this.src = this.href = url; - if (title) { - this.title = title; - } +function FakeLink(url, title) { + this.src = this.href = url; + if (!!title) { + this.title = title; } - hasAttribute(attr) { +} +FakeLink.prototype = Object.freeze({ + childNodes: Object.freeze([]), + hasAttribute: function(attr) { return (attr in this); - } - getAttribute(attr) { + }, + getAttribute: function(attr) { return (attr in this) ? this[attr] : null; - } - toString() { + }, + toString: function() { return this.href; } -} -FakeLink.prototype.childNodes = Object.freeze([]); +}); /** * Parses a text looking for any URLs with supported protocols diff --git a/modules/support/urlmanager.js b/modules/support/urlmanager.js index c62d5207d..365779bec 100644 --- a/modules/support/urlmanager.js +++ b/modules/support/urlmanager.js @@ -9,6 +9,7 @@ const { normalizeSlashes, removeLeadingSlash, removeFinalSlash, + removeLeadingChar, toURL } = require("./stringfuncs"); @@ -17,12 +18,11 @@ function compareFn(a, b) { return rv ? rv : (Math.floor(Math.random() * 3) - 1); } -class UrlManager { - constructor(urls) { - this.initByArray(urls); - } - - initByArray(urls) { +function UrlManager(urls) { + this.initByArray(urls); +} +UrlManager.prototype = { + initByArray: function um_initByArray(urls) { this._urls = []; for (let u of urls) { if (u instanceof URL || (u.url && u.url instanceof Ci.nsIURI)) { @@ -45,28 +45,20 @@ class UrlManager { this._url = this._urls[0].url; this._usable = this._urls[0].usable; this._makeGood(); - } - _usableURL() { - return toURL(this._usable); - } - _usableURLPath() { - let rv = this.usableURL.path || this.usableURL.pathQueryRef; + }, + _usableURL: function() { return toURL(this._usable); }, + _usableURLPath: function() { + let rv = this.usableURL.path; if (rv.length) { rv = removeFinalSlash(normalizeSlashes(rv.substring(0, rv.lastIndexOf("/")))); } return removeLeadingSlash(rv); - } - _host() { - return this.usableURL.host; - } - _spec() { - return this._url.spec; - } - _domain() { - return Limits.getEffectiveHost(this._url); - } - add(url) { - if (!(url instanceof URL)) { + }, + _host: function() { return this.usableURL.host; }, + _spec: function() { return this._url.spec; }, + _domain: function() { return Limits.getEffectiveHost(this._url); }, + add: function um_add(url) { + if (!url instanceof URL) { throw new Exception(url + " is not an URL"); } for (let i = 0; i < this._urls.length; ++i) { @@ -75,18 +67,16 @@ class UrlManager { } } this._urls.push(url); - } - _rotate() { + }, + _rotate: function um_rotate() { if (this.good.length < 2) { return; } this.good.push(this.good.shift()); - } - static _makeGood_check(u) { - return !('bad' in u); - } - _makeGood() { - this.good = this._urls.filter(UrlManager._makeGood_check); + }, + _makeGood_check: function(u) { return !('bad' in u); }, + _makeGood: function um_makeGood() { + this.good = this._urls.filter(this._makeGood_check); if (!this.good.length) { // all marked bad; actually a bug Cu.reportError("UM: all marked bad"); @@ -95,29 +85,23 @@ class UrlManager { } this.good = this._urls.map(e => e); } - } - getURL(idx) { + }, + getURL: function um_getURL(idx) { let rv = this.good[0]; this._rotate(); return rv; - } - get url() { - return this._url; - } - get usable() { - return this._usable; - } - get length() { - return this._urls.length; - } + }, + get url() { return this._url; }, + get usable() { return this._usable; }, + get length() { return this._urls.length; }, get all() { return this.toArray(); - } - replace(url, newurl) { + }, + replace: function(url, newurl) { this._urls = this._urls.map(u => u.spec === url.spec ? newurl : u); this._makeGood(); - } - markBad(url) { + }, + markBad: function um_markBad(url) { if (this.good.length === 1) { // cannot mark the last url bad :p return false; @@ -133,18 +117,14 @@ class UrlManager { } this._makeGood(); return true; - } - toJSON() { - return this._urls; - } - toString() { + }, + toJSON: function um_toJSON() { return this._urls; }, + toString: function() { return this._urls.reduce((v, u) => v + u.preference + " " + u.url + "\n"); - } + }, // clone ;) - toArray() { - return this._urls.map(e => e); - } -} + toArray: function() { return this._urls.map(e => e); } +}; lazyProto(UrlManager.prototype, "usableURL", UrlManager.prototype._usableURL); lazyProto(UrlManager.prototype, "usableURLPath", UrlManager.prototype._usableURLPath); lazyProto(UrlManager.prototype, "host", UrlManager.prototype._host); diff --git a/modules/utils.js b/modules/utils.js index 92f802684..12c55a0f1 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -45,17 +45,17 @@ exports.newUUIDString = function newUUIDString() { * @param stop Stop value (exclusive) * @param step Optional. Step value (default: 1/-1) */ -exports.range = function* range(...args) { - if (!args.length) { +exports.range = function* range() { + if (!arguments.length) { throw Components.results.NS_ERROR_INVALID_ARG; } - let start = 0, stop = parseInt(args[0], 10), step; - if (args.length >= 2) { + let start = 0, stop = parseInt(arguments[0], 10), step; + if (arguments.length >= 2) { start = stop; - stop = parseInt(args[1], 10); + stop = parseInt(arguments[1], 10); } - if (args.length >= 3) { - step = parseInt(args[2], 10); + if (arguments.length >= 3) { + step = parseInt(arguments[2], 10); } else { step = stop - start > 0 ? 1 : -1; @@ -151,29 +151,13 @@ exports.mapInSitu = fmi.mapInSitu; exports.filterMapInSitu = fmi.filterMapInSitu; exports.mapFilterInSitu = fmi.mapFilterInSitu; -exports.shuffle = function(a) { - let c, e = a.length; - if (e < 2) { - return; - } - if (e === 2) { - [a[0], a[1]] = [a[1], a[0]]; - return; - } - - while (e > 1) { - c = Math.floor(Math.random() * (e--)); - // swap - [a[e], a[c]] = [a[c], a[e]]; - } -}; - exports.randint = function(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min)) + min; }; + /** * Sorts an array with natural sort order. * @param arr (array) Array to sort @@ -337,8 +321,8 @@ exports.SimpleIterator = Object.freeze(SimpleIterator); * regular JS properties. * @param properties (nsIProperties) initial properties */ -function Properties(...args) { - for (let p of args) { +function Properties() { + for (let p of Array.slice(arguments)) { this._parse(p); } } @@ -427,17 +411,16 @@ exports.Properties = Object.freeze(Properties); /** * Mime quality param constructor */ -class MimeQuality { - constructor() { - this._q = {}; - } - +function MimeQuality() { + this._q = {}; +} +MimeQuality.prototype = Object.freeze({ /** * Add new item * @param v (string) Parameter value * @param q (number) Quality number */ - add(v, q) { + add: function(v, q) { if (typeof q !== "number" || q > 1 || q < 0) { throw new Error("Invalid q"); } @@ -447,12 +430,12 @@ class MimeQuality { } this._q[q].push(v); return this; - } + }, /** * String representation to be used as Mime parameter literal * @return Representation */ - toString() { + toString: function() { function qval(x, i) { return i + (x >= 1 ? "" : ";q=" + x); } @@ -471,7 +454,7 @@ class MimeQuality { }); return exports.mapInSitu(rv, e => e.v).join(","); } -} +}); exports.MimeQuality = Object.freeze(MimeQuality); let _bundles = Object.create(null); @@ -490,8 +473,7 @@ function _loadBundles(urls) { strings[s.key] = s.value; } if (uri.host === "dta") { - const path = uri.path || uri.pathQueryRef; - url = "chrome://dta-locale" + path.replace("/locale/", "/content/"); + url = "chrome://dta-locale" + uri.path.replace("/locale/", "/content/"); log(LOG_DEBUG, "also loading: " + url); for (let s of new SimpleIterator(bundle(url), Ci.nsIPropertyElement)) { let k = s.key; @@ -532,27 +514,31 @@ function _loadBundles(urls) { * @see _ */ var StringBundles_params; -class StringBundles { - constructor(documentOrStrings) { - if (!('getElementsByTagNameNS' in documentOrStrings)) { - this._strings = _loadBundles(documentOrStrings); - } - else { - this._strings = _loadBundles(Array.map( - documentOrStrings.getElementsByTagNameNS(NS_DTA, 'stringbundle'), +function StringBundles(documentOrStrings) { + if (!('getElementsByTagNameNS' in documentOrStrings)) { + this._strings = _loadBundles(documentOrStrings); + } + else { + this._strings = _loadBundles(Array.map( + documentOrStrings.getElementsByTagNameNS(NS_DTA, 'stringbundle'), + e => e.getAttribute('src') + ).concat( + Array.map( + documentOrStrings.getElementsByTagNameNS(NS_XUL, 'stringbundle'), e => e.getAttribute('src') - ).concat( - Array.map( - documentOrStrings.getElementsByTagNameNS(NS_XUL, 'stringbundle'), - e => e.getAttribute('src') - ) - )); - } + ) + )); } - getString(id) { +} +StringBundles._br = /%S/gi; +StringBundles._repl = function() { + return StringBundles_params.shift(); +}; +StringBundles.prototype = Object.freeze({ + getString: function(id) { return this._strings[id]; - } - getFormattedString(id, params, num) { + }, + getFormattedString: function(id, params, num) { let fmt = this.getString(id); if (isFinite(num)) { fmt = PluralForm.get(num, fmt); @@ -566,11 +552,7 @@ class StringBundles { } return fmt; } -} -StringBundles._br = /%S/gi; -StringBundles._repl = function() { - return StringBundles_params.shift(); -}; +}); const StringBundles_Observer = { observe: function() { _bundles = Object.create(null); @@ -667,23 +649,23 @@ exports.normalizeMetaPrefs = function(urls) { const makeDirCache = new LRUMap(10); -exports.makeDir = async function(dir, perms, force) { +exports.makeDir = function*(dir, perms, force) { if (!force && makeDirCache.has(dir.path)) { return; } try { - await OS.File.makeDir(dir.path, {unixMode: perms}); + yield OS.File.makeDir(dir.path, {unixMode: perms}); makeDirCache.set(dir.path, perms); } catch (ex if ex.becauseExists) { // no op } catch (ex if ex.becauseNoSuchFile) { - await exports.makeDir(dir.parent, perms); - await exports.makeDir(dir, perms); + yield exports.makeDir(dir.parent, perms); + yield exports.makeDir(dir, perms); } catch (ex if ex.winLastError === 3) { - await exports.makeDir(dir.parent, perms); - await exports.makeDir(dir, perms); + yield exports.makeDir(dir.parent, perms); + yield exports.makeDir(dir, perms); } }; diff --git a/modules/version.js b/modules/version.js index 4d8c9e480..177e2b72e 100644 --- a/modules/version.js +++ b/modules/version.js @@ -3,7 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/ */ "use strict"; -const ID = 'dta@downthemall.net'; +const ID = "{DDC359D1-844A-42a7-9AA1-88A850A938A8}" let _callbacks = []; Object.defineProperties(exports, { From ac8359c7b41c4378f2ca9012ec4b174a573289ec Mon Sep 17 00:00:00 2001 From: dirkf Date: Sat, 25 Sep 2021 13:08:21 +0100 Subject: [PATCH 2/3] Update SeaMonkey compatibility --- install.rdf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.rdf b/install.rdf index c41689daa..61680e513 100644 --- a/install.rdf +++ b/install.rdf @@ -4,7 +4,7 @@ {DDC359D1-844A-42a7-9AA1-88A850A938A8} DownThemAll! The mass downloader for Firefox. - 3.0.9 + 3.0.9.1 true true 2 @@ -35,7 +35,7 @@ {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} 2.40 - 2.49.* + 2.53.9.* From 7ee407d17cb7c54d72415c751e294b9ed089bade Mon Sep 17 00:00:00 2001 From: dirkf Date: Mon, 22 Nov 2021 14:21:06 +0000 Subject: [PATCH 3/3] Replace non-standard 'catch (ex if cond)' --- chrome.manifest | 12 +++++++----- chrome/content/dta/manager/manager.js | 28 +++++++++++++++++++++------ install.rdf | 4 ++-- modules/components.js | 10 +++++++--- modules/manager/chunk.js | 24 +++++++++++++++++------ modules/manager/connection.js | 10 +++++++--- modules/support/alertservice.js | 20 ++++++++++++------- modules/support/filtermanager.js | 8 ++++++-- modules/utils.js | 21 ++++++++++---------- 9 files changed, 92 insertions(+), 45 deletions(-) diff --git a/chrome.manifest b/chrome.manifest index f1e384bfe..564906922 100644 --- a/chrome.manifest +++ b/chrome.manifest @@ -8,6 +8,7 @@ content dta-public chrome/public/ contentaccessible skin dta-public classic/1.0 chrome/public/ ## aux packages ## +content dta-tests tests/ content dta-modules modules/ ## additional locales @@ -33,14 +34,15 @@ locale dta zh-CN chrome/locale/zh-CN/ locale dta zh-TW chrome/locale/zh-TW/ ## platform package ## -content dta-platform chrome/content/ platform -skin dta-platform classic/1.0 chrome/skin/ +content dta-platform chrome/content/unix/ +content dta-platform chrome/content/mac/ os=Darwin +content dta-platform chrome/content/win/ os=WINNT +skin dta-platform classic/1.0 chrome/skin/unix/ +skin dta-platform classic/1.0 chrome/skin/mac/ os=Darwin +skin dta-platform classic/1.0 chrome/skin/win/ os=WINNT override chrome://dta/content/dta/manager.xul chrome://dta/content/dta/manager-aero.xul os=WINNT osversion=6 override chrome://dta/content/dta/manager.xul chrome://dta/content/dta/manager-newer.xul os=WINNT osversion>6 override chrome://dta/content/dta/manager.xul chrome://dta/content/dta/manager-aero.xul os=WINNT osversion=6.1 override chrome://dta-platform/skin/common.css chrome://dta-platform/skin/common-aero.css os=WINNT osversion>=6 override chrome://dta/skin/manager/netstatus.png chrome://dta-platform/skin/netstatus-aero.png os=WINNT osversion>=6 - -# Hack to prevent .Net Framework Assistant from messing up the browser, courtesy of AdBlock Plus -override chrome://dotnetassistant/content/bootstrap.xul data:text/xml, diff --git a/chrome/content/dta/manager/manager.js b/chrome/content/dta/manager/manager.js index 07c04dec7..6b9f6b9d5 100644 --- a/chrome/content/dta/manager/manager.js +++ b/chrome/content/dta/manager/manager.js @@ -2071,8 +2071,12 @@ QueueItem.prototype = { try { yield OS.File.remove(file.path); } - catch (ex if ex.becauseNoSuchFile) { - // no op + catch (ex) { + if (!(ex.becauseNoSuchFile)) { + throw ex; + } /* else { + // no op + } */ } } @@ -2438,8 +2442,12 @@ QueueItem.prototype = { try { yield Utils.makeDir(file.parent, Prefs.dirPermissions); } - catch (ex if ex.becauseExists) { + catch (ex) { + if (!(ex.becauseExists)) { + throw ex; + } /* else { // no op + } */ } try { if (this.totalSize === (yield OS.File.stat(file.path)).size) { @@ -2447,8 +2455,12 @@ QueueItem.prototype = { return; } } - catch (ex if ex.becauseNoSuchFile) { + catch (ex) { + if (!(ex.becauseNoSuchFile)) { + throw ex; + } /* else { // no op + } */ } let pa = Preallocator.prealloc( file, @@ -2486,8 +2498,12 @@ QueueItem.prototype = { Task.spawn(function*() { try { yield OS.File.remove(tmpFile.path); - } catch (ex if ex.becauseNoSuchFile) { - // no op + } catch (ex) { + if (!(ex.becauseNoSuchFile)) { + throw ex; + } /* else { + // no op + } */ } }).then(null, function(ex) { log(LOG_ERROR, "failed to remove tmpfile: " + tmpFile.path, ex); diff --git a/install.rdf b/install.rdf index 61680e513..cbd34f329 100644 --- a/install.rdf +++ b/install.rdf @@ -4,7 +4,7 @@ {DDC359D1-844A-42a7-9AA1-88A850A938A8} DownThemAll! The mass downloader for Firefox. - 3.0.9.1 + 3.0.9.2 true true 2 @@ -35,7 +35,7 @@ {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} 2.40 - 2.53.9.* + 2.53.10.* diff --git a/modules/components.js b/modules/components.js index c341258aa..90b53ae24 100644 --- a/modules/components.js +++ b/modules/components.js @@ -28,9 +28,13 @@ function createFactory(direct, cls) { try { Cm.registerFactory(i.classID, i.classDescription, i.contractID, this); } - catch (ex if ex.result === Cr.NS_ERROR_FACTORY_EXISTS) { - defer(this.register.bind(this)); - return; + catch (ex) { + if (ex.result === Cr.NS_ERROR_FACTORY_EXISTS) { + defer(this.register.bind(this)); + return; + } else { + throw ex; + } } if (i.xpcom_categories) { diff --git a/modules/manager/chunk.js b/modules/manager/chunk.js index 0707c12ec..ba9a61332 100644 --- a/modules/manager/chunk.js +++ b/modules/manager/chunk.js @@ -214,8 +214,12 @@ Chunk.prototype = { try { yield makeDir(file.parent, Prefs.dirPermissions, true); } - catch (ex if ex.becauseExists) { - // no op + catch (ex) { + if (!(ex.becauseExists)) { + throw ex; + } /* else { + // no op + } */ } let outStream = this._fileOutputStream = new Instances.FileOutputStream( file, @@ -422,8 +426,12 @@ Chunk.prototype = { try { written = this._outStream.writeFrom(aInputStream, bytes); } - catch (ex if ex.result == Cr.NS_BASE_STREAM_WOULD_BLOCK || ex == Cr.NS_BASE_STREAM_WOULD_BLOCK) { - // aka still nothing written + catch (ex) { + if (!(ex.result == Cr.NS_BASE_STREAM_WOULD_BLOCK || ex == Cr.NS_BASE_STREAM_WOULD_BLOCK)) { + throw ex; + } /* else { + // aka still nothing written + } */ } /* jshint +W116 */ let remain = bytes - written; @@ -474,8 +482,12 @@ Chunk.prototype = { try { written = this._outStream.writeFrom(instream, avail); } - catch (ex if ex.result == Cr.NS_BASE_STREAM_WOULD_BLOCK || ex == Cr.NS_BASE_STREAM_WOULD_BLOCK) { - // nothing written + catch (ex) { + if (!(ex.result == Cr.NS_BASE_STREAM_WOULD_BLOCK || ex == Cr.NS_BASE_STREAM_WOULD_BLOCK)) { + throw ex; + } /* else { + // nothing written + } */ } /* jshint +W116 */ avail -= written; diff --git a/modules/manager/connection.js b/modules/manager/connection.js index 02ad8a2ce..db9cc7848 100644 --- a/modules/manager/connection.js +++ b/modules/manager/connection.js @@ -491,9 +491,13 @@ Connection.prototype = { this.discard(aInputStream, aCount - written); } } - catch (ex if (ex !== NS_ERROR_BINDING_ABORTED && ex.result !== NS_ERROR_BINDING_ABORTED)) { - log(LOG_ERROR, 'onDataAvailable', ex); - this.writeFailed(ex); + catch (ex) { + if (ex !== NS_ERROR_BINDING_ABORTED && ex.result !== NS_ERROR_BINDING_ABORTED) { + log(LOG_ERROR, 'onDataAvailable', ex); + this.writeFailed(ex); + } else { + throw ex; + } } }, diff --git a/modules/support/alertservice.js b/modules/support/alertservice.js index 08329cf3b..41c86f81b 100644 --- a/modules/support/alertservice.js +++ b/modules/support/alertservice.js @@ -118,13 +118,19 @@ exports.show = function alertservice_show(title, msg, callback, icon) { "@downthemall.net/" + Date.now().toString() ); } - catch (ex if ex.result === Cr.NS_ERROR_NOT_IMPLEMENTED) { - log(LOG_DEBUG, "alertsservice not available after all", ex); - } - catch (ex if ex.result === Cr.NS_ERROR_NOT_AVAILABLE) { - log(LOG_DEBUG, "alertsservice (temporarily) not available", ex); - } catch (ex) { - log(LOG_ERROR, "alertsservice unexpectedly failed", ex); + switch (ex.result) { + case Cr.NS_ERROR_NOT_IMPLEMENTED: { + log(LOG_DEBUG, "alertsservice not available after all", ex); + break; + } + case Cr.NS_ERROR_NOT_AVAILABLE: { + log(LOG_DEBUG, "alertsservice (temporarily) not available", ex); + break; + } + default: { + log(LOG_ERROR, "alertsservice unexpectedly failed", ex); + } + } } }; diff --git a/modules/support/filtermanager.js b/modules/support/filtermanager.js index e27706227..3d0ed5f52 100644 --- a/modules/support/filtermanager.js +++ b/modules/support/filtermanager.js @@ -610,8 +610,12 @@ FilterManagerImpl.prototype = { try { yield OS.File.makeDir(this._file.parent.path, {unixMode: 0o775, ignoreExisting: true}); } - catch (ex if ex.becauseExists) { - // no op; + catch (ex) { + if (!(ex.becauseExists)) { + throw ex; + } /* else { + // no op; + } */ } yield this._saver.saveChanges(); } diff --git a/modules/utils.js b/modules/utils.js index 12c55a0f1..02db677d8 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -657,15 +657,14 @@ exports.makeDir = function*(dir, perms, force) { yield OS.File.makeDir(dir.path, {unixMode: perms}); makeDirCache.set(dir.path, perms); } - catch (ex if ex.becauseExists) { - // no op - } - catch (ex if ex.becauseNoSuchFile) { - yield exports.makeDir(dir.parent, perms); - yield exports.makeDir(dir, perms); - } - catch (ex if ex.winLastError === 3) { - yield exports.makeDir(dir.parent, perms); - yield exports.makeDir(dir, perms); - } + catch (ex) { + if (ex.becauseExists) { + // no op + } else if (ex.becauseNoSuchFile || (ex.winLastError === 3)) { + yield exports.makeDir(dir.parent, perms); + yield exports.makeDir(dir, perms); + } else { + throw ex; + } + } };