Bug 492008: Drop support for contents.rdf chrome registrations. r=bsmedberg, r=robstrong

This commit is contained in:
Dave Townsend 2009-06-11 10:54:26 +01:00
parent 9d02a47048
commit bc0b863ee6
4 changed files with 90 additions and 1146 deletions

View File

@ -41,33 +41,9 @@
interface nsIURI;
interface nsIUTF8StringEnumerator;
[scriptable, uuid(94490b3f-f094-418e-b1b9-73878d29bff3)]
[scriptable, uuid(8727651c-9530-45a0-b81e-0e0690c30c50)]
interface nsIToolkitChromeRegistry : nsIXULChromeRegistry
{
/**
* The "canonical" manifest is a plaintext file which sits outside of a
* JAR file. To provide backwards-compatibility with contents.rdf, we provide
* this function which reads a contents.rdf manifest and writes it to a file.
*
* @param aOldManifestURI The URI of an old manifest to read, without
* the trailing "contents.rdf", e.g.
* "jar:resource:///chrome/foo.jar!/content/foo/" or
* "file://path/to/contents/rdf/"
* @param aFile The URI of a manifest file to write. It's a good
* idea to use a resource: URI if possible.
* @param aBaseURI The base URI for relative path creation
* "jar:resource:///chrome/foo.jar!/content/foo/"
* this is a separate param from aOldManifestURI so
* the "contents.rdf" can be read outside of the jar
* to keep the zipreader cache from holding it open.
* @param aAppend Whether we should append to an existing manifest
* or truncate and start empty.
* @param aSkinOnly Only allow skin packages.
*/
void processContentsManifest(in nsIURI aOldManifestURI, in nsIURI aFile,
in nsIURI aBaseURI, in boolean aAppend,
in boolean aSkinOnly);
/**
* If the OS has a "high-visibility" or "disabled-friendly" theme set,
* we want to force mozilla into the classic theme, which (for the most part

View File

@ -115,29 +115,6 @@
#include "nsIXULRuntime.h"
#include "nsPresShellIterator.h"
#ifdef MOZ_XUL
// keep all the RDF stuff together, in case we can remove it in the far future
#include "rdf.h"
#include "nsRDFCID.h"
#include "nsIRDFService.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFObserver.h"
#include "nsIRDFRemoteDataSource.h"
#include "nsIRDFXMLSink.h"
#include "nsIRDFResource.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFContainer.h"
#include "nsIRDFContainerUtils.h"
#define CHROME_URI "http://www.mozilla.org/rdf/chrome#"
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, packages);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, package);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, name);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, platformPackage);
#endif
#define UILOCALE_CMD_LINE_ARG "UILocale"
#define MATCH_OS_LOCALE_PREF "intl.locale.matchOS"
@ -485,21 +462,6 @@ nsChromeRegistry::Init()
{
nsresult rv;
// these atoms appear in almost every chrome registry contents.rdf
// in some form or another. making static atoms prevents the atoms
// from constantly being created/destroyed during parsing
static const nsStaticAtom atoms[] = {
{ "chrome", nsnull },
{ "NC", nsnull },
{ "allowScripts", nsnull },
{ "package", nsnull },
{ "packages", nsnull },
{ "hasOverlays", nsnull },
};
NS_RegisterStaticAtoms(atoms, NS_ARRAY_LENGTH(atoms));
// Check to see if necko and the JAR protocol handler are registered yet
// if not, somebody is doing work during XPCOM registration that they
// shouldn't be doing. See bug 292549, where JS components are trying
@ -1197,10 +1159,6 @@ nsChromeRegistry::CheckForNewChrome()
rv = NS_NewURI(getter_AddRefs(manifestURI),
NS_LITERAL_CSTRING("resource:///chrome/app-chrome.manifest"));
// this is the main manifest; if it doesn't exist we generate it from
// installed-chrome.txt. When the build system learns about the new system,
// this code can go away.
nsCOMPtr<nsIFileURL> manifestFileURL (do_QueryInterface(manifestURI));
NS_ASSERTION(manifestFileURL, "Not a nsIFileURL!");
NS_ENSURE_TRUE(manifestFileURL, NS_ERROR_UNEXPECTED);
@ -1213,35 +1171,6 @@ nsChromeRegistry::CheckForNewChrome()
rv = manifest->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
#ifdef DEBUG
// In debug builds, installed-chrome.txt may change during development;
// we just rebuild it every time because we're not worried about startup
// time or other bad/goodness.
if (exists) {
manifest->Remove(PR_FALSE);
exists = PR_FALSE;
}
#endif
if (!exists) {
nsCOMPtr<nsIFile> installed;
manifest->Clone(getter_AddRefs(installed));
if (!installed)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsILocalFile> linstalled (do_QueryInterface(installed));
NS_ENSURE_TRUE(linstalled, NS_NOINTERFACE);
linstalled->SetNativeLeafName(NS_LITERAL_CSTRING("installed-chrome.txt"));
rv = linstalled->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
// process installed-chrome.txt into app-chrome.manifest
if (exists)
ProcessNewChromeFile(linstalled, manifestURI);
}
nsCOMPtr<nsIProperties> dirSvc (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
NS_ENSURE_TRUE(dirSvc, NS_ERROR_FAILURE);
@ -1358,125 +1287,6 @@ nsChromeRegistry::WrappersEnabled(nsIURI *aURI)
entry->flags & PackageEntry::XPCNATIVEWRAPPERS;
}
nsresult
nsChromeRegistry::ProcessNewChromeFile(nsILocalFile *aListFile, nsIURI* aManifest)
{
nsresult rv;
PRFileDesc *file;
rv = aListFile->OpenNSPRFileDesc(PR_RDONLY, 0, &file);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 n, size;
char *buf;
size = PR_Available(file);
if (size == -1) {
rv = NS_ERROR_UNEXPECTED;
goto end;
}
buf = (char *) malloc(size + 1);
if (!buf) {
rv = NS_ERROR_OUT_OF_MEMORY;
goto end;
}
n = PR_Read(file, buf, size);
if (n > 0)
rv = ProcessNewChromeBuffer(buf, size, aManifest);
free(buf);
end:
PR_Close(file);
return rv;
}
nsresult
nsChromeRegistry::ProcessNewChromeBuffer(char *aBuffer, PRInt32 aLength,
nsIURI* aManifest)
{
nsresult rv = NS_OK;
char *bufferEnd = aBuffer + aLength;
char *chromeType, // "content", "locale" or "skin"
*chromeProfile, // "install" or "profile"
*chromeLocType, // type of location (local path or URL)
*chromeLocation; // base location of chrome (jar file)
nsCOMPtr<nsIURI> baseURI;
// process chromeType, chromeProfile, chromeLocType, chromeLocation
while (aBuffer < bufferEnd) {
// parse one line of installed-chrome.txt
chromeType = aBuffer;
while (aBuffer < bufferEnd && *aBuffer != ',')
++aBuffer;
*aBuffer = '\0';
chromeProfile = ++aBuffer;
if (aBuffer >= bufferEnd)
break;
while (aBuffer < bufferEnd && *aBuffer != ',')
++aBuffer;
*aBuffer = '\0';
chromeLocType = ++aBuffer;
if (aBuffer >= bufferEnd)
break;
while (aBuffer < bufferEnd && *aBuffer != ',')
++aBuffer;
*aBuffer = '\0';
chromeLocation = ++aBuffer;
if (aBuffer >= bufferEnd)
break;
while (aBuffer < bufferEnd &&
(*aBuffer != '\r' && *aBuffer != '\n' && *aBuffer != ' '))
++aBuffer;
*aBuffer = '\0';
// process the line
// We don't do skin or locale selection from installed-chrome.txt since
// ffox 0.9. Just ignore the "select" lines.
if (strcmp(chromeLocType,"select")) {
if (!strcmp(chromeLocType, "path")) {
// location is a (full) path. convert it to an URL.
/* this is some convoluted shit... this creates a file, inits it with
* the path parsed above (chromeLocation), makes a url, and inits it
* with the file created. the purpose of this is just to have the
* canonical url of the stupid thing.
*/
nsCOMPtr<nsILocalFile> chromeFile;
rv = NS_NewNativeLocalFile(nsDependentCString(chromeLocation),
PR_TRUE, getter_AddRefs(chromeFile));
NS_ENSURE_SUCCESS(rv, rv);
/*
* all we want here is the canonical url
*/
rv = NS_NewFileURI(getter_AddRefs(baseURI), chromeFile);
if (NS_FAILED(rv)) return rv;
}
else {
rv = NS_NewURI(getter_AddRefs(baseURI), chromeLocation);
if (NS_FAILED(rv)) return rv;
}
ProcessContentsManifest(baseURI, aManifest, baseURI, PR_TRUE,
strcmp(chromeType, "skin") == 0);
}
while (aBuffer < bufferEnd && (*aBuffer == '\0' || *aBuffer == ' ' || *aBuffer == '\r' || *aBuffer == '\n'))
++aBuffer;
}
return NS_OK;
}
NS_IMETHODIMP nsChromeRegistry::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
{
nsresult rv = NS_OK;
@ -1527,416 +1337,6 @@ NS_IMETHODIMP nsChromeRegistry::Observe(nsISupports *aSubject, const char *aTopi
return rv;
}
#ifdef MOZ_XUL
static nsresult
GetContainerEnumerator(nsIRDFDataSource* ds, nsIRDFResource* res,
nsISimpleEnumerator* *aResult, PRInt32 *aCountResult = nsnull)
{
nsresult rv;
nsCOMPtr<nsIRDFContainer> container
(do_CreateInstance("@mozilla.org/rdf/container;1"));
NS_ENSURE_TRUE(container, NS_ERROR_FAILURE);
rv = container->Init(ds, res);
if (NS_FAILED(rv)) return rv;
if (aCountResult)
container->GetCount(aCountResult);
return container->GetElements(aResult);
}
static void
FollowLiteral(nsIRDFDataSource* ds, nsIRDFResource* res,
nsIRDFResource* arc, nsACString& result)
{
nsresult rv;
nsCOMPtr<nsIRDFNode> node;
rv = ds->GetTarget(res, arc, PR_TRUE, getter_AddRefs(node));
if (NS_FAILED(rv) || !node) {
result.Truncate();
return;
}
nsCOMPtr<nsIRDFLiteral> literal (do_QueryInterface(node));
if (!literal) {
NS_ERROR("Arc found, but doesn't point to expected literal!");
result.Truncate();
return;
}
const PRUnichar* value;
literal->GetValueConst(&value);
CopyUTF16toUTF8(value, result);
}
static void
FollowResource(nsIRDFDataSource* ds, nsIRDFResource* res, nsIRDFResource* arc,
nsIRDFResource* *result)
{
nsresult rv;
nsCOMPtr<nsIRDFNode> node;
rv = ds->GetTarget(res, arc, PR_TRUE, getter_AddRefs(node));
if (NS_FAILED(rv) || !node) {
*result = nsnull;
return;
}
CallQueryInterface(node, result);
}
static void
GetRelativePath(nsIURI* base, nsIURI* relative, nsACString& result)
{
nsresult rv;
nsCOMPtr<nsIJARURI> jarrelative (do_QueryInterface(relative));
if (jarrelative) {
nsCOMPtr<nsIURI> jarbase;
jarrelative->GetJARFile(getter_AddRefs(jarbase));
nsCAutoString relativeBase;
GetRelativePath(base, jarbase, relativeBase);
nsCAutoString jarEntry;
jarrelative->GetJAREntry(jarEntry);
result.Assign(NS_LITERAL_CSTRING("jar:"));
result.Append(relativeBase);
result.Append(NS_LITERAL_CSTRING("!/"));
result.Append(jarEntry);
return;
}
nsCOMPtr<nsIURL> baseURL (do_QueryInterface(base));
if (!baseURL) {
relative->GetSpec(result);
return;
}
rv = baseURL->GetRelativeSpec(relative, result);
if (NS_FAILED(rv)) {
relative->GetSpec(result);
}
}
static const PRInt32 kNSPR_APPEND_FLAGS = PR_WRONLY | PR_CREATE_FILE | PR_APPEND;
static const PRInt32 kNSPR_TRUNCATE_FLAGS = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
NS_IMETHODIMP
nsChromeRegistry::ProcessContentsManifest(nsIURI* aOldManifest, nsIURI* aFile,
nsIURI* aBaseURI, PRBool aAppend,
PRBool aSkinOnly)
{
nsresult rv;
nsCAutoString relativePath;
GetRelativePath(aFile, aBaseURI, relativePath);
nsCAutoString spec;
aOldManifest->GetSpec(spec);
NS_ASSERTION(spec.Last() == '/', "installed-chrome manifest URI doesn't end in a slash! It probably won't work.");
spec.AppendLiteral("contents.rdf");
nsCOMPtr<nsIRDFService> rdfs (do_GetService("@mozilla.org/rdf/rdf-service;1"));
NS_ENSURE_TRUE(rdfs, NS_ERROR_FAILURE);
nsCOMPtr<nsIRDFResource> namearc, platformarc;
rdfs->GetResource(NS_LITERAL_CSTRING(kURICHROME_name),
getter_AddRefs(namearc));
rdfs->GetResource(NS_LITERAL_CSTRING(kURICHROME_platformPackage),
getter_AddRefs(platformarc));
if (!(namearc && platformarc))
return NS_ERROR_FAILURE;
nsCOMPtr<nsIRDFDataSource> ds;
rv = rdfs->GetDataSourceBlocking(spec.get(), getter_AddRefs(ds));
if (NS_FAILED(rv)) {
LogMessage("Failed to load old-style contents.rdf at '%s'.",
spec.get());
return rv;
}
nsCOMPtr<nsIFileURL> fileURL (do_QueryInterface(aFile));
NS_ENSURE_TRUE(fileURL, NS_ERROR_INVALID_ARG);
nsCOMPtr<nsIFile> file;
rv = fileURL->GetFile(getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILocalFile> lfile (do_QueryInterface(file));
NS_ENSURE_TRUE(lfile, NS_ERROR_NO_INTERFACE);
PRFileDesc* fd;
rv = lfile->OpenNSPRFileDesc(aAppend ? kNSPR_APPEND_FLAGS : kNSPR_TRUNCATE_FLAGS,
0664, &fd);
NS_ENSURE_SUCCESS(rv, rv);
if (aAppend)
PR_Write(fd, "\n", 1);
nsCOMPtr<nsIRDFResource> root;
rv = rdfs->GetResource(NS_LITERAL_CSTRING("urn:mozilla:skin:root"),
getter_AddRefs(root));
if (NS_SUCCEEDED(rv))
ProcessProvider(fd, rdfs, ds, root, PR_FALSE, relativePath);
rv = rdfs->GetResource(NS_LITERAL_CSTRING("urn:mozilla:stylesheets"),
getter_AddRefs(root));
if (NS_SUCCEEDED(rv))
ProcessOverlays(fd, ds, root, NS_LITERAL_CSTRING("style"));
if (!aSkinOnly) {
rv = rdfs->GetResource(NS_LITERAL_CSTRING("urn:mozilla:locale:root"),
getter_AddRefs(root));
if (NS_SUCCEEDED(rv))
ProcessProvider(fd, rdfs, ds, root, PR_TRUE, relativePath);
rv = rdfs->GetResource(NS_LITERAL_CSTRING("urn:mozilla:overlays"),
getter_AddRefs(root));
if (NS_SUCCEEDED(rv))
ProcessOverlays(fd, ds, root, NS_LITERAL_CSTRING("overlay"));
/* content packages are easier, but different */
rv = rdfs->GetResource(NS_LITERAL_CSTRING("urn:mozilla:package:root"),
getter_AddRefs(root));
nsCOMPtr<nsISimpleEnumerator> packages;
if (NS_SUCCEEDED(rv))
rv = GetContainerEnumerator(ds, root, getter_AddRefs(packages));
if (NS_SUCCEEDED(rv)) {
PRBool more;
nsCOMPtr<nsISupports> next;
nsCOMPtr<nsIRDFResource> package;
while (NS_SUCCEEDED(packages->HasMoreElements(&more)) && more) {
packages->GetNext(getter_AddRefs(next));
package = do_QueryInterface(next);
if (!package) {
NS_WARNING("Arc from urn:mozilla:package:root points to non-resource node.");
continue;
}
nsCAutoString name;
FollowLiteral(ds, package, namearc, name);
if (name.IsEmpty())
continue;
nsCAutoString isPlatform;
FollowLiteral(ds, package, platformarc, isPlatform);
name.Insert(NS_LITERAL_CSTRING("content\t"), 0);
name.Append('\t');
name.Append(relativePath);
if (!isPlatform.IsEmpty())
name.AppendLiteral("\tplatform");
name.AppendLiteral(NS_LINEBREAK);
PR_Write(fd, name.get(), name.Length());
}
}
}
PR_Close(fd);
return NS_OK;
}
static void
GetResourceName(nsIRDFResource* res, nsACString& result)
{
// we need to get the provider name. Instead of doing something sane,
// we munge the resource URI, looking from the right for colons.
nsCAutoString providerURI;
res->GetValueUTF8(providerURI);
PRInt32 found = providerURI.RFindChar(':');
if (found == kNotFound) {
result.Truncate();
return;
}
result.Assign(Substring(providerURI, found + 1));
}
void
nsChromeRegistry::ProcessProvider(PRFileDesc *fd, nsIRDFService* aRDFs,
nsIRDFDataSource* aDS, nsIRDFResource* aRoot,
PRBool aIsLocale, const nsACString& aBaseURL)
{
NS_NAMED_LITERAL_CSTRING(kSlash, "/");
NS_NAMED_LITERAL_CSTRING(kTab, "\t");
nsresult rv;
nsCOMPtr<nsIRDFResource> packagesarc;
aRDFs->GetResource(NS_LITERAL_CSTRING(kURICHROME_packages),
getter_AddRefs(packagesarc));
if (!packagesarc) return;
nsCOMPtr<nsISimpleEnumerator> providers;
rv = GetContainerEnumerator(aDS, aRoot, getter_AddRefs(providers));
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsISupports> next;
PRBool more;
while (NS_SUCCEEDED(providers->HasMoreElements(&more)) && more) {
providers->GetNext(getter_AddRefs(next));
NS_ASSERTION(next, "GetNext failed after HasMoreElements succeeded.");
nsCOMPtr<nsIRDFResource> provider (do_QueryInterface(next));
if (!provider) {
NS_WARNING("Provider isn't a nsIRDFResource.");
continue;
}
nsCAutoString providerName;
GetResourceName(provider, providerName);
if (providerName.IsEmpty()) {
NS_WARNING("Couldn't calculate resource name.");
continue;
}
nsCOMPtr<nsIRDFResource> packages;
FollowResource(aDS, provider, packagesarc, getter_AddRefs(packages));
if (!packages) {
NS_WARNING("No chrome:packages arc found!");
continue;
}
PRInt32 count;
nsCOMPtr<nsISimpleEnumerator> packageList;
rv = GetContainerEnumerator(aDS, packages, getter_AddRefs(packageList), &count);
if (NS_FAILED(rv)) {
NS_WARNING("chrome:packages was not a sequence.");
continue;
}
nsCOMPtr<nsISupports> nextPackage;
PRBool morePackages;
while (NS_SUCCEEDED(packageList->HasMoreElements(&morePackages)) &&
morePackages) {
packageList->GetNext(getter_AddRefs(nextPackage));
nsCOMPtr<nsIRDFResource> packageRes (do_QueryInterface(nextPackage));
if (!packageRes) {
NS_WARNING("chrome:packages Seq points to a non-resource!");
continue;
}
nsCAutoString packageName;
GetResourceName(packageRes, packageName);
if (packageName.IsEmpty()) {
NS_WARNING("couldn't extract a package name.");
continue;
}
nsCAutoString line;
if (aIsLocale)
line.AppendLiteral("locale\t");
else
line.AppendLiteral("skin\t");
line += packageName + kTab + providerName + kTab + aBaseURL;
if (count > 1) {
line += packageName + kSlash;
}
line.AppendLiteral(NS_LINEBREAK);
PR_Write(fd, line.get(), line.Length());
}
}
}
static void
GetLiteralText(nsIRDFLiteral* lit, nsACString& result)
{
const PRUnichar* value;
lit->GetValueConst(&value);
CopyUTF16toUTF8(value, result);
}
void
nsChromeRegistry::ProcessOverlays(PRFileDesc *fd, nsIRDFDataSource* aDS,
nsIRDFResource* aRoot,
const nsCSubstring& aType)
{
NS_NAMED_LITERAL_CSTRING(kTab, "\t");
NS_NAMED_LITERAL_CSTRING(kLinebreak, NS_LINEBREAK);
nsresult rv;
nsCOMPtr<nsISimpleEnumerator> overlaids;
rv = GetContainerEnumerator(aDS, aRoot, getter_AddRefs(overlaids));
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsISupports> next;
PRBool more;
while (NS_SUCCEEDED(overlaids->HasMoreElements(&more)) && more) {
overlaids->GetNext(getter_AddRefs(next));
NS_ASSERTION(next, "GetNext failed after HasMoreElements succeeded.");
nsCOMPtr<nsIRDFResource> overlaid (do_QueryInterface(next));
if (!overlaid) {
NS_WARNING("Overlay arc is not a nsIRDFResource.");
continue;
}
nsCAutoString overlaidName;
overlaid->GetValueUTF8(overlaidName);
nsCOMPtr<nsISimpleEnumerator> overlays;
rv = GetContainerEnumerator(aDS, overlaid, getter_AddRefs(overlays));
if (NS_FAILED(rv))
continue;
while (NS_SUCCEEDED(overlays->HasMoreElements(&more)) && more) {
overlays->GetNext(getter_AddRefs(next));
NS_ASSERTION(next, "GetNext failed after HasMoreElements succeeded.");
nsCOMPtr<nsIRDFLiteral> overlay (do_QueryInterface(next));
if (!overlay) {
NS_WARNING("Overlay was not an RDF literal.");
continue;
}
nsCAutoString overlayName;
GetLiteralText(overlay, overlayName);
overlayName.Insert(aType + kTab + overlaidName + kTab, 0);
overlayName.Append(kLinebreak);
PR_Write(fd, overlayName.get(), overlayName.Length());
}
}
}
#else // MOZ_XUL
NS_IMETHODIMP
nsChromeRegistry::ProcessContentsManifest(nsIURI* aOldManifest, nsIURI* aFile,
nsIURI* aBaseURI, PRBool aAppend,
PRBool aSkinOnly)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
#endif // MOZ_XUL
nsresult
nsChromeRegistry::ProcessManifest(nsILocalFile* aManifest, PRBool aSkinOnly)
{

View File

@ -72,9 +72,6 @@ missingFileMessage=%S could not load this item because the file %S was missing.
malformedMessage=%S could not install this item because "%S" (provided by the item) is not well-formed or does not exist. Please contact the author about this problem.
malformedTitle=Malformed File
malformedRegistrationTitle=Chrome Registration Failed
malformedRegistrationMessage=%S could not install this item because of a failure in Chrome Registration. Please contact the author about this problem.
invalidFileExtTitle=Invalid File Extension
invalidFileExtMessage="%S" could not be installed because this item has an invalid file extension (%S is not a valid file extension for a %S). Please contact the author about this problem.
missingPackageFilesTitle=Missing Installation Files

View File

@ -86,7 +86,6 @@ const FILE_EXTENSIONS_STARTUP_CACHE = "extensions.cache";
const FILE_EXTENSIONS_LOG = "extensions.log";
const FILE_AUTOREG = ".autoreg";
const FILE_INSTALL_MANIFEST = "install.rdf";
const FILE_CONTENTS_MANIFEST = "contents.rdf";
const FILE_CHROME_MANIFEST = "chrome.manifest";
const UNKNOWN_XPCOM_ABI = "unknownABI";
@ -124,7 +123,6 @@ const CATEGORY_INSTALL_LOCATIONS = "extension-install-locations";
const CATEGORY_UPDATE_PARAMS = "extension-update-params";
const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
const PREFIX_NS_CHROME = "http://www.mozilla.org/rdf/chrome#";
const PREFIX_ITEM_URI = "urn:mozilla:item:";
const PREFIX_EXTENSION = "urn:mozilla:extension:";
const PREFIX_THEME = "urn:mozilla:theme:";
@ -241,10 +239,6 @@ function EM_NS(property) {
return PREFIX_NS_EM + property;
}
function CHROME_NS(property) {
return PREFIX_NS_CHROME + property;
}
function EM_R(property) {
return gRDF.GetResource(EM_NS(property));
}
@ -1539,377 +1533,8 @@ WinRegInstallLocation.prototype = {
#endif
/**
* An object which handles the installation of an Extension.
* @constructor
*/
function Installer(ds, id, installLocation, type) {
this._ds = ds;
this._id = id;
this._type = type;
this._installLocation = installLocation;
}
Installer.prototype = {
// Item metadata
_id: null,
_ds: null,
_installLocation: null,
_metadataDS: null,
/**
* Gets the Install Manifest datasource we are installing from.
*/
get metadataDS() {
if (!this._metadataDS) {
var metadataFile = this._installLocation
.getItemFile(this._id, FILE_INSTALL_MANIFEST);
if (!metadataFile.exists())
return null;
this._metadataDS = getInstallManifest(metadataFile);
if (!this._metadataDS) {
LOG("Installer::install: metadata datasource for extension " +
this._id + " at " + metadataFile.path + " could not be loaded. " +
" Installation will not proceed.");
}
}
return this._metadataDS;
},
/**
* Installs the Extension
* @param file
* A XPI/JAR file to install from. If this is null or does not exist,
* the item is assumed to be an expanded directory, located at the GUID
* key in the supplied Install Location.
*/
installFromFile: function Installer_installFromFile(file) {
// Move files from the staging dir into the extension's final home.
if (file && file.exists()) {
this._installExtensionFiles(file);
}
if (!this.metadataDS)
return;
// Upgrade old-style contents.rdf Chrome Manifests if necessary.
if (this._type == Ci.nsIUpdateItem.TYPE_THEME)
this.upgradeThemeChrome();
else
this.upgradeExtensionChrome();
// Add metadata for the extension to the global extension metadata set
this._ds.addItemMetadata(this._id, this.metadataDS, this._installLocation);
},
/**
* Safely extract the Extension's files into the target folder.
* @param file
* The XPI/JAR file to install from.
*/
_installExtensionFiles: function Installer__installExtensionFiles(file) {
/**
* Callback for |safeInstallOperation| that performs file level installation
* steps for an Extension.
* @param extensionID
* The GUID of the Extension being installed.
* @param installLocation
* The Install Location where the Extension is being installed.
* @param xpiFile
* The source XPI file that contains the Extension.
*/
function extractExtensionFiles(extensionID, installLocation, xpiFile) {
// Create a logger to log install operations for uninstall. This must be
// created in the |safeInstallOperation| callback, since it creates a file
// in the target directory. If we do this outside of the callback, we may
// be clobbering a file we should not be.
var zipReader = getZipReaderForFile(xpiFile);
// create directories first
var entries = zipReader.findEntries("*/");
while (entries.hasMore()) {
var entryName = entries.getNext();
var target = installLocation.getItemFile(extensionID, entryName);
if (!target.exists()) {
try {
target.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
}
catch (e) {
ERROR("extractExtensionsFiles: failed to create target directory for extraction " +
" file = " + target.path + ", exception = " + e + "\n");
}
}
}
entries = zipReader.findEntries(null);
while (entries.hasMore()) {
var entryName = entries.getNext();
target = installLocation.getItemFile(extensionID, entryName);
if (target.exists())
continue;
try {
target.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
}
catch (e) {
ERROR("extractExtensionsFiles: failed to create target file for extraction " +
" file = " + target.path + ", exception = " + e + "\n");
}
zipReader.extract(entryName, target);
}
zipReader.close();
}
/**
* Callback for |safeInstallOperation| that performs file level installation
* steps for a Theme.
* @param id
* The GUID of the Theme being installed.
* @param installLocation
* The Install Location where the Theme is being installed.
* @param jarFile
* The source JAR file that contains the Theme.
*/
function extractThemeFiles(id, installLocation, jarFile) {
var themeDirectory = installLocation.getItemLocation(id);
var zipReader = getZipReaderForFile(jarFile);
// The only critical file is the install.rdf and we would not have
// gotten this far without one.
var rootFiles = [FILE_INSTALL_MANIFEST, FILE_CHROME_MANIFEST,
"preview.png", "icon.png"];
for (var i = 0; i < rootFiles.length; ++i) {
try {
var target = installLocation.getItemFile(id, rootFiles[i]);
zipReader.extract(rootFiles[i], target);
}
catch (e) {
}
}
var manifestFile = installLocation.getItemFile(id, FILE_CHROME_MANIFEST);
// new theme structure requires a chrome.manifest file
if (manifestFile.exists()) {
var entries = zipReader.findEntries(DIR_CHROME + "/*");
while (entries.hasMore()) {
var entryName = entries.getNext();
if (entryName.charAt(entryName.length - 1) == "/")
continue;
target = installLocation.getItemFile(id, entryName);
try {
target.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
}
catch (e) {
ERROR("extractThemeFiles: failed to create target file for extraction " +
" file = " + target.path + ", exception = " + e + "\n");
}
zipReader.extract(entryName, target);
}
zipReader.close();
}
else { // old theme structure requires only an install.rdf
try {
var contentsManifestFile = installLocation.getItemFile(id, FILE_CONTENTS_MANIFEST);
contentsManifestFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
zipReader.extract(FILE_CONTENTS_MANIFEST, contentsManifestFile);
}
catch (e) {
zipReader.close();
ERROR("extractThemeFiles: failed to extract contents.rdf: " + target.path);
throw e; // let the safe-op clean up
}
zipReader.close();
var chromeDir = installLocation.getItemFile(id, DIR_CHROME);
try {
jarFile.copyTo(chromeDir, jarFile.leafName);
}
catch (e) {
ERROR("extractThemeFiles: failed to copy theme JAR file to: " + chromeDir.path);
throw e; // let the safe-op clean up
}
if (!installer.metadataDS && installer._type == Ci.nsIUpdateItem.TYPE_THEME) {
var themeName = extensionStrings.GetStringFromName("incompatibleThemeName");
if (contentsManifestFile && contentsManifestFile.exists()) {
var contentsManifest = gRDF.GetDataSourceBlocking(getURLSpecFromFile(contentsManifestFile));
try {
var ctr = getContainer(contentsManifest,
gRDF.GetResource("urn:mozilla:skin:root"));
var elts = ctr.GetElements();
var nameArc = gRDF.GetResource(CHROME_NS("displayName"));
while (elts.hasMoreElements()) {
var elt = elts.getNext().QueryInterface(Ci.nsIRDFResource);
themeName = stringData(contentsManifest.GetTarget(elt, nameArc, true));
if (themeName)
break;
}
}
catch (e) {
themeName = extensionStrings.GetStringFromName("incompatibleThemeName");
}
}
showIncompatibleError({ name: themeName, version: "",
type: Ci.nsIUpdateItem.TYPE_THEME });
LOG("Theme JAR file: " + jarFile.leafName + " contains an Old-Style " +
"Theme that is not compatible with this version of the software.");
throw new Error("Old Theme"); // let the safe-op clean up
}
}
}
var installer = this;
var callback = extractExtensionFiles;
if (this._type == Ci.nsIUpdateItem.TYPE_THEME)
callback = extractThemeFiles;
safeInstallOperation(this._id, this._installLocation,
{ callback: callback, data: file });
},
/**
* Upgrade contents.rdf Chrome Manifests used by this Theme to the new
* chrome.manifest format if necessary.
*/
upgradeThemeChrome: function Installer_upgradeThemeChrome() {
// Use the Chrome Registry API to install the theme there
var cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(Ci.nsIToolkitChromeRegistry);
var manifestFile = this._installLocation.getItemFile(this._id, FILE_CHROME_MANIFEST);
if (manifestFile.exists() ||
this._id == stripPrefix(RDFURI_DEFAULT_THEME, PREFIX_ITEM_URI))
return;
try {
// creates a chrome manifest for themes
var manifestURI = getURIFromFile(manifestFile);
var chromeDir = this._installLocation.getItemFile(this._id, DIR_CHROME);
// We're relying on the fact that there is only one JAR file
// in the "chrome" directory. This is a hack, but it works.
var entries = chromeDir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
var jarFile = entries.nextFile;
if (jarFile) {
var jarFileURI = getURIFromFile(jarFile);
var contentsURI = newURI("jar:" + jarFileURI.spec + "!/");
var contentsFile = this._installLocation.getItemFile(this._id, FILE_CONTENTS_MANIFEST);
var contentsFileURI = getURIFromFile(contentsFile.parent);
cr.processContentsManifest(contentsFileURI, manifestURI, contentsURI, false, true);
}
entries.close();
contentsFile.remove(false);
}
catch (e) {
// Failed to register chrome, for any number of reasons - non-existent
// contents.rdf file at the location specified, malformed contents.rdf,
// etc. Set the pending op to be OP_NEEDS_UNINSTALL so that the
// extension is uninstalled properly during the subsequent uninstall
// pass in |ExtensionManager::_finishOperations|
ERROR("upgradeThemeChrome: failed for theme " + this._id + " - why " +
"not convert to the new chrome.manifest format while you're at it? " +
"Failure exception: " + e);
showMessage("malformedRegistrationTitle", [], "malformedRegistrationMessage",
[BundleManager.appName]);
var stageFile = this._installLocation.getStageFile(this._id);
if (stageFile)
this._installLocation.removeFile(stageFile);
StartupCache.put(this._installLocation, this._id, OP_NEEDS_UNINSTALL, true);
StartupCache.write();
}
},
/**
* Upgrade contents.rdf Chrome Manifests used by this Extension to the new
* chrome.manifest format if necessary.
*/
upgradeExtensionChrome: function Installer_upgradeExtensionChrome() {
// If the extension is aware of the new flat chrome manifests and has
// included one, just use it instead of generating one from the
// install.rdf/contents.rdf data.
var manifestFile = this._installLocation.getItemFile(this._id, FILE_CHROME_MANIFEST);
if (manifestFile.exists())
return;
try {
// Enumerate the metadata datasource files collection and register chrome
// for each file, calling _registerChrome for each.
var chromeDir = this._installLocation.getItemFile(this._id, DIR_CHROME);
if (!manifestFile.parent.exists())
return;
// Even if an extension doesn't have any chrome, we generate an empty
// manifest file so that we don't try to upgrade from the "old-style"
// chrome manifests at every startup.
manifestFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
var manifestURI = getURIFromFile(manifestFile);
var files = this.metadataDS.GetTargets(gInstallManifestRoot, EM_R("file"), true);
while (files.hasMoreElements()) {
var file = files.getNext().QueryInterface(Ci.nsIRDFResource);
var chromeFile = chromeDir.clone();
var fileName = file.Value.substr("urn:mozilla:extension:file:".length, file.Value.length);
chromeFile.append(fileName);
var fileURLSpec = getURLSpecFromFile(chromeFile);
if (!chromeFile.isDirectory()) {
var zipReader = getZipReaderForFile(chromeFile);
fileURLSpec = "jar:" + fileURLSpec + "!/";
var contentsFile = this._installLocation.getItemFile(this._id, FILE_CONTENTS_MANIFEST);
contentsFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
}
var providers = [EM_R("package"), EM_R("skin"), EM_R("locale")];
for (var i = 0; i < providers.length; ++i) {
var items = this.metadataDS.GetTargets(file, providers[i], true);
while (items.hasMoreElements()) {
var item = items.getNext().QueryInterface(Ci.nsIRDFLiteral);
var fileURI = newURI(fileURLSpec + item.Value);
// Extract the contents.rdf files instead of opening them inside of
// the jar. This prevents the jar from being cached by the zip
// reader which will keep the jar in use and prevent deletion.
if (zipReader) {
zipReader.extract(item.Value + FILE_CONTENTS_MANIFEST, contentsFile);
var contentsFileURI = getURIFromFile(contentsFile.parent);
}
else
contentsFileURI = fileURI;
var cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(Ci.nsIToolkitChromeRegistry);
cr.processContentsManifest(contentsFileURI, manifestURI, fileURI, true, false);
}
}
if (zipReader) {
zipReader.close();
zipReader = null;
contentsFile.remove(false);
}
}
}
catch (e) {
// Failed to register chrome, for any number of reasons - non-existent
// contents.rdf file at the location specified, malformed contents.rdf,
// etc. Set the pending op to be OP_NEEDS_UNINSTALL so that the
// extension is uninstalled properly during the subsequent uninstall
// pass in |ExtensionManager::_finishOperations|
ERROR("upgradeExtensionChrome: failed for extension " + this._id + " - why " +
"not convert to the new chrome.manifest format while you're at it? " +
"Failure exception: " + e);
showMessage("malformedRegistrationTitle", [], "malformedRegistrationMessage",
[BundleManager.appName]);
var stageFile = this._installLocation.getStageFile(this._id);
if (stageFile)
this._installLocation.removeFile(stageFile);
StartupCache.put(this._installLocation, this._id, OP_NEEDS_UNINSTALL, true);
StartupCache.write();
}
}
};
/**
* Safely attempt to perform a caller-defined install operation for a given
* item ID. Using aggressive success-safety checks, this function will attempt
* Safely attempt to install or uninstall a given item ID in an install
* location. Using aggressive success-safety checks, this function will attempt
* to move an existing location for an item aside and then allow installation
* into the appropriate folder. If any operation fails the installation will
* abort and roll back from the moved-aside old version.
@ -1917,16 +1542,10 @@ Installer.prototype = {
* The GUID of the item to perform the operation on.
* @param installLocation
* The Install Location where the item is installed.
* @param installCallback
* A caller supplied JS object with the following properties:
* "data" A data parameter to be passed to the callback.
* "callback" A function to perform the install operation. This
* function is passed three parameters:
* 1. The GUID of the item being operated on.
* 2. The Install Location where the item is installed.
* 3. The "data" parameter on the installCallback object.
* @param file
* An xpi file to install to the location or null to just uninstall
*/
function safeInstallOperation(itemID, installLocation, installCallback) {
function safeInstallOperation(itemID, installLocation, file) {
var movedFiles = [];
/**
@ -2019,6 +1638,53 @@ function safeInstallOperation(itemID, installLocation, installCallback) {
}
}
/**
* Extracts an XPI's files into the item's directory.
* @param itemID
* The ID of the item being installed.
* @param installLocation
* The install location where the item is being installed.
* @param xpiFile
* The source XPI file that contains the item.
*/
function extractFiles(itemID, installLocation, xpiFile) {
var zipReader = getZipReaderForFile(xpiFile);
// create directories first
var entries = zipReader.findEntries("*/");
while (entries.hasMore()) {
var entryName = entries.getNext();
var target = installLocation.getItemFile(itemID, entryName);
if (!target.exists()) {
try {
target.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
}
catch (e) {
ERROR("extractFiles: failed to create target directory for extraction " +
"file = " + target.path + ", exception = " + e + "\n");
}
}
}
entries = zipReader.findEntries(null);
while (entries.hasMore()) {
var entryName = entries.getNext();
target = installLocation.getItemFile(itemID, entryName);
if (target.exists())
continue;
try {
target.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
}
catch (e) {
ERROR("extractFiles: failed to create target file for extraction " +
"file = " + target.path + ", exception = " + e + "\n");
}
zipReader.extract(entryName, target);
}
zipReader.close();
}
if (!installLocation.itemIsManagedIndependently(itemID)) {
var itemLocation = installLocation.getItemLocation(itemID);
if (itemLocation.exists()) {
@ -2088,28 +1754,30 @@ function safeInstallOperation(itemID, installLocation, installCallback) {
}
}
// Now tell the client to do their stuff.
try {
installCallback.callback(itemID, installLocation, installCallback.data);
}
catch (e) {
// This means the install operation failed. Remove everything and roll back.
ERROR("safeInstallOperation: install operation (caller-supplied callback) failed, " +
"rolling back file moves and aborting installation.");
if (file) {
// Extract the xpi's files into the new directory
try {
// Us-generated. Safe.
removeDirRecursive(itemLocation);
extractFiles(itemID, installLocation, file);
}
catch (e) {
ERROR("safeInstallOperation: failed to remove the folder we failed to install " +
"an item into: " + itemLocation.path + " -- There is not much to suggest " +
"here... maybe restart and try again?");
// This means the install operation failed. Remove everything and roll back.
ERROR("safeInstallOperation: file extraction failed, " +
"rolling back file moves and aborting installation.");
try {
// Us-generated. Safe.
removeDirRecursive(itemLocation);
}
catch (e) {
ERROR("safeInstallOperation: failed to remove the folder we failed to install " +
"an item into: " + itemLocation.path + " -- There is not much to suggest " +
"here... maybe restart and try again?");
cleanUpTrash(itemLocationTrash);
throw e;
}
rollbackMove();
cleanUpTrash(itemLocationTrash);
throw e;
}
rollbackMove();
cleanUpTrash(itemLocationTrash);
throw e;
}
// Now, and only now - after everything else has succeeded (against all odds!)
@ -3294,107 +2962,6 @@ ExtensionManager.prototype = {
return isDirty;
},
/**
* Upgrades contents.rdf files to chrome.manifest files for any existing
* Extensions and Themes.
* @returns true if actions were performed that require a restart, false
* otherwise.
*/
_upgradeChrome: function EM__upgradeChrome() {
if (inSafeMode())
return false;
var checkForNewChrome = false;
var ds = this.datasource;
// If we have extensions that were installed before the new flat chrome
// manifests, and are still valid, we need to manually create the flat
// manifest files.
var extensions = this._getActiveItems(Ci.nsIUpdateItem.TYPE_EXTENSION +
Ci.nsIUpdateItem.TYPE_LOCALE);
for (var i = 0; i < extensions.length; ++i) {
var e = extensions[i];
var itemLocation = e.location.getItemLocation(e.id);
var manifest = itemLocation.clone();
manifest.append(FILE_CHROME_MANIFEST);
if (!manifest.exists()) {
var installRDF = itemLocation.clone();
installRDF.append(FILE_INSTALL_MANIFEST);
var installLocation = this.getInstallLocation(e.id);
if (installLocation && installRDF.exists()) {
var itemLocation = installLocation.getItemLocation(e.id);
if (itemLocation.exists() && itemLocation.isDirectory()) {
var installer = new Installer(ds, e.id, installLocation,
Ci.nsIUpdateItem.TYPE_EXTENSION);
installer.upgradeExtensionChrome();
}
}
else {
ds.removeItemMetadata(e.id);
ds.removeItemFromContainer(e.id);
}
checkForNewChrome = true;
}
}
var themes = this._getActiveItems(Ci.nsIUpdateItem.TYPE_THEME);
// If we have themes that were installed before the new flat chrome
// manifests, and are still valid, we need to manually create the flat
// manifest files.
for (i = 0; i < themes.length; ++i) {
var item = themes[i];
var itemLocation = item.location.getItemLocation(item.id);
var manifest = itemLocation.clone();
manifest.append(FILE_CHROME_MANIFEST);
if (manifest.exists() ||
item.id == stripPrefix(RDFURI_DEFAULT_THEME, PREFIX_ITEM_URI))
continue;
var entries;
try {
var manifestURI = getURIFromFile(manifest);
var chromeDir = itemLocation.clone();
chromeDir.append(DIR_CHROME);
if (!chromeDir.exists() || !chromeDir.isDirectory()) {
ds.removeItemMetadata(item.id);
ds.removeItemFromContainer(item.id);
continue;
}
// We're relying on the fact that there is only one JAR file
// in the "chrome" directory. This is a hack, but it works.
entries = chromeDir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
var jarFile = entries.nextFile;
if (jarFile) {
var jarFileURI = getURIFromFile(jarFile);
var contentsURI = newURI("jar:" + jarFileURI.spec + "!/");
// Use the Chrome Registry API to install the theme there
var cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(Ci.nsIToolkitChromeRegistry);
cr.processContentsManifest(contentsURI, manifestURI, contentsURI, false, true);
}
entries.close();
}
catch (e) {
ERROR("_upgradeChrome: failed to upgrade contents manifest for " +
"theme: " + item.id + ", exception: " + e + "... The theme will be " +
"disabled.");
this._appDisableItem(item.id);
}
finally {
try {
entries.close();
}
catch (e) {
}
}
checkForNewChrome = true;
}
return checkForNewChrome;
},
_checkForUncoveredItem: function EM__checkForUncoveredItem(id) {
var ds = this.datasource;
var oldLocation = this.getInstallLocation(id);
@ -3559,14 +3126,6 @@ ExtensionManager.prototype = {
}
while (PendingOperations.size > 0);
// Upgrade contents.rdf files to the new chrome.manifest format for
// existing Extensions and Themes
if (this._upgradeChrome()) {
var cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(Ci.nsIChromeRegistry);
cr.checkForNewChrome();
}
// If no additional restart is required, it implies that there are
// no new components that need registering so we can inform the app
// not to do any extra startup checking next time round.
@ -4750,10 +4309,23 @@ ExtensionManager.prototype = {
if (!file && "stageFile" in installLocation)
file = installLocation.getStageFile(id);
// If |file| is null or does not exist, the installer assumes the item is
// a dropped-in directory.
var installer = new Installer(this.datasource, id, installLocation, type);
installer.installFromFile(file);
// If there is a staged file then we must extract it to the correct place,
// otherwise we are dealing with a dropped-in directory.
if (file && file.exists())
safeInstallOperation(id, installLocation, file);
var metadataFile = installLocation.getItemFile(id, FILE_INSTALL_MANIFEST);
if (metadataFile.exists()) {
var metadataDS = getInstallManifest(metadataFile);
if (metadataDS) {
// Add metadata for the item to the extensions datasource
this.datasource.addItemMetadata(id, metadataDS, installLocation);
}
}
else {
LOG("_finalizeInstall: install manifest for extension " + id + " at " +
metadataFile.path + " could not be loaded. Add-on is not usable.");
}
// If the file was staged, we must clean it up ourselves, otherwise the
// EM caller is responsible for doing so (e.g. XPInstall)
@ -4819,10 +4391,9 @@ ExtensionManager.prototype = {
var installLocation = this.getInstallLocation(id);
if (!installLocation.itemIsManagedIndependently(id)) {
try {
// Having a callback that does nothing just causes the directory to be
// Passing null for the file to install will just cause the directory
// removed.
safeInstallOperation(id, installLocation,
{ data: null, callback: function() { } });
safeInstallOperation(id, installLocation, null);
}
catch (e) {
ERROR("_finalizeUninstall: failed to remove directory for item: " + id +