1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/jsdownloads/src/DownloadImport.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,195 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +"use strict"; 1.11 + 1.12 +this.EXPORTED_SYMBOLS = [ 1.13 + "DownloadImport", 1.14 +]; 1.15 + 1.16 +//////////////////////////////////////////////////////////////////////////////// 1.17 +//// Globals 1.18 + 1.19 +const Cc = Components.classes; 1.20 +const Ci = Components.interfaces; 1.21 +const Cu = Components.utils; 1.22 +const Cr = Components.results; 1.23 + 1.24 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.25 + 1.26 +XPCOMUtils.defineLazyModuleGetter(this, "Downloads", 1.27 + "resource://gre/modules/Downloads.jsm"); 1.28 +XPCOMUtils.defineLazyModuleGetter(this, "OS", 1.29 + "resource://gre/modules/osfile.jsm") 1.30 +XPCOMUtils.defineLazyModuleGetter(this, "Task", 1.31 + "resource://gre/modules/Task.jsm"); 1.32 +XPCOMUtils.defineLazyModuleGetter(this, "Sqlite", 1.33 + "resource://gre/modules/Sqlite.jsm"); 1.34 +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", 1.35 + "resource://gre/modules/NetUtil.jsm"); 1.36 + 1.37 +/** 1.38 + * These values come from the previous interface 1.39 + * nsIDownloadManager, which has now been deprecated. 1.40 + * These are the only types of download states that 1.41 + * we will import. 1.42 + */ 1.43 +const DOWNLOAD_NOTSTARTED = -1; 1.44 +const DOWNLOAD_DOWNLOADING = 0; 1.45 +const DOWNLOAD_PAUSED = 4; 1.46 +const DOWNLOAD_QUEUED = 5; 1.47 + 1.48 +//////////////////////////////////////////////////////////////////////////////// 1.49 +//// DownloadImport 1.50 + 1.51 +/** 1.52 + * Provides an object that has a method to import downloads 1.53 + * from the previous SQLite storage format. 1.54 + * 1.55 + * @param aList A DownloadList where each successfully 1.56 + * imported download will be added. 1.57 + * @param aPath The path to the database file. 1.58 + */ 1.59 +this.DownloadImport = function (aList, aPath) 1.60 +{ 1.61 + this.list = aList; 1.62 + this.path = aPath; 1.63 +} 1.64 + 1.65 +this.DownloadImport.prototype = { 1.66 + /** 1.67 + * Imports unfinished downloads from the previous SQLite storage 1.68 + * format (supporting schemas 7 and up), to the new Download object 1.69 + * format. Each imported download will be added to the DownloadList 1.70 + * 1.71 + * @return {Promise} 1.72 + * @resolves When the operation has completed (i.e., every download 1.73 + * from the previous database has been read and added to 1.74 + * the DownloadList) 1.75 + */ 1.76 + import: function () { 1.77 + return Task.spawn(function task_DI_import() { 1.78 + let connection = yield Sqlite.openConnection({ path: this.path }); 1.79 + 1.80 + try { 1.81 + let schemaVersion = yield connection.getSchemaVersion(); 1.82 + // We don't support schemas older than version 7 (from 2007) 1.83 + // - Version 7 added the columns mimeType, preferredApplication 1.84 + // and preferredAction in 2007 1.85 + // - Version 8 added the column autoResume in 2007 1.86 + // (if we encounter version 7 we will treat autoResume = false) 1.87 + // - Version 9 is the last known version, which added a unique 1.88 + // GUID text column that is not used here 1.89 + if (schemaVersion < 7) { 1.90 + throw new Error("Unable to import in-progress downloads because " 1.91 + + "the existing profile is too old."); 1.92 + } 1.93 + 1.94 + let rows = yield connection.execute("SELECT * FROM moz_downloads"); 1.95 + 1.96 + for (let row of rows) { 1.97 + try { 1.98 + // Get the DB row data 1.99 + let source = row.getResultByName("source"); 1.100 + let target = row.getResultByName("target"); 1.101 + let tempPath = row.getResultByName("tempPath"); 1.102 + let startTime = row.getResultByName("startTime"); 1.103 + let state = row.getResultByName("state"); 1.104 + let referrer = row.getResultByName("referrer"); 1.105 + let maxBytes = row.getResultByName("maxBytes"); 1.106 + let mimeType = row.getResultByName("mimeType"); 1.107 + let preferredApplication = row.getResultByName("preferredApplication"); 1.108 + let preferredAction = row.getResultByName("preferredAction"); 1.109 + let entityID = row.getResultByName("entityID"); 1.110 + 1.111 + let autoResume = false; 1.112 + try { 1.113 + autoResume = (row.getResultByName("autoResume") == 1); 1.114 + } catch (ex) { 1.115 + // autoResume wasn't present in schema version 7 1.116 + } 1.117 + 1.118 + if (!source) { 1.119 + throw new Error("Attempted to import a row with an empty " + 1.120 + "source column."); 1.121 + } 1.122 + 1.123 + let resumeDownload = false; 1.124 + 1.125 + switch (state) { 1.126 + case DOWNLOAD_NOTSTARTED: 1.127 + case DOWNLOAD_QUEUED: 1.128 + case DOWNLOAD_DOWNLOADING: 1.129 + resumeDownload = true; 1.130 + break; 1.131 + 1.132 + case DOWNLOAD_PAUSED: 1.133 + resumeDownload = autoResume; 1.134 + break; 1.135 + 1.136 + default: 1.137 + // We won't import downloads in other states 1.138 + continue; 1.139 + } 1.140 + 1.141 + // Transform the data 1.142 + let targetPath = NetUtil.newURI(target) 1.143 + .QueryInterface(Ci.nsIFileURL).file.path; 1.144 + 1.145 + let launchWhenSucceeded = (preferredAction != Ci.nsIMIMEInfo.saveToDisk); 1.146 + 1.147 + let downloadOptions = { 1.148 + source: { 1.149 + url: source, 1.150 + referrer: referrer 1.151 + }, 1.152 + target: { 1.153 + path: targetPath, 1.154 + partFilePath: tempPath, 1.155 + }, 1.156 + saver: { 1.157 + type: "copy", 1.158 + entityID: entityID 1.159 + }, 1.160 + startTime: new Date(startTime / 1000), 1.161 + totalBytes: maxBytes, 1.162 + hasPartialData: !!tempPath, 1.163 + tryToKeepPartialData: true, 1.164 + launchWhenSucceeded: launchWhenSucceeded, 1.165 + contentType: mimeType, 1.166 + launcherPath: preferredApplication 1.167 + }; 1.168 + 1.169 + // Paused downloads that should not be auto-resumed are considered 1.170 + // in a "canceled" state. 1.171 + if (!resumeDownload) { 1.172 + downloadOptions.canceled = true; 1.173 + } 1.174 + 1.175 + let download = yield Downloads.createDownload(downloadOptions); 1.176 + 1.177 + yield this.list.add(download); 1.178 + 1.179 + if (resumeDownload) { 1.180 + download.start(); 1.181 + } else { 1.182 + yield download.refresh(); 1.183 + } 1.184 + 1.185 + } catch (ex) { 1.186 + Cu.reportError("Error importing download: " + ex); 1.187 + } 1.188 + } 1.189 + 1.190 + } catch (ex) { 1.191 + Cu.reportError(ex); 1.192 + } finally { 1.193 + yield connection.close(); 1.194 + } 1.195 + }.bind(this)); 1.196 + } 1.197 +} 1.198 +