/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et tw=78: */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Original Author: David W. Hyatt (hyatt@netscape.com)
 *   Gagan Saksena <gagan@netscape.com>
 *   Benjamin Smedberg <bsmedberg@covad.net>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include <string.h>
#include "nsArrayEnumerator.h"
#include "nsCOMPtr.h"
#include "nsIChromeRegistry.h"
#include "nsChromeRegistry.h"
#include "nsChromeUIDataSource.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFObserver.h"
#include "nsIRDFRemoteDataSource.h"
#include "nsIRDFXMLSink.h"
#include "rdf.h"
#include "nsIServiceManager.h"
#include "nsIRDFService.h"
#include "nsRDFCID.h"
#include "nsIRDFResource.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFContainer.h"
#include "nsIRDFContainerUtils.h"
#include "nsHashtable.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsXPIDLString.h"
#include "nsISimpleEnumerator.h"
#include "nsNetUtil.h"
#include "nsIFileChannel.h"
#include "nsIXBLService.h"
#include "nsIDOMWindowInternal.h"
#include "nsIDOMWindowCollection.h"
#include "nsIDOMLocation.h"
#include "nsIWindowMediator.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIStyleSheet.h"
#include "nsICSSStyleSheet.h"
#include "nsIPresShell.h"
#include "nsIDocShell.h"
#include "nsISupportsArray.h"
#include "nsIDocumentObserver.h"
#include "nsIIOService.h"
#include "nsLayoutCID.h"
#include "prio.h"
#include "nsInt64.h"
#include "nsEscape.h"
#include "nsIDirectoryService.h"
#include "nsILocalFile.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsIObserverService.h"
#include "nsIDOMElement.h"
#include "nsIDOMWindowCollection.h"
#include "nsIAtom.h"
#include "nsStaticAtom.h"
#include "nsNetCID.h"
#include "nsIJARURI.h"
#include "nsIFileURL.h"
#include "nsIXPConnect.h"
#include "nsPresShellIterator.h"

static char kChromePrefix[] = "chrome://";
nsIAtom* nsChromeRegistry::sCPrefix; // atom for "c"

#define kChromeFileName           NS_LITERAL_CSTRING("chrome.rdf")
#define kInstalledChromeFileName  NS_LITERAL_CSTRING("installed-chrome.txt")

static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kRDFXMLDataSourceCID, NS_RDFXMLDATASOURCE_CID);
static NS_DEFINE_CID(kRDFContainerUtilsCID,      NS_RDFCONTAINERUTILS_CID);
static NS_DEFINE_CID(kCSSLoaderCID, NS_CSS_LOADER_CID);

class nsChromeRegistry;

nsIChromeRegistry* gChromeRegistry = nsnull;

#define CHROME_URI "http://www.mozilla.org/rdf/chrome#"

DEFINE_RDF_VOCAB(CHROME_URI, CHROME, selectedSkin);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, selectedLocale);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, baseURL);
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, image);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, locType);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, allowScripts);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, hasOverlays);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, hasStylesheets);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, skinVersion);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, localeVersion);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, packageVersion);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, disabled);
DEFINE_RDF_VOCAB(CHROME_URI, CHROME, xpcNativeWrappers);

////////////////////////////////////////////////////////////////////////////////

nsChromeRegistry::nsChromeRegistry() : mRDFService(nsnull),
                                       mRDFContainerUtils(nsnull),
                                       mInstallInitialized(PR_FALSE),
                                       mProfileInitialized(PR_FALSE),
                                       mRuntimeProvider(PR_FALSE),
                                       mBatchInstallFlushes(PR_FALSE),
                                       mSearchedForOverride(PR_FALSE),
                                       mLegacyOverlayinfo(PR_FALSE)
{
  mDataSourceTable = nsnull;
}


static PRBool PR_CALLBACK
DatasourceEnumerator(nsHashKey *aKey, void *aData, void* closure)
{
  if (!closure || !aData)
    return PR_FALSE;

  nsIRDFCompositeDataSource* compositeDS = (nsIRDFCompositeDataSource*) closure;

  nsCOMPtr<nsISupports> supports = (nsISupports*)aData;

  nsCOMPtr<nsIRDFDataSource> dataSource = do_QueryInterface(supports);
  if (!dataSource)
    return PR_FALSE;

#ifdef DEBUG
  nsresult rv =
#endif
  compositeDS->RemoveDataSource(dataSource);
  NS_ASSERTION(NS_SUCCEEDED(rv), "failed to RemoveDataSource");
  return PR_TRUE;
}


nsChromeRegistry::~nsChromeRegistry()
{
  gChromeRegistry = nsnull;
  
  if (mDataSourceTable) {
      mDataSourceTable->Enumerate(DatasourceEnumerator, mChromeDataSource);
      delete mDataSourceTable;
  }

  NS_IF_RELEASE(mRDFService);
  NS_IF_RELEASE(mRDFContainerUtils);

}

NS_IMPL_THREADSAFE_ISUPPORTS6(nsChromeRegistry,
                              nsIChromeRegistry,
                              nsIXULChromeRegistry,
                              nsIChromeRegistrySea,
                              nsIXULOverlayProvider,
                              nsIObserver,
                              nsISupportsWeakReference)

////////////////////////////////////////////////////////////////////////////////
// nsIChromeRegistry methods:

nsresult
nsChromeRegistry::Init()
{
  // these atoms appear in almost every chrome registry manifest.rdf
  // in some form or another. making static atoms prevents the atoms
  // from constantly being created/destroyed during parsing
  
  static const nsStaticAtom atoms[] = {
    { "c",             &sCPrefix },
    { "chrome",        nsnull },
    { "NC",            nsnull },
    { "baseURL",       nsnull},
    { "allowScripts",  nsnull },
    { "skinVersion",   nsnull },
    { "package",       nsnull },
    { "packages",      nsnull },
    { "locType",       nsnull },
    { "displayName",   nsnull },
    { "author",        nsnull },
    { "localeVersion", nsnull },
    { "localeType",    nsnull },
    { "selectedLocale", nsnull },
    { "selectedSkin",  nsnull },
    { "hasOverlays",   nsnull },
    { "xpcNativeWrappers", nsnull },
    { "previewURL", nsnull },
  };

  NS_RegisterStaticAtoms(atoms, NS_ARRAY_LENGTH(atoms));
  
  gChromeRegistry = this;
  
  nsresult rv;
  rv = CallGetService(kRDFServiceCID, &mRDFService);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = CallGetService(kRDFContainerUtilsCID, &mRDFContainerUtils);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_selectedSkin),
                                getter_AddRefs(mSelectedSkin));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_selectedLocale),
                                getter_AddRefs(mSelectedLocale));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_baseURL),
                                getter_AddRefs(mBaseURL));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_packages),
                                getter_AddRefs(mPackages));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_package),
                                getter_AddRefs(mPackage));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_name),
                                getter_AddRefs(mName));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_image),
                                getter_AddRefs(mImage));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_locType),
                                getter_AddRefs(mLocType));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_allowScripts),
                                getter_AddRefs(mAllowScripts));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_hasOverlays),
                                getter_AddRefs(mHasOverlays));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_hasStylesheets),
                                getter_AddRefs(mHasStylesheets));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_skinVersion),
                                getter_AddRefs(mSkinVersion));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_localeVersion),
                                getter_AddRefs(mLocaleVersion));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_packageVersion),
                                getter_AddRefs(mPackageVersion));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_disabled),
                                getter_AddRefs(mDisabled));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  rv = mRDFService->GetResource(nsDependentCString(kURICHROME_xpcNativeWrappers),
                                getter_AddRefs(mXPCNativeWrappers));
  NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource");

  nsCOMPtr<nsIObserverService> observerService =
           do_GetService("@mozilla.org/observer-service;1", &rv);
  if (observerService) {
    observerService->AddObserver(this, "profile-before-change", PR_TRUE);
    observerService->AddObserver(this, "profile-after-change", PR_TRUE);
  }

  CheckForNewChrome();
  // CheckForNewChrome suppresses these during chrome registration
  FlagXPCNativeWrappers();

  return NS_OK;
}


static nsresult
SplitURL(nsIURI *aChromeURI, nsCString& aPackage, nsCString& aProvider, nsCString& aFile,
         PRBool *aModified = nsnull)
{
  // Splits a "chrome:" URL into its package, provider, and file parts.
  // Here are the current portions of a
  // chrome: url that make up the chrome-
  //
  //     chrome://global/skin/foo?bar
  //     \------/ \----/\---/ \-----/
  //         |       |     |     |
  //         |       |     |     `-- RemainingPortion
  //         |       |     |
  //         |       |     `-- Provider
  //         |       |
  //         |       `-- Package
  //         |
  //         `-- Always "chrome://"
  //
  //

  nsresult rv;

  nsCAutoString str;
  rv = aChromeURI->GetSpec(str);
  if (NS_FAILED(rv)) return rv;

  // We only want to deal with "chrome:" URLs here. We could return
  // an error code if the URL isn't properly prefixed here...
  if (PL_strncmp(str.get(), kChromePrefix, sizeof(kChromePrefix) - 1) != 0)
    return NS_ERROR_INVALID_ARG;

  // Cull out the "package" string; e.g., "navigator"
  aPackage = str.get() + sizeof(kChromePrefix) - 1;

  PRInt32 idx;
  idx = aPackage.FindChar('/');
  if (idx < 0)
    return NS_OK;

  // Cull out the "provider" string; e.g., "content"
  aPackage.Right(aProvider, aPackage.Length() - (idx + 1));
  aPackage.Truncate(idx);

  idx = aProvider.FindChar('/');
  if (idx < 0) {
    // Force the provider to end with a '/'
    idx = aProvider.Length();
    aProvider.Append('/');
  }

  // Cull out the "file"; e.g., "navigator.xul"
  aProvider.Right(aFile, aProvider.Length() - (idx + 1));
  aProvider.Truncate(idx);

  PRBool nofile = aFile.IsEmpty();
  if (nofile) {
    // If there is no file, then construct the default file
    aFile = aPackage;

    if (aProvider.Equals("content")) {
      aFile += ".xul";
    }
    else if (aProvider.Equals("skin")) {
      aFile += ".css";
    }
    else if (aProvider.Equals("locale")) {
      aFile += ".dtd";
    }
    else {
      NS_ERROR("unknown provider");
      return NS_ERROR_FAILURE;
    }
  } else {
    // Protect against URIs containing .. that reach up out of the
    // chrome directory to grant chrome privileges to non-chrome files.
    // XXX: If we find %-escaped dot or % in a chrome URI we assume
    // someone is trying to trick us.
    const char* pos = aFile.BeginReading();
    const char* end = aFile.EndReading();
    while (pos < end) {
      switch (*pos) {
        case ':':
          return NS_ERROR_FAILURE;
        case '.':
          if (pos[1] == '.')
            return NS_ERROR_FAILURE;
          break;
        case '%':
          // chrome: URIs with escaped dots are trying to trick us.
          // Double-escapes (%25) doubly so
          if (pos[1] == '2' &&
               ( pos[2] == 'e' || pos[2] == 'E' || 
                 pos[2] == '5' ))
            return NS_ERROR_FAILURE;
          break;
        case '?':
        case '#':
          // leave any query or ref section alone
          pos = end;
          continue;
      }
      ++pos;
    }
  }
  if (aModified)
    *aModified = nofile;
  return NS_OK;
}

static nsresult
GetBaseURLFile(const nsACString& aBaseURL, nsIFile** aFile)
{
  NS_ENSURE_ARG_POINTER(aFile);
  *aFile = nsnull;

  nsresult rv;
  nsCOMPtr<nsIIOService> ioServ(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsIURI> uri;
  rv = ioServ->NewURI(aBaseURL, nsnull, nsnull, getter_AddRefs(uri));
  if (NS_FAILED(rv)) return rv;

  // Loop, jar: URIs can nest (e.g. jar:jar:A.jar!B.jar!C.xml).
  // Often, however, we have jar:resource:/chrome/A.jar!C.xml.
  nsCOMPtr<nsIJARURI> jarURI;
  while ((jarURI = do_QueryInterface(uri)) != nsnull)
    jarURI->GetJARFile(getter_AddRefs(uri));

  // Here we must have a URL of the form resource:/chrome/A.jar
  // or file:/some/path/to/A.jar.
  nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(uri));
  if (fileURL) {
    nsCOMPtr<nsIFile> file;
    fileURL->GetFile(getter_AddRefs(file));
    if (file) {
      NS_ADDREF(*aFile = file);
      return NS_OK;
    }
  }
  NS_ERROR("GetBaseURLFile() failed. Remote chrome?");
  return NS_ERROR_FAILURE;
}

nsresult
nsChromeRegistry::Canonify(nsIURI* aChromeURI)
{
  // Canonicalize 'chrome:' URLs. We'll take any 'chrome:' URL
  // without a filename, and change it to a URL -with- a filename;
  // e.g., "chrome://navigator/content" to
  // "chrome://navigator/content/navigator.xul".
  if (! aChromeURI)
      return NS_ERROR_NULL_POINTER;

  PRBool modified = PR_TRUE; // default is we do canonification
  nsCAutoString package, provider, file;
  nsresult rv;
  rv = SplitURL(aChromeURI, package, provider, file, &modified);
  if (NS_FAILED(rv))
    return rv;

  if (!modified)
    return NS_OK;

  nsCAutoString canonical( kChromePrefix );
  canonical += package;
  canonical += "/";
  canonical += provider;
  canonical += "/";
  canonical += file;

  return aChromeURI->SetSpec(canonical);
}

NS_IMETHODIMP
nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURL, nsIURI* *aResult)
{
  nsresult rv = NS_OK;
  NS_ENSURE_ARG_POINTER(aChromeURL);

  // No need to canonify as the SplitURL() that we
  // do is the equivalent of canonification without modifying
  // aChromeURL

  // Obtain the package, provider and remaining from the URL
  nsCAutoString package, provider, remaining;

  rv = SplitURL(aChromeURL, package, provider, remaining);
  if (NS_FAILED(rv)) return rv;

  // Try for the profile data source first because it
  // will load the install data source as well.
  if (!mProfileInitialized) {
    rv = LoadProfileDataSource();
    if (NS_FAILED(rv)) return rv;
  }
  if (!mInstallInitialized) {
    rv = LoadInstallDataSource();
    if (NS_FAILED(rv)) return rv;
  }

  nsCAutoString finalURL;

  rv = GetOverrideURL(package, provider, remaining, finalURL);
  if (NS_SUCCEEDED(rv))
    return NS_OK;
  
  rv = GetBaseURL(package, provider, finalURL);
#ifdef DEBUG
  if (NS_FAILED(rv)) {
    nsCAutoString msg("chrome: failed to get base url");
    nsCAutoString url;
    rv = aChromeURL->GetSpec(url);
    if (NS_SUCCEEDED(rv)) {
      msg += " for ";
      msg += url.get();
    }
    msg += " -- using wacky default";
    NS_WARNING(msg.get());
  }
#endif
  if (finalURL.IsEmpty()) {
    // hard-coded fallback
    if (provider.Equals("skin")) {
      finalURL = "resource:/chrome/skins/classic/";
    }
    else if (provider.Equals("locale")) {
      finalURL = "resource:/chrome/locales/en-US/";
    }
    else if (package.Equals("aim")) {
      finalURL = "resource:/chrome/packages/aim/";
    }
    else if (package.Equals("messenger")) {
      finalURL = "resource:/chrome/packages/messenger/";
    }
    else if (package.Equals("global")) {
      finalURL = "resource:/chrome/packages/widget-toolkit/";
    }
    else {
      finalURL = "resource:/chrome/packages/core/";
    }
  }

  finalURL.Append(remaining);

  return NS_NewURI(aResult, finalURL);
}

