toolkit/components/jsdownloads/src/DownloadStore.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /**
     8  * Handles serialization of Download objects and persistence into a file, so
     9  * that the state of downloads can be restored across sessions.
    10  *
    11  * The file is stored in JSON format, without indentation.  With indentation
    12  * applied, the file would look like this:
    13  *
    14  * {
    15  *   "list": [
    16  *     {
    17  *       "source": "http://www.example.com/download.txt",
    18  *       "target": "/home/user/Downloads/download.txt"
    19  *     },
    20  *     {
    21  *       "source": {
    22  *         "url": "http://www.example.com/download.txt",
    23  *         "referrer": "http://www.example.com/referrer.html"
    24  *       },
    25  *       "target": "/home/user/Downloads/download-2.txt"
    26  *     }
    27  *   ]
    28  * }
    29  */
    31 "use strict";
    33 this.EXPORTED_SYMBOLS = [
    34   "DownloadStore",
    35 ];
    37 ////////////////////////////////////////////////////////////////////////////////
    38 //// Globals
    40 const Cc = Components.classes;
    41 const Ci = Components.interfaces;
    42 const Cu = Components.utils;
    43 const Cr = Components.results;
    45 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    47 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
    48                                   "resource://gre/modules/Downloads.jsm");
    49 XPCOMUtils.defineLazyModuleGetter(this, "OS",
    50                                   "resource://gre/modules/osfile.jsm")
    51 XPCOMUtils.defineLazyModuleGetter(this, "Task",
    52                                   "resource://gre/modules/Task.jsm");
    54 XPCOMUtils.defineLazyGetter(this, "gTextDecoder", function () {
    55   return new TextDecoder();
    56 });
    58 XPCOMUtils.defineLazyGetter(this, "gTextEncoder", function () {
    59   return new TextEncoder();
    60 });
    62 ////////////////////////////////////////////////////////////////////////////////
    63 //// DownloadStore
    65 /**
    66  * Handles serialization of Download objects and persistence into a file, so
    67  * that the state of downloads can be restored across sessions.
    68  *
    69  * @param aList
    70  *        DownloadList object to be populated or serialized.
    71  * @param aPath
    72  *        String containing the file path where data should be saved.
    73  */
    74 this.DownloadStore = function (aList, aPath)
    75 {
    76   this.list = aList;
    77   this.path = aPath;
    78 }
    80 this.DownloadStore.prototype = {
    81   /**
    82    * DownloadList object to be populated or serialized.
    83    */
    84   list: null,
    86   /**
    87    * String containing the file path where data should be saved.
    88    */
    89   path: "",
    91   /**
    92    * This function is called with a Download object as its first argument, and
    93    * should return true if the item should be saved.
    94    */
    95   onsaveitem: () => true,
    97   /**
    98    * Loads persistent downloads from the file to the list.
    99    *
   100    * @return {Promise}
   101    * @resolves When the operation finished successfully.
   102    * @rejects JavaScript exception.
   103    */
   104   load: function DS_load()
   105   {
   106     return Task.spawn(function task_DS_load() {
   107       let bytes;
   108       try {
   109         bytes = yield OS.File.read(this.path);
   110       } catch (ex if ex instanceof OS.File.Error && ex.becauseNoSuchFile) {
   111         // If the file does not exist, there are no downloads to load.
   112         return;
   113       }
   115       let storeData = JSON.parse(gTextDecoder.decode(bytes));
   117       // Create live downloads based on the static snapshot.
   118       for (let downloadData of storeData.list) {
   119         try {
   120           let download = yield Downloads.createDownload(downloadData);
   121           try {
   122             if (!download.succeeded && !download.canceled && !download.error) {
   123               // Try to restart the download if it was in progress during the
   124               // previous session.
   125               download.start();
   126             } else {
   127               // If the download was not in progress, try to update the current
   128               // progress from disk.  This is relevant in case we retained
   129               // partially downloaded data.
   130               yield download.refresh();
   131             }
   132           } finally {
   133             // Add the download to the list if we succeeded in creating it,
   134             // after we have updated its initial state.
   135             yield this.list.add(download);
   136           }
   137         } catch (ex) {
   138           // If an item is unrecognized, don't prevent others from being loaded.
   139           Cu.reportError(ex);
   140         }
   141       }
   142     }.bind(this));
   143   },
   145   /**
   146    * Saves persistent downloads from the list to the file.
   147    *
   148    * If an error occurs, the previous file is not deleted.
   149    *
   150    * @return {Promise}
   151    * @resolves When the operation finished successfully.
   152    * @rejects JavaScript exception.
   153    */
   154   save: function DS_save()
   155   {
   156     return Task.spawn(function task_DS_save() {
   157       let downloads = yield this.list.getAll();
   159       // Take a static snapshot of the current state of all the downloads.
   160       let storeData = { list: [] };
   161       let atLeastOneDownload = false;
   162       for (let download of downloads) {
   163         try {
   164           if (!this.onsaveitem(download)) {
   165             continue;
   166           }
   167           storeData.list.push(download.toSerializable());
   168           atLeastOneDownload = true;
   169         } catch (ex) {
   170           // If an item cannot be converted to a serializable form, don't
   171           // prevent others from being saved.
   172           Cu.reportError(ex);
   173         }
   174       }
   176       if (atLeastOneDownload) {
   177         // Create or overwrite the file if there are downloads to save.
   178         let bytes = gTextEncoder.encode(JSON.stringify(storeData));
   179         yield OS.File.writeAtomic(this.path, bytes,
   180                                   { tmpPath: this.path + ".tmp" });
   181       } else {
   182         // Remove the file if there are no downloads to save at all.
   183         try {
   184           yield OS.File.remove(this.path);
   185         } catch (ex if ex instanceof OS.File.Error &&
   186                  (ex.becauseNoSuchFile || ex.becauseAccessDenied)) {
   187           // On Windows, we may get an access denied error instead of a no such
   188           // file error if the file existed before, and was recently deleted.
   189         }
   190       }
   191     }.bind(this));
   192   },
   193 };

mercurial