Bug 1013433 - complete the packaged app update process; r=mfinkle,marco

This commit is contained in:
Myk Melez 2014-06-06 12:10:59 -07:00
Родитель aa5cdadcf0
Коммит db078bd435
2 изменённых файлов: 156 добавлений и 113 удалений

Просмотреть файл

@ -1333,12 +1333,12 @@ this.DOMApplicationRegistry = {
let app = this.webapps[id];
if (!app) {
debug("startDownload: No app found for " + aManifestURL);
return;
throw new Error("NO_SUCH_APP");
}
if (app.downloading) {
debug("app is already downloading. Ignoring.");
return;
throw new Error("APP_IS_DOWNLOADING");
}
// If the caller is trying to start a download but we have nothing to
@ -1352,7 +1352,7 @@ this.DOMApplicationRegistry = {
eventType: "downloaderror",
manifestURL: app.manifestURL
});
return;
throw new Error("NO_DOWNLOAD_AVAILABLE");
}
// First of all, we check if the download is supposed to update an
@ -1409,7 +1409,7 @@ this.DOMApplicationRegistry = {
if (!json) {
debug("startDownload: No update manifest found at " + file.path + " " +
aManifestURL);
return;
throw new Error("MISSING_UPDATE_MANIFEST");
}
let manifest = new ManifestHelper(json, app.manifestURL);
@ -1450,104 +1450,103 @@ this.DOMApplicationRegistry = {
}
}),
applyDownload: function applyDownload(aManifestURL) {
applyDownload: Task.async(function*(aManifestURL) {
debug("applyDownload for " + aManifestURL);
let id = this._appIdForManifestURL(aManifestURL);
let app = this.webapps[id];
if (!app || (app && !app.readyToApplyDownload)) {
return;
if (!app) {
throw new Error("NO_SUCH_APP");
}
if (!app.readyToApplyDownload) {
throw new Error("NOT_READY_TO_APPLY_DOWNLOAD");
}
// We need to get the old manifest to unregister web activities.
this.getManifestFor(aManifestURL).then((aOldManifest) => {
// Move the application.zip and manifest.webapp files out of TmpD
let tmpDir = FileUtils.getDir("TmpD", ["webapps", id], true, true);
let manFile = tmpDir.clone();
manFile.append("manifest.webapp");
let appFile = tmpDir.clone();
appFile.append("application.zip");
let oldManifest = yield this.getManifestFor(aManifestURL);
// Move the application.zip and manifest.webapp files out of TmpD
let tmpDir = FileUtils.getDir("TmpD", ["webapps", id], true, true);
let manFile = tmpDir.clone();
manFile.append("manifest.webapp");
let appFile = tmpDir.clone();
appFile.append("application.zip");
let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
appFile.moveTo(dir, "application.zip");
manFile.moveTo(dir, "manifest.webapp");
let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
appFile.moveTo(dir, "application.zip");
manFile.moveTo(dir, "manifest.webapp");
// Move the staged update manifest to a non staged one.
let staged = dir.clone();
staged.append("staged-update.webapp");
// Move the staged update manifest to a non staged one.
let staged = dir.clone();
staged.append("staged-update.webapp");
// If we are applying after a restarted download, we have no
// staged update manifest.
if (staged.exists()) {
staged.moveTo(dir, "update.webapp");
// If we are applying after a restarted download, we have no
// staged update manifest.
if (staged.exists()) {
staged.moveTo(dir, "update.webapp");
}
try {
tmpDir.remove(true);
} catch(e) { }
// Clean up the deprecated manifest cache if needed.
if (id in this._manifestCache) {
delete this._manifestCache[id];
}
// Flush the zip reader cache to make sure we use the new application.zip
// when re-launching the application.
let zipFile = dir.clone();
zipFile.append("application.zip");
Services.obs.notifyObservers(zipFile, "flush-cache-entry", null);
// Get the manifest, and set properties.
let newManifest = yield this.getManifestFor(aManifestURL);
app.downloading = false;
app.downloadAvailable = false;
app.downloadSize = 0;
app.installState = "installed";
app.readyToApplyDownload = false;
// Update the staged properties.
if (app.staged) {
for (let prop in app.staged) {
app[prop] = app.staged[prop];
}
delete app.staged;
}
try {
tmpDir.remove(true);
} catch(e) { }
delete app.retryingDownload;
// Clean up the deprecated manifest cache if needed.
if (id in this._manifestCache) {
delete this._manifestCache[id];
}
// Update the asm.js scripts we need to compile.
yield ScriptPreloader.preload(app, newManifest);
yield this._saveApps();
// Update the handlers and permissions for this app.
this.updateAppHandlers(oldManifest, newManifest, app);
// Flush the zip reader cache to make sure we use the new application.zip
// when re-launching the application.
let zipFile = dir.clone();
zipFile.append("application.zip");
Services.obs.notifyObservers(zipFile, "flush-cache-entry", null);
let updateManifest = yield AppsUtils.loadJSONAsync(staged.path);
let appObject = AppsUtils.cloneAppObject(app);
appObject.updateManifest = updateManifest;
this.notifyUpdateHandlers(appObject, newManifest, appFile.path);
// Get the manifest, and set properties.
this.getManifestFor(aManifestURL).then((aData) => {
app.downloading = false;
app.downloadAvailable = false;
app.downloadSize = 0;
app.installState = "installed";
app.readyToApplyDownload = false;
// Update the staged properties.
if (app.staged) {
for (let prop in app.staged) {
app[prop] = app.staged[prop];
}
delete app.staged;
}
delete app.retryingDownload;
// Update the asm.js scripts we need to compile.
ScriptPreloader.preload(app, aData)
.then(() => this._saveApps()).then(() => {
// Update the handlers and permissions for this app.
this.updateAppHandlers(aOldManifest, aData, app);
AppsUtils.loadJSONAsync(staged.path).then((aUpdateManifest) => {
let appObject = AppsUtils.cloneAppObject(app);
appObject.updateManifest = aUpdateManifest;
this.notifyUpdateHandlers(appObject, aData, appFile.path);
});
if (supportUseCurrentProfile()) {
PermissionsInstaller.installPermissions(
{ manifest: aData,
origin: app.origin,
manifestURL: app.manifestURL },
true);
}
this.updateDataStore(this.webapps[id].localId, app.origin,
app.manifestURL, aData, app.appStatus);
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: aData,
manifestURL: app.manifestURL
});
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadapplied",
manifestURL: app.manifestURL
});
});
});
if (supportUseCurrentProfile()) {
PermissionsInstaller.installPermissions(
{ manifest: newManifest,
origin: app.origin,
manifestURL: app.manifestURL },
true);
}
this.updateDataStore(this.webapps[id].localId, app.origin,
app.manifestURL, newManifest, app.appStatus);
this.broadcastMessage("Webapps:UpdateState", {
app: app,
manifest: newManifest,
manifestURL: app.manifestURL
});
},
this.broadcastMessage("Webapps:FireEvent", {
eventType: "downloadapplied",
manifestURL: app.manifestURL
});
}),
startOfflineCacheDownload: function(aManifest, aApp, aProfileDir, aIsUpdate) {
if (!aManifest.appcache_path) {
@ -2773,9 +2772,11 @@ this.DOMApplicationRegistry = {
if (oldPackage) {
debug("package's etag or hash unchanged; sending 'applied' event");
// The package's Etag or hash has not changed.
// We send a "applied" event right away.
// We send an "applied" event right away so code awaiting that event
// can proceed to access the app. We also throw an error to alert
// the caller that the package wasn't downloaded.
this._sendAppliedEvent(aNewApp, oldApp, id);
return;
throw new Error("PACKAGE_UNCHANGED");
}
let newManifest = yield this._openAndReadPackage(zipFile, oldApp, aNewApp,
@ -3488,6 +3489,8 @@ this.DOMApplicationRegistry = {
});
});
AppDownloadManager.remove(aNewApp.manifestURL);
throw aError;
},
doUninstall: function(aData, aMm) {

Просмотреть файл

@ -158,37 +158,46 @@ this.WebappManager = {
return deferred.promise;
},
askInstall: function(aData) {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(aData.profilePath);
_deleteAppcachePath: function(aManifest) {
// We don't yet support pre-installing an appcache because it isn't clear
// how to do it without degrading the user experience (since users expect
// apps to be available after the system tells them they've been installed,
// which has already happened) and because nsCacheService shuts down
// when we trigger the native install dialog and doesn't re-init itself
// afterward (TODO: file bug about this behavior).
if ("appcache_path" in aData.app.manifest) {
debug("deleting appcache_path from manifest: " + aData.app.manifest.appcache_path);
delete aData.app.manifest.appcache_path;
if ("appcache_path" in aManifest) {
debug("deleting appcache_path from manifest: " + aManifest.appcache_path);
delete aManifest.appcache_path;
}
},
askInstall: function(aData) {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(aData.profilePath);
this._deleteAppcachePath(aData.app.manifest);
DOMApplicationRegistry.registryReady.then(() => {
DOMApplicationRegistry.confirmInstall(aData, file, (function(aManifest) {
let localeManifest = new ManifestHelper(aManifest, aData.app.origin);
// aData.app.origin may now point to the app: url that hosts this app.
sendMessageToJava({
type: "Webapps:Postinstall",
apkPackageName: aData.app.apkPackageName,
origin: aData.app.origin,
});
this.writeDefaultPrefs(file, localeManifest);
this._postInstall(aData.profilePath, aManifest, aData.app.origin, aData.app.apkPackageName);
}).bind(this));
});
},
_postInstall: function(aProfilePath, aNewManifest, aOrigin, aApkPackageName) {
// aOrigin may now point to the app: url that hosts this app.
sendMessageToJava({
type: "Webapps:Postinstall",
apkPackageName: aApkPackageName,
origin: aOrigin,
});
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(aProfilePath);
let localeManifest = new ManifestHelper(aNewManifest, aOrigin);
this.writeDefaultPrefs(file, localeManifest);
},
launch: function({ manifestURL, origin }) {
debug("launchWebapp: " + manifestURL);
@ -211,11 +220,16 @@ this.WebappManager = {
},
autoInstall: function(aData) {
let oldApp = DOMApplicationRegistry.getAppByManifestURL(aData.manifestURL);
if (oldApp) {
// If the app is already installed, update the existing installation.
this._autoUpdate(aData, oldApp);
return;
debug("autoInstall " + aData.manifestURL);
// If the app is already installed, update the existing installation.
// We should be able to use DOMApplicationRegistry.getAppByManifestURL,
// but it returns a mozIApplication, while _autoUpdate needs the original
// object from DOMApplicationRegistry.webapps in order to modify it.
for (let [ , app] in Iterator(DOMApplicationRegistry.webapps)) {
if (app.manifestURL == aData.manifestURL) {
return this._autoUpdate(aData, app);
}
}
let mm = {
@ -276,13 +290,39 @@ this.WebappManager = {
}
if (aData.type == "hosted") {
this._deleteAppcachePath(aData.manifest);
let oldManifest = yield DOMApplicationRegistry.getManifestFor(aData.manifestURL);
DOMApplicationRegistry.updateHostedApp(aData, aOldApp.id, aOldApp, oldManifest, aData.manifest);
yield DOMApplicationRegistry.updateHostedApp(aData, aOldApp.id, aOldApp, oldManifest, aData.manifest);
} else {
DOMApplicationRegistry.updatePackagedApp(aData, aOldApp.id, aOldApp, aData.manifest);
yield this._autoUpdatePackagedApp(aData, aOldApp);
}
this._postInstall(aData.profilePath, aData.manifest, aOldApp.origin, aOldApp.apkPackageName);
}).bind(this)); },
_autoUpdatePackagedApp: Task.async(function*(aData, aOldApp) {
debug("_autoUpdatePackagedApp: " + aData.manifestURL);
if (aData.updateManifest && aData.zipFilePath) {
aData.updateManifest.package_path = aData.zipFilePath;
}
// updatePackagedApp just prepares the update, after which we must
// download the package via the misnamed startDownload and then apply it
// via applyDownload.
yield DOMApplicationRegistry.updatePackagedApp(aData, aOldApp.id, aOldApp, aData.updateManifest);
try {
yield DOMApplicationRegistry.startDownload(aData.manifestURL);
} catch (ex if ex.message == "PACKAGE_UNCHANGED") {
debug("package unchanged");
// If the package is unchanged, then there's nothing more to do.
return;
}
yield DOMApplicationRegistry.applyDownload(aData.manifestURL);
}),
_checkingForUpdates: false,
checkForUpdates: function(userInitiated) { return Task.spawn((function*() {