2012-07-14 12:09:39 +04:00
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const CC = Components.Constructor;
|
2012-07-16 22:22:16 +04:00
|
|
|
const Cu = Components.utils;
|
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
2013-08-02 04:00:22 +04:00
|
|
|
Cu.import("resource://gre/modules/FileUtils.jsm");
|
2012-07-14 12:09:39 +04:00
|
|
|
|
2012-10-31 20:13:28 +04:00
|
|
|
this.EXPORTED_SYMBOLS = ["WebappOSUtils"];
|
2012-07-14 12:09:39 +04:00
|
|
|
|
2013-09-13 16:32:47 +04:00
|
|
|
// Returns the MD5 hash of a string.
|
|
|
|
function computeHash(aString) {
|
|
|
|
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
|
|
|
createInstance(Ci.nsIScriptableUnicodeConverter);
|
|
|
|
converter.charset = "UTF-8";
|
|
|
|
let result = {};
|
|
|
|
// Data is an array of bytes.
|
|
|
|
let data = converter.convertToByteArray(aString, result);
|
|
|
|
|
|
|
|
let hasher = Cc["@mozilla.org/security/hash;1"].
|
|
|
|
createInstance(Ci.nsICryptoHash);
|
|
|
|
hasher.init(hasher.MD5);
|
|
|
|
hasher.update(data, data.length);
|
|
|
|
// We're passing false to get the binary hash and not base64.
|
|
|
|
let hash = hasher.finish(false);
|
|
|
|
|
|
|
|
function toHexString(charCode) {
|
|
|
|
return ("0" + charCode.toString(16)).slice(-2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the binary hash data to a hex string.
|
|
|
|
return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
|
|
|
|
}
|
|
|
|
|
2012-10-31 20:13:28 +04:00
|
|
|
this.WebappOSUtils = {
|
2013-08-02 04:00:22 +04:00
|
|
|
getUniqueName: function(aApp) {
|
2013-09-13 16:32:47 +04:00
|
|
|
return this.sanitizeStringForFilename(aApp.name).toLowerCase() + "-" +
|
|
|
|
computeHash(aApp.manifestURL);
|
2013-08-02 04:00:22 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the executable of the given app, identifying it by its unique name,
|
|
|
|
* which is in either the new format or the old format.
|
|
|
|
* On Mac OS X, it returns the identifier of the app.
|
|
|
|
*
|
|
|
|
* The new format ensures a readable and unique name for an app by combining
|
|
|
|
* its name with a hash of its manifest URL. The old format uses its origin,
|
|
|
|
* which is only unique until we support multiple apps per origin.
|
|
|
|
*/
|
|
|
|
getLaunchTarget: function(aApp) {
|
|
|
|
let uniqueName = this.getUniqueName(aApp);
|
|
|
|
|
2012-07-14 12:09:39 +04:00
|
|
|
#ifdef XP_WIN
|
|
|
|
let appRegKey;
|
|
|
|
try {
|
|
|
|
let open = CC("@mozilla.org/windows-registry-key;1",
|
|
|
|
"nsIWindowsRegKey", "open");
|
|
|
|
appRegKey = open(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
|
|
|
|
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" +
|
2013-08-02 04:00:22 +04:00
|
|
|
uniqueName, Ci.nsIWindowsRegKey.ACCESS_READ);
|
|
|
|
} catch (ex) {
|
|
|
|
try {
|
|
|
|
appRegKey = open(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
|
|
|
|
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" +
|
|
|
|
aApp.origin, Ci.nsIWindowsRegKey.ACCESS_READ);
|
|
|
|
} catch (ex) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let appFilename, installLocation;
|
|
|
|
try {
|
|
|
|
appFilename = appRegKey.readStringValue("AppFilename");
|
|
|
|
installLocation = appRegKey.readStringValue("InstallLocation");
|
|
|
|
} catch (ex) {
|
|
|
|
return null;
|
|
|
|
} finally {
|
|
|
|
appRegKey.close();
|
|
|
|
}
|
|
|
|
|
2013-08-15 00:20:39 +04:00
|
|
|
installLocation = installLocation.substring(1, installLocation.length - 1);
|
2013-08-02 04:00:22 +04:00
|
|
|
let initWithPath = CC("@mozilla.org/file/local;1",
|
|
|
|
"nsILocalFile", "initWithPath");
|
|
|
|
let launchTarget = initWithPath(installLocation);
|
|
|
|
launchTarget.append(appFilename + ".exe");
|
|
|
|
|
|
|
|
return launchTarget;
|
|
|
|
#elifdef XP_MACOSX
|
|
|
|
let mwaUtils = Cc["@mozilla.org/widget/mac-web-app-utils;1"]
|
|
|
|
.createInstance(Ci.nsIMacWebAppUtils);
|
|
|
|
|
|
|
|
try {
|
2013-09-13 16:32:47 +04:00
|
|
|
let path;
|
|
|
|
if (path = mwaUtils.pathForAppWithIdentifier(uniqueName)) {
|
|
|
|
return [ uniqueName, path ];
|
2013-08-02 04:00:22 +04:00
|
|
|
}
|
2013-09-13 16:32:47 +04:00
|
|
|
} catch(ex) {}
|
|
|
|
|
|
|
|
try {
|
|
|
|
let path;
|
|
|
|
if (path = mwaUtils.pathForAppWithIdentifier(aApp.origin)) {
|
|
|
|
return [ aApp.origin, path ];
|
2013-08-02 04:00:22 +04:00
|
|
|
}
|
|
|
|
} catch(ex) {}
|
|
|
|
|
2013-09-13 16:32:47 +04:00
|
|
|
return [ null, null ];
|
2013-08-02 04:00:22 +04:00
|
|
|
#elifdef XP_UNIX
|
|
|
|
let exeFile = Services.dirsvc.get("Home", Ci.nsIFile);
|
|
|
|
exeFile.append("." + uniqueName);
|
|
|
|
exeFile.append("webapprt-stub");
|
2012-07-14 12:09:39 +04:00
|
|
|
|
2013-08-02 04:00:22 +04:00
|
|
|
if (!exeFile.exists()) {
|
|
|
|
exeFile = Services.dirsvc.get("Home", Ci.nsIFile);
|
2012-07-14 12:09:39 +04:00
|
|
|
|
2013-08-02 04:00:22 +04:00
|
|
|
let origin = Services.io.newURI(aApp.origin, null, null);
|
|
|
|
let installDir = "." + origin.scheme + ";" +
|
|
|
|
origin.host +
|
|
|
|
(origin.port != -1 ? ";" + origin.port : "");
|
|
|
|
|
|
|
|
exeFile.append(installDir);
|
|
|
|
exeFile.append("webapprt-stub");
|
|
|
|
|
|
|
|
if (!exeFile.exists()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return exeFile;
|
|
|
|
#endif
|
|
|
|
},
|
|
|
|
|
2013-09-13 16:32:47 +04:00
|
|
|
getInstallPath: function(aApp) {
|
|
|
|
#ifdef XP_WIN
|
|
|
|
let execFile = this.getLaunchTarget(aApp);
|
|
|
|
if (!execFile) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return execFile.parent.path;
|
|
|
|
#elifdef XP_MACOSX
|
|
|
|
let [ bundleID, path ] = this.getLaunchTarget(aApp);
|
|
|
|
return path;
|
|
|
|
#elifdef MOZ_B2G
|
|
|
|
return aApp.basePath + "/" + aApp.id;
|
|
|
|
#elifdef MOZ_FENNEC
|
|
|
|
return aApp.basePath + "/" + aApp.id;
|
|
|
|
#elifdef XP_UNIX
|
|
|
|
let execFile = this.getLaunchTarget(aApp);
|
|
|
|
if (!execFile) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return execFile.parent.path;
|
|
|
|
#endif
|
|
|
|
},
|
|
|
|
|
2013-08-02 04:00:22 +04:00
|
|
|
launch: function(aApp) {
|
|
|
|
let uniqueName = this.getUniqueName(aApp);
|
|
|
|
|
|
|
|
#ifdef XP_WIN
|
|
|
|
let initProcess = CC("@mozilla.org/process/util;1",
|
|
|
|
"nsIProcess", "init");
|
|
|
|
|
|
|
|
let launchTarget = this.getLaunchTarget(aApp);
|
|
|
|
if (!launchTarget) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2012-07-14 12:09:39 +04:00
|
|
|
let process = initProcess(launchTarget);
|
|
|
|
process.runwAsync([], 0);
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#elifdef XP_MACOSX
|
2013-09-13 16:32:47 +04:00
|
|
|
let [ launchIdentifier, path ] = this.getLaunchTarget(aApp);
|
2013-08-02 04:00:22 +04:00
|
|
|
if (!launchIdentifier) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-07-14 12:09:39 +04:00
|
|
|
let mwaUtils = Cc["@mozilla.org/widget/mac-web-app-utils;1"]
|
2013-08-02 04:00:22 +04:00
|
|
|
.createInstance(Ci.nsIMacWebAppUtils);
|
2013-08-02 05:51:12 +04:00
|
|
|
|
2013-08-02 04:00:22 +04:00
|
|
|
try {
|
|
|
|
mwaUtils.launchAppWithIdentifier(launchIdentifier);
|
|
|
|
} catch(e) {
|
|
|
|
return false;
|
2012-07-14 12:09:39 +04:00
|
|
|
}
|
|
|
|
|
2013-08-02 04:00:22 +04:00
|
|
|
return true;
|
2012-07-14 12:09:39 +04:00
|
|
|
#elifdef XP_UNIX
|
2013-08-02 04:00:22 +04:00
|
|
|
let exeFile = this.getLaunchTarget(aApp);
|
|
|
|
if (!exeFile) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-07-14 12:09:39 +04:00
|
|
|
|
2013-08-02 05:51:12 +04:00
|
|
|
try {
|
2013-08-02 04:00:22 +04:00
|
|
|
let process = Cc["@mozilla.org/process/util;1"]
|
|
|
|
.createInstance(Ci.nsIProcess);
|
2013-08-02 04:00:22 +04:00
|
|
|
|
2013-08-02 04:00:22 +04:00
|
|
|
process.init(exeFile);
|
|
|
|
process.runAsync([], 0);
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2012-08-09 05:04:48 +04:00
|
|
|
#endif
|
|
|
|
},
|
|
|
|
|
2013-08-02 04:00:22 +04:00
|
|
|
uninstall: function(aApp) {
|
|
|
|
let uniqueName = this.getUniqueName(aApp);
|
|
|
|
|
2012-08-09 05:04:48 +04:00
|
|
|
#ifdef XP_UNIX
|
|
|
|
#ifndef XP_MACOSX
|
2013-08-02 04:00:22 +04:00
|
|
|
let exeFile = this.getLaunchTarget(aApp);
|
|
|
|
if (!exeFile) {
|
|
|
|
return false;
|
|
|
|
}
|
2013-08-02 04:00:22 +04:00
|
|
|
|
2013-08-02 04:00:22 +04:00
|
|
|
try {
|
|
|
|
let process = Cc["@mozilla.org/process/util;1"]
|
|
|
|
.createInstance(Ci.nsIProcess);
|
|
|
|
|
|
|
|
process.init(exeFile);
|
|
|
|
process.runAsync(["-remove"], 1);
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
},
|
2013-08-02 04:00:22 +04:00
|
|
|
|
2013-08-02 04:00:22 +04:00
|
|
|
/**
|
|
|
|
* Checks if the given app is locally installed.
|
|
|
|
*/
|
|
|
|
isLaunchable: function(aApp) {
|
|
|
|
let uniqueName = this.getUniqueName(aApp);
|
|
|
|
|
|
|
|
#ifdef XP_WIN
|
|
|
|
if (!this.getLaunchTarget(aApp)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#elifdef XP_MACOSX
|
2013-09-13 16:32:47 +04:00
|
|
|
if (!this.getInstallPath(aApp)) {
|
2013-08-02 04:00:22 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#elifdef XP_UNIX
|
|
|
|
let env = Cc["@mozilla.org/process/environment;1"]
|
|
|
|
.getService(Ci.nsIEnvironment);
|
|
|
|
|
|
|
|
let xdg_data_home_env;
|
2012-08-09 05:04:48 +04:00
|
|
|
try {
|
2013-08-02 04:00:22 +04:00
|
|
|
xdg_data_home_env = env.get("XDG_DATA_HOME");
|
|
|
|
} catch(ex) {}
|
|
|
|
|
|
|
|
let desktopINI;
|
|
|
|
if (xdg_data_home_env) {
|
|
|
|
desktopINI = new FileUtils.File(xdg_data_home_env);
|
|
|
|
} else {
|
|
|
|
desktopINI = FileUtils.getFile("Home", [".local", "share"]);
|
|
|
|
}
|
|
|
|
desktopINI.append("applications");
|
|
|
|
desktopINI.append("owa-" + uniqueName + ".desktop");
|
|
|
|
|
|
|
|
if (!desktopINI.exists()) {
|
|
|
|
if (xdg_data_home_env) {
|
|
|
|
desktopINI = new FileUtils.File(xdg_data_home_env);
|
|
|
|
} else {
|
|
|
|
desktopINI = FileUtils.getFile("Home", [".local", "share"]);
|
2012-08-09 05:04:48 +04:00
|
|
|
}
|
|
|
|
|
2013-08-02 04:00:22 +04:00
|
|
|
let origin = Services.io.newURI(aApp.origin, null, null);
|
|
|
|
let oldUniqueName = origin.scheme + ";" +
|
|
|
|
origin.host +
|
|
|
|
(origin.port != -1 ? ";" + origin.port : "");
|
|
|
|
|
|
|
|
desktopINI.append("owa-" + oldUniqueName + ".desktop");
|
|
|
|
|
|
|
|
return desktopINI.exists();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2012-07-14 12:09:39 +04:00
|
|
|
#endif
|
2013-08-02 04:00:22 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sanitize the filename (accepts only a-z, 0-9, - and _)
|
|
|
|
*/
|
|
|
|
sanitizeStringForFilename: function(aPossiblyBadFilenameString) {
|
|
|
|
return aPossiblyBadFilenameString.replace(/[^a-z0-9_\-]/gi, "");
|
2012-07-14 12:09:39 +04:00
|
|
|
}
|
|
|
|
}
|