Bug 1000305 - Part 1 - Add a getIcon() method. r=fabrice r=ehsan
☠☠ backed out by 84b9872f54c7 ☠ ☠
authorTed Clancy <tclancy@mozilla.com>
Thu, 18 Dec 2014 17:40:41 -0500
changeset 220578 45940d63b0c7628249cba30565dcf558e6f2a22f
parent 220577 ad534d0193f491131f7c2d0705242237b27e0f6f
child 220579 2996cc51cb0dcae8c4c42d9bb761f86085b97d84
push id10503
push userryanvm@gmail.com
push dateFri, 19 Dec 2014 20:13:42 +0000
treeherderfx-team@98ee95ac6be5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice, ehsan
bugs1000305
milestone37.0a1
Bug 1000305 - Part 1 - Add a getIcon() method. r=fabrice r=ehsan
dom/apps/Webapps.js
dom/apps/Webapps.jsm
dom/webidl/Apps.webidl
--- a/dom/apps/Webapps.js
+++ b/dom/apps/Webapps.js
@@ -743,16 +743,17 @@ WebappsApplicationMgmt.prototype = {
   init: function(aWindow, aHasFullMgmtPrivilege) {
     this._window = aWindow;
 
     this.initDOMRequestHelper(aWindow, ["Webapps:Uninstall:Return:OK",
                                         "Webapps:Uninstall:Broadcast:Return:OK",
                                         "Webapps:Uninstall:Return:KO",
                                         "Webapps:Install:Return:OK",
                                         "Webapps:GetNotInstalled:Return:OK",
+                                        "Webapps:GetIcon:Return",
                                         "Webapps:Import:Return",
                                         "Webapps:ExtractManifest:Return",
                                         "Webapps:SetEnabled:Return"]);
     cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
                           {
                             messages: ["Webapps:Install:Return:OK",
                                        "Webapps:Uninstall:Return:OK",
                                        "Webapps:Uninstall:Broadcast:Return:OK",
@@ -808,16 +809,31 @@ WebappsApplicationMgmt.prototype = {
     DOMApplicationRegistry.getAll((aApps) => {
       Services.DOMRequest.fireSuccessAsync(request,
                                            convertAppsArray(aApps, window));
     });
 
     return request;
   },
 
+  getIcon: function(aApp, aIconID, aEntryPoint) {
+    return this.createPromise(function(aResolve, aReject) {
+      cpmm.sendAsyncMessage("Webapps:GetIcon", {
+        oid: this._id,
+        manifestURL: aApp.manifestURL,
+        iconID: aIconID,
+        entryPoint: aEntryPoint,
+        requestID: this.getPromiseResolverId({
+          resolve: aResolve,
+          reject: aReject
+        })
+      });
+    }.bind(this));
+  },
+
   getNotInstalled: function() {
     let request = this.createRequest();
     let principal = this._window.document.nodePrincipal;
 
     cpmm.sendAsyncMessage("Webapps:GetNotInstalled", {
       oid: this._id,
       requestID: this.getRequestId(request)
     }, null, principal);
@@ -882,17 +898,18 @@ WebappsApplicationMgmt.prototype = {
   set onenabledstatechange(aCallback) {
     this.__DOM_IMPL__.setEventHandler("onenabledstatechange", aCallback);
   },
 
   receiveMessage: function(aMessage) {
     let msg = aMessage.data;
     let req;
 
-    if (["Webapps:Import:Return",
+    if (["Webapps:GetIcon:Return",
+         "Webapps:Import:Return",
          "Webapps:ExtractManifest:Return"]
          .indexOf(aMessage.name) != -1) {
       req = this.takePromiseResolver(msg.requestID);
     } else {
       req = this.getRequest(msg.requestID);
     }
 
     // We want Webapps:Install:Return:OK, Webapps:Uninstall:Broadcast:Return:OK
@@ -948,17 +965,28 @@ WebappsApplicationMgmt.prototype = {
       case "Webapps:SetEnabled:Return":
         {
           let app = createContentApplicationObject(this._window, msg);
           let event =
             new this._window.MozApplicationEvent("enabledstatechange", { application : app });
           this.__DOM_IMPL__.dispatchEvent(event);
         }
         break;
+      case "Webapps:GetIcon:Return":
+        if (msg.blob) {
+          req.resolve(Cu.cloneInto(msg.blob, this._window));
+        } else if (msg.error && msg.error == "NETWORK_ERROR"
+                             && !this._window.navigator.onLine) {
+          req.reject(new this._window.DOMError("NETWORK_OFFLINE"));
+        } else {
+          req.reject(new this._window.DOMError(msg.error || ""));
+        }
+        break;
     }
+
     if (aMessage.name !== "Webapps:Uninstall:Broadcast:Return:OK") {
       this.removeRequest(msg.requestID);
     }
   },
 
   classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -216,16 +216,17 @@ this.DOMApplicationRegistry = {
                      "Webapps:ApplyDownload",
                      "Webapps:Install:Return:Ack",
                      "Webapps:AddReceipt",
                      "Webapps:RemoveReceipt",
                      "Webapps:ReplaceReceipt",
                      "Webapps:RegisterBEP",
                      "Webapps:Export",
                      "Webapps:Import",
+                     "Webapps:GetIcon",
                      "Webapps:ExtractManifest",
                      "Webapps:SetEnabled",
                      "child-process-shutdown"];
 
     this.frameMessages = ["Webapps:ClearBrowserData"];
 
     this.messages.forEach((function(msgName) {
       ppmm.addMessageListener(msgName, this);
@@ -1400,16 +1401,19 @@ this.DOMApplicationRegistry = {
           this.removeReceipt(msg, mm);
           break;
         case "Webapps:ReplaceReceipt":
           this.replaceReceipt(msg, mm);
           break;
         case "Webapps:RegisterBEP":
           this.registerBrowserElementParentForApp(msg, mm);
           break;
+        case "Webapps:GetIcon":
+          this.getIcon(msg, mm);
+          break;
         case "Webapps:Export":
           this.doExport(msg, mm);
           break;
         case "Webapps:Import":
           this.doImport(msg, mm);
           break;
         case "Webapps:ExtractManifest":
           this.doExtractManifest(msg, mm);
@@ -4208,16 +4212,84 @@ this.DOMApplicationRegistry = {
 
     this._readManifests(tmp).then((aResult) => {
       for (let i = 0; i < aResult.length; i++)
         aData.apps[i].manifest = aResult[i].manifest;
       aMm.sendAsyncMessage("Webapps:GetNotInstalled:Return:OK", aData);
     });
   },
 
+  getIcon: function(aData, aMm) {
+    function sendError(aError) {
+      debug("getIcon error: " + aError);
+      aData.error = aError;
+      aMm.sendAsyncMessage("Webapps:GetIcon:Return", aData);
+    }
+
+    let app = this.getAppByManifestURL(aData.manifestURL);
+    if (!app) {
+      sendError("NO_APP");
+      return;
+    }
+
+    function loadIcon(aUrl) {
+      let fallbackMimeType = aUrl.indexOf('.') >= 0 ?
+                             "image/" + aUrl.split(".").reverse()[0] : "";
+      // Set up an xhr to download a blob.
+      let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+                  .createInstance(Ci.nsIXMLHttpRequest);
+      xhr.mozBackgroundRequest = true;
+      xhr.open("GET", aUrl, true);
+      xhr.responseType = "blob";
+      xhr.addEventListener("load", function() {
+        debug("Got http status=" + xhr.status + " for " + aUrl);
+        if (xhr.status == 200) {
+          let blob = xhr.response;
+          // Reusing aData with sendAsyncMessage() leads to an empty blob in
+          // the child.
+          let payload = {
+            "oid": aData.oid,
+            "requestID": aData.requestID,
+            "blob": blob,
+            "type": xhr.getResponseHeader("Content-Type") || fallbackMimeType
+          };
+          aMm.sendAsyncMessage("Webapps:GetIcon:Return", payload);
+        } else if (xhr.status === 0) {
+          sendError("NETWORK_ERROR");
+        } else {
+          sendError("FETCH_ICON_FAILED");
+        }
+      });
+      xhr.addEventListener("error", function() {
+        sendError("FETCH_ICON_FAILED");
+      });
+      xhr.send();
+    }
+
+    // Get the manifest, to find the icon url in the current locale.
+    this.getManifestFor(aData.manifestURL, aData.entryPoint)
+        .then((aManifest) => {
+      if (!aManifest) {
+        sendError("FETCH_ICON_FAILED");
+        return;
+      }
+
+      let manifest = new ManifestHelper(aManifest, app.origin, app.manifestURL);
+      let url = manifest.iconURLForSize(aData.iconID);
+      if (!url) {
+        sendError("NO_ICON");
+        return;
+      }
+      loadIcon(url);
+    }).catch(() => {
+      sendError("FETCH_ICON_FAILED");
+      return;
+    });
+  },
+
   getAll: function(aCallback) {
     debug("getAll");
     let apps = [];
     let tmp = [];
 
     for (let id in this.webapps) {
       let app = AppsUtils.cloneAppObject(this.webapps[id]);
       if (!this._isLaunchable(app))
@@ -4441,25 +4513,29 @@ this.DOMApplicationRegistry = {
 
     // Update customization.
     this.getManifestFor(app.manifestURL).then((aManifest) => {
       app.enabled ? UserCustomizations.register(aManifest, app)
                   : UserCustomizations.unregister(aManifest, app);
     });
   },
 
-  getManifestFor: function(aManifestURL) {
+  getManifestFor: function(aManifestURL, aEntryPoint) {
     let id = this._appIdForManifestURL(aManifestURL);
     let app = this.webapps[id];
     if (!id || (app.installState == "pending" && !app.retryingDownload)) {
       return Promise.resolve(null);
     }
 
     return this._readManifests([{ id: id }]).then((aResult) => {
-      return aResult[0].manifest;
+      if (aEntryPoint) {
+        return aResult[0].manifest.entry_points[aEntryPoint];
+      } else {
+        return aResult[0].manifest;
+      }
     });
   },
 
   getAppByManifestURL: function(aManifestURL) {
     return AppsUtils.getAppByManifestURL(this.webapps, aManifestURL);
   },
 
   _getAppWithManifest: Task.async(function*(aManifestURL) {
--- a/dom/webidl/Apps.webidl
+++ b/dom/webidl/Apps.webidl
@@ -93,13 +93,15 @@ interface DOMApplicationsManager : Event
   DOMRequest getNotInstalled();
   void applyDownload(DOMApplication app);
   DOMRequest uninstall(DOMApplication app);
 
   Promise<DOMApplication> import(Blob blob);
   Promise<any> extractManifest(Blob blob);
 
   void setEnabled(DOMApplication app, boolean state);
+  Promise<Blob> getIcon(DOMApplication app, DOMString iconID,
+                        optional DOMString entryPoint);
 
   attribute EventHandler oninstall;
   attribute EventHandler onuninstall;
   attribute EventHandler onenabledstatechange;
 };