diff --git a/dom/apps/src/Webapps.js b/dom/apps/src/Webapps.js index 9f0284d2ed8..d8c4c8b0a5f 100644 --- a/dom/apps/src/Webapps.js +++ b/dom/apps/src/Webapps.js @@ -188,10 +188,8 @@ WebappsRegistry.prototype = { // mozIDOMApplicationRegistry2 implementation - installPackage: function(aURL, aParams) { - let installURL = this._window.location.href; - let installOrigin = this._getOrigin(installURL); - this._validateScheme(aURL); + installPackage: function(aPackageURL, aParams) { + this._validateScheme(aPackageURL); let request = this.createRequest(); let requestID = this.getRequestId(request); @@ -200,54 +198,13 @@ WebappsRegistry.prototype = { Array.isArray(aParams.receipts)) ? aParams.receipts : []; let categories = (aParams && aParams.categories && Array.isArray(aParams.categories)) ? aParams.categories : []; - let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); - xhr.open("GET", aURL, true); - xhr.channel.loadFlags |= Ci.nsIRequest.VALIDATE_ALWAYS; - - xhr.addEventListener("load", (function() { - if (xhr.status == 200) { - let manifest; - try { - manifest = JSON.parse(xhr.responseText, installOrigin); - } catch(e) { - Services.DOMRequest.fireError(request, "MANIFEST_PARSE_ERROR"); - return; - } - if (!(AppsUtils.checkManifest(manifest, installOrigin) && - manifest.package_path)) { - Services.DOMRequest.fireError(request, "INVALID_MANIFEST"); - } else { - if (!this.checkAppStatus(manifest)) { - Services.DOMRequest.fireError(request, "INVALID_SECURITY_LEVEL"); - } else { - let receipts = (aParams && aParams.receipts && Array.isArray(aParams.receipts)) ? aParams.receipts : []; - let categories = (aParams && aParams.categories && Array.isArray(aParams.categories)) ? aParams.categories : []; - let etag = xhr.getResponseHeader("Etag"); - cpmm.sendAsyncMessage("Webapps:InstallPackage", { app: { - installOrigin: installOrigin, - origin: this._getOrigin(aURL), - manifestURL: aURL, - updateManifest: manifest, - etag: etag, - receipts: receipts, - categories: categories }, - from: installURL, - oid: this._id, - requestID: requestID, - isPackage: true }); - } - } - } - else { - Services.DOMRequest.fireError(request, "MANIFEST_URL_ERROR"); - } - }).bind(this), false); - - xhr.addEventListener("error", (function() { - Services.DOMRequest.fireError(request, "NETWORK_ERROR"); - }).bind(this), false); - - xhr.send(null); + cpmm.sendAsyncMessage("Webapps:InstallPackage", { url: aPackageURL, + receipts: receipts, + categories: categories, + requestID: requestID, + oid: this._id, + from: this._window.location.href, + installOrigin: this._getOrigin(this._window.location.href) }); return request; }, @@ -381,11 +338,9 @@ WebappsApplication.prototype = { "Webapps:Uninstall:Return:KO", "Webapps:OfflineCache", "Webapps:CheckForUpdate:Return:OK", - "Webapps:CheckForUpdate:Return:KO", - "Webapps:PackageEvent"]); + "Webapps:CheckForUpdate:Return:KO"]); cpmm.sendAsyncMessage("Webapps:RegisterForMessages", - ["Webapps:Uninstall:Return:OK", "Webapps:OfflineCache", - "Webapps:PackageEvent"]); + ["Webapps:Uninstall:Return:OK", "Webapps:OfflineCache"]); }, set onprogress(aCallback) { @@ -479,8 +434,7 @@ WebappsApplication.prototype = { uninit: function() { this._onprogress = null; cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", - ["Webapps:Uninstall:Return:OK", "Webapps:OfflineCache", - "Webapps:PackageEvent"]); + ["Webapps:Uninstall:Return:OK", "Webapps:OfflineCache"]); }, _fireEvent: function(aName, aHandler) { @@ -491,11 +445,9 @@ WebappsApplication.prototype = { }, receiveMessage: function(aMessage) { - let msg = aMessage.json; + var msg = aMessage.json; let req = this.takeRequest(msg.requestID); - if ((msg.oid != this._id || !req) && - aMessage.name !== "Webapps:OfflineCache" && - aMessage.name !== "Webapps:PackageEvent") + if ((msg.oid != this._id || !req) && aMessage.name !== "Webapps:OfflineCache") return; switch (aMessage.name) { case "Webapps:Uninstall:Return:OK": @@ -539,43 +491,6 @@ WebappsApplication.prototype = { case "Webapps:CheckForUpdate:Return:KO": Services.DOMRequest.fireError(req, msg.error); break; - case "Webapps:PackageEvent": - if (msg.manifestURL != this.manifestURL) - return; - switch(msg.type) { - case "error": - this._downloadError = msg.error; - this._fireEvent("downloaderror", this._ondownloaderror); - break; - case "progress": - this.progress = msg.progress; - this._fireEvent("downloadprogress", this._onprogress); - break; - case "installed": - let app = msg.app; - this.progress = app.progress || 0; - this.downloadAvailable = app.downloadAvailable; - this.downloading = app.downloading; - this.readyToApplyDownload = app.readyToApplyDownload; - this.downloadSize = app.downloadSize || 0; - this.installState = app.installState; - this.manifest = app.manifest; - this._fireEvent("downloaded", this._ondownloaded); - this._fireEvent("downloadapplied", this._ondownloadapplied); - break; - case "canceled": - app = msg.app; - this.progress = app.progress || 0; - this.downloadAvailable = app.downloadAvailable; - this.downloading = app.downloading; - this.readyToApplyDownload = app.readyToApplyDownload; - this.downloadSize = app.downloadSize || 0; - this.installState = app.installState; - this._downloadError = msg.error; - this._fireEvent("downloaderror", this._ondownloaderror); - break; - } - break; } }, diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index d0e9aeb2eb6..88d0bc6b49c 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -57,7 +57,6 @@ let DOMApplicationRegistry = { webapps: { }, children: [ ], allAppsLaunchable: false, - downloads: { }, init: function() { this.messages = ["Webapps:Install", "Webapps:Uninstall", @@ -471,8 +470,7 @@ let DOMApplicationRegistry = { mm.sendAsyncMessage("Webapps:GetAll:Return:KO", msg); break; case "Webapps:InstallPackage": - // always ask for UI to install - Services.obs.notifyObservers(mm, "webapps-ask-install", JSON.stringify(msg)); + this.installPackage(msg, mm); break; case "Webapps:GetBasePath": return this.webapps[msg.id].basePath; @@ -568,26 +566,6 @@ let DOMApplicationRegistry = { cancelDownload: function cancelDowload(aManifestURL) { // We can't cancel appcache dowloads for now. - if (!this.downloads[aManifestURL]) { - return; - } - // This is a HTTP channel. - let download = this.downloads[aManifestURL] - download.channel.cancel(Cr.NS_BINDING_ABORTED); - let app = this.webapps[dowload.appId]; - - app.progress = 0; - app.installState = app.previousState; - app.dowloading = false; - app.dowloadavailable = false; - app.downloadSize = 0; - this._saveApps((function() { - this.broadcastMessage("Webapps:PackageEvent", - { type: "canceled", - manifestURL: aApp.manifestURL, - app: app, - error: "DOWNLOAD_CANCELED" }); - }).bind(this)); }, startOfflineCacheDownload: function startOfflineCacheDownload(aManifest, aApp, aProfileDir) { @@ -736,8 +714,7 @@ let DOMApplicationRegistry = { confirmInstall: function(aData, aFromSync, aProfileDir, aOfflineCacheObserver) { let app = aData.app; app.removable = true; - - let id = app.syncId || this._appIdForManifestURL(app.manifestURL); + let id = app.syncId || this._appId(app.origin); let localId = this.getAppLocalIdByManifestURL(app.manifestURL); // Installing an application again is considered as an update. @@ -752,14 +729,9 @@ let DOMApplicationRegistry = { localId = this._nextLocalId(); } - let manifestName = "manifest.webapp"; - if (aData.isPackage) { + if (app.packageId) { // Override the origin with the correct id. app.origin = "app://" + id; - - // For packaged apps, keep the update manifest distinct from the app - // manifest. - manifestName = "update.webapp"; } let appObject = AppsUtils.cloneAppObject(app); @@ -774,23 +746,30 @@ let DOMApplicationRegistry = { let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true); let manFile = dir.clone(); - manFile.append(manifestName); - let jsonManifest = aData.isPackage ? app.updateManifest : app.manifest; - this._writeFile(manFile, JSON.stringify(jsonManifest), function() { }); + manFile.append("manifest.webapp"); + this._writeFile(manFile, JSON.stringify(app.manifest), function() { + // If this a packaged app, move the zip file from the temp directory, + // and delete the temp directory. + if (app.packageId) { + let appFile = FileUtils.getFile("TmpD", ["webapps", app.packageId, "application.zip"], + true, true); + appFile.moveTo(dir, "application.zip"); + let tmpDir = FileUtils.getDir("TmpD", ["webapps", app.packageId], + true, true); + try { + tmpDir.remove(true); + } catch(e) { + } + } + }); - let manifest = new DOMApplicationManifest(jsonManifest, app.manifestURL); + let manifest = new DOMApplicationManifest(app.manifest, app.origin); if (manifest.appcache_path) { appObject.installState = "pending"; appObject.downloadAvailable = true; appObject.downloading = true; - appObject.downloadSize = 0; - appObject.readyToApplyDownload = false; - } else if (manifest.package_path) { - appObject.installState = "pending"; - appObject.downloadAvailable = true; - appObject.downloading = true; - appObject.downloadSize = manifest.size; + appObject.downloadsize = 0; appObject.readyToApplyDownload = false; } else { appObject.installState = "installed"; @@ -799,13 +778,9 @@ let DOMApplicationRegistry = { appObject.readyToApplyDownload = false; } - appObject.name = manifest.name; + appObject.name = app.manifest.name; this.webapps[id] = appObject; - ["installState", "downloadAvailable", - "downloading", "downloadSize", "readyToApplyDownload"].forEach(function(aProp) { - aData.app[aProp] = appObject[aProp]; - }); if (!aFromSync) this._saveApps((function() { @@ -815,16 +790,11 @@ let DOMApplicationRegistry = { }).bind(this)); #ifdef MOZ_SYS_MSG - if (!aData.isPackage) { - this._registerSystemMessages(app.manifest, app); - this._registerActivities(app.manifest, app); - } + this._registerSystemMessages(app.manifest, app); + this._registerActivities(app.manifest, app); #endif this.startOfflineCacheDownload(manifest, appObject, aProfileDir); - if (manifest.package_path) { - this.downloadPackage(manifest, appObject); - } }, _nextLocalId: function() { @@ -841,14 +811,6 @@ let DOMApplicationRegistry = { return null; }, - _appIdForManifestURL: function(aURI) { - for (let id in this.webapps) { - if (this.webapps[id].manifestURL == aURI) - return id; - } - return null; - }, - makeAppId: function() { let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); return uuidGenerator.generateUUID().toString(); @@ -876,9 +838,6 @@ let DOMApplicationRegistry = { // the manifest file used to be named manifest.json, so fallback on this. let baseDir = (this.webapps[id].removable ? DIRECTORY_NAME : "coreAppsDir"); let file = FileUtils.getFile(baseDir, ["webapps", id, "manifest.webapp"], true); - if (!file.exists()) { - file = FileUtils.getFile(baseDir, ["webapps", id, "update.webapp"], true); - } if (!file.exists()) { file = FileUtils.getFile(baseDir, ["webapps", id, "manifest.json"], true); } @@ -892,7 +851,7 @@ let DOMApplicationRegistry = { }).bind(this)); }, - downloadPackage: function(aManifest, aApp) { + installPackage: function(aData, aMm) { // Here are the steps when installing a package: // - create a temp directory where to store the app. // - download the zip in this directory. @@ -901,22 +860,31 @@ let DOMApplicationRegistry = { // - add the new app to the registry. // If we fail at any step, we backout the previous ones and return an error. - debug(JSON.stringify(aApp)); + let id; + let manifestURL = "jar:" + aData.url + "!manifest.webapp"; + // Check if we reinstall a known application. + for (let appId in this.webapps) { + if (this.webapps[appId].manifestURL == manifestURL) { + id = appId; + } + } - let id = this._appIdForManifestURL(aApp.manifestURL); - let app = this.webapps[id]; + // New application. + if (!id) { + id = this.makeAppId(); + } + + let dir = FileUtils.getDir("TmpD", ["webapps", id], true, true); // Removes the directory we created, and sends an error to the DOM side. function cleanup(aError) { - debug("Cleanup: " + aError); - let dir = FileUtils.getDir("TmpD", ["webapps", id], true, true); try { dir.remove(true); } catch (e) { } - this.broadcastMessage("Webapps:PackageEvent", - { type: "error", - manifestURL: aApp.manifestURL, - error: aError}); + aMm.sendAsyncMessage("Webapps:Install:Return:KO", + { oid: aData.oid, + requestID: aData.requestID, + error: aError }); } function getInferedStatus() { @@ -960,40 +928,7 @@ let DOMApplicationRegistry = { return (getAppManifestStatus(aManifest) <= getInferedStatus()); } - debug("About to download " + aManifest.fullPackagePath()); - - let requestChannel = NetUtil.newChannel(aManifest.fullPackagePath()) - .QueryInterface(Ci.nsIHttpChannel); - this.downloads[aApp.manifestURL] = - { channel:requestChannel, - appId: id, - previousState: "pending" - }; - requestChannel.notificationCallbacks = { - QueryInterface: function notifQI(aIID) { - if (aIID.equals(Ci.nsISupports) || - aIID.equals(Ci.nsIProgressEventSink)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - getInterface: function notifGI(aIID) { - return this.QueryInterface(aIID); - }, - onProgress: function notifProgress(aRequest, aContext, - aProgress, aProgressMax) { - debug("onProgress: " + aProgress + "/" + aProgressMax); - app.progress = aProgress; - DOMApplicationRegistry.broadcastMessage("Webapps:PackageEvent", - { type: "progress", - manifestURL: aApp.manifestURL, - progress: aProgress }); - }, - onStatus: function notifStatus(aRequest, aContext, aStatus, aStatusArg) { } - } - - NetUtil.asyncFetch(requestChannel, - function(aInput, aResult, aRequest) { + NetUtil.asyncFetch(aData.url, function(aInput, aResult, aRequest) { if (!Components.isSuccessCode(aResult)) { // We failed to fetch the zip. cleanup("NETWORK_ERROR"); @@ -1009,7 +944,23 @@ let DOMApplicationRegistry = { cleanup("DOWNLOAD_ERROR"); return; } - + // Build a data structure to call the webapps confirmation dialog : + // - load the manifest from the zip + // - set data.app.(origin, install_origin, manifestURL, manifest, receipts, categories) + // - call notifyObservers(this, "webapps-ask-install", JSON.stringify(msg)); + let msg = { + from: aData.from, + oid: aData.oid, + requestID: aData.requestID, + app: { + packageId: id, + installOrigin: aData.installOrigin, + origin: "app://" + id, + manifestURL: manifestURL, + receipts: aData.receipts, + categories: aData.categories + } + } let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"] .createInstance(Ci.nsIZipReader); try { @@ -1019,50 +970,19 @@ let DOMApplicationRegistry = { } let istream = zipReader.getInputStream("manifest.webapp"); - - // Obtain a converter to read from a UTF-8 encoded input stream. - let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Ci.nsIScriptableUnicodeConverter); - converter.charset = "UTF-8"; - - let manifest = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(istream, - istream.available()) || "")); - - if (!AppsUtils.checkManifest(manifest, aApp.installOrigin)) { + msg.app.manifest = JSON.parse(NetUtil.readInputStreamToString(istream, + istream.available()) || ""); + if (!AppsUtils.checkManifest(msg.app.manifest, aData.installOrigin)) { throw "INVALID_MANIFEST"; } - if (!checkAppStatus(manifest)) { + if (!checkAppStatus(msg.app.manifest)) { throw "INVALID_SECURITY_LEVEL"; } - // Success! Move the zip out of TmpD. - let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true); - zipFile.moveTo(dir, "application.zip"); - let tmpDir = FileUtils.getDir("TmpD", ["webapps", id], true, true); - try { - tmpDir.remove(true); - } catch(e) { } - - // Save the manifest - let manFile = dir.clone(); - manFile.append("manifest.webapp"); - DOMApplicationRegistry._writeFile(manFile, - JSON.stringify(manifest), - function() { }); - // Set state and fire events. - app.installState = "installed"; - app.dowloading = false; - app.dowloadavailable = false; - DOMApplicationRegistry._saveApps(function() { - debug("About to fire Webapps:PackageEvent"); - DOMApplicationRegistry.broadcastMessage("Webapps:PackageEvent", - { type: "installed", - manifestURL: aApp.manifestURL, - app: app, - manifest: manifest }); - delete DOMApplicationRegistry.downloads[aApp.manifestURL] - }); + msg.app.appStatus = getAppStatus(msg.app.manifest); + Services.obs.notifyObservers(this, "webapps-ask-install", + JSON.stringify(msg)); } catch (e) { // XXX we may need new error messages. cleanup(e); @@ -1222,7 +1142,7 @@ let DOMApplicationRegistry = { return; let id = this._appId(aOrigin); - if (!id || this.webapps[id].installState == "pending") { + if (!id) { aCallback(null); return; } @@ -1575,14 +1495,6 @@ DOMApplicationManifest.prototype = { return this._localeProp("orientation"); }, - get package_path() { - return this._localeProp("package_path"); - }, - - get size() { - return this._manifest["size"] || 0; - }, - iconURLForSize: function(aSize) { let icons = this._localeProp("icons"); if (!icons) @@ -1626,11 +1538,6 @@ DOMApplicationManifest.prototype = { fullAppcachePath: function() { let appcachePath = this._localeProp("appcache_path"); return this._origin.resolve(appcachePath ? appcachePath : ""); - }, - - fullPackagePath: function() { - let packagePath = this._localeProp("package_path"); - return this._origin.resolve(packagePath ? packagePath : ""); } };