Back to index

lightning-sunbird  0.9+nobinonly
nsChromeRegistry.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Original Author: David W. Hyatt (hyatt@netscape.com)
00024  *   Gagan Saksena <gagan@netscape.com>
00025  *   Benjamin Smedberg <benjamin@smedbergs.us>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsChromeRegistry.h"
00042 
00043 #include <string.h>
00044 
00045 #include "prio.h"
00046 #include "prprf.h"
00047 
00048 #include "nsAppDirectoryServiceDefs.h"
00049 #include "nsArrayEnumerator.h"
00050 #include "nsStringEnumerator.h"
00051 #include "nsEnumeratorUtils.h"
00052 #include "nsCOMPtr.h"
00053 #include "nsDOMError.h"
00054 #include "nsEscape.h"
00055 #include "nsInt64.h"
00056 #include "nsLayoutCID.h"
00057 #include "nsNetCID.h"
00058 #include "nsNetUtil.h"
00059 #include "nsReadableUtils.h"
00060 #include "nsStaticAtom.h"
00061 #include "nsString.h"
00062 #include "nsUnicharUtils.h"
00063 #include "nsWidgetsCID.h"
00064 #include "nsXPIDLString.h"
00065 #include "nsXULAppAPI.h"
00066 
00067 #include "nsIAtom.h"
00068 #include "nsIBindingManager.h"
00069 #include "nsICommandLine.h"
00070 #include "nsICSSLoader.h"
00071 #include "nsICSSStyleSheet.h"
00072 #include "nsIConsoleService.h"
00073 #include "nsIDirectoryService.h"
00074 #include "nsIDocument.h"
00075 #include "nsIDOMDocument.h"
00076 #include "nsIDocShell.h"
00077 #include "nsIDocumentObserver.h"
00078 #include "nsIDOMElement.h"
00079 #include "nsIDOMLocation.h"
00080 #include "nsIDOMWindowCollection.h"
00081 #include "nsIDOMWindowInternal.h"
00082 #include "nsIFileChannel.h"
00083 #include "nsIFileURL.h"
00084 #include "nsIIOService.h"
00085 #include "nsIJARURI.h"
00086 #include "nsILocalFile.h"
00087 #include "nsILocaleService.h"
00088 #include "nsILookAndFeel.h"
00089 #include "nsIObserverService.h"
00090 #include "nsIPrefService.h"
00091 #include "nsIPrefBranch.h"
00092 #include "nsIPrefBranch2.h"
00093 #include "nsIPresShell.h"
00094 #include "nsIScriptError.h"
00095 #include "nsIServiceManager.h"
00096 #include "nsISimpleEnumerator.h"
00097 #include "nsIStyleSheet.h"
00098 #include "nsISupportsArray.h"
00099 #include "nsIVersionComparator.h"
00100 #include "nsIWindowMediator.h"
00101 #include "nsIXPConnect.h"
00102 #include "nsIXULAppInfo.h"
00103 #include "nsIXULRuntime.h"
00104 
00105 // keep all the RDF stuff together, in case we can remove it in the far future
00106 #include "rdf.h"
00107 #include "nsRDFCID.h"
00108 #include "nsIRDFService.h"
00109 #include "nsIRDFDataSource.h"
00110 #include "nsIRDFObserver.h"
00111 #include "nsIRDFRemoteDataSource.h"
00112 #include "nsIRDFXMLSink.h"
00113 #include "nsIRDFResource.h"
00114 #include "nsIRDFDataSource.h"
00115 #include "nsIRDFContainer.h"
00116 #include "nsIRDFContainerUtils.h"
00117 
00118 #define UILOCALE_CMD_LINE_ARG "UILocale"
00119 
00120 #define MATCH_OS_LOCALE_PREF "intl.locale.matchOS"
00121 #define SELECTED_LOCALE_PREF "general.useragent.locale"
00122 #define SELECTED_SKIN_PREF   "general.skins.selectedSkin"
00123 
00124 static NS_DEFINE_CID(kCSSLoaderCID, NS_CSS_LOADER_CID);
00125 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
00126 
00127 nsChromeRegistry* nsChromeRegistry::gChromeRegistry;
00128 
00129 #define CHROME_URI "http://www.mozilla.org/rdf/chrome#"
00130 
00131 DEFINE_RDF_VOCAB(CHROME_URI, CHROME, packages);
00132 DEFINE_RDF_VOCAB(CHROME_URI, CHROME, package);
00133 DEFINE_RDF_VOCAB(CHROME_URI, CHROME, name);
00134 DEFINE_RDF_VOCAB(CHROME_URI, CHROME, platformPackage);
00135 
00137 
00138 static void
00139 LogMessage(const char* aMsg, ...)
00140 {
00141   nsCOMPtr<nsIConsoleService> console 
00142     (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
00143   if (!console)
00144     return;
00145 
00146   va_list args;
00147   va_start(args, aMsg);
00148   char* formatted = PR_vsmprintf(aMsg, args);
00149   va_end(args);
00150   if (!formatted)
00151     return;
00152 
00153   console->LogStringMessage(NS_ConvertUTF8toUTF16(formatted).get());
00154   PR_smprintf_free(formatted);
00155 }
00156 
00157 static void
00158 LogMessageWithContext(nsIURI* aURL, PRUint32 aLineNumber, PRUint32 flags,
00159                       const char* aMsg, ...)
00160 {
00161   nsresult rv;
00162 
00163   nsCOMPtr<nsIConsoleService> console 
00164     (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
00165 
00166   nsCOMPtr<nsIScriptError> error
00167     (do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
00168   if (!console || !error)
00169     return;
00170 
00171   va_list args;
00172   va_start(args, aMsg);
00173   char* formatted = PR_vsmprintf(aMsg, args);
00174   va_end(args);
00175   if (!formatted)
00176     return;
00177 
00178   nsCString spec;
00179   if (aURL)
00180     aURL->GetSpec(spec);
00181 
00182   rv = error->Init(NS_ConvertUTF8toUTF16(formatted).get(),
00183                    NS_ConvertUTF8toUTF16(spec).get(),
00184                    nsnull,
00185                    aLineNumber, 0, flags, "chrome registration");
00186   PR_smprintf_free(formatted);
00187 
00188   if (NS_FAILED(rv))
00189     return;
00190 
00191   console->LogMessage(error);
00192 }
00193 
00194 // We use a "best-fit" algorithm for matching locales and themes. 
00195 // 1) the exact selected locale/theme
00196 // 2) (locales only) same language, different country
00197 //    e.g. en-GB is the selected locale, only en-US is available
00198 // 3) any available locale/theme
00199 
00206 static PRBool
00207 LanguagesMatch(const nsACString& a, const nsACString& b)
00208 {
00209   if (a.Length() < 2 || b.Length() < 2)
00210     return PR_FALSE;
00211 
00212   nsACString::const_iterator as, ae, bs, be;
00213   a.BeginReading(as);
00214   a.EndReading(ae);
00215   b.BeginReading(bs);
00216   b.EndReading(be);
00217 
00218   while (*as == *bs) {
00219     if (*as == '-')
00220       return PR_TRUE;
00221  
00222     ++as; ++bs;
00223 
00224     // reached the end
00225     if (as == ae && bs == be)
00226       return PR_TRUE;
00227 
00228     // "a" is short
00229     if (as == ae)
00230       return (*bs == '-');
00231 
00232     // "b" is short
00233     if (bs == be)
00234       return (*as == '-');
00235   }
00236 
00237   return PR_FALSE;
00238 }
00239 
00240 nsChromeRegistry::ProviderEntry*
00241 nsChromeRegistry::nsProviderArray::GetProvider(const nsACString& aPreferred, MatchType aType)
00242 {
00243   PRInt32 i = mArray.Count();
00244   if (!i)
00245     return nsnull;
00246 
00247   ProviderEntry* found = nsnull;  // Only set if we find a partial-match locale
00248   ProviderEntry* entry;
00249 
00250   while (i--) {
00251     entry = NS_REINTERPRET_CAST(ProviderEntry*, mArray[i]);
00252     if (aPreferred.Equals(entry->provider))
00253       return entry;
00254 
00255     if (aType != LOCALE)
00256       continue;
00257 
00258     if (LanguagesMatch(aPreferred, entry->provider)) {
00259       found = entry;
00260       continue;
00261     }
00262 
00263     if (!found && entry->provider.EqualsLiteral("en-US"))
00264       found = entry;
00265   }
00266 
00267   if (!found && aType != EXACT)
00268     return entry;
00269 
00270   return found;
00271 }
00272 
00273 nsIURI*
00274 nsChromeRegistry::nsProviderArray::GetBase(const nsACString& aPreferred, MatchType aType)
00275 {
00276   ProviderEntry* provider = GetProvider(aPreferred, aType);
00277 
00278   if (!provider)
00279     return nsnull;
00280 
00281   return provider->baseURI;
00282 }
00283 
00284 const nsACString&
00285 nsChromeRegistry::nsProviderArray::GetSelected(const nsACString& aPreferred, MatchType aType)
00286 {
00287   ProviderEntry* entry = GetProvider(aPreferred, aType);
00288 
00289   if (entry)
00290     return entry->provider;
00291 
00292   return EmptyCString();
00293 }
00294 
00295 void
00296 nsChromeRegistry::nsProviderArray::SetBase(const nsACString& aProvider, nsIURI* aBaseURL)
00297 {
00298   ProviderEntry* provider = GetProvider(aProvider, EXACT);
00299 
00300   if (provider) {
00301     provider->baseURI = aBaseURL;
00302     return;
00303   }
00304 
00305   // no existing entries, add a new one
00306   provider = new ProviderEntry(aProvider, aBaseURL);
00307   if (!provider)
00308     return; // It's safe to silently fail on OOM
00309 
00310   mArray.AppendElement(provider);
00311 }
00312 
00313 void
00314 nsChromeRegistry::nsProviderArray::EnumerateToArray(nsCStringArray *a)
00315 {
00316   PRInt32 i = mArray.Count();
00317   while (i--) {
00318     ProviderEntry *entry = NS_REINTERPRET_CAST(ProviderEntry*, mArray[i]);
00319     a->AppendCString(entry->provider);
00320   }
00321 }
00322 
00323 void
00324 nsChromeRegistry::nsProviderArray::Clear()
00325 {
00326   PRInt32 i = mArray.Count();
00327   while (i--) {
00328     ProviderEntry* entry = NS_REINTERPRET_CAST(ProviderEntry*, mArray[i]);
00329     delete entry;
00330   }
00331 
00332   mArray.Clear();
00333 }
00334 
00335 nsChromeRegistry::PackageEntry::PackageEntry(const nsACString& aPackage) :
00336   package(aPackage), flags(0)
00337 {
00338 }
00339 
00340 const void*
00341 nsChromeRegistry::GetKey(PLDHashTable *table, PLDHashEntryHdr *entry)
00342 {
00343   PackageEntry* pentry = NS_STATIC_CAST(PackageEntry*, entry);
00344   return (nsACString*) &pentry->package;
00345 }
00346 
00347 PLHashNumber
00348 nsChromeRegistry::HashKey(PLDHashTable *table, const void *key)
00349 {
00350   const nsACString& str = *NS_REINTERPRET_CAST(const nsACString*, key);
00351   return HashString(str);
00352 }
00353 
00354 PRBool
00355 nsChromeRegistry::MatchKey(PLDHashTable *table, const PLDHashEntryHdr *entry,
00356                            const void *key)
00357 {
00358   const nsACString& str = *NS_REINTERPRET_CAST(const nsACString*, key);
00359   const PackageEntry* pentry = NS_STATIC_CAST(const PackageEntry*, entry);
00360   return str.Equals(pentry->package);
00361 }
00362 
00363 void
00364 nsChromeRegistry::ClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
00365 {
00366   PackageEntry* pentry = NS_STATIC_CAST(PackageEntry*, entry);
00367   pentry->~PackageEntry();
00368 }
00369 
00370 PRBool
00371 nsChromeRegistry::InitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
00372                             const void *key)
00373 {
00374   const nsACString& str = *NS_REINTERPRET_CAST(const nsACString*, key);
00375 
00376   new (entry) PackageEntry(str);
00377   return PR_TRUE;
00378 }
00379 
00380 const PLDHashTableOps
00381 nsChromeRegistry::kTableOps = {
00382   PL_DHashAllocTable,
00383   PL_DHashFreeTable,
00384   GetKey,
00385   HashKey,
00386   MatchKey,
00387   PL_DHashMoveEntryStub,
00388   ClearEntry,
00389   PL_DHashFinalizeStub,
00390   InitEntry
00391 };
00392 
00393 void
00394 nsChromeRegistry::OverlayListEntry::AddURI(nsIURI* aURI)
00395 {
00396   PRInt32 i = mArray.Count();
00397   while (i--) {
00398     PRBool equals;
00399     if (NS_SUCCEEDED(aURI->Equals(mArray[i], &equals)) && equals)
00400         return;
00401   }
00402 
00403   mArray.AppendObject(aURI);
00404 }
00405 
00406 void
00407 nsChromeRegistry::OverlayListHash::Add(nsIURI* aBase, nsIURI* aOverlay)
00408 {
00409   OverlayListEntry* entry = mTable.PutEntry(aBase);
00410   if (entry)
00411     entry->AddURI(aOverlay);
00412 }
00413 
00414 const nsCOMArray<nsIURI>*
00415 nsChromeRegistry::OverlayListHash::GetArray(nsIURI* aBase)
00416 {
00417   OverlayListEntry* entry = mTable.GetEntry(aBase);
00418   if (!entry)
00419     return nsnull;
00420 
00421   return &entry->mArray;
00422 }
00423 
00424 nsChromeRegistry::~nsChromeRegistry()
00425 {
00426   PL_DHashTableFinish(&mPackagesHash);
00427   gChromeRegistry = nsnull;
00428 }
00429 
00430 NS_IMPL_THREADSAFE_ISUPPORTS6(nsChromeRegistry,
00431                               nsIChromeRegistry,
00432                               nsIXULChromeRegistry,
00433                               nsIToolkitChromeRegistry,
00434                               nsIXULOverlayProvider,
00435                               nsIObserver,
00436                               nsISupportsWeakReference)
00437 
00438 
00439 // nsIChromeRegistry methods:
00440 
00441 static nsresult
00442 getUILangCountry(nsACString& aUILang)
00443 {
00444   nsresult rv;
00445 
00446   nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
00447   NS_ENSURE_SUCCESS(rv, rv);
00448 
00449   nsAutoString uiLang;
00450   rv = localeService->GetLocaleComponentForUserAgent(uiLang);
00451   NS_ENSURE_SUCCESS(rv, rv);
00452 
00453   CopyUTF16toUTF8(uiLang, aUILang);
00454   return NS_OK;
00455 }
00456 
00457 nsresult
00458 nsChromeRegistry::Init()
00459 {
00460   nsresult rv;
00461 
00462   // these atoms appear in almost every chrome registry contents.rdf
00463   // in some form or another. making static atoms prevents the atoms
00464   // from constantly being created/destroyed during parsing
00465   
00466   static const nsStaticAtom atoms[] = {
00467     { "chrome",        nsnull },
00468     { "NC",            nsnull },
00469     { "allowScripts",  nsnull },
00470     { "package",       nsnull },
00471     { "packages",      nsnull },
00472     { "hasOverlays",   nsnull },
00473   };
00474 
00475   NS_RegisterStaticAtoms(atoms, NS_ARRAY_LENGTH(atoms));
00476   
00477   if (!PL_DHashTableInit(&mPackagesHash, &kTableOps,
00478                          nsnull, sizeof(PackageEntry), 16))
00479     return NS_ERROR_FAILURE;
00480 
00481   if (!mOverlayHash.Init() ||
00482       !mStyleHash.Init() ||
00483       !mOverrideTable.Init())
00484     return NS_ERROR_FAILURE;
00485 
00486   mSelectedLocale = NS_LITERAL_CSTRING("en-US");
00487   mSelectedSkin = NS_LITERAL_CSTRING("classic/1.0");
00488 
00489   // This initialization process is fairly complicated and may cause reentrant
00490   // getservice calls to resolve chrome URIs (especially locale files). We
00491   // don't want that, so we inform the protocol handler about our existence
00492   // before we are actually fully initialized.
00493   gChromeRegistry = this;
00494 
00495   PRBool safeMode = PR_FALSE;
00496   nsCOMPtr<nsIXULRuntime> xulrun (do_GetService(XULAPPINFO_SERVICE_CONTRACTID));
00497   if (xulrun)
00498     xulrun->GetInSafeMode(&safeMode);
00499   
00500   nsCOMPtr<nsIPrefService> prefserv (do_GetService(NS_PREFSERVICE_CONTRACTID));
00501   nsCOMPtr<nsIPrefBranch> prefs;
00502 
00503   if (safeMode)
00504     prefserv->GetDefaultBranch(nsnull, getter_AddRefs(prefs));
00505   else
00506     prefs = do_QueryInterface(prefserv);
00507 
00508   if (!prefs) {
00509     NS_WARNING("Could not get pref service!");
00510   }
00511 
00512   PRBool useLocalePref = PR_TRUE;
00513 
00514   if (prefs) {
00515     // check the pref first
00516     PRBool matchOS = PR_FALSE;
00517     rv = prefs->GetBoolPref(MATCH_OS_LOCALE_PREF, &matchOS);
00518 
00519     // match os locale
00520     if (NS_SUCCEEDED(rv) && matchOS) {
00521       // compute lang and region code only when needed!
00522       nsCAutoString uiLocale;
00523       rv = getUILangCountry(uiLocale);
00524       if (NS_SUCCEEDED(rv)) {
00525         useLocalePref = PR_FALSE;
00526         mSelectedLocale = uiLocale;
00527       }
00528     }
00529   }
00530       
00531   if (prefs) {
00532     nsXPIDLCString provider;
00533 
00534     rv = prefs->GetCharPref(SELECTED_SKIN_PREF, getter_Copies(provider));
00535     if (NS_SUCCEEDED(rv))
00536       mSelectedSkin = provider;
00537 
00538     nsCOMPtr<nsIPrefBranch2> prefs2 (do_QueryInterface(prefs));
00539 
00540     if (prefs2)
00541       rv = prefs2->AddObserver(SELECTED_SKIN_PREF, this, PR_TRUE);
00542 
00543     if (useLocalePref) {
00544       rv = prefs->GetCharPref(SELECTED_LOCALE_PREF, getter_Copies(provider));
00545       if (NS_SUCCEEDED(rv))
00546         mSelectedLocale = provider;
00547       
00548       if (prefs2)
00549         prefs2->AddObserver(SELECTED_LOCALE_PREF, this, PR_TRUE);
00550     }
00551   }
00552 
00553   CheckForNewChrome();
00554 
00555   mInitialized = PR_TRUE;
00556 
00557   return NS_OK;
00558 }
00559 
00560 NS_IMETHODIMP
00561 nsChromeRegistry::CheckForOSAccessibility()
00562 {
00563   nsresult rv;
00564 
00565   nsCOMPtr<nsILookAndFeel> lookAndFeel (do_GetService(kLookAndFeelCID));
00566   if (lookAndFeel) {
00567     PRInt32 useAccessibilityTheme = 0;
00568 
00569     rv = lookAndFeel->GetMetric(nsILookAndFeel::eMetric_UseAccessibilityTheme,
00570                                 useAccessibilityTheme);
00571 
00572     if (NS_SUCCEEDED(rv) && useAccessibilityTheme) {
00573       /* Set the skin to classic and remove pref observers */
00574       if (!mSelectedSkin.EqualsLiteral("classic/1.0")) {
00575         mSelectedSkin.AssignLiteral("classic/1.0");
00576         RefreshSkins();
00577       }
00578 
00579       nsCOMPtr<nsIPrefBranch2> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID));
00580       if (prefs) {
00581         prefs->RemoveObserver(SELECTED_SKIN_PREF, this);
00582       }
00583     }
00584   }
00585 
00586   return NS_OK;
00587 }
00588 
00589 nsresult
00590 nsChromeRegistry::GetProviderAndPath(nsIURL* aChromeURL,
00591                                      nsACString& aProvider, nsACString& aPath)
00592 {
00593   nsresult rv;
00594 
00595 #ifdef DEBUG
00596   PRBool isChrome;
00597   aChromeURL->SchemeIs("chrome", &isChrome);
00598   NS_ASSERTION(isChrome, "Non-chrome URI?");
00599 #endif
00600 
00601   nsCAutoString path;
00602   rv = aChromeURL->GetPath(path);
00603   NS_ENSURE_SUCCESS(rv, rv);
00604 
00605   if (path.Length() < 3) {
00606     LogMessage("Invalid chrome URI: %s", path.get());
00607     return NS_ERROR_FAILURE;
00608   }
00609 
00610   path.SetLength(nsUnescapeCount(path.BeginWriting()));
00611   NS_ASSERTION(path.First() == '/', "Path should always begin with a slash!");
00612 
00613   PRInt32 slash = path.FindChar('/', 1);
00614   if (slash == 1) {
00615     LogMessage("Invalid chrome URI: %s", path.get());
00616     return NS_ERROR_FAILURE;
00617   }
00618 
00619   if (slash == -1) {
00620     aPath.Truncate();
00621   }
00622   else {
00623     if (slash == (PRInt32) path.Length() - 1)
00624       aPath.Truncate();
00625     else
00626       aPath.Assign(path.get() + slash + 1, path.Length() - slash - 1);
00627 
00628     --slash;
00629   }
00630 
00631   aProvider.Assign(path.get() + 1, slash);
00632   return NS_OK;
00633 }
00634 
00635 
00636 nsresult
00637 nsChromeRegistry::Canonify(nsIURL* aChromeURL)
00638 {
00639   NS_NAMED_LITERAL_CSTRING(kSlash, "/");
00640 
00641   nsresult rv;
00642 
00643   nsCAutoString provider, path;
00644   rv = GetProviderAndPath(aChromeURL, provider, path);
00645   NS_ENSURE_SUCCESS(rv, rv);
00646 
00647   if (path.IsEmpty()) {
00648     nsCAutoString package;
00649     rv = aChromeURL->GetHost(package);
00650     NS_ENSURE_SUCCESS(rv, rv);
00651 
00652     // we re-use the "path" local string to build a new URL path
00653     path.Assign(kSlash + provider + kSlash + package);
00654     if (provider.EqualsLiteral("content")) {
00655       path.AppendLiteral(".xul");
00656     }
00657     else if (provider.EqualsLiteral("locale")) {
00658       path.AppendLiteral(".dtd");
00659     }
00660     else if (provider.EqualsLiteral("skin")) {
00661       path.AppendLiteral(".css");
00662     }
00663     else {
00664       return NS_ERROR_INVALID_ARG;
00665     }
00666     aChromeURL->SetPath(path);
00667   }
00668   else {
00669     // prevent directory traversals ("..")
00670     // path is already unescaped once, but uris can get unescaped twice
00671     const char* pos = path.BeginReading();
00672     const char* end = path.EndReading();
00673     while (pos < end) {
00674       switch (*pos) {
00675         case ':':
00676           return NS_ERROR_DOM_BAD_URI;
00677         case '.':
00678           if (pos[1] == '.')
00679             return NS_ERROR_DOM_BAD_URI;
00680           break;
00681         case '%':
00682           // chrome: URIs with double-escapes are trying to trick us.
00683           // watch for %2e, and %25 in case someone triple unescapes
00684           if (pos[1] == '2' &&
00685                ( pos[2] == 'e' || pos[2] == 'E' || 
00686                  pos[2] == '5' ))
00687             return NS_ERROR_DOM_BAD_URI;
00688           break;
00689         case '?':
00690         case '#':
00691           // ignore query or ref part, we're done
00692           pos = end;
00693           continue;
00694       }
00695       ++pos;
00696     }
00697   }
00698 
00699   return NS_OK;
00700 }
00701 
00702 NS_IMETHODIMP
00703 nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURI, nsIURI* *aResult)
00704 {
00705   nsresult rv;
00706   NS_ASSERTION(aChromeURI, "null url!");
00707 
00708   if (mOverrideTable.Get(aChromeURI, aResult))
00709     return NS_OK;
00710 
00711   nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aChromeURI));
00712   NS_ENSURE_TRUE(chromeURL, NS_NOINTERFACE);
00713 
00714   nsCAutoString package, provider, path;
00715   rv = chromeURL->GetHostPort(package);
00716   NS_ENSURE_SUCCESS(rv, rv);
00717 
00718   rv = GetProviderAndPath(chromeURL, provider, path);
00719   NS_ENSURE_SUCCESS(rv, rv);
00720 
00721   PackageEntry* entry =
00722     NS_STATIC_CAST(PackageEntry*, PL_DHashTableOperate(&mPackagesHash,
00723                                                        & (nsACString&) package,
00724                                                        PL_DHASH_LOOKUP));
00725 
00726   if (PL_DHASH_ENTRY_IS_FREE(entry)) {
00727     if (!mInitialized)
00728       return NS_ERROR_NOT_INITIALIZED;
00729 
00730     LogMessage("No chrome package registered for chrome://%s/%s/%s .",
00731                package.get(), provider.get(), path.get());
00732 
00733     return NS_ERROR_FAILURE;
00734   }
00735 
00736   if (entry->flags & PackageEntry::PLATFORM_PACKAGE) {
00737 #if defined(XP_WIN) || defined(XP_OS2)
00738     path.Insert("win/", 0);
00739 #elif defined(XP_MACOSX)
00740     path.Insert("mac/", 0);
00741 #else
00742     path.Insert("unix/", 0);
00743 #endif
00744   }
00745 
00746   nsIURI* baseURI = nsnull;
00747   if (provider.EqualsLiteral("locale")) {
00748     baseURI = entry->locales.GetBase(mSelectedLocale, nsProviderArray::LOCALE);
00749   }
00750   else if (provider.EqualsLiteral("skin")) {
00751     baseURI = entry->skins.GetBase(mSelectedSkin, nsProviderArray::ANY);
00752   }
00753   else {
00754     baseURI = entry->baseURI;
00755   }
00756 
00757   if (!baseURI) {
00758     LogMessage("No chrome package registered for chrome://%s/%s/%s .",
00759                package.get(), provider.get(), path.get());
00760     return NS_ERROR_FAILURE;
00761   }
00762 
00763   return NS_NewURI(aResult, path, nsnull, baseURI);
00764 }
00765 
00766 nsresult
00767 nsChromeRegistry::GetSelectedLocale(const nsACString& aPackage, nsACString& aLocale)
00768 {
00769   PackageEntry* entry =
00770     NS_STATIC_CAST(PackageEntry*, PL_DHashTableOperate(&mPackagesHash,
00771                                                        & aPackage,
00772                                                        PL_DHASH_LOOKUP));
00773 
00774   if (PL_DHASH_ENTRY_IS_FREE(entry))
00775     return NS_ERROR_FAILURE;
00776 
00777   aLocale = entry->locales.GetSelected(mSelectedLocale, nsProviderArray::LOCALE);
00778   if (aLocale.IsEmpty())
00779     return NS_ERROR_FAILURE;
00780 
00781   return NS_OK;
00782 }
00783 
00784 NS_IMETHODIMP
00785 nsChromeRegistry::GetLocalesForPackage(const nsACString& aPackage,
00786                                        nsIUTF8StringEnumerator* *aResult)
00787 {
00788   nsCStringArray *a = new nsCStringArray;
00789   if (!a)
00790     return NS_ERROR_OUT_OF_MEMORY;
00791 
00792   PackageEntry* entry =
00793     NS_STATIC_CAST(PackageEntry*, PL_DHashTableOperate(&mPackagesHash,
00794                                                        & aPackage,
00795                                                        PL_DHASH_LOOKUP));
00796 
00797   if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
00798     entry->locales.EnumerateToArray(a);
00799   }
00800 
00801   nsresult rv = NS_NewAdoptingUTF8StringEnumerator(aResult, a);
00802   if (NS_FAILED(rv))
00803     delete a;
00804 
00805   return rv;
00806 }
00807 
00808 NS_IMETHODIMP
00809 nsChromeRegistry::GetStyleOverlays(nsIURI *aChromeURL,
00810                                    nsISimpleEnumerator **aResult)
00811 {
00812   const nsCOMArray<nsIURI>* parray = mStyleHash.GetArray(aChromeURL);
00813   if (!parray)
00814     return NS_NewEmptyEnumerator(aResult);
00815 
00816   return NS_NewArrayEnumerator(aResult, *parray);
00817 }
00818 
00819 NS_IMETHODIMP
00820 nsChromeRegistry::GetXULOverlays(nsIURI *aChromeURL, nsISimpleEnumerator **aResult)
00821 {
00822   const nsCOMArray<nsIURI>* parray = mOverlayHash.GetArray(aChromeURL);
00823   if (!parray)
00824     return NS_NewEmptyEnumerator(aResult);
00825 
00826   return NS_NewArrayEnumerator(aResult, *parray);
00827 }
00828 
00830 
00831 // theme stuff
00832 
00833 
00834 static void FlushSkinBindingsForWindow(nsIDOMWindowInternal* aWindow)
00835 {
00836   // Get the DOM document.
00837   nsCOMPtr<nsIDOMDocument> domDocument;
00838   aWindow->GetDocument(getter_AddRefs(domDocument));
00839   if (!domDocument)
00840     return;
00841 
00842   nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
00843   if (!document)
00844     return;
00845 
00846   // Annihilate all XBL bindings.
00847   document->BindingManager()->FlushSkinBindings();
00848 }
00849 
00850 // XXXbsmedberg: move this to nsIWindowMediator
00851 NS_IMETHODIMP nsChromeRegistry::RefreshSkins()
00852 {
00853   nsCOMPtr<nsIWindowMediator> windowMediator
00854     (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
00855   if (!windowMediator)
00856     return NS_OK;
00857 
00858   nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
00859   windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
00860   PRBool more;
00861   windowEnumerator->HasMoreElements(&more);
00862   while (more) {
00863     nsCOMPtr<nsISupports> protoWindow;
00864     windowEnumerator->GetNext(getter_AddRefs(protoWindow));
00865     if (protoWindow) {
00866       nsCOMPtr<nsIDOMWindowInternal> domWindow = do_QueryInterface(protoWindow);
00867       if (domWindow)
00868         FlushSkinBindingsForWindow(domWindow);
00869     }
00870     windowEnumerator->HasMoreElements(&more);
00871   }
00872 
00873   FlushSkinCaches();
00874   
00875   windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
00876   windowEnumerator->HasMoreElements(&more);
00877   while (more) {
00878     nsCOMPtr<nsISupports> protoWindow;
00879     windowEnumerator->GetNext(getter_AddRefs(protoWindow));
00880     if (protoWindow) {
00881       nsCOMPtr<nsIDOMWindowInternal> domWindow = do_QueryInterface(protoWindow);
00882       if (domWindow)
00883         RefreshWindow(domWindow);
00884     }
00885     windowEnumerator->HasMoreElements(&more);
00886   }
00887    
00888   return NS_OK;
00889 }
00890 
00891 void
00892 nsChromeRegistry::FlushSkinCaches()
00893 {
00894   nsCOMPtr<nsIObserverService> obsSvc =
00895     do_GetService("@mozilla.org/observer-service;1");
00896   NS_ASSERTION(obsSvc, "Couldn't get observer service.");
00897 
00898   obsSvc->NotifyObservers(NS_STATIC_CAST(nsIChromeRegistry*, this),
00899                           NS_CHROME_FLUSH_SKINS_TOPIC, nsnull);
00900 }
00901 
00902 static PRBool IsChromeURI(nsIURI* aURI)
00903 {
00904     PRBool isChrome=PR_FALSE;
00905     if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome)
00906         return PR_TRUE;
00907     return PR_FALSE;
00908 }
00909 
00910 // XXXbsmedberg: move this to windowmediator
00911 nsresult nsChromeRegistry::RefreshWindow(nsIDOMWindowInternal* aWindow)
00912 {
00913   // Deal with our subframes first.
00914   nsCOMPtr<nsIDOMWindowCollection> frames;
00915   aWindow->GetFrames(getter_AddRefs(frames));
00916   PRUint32 length;
00917   frames->GetLength(&length);
00918   PRUint32 j;
00919   for (j = 0; j < length; j++) {
00920     nsCOMPtr<nsIDOMWindow> childWin;
00921     frames->Item(j, getter_AddRefs(childWin));
00922     nsCOMPtr<nsIDOMWindowInternal> childInt(do_QueryInterface(childWin));
00923     RefreshWindow(childInt);
00924   }
00925 
00926   nsresult rv;
00927   // Get the DOM document.
00928   nsCOMPtr<nsIDOMDocument> domDocument;
00929   aWindow->GetDocument(getter_AddRefs(domDocument));
00930   if (!domDocument)
00931     return NS_OK;
00932 
00933   nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
00934   if (!document)
00935     return NS_OK;
00936 
00937   // Deal with the agent sheets first.  Have to do all the style sets by hand.
00938   PRUint32 shellCount = document->GetNumberOfShells();
00939   for (PRUint32 k = 0; k < shellCount; k++) {
00940     nsIPresShell *shell = document->GetShellAt(k);
00941 
00942     // Reload only the chrome URL agent style sheets.
00943     nsCOMArray<nsIStyleSheet> agentSheets;
00944     rv = shell->GetAgentStyleSheets(agentSheets);
00945     NS_ENSURE_SUCCESS(rv, rv);
00946 
00947     nsCOMArray<nsIStyleSheet> newAgentSheets;
00948     for (PRInt32 l = 0; l < agentSheets.Count(); ++l) {
00949       nsIStyleSheet *sheet = agentSheets[l];
00950 
00951       nsCOMPtr<nsIURI> uri;
00952       rv = sheet->GetSheetURI(getter_AddRefs(uri));
00953       if (NS_FAILED(rv)) return rv;
00954 
00955       if (IsChromeURI(uri)) {
00956         // Reload the sheet.
00957         nsCOMPtr<nsICSSStyleSheet> newSheet;
00958         rv = LoadStyleSheetWithURL(uri, PR_TRUE, getter_AddRefs(newSheet));
00959         if (NS_FAILED(rv)) return rv;
00960         if (newSheet) {
00961           rv = newAgentSheets.AppendObject(newSheet) ? NS_OK : NS_ERROR_FAILURE;
00962           if (NS_FAILED(rv)) return rv;
00963         }
00964       }
00965       else {  // Just use the same sheet.
00966         rv = newAgentSheets.AppendObject(sheet) ? NS_OK : NS_ERROR_FAILURE;
00967         if (NS_FAILED(rv)) return rv;
00968       }
00969     }
00970 
00971     rv = shell->SetAgentStyleSheets(newAgentSheets);
00972     NS_ENSURE_SUCCESS(rv, rv);
00973   }
00974 
00975   // Build an array of nsIURIs of style sheets we need to load.
00976   nsCOMArray<nsIStyleSheet> oldSheets;
00977   nsCOMArray<nsIStyleSheet> newSheets;
00978 
00979   PRInt32 count = document->GetNumberOfStyleSheets();
00980 
00981   // Iterate over the style sheets.
00982   PRInt32 i;
00983   for (i = 0; i < count; i++) {
00984     // Get the style sheet
00985     nsIStyleSheet *styleSheet = document->GetStyleSheetAt(i);
00986     
00987     if (!oldSheets.AppendObject(styleSheet)) {
00988       return NS_ERROR_OUT_OF_MEMORY;
00989     }
00990   }
00991 
00992   // Iterate over our old sheets and kick off a sync load of the new 
00993   // sheet if and only if it's a chrome URL.
00994   for (i = 0; i < count; i++) {
00995     nsCOMPtr<nsIStyleSheet> sheet = oldSheets[i];
00996     nsCOMPtr<nsIURI> uri;
00997     rv = sheet->GetSheetURI(getter_AddRefs(uri));
00998     if (NS_FAILED(rv)) return rv;
00999 
01000     if (IsChromeURI(uri)) {
01001       // Reload the sheet.
01002 #ifdef DEBUG
01003       nsCOMPtr<nsICSSStyleSheet> oldCSSSheet = do_QueryInterface(sheet);
01004       NS_ASSERTION(oldCSSSheet, "Don't know how to reload a non-CSS sheet");
01005 #endif
01006       nsCOMPtr<nsICSSStyleSheet> newSheet;
01007       // XXX what about chrome sheets that have a title or are disabled?  This
01008       // only works by sheer dumb luck.
01009       // XXXbz this should really use the document's CSSLoader!
01010       LoadStyleSheetWithURL(uri, PR_FALSE, getter_AddRefs(newSheet));
01011       // Even if it's null, we put in in there.
01012       newSheets.AppendObject(newSheet);
01013     }
01014     else {
01015       // Just use the same sheet.
01016       newSheets.AppendObject(sheet);
01017     }
01018   }
01019 
01020   // Now notify the document that multiple sheets have been added and removed.
01021   document->UpdateStyleSheets(oldSheets, newSheets);
01022   return NS_OK;
01023 }
01024 
01025 void
01026 nsChromeRegistry::FlushAllCaches()
01027 {
01028   nsCOMPtr<nsIObserverService> obsSvc =
01029     do_GetService("@mozilla.org/observer-service;1");
01030   NS_ASSERTION(obsSvc, "Couldn't get observer service.");
01031 
01032   obsSvc->NotifyObservers((nsIChromeRegistry*) this,
01033                           NS_CHROME_FLUSH_TOPIC, nsnull);
01034 }  
01035 
01036 // xxxbsmedberg Move me to nsIWindowMediator
01037 NS_IMETHODIMP
01038 nsChromeRegistry::ReloadChrome()
01039 {
01040   FlushAllCaches();
01041   // Do a reload of all top level windows.
01042   nsresult rv = NS_OK;
01043 
01044   // Get the window mediator
01045   nsCOMPtr<nsIWindowMediator> windowMediator
01046     (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
01047   if (windowMediator) {
01048     nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
01049 
01050     rv = windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator));
01051     if (NS_SUCCEEDED(rv)) {
01052       // Get each dom window
01053       PRBool more;
01054       rv = windowEnumerator->HasMoreElements(&more);
01055       if (NS_FAILED(rv)) return rv;
01056       while (more) {
01057         nsCOMPtr<nsISupports> protoWindow;
01058         rv = windowEnumerator->GetNext(getter_AddRefs(protoWindow));
01059         if (NS_SUCCEEDED(rv)) {
01060           nsCOMPtr<nsIDOMWindowInternal> domWindow =
01061             do_QueryInterface(protoWindow);
01062           if (domWindow) {
01063             nsCOMPtr<nsIDOMLocation> location;
01064             domWindow->GetLocation(getter_AddRefs(location));
01065             if (location) {
01066               rv = location->Reload(PR_FALSE);
01067               if (NS_FAILED(rv)) return rv;
01068             }
01069           }
01070         }
01071         rv = windowEnumerator->HasMoreElements(&more);
01072         if (NS_FAILED(rv)) return rv;
01073       }
01074     }
01075   }
01076   return rv;
01077 }
01078 
01079 nsresult
01080 nsChromeRegistry::LoadStyleSheetWithURL(nsIURI* aURL, PRBool aEnableUnsafeRules, nsICSSStyleSheet** aSheet)
01081 {
01082   *aSheet = nsnull;
01083 
01084   nsCOMPtr<nsICSSLoader_MOZILLA_1_8_BRANCH> cssLoader = do_GetService(kCSSLoaderCID);
01085   if (!cssLoader) return NS_ERROR_FAILURE;
01086 
01087   return cssLoader->LoadSheetSync(aURL, aEnableUnsafeRules, aSheet);
01088 }
01089 
01090 NS_IMETHODIMP
01091 nsChromeRegistry::AllowScriptsForPackage(nsIURI* aChromeURI, PRBool *aResult)
01092 {
01093   nsresult rv;
01094   *aResult = PR_FALSE;
01095 
01096 #ifdef DEBUG
01097   PRBool isChrome;
01098   aChromeURI->SchemeIs("chrome", &isChrome);
01099   NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowScriptsForPackage!");
01100 #endif
01101 
01102   nsCOMPtr<nsIURL> url (do_QueryInterface(aChromeURI));
01103   NS_ENSURE_TRUE(url, NS_NOINTERFACE);
01104 
01105   nsCAutoString provider, file;
01106   rv = GetProviderAndPath(url, provider, file);
01107   NS_ENSURE_SUCCESS(rv, rv);
01108 
01109   if (!provider.EqualsLiteral("skin"))
01110     *aResult = PR_TRUE;
01111 
01112   return NS_OK;
01113 }
01114 
01115 static PLDHashOperator
01116 RemoveAll(PLDHashTable *table, PLDHashEntryHdr *entry, PRUint32 number, void *arg)
01117 {
01118   return (PLDHashOperator) (PL_DHASH_NEXT | PL_DHASH_REMOVE);
01119 }
01120 
01121 NS_IMETHODIMP
01122 nsChromeRegistry::CheckForNewChrome()
01123 {
01124   nsresult rv;
01125 
01126   PL_DHashTableEnumerate(&mPackagesHash, RemoveAll, nsnull);
01127   mOverlayHash.Clear();
01128   mStyleHash.Clear();
01129   mOverrideTable.Clear();
01130 
01131   nsCOMPtr<nsIURI> manifestURI;
01132   rv = NS_NewURI(getter_AddRefs(manifestURI),
01133                  NS_LITERAL_CSTRING("resource:///chrome/app-chrome.manifest"));
01134 
01135   // this is the main manifest; if it doesn't exist we generate it from
01136   // installed-chrome.txt. When the build system learns about the new system,
01137   // this code can go away.
01138 
01139   nsCOMPtr<nsIFileURL> manifestFileURL (do_QueryInterface(manifestURI));
01140   NS_ASSERTION(manifestFileURL, "Not a nsIFileURL!");
01141 
01142   nsCOMPtr<nsIFile> manifest;
01143   manifestFileURL->GetFile(getter_AddRefs(manifest));
01144   NS_ENSURE_TRUE(manifest, NS_ERROR_FAILURE);
01145 
01146   PRBool exists;
01147   rv = manifest->Exists(&exists);
01148   NS_ENSURE_SUCCESS(rv, rv);
01149 
01150 #ifdef DEBUG
01151   // In debug builds, installed-chrome.txt may change during development;
01152   // we just rebuild it every time because we're not worried about startup
01153   // time or other bad/goodness.
01154   if (exists) {
01155     manifest->Remove(PR_FALSE);
01156     exists = PR_FALSE;
01157   }
01158 #endif
01159 
01160   if (!exists) {
01161     nsCOMPtr<nsIFile> installed;
01162     manifest->Clone(getter_AddRefs(installed));
01163     if (!installed)
01164       return NS_ERROR_OUT_OF_MEMORY;
01165 
01166     nsCOMPtr<nsILocalFile> linstalled (do_QueryInterface(installed));
01167     NS_ENSURE_TRUE(linstalled, NS_NOINTERFACE);
01168 
01169     linstalled->SetNativeLeafName(NS_LITERAL_CSTRING("installed-chrome.txt"));
01170 
01171     // process installed-chrome.txt into app-chrome.manifest
01172     ProcessNewChromeFile(linstalled, manifestURI);
01173   }
01174 
01175   nsCOMPtr<nsIProperties> dirSvc (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
01176   NS_ENSURE_TRUE(dirSvc, NS_ERROR_FAILURE);
01177 
01178   // check the extra chrome directories
01179   nsCOMPtr<nsISimpleEnumerator> chromeML;
01180   rv = dirSvc->Get(NS_CHROME_MANIFESTS_FILE_LIST, NS_GET_IID(nsISimpleEnumerator),
01181                    getter_AddRefs(chromeML));
01182   if (NS_FAILED(rv)) {
01183     // ok, then simply load all .manifest files in the app chrome dir.
01184     nsCOMPtr<nsIFile> chromeDir;
01185     rv = dirSvc->Get(NS_APP_CHROME_DIR, NS_GET_IID(nsIFile),
01186                      getter_AddRefs(chromeDir));
01187     if (NS_FAILED(rv))
01188       return rv;
01189     rv = NS_NewSingletonEnumerator(getter_AddRefs(chromeML), chromeDir);
01190     if (NS_FAILED(rv))
01191       return rv;
01192   }
01193 
01194   nsCOMPtr<nsISupports> next;
01195   while (NS_SUCCEEDED(chromeML->HasMoreElements(&exists)) && exists) {
01196     chromeML->GetNext(getter_AddRefs(next));
01197     nsCOMPtr<nsILocalFile> lmanifest = do_QueryInterface(next);
01198     if (!lmanifest) {
01199       NS_ERROR("Directory enumerator returned a non-nsILocalFile");
01200       continue;
01201     }
01202 
01203     PRBool isDir;
01204     if (NS_SUCCEEDED(lmanifest->IsDirectory(&isDir)) && isDir) {
01205       nsCOMPtr<nsISimpleEnumerator> entries;
01206       rv = lmanifest->GetDirectoryEntries(getter_AddRefs(entries));
01207       if (NS_FAILED(rv))
01208         continue;
01209 
01210       while (NS_SUCCEEDED(entries->HasMoreElements(&exists)) && exists) {
01211         entries->GetNext(getter_AddRefs(next));
01212         lmanifest = do_QueryInterface(next);
01213         if (lmanifest) {
01214           nsCAutoString leafName;
01215           lmanifest->GetNativeLeafName(leafName);
01216           if (StringEndsWith(leafName, NS_LITERAL_CSTRING(".manifest"))) {
01217             rv = ProcessManifest(lmanifest, PR_FALSE);
01218             if (NS_FAILED(rv)) {
01219               nsCAutoString path;
01220               lmanifest->GetNativePath(path);
01221               LogMessage("Failed to process chrome manifest '%s'.",
01222                          path.get());
01223 
01224             }
01225           }
01226         }
01227       }
01228     }
01229     else {
01230       rv = ProcessManifest(lmanifest, PR_FALSE);
01231       if (NS_FAILED(rv)) {
01232         nsCAutoString path;
01233         lmanifest->GetNativePath(path);
01234         LogMessage("Failed to process chrome manifest: '%s'.",
01235                    path.get());
01236       }
01237     }
01238   }
01239 
01240   rv = dirSvc->Get(NS_SKIN_MANIFESTS_FILE_LIST, NS_GET_IID(nsISimpleEnumerator),
01241                    getter_AddRefs(chromeML));
01242   if (NS_FAILED(rv))
01243     return NS_OK;
01244 
01245   while (NS_SUCCEEDED(chromeML->HasMoreElements(&exists)) && exists) {
01246     chromeML->GetNext(getter_AddRefs(next));
01247     nsCOMPtr<nsILocalFile> lmanifest = do_QueryInterface(next);
01248     if (!lmanifest) {
01249       NS_ERROR("Directory enumerator returned a non-nsILocalFile");
01250       continue;
01251     }
01252 
01253     rv = ProcessManifest(lmanifest, PR_TRUE);
01254     if (NS_FAILED(rv)) {
01255       nsCAutoString path;
01256       lmanifest->GetNativePath(path);
01257       LogMessage("Failed to process chrome manifest: '%s'.",
01258                  path.get());
01259     }
01260   }
01261 
01262   return NS_OK;
01263 }
01264 
01265 nsresult
01266 nsChromeRegistry::ProcessNewChromeFile(nsILocalFile *aListFile, nsIURI* aManifest)
01267 {
01268   nsresult rv;
01269 
01270   PRFileDesc *file;
01271   rv = aListFile->OpenNSPRFileDesc(PR_RDONLY, 0, &file);
01272   NS_ENSURE_SUCCESS(rv, rv);
01273 
01274   PRInt32 n, size;
01275   char *buf;
01276 
01277   size = PR_Available(file);
01278   if (size == -1) {
01279     rv = NS_ERROR_UNEXPECTED;
01280     goto end;
01281   }
01282 
01283   buf = (char *) malloc(size + 1);
01284   if (!buf) {
01285     rv = NS_ERROR_OUT_OF_MEMORY;
01286     goto end;
01287   }
01288 
01289   n = PR_Read(file, buf, size);
01290   if (n > 0)
01291     rv = ProcessNewChromeBuffer(buf, size, aManifest);
01292   free(buf);
01293 
01294 end:
01295   PR_Close(file);
01296   return rv;
01297 }
01298 
01299 nsresult
01300 nsChromeRegistry::ProcessNewChromeBuffer(char *aBuffer, PRInt32 aLength,
01301                                          nsIURI* aManifest)
01302 {
01303   nsresult rv = NS_OK;
01304   char   *bufferEnd = aBuffer + aLength;
01305   char   *chromeType,      // "content", "locale" or "skin"
01306          *chromeProfile,   // "install" or "profile"
01307          *chromeLocType,   // type of location (local path or URL)
01308          *chromeLocation;  // base location of chrome (jar file)
01309 
01310   nsCOMPtr<nsIURI> baseURI;
01311 
01312   // process chromeType, chromeProfile, chromeLocType, chromeLocation
01313   while (aBuffer < bufferEnd) {
01314     // parse one line of installed-chrome.txt
01315     chromeType = aBuffer;
01316     while (aBuffer < bufferEnd && *aBuffer != ',')
01317       ++aBuffer;
01318     *aBuffer = '\0';
01319 
01320     chromeProfile = ++aBuffer;
01321     if (aBuffer >= bufferEnd)
01322       break;
01323 
01324     while (aBuffer < bufferEnd && *aBuffer != ',')
01325       ++aBuffer;
01326     *aBuffer = '\0';
01327 
01328     chromeLocType = ++aBuffer;
01329     if (aBuffer >= bufferEnd)
01330       break;
01331 
01332     while (aBuffer < bufferEnd && *aBuffer != ',')
01333       ++aBuffer;
01334     *aBuffer = '\0';
01335 
01336     chromeLocation = ++aBuffer;
01337     if (aBuffer >= bufferEnd)
01338       break;
01339 
01340     while (aBuffer < bufferEnd &&
01341            (*aBuffer != '\r' && *aBuffer != '\n' && *aBuffer != ' '))
01342       ++aBuffer;
01343     *aBuffer = '\0';
01344 
01345     // process the line
01346     // We don't do skin or locale selection from installed-chrome.txt since
01347     // ffox 0.9. Just ignore the "select" lines.
01348     if (strcmp(chromeLocType,"select")) {
01349       if (!strcmp(chromeLocType, "path")) {
01350         // location is a (full) path. convert it to an URL.
01351 
01352         /* this is some convoluted shit... this creates a file, inits it with
01353          * the path parsed above (chromeLocation), makes a url, and inits it
01354          * with the file created. the purpose of this is just to have the
01355          * canonical url of the stupid thing.
01356          */
01357         nsCOMPtr<nsILocalFile> chromeFile;
01358         rv = NS_NewNativeLocalFile(nsDependentCString(chromeLocation),
01359                                    PR_TRUE, getter_AddRefs(chromeFile));
01360         NS_ENSURE_SUCCESS(rv, rv);
01361 
01362         /* 
01363          * all we want here is the canonical url
01364          */
01365         rv = NS_NewFileURI(getter_AddRefs(baseURI), chromeFile);
01366         if (NS_FAILED(rv)) return rv;
01367       }
01368       else {
01369         rv = NS_NewURI(getter_AddRefs(baseURI), chromeLocation);
01370         if (NS_FAILED(rv)) return rv;
01371       }
01372 
01373       ProcessContentsManifest(baseURI, aManifest, baseURI, PR_TRUE,
01374                               strcmp(chromeType, "skin") == 0);
01375     }
01376     
01377     while (aBuffer < bufferEnd && (*aBuffer == '\0' || *aBuffer == ' ' || *aBuffer == '\r' || *aBuffer == '\n'))
01378       ++aBuffer;
01379   }
01380 
01381   return NS_OK;
01382 }
01383 
01384 NS_IMETHODIMP nsChromeRegistry::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
01385 {
01386   nsresult rv = NS_OK;
01387 
01388   if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
01389     nsCOMPtr<nsIPrefBranch> prefs (do_QueryInterface(aSubject));
01390     NS_ASSERTION(prefs, "Bad observer call!");
01391 
01392     NS_ConvertUTF16toUTF8 pref(someData);
01393 
01394     nsXPIDLCString provider;
01395     rv = prefs->GetCharPref(pref.get(), getter_Copies(provider));
01396     if (NS_FAILED(rv)) {
01397       NS_ERROR("Couldn't get new locale or skin pref!");
01398       return rv;
01399     }
01400 
01401     if (pref.EqualsLiteral(SELECTED_SKIN_PREF)) {
01402       mSelectedSkin = provider;
01403       RefreshSkins();
01404     }
01405     else if (pref.EqualsLiteral(SELECTED_LOCALE_PREF)) {
01406       mSelectedLocale = provider;
01407       FlushAllCaches();
01408     } else {
01409       NS_ERROR("Unexpected pref!");
01410     }
01411   }
01412   else if (!strcmp("command-line-startup", aTopic)) {
01413     nsCOMPtr<nsICommandLine> cmdLine (do_QueryInterface(aSubject));
01414     if (cmdLine) {
01415       nsAutoString uiLocale;
01416       rv = cmdLine->HandleFlagWithParam(NS_LITERAL_STRING(UILOCALE_CMD_LINE_ARG),
01417                                         PR_FALSE, uiLocale);
01418       if (NS_SUCCEEDED(rv) && !uiLocale.IsEmpty()) {
01419         CopyUTF16toUTF8(uiLocale, mSelectedLocale);
01420         nsCOMPtr<nsIPrefBranch2> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID));
01421         if (prefs) {
01422           prefs->RemoveObserver(SELECTED_LOCALE_PREF, this);
01423         }
01424       }
01425     }
01426   }
01427   else {
01428     NS_ERROR("Unexpected observer topic!");
01429   }
01430 
01431   return rv;
01432 }
01433 
01434 static nsresult
01435 GetContainerEnumerator(nsIRDFDataSource* ds, nsIRDFResource* res,
01436                        nsISimpleEnumerator* *aResult, PRInt32 *aCountResult = nsnull)
01437 {
01438   nsresult rv;
01439 
01440   nsCOMPtr<nsIRDFContainer> container
01441     (do_CreateInstance("@mozilla.org/rdf/container;1"));
01442   NS_ENSURE_TRUE(container, NS_ERROR_FAILURE);
01443 
01444   rv = container->Init(ds, res);
01445   if (NS_FAILED(rv)) return rv;
01446 
01447   if (aCountResult)
01448     container->GetCount(aCountResult);
01449 
01450   return container->GetElements(aResult);
01451 }
01452 
01453 static void
01454 FollowLiteral(nsIRDFDataSource* ds, nsIRDFResource* res,
01455               nsIRDFResource* arc, nsACString& result)
01456 {
01457   nsresult rv;
01458 
01459   nsCOMPtr<nsIRDFNode> node;
01460   rv = ds->GetTarget(res, arc, PR_TRUE, getter_AddRefs(node));
01461   if (NS_FAILED(rv) || !node) {
01462     result.Truncate();
01463     return;
01464   }
01465 
01466   nsCOMPtr<nsIRDFLiteral> literal (do_QueryInterface(node));
01467   if (!literal) {
01468     NS_ERROR("Arc found, but doesn't point to expected literal!");
01469     result.Truncate();
01470     return;
01471   }
01472 
01473   const PRUnichar* value;
01474   literal->GetValueConst(&value);
01475   CopyUTF16toUTF8(value, result);
01476 }
01477 
01478 static void
01479 FollowResource(nsIRDFDataSource* ds, nsIRDFResource* res, nsIRDFResource* arc,
01480                nsIRDFResource* *result)
01481 {
01482   nsresult rv;
01483 
01484   nsCOMPtr<nsIRDFNode> node;
01485   rv = ds->GetTarget(res, arc, PR_TRUE, getter_AddRefs(node));
01486   if (NS_FAILED(rv) || !node) {
01487     *result = nsnull;
01488     return;
01489   }
01490 
01491   CallQueryInterface(node, result);
01492 }
01493 
01494 static void
01495 GetRelativePath(nsIURI* base, nsIURI* relative, nsACString& result)
01496 {
01497   nsresult rv;
01498 
01499   nsCOMPtr<nsIJARURI> jarrelative (do_QueryInterface(relative));
01500   if (jarrelative) {
01501     nsCOMPtr<nsIURI> jarbase;
01502     jarrelative->GetJARFile(getter_AddRefs(jarbase));
01503 
01504     nsCAutoString relativeBase;
01505     GetRelativePath(base, jarbase, relativeBase);
01506 
01507     nsCAutoString jarEntry;
01508     jarrelative->GetJAREntry(jarEntry);
01509 
01510     result.Assign(NS_LITERAL_CSTRING("jar:"));
01511     result.Append(relativeBase);
01512     result.Append(NS_LITERAL_CSTRING("!/"));
01513     result.Append(jarEntry);
01514     return;
01515   }
01516 
01517   nsCOMPtr<nsIURL> baseURL (do_QueryInterface(base));
01518   if (!baseURL) {
01519     relative->GetSpec(result);
01520     return;
01521   }
01522 
01523   rv = baseURL->GetRelativeSpec(relative, result);
01524   if (NS_FAILED(rv)) {
01525     relative->GetSpec(result);
01526   }
01527 }
01528 
01529 static const PRInt32 kNSPR_APPEND_FLAGS = PR_WRONLY | PR_CREATE_FILE | PR_APPEND;
01530 static const PRInt32 kNSPR_TRUNCATE_FLAGS = PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE;
01531 
01532 NS_IMETHODIMP
01533 nsChromeRegistry::ProcessContentsManifest(nsIURI* aOldManifest, nsIURI* aFile,
01534                                           nsIURI* aBaseURI, PRBool aAppend,
01535                                           PRBool aSkinOnly)
01536 {
01537   nsresult rv;
01538 
01539   nsCAutoString relativePath;
01540   GetRelativePath(aFile, aBaseURI, relativePath);
01541 
01542   nsCAutoString spec;
01543   aOldManifest->GetSpec(spec);
01544 
01545   NS_ASSERTION(spec.Last() == '/', "installed-chrome manifest URI doesn't end in a slash! It probably won't work.");
01546 
01547   spec.AppendLiteral("contents.rdf");
01548 
01549   nsCOMPtr<nsIRDFService> rdfs (do_GetService("@mozilla.org/rdf/rdf-service;1"));
01550   NS_ENSURE_TRUE(rdfs, NS_ERROR_FAILURE);
01551 
01552   nsCOMPtr<nsIRDFResource> namearc, platformarc;
01553   rdfs->GetResource(NS_LITERAL_CSTRING(kURICHROME_name),
01554                     getter_AddRefs(namearc));
01555   rdfs->GetResource(NS_LITERAL_CSTRING(kURICHROME_platformPackage),
01556                     getter_AddRefs(platformarc));
01557   if (!(namearc && platformarc))
01558     return NS_ERROR_FAILURE;
01559 
01560   nsCOMPtr<nsIRDFDataSource> ds;
01561   rv = rdfs->GetDataSourceBlocking(spec.get(), getter_AddRefs(ds));
01562   if (NS_FAILED(rv)) {
01563     LogMessage("Failed to load old-style contents.rdf at '%s'.",
01564                spec.get());
01565     return rv;
01566   }
01567 
01568   nsCOMPtr<nsIFileURL> fileURL (do_QueryInterface(aFile));
01569   NS_ENSURE_TRUE(fileURL, NS_ERROR_INVALID_ARG);
01570 
01571   nsCOMPtr<nsIFile> file;
01572   rv = fileURL->GetFile(getter_AddRefs(file));
01573   NS_ENSURE_SUCCESS(rv, rv);
01574 
01575   nsCOMPtr<nsILocalFile> lfile (do_QueryInterface(file));
01576   NS_ENSURE_TRUE(lfile, NS_ERROR_NO_INTERFACE);
01577 
01578   PRFileDesc* fd;
01579   rv = lfile->OpenNSPRFileDesc(aAppend ? kNSPR_APPEND_FLAGS : kNSPR_TRUNCATE_FLAGS,
01580                                0664, &fd);
01581   NS_ENSURE_SUCCESS(rv, rv);
01582 
01583   if (aAppend)
01584     PR_Write(fd, "\n", 1);
01585 
01586   nsCOMPtr<nsIRDFResource> root;
01587   rv = rdfs->GetResource(NS_LITERAL_CSTRING("urn:mozilla:skin:root"),
01588                          getter_AddRefs(root));
01589   if (NS_SUCCEEDED(rv))
01590     ProcessProvider(fd, rdfs, ds, root, PR_FALSE, relativePath);
01591 
01592   rv = rdfs->GetResource(NS_LITERAL_CSTRING("urn:mozilla:stylesheets"),
01593                          getter_AddRefs(root));
01594   if (NS_SUCCEEDED(rv))
01595     ProcessOverlays(fd, ds, root, NS_LITERAL_CSTRING("style"));
01596 
01597   if (!aSkinOnly) {
01598     rv = rdfs->GetResource(NS_LITERAL_CSTRING("urn:mozilla:locale:root"),
01599                            getter_AddRefs(root));
01600     if (NS_SUCCEEDED(rv))
01601       ProcessProvider(fd, rdfs, ds, root, PR_TRUE, relativePath);
01602 
01603     rv = rdfs->GetResource(NS_LITERAL_CSTRING("urn:mozilla:overlays"),
01604                          getter_AddRefs(root));
01605     if (NS_SUCCEEDED(rv))
01606       ProcessOverlays(fd, ds, root, NS_LITERAL_CSTRING("overlay"));
01607 
01608     /* content packages are easier, but different */
01609 
01610     rv = rdfs->GetResource(NS_LITERAL_CSTRING("urn:mozilla:package:root"),
01611                            getter_AddRefs(root));
01612 
01613     nsCOMPtr<nsISimpleEnumerator> packages;
01614     if (NS_SUCCEEDED(rv))
01615       rv = GetContainerEnumerator(ds, root, getter_AddRefs(packages));
01616 
01617     if (NS_SUCCEEDED(rv)) {
01618       PRBool more;
01619       nsCOMPtr<nsISupports> next;
01620       nsCOMPtr<nsIRDFResource> package;
01621 
01622       while (NS_SUCCEEDED(packages->HasMoreElements(&more)) && more) {
01623         packages->GetNext(getter_AddRefs(next));
01624 
01625         package = do_QueryInterface(next);
01626         if (!package) {
01627           NS_WARNING("Arc from urn:mozilla:package:root points to non-resource node.");
01628           continue;
01629         }
01630 
01631         nsCAutoString name;
01632         FollowLiteral(ds, package, namearc, name);
01633         if (name.IsEmpty())
01634           continue;
01635 
01636         nsCAutoString isPlatform;
01637         FollowLiteral(ds, package, platformarc, isPlatform);
01638         name.Insert(NS_LITERAL_CSTRING("content\t"), 0);
01639         name.Append('\t');
01640         name.Append(relativePath);
01641         if (!isPlatform.IsEmpty())
01642           name.AppendLiteral("\tplatform");
01643 
01644         name.AppendLiteral(NS_LINEBREAK);
01645         PR_Write(fd, name.get(), name.Length());
01646       }
01647     }
01648   }
01649 
01650   PR_Close(fd);
01651 
01652   return NS_OK;
01653 }
01654 
01655 static void
01656 GetResourceName(nsIRDFResource* res, nsACString& result)
01657 {
01658   // we need to get the provider name. Instead of doing something sane,
01659   // we munge the resource URI, looking from the right for colons.
01660 
01661   nsCAutoString providerURI;
01662   res->GetValueUTF8(providerURI);
01663 
01664   PRInt32 found = providerURI.RFindChar(':');
01665   if (found == kNotFound) {
01666     result.Truncate();
01667     return;
01668   }
01669 
01670   result.Assign(Substring(providerURI, found + 1));
01671 }
01672 
01673 
01674 void
01675 nsChromeRegistry::ProcessProvider(PRFileDesc *fd, nsIRDFService* aRDFs,
01676                                   nsIRDFDataSource* aDS, nsIRDFResource* aRoot,
01677                                   PRBool aIsLocale, const nsACString& aBaseURL)
01678 {
01679   NS_NAMED_LITERAL_CSTRING(kSlash, "/");
01680   NS_NAMED_LITERAL_CSTRING(kTab, "\t");
01681 
01682   nsresult rv;
01683 
01684   nsCOMPtr<nsIRDFResource> packagesarc;
01685   aRDFs->GetResource(NS_LITERAL_CSTRING(kURICHROME_packages),
01686                     getter_AddRefs(packagesarc));
01687   if (!packagesarc) return;
01688 
01689   nsCOMPtr<nsISimpleEnumerator> providers;
01690   rv = GetContainerEnumerator(aDS, aRoot, getter_AddRefs(providers));
01691   if (NS_FAILED(rv)) {
01692     return;
01693   }
01694 
01695   nsCOMPtr<nsISupports> next;
01696 
01697   PRBool more;
01698   while (NS_SUCCEEDED(providers->HasMoreElements(&more)) && more) {
01699     providers->GetNext(getter_AddRefs(next));
01700     NS_ASSERTION(next, "GetNext failed after HasMoreElements succeeded.");
01701 
01702     nsCOMPtr<nsIRDFResource> provider (do_QueryInterface(next));
01703     if (!provider) {
01704       NS_WARNING("Provider isn't a nsIRDFResource.");
01705       continue;
01706     }
01707 
01708     nsCAutoString providerName;
01709     GetResourceName(provider, providerName);
01710     if (providerName.IsEmpty()) {
01711       NS_WARNING("Couldn't calculate resource name.");
01712       continue;
01713     }
01714 
01715     nsCOMPtr<nsIRDFResource> packages;
01716     FollowResource(aDS, provider, packagesarc, getter_AddRefs(packages));
01717     if (!packages) {
01718       NS_WARNING("No chrome:packages arc found!");
01719       continue;
01720     }
01721 
01722     PRInt32 count;
01723     nsCOMPtr<nsISimpleEnumerator> packageList;
01724     rv = GetContainerEnumerator(aDS, packages, getter_AddRefs(packageList), &count);
01725     if (NS_FAILED(rv)) {
01726       NS_WARNING("chrome:packages was not a sequence.");
01727       continue;
01728     }
01729 
01730     nsCOMPtr<nsISupports> nextPackage;
01731 
01732     PRBool morePackages;
01733     while (NS_SUCCEEDED(packageList->HasMoreElements(&morePackages)) &&
01734            morePackages) {
01735       packageList->GetNext(getter_AddRefs(nextPackage));
01736 
01737       nsCOMPtr<nsIRDFResource> packageRes (do_QueryInterface(nextPackage));
01738       if (!packageRes) {
01739         NS_WARNING("chrome:packages Seq points to a non-resource!");
01740         continue;
01741       }
01742 
01743       nsCAutoString packageName;
01744       GetResourceName(packageRes, packageName);
01745       if (packageName.IsEmpty()) {
01746         NS_WARNING("couldn't extract a package name.");
01747         continue;
01748       }
01749 
01750       nsCAutoString line;
01751 
01752       if (aIsLocale)
01753         line.AppendLiteral("locale\t");
01754       else
01755         line.AppendLiteral("skin\t");
01756 
01757       line += packageName + kTab + providerName + kTab + aBaseURL;
01758       if (count > 1) {
01759         line += packageName + kSlash;
01760       }
01761       line.AppendLiteral(NS_LINEBREAK);
01762       PR_Write(fd, line.get(), line.Length());
01763     }
01764   }
01765 }
01766 
01767 static void
01768 GetLiteralText(nsIRDFLiteral* lit, nsACString& result)
01769 {
01770   const PRUnichar* value;
01771   lit->GetValueConst(&value);
01772   CopyUTF16toUTF8(value, result);
01773 }
01774 
01775 void
01776 nsChromeRegistry::ProcessOverlays(PRFileDesc *fd, nsIRDFDataSource* aDS,
01777                                   nsIRDFResource* aRoot,
01778                                   const nsCSubstring& aType)
01779 {
01780   NS_NAMED_LITERAL_CSTRING(kTab, "\t");
01781   NS_NAMED_LITERAL_CSTRING(kLinebreak, NS_LINEBREAK);
01782 
01783   nsresult rv;
01784 
01785   nsCOMPtr<nsISimpleEnumerator> overlaids;
01786   rv = GetContainerEnumerator(aDS, aRoot, getter_AddRefs(overlaids));
01787   if (NS_FAILED(rv)) {
01788     return;
01789   }
01790 
01791   nsCOMPtr<nsISupports> next;
01792   PRBool more;
01793   while (NS_SUCCEEDED(overlaids->HasMoreElements(&more)) && more) {
01794     overlaids->GetNext(getter_AddRefs(next));
01795     NS_ASSERTION(next, "GetNext failed after HasMoreElements succeeded.");
01796 
01797     nsCOMPtr<nsIRDFResource> overlaid (do_QueryInterface(next));
01798     if (!overlaid) {
01799       NS_WARNING("Overlay arc is not a nsIRDFResource.");
01800       continue;
01801     }
01802 
01803     nsCAutoString overlaidName;
01804     overlaid->GetValueUTF8(overlaidName);
01805 
01806     nsCOMPtr<nsISimpleEnumerator> overlays;
01807     rv = GetContainerEnumerator(aDS, overlaid, getter_AddRefs(overlays));
01808     if (NS_FAILED(rv))
01809       continue;
01810 
01811     while (NS_SUCCEEDED(overlays->HasMoreElements(&more)) && more) {
01812       overlays->GetNext(getter_AddRefs(next));
01813       NS_ASSERTION(next, "GetNext failed after HasMoreElements succeeded.");
01814 
01815       nsCOMPtr<nsIRDFLiteral> overlay (do_QueryInterface(next));
01816       if (!overlay) {
01817         NS_WARNING("Overlay was not an RDF literal.");
01818         continue;
01819       }
01820 
01821       nsCAutoString overlayName;
01822       GetLiteralText(overlay, overlayName);
01823 
01824       overlayName.Insert(aType + kTab + overlaidName + kTab, 0);
01825       overlayName.Append(kLinebreak);
01826       PR_Write(fd, overlayName.get(), overlayName.Length());
01827     }
01828   }
01829 }
01830 
01831 nsresult
01832 nsChromeRegistry::ProcessManifest(nsILocalFile* aManifest, PRBool aSkinOnly)
01833 {
01834   nsresult rv;
01835 
01836   PRFileDesc* fd;
01837   rv = aManifest->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
01838   NS_ENSURE_SUCCESS(rv, rv);
01839 
01840   PRInt32 n, size;
01841   char *buf;
01842 
01843   size = PR_Available(fd);
01844   if (size == -1) {
01845     rv = NS_ERROR_UNEXPECTED;
01846     goto mend;
01847   }
01848 
01849   buf = (char *) malloc(size + 1);
01850   if (!buf) {
01851     rv = NS_ERROR_OUT_OF_MEMORY;
01852     goto mend;
01853   }
01854 
01855   n = PR_Read(fd, buf, size);
01856   if (n > 0) {
01857     buf[size] = '\0';
01858     rv = ProcessManifestBuffer(buf, size, aManifest, aSkinOnly);
01859   }
01860   free(buf);
01861 
01862 mend:
01863   PR_Close(fd);
01864   return rv;
01865 }
01866 
01867 static const char kWhitespace[] = "\t ";
01868 static const char kNewlines[]   = "\r\n";
01869 
01881 static PRBool
01882 CheckFlag(const nsSubstring& aFlag, const nsSubstring& aData, PRBool& aResult)
01883 {
01884   if (!StringBeginsWith(aData, aFlag))
01885     return PR_FALSE;
01886 
01887   if (aFlag.Length() == aData.Length()) {
01888     // the data is simply "flag", which is the same as "flag=yes"
01889     aResult = PR_TRUE;
01890     return PR_TRUE;
01891   }
01892 
01893   if (aData.CharAt(aFlag.Length()) != '=') {
01894     // the data is "flag2=", which is not anything we care about
01895     return PR_FALSE;
01896   }
01897 
01898   if (aData.Length() == aFlag.Length() + 1) {
01899     aResult = PR_FALSE;
01900     return PR_TRUE;
01901   }
01902 
01903   switch (aData.CharAt(aFlag.Length() + 1)) {
01904   case '1':
01905   case 't': //true
01906   case 'y': //yes
01907     aResult = PR_TRUE;
01908     return PR_TRUE;
01909 
01910   case '0':
01911   case 'f': //false
01912   case 'n': //no
01913     aResult = PR_FALSE;
01914     return PR_TRUE;
01915   }
01916 
01917   return PR_FALSE;
01918 }
01919 
01920 enum TriState {
01921   eUnspecified,
01922   eBad,
01923   eOK
01924 };
01925 
01937 static PRBool
01938 CheckStringFlag(const nsSubstring& aFlag, const nsSubstring& aData,
01939                 const nsSubstring& aValue, TriState& aResult)
01940 {
01941   if (aData.Length() < aFlag.Length() + 1)
01942     return PR_FALSE;
01943 
01944   if (!StringBeginsWith(aData, aFlag))
01945     return PR_FALSE;
01946 
01947   if (aData[aFlag.Length()] != '=')
01948     return PR_FALSE;
01949 
01950   if (aResult != eOK) {
01951     nsDependentSubstring testdata = Substring(aData, aFlag.Length() + 1);
01952     if (testdata.Equals(aValue))
01953       aResult = eOK;
01954     else
01955       aResult = eBad;
01956   }
01957 
01958   return PR_TRUE;
01959 }
01960 
01979 #define COMPARE_EQ    1 << 0
01980 #define COMPARE_LT    1 << 1
01981 #define COMPARE_GT    1 << 2
01982 
01983 static PRBool
01984 CheckVersionFlag(const nsSubstring& aFlag, const nsSubstring& aData,
01985                  const nsSubstring& aValue, nsIVersionComparator* aChecker,
01986                  TriState& aResult)
01987 {
01988   if (aData.Length() < aFlag.Length() + 2)
01989     return PR_FALSE;
01990 
01991   if (!StringBeginsWith(aData, aFlag))
01992     return PR_FALSE;
01993 
01994   PRUint32 comparison;
01995   nsAutoString testdata;
01996 
01997   switch (aData[aFlag.Length()]) {
01998   case '=':
01999     comparison = COMPARE_EQ;
02000     testdata = Substring(aData, aFlag.Length() + 1);
02001     break;
02002 
02003   case '<':
02004     if (aData[aFlag.Length() + 1] == '=') {
02005       comparison = COMPARE_EQ | COMPARE_LT;
02006       testdata = Substring(aData, aFlag.Length() + 2);
02007     }
02008     else {
02009       comparison = COMPARE_LT;
02010       testdata = Substring(aData, aFlag.Length() + 1);
02011     }
02012     break;
02013 
02014   case '>':
02015     if (aData[aFlag.Length() + 1] == '=') {
02016       comparison = COMPARE_EQ | COMPARE_GT;
02017       testdata = Substring(aData, aFlag.Length() + 2);
02018     }
02019     else {
02020       comparison = COMPARE_GT;
02021       testdata = Substring(aData, aFlag.Length() + 1);
02022     }
02023     break;
02024 
02025   default:
02026     return PR_FALSE;
02027   }
02028 
02029   if (testdata.Length() == 0)
02030     return PR_FALSE;
02031 
02032   if (aResult != eOK) {
02033     if (!aChecker) {
02034       aResult = eBad;
02035     }
02036     else {
02037       PRInt32 c;
02038       nsresult rv = aChecker->Compare(NS_ConvertUTF16toUTF8(aValue),
02039                                       NS_ConvertUTF16toUTF8(testdata), &c);
02040       if (NS_FAILED(rv)) {
02041         aResult = eBad;
02042       }
02043       else {
02044         if ((c == 0 && comparison & COMPARE_EQ) ||
02045             (c < 0 && comparison & COMPARE_LT) ||
02046             (c > 0 && comparison & COMPARE_GT))
02047           aResult = eOK;
02048         else
02049           aResult = eBad;
02050       }
02051     }
02052   }
02053 
02054   return PR_TRUE;
02055 }
02056 
02057 nsresult
02058 nsChromeRegistry::ProcessManifestBuffer(char *buf, PRInt32 length,
02059                                         nsILocalFile* aManifest,
02060                                         PRBool aSkinOnly)
02061 {
02062   nsresult rv;
02063 
02064   NS_NAMED_LITERAL_STRING(kPlatform, "platform");
02065   NS_NAMED_LITERAL_STRING(kXPCNativeWrappers, "xpcnativewrappers");
02066   NS_NAMED_LITERAL_STRING(kApplication, "application");
02067   NS_NAMED_LITERAL_STRING(kAppVersion, "appversion");
02068 
02069   nsCOMPtr<nsIIOService> io (do_GetIOService());
02070   if (!io) return NS_ERROR_FAILURE;
02071 
02072   nsCOMPtr<nsIURI> manifestURI;
02073   rv = io->NewFileURI(aManifest, getter_AddRefs(manifestURI));
02074   NS_ENSURE_SUCCESS(rv, rv);
02075 
02076   nsCOMPtr<nsIXPConnect> xpc (do_GetService("@mozilla.org/js/xpc/XPConnect;1"));
02077   nsCOMPtr<nsIVersionComparator> vc (do_GetService("@mozilla.org/xpcom/version-comparator;1"));
02078 
02079   nsAutoString appID;
02080   nsAutoString appVersion;
02081   nsCOMPtr<nsIXULAppInfo> xapp (do_GetService(XULAPPINFO_SERVICE_CONTRACTID));
02082   if (xapp) {
02083     nsCAutoString s;
02084     rv = xapp->GetID(s);
02085     if (NS_SUCCEEDED(rv))
02086       CopyUTF8toUTF16(s, appID);
02087 
02088     rv = xapp->GetVersion(s);
02089     if (NS_SUCCEEDED(rv))
02090       CopyUTF8toUTF16(s, appVersion);
02091   }
02092 
02093   char *token;
02094   char *newline = buf;
02095   PRUint32 line = 0;
02096 
02097   // outer loop tokenizes by newline
02098   while (nsnull != (token = nsCRT::strtok(newline, kNewlines, &newline))) {
02099     ++line;
02100 
02101     if (*token == '#') // ignore lines that begin with # as comments
02102       continue;
02103 
02104     char *whitespace = token;
02105     token = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02106     if (!token) continue;
02107 
02108     if (!strcmp(token, "content")) {
02109       if (aSkinOnly) {
02110         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02111                               "Warning: Ignoring content registration in skin-only manifest.");
02112         continue;
02113       }
02114       char *package = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02115       char *uri     = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02116       if (!package || !uri) {
02117         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02118                               "Warning: Malformed content registration.");
02119         continue;
02120       }
02121 
02122       // NOTE: We check for platform and xpcnativewrappers modifiers on
02123       // content packages, but they are *applied* to content|skin|locale.
02124 
02125       PRBool platform = PR_FALSE;
02126       PRBool xpcNativeWrappers = PR_TRUE;
02127       TriState stAppVersion = eUnspecified;
02128       TriState stApp = eUnspecified;
02129 
02130       PRBool badFlag = PR_FALSE;
02131 
02132       while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
02133              !badFlag) {
02134         NS_ConvertASCIItoUTF16 wtoken(token);
02135         ToLowerCase(wtoken);
02136 
02137         if (CheckFlag(kPlatform, wtoken, platform) ||
02138             CheckFlag(kXPCNativeWrappers, wtoken, xpcNativeWrappers) ||
02139             CheckStringFlag(kApplication, wtoken, appID, stApp) ||
02140             CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion))
02141           continue;
02142 
02143         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02144                               "Warning: Unrecognized chrome registration modifier '%s'.",
02145                               token);
02146         badFlag = PR_TRUE;
02147       }
02148 
02149       if (badFlag || stApp == eBad || stAppVersion == eBad)
02150         continue;
02151 
02152       nsCOMPtr<nsIURI> resolved;
02153       rv = io->NewURI(nsDependentCString(uri), nsnull, manifestURI,
02154                       getter_AddRefs(resolved));
02155       if (NS_FAILED(rv))
02156         continue;
02157 
02158       PackageEntry* entry =
02159         NS_STATIC_CAST(PackageEntry*, PL_DHashTableOperate(&mPackagesHash,
02160                                                             & (const nsACString&) nsDependentCString(package),
02161                                                             PL_DHASH_ADD));
02162       if (!entry)
02163         return NS_ERROR_OUT_OF_MEMORY;
02164 
02165       entry->baseURI = resolved;
02166 
02167       if (platform)
02168         entry->flags |= PackageEntry::PLATFORM_PACKAGE;
02169       if (xpcNativeWrappers) {
02170         entry->flags |= PackageEntry::XPCNATIVEWRAPPERS;
02171         if (xpc) {
02172           nsCAutoString urlp("chrome://");
02173           urlp.Append(package);
02174           urlp.Append('/');
02175 
02176           rv = xpc->FlagSystemFilenamePrefix(urlp.get());
02177           NS_ENSURE_SUCCESS(rv, rv);
02178         }
02179       }
02180     }
02181     else if (!strcmp(token, "locale")) {
02182       if (aSkinOnly) {
02183         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02184                               "Warning: Ignoring locale registration in skin-only manifest.");
02185         continue;
02186       }
02187       char *package  = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02188       char *provider = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02189       char *uri      = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02190       if (!package || !provider || !uri) {
02191         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02192                               "Warning: Malformed locale registration.");
02193         continue;
02194       }
02195 
02196       TriState stAppVersion = eUnspecified;
02197       TriState stApp = eUnspecified;
02198 
02199       PRBool badFlag = PR_FALSE;
02200 
02201       while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
02202              !badFlag) {
02203         NS_ConvertASCIItoUTF16 wtoken(token);
02204         ToLowerCase(wtoken);
02205 
02206         if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
02207             CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion))
02208           continue;
02209 
02210         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02211                               "Warning: Unrecognized chrome registration modifier '%s'.",
02212                               token);
02213         badFlag = PR_TRUE;
02214       }
02215 
02216       if (badFlag || stApp == eBad || stAppVersion == eBad)
02217         continue;
02218 
02219       nsCOMPtr<nsIURI> resolved;
02220       rv = io->NewURI(nsDependentCString(uri), nsnull, manifestURI,
02221                       getter_AddRefs(resolved));
02222       if (NS_FAILED(rv))
02223         continue;
02224 
02225       PackageEntry* entry =
02226         NS_STATIC_CAST(PackageEntry*, PL_DHashTableOperate(&mPackagesHash,
02227                                                             & (const nsACString&) nsDependentCString(package),
02228                                                             PL_DHASH_ADD));
02229       if (!entry)
02230         return NS_ERROR_OUT_OF_MEMORY;
02231 
02232       entry->locales.SetBase(nsDependentCString(provider), resolved);
02233     }
02234     else if (!strcmp(token, "skin")) {
02235       char *package  = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02236       char *provider = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02237       char *uri      = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02238       if (!package || !provider || !uri) {
02239         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02240                               "Warning: Malformed skin registration.");
02241         continue;
02242       }
02243 
02244       TriState stAppVersion = eUnspecified;
02245       TriState stApp = eUnspecified;
02246 
02247       PRBool badFlag = PR_FALSE;
02248 
02249       while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
02250              !badFlag) {
02251         NS_ConvertASCIItoUTF16 wtoken(token);
02252         ToLowerCase(wtoken);
02253 
02254         if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
02255             CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion))
02256           continue;
02257 
02258         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02259                               "Warning: Unrecognized chrome registration modifier '%s'.",
02260                               token);
02261         badFlag = PR_TRUE;
02262       }
02263 
02264       if (badFlag || stApp == eBad || stAppVersion == eBad)
02265         continue;
02266 
02267       nsCOMPtr<nsIURI> resolved;
02268       rv = io->NewURI(nsDependentCString(uri), nsnull, manifestURI,
02269                       getter_AddRefs(resolved));
02270       if (NS_FAILED(rv))
02271         continue;
02272 
02273       PackageEntry* entry =
02274         NS_STATIC_CAST(PackageEntry*, PL_DHashTableOperate(&mPackagesHash,
02275                                                             & (const nsACString&) nsDependentCString(package),
02276                                                             PL_DHASH_ADD));
02277       if (!entry)
02278         return NS_ERROR_OUT_OF_MEMORY;
02279 
02280       entry->skins.SetBase(nsDependentCString(provider), resolved);
02281     }
02282     else if (!strcmp(token, "overlay")) {
02283       if (aSkinOnly) {
02284         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02285                               "Warning: Ignoring overlay registration in skin-only manifest.");
02286         continue;
02287       }
02288       char *base    = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02289       char *overlay = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02290       if (!base || !overlay) {
02291         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02292                               "Warning: malformed chrome overlay instruction.");
02293         continue;
02294       }
02295 
02296       TriState stAppVersion = eUnspecified;
02297       TriState stApp = eUnspecified;
02298 
02299       PRBool badFlag = PR_FALSE;
02300 
02301       while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
02302              !badFlag) {
02303         NS_ConvertASCIItoUTF16 wtoken(token);
02304         ToLowerCase(wtoken);
02305 
02306         if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
02307             CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion))
02308           continue;
02309 
02310         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02311                               "Warning: Unrecognized chrome registration modifier '%s'.",
02312                               token);
02313         badFlag = PR_TRUE;
02314       }
02315 
02316       if (badFlag || stApp == eBad || stAppVersion == eBad)
02317         continue;
02318 
02319       nsCOMPtr<nsIURI> baseuri, overlayuri;
02320       rv  = io->NewURI(nsDependentCString(base), nsnull, nsnull,
02321                        getter_AddRefs(baseuri));
02322       rv |= io->NewURI(nsDependentCString(overlay), nsnull, nsnull,
02323                        getter_AddRefs(overlayuri));
02324       if (NS_FAILED(rv)) {
02325         NS_WARNING("Could not make URIs for overlay directive. Ignoring.");
02326         continue;
02327       }
02328 
02329       mOverlayHash.Add(baseuri, overlayuri);
02330     }
02331     else if (!strcmp(token, "style")) {
02332       char *base    = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02333       char *overlay = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02334       if (!base || !overlay) {
02335         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02336                               "Warning: malformed chrome style instruction.");
02337         continue;
02338       }
02339 
02340       TriState stAppVersion = eUnspecified;
02341       TriState stApp = eUnspecified;
02342 
02343       PRBool badFlag = PR_FALSE;
02344 
02345       while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
02346              !badFlag) {
02347         NS_ConvertASCIItoUTF16 wtoken(token);
02348         ToLowerCase(wtoken);
02349 
02350         if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
02351             CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion))
02352           continue;
02353 
02354         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02355                               "Warning: Unrecognized chrome registration modifier '%s'.",
02356                               token);
02357         badFlag = PR_TRUE;
02358       }
02359 
02360       if (badFlag || stApp == eBad || stAppVersion == eBad)
02361         continue;
02362 
02363       nsCOMPtr<nsIURI> baseuri, overlayuri;
02364       rv  = io->NewURI(nsDependentCString(base), nsnull, nsnull,
02365                       getter_AddRefs(baseuri));
02366       rv |= io->NewURI(nsDependentCString(overlay), nsnull, nsnull,
02367                        getter_AddRefs(overlayuri));
02368       if (NS_FAILED(rv))
02369         continue;
02370 
02371       mStyleHash.Add(baseuri, overlayuri);
02372     }
02373     else if (!strcmp(token, "override")) {
02374       if (aSkinOnly) {
02375         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02376                               "Warning: Ignoring override registration in skin-only manifest.");
02377         continue;
02378       }
02379 
02380       char *chrome    = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02381       char *resolved  = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
02382       if (!chrome || !resolved) {
02383         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02384                               "Warning: malformed chrome override instruction.");
02385         continue;
02386       }
02387 
02388       TriState stAppVersion = eUnspecified;
02389       TriState stApp = eUnspecified;
02390 
02391       PRBool badFlag = PR_FALSE;
02392 
02393       while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
02394              !badFlag) {
02395         NS_ConvertASCIItoUTF16 wtoken(token);
02396         ToLowerCase(wtoken);
02397 
02398         if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
02399             CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion))
02400           continue;
02401 
02402         LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02403                               "Warning: Unrecognized chrome registration modifier '%s'.",
02404                               token);
02405         badFlag = PR_TRUE;
02406       }
02407 
02408       if (badFlag || stApp == eBad || stAppVersion == eBad)
02409         continue;
02410 
02411       nsCOMPtr<nsIURI> chromeuri, resolveduri;
02412       rv  = io->NewURI(nsDependentCString(chrome), nsnull, nsnull,
02413                       getter_AddRefs(chromeuri));
02414       rv |= io->NewURI(nsDependentCString(resolved), nsnull, manifestURI,
02415                        getter_AddRefs(resolveduri));
02416       if (NS_FAILED(rv))
02417         continue;
02418 
02419       mOverrideTable.Put(chromeuri, resolveduri);
02420     }
02421     else {
02422       LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag,
02423                             "Warning: Ignoring unrecognized chrome manifest instruction.");
02424     }
02425   }
02426 
02427   return NS_OK;
02428 }