nsresult
nsChromeRegistry::GetBaseURL(const nsACString& aPackage,
                             const nsACString& aProvider,
                             nsACString& aBaseURL)
{
  nsCOMPtr<nsIRDFResource> resource;

  nsCAutoString resourceStr("urn:mozilla:package:");
  resourceStr += aPackage;

  // Obtain the resource.
  nsresult rv = NS_OK;
  nsCOMPtr<nsIRDFResource> packageResource;
  rv = GetResource(resourceStr, getter_AddRefs(packageResource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the package resource.");
    return rv;
  }

  // Follow the "selectedSkin" or "selectedLocale" arc.
  nsCOMPtr<nsIRDFResource> arc;
  if (aProvider.EqualsLiteral("skin")) {
    arc = mSelectedSkin;
  }
  else if (aProvider.EqualsLiteral("locale")) {
    arc = mSelectedLocale;
  }
  else
    // We're a package.
    resource = packageResource;

  if (arc) {

    nsCOMPtr<nsIRDFNode> selectedProvider;
    if (NS_FAILED(rv = mChromeDataSource->GetTarget(packageResource, arc, PR_TRUE, getter_AddRefs(selectedProvider)))) {
      NS_ERROR("Unable to obtain the provider.");
      return rv;
    }

    resource = do_QueryInterface(selectedProvider);

    if (resource) {
      PRBool providerOK;
      rv = VerifyCompatibleProvider(packageResource, resource, arc, &providerOK);
      if (NS_FAILED(rv)) return rv;
      if (!providerOK) {
        // We had a selection but it was incompatible or not present.
        // That selection may have come from the profile, so check only
        // the part of the datasource in the install.  (If this succeeds,
        // we won't remember the choice, either, in case the user
        // switches back to a build where this theme does work.)
        if (NS_FAILED(rv = mInstallDirChromeDataSource->GetTarget(packageResource, arc, PR_TRUE, getter_AddRefs(selectedProvider)))) {
          NS_ERROR("Unable to obtain the provider.");
          return rv;
        }
        resource = do_QueryInterface(selectedProvider);
        if (resource) {
          rv = VerifyCompatibleProvider(packageResource, resource, arc, &providerOK);
          if (NS_FAILED(rv)) return rv;
          if (!providerOK) 
            selectedProvider = nsnull;
        }
      }
    }

    if (!selectedProvider) {
      // FindProvider will attempt to auto-select a version-compatible provider (skin).  If none
      // exist it will return nsnull in the selectedProvider variable.
      FindProvider(aPackage, aProvider, arc, getter_AddRefs(selectedProvider));
      resource = do_QueryInterface(selectedProvider);
    }

    if (!selectedProvider)
      return rv;

    if (!resource)
      return NS_ERROR_FAILURE;
  }

  // From this resource, follow the "baseURL" arc.
  return FollowArc(mChromeDataSource, aBaseURL, resource, mBaseURL);
}

nsresult
nsChromeRegistry::GetOverrideURL(const nsACString& aPackage,
                                 const nsACString& aProvider,
                                 const nsACString& aPath,
                                 nsACString& aResult)
{
  nsresult rv = InitOverrideJAR();
  if (NS_FAILED(rv)) return rv;

  // ok, if we get here, we have an override JAR

  aResult.SetCapacity(mOverrideJARURL.Length() +
                      aPackage.Length() +
                      aProvider.Length() +
                      aPath.Length() + 2);
  
  aResult = mOverrideJARURL;
  aResult += aPackage;
  aResult += '/';
  aResult += aProvider;
  aResult += '/';

  // skins and locales get their name tacked on, like
  // skin/modern/foo.css or
  // locale/en-US/navigator.properties
  if (aProvider.EqualsLiteral("skin") ||
      aProvider.EqualsLiteral("locale")) {

    // little hack here to get the right arc
    nsIRDFResource* providerArc;
    if (aProvider.Equals("skin"))
      providerArc = mSelectedSkin;
    else
      providerArc = mSelectedLocale;
    
    nsCAutoString selectedProvider;
    rv = GetSelectedProvider(aPackage, aProvider, providerArc, selectedProvider);

    if (NS_SUCCEEDED(rv)) {
      aResult += selectedProvider;
      aResult += '/';
    }
  }
  
  aResult += aPath;

  // Check if the item exists in the JAR
  PRBool ok;
  rv = mOverrideJAR->HasEntry(aResult, &ok);
  if (NS_FAILED(rv) || !ok) {
    aResult.Truncate();
  }
  return rv;
}

nsresult
nsChromeRegistry::InitOverrideJAR()
{
  // generic failure if we know there's no override
  if (mSearchedForOverride && !mOverrideJAR)
    return NS_ERROR_FAILURE;

  mSearchedForOverride = PR_TRUE;

  nsresult rv;
  //
  // look for custom.jar
  //
  nsCOMPtr<nsIFile> overrideFile;
  rv = GetInstallRoot(getter_AddRefs(overrideFile));
  if (NS_FAILED(rv)) return rv;

  rv = overrideFile->AppendNative(NS_LITERAL_CSTRING("custom.jar"));
  if (NS_FAILED(rv)) return rv;

  PRBool exists;
  rv = overrideFile->Exists(&exists);
  if (NS_FAILED(rv)) return rv;

  // ok, if the file doesn't exist, its just a generic failure
  if (!exists)
    return NS_ERROR_FAILURE;

  //
  // cache the url so we can later append
  //
  mOverrideJARURL.Assign("jar:");
  nsCAutoString jarURL;
  rv = NS_GetURLSpecFromFile(overrideFile, jarURL);
  if (NS_FAILED(rv)) return rv;

  mOverrideJARURL.Append(jarURL);
  mOverrideJARURL.Append("!/");
  if (NS_FAILED(rv)) return rv;

  //
  // also cache the zip file itself
  //
  nsCOMPtr<nsIZipReaderCache> readerCache =
    do_CreateInstance("@mozilla.org/libjar/zip-reader-cache;1", &rv);
  if (NS_FAILED(rv)) return rv;

  rv = readerCache->Init(32);
  
  rv = readerCache->GetZip(overrideFile, getter_AddRefs(mOverrideJAR));
  if (NS_FAILED(rv)) {
    mOverrideJARURL.Truncate();
    return rv;
  }
  
  return NS_OK;
}

nsresult
nsChromeRegistry::VerifyCompatibleProvider(nsIRDFResource* aPackageResource,
                                           nsIRDFResource* aProviderResource,
                                           nsIRDFResource* aArc,
                                           PRBool *aAcceptable)
{
  // We found a selected provider, but now we need to verify that the version
  // specified by the package and the version specified by the provider are
  // one and the same.  If they aren't, then we cannot use this provider.
  nsCOMPtr<nsIRDFResource> versionArc;
  if (aArc == mSelectedSkin)
    versionArc = mSkinVersion;
  else // Locale arc
    versionArc = mLocaleVersion;

  nsCOMPtr<nsIRDFNode> packageVersionNode;
  mChromeDataSource->GetTarget(aPackageResource, versionArc, PR_TRUE,
                               getter_AddRefs(packageVersionNode));
  if (packageVersionNode) {
    // The package only wants providers (skins) that say they can work
    // with it.  Let's find out if our provider (skin) can work with it.
    mChromeDataSource->HasAssertion(aProviderResource, versionArc,
                                    packageVersionNode, PR_TRUE, aAcceptable);
    if (!*aAcceptable)
      return NS_OK;
  }
  
  // Ensure that the provider actually exists.
  // XXX This will have to change if we handle remote chrome.
  nsCAutoString providerBaseURL;
  nsresult rv = FollowArc(mChromeDataSource, providerBaseURL,
                          aProviderResource, mBaseURL);
  if (NS_FAILED(rv))
    return rv;
  nsCOMPtr<nsIFile> baseURLFile;
  rv = GetBaseURLFile(providerBaseURL, getter_AddRefs(baseURLFile));
  if (NS_FAILED(rv))
    return rv;
  rv = baseURLFile->Exists(aAcceptable);
#if DEBUG
  if (NS_FAILED(rv) || !*aAcceptable)
    printf("BaseURL %s cannot be found.\n",
           PromiseFlatCString(providerBaseURL).get());
#endif
  return rv;
}

// locate
nsresult
nsChromeRegistry::FindProvider(const nsACString& aPackage,
                               const nsACString& aProvider,
                               nsIRDFResource *aArc,
                               nsIRDFNode **aSelectedProvider)
{
  *aSelectedProvider = nsnull;

  nsCAutoString rootStr("urn:mozilla:");
  nsresult rv = NS_OK;

  rootStr += aProvider;
  rootStr += ":root";

  // obtain the provider root resource
  nsCOMPtr<nsIRDFResource> resource;
  rv = GetResource(rootStr, getter_AddRefs(resource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the provider root resource.");
    return rv;
  }

  // wrap it in a container
  nsCOMPtr<nsIRDFContainer> container =
      do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
  if (NS_FAILED(rv)) return rv;

  rv = container->Init(mChromeDataSource, resource);
  if (NS_FAILED(rv)) return rv;

  // step through its (seq) arcs
  nsCOMPtr<nsISimpleEnumerator> arcs;
  rv = container->GetElements(getter_AddRefs(arcs));
  if (NS_FAILED(rv)) return rv;

  // XXX This needs to be something other than random.  See bug 191957.
  PRBool moreElements;
  rv = arcs->HasMoreElements(&moreElements);
  if (NS_FAILED(rv)) return rv;
  for ( ; moreElements; arcs->HasMoreElements(&moreElements)) {

    // get next arc resource
    nsCOMPtr<nsISupports> supports;
    rv = arcs->GetNext(getter_AddRefs(supports));
    if (NS_FAILED(rv)) return rv;
    nsCOMPtr<nsIRDFResource> kid = do_QueryInterface(supports);

    if (kid) {
      // get its name
      nsCAutoString providerName;
      rv = FollowArc(mChromeDataSource, providerName, kid, mName);
      if (NS_FAILED(rv)) return rv;

      // get its package list
      nsCOMPtr<nsIRDFNode> packageNode;
      nsCOMPtr<nsIRDFResource> packageList;
      rv = mChromeDataSource->GetTarget(kid, mPackages, PR_TRUE, getter_AddRefs(packageNode));
      if (NS_SUCCEEDED(rv))
        packageList = do_QueryInterface(packageNode);
      if (!packageList)
        continue;

      // if aPackage is named in kid's package list, select it and we're done
      rv = SelectPackageInProvider(packageList, aPackage, aProvider, providerName,
                                   aArc, aSelectedProvider);
      if (NS_FAILED(rv))
        continue; // Don't let this be disastrous.  We may find another acceptable match.

      if (*aSelectedProvider)
        return NS_OK;
    }
  }
  return NS_ERROR_FAILURE;
}

nsresult
nsChromeRegistry::SelectPackageInProvider(nsIRDFResource *aPackageList,
                                          const nsACString& aPackage,
                                          const nsACString& aProvider,
                                          const nsACString& aProviderName,
                                          nsIRDFResource *aArc,
                                          nsIRDFNode **aSelectedProvider)
{
  *aSelectedProvider = nsnull;

  nsresult rv = NS_OK;

  // wrap aPackageList in a container
  nsCOMPtr<nsIRDFContainer> container =
      do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
  if (NS_SUCCEEDED(rv))
    rv = container->Init(mChromeDataSource, aPackageList);
  if (NS_FAILED(rv))
    return rv;

  // step through its (seq) arcs
  nsCOMPtr<nsISimpleEnumerator> arcs;
  rv = container->GetElements(getter_AddRefs(arcs));
  if (NS_FAILED(rv)) return rv;

  PRBool moreElements;
  rv = arcs->HasMoreElements(&moreElements);
  if (NS_FAILED(rv)) return rv;
  for ( ; moreElements; arcs->HasMoreElements(&moreElements)) {

    // get next arc resource
    nsCOMPtr<nsISupports> supports;
    rv = arcs->GetNext(getter_AddRefs(supports));
    if (NS_FAILED(rv)) return rv;
    nsCOMPtr<nsIRDFResource> kid = do_QueryInterface(supports);

    if (kid) {
      // get its package resource
      nsCOMPtr<nsIRDFNode> packageNode;
      nsCOMPtr<nsIRDFResource> package;
      rv = mChromeDataSource->GetTarget(kid, mPackage, PR_TRUE, getter_AddRefs(packageNode));
      if (NS_SUCCEEDED(rv))
        package = do_QueryInterface(packageNode);
      if (!package)
        continue;

      // get its name
      nsCAutoString packageName;
      rv = FollowArc(mChromeDataSource, packageName, package, mName);
      if (NS_FAILED(rv))
        continue;       // don't fail if package has not yet been installed

      if (packageName.Equals(aPackage)) {
        PRBool useProfile = !mProfileRoot.IsEmpty();
        // XXXldb Do we really want to do this?  We risk crossing skins.
        if (packageName.Equals("global") || packageName.Equals("communicator"))
          useProfile = PR_FALSE; // Always force the auto-selection to be in the
                                 // install dir for the packages required to bring up the profile UI.
        rv = SelectProviderForPackage(aProvider,
                                      aProviderName,
                                      NS_ConvertASCIItoUTF16(packageName).get(),
                                      aArc, useProfile, PR_TRUE);
        if (NS_FAILED(rv))
          return NS_ERROR_FAILURE;

        *aSelectedProvider = kid;
        NS_ADDREF(*aSelectedProvider);
        return NS_OK;
      }
    }
  }
  return NS_OK;
}

nsresult
nsChromeRegistry::GetDynamicDataSource(nsIURI *aChromeURL,
                                       PRBool aIsOverlay, PRBool aUseProfile,
                                       PRBool aCreateDS,
                                       nsIRDFDataSource **aResult)
{
  *aResult = nsnull;

  nsresult rv;

  if (!mDataSourceTable)
    return NS_OK;

  // Obtain the package, provider and remaining from the URL
  nsCAutoString package, provider, remaining;

  rv = SplitURL(aChromeURL, package, provider, remaining);
  NS_ENSURE_SUCCESS(rv, rv);

  if (!aCreateDS) {
    // We are not supposed to create the data source, which means
    // we should first check our chrome.rdf file to see if this
    // package even has dynamic data.  Only if it claims to have
    // dynamic data are we willing to hand back a datasource.
    nsDependentCString dataSourceStr(kChromeFileName);
    nsCOMPtr<nsIRDFDataSource> mainDataSource;
    rv = LoadDataSource(dataSourceStr, getter_AddRefs(mainDataSource), aUseProfile, nsnull);
    NS_ENSURE_SUCCESS(rv, rv);
    
    // Now that we have the appropriate chrome.rdf file, we
    // must check the package resource for stylesheets or overlays.
    nsCOMPtr<nsIRDFResource> hasDynamicDataArc;
    if (aIsOverlay)
      hasDynamicDataArc = mHasOverlays;
    else
      hasDynamicDataArc = mHasStylesheets;
    
    // Obtain the resource for the package.
    nsCAutoString packageResourceStr("urn:mozilla:package:");
    packageResourceStr += package;
    nsCOMPtr<nsIRDFResource> packageResource;
    GetResource(packageResourceStr, getter_AddRefs(packageResource));
    
    // Follow the dynamic data arc to see if we should continue.
    // Only if it claims to have dynamic data do we even bother.
    nsCOMPtr<nsIRDFNode> hasDynamicDSNode;
    mainDataSource->GetTarget(packageResource, hasDynamicDataArc, PR_TRUE,
                              getter_AddRefs(hasDynamicDSNode));
    if (!hasDynamicDSNode)
      return NS_OK; // No data source exists.
  }

  // Retrieve the mInner data source.
  nsCAutoString overlayFile; 
  if (aUseProfile && mLegacyOverlayinfo)
  {
    overlayFile.AppendLiteral("overlayinfo/");
    overlayFile += package;
    if (aIsOverlay)
      overlayFile.AppendLiteral("/content/");
    else
      overlayFile.AppendLiteral("/skin/");
  }
  if (aIsOverlay)
    overlayFile.AppendLiteral("overlays.rdf");
  else
    overlayFile.AppendLiteral("stylesheets.rdf");

  return LoadDataSource(overlayFile, aResult, aUseProfile, nsnull);
}

NS_IMETHODIMP
nsChromeRegistry::GetStyleOverlays(nsIURI *aChromeURL,
                                   nsISimpleEnumerator **aResult)
{
  return GetDynamicInfo(aChromeURL, PR_FALSE, aResult);
}

NS_IMETHODIMP
nsChromeRegistry::GetXULOverlays(nsIURI *aChromeURL, nsISimpleEnumerator **aResult)
{
  return GetDynamicInfo(aChromeURL, PR_TRUE, aResult);
}

nsresult
nsChromeRegistry::GetURIList(nsIRDFDataSource *aSource,
                             nsIRDFResource *aResource,
                             nsCOMArray<nsIURI>& aArray)
{
  nsresult rv;
  nsCOMPtr<nsISimpleEnumerator> arcs;
  nsCOMPtr<nsIRDFContainer> container =
    do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
  if (NS_FAILED(rv)) goto end_GetURIList;

  rv = container->Init(aSource, aResource);
  if (NS_FAILED(rv)) {
    rv = NS_OK;
    goto end_GetURIList;
  }

  rv = container->GetElements(getter_AddRefs(arcs));
  if (NS_FAILED(rv)) goto end_GetURIList;

  {
    nsCOMPtr<nsISupports> supports;
    nsCOMPtr<nsIRDFLiteral> value;
    nsCOMPtr<nsIURI> uri;
    PRBool hasMore;

    while (NS_SUCCEEDED(rv = arcs->HasMoreElements(&hasMore)) && hasMore) {
      rv = arcs->GetNext(getter_AddRefs(supports));
      if (NS_FAILED(rv)) break;

      value = do_QueryInterface(supports, &rv);
      if (NS_FAILED(rv)) continue;

      const PRUnichar* valueStr;
      rv = value->GetValueConst(&valueStr);
      if (NS_FAILED(rv)) continue;

      rv = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(valueStr));
      if (NS_FAILED(rv)) continue;

      if (IsOverlayAllowed(uri)) {
        if (!aArray.AppendObject(uri)) {
          rv = NS_ERROR_OUT_OF_MEMORY;
          break;
        }
      }
    }
  }

end_GetURIList:
  return rv;
}

nsresult
nsChromeRegistry::GetDynamicInfo(nsIURI *aChromeURL, PRBool aIsOverlay,
                                 nsISimpleEnumerator **aResult)
{
  *aResult = nsnull;

  nsresult rv;

  if (!mDataSourceTable)
    return NS_OK;

  nsCOMPtr<nsIRDFDataSource> installSource;
  rv = GetDynamicDataSource(aChromeURL, aIsOverlay, PR_FALSE, PR_FALSE,
                            getter_AddRefs(installSource));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIRDFDataSource> profileSource;
  if (mProfileInitialized) {
    rv = GetDynamicDataSource(aChromeURL, aIsOverlay, PR_TRUE, PR_FALSE,
                              getter_AddRefs(profileSource));
    NS_ENSURE_SUCCESS(rv, rv);
  }

  nsCAutoString lookup;
  rv = aChromeURL->GetSpec(lookup);
  NS_ENSURE_SUCCESS(rv, rv);
   
  // Get the chromeResource from this lookup string
  nsCOMPtr<nsIRDFResource> chromeResource;
  rv = GetResource(lookup, getter_AddRefs(chromeResource));
  if (NS_FAILED(rv)) {
      NS_ERROR("Unable to retrieve the resource corresponding to the chrome skin or content.");
      return rv;
  }

  nsCOMArray<nsIURI> overlayURIs;

  if (installSource) {
    GetURIList(installSource, chromeResource, overlayURIs);
  }
  if (profileSource) {
    GetURIList(profileSource, chromeResource, overlayURIs);
  }

  return NS_NewArrayEnumerator(aResult, overlayURIs);
}

nsresult
nsChromeRegistry::LoadDataSource(const nsACString &aFileName,
                                 nsIRDFDataSource **aResult,
                                 PRBool aUseProfileDir,
                                 const char *aProfilePath)
{
  // Init the data source to null.
  *aResult = nsnull;

  nsCAutoString key;

  // Try the profile root first.
  if (aUseProfileDir) {
    // use given profile path if non-null
    if (aProfilePath) {
      key = aProfilePath;
      key += "chrome/";
    }
    else
      key = mProfileRoot;

    key += aFileName;
  }
  else {
    key = mInstallRoot;
    key += aFileName;
  }

  if (mDataSourceTable)
  {
    nsCStringKey skey(key);
    nsCOMPtr<nsISupports> supports =
      getter_AddRefs(static_cast<nsISupports*>(mDataSourceTable->Get(&skey)));

    if (supports)
    {
      nsCOMPtr<nsIRDFDataSource> dataSource = do_QueryInterface(supports);
      if (dataSource)
      {
        *aResult = dataSource;
        NS_ADDREF(*aResult);
        return NS_OK;
      }
      return NS_ERROR_FAILURE;
    }
  }

  nsresult rv = CallCreateInstance(kRDFXMLDataSourceCID, aResult);
  if (NS_FAILED(rv)) return rv;

  // Seed the datasource with the ``chrome'' namespace
  nsCOMPtr<nsIRDFXMLSink> sink = do_QueryInterface(*aResult);
  if (sink)
    sink->AddNameSpace(sCPrefix, NS_ConvertASCIItoUTF16(CHROME_URI));

  nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(*aResult);
  if (! remote)
      return NS_ERROR_UNEXPECTED;

  if (!mDataSourceTable)
    mDataSourceTable = new nsSupportsHashtable;

  // We need to read this synchronously.
  rv = remote->Init(key.get());
  if (NS_SUCCEEDED(rv))
    rv = remote->Refresh(PR_TRUE);

  nsCOMPtr<nsISupports> supports = do_QueryInterface(remote);
  nsCStringKey skey(key);
  mDataSourceTable->Put(&skey, supports.get());

  return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////

nsresult
nsChromeRegistry::GetResource(const nsACString& aURL,
                              nsIRDFResource** aResult)
{
  nsresult rv = NS_OK;
  if (NS_FAILED(rv = mRDFService->GetResource(aURL, aResult))) {
    NS_ERROR("Unable to retrieve a resource for this URL.");
    *aResult = nsnull;
    return rv;
  }
  return NS_OK;
}

nsresult
nsChromeRegistry::FollowArc(nsIRDFDataSource *aDataSource,
                            nsACString& aResult,
                            nsIRDFResource* aChromeResource,
                            nsIRDFResource* aProperty)
{
  if (!aDataSource)
    return NS_ERROR_FAILURE;

  nsresult rv;

  nsCOMPtr<nsIRDFNode> chromeBase;
  rv = aDataSource->GetTarget(aChromeResource, aProperty, PR_TRUE, getter_AddRefs(chromeBase));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain a base resource.");
    return rv;
  }

  if (chromeBase == nsnull)
    return NS_ERROR_FAILURE;

  nsCOMPtr<nsIRDFResource> resource(do_QueryInterface(chromeBase));

  if (resource) {
    nsXPIDLCString uri;
    rv = resource->GetValue(getter_Copies(uri));
    if (NS_FAILED(rv)) return rv;
    aResult.Assign(uri);
    return NS_OK;
  }

  nsCOMPtr<nsIRDFLiteral> literal(do_QueryInterface(chromeBase));
  if (literal) {
    const PRUnichar *s;
    rv = literal->GetValueConst(&s);
    if (NS_FAILED(rv)) return rv;
    CopyUTF16toUTF8(s, aResult);
  }
  else {
    // This should _never_ happen.
    NS_ERROR("uh, this isn't a resource or a literal!");
    return NS_ERROR_UNEXPECTED;
  }

  return NS_OK;
}

nsresult
nsChromeRegistry::UpdateArc(nsIRDFDataSource *aDataSource, nsIRDFResource* aSource,
                            nsIRDFResource* aProperty,
                            nsIRDFNode *aTarget, PRBool aRemove)
{
  nsresult rv;
  // Get the old targets
  nsCOMPtr<nsIRDFNode> retVal;
  rv = aDataSource->GetTarget(aSource, aProperty, PR_TRUE, getter_AddRefs(retVal));
  if (NS_FAILED(rv)) return rv;

  if (retVal) {
    if (!aRemove)
      aDataSource->Change(aSource, aProperty, retVal, aTarget);
    else
      aDataSource->Unassert(aSource, aProperty, aTarget);
  }
  else if (!aRemove)
    aDataSource->Assert(aSource, aProperty, aTarget, PR_TRUE);

  return NS_OK;
}

////////////////////////////////////////////////////////////////////////

// theme stuff


static void FlushSkinBindingsForWindow(nsIDOMWindowInternal* aWindow)
{
  // Get the DOM document.
  nsCOMPtr<nsIDOMDocument> domDocument;
  aWindow->GetDocument(getter_AddRefs(domDocument));
  if (!domDocument)
    return;

  nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
  if (!document)
    return;

  // Annihilate all XBL bindings.
  document->FlushSkinBindings();
}

// XXXbsmedberg: move this to nsIWindowMediator
NS_IMETHODIMP nsChromeRegistry::RefreshSkins()
{
  nsCOMPtr<nsIWindowMediator> windowMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
  if (!windowMediator)
    return NS_OK;

  nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
  windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
  PRBool more;
  windowEnumerator->HasMoreElements(&more);
  while (more) {
    nsCOMPtr<nsISupports> protoWindow;
    windowEnumerator->GetNext(getter_AddRefs(protoWindow));
    if (protoWindow) {
      nsCOMPtr<nsIDOMWindowInternal> domWindow = do_QueryInterface(protoWindow);
      if (domWindow)
        FlushSkinBindingsForWindow(domWindow);
    }
    windowEnumerator->HasMoreElements(&more);
  }

  FlushSkinCaches();
  
  windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
  windowEnumerator->HasMoreElements(&more);
  while (more) {
    nsCOMPtr<nsISupports> protoWindow;
    windowEnumerator->GetNext(getter_AddRefs(protoWindow));
    if (protoWindow) {
      nsCOMPtr<nsIDOMWindowInternal> domWindow = do_QueryInterface(protoWindow);
      if (domWindow)
        RefreshWindow(domWindow);
    }
    windowEnumerator->HasMoreElements(&more);
  }
   
  return NS_OK;
}


void
nsChromeRegistry::FlushSkinCaches()
{
  nsCOMPtr<nsIObserverService> obsSvc =
    do_GetService("@mozilla.org/observer-service;1");
  NS_ASSERTION(obsSvc, "Couldn't get observer service.");

  obsSvc->NotifyObservers((nsIChromeRegistry*) this,
                          NS_CHROME_FLUSH_SKINS_TOPIC, nsnull);
}

static PRBool IsChromeURI(nsIURI* aURI)
{
    PRBool isChrome=PR_FALSE;
    if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome)
        return PR_TRUE;
    return PR_FALSE;
}

