Bug 352762 - Allow PFS to launch an executable file to run plugins, instead of installing an XPI. This fixes Windows Vista permissions errors in XPInstall, r=doron

This commit is contained in:
benjamin@smedbergs.us 2007-07-25 11:54:46 -07:00
parent 77a82790dc
commit bcbbe43a8f
3 changed files with 226 additions and 36 deletions

View File

@ -126,6 +126,8 @@ nsRDFItemUpdater.prototype = {
pid: getPFSValueFromRDF("guid"),
version: getPFSValueFromRDF("version"),
IconUrl: getPFSValueFromRDF("IconUrl"),
InstallerLocation: getPFSValueFromRDF("InstallerLocation"),
InstallerHash: getPFSValueFromRDF("InstallerHash"),
XPILocation: getPFSValueFromRDF("XPILocation"),
XPIHash: getPFSValueFromRDF("XPIHash"),
InstallerShowsUI: getPFSValueFromRDF("InstallerShowsUI"),
@ -145,7 +147,7 @@ nsRDFItemUpdater.prototype = {
onDatasourceError: function pfs_onDatasourceError (aPluginRequestItem, aError){
this._os.notifyObservers(aPluginRequestItem, "error", aError);
gPluginInstaller.pluginInfoReceived(null);
},
}
};
function nsPluginXMLRDFDSObserver(aUpdater, aPluginRequestItem){

View File

@ -35,33 +35,222 @@
*
* ***** END LICENSE BLOCK ***** */
var PluginInstallService = {
init: function ()
const nsIXPIProgressDialog = Components.interfaces.nsIXPIProgressDialog;
function getLocalizedError(key)
{
return document.getElementById("xpinstallStrings").getString(key);
}
function binaryToHex(input)
{
return [('0' + input.charCodeAt(i).toString(16)).slice(-2)
for (i in input)].join('');
}
function verifyHash(aFile, aHash)
{
try {
var [, method, hash] = /^([A-Za-z0-9]+):(.*)$/.exec(aHash);
var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
createInstance(Components.interfaces.nsIFileInputStream);
fis.init(aFile, -1, -1, 0);
var hasher = Components.classes['@mozilla.org/security/hash;1'].
createInstance(Components.interfaces.nsICryptoHash);
hasher.initWithString(method);
hasher.updateFromStream(fis, -1);
dlhash = binaryToHex(hasher.finish(false));
return dlhash == hash;
}
catch (e) {
Components.utils.reportError(e);
return false;
}
}
function InstallerObserver(aPlugin)
{
this._plugin = aPlugin;
this._init();
}
InstallerObserver.prototype = {
_init: function()
{
try {
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(this._plugin.InstallerLocation, null, null);
uri.QueryInterface(Components.interfaces.nsIURL);
var leafName = uri.fileName;
if (leafName.indexOf('.') == -1)
throw "Filename needs to contain a dot for platform-native launching to work correctly.";
var dirs = Components.classes["@mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties);
var resultFile = dirs.get("TmpD", Components.interfaces.nsIFile);
resultFile.append(leafName);
resultFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE,
0x770);
var channel = ios.newChannelFromURI(uri);
this._downloader =
Components.classes["@mozilla.org/network/downloader;1"].
createInstance(Components.interfaces.nsIDownloader);
this._downloader.init(this, resultFile);
channel.notificationCallbacks = this;
this._fireNotification(nsIXPIProgressDialog.DOWNLOAD_START, null);
channel.asyncOpen(this._downloader, null);
}
catch (e) {
this._fireNotification(nsIXPIProgressDialog.INSTALL_DONE,
getLocalizedError("error-228"));
if (resultFile && resultFile.exists())
resultfile.remove(false);
}
},
pluginPidArray: null,
/**
* Inform the gPluginInstaller about what's going on.
*/
_fireNotification: function(aStatus, aErrorMsg)
{
gPluginInstaller.pluginInstallationProgress(this._plugin.pid,
aStatus, aErrorMsg);
startPluginInstallation: function (aPluginXPIUrlsArray,
aPluginHashes,
aPluginPidArray) {
this.pluginPidArray = aPluginPidArray;
if (aStatus == nsIXPIProgressDialog.INSTALL_DONE) {
--PluginInstallService._installersPending;
PluginInstallService._fireFinishedNotification();
}
},
var xpiManager = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
.createInstance(Components.interfaces.nsIXPInstallManager);
xpiManager.initManagerWithHashes(aPluginXPIUrlsArray, aPluginHashes,
aPluginXPIUrlsArray.length, this);
QueryInterface: function(iid)
{
if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsIInterfaceRequestor) ||
iid.equals(Components.interfaces.nsIDownloadObserver) ||
iid.equals(Components.interfaces.nsIProgressEventSink))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
getInterface: function(iid)
{
if (iid.equals(Components.interfaces.nsIProgressEventSink))
return this;
return null;
},
onDownloadComplete: function(downloader, request, ctxt, status, result)
{
if (!Components.isSuccessCode(status)) {
// xpinstall error 228 is "Download Error"
this._fireNotification(nsIXPIProgressDialog.INSTALL_DONE,
getLocalizedError("error-228"));
result.remove(false);
return;
}
this._fireNotification(nsIXPIProgressDialog.DOWNLOAD_DONE);
if (this._plugin.InstallerHash &&
!verifyHash(result, this._plugin.InstallerHash)) {
// xpinstall error 261 is "Invalid file hash..."
this._fireNotification(nsIXPIProgressDialog.INSTALL_DONE,
getLocalizedError("error-261"));
result.remove(false);
return;
}
this._fireNotification(nsIXPIProgressDialog.INSTALL_START);
result.QueryInterface(Components.interfaces.nsILocalFile);
try {
result.launch();
this._fireNotification(nsIXPIProgressDialog.INSTALL_DONE, null);
// It would be nice to remove the tempfile, but we don't have
// any way to know when it will stop being used :-(
}
catch (e) {
Components.utils.reportError(e);
this._fireNotification(nsIXPIProgressDialog.INSTALL_DONE,
getLocalizedError("error-207"));
result.remove(false);
}
},
onProgress: function(aRequest, aContext, aProgress, aProgressMax)
{
gPluginInstaller.pluginInstallationProgressMeter(this._plugin.pid,
aProgress,
aProgressMax);
},
onStatus: function(aRequest, aContext, aStatus, aStatusArg)
{
/* pass */
}
};
var PluginInstallService = {
/**
* Start installation of installers and XPI plugins.
* @param aInstallerPlugins An array of objects which should have the
* properties "pid", "InstallerLocation",
* and "InstallerHash"
* @param aXPIPlugins An array of objects which should have the
* properties "pid", "XPILocation",
* and "XPIHash"
*/
startPluginInstallation: function (aInstallerPlugins,
aXPIPlugins)
{
this._installerPlugins = [new InstallerObserver(plugin)
for each (plugin in aInstallerPlugins)];
this._installersPending = this._installerPlugins.length;
this._xpiPlugins = aXPIPlugins;
if (this._xpiPlugins.length > 0) {
this._xpisDone = false;
var xpiManager = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
.createInstance(Components.interfaces.nsIXPInstallManager);
xpiManager.initManagerWithHashes(
[plugin.XPILocation for each (plugin in this._xpiPlugins)],
[plugin.XPIHash for each (plugin in this._xpiPlugins)],
this._xpiPlugins.length, this);
}
else {
this._xpisDone = true;
}
},
_fireFinishedNotification: function()
{
if (this._installersPending == 0 && this._xpisDone)
gPluginInstaller.
pluginInstallationProgress(null, nsIXPIProgressDialog.DIALOG_CLOSE,
null);
},
// XPI progress listener stuff
onStateChange: function (aIndex, aState, aValue)
{
// get the pid to return to the wizard
var pid = this.pluginPidArray[aIndex];
var pid = this._xpiPlugins[aIndex].pid;
var errorMsg;
if (aState == Components.interfaces.nsIXPIProgressDialog.INSTALL_DONE) {
if (aState == nsIXPIProgressDialog.INSTALL_DONE) {
if (aValue != 0) {
var xpinstallStrings = document.getElementById("xpinstallStrings");
try {
@ -73,15 +262,19 @@ var PluginInstallService = {
}
}
gPluginInstaller.pluginInstallationProgress(pid, aState, errorMsg);
if (aState == nsIXPIProgressDialog.DIALOG_CLOSE) {
this._xpisDone = true;
this._fireFinishedNotification();
}
else {
gPluginInstaller.pluginInstallationProgress(pid, aState, errorMsg);
}
},
onProgress: function (aIndex, aValue, aMaxValue)
{
// get the pid to return to the wizard
var pid = this.pluginPidArray[aIndex];
var pid = this._xpiPlugins[aIndex].pid;
gPluginInstaller.pluginInstallationProgressMeter(pid, aValue, aMaxValue);
}
}

View File

@ -337,30 +337,23 @@ nsPluginInstallerWizard.prototype.startPluginInstallation = function (){
this.canAdvance(false);
this.canRewind(false);
// since the user can choose what plugins to install, we need to store
// which ones were choosen, as nsIXPInstallManager returns an index and not the
// mimetype. So store the pids.
var pluginURLArray = new Array();
var pluginHashArray = new Array();
var pluginPidArray = new Array();
var installerPlugins = [];
var xpiPlugins = [];
for (pluginInfoItem in this.mPluginInfoArray){
var pluginItem = this.mPluginInfoArray[pluginInfoItem];
// only push to the array if it has an XPILocation, else nsIXPInstallManager
// will complain.
if (pluginItem.toBeInstalled && pluginItem.XPILocation && pluginItem.licenseAccepted) {
pluginURLArray.push(pluginItem.XPILocation);
pluginHashArray.push(pluginItem.XPIHash);
pluginPidArray.push(pluginItem.pid);
if (pluginItem.toBeInstalled && pluginItem.licenseAccepted) {
if (pluginItem.InstallerLocation)
installerPlugins.push(pluginItem);
else if (pluginItem.XPILocation)
xpiPlugins.push(pluginItem);
}
}
if (pluginURLArray.length > 0)
PluginInstallService.startPluginInstallation(pluginURLArray,
pluginHashArray,
pluginPidArray);
if (installerPlugins.length > 0 || xpiPlugins.length > 0)
PluginInstallService.startPluginInstallation(installerPlugins,
xpiPlugins);
else
this.advancePage(null, true, false, false);
}
@ -622,6 +615,8 @@ function PluginInfo(aResult) {
this.pid = aResult.pid;
this.version = aResult.version;
this.IconUrl = aResult.IconUrl;
this.InstallerLocation = aResult.InstallerLocation;
this.InstallerHash = aResult.InstallerHash;
this.XPILocation = aResult.XPILocation;
this.XPIHash = aResult.XPIHash;
this.InstallerShowsUI = aResult.InstallerShowsUI;