Bug 1163937 - Add forceSave function to DownloadIntegration and ensured that downloads removed in Sanitizer do not persist. r=margaret, a=ritu
authorDylan Roeh <droeh@mozilla.com>
Tue, 11 Aug 2015 15:33:57 -0500
changeset 289001 6714dfb68afbc997ba0eeb5c6c83e86e672bc58d
parent 289000 e975c8dbdb1832a034a9b8dd2aafb4d52201b81d
child 289002 835be6d79b43f6ddf8ac420ead13beb4810d4742
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret, ritu
bugs1163937
milestone42.0a2
Bug 1163937 - Add forceSave function to DownloadIntegration and ensured that downloads removed in Sanitizer do not persist. r=margaret, a=ritu
mobile/android/chrome/content/browser.js
mobile/android/modules/Sanitizer.jsm
toolkit/components/jsdownloads/src/DownloadIntegration.jsm
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -908,17 +908,17 @@ var BrowserApp = {
       });
 
     NativeWindow.contextmenus.add(stringGetter("contextmenu.mute"),
       NativeWindow.contextmenus.mediaContext("media-unmuted"),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_mute");
         aTarget.muted = true;
       });
-  
+
     NativeWindow.contextmenus.add(stringGetter("contextmenu.unmute"),
       NativeWindow.contextmenus.mediaContext("media-muted"),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_unmute");
         aTarget.muted = false;
       });
 
     NativeWindow.contextmenus.add(stringGetter("contextmenu.copyImageLocation"),
@@ -1600,24 +1600,24 @@ var BrowserApp = {
     // even if we crash very soon after.
     if (json.flush) {
       Services.prefs.savePrefFile(null);
     }
   },
 
   sanitize: function (aItems, callback) {
     let success = true;
+    var promises = [];
 
     for (let key in aItems) {
       if (!aItems[key])
         continue;
 
       key = key.replace("private.data.", "");
 
-      var promises = [];
       switch (key) {
         case "cookies_sessions":
           promises.push(Sanitizer.clearItem("cookies"));
           promises.push(Sanitizer.clearItem("sessions"));
           break;
         default:
           promises.push(Sanitizer.clearItem(key));
       }
@@ -2323,17 +2323,17 @@ var NativeWindow = {
       Messaging.sendRequest({ type: "Menu:Remove", id: aId });
     },
 
     update: function(aId, aOptions) {
       if (!aOptions)
         return;
 
       Messaging.sendRequest({
-        type: "Menu:Update", 
+        type: "Menu:Update",
         id: aId,
         options: aOptions
       });
     }
   },
 
   doorhanger: {
     _callbacks: {},
@@ -3727,17 +3727,17 @@ Tab.prototype = {
 
     // We add in a bit of fudge just so that the end characters
     // don't accidentally get clipped. 15px is an arbitrary choice.
     gReflowPending = setTimeout(doChangeMaxLineBoxWidth,
                                 reflozTimeout,
                                 viewportWidth - 15);
   },
 
-  /** 
+  /**
    * Reloads the tab with the desktop mode setting.
    */
   reloadWithMode: function (aDesktopMode) {
     // notify desktopmode for PIDOMWindow
     let win = this.browser.contentWindow;
     let dwi = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
     dwi.setDesktopModeViewport(aDesktopMode);
 
@@ -7231,17 +7231,17 @@ var IdentityHandler = {
    * Determine the identity of the page being displayed by examining its SSL cert
    * (if available). Return the data needed to update the UI.
    */
   checkIdentity: function checkIdentity(aState, aBrowser) {
     this._lastStatus = aBrowser.securityUI
                                .QueryInterface(Components.interfaces.nsISSLStatusProvider)
                                .SSLStatus;
 
-    // Don't pass in the actual location object, since it can cause us to 
+    // Don't pass in the actual location object, since it can cause us to
     // hold on to the window object too long.  Just pass in the fields we
     // care about. (bug 424829)
     let locationObj = {};
     try {
       let location = aBrowser.contentWindow.location;
       locationObj.host = location.host;
       locationObj.hostname = location.hostname;
       locationObj.port = location.port;
--- a/mobile/android/modules/Sanitizer.jsm
+++ b/mobile/android/modules/Sanitizer.jsm
@@ -14,16 +14,19 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/LoadContextInfo.jsm");
 Cu.import("resource://gre/modules/FormHistory.jsm");
 Cu.import("resource://gre/modules/Messaging.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Downloads.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Accounts.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration",
+                                  "resource://gre/modules/DownloadIntegration.jsm");
+
 function dump(a) {
   Services.console.logStringMessage(a);
 }
 
 this.EXPORTED_SYMBOLS = ["Sanitizer"];
 
 function Sanitizer() {}
 Sanitizer.prototype = {
@@ -195,42 +198,46 @@ Sanitizer.prototype = {
         FormHistory.count({}, countDone);
       }
     },
 
     downloadFiles: {
       clear: Task.async(function* () {
         let list = yield Downloads.getList(Downloads.ALL);
         let downloads = yield list.getAll();
+        var finalizePromises = [];
 
         // Logic copied from DownloadList.removeFinished. Ideally, we would
         // just use that method directly, but we want to be able to remove the
         // downloaded files as well.
         for (let download of downloads) {
           // Remove downloads that have been canceled, even if the cancellation
           // operation hasn't completed yet so we don't check "stopped" here.
           // Failed downloads with partial data are also removed.
           if (download.stopped && (!download.hasPartialData || download.error)) {
             // Remove the download first, so that the views don't get the change
             // notifications that may occur during finalization.
             yield list.remove(download);
             // Ensure that the download is stopped and no partial data is kept.
             // This works even if the download state has changed meanwhile.  We
             // don't need to wait for the procedure to be complete before
             // processing the other downloads in the list.
-            download.finalize(true).then(null, Cu.reportError);
+            finalizePromises.push(download.finalize(true).then(() => null, Cu.reportError));
 
             // Delete the downloaded files themselves.
-            OS.File.remove(download.target.path).then(null, ex => {
+            OS.File.remove(download.target.path).then(() => null, ex => {
               if (!(ex instanceof OS.File.Error && ex.becauseNoSuchFile)) {
                 Cu.reportError(ex);
               }
             });
           }
         }
+
+        yield Promise.all(finalizePromises);
+        yield DownloadIntegration.forceSave();
       }),
 
       get canClear()
       {
         return true;
       }
     },
 
--- a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm
+++ b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm
@@ -909,16 +909,30 @@ this.DownloadIntegration = {
       for (let topic of kObserverTopics) {
         Services.obs.addObserver(DownloadObserver, topic, false);
       }
     }
     return Promise.resolve();
   },
 
   /**
+   * Force a save on _store if it exists. Used to ensure downloads do not
+   * persist after being sanitized on Android.
+   *
+   * @return {Promise}
+   * @resolves When _store.save() completes.
+   */
+  forceSave: function DI_forceSave() {
+    if (this._store) {
+      return this._store.save();
+    }
+    return Promise.resolve();
+  },
+
+  /**
    * Checks if we have already imported (or attempted to import)
    * the downloads database from the previous SQLite storage.
    *
    * @return boolean True if we the previous DB was imported.
    */
   get _importedFromSqlite() {
     try {
       return Services.prefs.getBoolPref(kPrefImportedFromSqlite);