mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 14:15:30 +00:00
1e2b436914
PopupNotifications are great at noticing when the top outer frame changes location, and clears any pending notifications accordingly. It is, however, not so great at noticing when subframes that have caused notifications change location. Now, when prompting for install / uninstall permissions, we add an nsIWebProgressListener to the gBrowser that will remove the notification if we hear that the requesting content has browsed away. --HG-- extra : rebase_source : 330bcb80b5dd0ca7b2044bfa9918dc08d6403472
340 lines
11 KiB
JavaScript
340 lines
11 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* 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/. */
|
|
|
|
this.EXPORTED_SYMBOLS = ["WebappManager"];
|
|
|
|
let Ci = Components.interfaces;
|
|
let Cc = Components.classes;
|
|
let Cu = Components.utils;
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://gre/modules/Webapps.jsm");
|
|
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
|
Cu.import("resource://gre/modules/Task.jsm");
|
|
Cu.import("resource://gre/modules/Promise.jsm");
|
|
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "NativeApp",
|
|
"resource://gre/modules/NativeApp.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils",
|
|
"resource://gre/modules/WebappOSUtils.jsm");
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
|
"@mozilla.org/childprocessmessagemanager;1",
|
|
"nsIMessageSender");
|
|
|
|
this.WebappManager = {
|
|
// List of promises for in-progress installations
|
|
installations: {},
|
|
|
|
init: function() {
|
|
Services.obs.addObserver(this, "webapps-ask-install", false);
|
|
Services.obs.addObserver(this, "webapps-ask-uninstall", false);
|
|
Services.obs.addObserver(this, "webapps-launch", false);
|
|
Services.obs.addObserver(this, "webapps-uninstall", false);
|
|
cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
|
|
{ messages: ["Webapps:Install:Return:OK",
|
|
"Webapps:Install:Return:KO",
|
|
"Webapps:UpdateState"]});
|
|
cpmm.addMessageListener("Webapps:Install:Return:OK", this);
|
|
cpmm.addMessageListener("Webapps:Install:Return:KO", this);
|
|
cpmm.addMessageListener("Webapps:UpdateState", this);
|
|
},
|
|
|
|
uninit: function() {
|
|
Services.obs.removeObserver(this, "webapps-ask-install");
|
|
Services.obs.removeObserver(this, "webapps-ask-uninstall");
|
|
Services.obs.removeObserver(this, "webapps-launch");
|
|
Services.obs.removeObserver(this, "webapps-uninstall");
|
|
cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
|
|
["Webapps:Install:Return:OK",
|
|
"Webapps:Install:Return:KO",
|
|
"Webapps:UpdateState"]);
|
|
cpmm.removeMessageListener("Webapps:Install:Return:OK", this);
|
|
cpmm.removeMessageListener("Webapps:Install:Return:KO", this);
|
|
cpmm.removeMessageListener("Webapps:UpdateState", this);
|
|
},
|
|
|
|
receiveMessage: function(aMessage) {
|
|
let data = aMessage.data;
|
|
|
|
let manifestURL = data.manifestURL ||
|
|
(data.app && data.app.manifestURL) ||
|
|
data.manifest;
|
|
|
|
if (!this.installations[manifestURL]) {
|
|
return;
|
|
}
|
|
|
|
if (aMessage.name == "Webapps:UpdateState") {
|
|
if (data.error) {
|
|
this.installations[manifestURL].reject(data.error);
|
|
} else if (data.app.installState == "installed") {
|
|
this.installations[manifestURL].resolve();
|
|
}
|
|
} else if (aMessage.name == "Webapps:Install:Return:OK" &&
|
|
!data.isPackage) {
|
|
let manifest = new ManifestHelper(data.app.manifest,
|
|
data.app.origin,
|
|
data.app.manifestURL);
|
|
if (!manifest.appcache_path) {
|
|
this.installations[manifestURL].resolve();
|
|
}
|
|
} else if (aMessage.name == "Webapps:Install:Return:KO") {
|
|
this.installations[manifestURL].reject(data.error);
|
|
}
|
|
},
|
|
|
|
observe: function(aSubject, aTopic, aData) {
|
|
let data = JSON.parse(aData);
|
|
data.mm = aSubject;
|
|
|
|
let browser;
|
|
switch(aTopic) {
|
|
case "webapps-ask-install":
|
|
browser = this._getBrowserForId(data.topId);
|
|
if (browser) {
|
|
this.doInstall(data, browser);
|
|
}
|
|
break;
|
|
case "webapps-ask-uninstall":
|
|
browser = this._getBrowserForId(data.topId);
|
|
if (browser) {
|
|
this.doUninstall(data, browser);
|
|
}
|
|
break;
|
|
case "webapps-launch":
|
|
WebappOSUtils.launch(data);
|
|
break;
|
|
case "webapps-uninstall":
|
|
WebappOSUtils.uninstall(data);
|
|
break;
|
|
}
|
|
},
|
|
|
|
_getBrowserForId: function(aId) {
|
|
let windows = Services.wm.getEnumerator("navigator:browser");
|
|
while (windows.hasMoreElements()) {
|
|
let window = windows.getNext();
|
|
let tabbrowser = window.gBrowser;
|
|
let foundBrowser = tabbrowser.getBrowserForOuterWindowID(aId);
|
|
if (foundBrowser) {
|
|
return foundBrowser;
|
|
}
|
|
}
|
|
let foundWindow = Services.wm.getOuterWindowWithId(aId);
|
|
if (foundWindow) {
|
|
return foundWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShell)
|
|
.chromeEventHandler;
|
|
}
|
|
return null;
|
|
},
|
|
|
|
doInstall: function(aData, aBrowser) {
|
|
let chromeDoc = aBrowser.ownerDocument;
|
|
let chromeWin = chromeDoc.defaultView;
|
|
let popupProgressContent =
|
|
chromeDoc.getElementById("webapps-install-progress-content");
|
|
|
|
let bundle = chromeWin.gNavigatorBundle;
|
|
|
|
let jsonManifest = aData.isPackage ? aData.app.updateManifest : aData.app.manifest;
|
|
|
|
let notification;
|
|
|
|
let mainAction = {
|
|
label: bundle.getString("webapps.install"),
|
|
accessKey: bundle.getString("webapps.install.accesskey"),
|
|
callback: () => {
|
|
notification.remove();
|
|
|
|
notification = chromeWin.PopupNotifications.
|
|
show(aBrowser,
|
|
"webapps-install-progress",
|
|
bundle.getString("webapps.install.inprogress"),
|
|
"webapps-notification-icon");
|
|
|
|
let progressMeter = chromeDoc.createElement("progressmeter");
|
|
progressMeter.setAttribute("mode", "undetermined");
|
|
popupProgressContent.appendChild(progressMeter);
|
|
|
|
let manifestURL = aData.app.manifestURL;
|
|
|
|
let nativeApp = new NativeApp(aData.app, jsonManifest,
|
|
aData.app.categories);
|
|
|
|
this.installations[manifestURL] = Promise.defer();
|
|
this.installations[manifestURL].promise.then(() => {
|
|
notifyInstallSuccess(aData.app, nativeApp, bundle,
|
|
PrivateBrowsingUtils.isBrowserPrivate(aBrowser));
|
|
}, (error) => {
|
|
Cu.reportError("Error installing webapp: " + error);
|
|
}).then(() => {
|
|
popupProgressContent.removeChild(progressMeter);
|
|
delete this.installations[manifestURL];
|
|
if (Object.getOwnPropertyNames(this.installations).length == 0) {
|
|
notification.remove();
|
|
}
|
|
});
|
|
|
|
let localDir;
|
|
try {
|
|
localDir = nativeApp.createProfile();
|
|
} catch (ex) {
|
|
DOMApplicationRegistry.denyInstall(aData);
|
|
return;
|
|
}
|
|
|
|
DOMApplicationRegistry.confirmInstall(aData, localDir,
|
|
Task.async(function*(aApp, aManifest, aZipPath) {
|
|
try {
|
|
yield nativeApp.install(aApp, aManifest, aZipPath);
|
|
} catch (ex) {
|
|
Cu.reportError("Error installing webapp: " + ex);
|
|
throw ex;
|
|
}
|
|
})
|
|
);
|
|
}
|
|
};
|
|
|
|
let requestingURI = chromeWin.makeURI(aData.from);
|
|
let app = aData.app;
|
|
let manifest = new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
|
|
|
|
let host;
|
|
try {
|
|
host = requestingURI.host;
|
|
} catch(e) {
|
|
host = requestingURI.spec;
|
|
}
|
|
|
|
let message = bundle.getFormattedString("webapps.requestInstall",
|
|
[manifest.name, host], 2);
|
|
|
|
let eventCallback = null;
|
|
let gBrowser = chromeWin.gBrowser;
|
|
if (gBrowser) {
|
|
let windowID = aData.oid;
|
|
|
|
let listener = {
|
|
onLocationChange(webProgress) {
|
|
if (webProgress.DOMWindowID == windowID) {
|
|
notification.remove();
|
|
}
|
|
}
|
|
};
|
|
|
|
gBrowser.addProgressListener(listener);
|
|
|
|
eventCallback = (event) => {
|
|
if (event != "removed") {
|
|
return;
|
|
}
|
|
// The notification was removed, so we should
|
|
// remove our listener.
|
|
gBrowser.removeProgressListener(listener);
|
|
}
|
|
}
|
|
|
|
notification = chromeWin.PopupNotifications.show(aBrowser,
|
|
"webapps-install",
|
|
message,
|
|
"webapps-notification-icon",
|
|
mainAction, [],
|
|
eventCallback);
|
|
},
|
|
|
|
doUninstall: function(aData, aBrowser) {
|
|
let chromeDoc = aBrowser.ownerDocument;
|
|
let chromeWin = chromeDoc.defaultView;
|
|
|
|
let bundle = chromeWin.gNavigatorBundle;
|
|
let jsonManifest = aData.app.manifest;
|
|
|
|
let notification;
|
|
|
|
let mainAction = {
|
|
label: bundle.getString("webapps.uninstall"),
|
|
accessKey: bundle.getString("webapps.uninstall.accesskey"),
|
|
callback: () => {
|
|
notification.remove();
|
|
DOMApplicationRegistry.confirmUninstall(aData);
|
|
}
|
|
};
|
|
|
|
let secondaryAction = {
|
|
label: bundle.getString("webapps.doNotUninstall"),
|
|
accessKey: bundle.getString("webapps.doNotUninstall.accesskey"),
|
|
callback: () => {
|
|
notification.remove();
|
|
DOMApplicationRegistry.denyUninstall(aData, "USER_DECLINED");
|
|
}
|
|
};
|
|
|
|
let manifest = new ManifestHelper(jsonManifest, aData.app.origin,
|
|
aData.app.manifestURL);
|
|
|
|
let message = bundle.getFormattedString("webapps.requestUninstall",
|
|
[manifest.name]);
|
|
|
|
|
|
let eventCallback = null;
|
|
let gBrowser = chromeWin.gBrowser;
|
|
if (gBrowser) {
|
|
let windowID = aData.oid;
|
|
|
|
let listener = {
|
|
onLocationChange(webProgress) {
|
|
if (webProgress.DOMWindowID == windowID) {
|
|
notification.remove();
|
|
}
|
|
}
|
|
};
|
|
|
|
gBrowser.addProgressListener(listener);
|
|
|
|
eventCallback = (event) => {
|
|
if (event != "removed") {
|
|
return;
|
|
}
|
|
// The notification was removed, so we should
|
|
// remove our listener.
|
|
gBrowser.removeProgressListener(listener);
|
|
}
|
|
}
|
|
|
|
notification = chromeWin.PopupNotifications.show(
|
|
aBrowser, "webapps-uninstall", message,
|
|
"webapps-notification-icon",
|
|
mainAction, [secondaryAction],
|
|
eventCallback);
|
|
}
|
|
}
|
|
|
|
function notifyInstallSuccess(aApp, aNativeApp, aBundle, aInPrivateBrowsing) {
|
|
let launcher = {
|
|
observe: function(aSubject, aTopic) {
|
|
if (aTopic == "alertclickcallback") {
|
|
WebappOSUtils.launch(aApp);
|
|
}
|
|
}
|
|
};
|
|
|
|
try {
|
|
let notifier = Cc["@mozilla.org/alerts-service;1"].
|
|
getService(Ci.nsIAlertsService);
|
|
|
|
notifier.showAlertNotification(aNativeApp.iconURI.spec,
|
|
aBundle.getString("webapps.install.success"),
|
|
aNativeApp.appNameAsFilename,
|
|
true, null, launcher, "", "", "", "", null,
|
|
aInPrivateBrowsing);
|
|
} catch (ex) {}
|
|
}
|