diff --git a/dom/apps/Webapps.js b/dom/apps/Webapps.js index 0d3c6c361704..ee06bd30e74e 100644 --- a/dom/apps/Webapps.js +++ b/dom/apps/Webapps.js @@ -748,6 +748,7 @@ WebappsApplicationMgmt.prototype = { "Webapps:Uninstall:Return:KO", "Webapps:Install:Return:OK", "Webapps:GetNotInstalled:Return:OK", + "Webapps:GetIcon:Return", "Webapps:Import:Return", "Webapps:ExtractManifest:Return", "Webapps:SetEnabled:Return"]); @@ -813,6 +814,21 @@ WebappsApplicationMgmt.prototype = { 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; @@ -887,7 +903,8 @@ WebappsApplicationMgmt.prototype = { 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); @@ -953,7 +970,18 @@ WebappsApplicationMgmt.prototype = { 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); } diff --git a/dom/apps/Webapps.jsm b/dom/apps/Webapps.jsm index 432dfb3adb8d..c48fa1e0b22c 100755 --- a/dom/apps/Webapps.jsm +++ b/dom/apps/Webapps.jsm @@ -221,6 +221,7 @@ this.DOMApplicationRegistry = { "Webapps:RegisterBEP", "Webapps:Export", "Webapps:Import", + "Webapps:GetIcon", "Webapps:ExtractManifest", "Webapps:SetEnabled", "child-process-shutdown"]; @@ -1405,6 +1406,9 @@ this.DOMApplicationRegistry = { case "Webapps:RegisterBEP": this.registerBrowserElementParentForApp(msg, mm); break; + case "Webapps:GetIcon": + this.getIcon(msg, mm); + break; case "Webapps:Export": this.doExport(msg, mm); break; @@ -4213,6 +4217,74 @@ this.DOMApplicationRegistry = { }); }, + 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 = []; @@ -4446,7 +4518,7 @@ this.DOMApplicationRegistry = { }); }, - getManifestFor: function(aManifestURL) { + getManifestFor: function(aManifestURL, aEntryPoint) { let id = this._appIdForManifestURL(aManifestURL); let app = this.webapps[id]; if (!id || (app.installState == "pending" && !app.retryingDownload)) { @@ -4454,7 +4526,11 @@ this.DOMApplicationRegistry = { } 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; + } }); }, diff --git a/dom/webidl/Apps.webidl b/dom/webidl/Apps.webidl index 212ac8a415ec..b1180e262730 100644 --- a/dom/webidl/Apps.webidl +++ b/dom/webidl/Apps.webidl @@ -98,6 +98,8 @@ interface DOMApplicationsManager : EventTarget { Promise extractManifest(Blob blob); void setEnabled(DOMApplication app, boolean state); + Promise getIcon(DOMApplication app, DOMString iconID, + optional DOMString entryPoint); attribute EventHandler oninstall; attribute EventHandler onuninstall;