// XXXbsmedberg: move this to windowmediator
nsresult nsChromeRegistry::RefreshWindow(nsIDOMWindowInternal* aWindow)
{
  // Deal with our subframes first.
  nsCOMPtr<nsIDOMWindowCollection> frames;
  aWindow->GetFrames(getter_AddRefs(frames));
  PRUint32 length;
  frames->GetLength(&length);
  PRUint32 j;
  for (j = 0; j < length; j++) {
    nsCOMPtr<nsIDOMWindow> childWin;
    frames->Item(j, getter_AddRefs(childWin));
    nsCOMPtr<nsIDOMWindowInternal> childInt(do_QueryInterface(childWin));
    RefreshWindow(childInt);
  }

  nsresult rv;
  // Get the DOM document.
  nsCOMPtr<nsIDOMDocument> domDocument;
  aWindow->GetDocument(getter_AddRefs(domDocument));
  if (!domDocument)
    return NS_OK;

  nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
  if (!document)
    return NS_OK;

  // Deal with the agent sheets first.  Have to do all the style sets by hand.
  nsPresShellIterator iter(document);
  nsCOMPtr<nsIPresShell> shell;
  while ((shell = iter.GetNextShell())) {
    // Reload only the chrome URL agent style sheets.
    nsCOMArray<nsIStyleSheet> agentSheets;
    rv = shell->GetAgentStyleSheets(agentSheets);
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMArray<nsIStyleSheet> newAgentSheets;
    for (PRInt32 l = 0; l < agentSheets.Count(); ++l) {
      nsIStyleSheet *sheet = agentSheets[l];

      nsCOMPtr<nsIURI> uri;
      rv = sheet->GetSheetURI(getter_AddRefs(uri));
      if (NS_FAILED(rv)) return rv;

      if (IsChromeURI(uri)) {
        // Reload the sheet.
        nsCOMPtr<nsICSSStyleSheet> newSheet;
        rv = LoadStyleSheetWithURL(uri, PR_TRUE, getter_AddRefs(newSheet));
        if (NS_FAILED(rv)) return rv;
        if (newSheet) {
          rv = newAgentSheets.AppendObject(newSheet) ? NS_OK : NS_ERROR_FAILURE;
          if (NS_FAILED(rv)) return rv;
        }
      }
      else {  // Just use the same sheet.
        rv = newAgentSheets.AppendObject(sheet) ? NS_OK : NS_ERROR_FAILURE;
        if (NS_FAILED(rv)) return rv;
      }
    }

    rv = shell->SetAgentStyleSheets(newAgentSheets);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // Build an array of nsIURIs of style sheets we need to load.
  nsCOMArray<nsIStyleSheet> oldSheets;
  nsCOMArray<nsIStyleSheet> newSheets;

  PRInt32 count = document->GetNumberOfStyleSheets();

  // Iterate over the style sheets.
  PRInt32 i;
  for (i = 0; i < count; i++) {
    // Get the style sheet
    nsIStyleSheet *styleSheet = document->GetStyleSheetAt(i);
    
    if (!oldSheets.AppendObject(styleSheet)) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
  }

  // Iterate over our old sheets and kick off a sync load of the new 
  // sheet if and only if it's a chrome URL.
  for (i = 0; i < count; i++) {
    nsCOMPtr<nsIStyleSheet> sheet = oldSheets[i];
    nsCOMPtr<nsIURI> uri;
    rv = sheet->GetSheetURI(getter_AddRefs(uri));
    if (NS_FAILED(rv)) return rv;

    if (IsChromeURI(uri)) {
      // Reload the sheet.
#ifdef DEBUG
      nsCOMPtr<nsICSSStyleSheet> oldCSSSheet = do_QueryInterface(sheet);
      NS_ASSERTION(oldCSSSheet, "Don't know how to reload a non-CSS sheet");
#endif
      nsCOMPtr<nsICSSStyleSheet> newSheet;
      // XXX what about chrome sheets that have a title or are disabled?  This
      // only works by sheer dumb luck.
      // XXXbz this should really use the document's CSSLoader!
      LoadStyleSheetWithURL(uri, PR_FALSE, getter_AddRefs(newSheet));
      // Even if it's null, we put in in there.
      newSheets.AppendObject(newSheet);
    }
    else {
      // Just use the same sheet.
      newSheets.AppendObject(sheet);
    }
  }

  // Now notify the document that multiple sheets have been added and removed.
  document->UpdateStyleSheets(oldSheets, newSheets);
  return NS_OK;
}

nsresult
nsChromeRegistry::WriteInfoToDataSource(const char *aDocURI,
                                        const PRUnichar *aOverlayURI,
                                        PRBool aIsOverlay,
                                        PRBool aUseProfile,
                                        PRBool aRemove)
{
  nsresult rv;
  nsCOMPtr<nsIURI> uri;
  nsCAutoString str(aDocURI);
  rv = NS_NewURI(getter_AddRefs(uri), str);
  if (NS_FAILED(rv)) return rv;

  if (!aRemove) {
    // We are installing a dynamic overlay or package.  
    // We must split the doc URI and obtain our package or skin.
    // We then annotate the chrome.rdf datasource in the appropriate
    // install/profile dir (based off aUseProfile) with the knowledge
    // that we have overlays or stylesheets.
    nsCAutoString package, provider, file;
    rv = SplitURL(uri, package, provider, file);
    if (NS_FAILED(rv)) return NS_OK;
    
    // Obtain our chrome data source.
    nsDependentCString dataSourceStr(kChromeFileName);
    nsCOMPtr<nsIRDFDataSource> mainDataSource;
    rv = LoadDataSource(dataSourceStr, getter_AddRefs(mainDataSource), aUseProfile, nsnull);
    if (NS_FAILED(rv)) return rv;
    
    // Now that we have the appropriate chrome.rdf file, we 
    // must annotate the package resource with the knowledge of
    // whether or not we have stylesheets or overlays.
    nsCOMPtr<nsIRDFResource> hasDynamicDataArc;
    if (aIsOverlay)
      hasDynamicDataArc = mHasOverlays;
    else
      hasDynamicDataArc = mHasStylesheets;
    
    // Obtain the resource for the package.
    nsCAutoString packageResourceStr("urn:mozilla:package:");
    packageResourceStr += package;
    nsCOMPtr<nsIRDFResource> packageResource;
    GetResource(packageResourceStr, getter_AddRefs(packageResource));
    
    // Now add the arc to the package.
    nsCOMPtr<nsIRDFLiteral> trueLiteral;
    mRDFService->GetLiteral(NS_LITERAL_STRING("true").get(), getter_AddRefs(trueLiteral));
    nsChromeRegistry::UpdateArc(mainDataSource, packageResource, 
                                hasDynamicDataArc, 
                                trueLiteral, PR_FALSE);
  }

  nsCOMPtr<nsIRDFDataSource> dataSource;
  rv = GetDynamicDataSource(uri, aIsOverlay, aUseProfile, PR_TRUE, getter_AddRefs(dataSource));
  if (NS_FAILED(rv)) return rv;

  if (!dataSource)
    return NS_OK;

  nsCOMPtr<nsIRDFResource> resource;
  rv = GetResource(str, getter_AddRefs(resource));

  if (NS_FAILED(rv))
    return NS_OK;

  nsCOMPtr<nsIRDFContainer> container;
  rv = mRDFContainerUtils->MakeSeq(dataSource, resource, getter_AddRefs(container));
  if (NS_FAILED(rv)) return rv;
  if (!container) {
    // Already exists. Create a container instead.
    container = do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
    if (NS_FAILED(rv)) return rv;
    rv = container->Init(dataSource, resource);
    if (NS_FAILED(rv)) return rv;
  }

  nsAutoString unistr(aOverlayURI);
  nsCOMPtr<nsIRDFLiteral> literal;
  rv = mRDFService->GetLiteral(unistr.get(), getter_AddRefs(literal));
  if (NS_FAILED(rv)) return rv;

  if (aRemove) {
    rv = container->RemoveElement(literal, PR_TRUE);
    if (NS_FAILED(rv)) return rv;
  }
  else {
    PRInt32 index;
    rv = container->IndexOf(literal, &index);
    if (NS_FAILED(rv)) return rv;
    if (index == -1) {
      rv = container->AppendElement(literal);
      if (NS_FAILED(rv)) return rv;
    }
  }

  nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(dataSource, &rv);
  if (NS_SUCCEEDED(rv)) {
    rv = remote->Flush();
  }

  return rv;
}

nsresult
nsChromeRegistry::UpdateDynamicDataSource(nsIRDFDataSource *aDataSource,
                                          nsIRDFResource *aResource,
                                          PRBool aIsOverlay,
                                          PRBool aUseProfile, PRBool aRemove)
{
  nsresult rv;

  nsCOMPtr<nsIRDFContainer> container =
      do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
  if (NS_FAILED(rv)) return rv;

  rv = container->Init(aDataSource, aResource);
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsISimpleEnumerator> arcs;
  rv = container->GetElements(getter_AddRefs(arcs));
  if (NS_FAILED(rv)) return rv;

  PRBool moreElements;
  rv = arcs->HasMoreElements(&moreElements);
  if (NS_FAILED(rv)) return rv;

  const char *value;
  rv = aResource->GetValueConst(&value);
  if (NS_FAILED(rv)) return rv;

  while (moreElements)
  {
    nsCOMPtr<nsISupports> supports;
    rv = arcs->GetNext(getter_AddRefs(supports));
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(supports, &rv);

    if (NS_SUCCEEDED(rv))
    {
      const PRUnichar* valueStr;
      rv = literal->GetValueConst(&valueStr);
      if (NS_FAILED(rv)) return rv;

      rv = WriteInfoToDataSource(value, valueStr, aIsOverlay, aUseProfile, aRemove);
      if (NS_FAILED(rv)) return rv;
    }
    rv = arcs->HasMoreElements(&moreElements);
    if (NS_FAILED(rv)) return rv;
  }

  return NS_OK;
}


nsresult
nsChromeRegistry::UpdateDynamicDataSources(nsIRDFDataSource *aDataSource,
                                           PRBool aIsOverlay,
                                           PRBool aUseProfile, PRBool aRemove)
{
  nsresult rv;
  nsCOMPtr<nsIRDFResource> resource;
  nsCAutoString root;
  if (aIsOverlay)
    root.Assign("urn:mozilla:overlays");
  else root.Assign("urn:mozilla:stylesheets");

  rv = GetResource(root, getter_AddRefs(resource));

  if (!resource)
    return NS_OK;

  nsCOMPtr<nsIRDFContainer> container(do_CreateInstance("@mozilla.org/rdf/container;1"));
  if (!container)
    return NS_OK;

  if (NS_FAILED(container->Init(aDataSource, resource)))
    return NS_OK;

  nsCOMPtr<nsISimpleEnumerator> arcs;
  if (NS_FAILED(container->GetElements(getter_AddRefs(arcs))))
    return NS_OK;

  PRBool moreElements;
  rv = arcs->HasMoreElements(&moreElements);
  if (NS_FAILED(rv)) return rv;

  while (moreElements)
  {
    nsCOMPtr<nsISupports> supports;
    rv = arcs->GetNext(getter_AddRefs(supports));
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIRDFResource> resource2 = do_QueryInterface(supports, &rv);

    if (NS_SUCCEEDED(rv))
    {
      rv = UpdateDynamicDataSource(aDataSource, resource2, aIsOverlay, aUseProfile, aRemove);
      if (NS_FAILED(rv)) return rv;
    }

    rv = arcs->HasMoreElements(&moreElements);
    if (NS_FAILED(rv)) return rv;
  }

  return NS_OK;
}

NS_IMETHODIMP nsChromeRegistry::SelectSkin(const nsACString& aSkin,
                                        PRBool aUseProfile)
{
  nsresult rv = SetProvider(NS_LITERAL_CSTRING("skin"), mSelectedSkin, aSkin, aUseProfile, nsnull, PR_TRUE);
  FlushSkinCaches();
  return rv;
}

NS_IMETHODIMP nsChromeRegistry::SelectLocale(const nsACString& aLocale,
                                          PRBool aUseProfile)
{
  return SetProvider(NS_LITERAL_CSTRING("locale"), mSelectedLocale, aLocale, aUseProfile, nsnull, PR_TRUE);
}

NS_IMETHODIMP nsChromeRegistry::SelectLocaleForProfile(const nsACString& aLocale,
                                                       const PRUnichar *aProfilePath)
{
  // to be changed to use given path
  return SetProvider(NS_LITERAL_CSTRING("locale"), mSelectedLocale, aLocale, PR_TRUE, NS_ConvertUTF16toUTF8(aProfilePath).get(), PR_TRUE);
}

NS_IMETHODIMP nsChromeRegistry::SelectSkinForProfile(const nsACString& aSkin,
                                                     const PRUnichar *aProfilePath)
{
  return SetProvider(NS_LITERAL_CSTRING("skin"), mSelectedSkin, aSkin, PR_TRUE, NS_ConvertUTF16toUTF8(aProfilePath).get(), PR_TRUE);
}

/* void setRuntimeProvider (in boolean runtimeProvider); */
// should we inline this one?
NS_IMETHODIMP nsChromeRegistry::SetRuntimeProvider(PRBool runtimeProvider)
{
  mRuntimeProvider = runtimeProvider;
  return NS_OK;
}


/* ACString getSelectedLocale (ACString packageName); */
NS_IMETHODIMP
nsChromeRegistry::GetSelectedLocale(const nsACString& aPackageName,
                                    nsACString& aResult)
{
  return GetSelectedProvider(aPackageName,
                             NS_LITERAL_CSTRING("locale"), mSelectedLocale,
                             aResult);
}

NS_IMETHODIMP
nsChromeRegistry::GetSelectedSkin(const nsACString& aPackageName,
                                  nsACString& aResult)
{
  return GetSelectedProvider(aPackageName,
                             NS_LITERAL_CSTRING("skin"), mSelectedSkin,
                             aResult);
}

nsresult
nsChromeRegistry::GetSelectedProvider(const nsACString& aPackageName,
                                      const nsACString& aProvider,
                                      nsIRDFResource* aSelectionArc,
                                      nsACString& _retval)
{
  // check if mChromeDataSource is null; do we need to apply this to every instance?
  // is there a better way to test if the data source is ready?
  if (!mChromeDataSource) {
    return NS_ERROR_FAILURE;
  }

  nsCAutoString resourceStr("urn:mozilla:package:");
  resourceStr += aPackageName;

  // Obtain the resource.
  nsresult rv = NS_OK;
  nsCOMPtr<nsIRDFResource> resource;
  rv = GetResource(resourceStr, getter_AddRefs(resource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the package resource.");
    return rv;
  }

  if (mChromeDataSource == nsnull)
    return NS_ERROR_NULL_POINTER;

  // Follow the "selectedLocale" arc.
  nsCOMPtr<nsIRDFNode> selectedProvider;
  if (NS_FAILED(rv = mChromeDataSource->GetTarget(resource, aSelectionArc, PR_TRUE, getter_AddRefs(selectedProvider)))) {
    NS_ERROR("Unable to obtain the provider.");
    return rv;
  }

  if (!selectedProvider) {
    rv = FindProvider(aPackageName, aProvider, aSelectionArc, getter_AddRefs(selectedProvider));
    if (!selectedProvider)
      return rv;
  }

  resource = do_QueryInterface(selectedProvider);
  if (!resource)
    return NS_ERROR_FAILURE;

  // selectedProvider.mURI now looks like "urn:mozilla:locale:ja-JP:navigator"
  const char *uri;
  if (NS_FAILED(rv = resource->GetValueConst(&uri)))
    return rv;

  // trim down to "urn:mozilla:locale:ja-JP"
  nsCAutoString packageStr(":");
  packageStr += aPackageName;

  nsCAutoString ustr(uri);
  PRInt32 pos = ustr.RFind(packageStr);
  nsCAutoString urn;
  ustr.Left(urn, pos);

  rv = GetResource(urn, getter_AddRefs(resource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the provider resource.");
    return rv;
  }

  // From this resource, follow the "name" arc.
  return FollowArc(mChromeDataSource, _retval, resource, mName);
}

NS_IMETHODIMP nsChromeRegistry::DeselectSkin(const nsACString& aSkin,
                                        PRBool aUseProfile)
{
  nsresult rv = SetProvider(NS_LITERAL_CSTRING("skin"), mSelectedSkin, aSkin, aUseProfile, nsnull, PR_FALSE);
  FlushSkinCaches();
  return rv;
}

NS_IMETHODIMP nsChromeRegistry::DeselectLocale(const nsACString& aLocale,
                                          PRBool aUseProfile)
{
  return SetProvider(NS_LITERAL_CSTRING("locale"), mSelectedLocale, aLocale, aUseProfile, nsnull, PR_FALSE);
}

nsresult
nsChromeRegistry::SetProvider(const nsACString& aProvider,
                              nsIRDFResource* aSelectionArc,
                              const nsACString& aProviderName,
                              PRBool aUseProfile, const char *aProfilePath,
                              PRBool aIsAdding)
{
  // Build the provider resource str.
  // e.g., urn:mozilla:skin:aqua/1.0
  nsCAutoString resourceStr( "urn:mozilla:" );
  resourceStr += aProvider;
  resourceStr += ":";
  resourceStr += aProviderName;

  // Obtain the provider resource.
  nsresult rv = NS_OK;
  nsCOMPtr<nsIRDFResource> resource;
  rv = GetResource(resourceStr, getter_AddRefs(resource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the package resource.");
    return rv;
  }
  NS_ASSERTION(resource, "failed to GetResource");

  // Follow the packages arc to the package resources.
  nsCOMPtr<nsIRDFNode> packageList;
  rv = mChromeDataSource->GetTarget(resource, mPackages, PR_TRUE, getter_AddRefs(packageList));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the SEQ for the package list.");
    return rv;
  }
  // ok for packageList to be null here -- it just means that we haven't encountered that package yet

  nsCOMPtr<nsIRDFResource> packageSeq(do_QueryInterface(packageList, &rv));
  if (NS_FAILED(rv)) return rv;

  // Build an RDF container to wrap the SEQ
  nsCOMPtr<nsIRDFContainer> container =
      do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
  if (NS_FAILED(rv))
    return NS_OK;

  if (NS_FAILED(container->Init(mChromeDataSource, packageSeq)))
    return NS_OK;

  nsCOMPtr<nsISimpleEnumerator> arcs;
  if (NS_FAILED(container->GetElements(getter_AddRefs(arcs))))
    return NS_OK;

  // For each skin/package entry, follow the arcs to the real package
  // resource.
  PRBool more;
  rv = arcs->HasMoreElements(&more);
  if (NS_FAILED(rv)) return rv;
  while (more) {
    nsCOMPtr<nsISupports> packageSkinEntry;
    rv = arcs->GetNext(getter_AddRefs(packageSkinEntry));
    if (NS_SUCCEEDED(rv) && packageSkinEntry) {
      nsCOMPtr<nsIRDFResource> entry = do_QueryInterface(packageSkinEntry);
      if (entry) {
         // Obtain the real package resource.
         nsCOMPtr<nsIRDFNode> packageNode;
         rv = mChromeDataSource->GetTarget(entry, mPackage, PR_TRUE, getter_AddRefs(packageNode));
         if (NS_FAILED(rv)) {
           NS_ERROR("Unable to obtain the package resource.");
           return rv;
         }

         // Select the skin for this package resource.
         nsCOMPtr<nsIRDFResource> packageResource(do_QueryInterface(packageNode));
         if (packageResource) {
           rv = SetProviderForPackage(aProvider, packageResource, entry, aSelectionArc, aUseProfile, aProfilePath, aIsAdding);
           if (NS_FAILED(rv))
             continue; // Well, let's set as many sub-packages as we can...
         }
      }
    }
    rv = arcs->HasMoreElements(&more);
    if (NS_FAILED(rv)) return rv;
  }

  // always reset the flag
  mRuntimeProvider = PR_FALSE;

  return NS_OK;
}

nsresult
nsChromeRegistry::SetProviderForPackage(const nsACString& aProvider,
                                        nsIRDFResource* aPackageResource,
                                        nsIRDFResource* aProviderPackageResource,
                                        nsIRDFResource* aSelectionArc,
                                        PRBool aUseProfile, const char *aProfilePath,
                                        PRBool aIsAdding)
{
  nsresult rv;
  
  if (aUseProfile && !mProfileInitialized) {
    rv = LoadProfileDataSource();
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // Figure out which file we're needing to modify, e.g., is it the install
  // dir or the profile dir, and get the right datasource.
  nsCOMPtr<nsIRDFDataSource> dataSource;
  rv = LoadDataSource(kChromeFileName, getter_AddRefs(dataSource), aUseProfile, aProfilePath);
  if (NS_FAILED(rv)) return rv;

  rv = nsChromeRegistry::UpdateArc(dataSource, aPackageResource, aSelectionArc, aProviderPackageResource, !aIsAdding);
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(dataSource, &rv);
  if (NS_FAILED(rv)) return rv;

  // add one more check: 
  //   assert the data source only when we are not setting runtime-only provider
  if (!mBatchInstallFlushes && !mRuntimeProvider)
    rv = remote->Flush();

  return rv;
}

NS_IMETHODIMP nsChromeRegistry::SelectSkinForPackage(const nsACString& aSkin,
                                                     const PRUnichar *aPackageName,
                                                  PRBool aUseProfile)
{
  return SelectProviderForPackage(NS_LITERAL_CSTRING("skin"), aSkin, aPackageName, mSelectedSkin, aUseProfile, PR_TRUE);
}

NS_IMETHODIMP nsChromeRegistry::SelectLocaleForPackage(const nsACString& aLocale,
                                                       const PRUnichar *aPackageName,
                                                    PRBool aUseProfile)
{
  return SelectProviderForPackage(NS_LITERAL_CSTRING("locale"), aLocale, aPackageName, mSelectedLocale, aUseProfile, PR_TRUE);
}

NS_IMETHODIMP nsChromeRegistry::DeselectSkinForPackage(const nsACString& aSkin,
                                                  const PRUnichar *aPackageName,
                                                  PRBool aUseProfile)
{
  return SelectProviderForPackage(NS_LITERAL_CSTRING("skin"), aSkin, aPackageName, mSelectedSkin, aUseProfile, PR_FALSE);
}

NS_IMETHODIMP nsChromeRegistry::DeselectLocaleForPackage(const nsACString& aLocale,
                                                    const PRUnichar *aPackageName,
                                                    PRBool aUseProfile)
{
  return SelectProviderForPackage(NS_LITERAL_CSTRING("locale"), aLocale, aPackageName, mSelectedLocale, aUseProfile, PR_FALSE);
}

NS_IMETHODIMP nsChromeRegistry::IsSkinSelectedForPackage(const nsACString& aSkin,
                                                  const PRUnichar *aPackageName,
                                                  PRBool aUseProfile, PRBool* aResult)
{
  return IsProviderSelectedForPackage(NS_LITERAL_CSTRING("skin"), aSkin, aPackageName, mSelectedSkin, aUseProfile, aResult);
}

NS_IMETHODIMP nsChromeRegistry::IsLocaleSelectedForPackage(const nsACString& aLocale,
                                                    const PRUnichar *aPackageName,
                                                    PRBool aUseProfile, PRBool* aResult)
{
  return IsProviderSelectedForPackage(NS_LITERAL_CSTRING("locale"), aLocale, aPackageName, mSelectedLocale, aUseProfile, aResult);
}

nsresult
nsChromeRegistry::SelectProviderForPackage(const nsACString& aProviderType,
                                           const nsACString& aProviderName,
                                           const PRUnichar *aPackageName,
                                           nsIRDFResource* aSelectionArc,
                                           PRBool aUseProfile, PRBool aIsAdding)
{
  nsCAutoString package( "urn:mozilla:package:" );
  AppendUTF16toUTF8(aPackageName, package);

  nsCAutoString provider( "urn:mozilla:" );
  provider += aProviderType;
  provider += ":";
  provider += aProviderName;
  provider += ":";
  AppendUTF16toUTF8(aPackageName, provider);

  // Obtain the package resource.
  nsresult rv = NS_OK;
  nsCOMPtr<nsIRDFResource> packageResource;
  rv = GetResource(package, getter_AddRefs(packageResource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the package resource.");
    return rv;
  }
  NS_ASSERTION(packageResource, "failed to get packageResource");

  // Obtain the provider resource.
  nsCOMPtr<nsIRDFResource> providerResource;
  rv = GetResource(provider, getter_AddRefs(providerResource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the provider resource.");
    return rv;
  }
  NS_ASSERTION(providerResource, "failed to get providerResource");

  // Version-check before selecting.  If this skin isn't a compatible version, then
  // don't allow the selection.
  PRBool acceptable;
  rv = VerifyCompatibleProvider(packageResource, providerResource,
                                aSelectionArc, &acceptable);
  if (NS_FAILED(rv))
    return rv;
  if (!acceptable)
    return NS_ERROR_FAILURE;

  rv = SetProviderForPackage(aProviderType, packageResource, providerResource, aSelectionArc,
                             aUseProfile, nsnull, aIsAdding);
  // always reset the flag
  mRuntimeProvider = PR_FALSE;

  return rv;
}

NS_IMETHODIMP nsChromeRegistry::IsSkinSelected(const nsACString& aSkin,
                                               PRBool aUseProfile, PRInt32* aResult)
{
  return IsProviderSelected(NS_LITERAL_CSTRING("skin"), aSkin, mSelectedSkin, aUseProfile, aResult);
}

NS_IMETHODIMP nsChromeRegistry::IsLocaleSelected(const nsACString& aLocale,
                                                 PRBool aUseProfile, PRInt32* aResult)
{
  return IsProviderSelected(NS_LITERAL_CSTRING("locale"), aLocale, mSelectedLocale, aUseProfile, aResult);
}

nsresult
nsChromeRegistry::IsProviderSelected(const nsACString& aProvider,
                                     const nsACString& aProviderName,
                                     nsIRDFResource* aSelectionArc,
                                     PRBool aUseProfile, PRInt32* aResult)
{
  // Build the provider resource str.
  // e.g., urn:mozilla:skin:aqua/1.0
  *aResult = NONE;
  nsCAutoString resourceStr( "urn:mozilla:" );
  resourceStr += aProvider;
  resourceStr += ":";
  resourceStr += aProviderName;
  // Obtain the provider resource.
  nsresult rv = NS_OK;
  nsCOMPtr<nsIRDFResource> resource;
  rv = GetResource(resourceStr, getter_AddRefs(resource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the package resource.");
    return rv;
  }
  NS_ASSERTION(resource, "failed to GetResource");

  // Follow the packages arc to the package resources.
  nsCOMPtr<nsIRDFNode> packageList;
  rv = mChromeDataSource->GetTarget(resource, mPackages, PR_TRUE, getter_AddRefs(packageList));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the SEQ for the package list.");
    return rv;
  }
  // ok for packageList to be null here -- it just means that we haven't encountered that package yet

  nsCOMPtr<nsIRDFResource> packageSeq(do_QueryInterface(packageList, &rv));
  if (NS_FAILED(rv)) return rv;

  // Build an RDF container to wrap the SEQ
  nsCOMPtr<nsIRDFContainer> container(do_CreateInstance("@mozilla.org/rdf/container;1"));
  if (NS_FAILED(container->Init(mChromeDataSource, packageSeq)))
    return NS_OK;

  nsCOMPtr<nsISimpleEnumerator> arcs;
  container->GetElements(getter_AddRefs(arcs));

  // For each skin/package entry, follow the arcs to the real package
  // resource.
  PRBool more;
  PRInt32 numSet = 0;
  PRInt32 numPackages = 0;
  rv = arcs->HasMoreElements(&more);
  if (NS_FAILED(rv)) return rv;
  while (more) {
    nsCOMPtr<nsISupports> packageSkinEntry;
    rv = arcs->GetNext(getter_AddRefs(packageSkinEntry));
    if (NS_SUCCEEDED(rv) && packageSkinEntry) {
      nsCOMPtr<nsIRDFResource> entry = do_QueryInterface(packageSkinEntry);
      if (entry) {
         // Obtain the real package resource.
         nsCOMPtr<nsIRDFNode> packageNode;
         rv = mChromeDataSource->GetTarget(entry, mPackage, PR_TRUE, getter_AddRefs(packageNode));
         if (NS_FAILED(rv)) {
           NS_ERROR("Unable to obtain the package resource.");
           return rv;
         }

         // Select the skin for this package resource.
         nsCOMPtr<nsIRDFResource> packageResource(do_QueryInterface(packageNode));
         if (packageResource) {
           PRBool isSet = PR_FALSE;
           rv = IsProviderSetForPackage(aProvider, packageResource, entry, aSelectionArc, aUseProfile, &isSet);
           if (NS_FAILED(rv)) {
             NS_ERROR("Unable to set provider for package resource.");
             return rv;
           }
           ++numPackages;
           if (isSet)
             ++numSet;
         }
      }
    }
    rv = arcs->HasMoreElements(&more);
    if (NS_FAILED(rv)) return rv;
  }
  if (numPackages == numSet)
    *aResult = FULL;
  else if (numSet)
    *aResult = PARTIAL;
  return NS_OK;
}

nsresult
nsChromeRegistry::IsProviderSelectedForPackage(const nsACString& aProviderType,
                                               const nsACString& aProviderName,
                                               const PRUnichar *aPackageName,
                                               nsIRDFResource* aSelectionArc,
                                               PRBool aUseProfile, PRBool* aResult)
{
  *aResult = PR_FALSE;
  nsCAutoString package( "urn:mozilla:package:" );
  AppendUTF16toUTF8(aPackageName, package);

  nsCAutoString provider( "urn:mozilla:" );
  provider += aProviderType;
  provider += ":";
  provider += aProviderName;
  provider += ":";
  AppendUTF16toUTF8(aPackageName, provider);

  // Obtain the package resource.
  nsresult rv = NS_OK;
  nsCOMPtr<nsIRDFResource> packageResource;
  rv = GetResource(package, getter_AddRefs(packageResource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the package resource.");
    return rv;
  }
  NS_ASSERTION(packageResource, "failed to get packageResource");

  // Obtain the provider resource.
  nsCOMPtr<nsIRDFResource> providerResource;
  rv = GetResource(provider, getter_AddRefs(providerResource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the provider resource.");
    return rv;
  }
  NS_ASSERTION(providerResource, "failed to get providerResource");

  return IsProviderSetForPackage(aProviderType, packageResource, providerResource, aSelectionArc,
                                 aUseProfile, aResult);
}

nsresult
nsChromeRegistry::IsProviderSetForPackage(const nsACString& aProvider,
                                          nsIRDFResource* aPackageResource,
                                          nsIRDFResource* aProviderPackageResource,
                                          nsIRDFResource* aSelectionArc,
                                          PRBool aUseProfile, PRBool* aResult)
{
  nsresult rv;
  // Figure out which file we're needing to modify, e.g., is it the install
  // dir or the profile dir, and get the right datasource.
  
  nsCOMPtr<nsIRDFDataSource> dataSource;
  rv = LoadDataSource(kChromeFileName, getter_AddRefs(dataSource), aUseProfile, nsnull);
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsIRDFNode> retVal;
  dataSource->GetTarget(aPackageResource, aSelectionArc, PR_TRUE, getter_AddRefs(retVal));
  if (retVal) {
    nsCOMPtr<nsIRDFNode> node(do_QueryInterface(aProviderPackageResource));
    if (node == retVal)
      *aResult = PR_TRUE;
  }

  return NS_OK;
}

nsresult
nsChromeRegistry::InstallProvider(const nsACString& aProviderType,
                                  const nsACString& aBaseURL,
                                  PRBool aUseProfile, PRBool aAllowScripts,
                                  PRBool aRemove)
{
  // XXX don't allow local chrome overrides of install chrome!
#ifdef DEBUG
  printf("*** Chrome Registration of %-7s: Checking for contents.rdf at %s\n", PromiseFlatCString(aProviderType).get(), PromiseFlatCString(aBaseURL).get());
#endif

  // Load the data source found at the base URL.
  nsresult rv;
  nsCOMPtr<nsIRDFDataSource> dataSource =
      do_CreateInstance(kRDFXMLDataSourceCID, &rv);
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(dataSource, &rv);
  if (NS_FAILED(rv)) return rv;

  // We need to read this synchronously.
  nsCAutoString key(aBaseURL);
  key += "contents.rdf";
  remote->Init(key.get());
  remote->Refresh(PR_TRUE);

  PRBool skinCount = GetProviderCount(NS_LITERAL_CSTRING("skin"), dataSource);
  PRBool localeCount = GetProviderCount(NS_LITERAL_CSTRING("locale"), dataSource);
  PRBool packageCount = GetProviderCount(NS_LITERAL_CSTRING("package"), dataSource);

  PRBool appendPackage = PR_FALSE;
  PRBool appendProvider = PR_FALSE;
  PRBool appendProviderName = PR_FALSE;

  if (skinCount == 0 && localeCount == 0 && packageCount == 0) {
    // Try the old-style manifest.rdf instead
    key = aBaseURL;
    key += "manifest.rdf";
    (void)remote->Init(key.get());      // ignore failure here
    rv = remote->Refresh(PR_TRUE);
    if (NS_FAILED(rv)) return rv;
    appendPackage = PR_TRUE;
    appendProvider = PR_TRUE;
    NS_WARNING("Trying old-style manifest.rdf. Please update to contents.rdf.");
  }
  else {
    if ((skinCount > 1 && aProviderType.Equals("skin")) ||
        (localeCount > 1 && aProviderType.Equals("locale")))
      appendProviderName = PR_TRUE;

    if (!appendProviderName && packageCount > 1) {
      appendPackage = PR_TRUE;
    }

    if (aProviderType.Equals("skin")) {
      if (!appendProviderName && (localeCount == 1 || packageCount != 0))
        appendProvider = PR_TRUE;
    }
    else if (aProviderType.Equals("locale")) {
      if (!appendProviderName && (skinCount == 1 || packageCount != 0))
        appendProvider = PR_TRUE;
    }
    else {
      // Package install.
      if (localeCount == 1 || skinCount == 1)
        appendProvider = PR_TRUE;
    }
  }

  // Load the install data source that we wish to manipulate.
  nsCOMPtr<nsIRDFDataSource> installSource;
  rv = LoadDataSource(kChromeFileName, getter_AddRefs(installSource), aUseProfile, nsnull);
  if (NS_FAILED(rv)) return rv;
  NS_ASSERTION(installSource, "failed to get installSource");

  // install our dynamic overlays
  if (aProviderType.Equals("package"))
    rv = UpdateDynamicDataSources(dataSource, PR_TRUE, aUseProfile, aRemove);
  else if (aProviderType.Equals("skin"))
    rv = UpdateDynamicDataSources(dataSource, PR_FALSE, aUseProfile, aRemove);
  if (NS_FAILED(rv)) return rv;

  // Get the literal for our loc type.
  nsAutoString locstr;
  if (aUseProfile)
    locstr.AssignLiteral("profile");
  else locstr.AssignLiteral("install");
  nsCOMPtr<nsIRDFLiteral> locLiteral;
  rv = mRDFService->GetLiteral(locstr.get(), getter_AddRefs(locLiteral));
  if (NS_FAILED(rv)) return rv;

  // Get the literal for our script access.
  nsCOMPtr<nsIRDFLiteral> scriptLiteral;
  rv = mRDFService->GetLiteral(NS_LITERAL_STRING("false").get(),
                               getter_AddRefs(scriptLiteral));
  if (NS_FAILED(rv)) return rv;

  // Build the prefix string. Only resources with this prefix string will have their
  // assertions copied.
  nsCAutoString prefix( "urn:mozilla:" );
  prefix += aProviderType;
  prefix += ":";

  // Get all the resources
  nsCOMPtr<nsISimpleEnumerator> resources;
  rv = dataSource->GetAllResources(getter_AddRefs(resources));
  if (NS_FAILED(rv)) return rv;

  // For each resource
  PRBool moreElements;
  rv = resources->HasMoreElements(&moreElements);
  if (NS_FAILED(rv)) return rv;

  while (moreElements) {
    nsCOMPtr<nsISupports> supports;
    rv = resources->GetNext(getter_AddRefs(supports));
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(supports);

    // Check against the prefix string
    const char* value;
    rv = resource->GetValueConst(&value);
    if (NS_FAILED(rv)) return rv;
    nsCAutoString val(value);
    if (val.Find(prefix) == 0) {
      // It's valid.

      if (aProviderType.Equals("package") && !val.Equals("urn:mozilla:package:root")) {
        // Add arcs for the base url and loctype
        // Get the value of the base literal.
        nsCAutoString baseURL(aBaseURL);

        // Peel off the package.
        const char* val2;
        rv = resource->GetValueConst(&val2);
        if (NS_FAILED(rv)) return rv;
        nsCAutoString value2(val2);
        PRInt32 index = value2.RFind(":");
        nsCAutoString packageName;
        value2.Right(packageName, value2.Length() - index - 1);

        if (appendPackage) {
          baseURL += packageName;
          baseURL += "/";
        }
        if (appendProvider) {
          baseURL += "content/";
        }

        nsCOMPtr<nsIRDFLiteral> baseLiteral;
        mRDFService->GetLiteral(NS_ConvertASCIItoUTF16(baseURL).get(), getter_AddRefs(baseLiteral));

        rv = nsChromeRegistry::UpdateArc(installSource, resource, mBaseURL, baseLiteral, aRemove);
        if (NS_FAILED(rv)) return rv;
        rv = nsChromeRegistry::UpdateArc(installSource, resource, mLocType, locLiteral, aRemove);
        if (NS_FAILED(rv)) return rv;
      }

      nsCOMPtr<nsIRDFContainer> container =
          do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
      if (NS_FAILED(rv)) return rv;
      rv = container->Init(dataSource, resource);
      if (NS_SUCCEEDED(rv)) {
        // XXX Deal with BAGS and ALTs? Aww, to hell with it. Who cares? I certainly don't.
        // We're a SEQ. Different rules apply. Do an AppendElement instead.
        // First do the decoration in the install data source.
        nsCOMPtr<nsIRDFContainer> installContainer;
        rv = mRDFContainerUtils->MakeSeq(installSource, resource, getter_AddRefs(installContainer));
        if (NS_FAILED(rv)) return rv;
        if (!installContainer) {
          // Already exists. Create a container instead.
          installContainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
          if (NS_FAILED(rv)) return rv;
          rv = installContainer->Init(installSource, resource);
          if (NS_FAILED(rv)) return rv;
        }

        // Put all our elements into the install container.
        nsCOMPtr<nsISimpleEnumerator> seqKids;
        rv = container->GetElements(getter_AddRefs(seqKids));
        if (NS_FAILED(rv)) return rv;
        PRBool moreKids;
        rv = seqKids->HasMoreElements(&moreKids);
        if (NS_FAILED(rv)) return rv;
        while (moreKids) {
          nsCOMPtr<nsISupports> supp;
          rv = seqKids->GetNext(getter_AddRefs(supp));
          if (NS_FAILED(rv)) return rv;
          nsCOMPtr<nsIRDFNode> kid = do_QueryInterface(supp);
          if (aRemove) {
            rv = installContainer->RemoveElement(kid, PR_TRUE);
            if (NS_FAILED(rv)) return rv;
          }
          else {
            PRInt32 index;
            rv = installContainer->IndexOf(kid, &index);
            if (NS_FAILED(rv)) return rv;
            if (index == -1) {
              rv = installContainer->AppendElement(kid);
              if (NS_FAILED(rv)) return rv;
            }
            rv = seqKids->HasMoreElements(&moreKids);
            if (NS_FAILED(rv)) return rv;
          }
        }

        // See if we're a packages seq in a skin/locale.  If so, we need to set up the baseURL, allowScripts
        // and package arcs.
        if (val.Find(":packages") != -1 && !aProviderType.EqualsLiteral("package")) {
          PRBool doAppendPackage = appendPackage;
          PRInt32 perProviderPackageCount;
          container->GetCount(&perProviderPackageCount);
          if (perProviderPackageCount > 1)
            doAppendPackage = PR_TRUE;

          // Iterate over our kids a second time.
          nsCOMPtr<nsISimpleEnumerator> seqKids2;
          rv = container->GetElements(getter_AddRefs(seqKids2));
          if (NS_FAILED(rv)) return rv;
          PRBool moreKids2;
          rv = seqKids2->HasMoreElements(&moreKids2);
          if (NS_FAILED(rv)) return rv;
          while (moreKids2) {
            nsCOMPtr<nsISupports> supp;
            rv = seqKids2->GetNext(getter_AddRefs(supp));
            if (NS_FAILED(rv)) return rv;
            nsCOMPtr<nsIRDFResource> entry(do_QueryInterface(supp));
            if (entry) {
              // Get the value of the base literal.
              nsCAutoString baseURL(aBaseURL);

              // Peel off the package and the provider.
              const char* val2;
              rv = entry->GetValueConst(&val2);
              if (NS_FAILED(rv)) return rv;
              nsCAutoString value2(val2);
              PRInt32 index = value2.RFind(":");
              nsCAutoString packageName;
              value2.Right(packageName, value2.Length() - index - 1);
              nsCAutoString remainder;
              value2.Left(remainder, index);

              nsCAutoString providerName;
              index = remainder.RFind(":");
              remainder.Right(providerName, remainder.Length() - index - 1);

              // Append them to the base literal and tack on a final slash.
              if (appendProviderName) {
                baseURL += providerName;
                baseURL += "/";
              }
              if (doAppendPackage) {
                baseURL += packageName;
                baseURL += "/";
              }
              if (appendProvider) {
                baseURL += aProviderType;
                baseURL += "/";
              }

              nsCOMPtr<nsIRDFLiteral> baseLiteral;
              mRDFService->GetLiteral(NS_ConvertASCIItoUTF16(baseURL).get(), getter_AddRefs(baseLiteral));

              rv = nsChromeRegistry::UpdateArc(installSource, entry, mBaseURL, baseLiteral, aRemove);
              if (NS_FAILED(rv)) return rv;
              if (aProviderType.EqualsLiteral("skin") && !aAllowScripts) {
                rv = nsChromeRegistry::UpdateArc(installSource, entry, mAllowScripts, scriptLiteral, aRemove);
                if (NS_FAILED(rv)) return rv;
              }

              // Now set up the package arc.
              if (index != -1) {
                // Peel off the package name.

                nsCAutoString resourceName("urn:mozilla:package:");
                resourceName += packageName;
                nsCOMPtr<nsIRDFResource> packageResource;
                rv = GetResource(resourceName, getter_AddRefs(packageResource));
                if (NS_FAILED(rv)) return rv;
                if (packageResource) {
                  rv = nsChromeRegistry::UpdateArc(installSource, entry, mPackage, packageResource, aRemove);
                  if (NS_FAILED(rv)) return rv;
                }
              }
            }

            rv = seqKids2->HasMoreElements(&moreKids2);
            if (NS_FAILED(rv)) return rv;
          }
        }
      }
      else {
        // We're not a seq. Get all of the arcs that go out.
        nsCOMPtr<nsISimpleEnumerator> arcs;
        rv = dataSource->ArcLabelsOut(resource, getter_AddRefs(arcs));
        if (NS_FAILED(rv)) return rv;

        PRBool moreArcs;
        rv = arcs->HasMoreElements(&moreArcs);
        if (NS_FAILED(rv)) return rv;
        while (moreArcs) {
          nsCOMPtr<nsISupports> supp;
          rv = arcs->GetNext(getter_AddRefs(supp));
          if (NS_FAILED(rv)) return rv;
          nsCOMPtr<nsIRDFResource> arc = do_QueryInterface(supp);

          if (arc == mPackages) {
            // We are the main entry for a skin/locale.
            // Set up our loctype and our script access
            rv = nsChromeRegistry::UpdateArc(installSource, resource, mLocType, locLiteral, aRemove);
            if (NS_FAILED(rv)) return rv;
          }

          nsCOMPtr<nsISimpleEnumerator> targets;
          rv = installSource->GetTargets(resource, arc, PR_TRUE, getter_AddRefs(targets));
          if (NS_FAILED(rv)) return rv;

          PRBool moreTargets;
          rv = targets->HasMoreElements(&moreTargets);
          if (NS_FAILED(rv)) return rv;

          while (moreTargets) {
            targets->GetNext(getter_AddRefs(supp));
            nsCOMPtr<nsIRDFNode> node(do_QueryInterface(supp));
            installSource->Unassert(resource, arc, node);

            rv = targets->HasMoreElements(&moreTargets);
            if (NS_FAILED(rv)) return rv;
          }

          if (!aRemove) {
            rv = dataSource->GetTargets(resource, arc, PR_TRUE, getter_AddRefs(targets));
            if (NS_FAILED(rv)) return rv;

            rv = targets->HasMoreElements(&moreTargets);
            if (NS_FAILED(rv)) return rv;

            while (moreTargets) {
              nsresult rv = targets->GetNext(getter_AddRefs(supp));
              if (NS_FAILED(rv)) return rv;
              nsCOMPtr<nsIRDFNode> newTarget(do_QueryInterface(supp));

              if (arc == mImage) {
                // We are an image URL.  Check to see if we're a relative URL.
                nsCOMPtr<nsIRDFLiteral> literal(do_QueryInterface(newTarget));
                if (literal) {
                  const PRUnichar* valueStr;
                  literal->GetValueConst(&valueStr);
                  nsAutoString imageURL(valueStr);
                  if (imageURL.FindChar(':') == -1) {
                    // We're relative. Prepend the base URL of the
                    // package.
                    NS_ConvertUTF8toUTF16 fullURL(aBaseURL);
                    fullURL += imageURL;
                    mRDFService->GetLiteral(fullURL.get(), getter_AddRefs(literal));
                    newTarget = do_QueryInterface(literal);
                  }
                }
              }

              rv = installSource->Assert(resource, arc, newTarget, PR_TRUE);
              if (NS_FAILED(rv)) return rv;

              rv = targets->HasMoreElements(&moreTargets);
              if (NS_FAILED(rv)) return rv;
            }
          }

          rv = arcs->HasMoreElements(&moreArcs);
          if (NS_FAILED(rv)) return rv;
        }
      }
    }
    rv = resources->HasMoreElements(&moreElements);
    if (NS_FAILED(rv)) return rv;
  }

  // Flush the install source
  nsCOMPtr<nsIRDFRemoteDataSource> remoteInstall = do_QueryInterface(installSource, &rv);
  if (NS_FAILED(rv))
    return NS_OK;

  if (!mBatchInstallFlushes) {
    rv = remoteInstall->Flush();
    if (NS_SUCCEEDED(rv) && aProviderType.Equals("package"))
      rv = FlagXPCNativeWrappers();
  }

  // XXX Handle the installation of overlays.

  return rv;
}

NS_IMETHODIMP nsChromeRegistry::SetAllowOverlaysForPackage(const PRUnichar *aPackageName, PRBool allowOverlays)
{
  nsCAutoString package("urn:mozilla:package:");
  AppendUTF16toUTF8(aPackageName, package);

  // Obtain the package resource.
  nsCOMPtr<nsIRDFResource> packageResource;
  nsresult rv = GetResource(package, getter_AddRefs(packageResource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the package resource.");
    return rv;
  }
  NS_ASSERTION(packageResource, "failed to get packageResource");

  nsCOMPtr<nsIRDFDataSource> dataSource;
  rv = LoadDataSource(kChromeFileName, getter_AddRefs(dataSource), PR_TRUE, nsnull);
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsIRDFLiteral> trueLiteral;
  mRDFService->GetLiteral(NS_LITERAL_STRING("true").get(), getter_AddRefs(trueLiteral));
  nsChromeRegistry::UpdateArc(dataSource, packageResource, 
                              mDisabled, 
                              trueLiteral, allowOverlays);

  nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(dataSource, &rv);
  if (NS_FAILED(rv)) return rv;

  rv = remote->Flush();
  return rv;
}

PRBool nsChromeRegistry::IsOverlayAllowed(nsIURI *aChromeURL)
{
  nsCAutoString package, provider, file;
  nsresult rv = SplitURL(aChromeURL, package, provider, file);
  if (NS_FAILED(rv)) return PR_FALSE;

  // Get the chrome resource for the package.
  nsCAutoString rdfpackage( "urn:mozilla:package:" );
  rdfpackage.Append(package);

  // Obtain the package resource.
  nsCOMPtr<nsIRDFResource> packageResource;
  rv = GetResource(rdfpackage, getter_AddRefs(packageResource));
  if (NS_FAILED(rv) || !packageResource) {
    NS_ERROR("Unable to obtain the package resource.");
    return PR_FALSE;
  }

  // See if the disabled arc is set for the package.
  nsCOMPtr<nsIRDFNode> disabledNode;
  mChromeDataSource->GetTarget(packageResource, mDisabled, PR_TRUE,
                               getter_AddRefs(disabledNode));
  return !disabledNode;
}

NS_IMETHODIMP nsChromeRegistry::InstallSkin(const char* aBaseURL, PRBool aUseProfile, PRBool aAllowScripts)
{
  return InstallProvider(NS_LITERAL_CSTRING("skin"),
                         nsDependentCString(aBaseURL),
                         aUseProfile, aAllowScripts, PR_FALSE);
}

NS_IMETHODIMP nsChromeRegistry::InstallLocale(const char* aBaseURL, PRBool aUseProfile)
{
  return InstallProvider(NS_LITERAL_CSTRING("locale"),
                         nsDependentCString(aBaseURL),
                         aUseProfile, PR_TRUE, PR_FALSE);
}

NS_IMETHODIMP nsChromeRegistry::InstallPackage(const char* aBaseURL, PRBool aUseProfile)
{
  return InstallProvider(NS_LITERAL_CSTRING("package"),
                         nsDependentCString(aBaseURL),
                         aUseProfile, PR_TRUE, PR_FALSE);
}

NS_IMETHODIMP nsChromeRegistry::UninstallSkin(const nsACString& aSkinName, PRBool aUseProfile)
{
  // The skin must first be deselected.
  DeselectSkin(aSkinName, aUseProfile);

  // Now uninstall it.
  return UninstallProvider(NS_LITERAL_CSTRING("skin"), aSkinName, aUseProfile);
}

NS_IMETHODIMP nsChromeRegistry::UninstallLocale(const nsACString& aLocaleName, PRBool aUseProfile)
{
  // The locale must first be deselected.
  DeselectLocale(aLocaleName, aUseProfile);

  return UninstallProvider(NS_LITERAL_CSTRING("locale"), aLocaleName, aUseProfile);
}

NS_IMETHODIMP nsChromeRegistry::UninstallPackage(const nsACString& aPackageName, PRBool aUseProfile)
{
  NS_ERROR("XXX Write me!\n");
  return NS_ERROR_FAILURE;
}

nsresult
nsChromeRegistry::UninstallProvider(const nsACString& aProviderType,
                                    const nsACString& aProviderName,
                                    PRBool aUseProfile)
{
  // XXX We are going to simply do a snip of the arc from the seq ROOT to
  // the associated package.  waterson is going to provide the ability to name
  // roots in a datasource, and only resources that are reachable from the
  // root will be saved.
  nsresult rv = NS_OK;
  nsCAutoString prefix( "urn:mozilla:" );
  prefix += aProviderType;
  prefix += ":";

  // Obtain the root.
  nsCAutoString providerRoot(prefix);
  providerRoot += "root";

  // Obtain the child we wish to remove.
  nsCAutoString specificChild(prefix);
  specificChild += aProviderName;

  // Instantiate the data source we wish to modify.
  nsCOMPtr<nsIRDFDataSource> installSource;
  rv = LoadDataSource(kChromeFileName, getter_AddRefs(installSource), aUseProfile, nsnull);
  if (NS_FAILED(rv)) return rv;
  NS_ASSERTION(installSource, "failed to get installSource");

  // Now make a container out of the root seq.
  nsCOMPtr<nsIRDFContainer> container(do_CreateInstance("@mozilla.org/rdf/container;1"));

  // Get the resource for the root.
  nsCOMPtr<nsIRDFResource> chromeResource;
  if (NS_FAILED(rv = GetResource(providerRoot, getter_AddRefs(chromeResource)))) {
    NS_ERROR("Unable to retrieve the resource corresponding to the skin/locale root.");
    return rv;
  }

  if (NS_FAILED(container->Init(installSource, chromeResource)))
    return NS_ERROR_FAILURE;

  // Get the resource for the child.
  nsCOMPtr<nsIRDFResource> childResource;
  if (NS_FAILED(rv = GetResource(specificChild, getter_AddRefs(childResource)))) {
    NS_ERROR("Unable to retrieve the resource corresponding to the skin/locale child being removed.");
    return rv;
  }

  // Remove the child from the container.
  container->RemoveElement(childResource, PR_TRUE);

  // Now flush the datasource.
  nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(installSource);
  remote->Flush();

  return NS_OK;
}

nsresult
nsChromeRegistry::GetProfileRoot(nsACString& aFileURL)
{
   nsresult rv;
   nsCOMPtr<nsIFile> userChromeDir;

   // Build a fileSpec that points to the destination
   // (profile dir + chrome)
   rv = NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR, getter_AddRefs(userChromeDir));
   if (NS_FAILED(rv) || !userChromeDir)
     return NS_ERROR_FAILURE;

   PRBool exists;
   rv = userChromeDir->Exists(&exists);
   if (NS_SUCCEEDED(rv) && !exists) {
     rv = userChromeDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
     if (NS_SUCCEEDED(rv)) {
       // now we need to put the userContent.css and userChrome.css
       // stubs into place

       // first get the locations of the defaults
       nsCOMPtr<nsIFile> defaultUserContentFile;
       nsCOMPtr<nsIFile> defaultUserChromeFile;
       rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_50_DIR,
                                   getter_AddRefs(defaultUserContentFile));
       if (NS_FAILED(rv))
         rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR,
                                     getter_AddRefs(defaultUserContentFile));
       if (NS_FAILED(rv))
         return(rv);
       rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_50_DIR,
                                   getter_AddRefs(defaultUserChromeFile));
       if (NS_FAILED(rv))
         rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR,
                                     getter_AddRefs(defaultUserChromeFile));
       if (NS_FAILED(rv))
         return(rv);
       defaultUserContentFile->AppendNative(NS_LITERAL_CSTRING("chrome"));
       defaultUserContentFile->AppendNative(NS_LITERAL_CSTRING("userContent-example.css"));
       defaultUserChromeFile->AppendNative(NS_LITERAL_CSTRING("chrome"));
       defaultUserChromeFile->AppendNative(NS_LITERAL_CSTRING("userChrome-example.css"));

       const nsAFlatCString& empty = EmptyCString();

       // copy along
       // It ain't an error if these files don't exist
       defaultUserContentFile->CopyToNative(userChromeDir, empty);
       defaultUserChromeFile->CopyToNative(userChromeDir, empty);
     }
   }
   if (NS_FAILED(rv))
     return rv;

   return NS_GetURLSpecFromFile(userChromeDir, aFileURL);
}

nsresult
nsChromeRegistry::GetInstallRoot(nsIFile** aFileURL)
{
  return NS_GetSpecialDirectory(NS_APP_CHROME_DIR, aFileURL);
}

void
nsChromeRegistry::FlushAllCaches()
{
  nsCOMPtr<nsIObserverService> obsSvc =
    do_GetService("@mozilla.org/observer-service;1");
  NS_ASSERTION(obsSvc, "Couldn't get observer service.");

  obsSvc->NotifyObservers((nsIChromeRegistry*) this,
                          NS_CHROME_FLUSH_TOPIC, nsnull);

}  

// xxxbsmedberg Move me to nsIWindowMediator
NS_IMETHODIMP
nsChromeRegistry::ReloadChrome()
{
  FlushAllCaches();
  // Do a reload of all top level windows.
  nsresult rv = NS_OK;

  // Get the window mediator
  nsCOMPtr<nsIWindowMediator> windowMediator = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv)) {
    nsCOMPtr<nsISimpleEnumerator> windowEnumerator;

    rv = windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
    if (NS_SUCCEEDED(rv)) {
      // Get each dom window
      PRBool more;
      rv = windowEnumerator->HasMoreElements(&more);
      if (NS_FAILED(rv)) return rv;
      while (more) {
        nsCOMPtr<nsISupports> protoWindow;
        rv = windowEnumerator->GetNext(getter_AddRefs(protoWindow));
        if (NS_SUCCEEDED(rv)) {
          nsCOMPtr<nsIDOMWindowInternal> domWindow =
            do_QueryInterface(protoWindow);
          if (domWindow) {
            nsCOMPtr<nsIDOMLocation> location;
            domWindow->GetLocation(getter_AddRefs(location));
            if (location) {
              rv = location->Reload(PR_FALSE);
              if (NS_FAILED(rv)) return rv;
            }
          }
        }
        rv = windowEnumerator->HasMoreElements(&more);
        if (NS_FAILED(rv)) return rv;
      }
    }
  }
  return rv;
}

nsresult
nsChromeRegistry::GetArcs(nsIRDFDataSource* aDataSource,
                          const nsACString& aType,
                          nsISimpleEnumerator** aResult)
{
  nsresult rv;

  nsCOMPtr<nsIRDFContainer> container =
      do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
  if (NS_FAILED(rv))
    return NS_OK;

  nsCAutoString lookup("chrome:");
  lookup += aType;

  // Get the chromeResource from this lookup string
  nsCOMPtr<nsIRDFResource> chromeResource;
  if (NS_FAILED(rv = GetResource(lookup, getter_AddRefs(chromeResource)))) {
    NS_ERROR("Unable to retrieve the resource corresponding to the chrome skin or content.");
    return rv;
  }

  if (NS_FAILED(container->Init(aDataSource, chromeResource)))
    return NS_OK;

  nsCOMPtr<nsISimpleEnumerator> arcs;
  if (NS_FAILED(container->GetElements(getter_AddRefs(arcs))))
    return NS_OK;

  *aResult = arcs;
  NS_IF_ADDREF(*aResult);
  return NS_OK;
}

nsresult
nsChromeRegistry::AddToCompositeDataSource(PRBool aUseProfile)
{
  nsresult rv = NS_OK;
  if (!mChromeDataSource) {
    mChromeDataSource = do_CreateInstance(
        "@mozilla.org/rdf/datasource;1?name=composite-datasource", &rv);
    if (NS_FAILED(rv))
      return rv;

    // Also create and hold on to our UI data source.
    rv = NS_NewChromeUIDataSource(mChromeDataSource, getter_AddRefs(mUIDataSource));
    if (NS_FAILED(rv)) return rv;
  }

  if (aUseProfile) {
    // Profiles take precedence.  Load them first.
    nsCOMPtr<nsIRDFDataSource> dataSource;
    LoadDataSource(kChromeFileName, getter_AddRefs(dataSource), PR_TRUE, nsnull);
    mChromeDataSource->AddDataSource(dataSource);
  }

  // Always load the install dir datasources
  LoadDataSource(kChromeFileName, getter_AddRefs(mInstallDirChromeDataSource), PR_FALSE, nsnull);
  mChromeDataSource->AddDataSource(mInstallDirChromeDataSource);

  return rv;
}

nsresult
nsChromeRegistry::FlagXPCNativeWrappers()
{
  nsresult rv;
  // List all packages that want XPC native wrappers
  nsCOMPtr<nsIXPConnect> xpc(do_GetService("@mozilla.org/js/xpc/XPConnect;1", &rv));
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsISimpleEnumerator> arcs;
  nsCOMPtr<nsIRDFLiteral> trueLiteral;
  mRDFService->GetLiteral(NS_LITERAL_STRING("true").get(), getter_AddRefs(trueLiteral));
  rv = mChromeDataSource->GetSources(mXPCNativeWrappers, trueLiteral, PR_TRUE,
                                     getter_AddRefs(arcs));
  if (NS_FAILED(rv)) return rv;

  nsCAutoString uri;
  PRBool more;
  rv = arcs->HasMoreElements(&more);
  if (NS_FAILED(rv)) return rv;
  while (more) {
    nsCOMPtr<nsISupports> supp;
    rv = arcs->GetNext(getter_AddRefs(supp));
    if (NS_FAILED(rv)) return rv;
    nsCOMPtr<nsIRDFResource> package(do_QueryInterface(supp));
    if (package) {
      const char urn[] = "urn:mozilla:package:";
      const char* source;
      package->GetValueConst(&source);
      if (!memcmp(source, urn, sizeof urn - 1)) {
        uri.AssignLiteral("chrome://");
        uri.Append(source + sizeof urn - 1);
        uri.Append('/');
        rv = xpc->FlagSystemFilenamePrefix(uri.get(), PR_TRUE);
        NS_ENSURE_SUCCESS(rv, rv);
      }
    }
    rv = arcs->HasMoreElements(&more);
    if (NS_FAILED(rv)) return rv;
  }

  return NS_OK;
}

nsresult nsChromeRegistry::LoadStyleSheetWithURL(nsIURI* aURL, PRBool aEnableUnsafeRules, nsICSSStyleSheet** aSheet)
{
  *aSheet = nsnull;

  if (!mCSSLoader) {
    nsresult rv;
    mCSSLoader = do_CreateInstance(kCSSLoaderCID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  return mCSSLoader->LoadSheetSync(aURL, aEnableUnsafeRules,
                                   aEnableUnsafeRules, aSheet);
}

nsresult nsChromeRegistry::LoadInstallDataSource()
{
  nsCOMPtr<nsIFile> installRootFile;
  
  nsresult rv = GetInstallRoot(getter_AddRefs(installRootFile));
  NS_ENSURE_SUCCESS(rv, rv);
  
  rv = NS_GetURLSpecFromFile(installRootFile, mInstallRoot);
  NS_ENSURE_SUCCESS(rv, rv);
  
  mInstallInitialized = PR_TRUE;
  return AddToCompositeDataSource(PR_FALSE);
}

nsresult nsChromeRegistry::LoadProfileDataSource()
{
  mLegacyOverlayinfo = PR_FALSE;
  nsresult rv = GetProfileRoot(mProfileRoot);
  if (NS_SUCCEEDED(rv)) {
    // Load the profile search path for skins, content, and locales
    // Prepend them to our list of substitutions.
    mProfileInitialized = mInstallInitialized = PR_TRUE;
    mChromeDataSource = nsnull;
    rv = AddToCompositeDataSource(PR_TRUE);
    if (NS_FAILED(rv)) return rv;
    rv = FlagXPCNativeWrappers();
    if (NS_FAILED(rv)) return rv;

    // XXX this sucks ASS. This is a temporary hack until we get
    // around to fixing the skin switching bugs.
    // Select and Remove skins based on a pref set in a previous session.
    nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
    if (prefBranch) {
      nsXPIDLCString skinToSelect;
      rv = prefBranch->GetCharPref("general.skins.selectedSkin", getter_Copies(skinToSelect));
      if (NS_SUCCEEDED(rv)) {
        rv = SelectSkin(skinToSelect, PR_TRUE);
        if (NS_SUCCEEDED(rv))
          prefBranch->DeleteBranch("general.skins.selectedSkin");
      }
    }

    // We have to flush the chrome skin cache...
    FlushSkinCaches();
    
    // make sure we don't lose any old profile overlayinfo
    // by checking the existence of the respective overlayinfo/ directory
    nsCOMPtr<nsIFile> overlayinfoDir;
    rv = NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR, getter_AddRefs(overlayinfoDir));
    if (NS_SUCCEEDED(rv))
    {
      rv = overlayinfoDir->AppendNative(NS_LITERAL_CSTRING("overlayinfo"));
      if (NS_SUCCEEDED(rv))
      {
        PRBool isLegacyOverlayinfo;
        rv = overlayinfoDir->IsDirectory(&isLegacyOverlayinfo);
        mLegacyOverlayinfo = NS_SUCCEEDED(rv) && isLegacyOverlayinfo;
      }
    }

  }
  return NS_OK;
}

NS_IMETHODIMP
nsChromeRegistry::AllowScriptsForPackage(nsIURI* aChromeURI, PRBool *aResult)
{
  *aResult = PR_TRUE;

  // split the url
  nsCAutoString package, provider, file;
  nsresult rv;
  rv = SplitURL(aChromeURI, package, provider, file);
  if (NS_FAILED(rv)) return NS_OK;

  // verify it's a skin url
  if (!provider.Equals("skin"))
    return NS_OK;

  // XXX could factor this with selectproviderforpackage
  // get the selected skin resource for the package
  nsCOMPtr<nsIRDFNode> selectedProvider;

  nsCAutoString resourceStr("urn:mozilla:package:");
  resourceStr += package;

  // Obtain the resource.
  nsCOMPtr<nsIRDFResource> resource;
  rv = GetResource(resourceStr, getter_AddRefs(resource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the package resource.");
    return rv;
  }

  if (NS_FAILED(rv = mChromeDataSource->GetTarget(resource, mSelectedSkin, PR_TRUE, getter_AddRefs(selectedProvider))))
    return NS_OK;

  if (!selectedProvider) {
    rv = FindProvider(package, provider, mSelectedSkin, getter_AddRefs(selectedProvider));
    if (NS_FAILED(rv)) return rv;
  }
  if (!selectedProvider)
    return NS_OK;

  resource = do_QueryInterface(selectedProvider, &rv);
  if (NS_SUCCEEDED(rv)) {
    // get its script access
    nsCOMPtr<nsIRDFNode> scriptAccessNode;
    mChromeDataSource->GetTarget(resource, mAllowScripts, PR_TRUE,
                                 getter_AddRefs(scriptAccessNode));
    if (scriptAccessNode)
      *aResult = PR_FALSE;
  }
  return NS_OK;
}

NS_IMETHODIMP
nsChromeRegistry::AllowContentToAccess(nsIURI*, PRBool *aResult)
{
  *aResult = PR_TRUE;
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
nsChromeRegistry::CheckForNewChrome()
{
  nsresult rv;

  rv = LoadInstallDataSource();
  if (NS_FAILED(rv)) return rv;

  // open the installed-chrome file
  nsCOMPtr<nsILocalFile> listFile;
  nsCOMPtr<nsIProperties> directoryService =
           do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
  if (NS_FAILED(rv))
    return rv;
  rv = directoryService->Get(NS_APP_CHROME_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(listFile));
  if (NS_FAILED(rv))
    return rv;

  nsCOMPtr<nsIFile> chromeFile;
  rv = listFile->Clone(getter_AddRefs(chromeFile));
  if (NS_FAILED(rv)) return rv;
  rv = chromeFile->AppendNative(kChromeFileName);
  if (NS_FAILED(rv)) return rv;
  // XXXldb For the case where the file is nonexistent, we're depending
  // on the fact that the nsInt64 constructor initializes to 0 and
  // |GetLastModifiedTime| doesn't touch the out parameter.
  nsInt64 chromeDate;
  (void)chromeFile->GetLastModifiedTime(&chromeDate.mValue);

  rv = listFile->AppendRelativeNativePath(kInstalledChromeFileName);
  if (NS_FAILED(rv)) return rv;
  nsInt64 listFileDate;
  (void)listFile->GetLastModifiedTime(&listFileDate.mValue);

  if (listFileDate < chromeDate)
    return NS_OK;

  PRFileDesc *file;
  rv = listFile->OpenNSPRFileDesc(PR_RDONLY, 0, &file);
  if (NS_FAILED(rv)) return rv;

  // file is open.

  PRFileInfo finfo;

  if (PR_GetOpenFileInfo(file, &finfo) == PR_SUCCESS) {
    char *dataBuffer = new char[finfo.size+1];
    if (dataBuffer) {
      PRInt32 bufferSize = PR_Read(file, dataBuffer, finfo.size);
      if (bufferSize > 0) {
        mBatchInstallFlushes = PR_TRUE;
        rv = ProcessNewChromeBuffer(dataBuffer, bufferSize);
        mBatchInstallFlushes = PR_FALSE;
      }
      delete [] dataBuffer;
    }
  }
  PR_Close(file);
  // listFile->Remove(PR_FALSE);

  return rv;
}

// flaming unthreadsafe function
nsresult
nsChromeRegistry::ProcessNewChromeBuffer(char *aBuffer, PRInt32 aLength)
{
  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)
  PRBool isProfile;
  PRBool isSelection;

  NS_NAMED_LITERAL_CSTRING(content, "content");
  NS_NAMED_LITERAL_CSTRING(locale, "locale");
  NS_NAMED_LITERAL_CSTRING(skin, "skin");
  NS_NAMED_LITERAL_CSTRING(profile, "profile");
  NS_NAMED_LITERAL_CSTRING(select, "select");
  NS_NAMED_LITERAL_CSTRING(path, "path");
  nsCAutoString fileURL;
  nsCAutoString chromeURL;

  // 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 parsed line
    isSelection = select.Equals(chromeLocType);
    isProfile = profile.Equals(chromeProfile);
    if (isProfile && !mProfileInitialized) 
    { // load profile chrome.rdf only if needed
      rv = LoadProfileDataSource();
      if (NS_FAILED(rv)) 
        return rv;
    }

    if (path.Equals(chromeLocType)) {
      // 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(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
      if (NS_FAILED(rv))
        return rv;
      // assuming chromeLocation is given in the charset of the current locale
      rv = chromeFile->InitWithNativePath(nsDependentCString(chromeLocation));
      if (NS_FAILED(rv))
        return rv;

      /* 
       * all we want here is the canonical url
       */
      rv = NS_GetURLSpecFromFile(chromeFile, chromeURL);
      if (NS_FAILED(rv)) return rv;

      /* if we're a file, we must be a jar file. do appropriate string munging.
       * otherwise, the string we got from GetSpec is fine.
       */
      PRBool isFile;
      rv = chromeFile->IsFile(&isFile);
      if (NS_FAILED(rv))
        return rv;

      if (isFile) {
        fileURL = "jar:";
        fileURL += chromeURL;
        fileURL += "!/";
        chromeURL = fileURL;
      }
    }
    else {
      // not "path". assume "url".
      chromeURL = chromeLocation;
    }

    // process the line
    if (skin.Equals(chromeType)) {
      if (isSelection) {

        rv = SelectSkin(nsDependentCString(chromeLocation), isProfile);
#ifdef DEBUG
        printf("***** Chrome Registration: Selecting skin %s as default\n", (const char*)chromeLocation);
#endif
      }
      else
        rv = InstallSkin(chromeURL.get(), isProfile, PR_FALSE);
    }
    else if (content.Equals(chromeType))
      rv = InstallPackage(chromeURL.get(), isProfile);
    else if (locale.Equals(chromeType)) {
      if (isSelection) {

        rv = SelectLocale(nsDependentCString(chromeLocation), isProfile);
#ifdef DEBUG
        printf("***** Chrome Registration: Selecting locale %s as default\n", (const char*)chromeLocation);
#endif
      }
      else
        rv = InstallLocale(chromeURL.get(), isProfile);
    }
    
    while (aBuffer < bufferEnd && (*aBuffer == '\0' || *aBuffer == ' ' || *aBuffer == '\r' || *aBuffer == '\n'))
      ++aBuffer;
  }

  nsCOMPtr<nsIRDFDataSource> dataSource;
  LoadDataSource(kChromeFileName, getter_AddRefs(dataSource), PR_FALSE, nsnull);
  nsCOMPtr<nsIRDFRemoteDataSource> remote(do_QueryInterface(dataSource));
  remote->Flush();
  return NS_OK;
}

PRBool
nsChromeRegistry::GetProviderCount(const nsACString& aProviderType, nsIRDFDataSource* aDataSource)
{
  nsresult rv;

  nsCAutoString rootStr("urn:mozilla:");
  rootStr += aProviderType;
  rootStr += ":root";

  // obtain the provider root resource
  nsCOMPtr<nsIRDFResource> resource;
  rv = GetResource(rootStr, getter_AddRefs(resource));
  if (NS_FAILED(rv))
    return 0;

  // wrap it in a container
  nsCOMPtr<nsIRDFContainer> container =
      do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
  if (NS_FAILED(rv)) return 0;

  rv = container->Init(aDataSource, resource);
  if (NS_FAILED(rv)) return 0;

  PRInt32 count;
  container->GetCount(&count);
  return count;
}


NS_IMETHODIMP nsChromeRegistry::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
{
  nsresult rv = NS_OK;

  if (!strcmp("profile-before-change", aTopic)) {

    mChromeDataSource = nsnull;
    mInstallInitialized = mProfileInitialized = PR_FALSE;

    if (!strcmp("shutdown-cleanse", NS_ConvertUTF16toUTF8(someData).get())) {
      nsCOMPtr<nsIFile> userChromeDir;
      rv = NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR, getter_AddRefs(userChromeDir));
      if (NS_SUCCEEDED(rv) && userChromeDir)
        rv = userChromeDir->Remove(PR_TRUE);
    }
    FlushAllCaches();
  }
  else if (!strcmp("profile-after-change", aTopic)) {
    rv = LoadProfileDataSource();
  }

  return rv;
}

NS_IMETHODIMP nsChromeRegistry::CheckThemeVersion(const nsACString& aSkin,
                                                  PRBool* aResult)
{
  return CheckProviderVersion(NS_LITERAL_CSTRING("skin"), aSkin, mSkinVersion, aResult);
}


NS_IMETHODIMP nsChromeRegistry::CheckLocaleVersion(const nsACString& aLocale,
                                                   PRBool* aResult)
{
  nsCAutoString provider("locale");
  return CheckProviderVersion(NS_LITERAL_CSTRING("skin"), aLocale, mLocaleVersion, aResult);
}


nsresult
nsChromeRegistry::CheckProviderVersion (const nsACString& aProviderType,
                                        const nsACString& aProviderName,
                                        nsIRDFResource* aSelectionArc,
                                        PRBool *aCompatible)
{
  *aCompatible = PR_TRUE;

  // Build the provider resource str.
  // e.g., urn:mozilla:skin:aqua/1.0
  nsCAutoString resourceStr( "urn:mozilla:" );
  resourceStr += aProviderType;
  resourceStr += ":";
  resourceStr += aProviderName;

  // Obtain the provider resource.
  nsresult rv = NS_OK;
  nsCOMPtr<nsIRDFResource> resource;
  rv = GetResource(resourceStr, getter_AddRefs(resource));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the package resource.");
    return rv;
  }

  // Follow the packages arc to the package resources.
  nsCOMPtr<nsIRDFNode> packageList;
  rv = mChromeDataSource->GetTarget(resource, mPackages, PR_TRUE, getter_AddRefs(packageList));
  if (NS_FAILED(rv)) {
    NS_ERROR("Unable to obtain the SEQ for the package list.");
    return rv;
  }
  // ok for packageList to be null here -- it just means that we haven't encountered that package yet

  nsCOMPtr<nsIRDFResource> packageSeq(do_QueryInterface(packageList, &rv));
  if (NS_FAILED(rv)) return rv;

  // Build an RDF container to wrap the SEQ
  nsCOMPtr<nsIRDFContainer> container =
      do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
  if (NS_FAILED(rv))
    return rv;

  rv = container->Init(mChromeDataSource, packageSeq);
  if (NS_FAILED(rv))
    return rv;

  nsCOMPtr<nsISimpleEnumerator> arcs;

  rv = container->GetElements(getter_AddRefs(arcs));
  if (NS_FAILED(rv))
    return NS_OK;

  // For each skin-package entry, follow the arcs to the real package
  // resource.
  PRBool more;
  rv = arcs->HasMoreElements(&more);
  if (NS_FAILED(rv)) return rv;
  while (more) {
    nsCOMPtr<nsISupports> packageSkinEntry;
    rv = arcs->GetNext(getter_AddRefs(packageSkinEntry));
    if (NS_SUCCEEDED(rv) && packageSkinEntry) {
      nsCOMPtr<nsIRDFResource> entry = do_QueryInterface(packageSkinEntry);
      if (entry) {
        // Obtain the real package resource.
        nsCOMPtr<nsIRDFNode> packageNode;
        rv = mChromeDataSource->GetTarget(entry, mPackage, PR_TRUE, getter_AddRefs(packageNode));
        if (NS_FAILED(rv)) {
          NS_ERROR("Unable to obtain the package resource.");
          return rv;
        }

        nsCOMPtr<nsIRDFResource> packageResource(do_QueryInterface(packageNode));
        if (packageResource) {
          nsCOMPtr<nsIRDFNode> packageNameNode;
          mChromeDataSource->GetTarget(packageResource, mName, PR_TRUE,
                                       getter_AddRefs(packageNameNode));

          if (packageNameNode) {
            nsCOMPtr<nsIRDFNode> packageVersionNode;
            mChromeDataSource->GetTarget(packageResource, aSelectionArc, PR_TRUE,
                                         getter_AddRefs(packageVersionNode));

            if (packageVersionNode) {
              mChromeDataSource->HasAssertion(entry, aSelectionArc,
                                              packageVersionNode, PR_TRUE,
                                              aCompatible);
              // if just one theme package is NOT compatible, the theme will be disabled
              if (!*aCompatible)
                return NS_OK;
            }
          }
        }
      }
    }
    rv = arcs->HasMoreElements(&more);
    if (NS_FAILED(rv))
      return rv;
  }

  return NS_OK;
}

