Bug 992766 - Infer error when recieving NS_ERROR_FAILURE by going through device storage sanity checks, if device storage api is available. r=fabrice, a=1.4+
authorGhislain 'Aus' Lacroix <glacroix@mozilla.com>
Thu, 10 Apr 2014 09:32:53 -0700
changeset 192746 dd3d371d6f039d9c6d6dc8ba9b3fc2bb1f1da833
parent 192745 fcb53386d49e9b65ec60469723b799f3812ac4ea
child 192747 753b4e48a2d654cfcc2f4a58b1527107ca2c1304
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice, 1
bugs992766
milestone30.0a2
Bug 992766 - Infer error when recieving NS_ERROR_FAILURE by going through device storage sanity checks, if device storage api is available. r=fabrice, a=1.4+
dom/downloads/src/DownloadsAPI.js
dom/downloads/src/DownloadsIPC.jsm
--- a/dom/downloads/src/DownloadsAPI.js
+++ b/dom/downloads/src/DownloadsAPI.js
@@ -2,16 +2,17 @@
  * 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";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
+const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
 Cu.import("resource://gre/modules/DownloadsIPC.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
@@ -314,27 +315,75 @@ DOMDownloadImpl.prototype = {
     props.forEach((prop) => {
       if (aDownload[prop] && (aDownload[prop] != this[prop])) {
         this[prop] = aDownload[prop];
         changed = true;
       }
     });
 
     if (aDownload.error) {
+      //
+      // When we get a generic error failure back from the js downloads api
+      // we will verify the status of device storage to see if we can't provide
+      // a better error result value.
+      //
+      // XXX If these checks expand further, consider moving them into their
+      // own function.
+      //
+      let result = aDownload.error.result;
+      let storage = this._window.navigator.getDeviceStorage("sdcard");
+
+      // If we don't have access to device storage we'll opt out of these
+      // extra checks as they are all dependent on the state of the storage.
+      if (result == Cr.NS_ERROR_FAILURE && storage) {
+        // We will delay sending the notification until we've inferred which
+        // error is really happening.
+        changed = false;
+        debug("Attempting to infer error via device storage sanity checks.");
+        // Get device storage and request availability status.
+        let available = storage.available();
+        available.onsuccess = (function() {
+          debug("Storage Status = '" + available.result + "'");
+          let inferredError = result;
+          switch (available.result) {
+            case "unavailable":
+              inferredError = Cr.NS_ERROR_FILE_NOT_FOUND;
+              break;
+            case "shared":
+              inferredError = Cr.NS_ERROR_FILE_ACCESS_DENIED;
+              break;
+          }
+          this._updateWithError(aDownload, inferredError);
+        }).bind(this);
+        available.onerror = (function() {
+          this._updateWithError(aDownload, result);
+        }).bind(this);
+      }
+
       this.error =
-        new this._window.DOMError("DownloadError", aDownload.error.result);
+        new this._window.DOMError("DownloadError", result);
     } else {
       this.error = null;
     }
 
     // The visible state has not changed, so no need to fire an event.
     if (!changed) {
       return;
     }
 
+    this._sendStateChange();
+  },
+
+  _updateWithError: function(aDownload, aError) {
+    this.error =
+      new this._window.DOMError("DownloadError", aError);
+    this._sendStateChange();
+  },
+
+  _sendStateChange: function() {
     // __DOM_IMPL__ may not be available at first update.
     if (this.__DOM_IMPL__) {
       let event = new this._window.DownloadEvent("statechange", {
         download: this.__DOM_IMPL__
       });
       debug("Dispatching statechange event. state=" + this.state);
       this.__DOM_IMPL__.dispatchEvent(event);
     }
--- a/dom/downloads/src/DownloadsIPC.jsm
+++ b/dom/downloads/src/DownloadsIPC.jsm
@@ -136,17 +136,17 @@ this.DownloadsIPC = {
           this.notifyChanges(download.id);
         }
         break;
       case "Downloads:Remove:Return":
       case "Downloads:Pause:Return":
       case "Downloads:Resume:Return":
         if (this.downloadPromises[download.promiseId]) {
           if (!download.error) {
-          this.downloadPromises[download.promiseId].resolve(download);
+            this.downloadPromises[download.promiseId].resolve(download);
           } else {
             this.downloadPromises[download.promiseId].reject(download);
           }
           delete this.downloadPromises[download.promiseId];
         }
         break;
     }
   },