begin to work on extension manager install functionality

This commit is contained in:
ben%bengoodger.com 2004-04-29 09:17:58 +00:00
Родитель bd2c7f02f3
Коммит c6486377ab
2 изменённых файлов: 288 добавлений и 24 удалений

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

@ -38,14 +38,18 @@
#include "nsISupports.idl" #include "nsISupports.idl"
interface nsIInputStream; interface nsIZipReader;
interface nsIRDFDataSource; interface nsIRDFDataSource;
interface nsIUpdateItem; interface nsIUpdateItem;
[scriptable, uuid(c3515b0f-99f4-453b-805e-1fdf5724d6d9)] [scriptable, uuid(c3515b0f-99f4-453b-805e-1fdf5724d6d9)]
interface nsIExtensionManager : nsISupports interface nsIExtensionManager : nsISupports
{ {
void installExtensionFromStream(in nsIInputStream aStream, in boolean aUseProfile); const unsigned long FLAG_INSTALL_PROFILE = 0x01;
const unsigned long FLAG_INSTALL_GLOBAL = 0x02;
void installExtensionFromXPI(in nsIZipReader aZipReader, in unsigned long aFlags);
void uninstallExtension(in string aExtensionID); void uninstallExtension(in string aExtensionID);
void enableExtension(in string aExtensionID); void enableExtension(in string aExtensionID);
void disableExtension(in string aExtensionID); void disableExtension(in string aExtensionID);

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

@ -45,6 +45,60 @@ const PREF_EM_APP_VERSION = "app.version";
const PREF_EM_LAST_APP_VERSION = "extensions.lastAppVersion"; const PREF_EM_LAST_APP_VERSION = "extensions.lastAppVersion";
const PREF_UPDATE_COUNT = "update.extensions.count"; const PREF_UPDATE_COUNT = "update.extensions.count";
function getDir(aKey, aSubDirs)
{
var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties);
var dir = fileLocator.get(aKey, Components.interfaces.nsIFile);
for (var i = 0; i < aSubDirs.length; ++i) {
dir.append(aSubDirs[i]);
if (!dir.exists())
dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
}
return dir;
}
function nsInstallLogger(aExtensionID, aIsProfile)
{
this._isProfile = aIsProfile; // XXXben
this._extensionDir = getDir(aIsProfile ? "ProfD" : "ProfD", ["extensions", aExtensionID]); // XXXben XCurProcDir
this._uninstallDir = this._extensionDir.clone();
this._uninstallDir.append("uninstall");
var uninstallLog = this._uninstallDir.clone();
uninstallLog.append("uninstall.rdf");
var ioServ = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var fph = ioServ.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler);
var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
.getService(Components.interfaces.nsIRDFService);
this._ds = rdf.GetDataSourceBlocking(fph.getURLSpecFromFile(uninstallLog));
}
nsInstallLogger.prototype = {
_extensionDir: null,
_uninstallDir: null,
_ds: null,
addFile: function (aFile)
{
var aFileLF = aFile.QueryInterface(Components.interfaces.nsILocalFile);
var eDirLF = this._extensionDir.QueryInterface(Components.interfaces.nsILocalFile);
// dump("*** reldesc1 = " + aFileLF.getRelativeDescriptor(this._extensionDir) + "\n");
// dump("*** reldesc2 = " + eDirLF.getRelativeDescriptor(aFile) + "\n");
},
replaceFile: function (aFile)
{
},
removeFile: function (aFile)
{
}
};
function VersionChecker(aExtensionResource, aAppID, aAppVersion, aDataSource) function VersionChecker(aExtensionResource, aAppID, aAppVersion, aDataSource)
{ {
this._extensionResource = aExtensionResource; this._extensionResource = aExtensionResource;
@ -117,6 +171,74 @@ VersionChecker.prototype = {
} }
} }
function nsJarFileExtractor(aXPIFile, aTargetDir)
{
this._xpiFile = aXPIFile.path;
this._targetDir = aTargetDir.path;
this._proxyObject(Components, Components.interfaces.nsIXPCComponents, "_components");
/*
this._proxyObject(aXPIFile, Components.interfaces.nsIFile, "_xpiFile");
this._proxyObject(aTargetDir, Components.interfaces.nsIFile, "_targetDir");
*/
}
nsJarFileExtractor.prototype = {
// proxied objects
_xpiFile: null,
_targetDir: null,
_components: null,
_proxyObject: function (aObject, aIID, aTarget)
{
const nsIEventQueueService = Components.interfaces.nsIEventQueueService;
var eqService = Components.classes["@mozilla.org/event-queue-service;1"]
.getService(nsIEventQueueService);
var uiQ = eqService.getSpecialEventQueue(nsIEventQueueService.UI_THREAD_EVENT_QUEUE);
var proxyObjectManager = Components.classes["@mozilla.org/xpcomproxy;1"]
.getService(Components.interfaces.nsIProxyObjectManager);
const PROXY_SYNC = 0x01;
const PROXY_ALWAYS = 0x04;
this[aTarget] = proxyObjectManager.getProxyForObject(uiQ, aIID, aObject,
PROXY_SYNC | PROXY_ALWAYS);
},
extract: function ()
{
const nsIThread = Components.interfaces.nsIThread;
var thread = Components.classes["@mozilla.org/thread;1"]
.createInstance(nsIThread);
thread.init(this, 0, nsIThread.PRIORITY_NORMAL,
nsIThread.SCOPE_GLOBAL,
nsIThread.STATE_JOINABLE);
},
/////////////////////////////////////////////////////////////////////////////
// nsIRunnable
run: function ()
{
// dump("*** RUNNING THREAD\n");
/*
var xpiFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
xpiFile.initWithPath(this._xpiFile);
var targetDir = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
targetDir.initWithPath(this._targetDir);
var zipReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]
.createInstance(Components.interfaces.nsIZipReader);
zipReader.init(xpiFile);
var entries = zipReader.findEntries("*");
while (entries.hasMoreElements()) {
var entry = entries.getNext().QueryInterface(Components.interfaces.nsIZipEntry);
dump("*** zip entry = " + entry.name + "\n");
}
*/
}
};
function nsExtensionManager() function nsExtensionManager()
{ {
var os = Components.classes["@mozilla.org/observer-service;1"] var os = Components.classes["@mozilla.org/observer-service;1"]
@ -154,11 +276,25 @@ nsExtensionManager.prototype = {
// and as such we must load it now and see what needs to happen. // and as such we must load it now and see what needs to happen.
this._ensureDS(); this._ensureDS();
// Look for items that need to be installed
var items = this._ds.getItemsWithFlagSet("toBeInstalled");
for (var i = 0; i < items.length; ++i)
this._finalizeInstall(items[i]);
// Look for extensions that need to be enabled // Look for extensions that need to be enabled
items = this._ds.getItemsWithFlagSet("toBeEnabled");
for (var i = 0; i < items.length; ++i)
this._finalizeEnable(items[i]);
// Look for extensions that need to be disabled // Look for extensions that need to be disabled
items = this._ds.getItemsWithFlagSet("toBeDisabled");
for (var i = 0; i < items.length; ++i)
this._finalizeDisable(items[i]);
// Look for extensions that need to be removed // Look for extensions that need to be removed
items = this._ds.getItemsWithFlagSet("toBeUninstalled");
for (var i = 0; i < items.length; ++i)
this._finalizeUninstall(items[i]);
// Now that we've finalized the EM operation, remove the autoreg file so // Now that we've finalized the EM operation, remove the autoreg file so
// we don't do this every time we start. // we don't do this every time we start.
@ -342,29 +478,133 @@ nsExtensionManager.prototype = {
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// nsIExtensionManager // nsIExtensionManager
installExtensionFromStream: function (aStream, aUseProfile) installExtensionFromXPI: function (aZipReader, aFlags)
{ {
var parser = Components.classes["@mozilla.org/rdf/xml-parser;1"] // Since we're installing a "new type" extension, we assume a file layout
.createInstance(Components.interfaces.nsIRDFXMLParser); // within the XPI like so:
var ds = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"] // foo.xpi/
.createInstance(Components.interfaces.nsIRDFDataSource); // extension.rdf
var streamListener = parser.parseAsync(ds, null); // chrome/
// components/
// defaults/
// prefs/
var installProfile = aFlags & nsIExtensionManager.FLAG_INSTALL_PROFILE;
var tempManifest = getDir(installProfile ? "ProfD" : "ProfD",
["extensions", "temp"]);
tempManifest.append("extension.rdf");
aZipReader.extract("extension.rdf", tempManifest);
var bytesAvailable; var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
do { .getService(Components.interfaces.nsIRDFService);
bytesAvailable = aStream.available(); var ioServ = Components.classes["@mozilla.org/network/io-service;1"]
if (!bytesAvailable) .getService(Components.interfaces.nsIIOService);
break; var fph = ioServ.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler);
var ds = rdf.GetDataSourceBlocking(fph.getURLSpecFromFile(tempManifest));
streamListener.onDataAvailable(null, null, aStream, 0, bytesAvailable);
// We do a basic version check first just to make sure we somehow weren't
// tricked into installing an incompatible extension...
var extensionID = this._canInstallExtension(ds);
if (extensionID) {
this._ensureDS();
this._ds.addExtensionEntry(extensionID, installProfile);
// Then we stage the extension's files into a temporary directory so we
// can install them after the next restart.
this._stageExtensionFiles(aZipReader, extensionID,
aFlags & nsIExtensionManager.FLAG_INSTALL_PROFILE);
} }
while (1); tempManifest.remove(false);
this._ensureDS();
this._ds.installExtension(ds, aUseProfile);
this._writeProfileData(); this._writeProfileData();
}, },
_canInstallExtension: function (aDataSource)
{
var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
.getService(Components.interfaces.nsIRDFService);
var manifestRoot = rdf.GetResource("extension:manifest");
var id = rdf.GetResource(EM_NS("id"));
// XXXben - do version check
var idLiteral = aDataSource.GetTarget(manifestRoot, id, true);
return idLiteral.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
},
_stageExtensionFiles: function (aZipReader, aExtensionID, aInstallProfile)
{
// Get the staging dir
var dir = getDir(aInstallProfile ? "ProfD" : "ProfD",
["extensions", "temp", aExtensionID]);
var entries = aZipReader.findEntries("*");
while (entries.hasMoreElements()) {
var entry = entries.getNext().QueryInterface(Components.interfaces.nsIZipEntry);
var file = dir.clone();
var parts = entry.name.split("/");
for (var i = 0; i < parts.length; ++i) {
file.append(parts[i]);
if (!file.exists() && i < parts.length - 1)
file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
}
if (!file.exists() || !file.isDirectory())
aZipReader.extract(entry.name, file);
}
// (new nsJarFileExtractor(aZipReader.file, dir)).extract();
},
// This function is called on the next startup
_finalizeInstall: function (aExtensionID)
{
var installLocation = this._ds.getExtensionProperty(aExtensionID, "installLocation");
var logger = new nsInstallLogger(aExtensionID, installLocation == "profile");
// Move files from the staging dir into the extension's final home.
// This function generates uninstall log files and creates backups of
// existing files.
this._installExtensionFiles(aExtensionID, logger);
// Load the metadata datasource
// Add metadata for the extension to the global extension metadata set
this._ds.addExtensionMetaData(aExtensionID, logger);
// Register chrome packages for files specified in the extension manifest
this._registerChromeForExtension(aExtensionID, logger);
},
_installExtensionFiles: function (aExtensionID, aLogger)
{
var tempDir = getDir(aLogger._isProfile ? "ProfD" : "ProfD", ["extensions", "temp", aExtensionID]); // XXXben XCurProcDir
this._goatFile(tempDir, aLogger);
},
_goatFile: function (aParent, aLogger)
{
var entries = aParent.directoryEntries;
while (entries.hasMoreElements()) {
var entry = entries.getNext().QueryInterface(Components.interfaces.nsIFile);
if (entry.isDirectory())
this._goatFile(entry, aLogger);
else
aLogger.addFile(entry);
return;
}
},
_finalizeEnable: function (aExtensionID)
{
},
_finalizeDisable: function (aExtensionID)
{
},
_finalizeUninstall: function (aExtensionID)
{
},
uninstallExtension: function (aExtensionID) uninstallExtension: function (aExtensionID)
{ {
@ -628,6 +868,24 @@ nsExtensionsDataSource.prototype = {
return items; return items;
}, },
getItemsWithFlagSet: function (aFlag)
{
var items = [];
var sources = this.GetSources(this._emR(aFlag), this._emL("true"), true);
while (sources.hasMoreElements()) {
var e = sources.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
items.push(this._stripPrefix(e.Value));
}
return items;
},
addExtensionEntry: function (aExtensionID, aInstallProfile)
{
this._setExtensionProperty(aExtensionID, this._emR("toBeInstalled"),
this._emL("true"), aInstallProfile);
},
_setProperty: function (aDS, aSource, aProperty, aNewValue) _setProperty: function (aDS, aSource, aProperty, aNewValue)
{ {
var oldValue = aDS.GetTarget(aSource, aProperty, true); var oldValue = aDS.GetTarget(aSource, aProperty, true);
@ -647,16 +905,18 @@ nsExtensionsDataSource.prototype = {
return ""; return "";
}, },
_setExtensionProperty: function (aExtensionID, aPropertyArc, aPropertyValue) _setExtensionProperty: function (aExtensionID, aPropertyArc, aPropertyValue, aIsProfile)
{ {
var extension = this._rdf.GetResource("urn:mozilla:extension:" + aExtensionID); var extension = this._rdf.GetResource("urn:mozilla:extension:" + aExtensionID);
var installLocation = this.GetTarget(extension, this._emR("installLocation"), true); if (aIsProfile === undefined) {
var isProfile = installLocation.EqualsNode(this._emL("profile")); var installLocation = this.GetTarget(extension, this._emR("installLocation"), true);
var ds = isProfile ? this._profileExtensions : this._appExtensions; aIsProfile = installLocation.EqualsNode(this._emL("profile"));
}
var ds = aIsProfile ? this._profileExtensions : this._appExtensions;
this._setProperty(ds, extension, aPropertyArc, aPropertyValue); this._setProperty(ds, extension, aPropertyArc, aPropertyValue);
this._flush(isProfile); this._flush(aIsProfile);
}, },
enableExtension: function (aExtensionID) enableExtension: function (aExtensionID)