Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | "use strict"; |
michael@0 | 8 | |
michael@0 | 9 | this.EXPORTED_SYMBOLS = [ |
michael@0 | 10 | "DownloadImport", |
michael@0 | 11 | ]; |
michael@0 | 12 | |
michael@0 | 13 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 14 | //// Globals |
michael@0 | 15 | |
michael@0 | 16 | const Cc = Components.classes; |
michael@0 | 17 | const Ci = Components.interfaces; |
michael@0 | 18 | const Cu = Components.utils; |
michael@0 | 19 | const Cr = Components.results; |
michael@0 | 20 | |
michael@0 | 21 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 22 | |
michael@0 | 23 | XPCOMUtils.defineLazyModuleGetter(this, "Downloads", |
michael@0 | 24 | "resource://gre/modules/Downloads.jsm"); |
michael@0 | 25 | XPCOMUtils.defineLazyModuleGetter(this, "OS", |
michael@0 | 26 | "resource://gre/modules/osfile.jsm") |
michael@0 | 27 | XPCOMUtils.defineLazyModuleGetter(this, "Task", |
michael@0 | 28 | "resource://gre/modules/Task.jsm"); |
michael@0 | 29 | XPCOMUtils.defineLazyModuleGetter(this, "Sqlite", |
michael@0 | 30 | "resource://gre/modules/Sqlite.jsm"); |
michael@0 | 31 | XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", |
michael@0 | 32 | "resource://gre/modules/NetUtil.jsm"); |
michael@0 | 33 | |
michael@0 | 34 | /** |
michael@0 | 35 | * These values come from the previous interface |
michael@0 | 36 | * nsIDownloadManager, which has now been deprecated. |
michael@0 | 37 | * These are the only types of download states that |
michael@0 | 38 | * we will import. |
michael@0 | 39 | */ |
michael@0 | 40 | const DOWNLOAD_NOTSTARTED = -1; |
michael@0 | 41 | const DOWNLOAD_DOWNLOADING = 0; |
michael@0 | 42 | const DOWNLOAD_PAUSED = 4; |
michael@0 | 43 | const DOWNLOAD_QUEUED = 5; |
michael@0 | 44 | |
michael@0 | 45 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 46 | //// DownloadImport |
michael@0 | 47 | |
michael@0 | 48 | /** |
michael@0 | 49 | * Provides an object that has a method to import downloads |
michael@0 | 50 | * from the previous SQLite storage format. |
michael@0 | 51 | * |
michael@0 | 52 | * @param aList A DownloadList where each successfully |
michael@0 | 53 | * imported download will be added. |
michael@0 | 54 | * @param aPath The path to the database file. |
michael@0 | 55 | */ |
michael@0 | 56 | this.DownloadImport = function (aList, aPath) |
michael@0 | 57 | { |
michael@0 | 58 | this.list = aList; |
michael@0 | 59 | this.path = aPath; |
michael@0 | 60 | } |
michael@0 | 61 | |
michael@0 | 62 | this.DownloadImport.prototype = { |
michael@0 | 63 | /** |
michael@0 | 64 | * Imports unfinished downloads from the previous SQLite storage |
michael@0 | 65 | * format (supporting schemas 7 and up), to the new Download object |
michael@0 | 66 | * format. Each imported download will be added to the DownloadList |
michael@0 | 67 | * |
michael@0 | 68 | * @return {Promise} |
michael@0 | 69 | * @resolves When the operation has completed (i.e., every download |
michael@0 | 70 | * from the previous database has been read and added to |
michael@0 | 71 | * the DownloadList) |
michael@0 | 72 | */ |
michael@0 | 73 | import: function () { |
michael@0 | 74 | return Task.spawn(function task_DI_import() { |
michael@0 | 75 | let connection = yield Sqlite.openConnection({ path: this.path }); |
michael@0 | 76 | |
michael@0 | 77 | try { |
michael@0 | 78 | let schemaVersion = yield connection.getSchemaVersion(); |
michael@0 | 79 | // We don't support schemas older than version 7 (from 2007) |
michael@0 | 80 | // - Version 7 added the columns mimeType, preferredApplication |
michael@0 | 81 | // and preferredAction in 2007 |
michael@0 | 82 | // - Version 8 added the column autoResume in 2007 |
michael@0 | 83 | // (if we encounter version 7 we will treat autoResume = false) |
michael@0 | 84 | // - Version 9 is the last known version, which added a unique |
michael@0 | 85 | // GUID text column that is not used here |
michael@0 | 86 | if (schemaVersion < 7) { |
michael@0 | 87 | throw new Error("Unable to import in-progress downloads because " |
michael@0 | 88 | + "the existing profile is too old."); |
michael@0 | 89 | } |
michael@0 | 90 | |
michael@0 | 91 | let rows = yield connection.execute("SELECT * FROM moz_downloads"); |
michael@0 | 92 | |
michael@0 | 93 | for (let row of rows) { |
michael@0 | 94 | try { |
michael@0 | 95 | // Get the DB row data |
michael@0 | 96 | let source = row.getResultByName("source"); |
michael@0 | 97 | let target = row.getResultByName("target"); |
michael@0 | 98 | let tempPath = row.getResultByName("tempPath"); |
michael@0 | 99 | let startTime = row.getResultByName("startTime"); |
michael@0 | 100 | let state = row.getResultByName("state"); |
michael@0 | 101 | let referrer = row.getResultByName("referrer"); |
michael@0 | 102 | let maxBytes = row.getResultByName("maxBytes"); |
michael@0 | 103 | let mimeType = row.getResultByName("mimeType"); |
michael@0 | 104 | let preferredApplication = row.getResultByName("preferredApplication"); |
michael@0 | 105 | let preferredAction = row.getResultByName("preferredAction"); |
michael@0 | 106 | let entityID = row.getResultByName("entityID"); |
michael@0 | 107 | |
michael@0 | 108 | let autoResume = false; |
michael@0 | 109 | try { |
michael@0 | 110 | autoResume = (row.getResultByName("autoResume") == 1); |
michael@0 | 111 | } catch (ex) { |
michael@0 | 112 | // autoResume wasn't present in schema version 7 |
michael@0 | 113 | } |
michael@0 | 114 | |
michael@0 | 115 | if (!source) { |
michael@0 | 116 | throw new Error("Attempted to import a row with an empty " + |
michael@0 | 117 | "source column."); |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | let resumeDownload = false; |
michael@0 | 121 | |
michael@0 | 122 | switch (state) { |
michael@0 | 123 | case DOWNLOAD_NOTSTARTED: |
michael@0 | 124 | case DOWNLOAD_QUEUED: |
michael@0 | 125 | case DOWNLOAD_DOWNLOADING: |
michael@0 | 126 | resumeDownload = true; |
michael@0 | 127 | break; |
michael@0 | 128 | |
michael@0 | 129 | case DOWNLOAD_PAUSED: |
michael@0 | 130 | resumeDownload = autoResume; |
michael@0 | 131 | break; |
michael@0 | 132 | |
michael@0 | 133 | default: |
michael@0 | 134 | // We won't import downloads in other states |
michael@0 | 135 | continue; |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | // Transform the data |
michael@0 | 139 | let targetPath = NetUtil.newURI(target) |
michael@0 | 140 | .QueryInterface(Ci.nsIFileURL).file.path; |
michael@0 | 141 | |
michael@0 | 142 | let launchWhenSucceeded = (preferredAction != Ci.nsIMIMEInfo.saveToDisk); |
michael@0 | 143 | |
michael@0 | 144 | let downloadOptions = { |
michael@0 | 145 | source: { |
michael@0 | 146 | url: source, |
michael@0 | 147 | referrer: referrer |
michael@0 | 148 | }, |
michael@0 | 149 | target: { |
michael@0 | 150 | path: targetPath, |
michael@0 | 151 | partFilePath: tempPath, |
michael@0 | 152 | }, |
michael@0 | 153 | saver: { |
michael@0 | 154 | type: "copy", |
michael@0 | 155 | entityID: entityID |
michael@0 | 156 | }, |
michael@0 | 157 | startTime: new Date(startTime / 1000), |
michael@0 | 158 | totalBytes: maxBytes, |
michael@0 | 159 | hasPartialData: !!tempPath, |
michael@0 | 160 | tryToKeepPartialData: true, |
michael@0 | 161 | launchWhenSucceeded: launchWhenSucceeded, |
michael@0 | 162 | contentType: mimeType, |
michael@0 | 163 | launcherPath: preferredApplication |
michael@0 | 164 | }; |
michael@0 | 165 | |
michael@0 | 166 | // Paused downloads that should not be auto-resumed are considered |
michael@0 | 167 | // in a "canceled" state. |
michael@0 | 168 | if (!resumeDownload) { |
michael@0 | 169 | downloadOptions.canceled = true; |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | let download = yield Downloads.createDownload(downloadOptions); |
michael@0 | 173 | |
michael@0 | 174 | yield this.list.add(download); |
michael@0 | 175 | |
michael@0 | 176 | if (resumeDownload) { |
michael@0 | 177 | download.start(); |
michael@0 | 178 | } else { |
michael@0 | 179 | yield download.refresh(); |
michael@0 | 180 | } |
michael@0 | 181 | |
michael@0 | 182 | } catch (ex) { |
michael@0 | 183 | Cu.reportError("Error importing download: " + ex); |
michael@0 | 184 | } |
michael@0 | 185 | } |
michael@0 | 186 | |
michael@0 | 187 | } catch (ex) { |
michael@0 | 188 | Cu.reportError(ex); |
michael@0 | 189 | } finally { |
michael@0 | 190 | yield connection.close(); |
michael@0 | 191 | } |
michael@0 | 192 | }.bind(this)); |
michael@0 | 193 | } |
michael@0 | 194 | } |
michael@0 | 195 |