/* -*- 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 * Benjamin Smedberg * * Alternatively, the contents of this file may be used under the terms of * either 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 "nsChromeRegistry.h" #include #include "prio.h" #include "prprf.h" #if defined(XP_WIN) #include #elif defined(XP_MACOSX) #include #elif defined(MOZ_WIDGET_GTK2) #include #endif #include "nsAppDirectoryServiceDefs.h" #include "nsArrayEnumerator.h" #include "nsStringEnumerator.h" #include "nsEnumeratorUtils.h" #include "nsCOMPtr.h" #include "nsDOMError.h" #include "nsEscape.h" #include "nsInt64.h" #include "nsLayoutCID.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsReadableUtils.h" #include "nsStaticAtom.h" #include "nsString.h" #include "nsUnicharUtils.h" #include "nsWidgetsCID.h" #include "nsXPCOMCIDInternal.h" #include "nsXPIDLString.h" #include "nsXULAppAPI.h" #include "nsTextFormatter.h" #include "nsIAtom.h" #include "nsICommandLine.h" #include "nsICSSLoader.h" #include "nsICSSStyleSheet.h" #include "nsIConsoleService.h" #include "nsIDirectoryService.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" #include "nsIDocShell.h" #include "nsIDocumentObserver.h" #include "nsIDOMElement.h" #include "nsIDOMLocation.h" #include "nsIDOMWindowCollection.h" #include "nsIDOMWindowInternal.h" #include "nsIFileChannel.h" #include "nsIFileURL.h" #include "nsIIOService.h" #include "nsIJARProtocolHandler.h" #include "nsIJARURI.h" #include "nsILocalFile.h" #include "nsILocaleService.h" #include "nsILookAndFeel.h" #include "nsIObserverService.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIPrefBranch2.h" #include "nsIPresShell.h" #include "nsIProtocolHandler.h" #include "nsIResProtocolHandler.h" #include "nsIScriptError.h" #include "nsIServiceManager.h" #include "nsISimpleEnumerator.h" #include "nsIStyleSheet.h" #include "nsISupportsArray.h" #include "nsIVersionComparator.h" #include "nsIWindowMediator.h" #include "nsIXPConnect.h" #include "nsIXULAppInfo.h" #include "nsIXULRuntime.h" #define UILOCALE_CMD_LINE_ARG "UILocale" #define MATCH_OS_LOCALE_PREF "intl.locale.matchOS" #define SELECTED_LOCALE_PREF "general.useragent.locale" #define SELECTED_SKIN_PREF "general.skins.selectedSkin" static NS_DEFINE_CID(kCSSLoaderCID, NS_CSS_LOADER_CID); static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); nsChromeRegistry* nsChromeRegistry::gChromeRegistry; //////////////////////////////////////////////////////////////////////////////// static void LogMessage(const char* aMsg, ...) { nsCOMPtr console (do_GetService(NS_CONSOLESERVICE_CONTRACTID)); if (!console) return; va_list args; va_start(args, aMsg); char* formatted = PR_vsmprintf(aMsg, args); va_end(args); if (!formatted) return; console->LogStringMessage(NS_ConvertUTF8toUTF16(formatted).get()); PR_smprintf_free(formatted); } static void LogMessageWithContext(nsIURI* aURL, PRUint32 aLineNumber, PRUint32 flags, const char* aMsg, ...) { nsresult rv; nsCOMPtr console (do_GetService(NS_CONSOLESERVICE_CONTRACTID)); nsCOMPtr error (do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); if (!console || !error) return; va_list args; va_start(args, aMsg); char* formatted = PR_vsmprintf(aMsg, args); va_end(args); if (!formatted) return; nsCString spec; if (aURL) aURL->GetSpec(spec); rv = error->Init(NS_ConvertUTF8toUTF16(formatted).get(), NS_ConvertUTF8toUTF16(spec).get(), nsnull, aLineNumber, 0, flags, "chrome registration"); PR_smprintf_free(formatted); if (NS_FAILED(rv)) return; console->LogMessage(error); } // We use a "best-fit" algorithm for matching locales and themes. // 1) the exact selected locale/theme // 2) (locales only) same language, different country // e.g. en-GB is the selected locale, only en-US is available // 3) any available locale/theme /** * Match the language-part of two lang-COUNTRY codes, hopefully but * not guaranteed to be in the form ab-CD or abz-CD. "ab" should also * work, any other garbage-in will produce undefined results as long * as it does not crash. */ static PRBool LanguagesMatch(const nsACString& a, const nsACString& b) { if (a.Length() < 2 || b.Length() < 2) return PR_FALSE; nsACString::const_iterator as, ae, bs, be; a.BeginReading(as); a.EndReading(ae); b.BeginReading(bs); b.EndReading(be); while (*as == *bs) { if (*as == '-') return PR_TRUE; ++as; ++bs; // reached the end if (as == ae && bs == be) return PR_TRUE; // "a" is short if (as == ae) return (*bs == '-'); // "b" is short if (bs == be) return (*as == '-'); } return PR_FALSE; } static PRBool CanLoadResource(nsIURI* aResourceURI) { PRBool isLocalResource = PR_FALSE; (void)NS_URIChainHasFlags(aResourceURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, &isLocalResource); return isLocalResource; } nsChromeRegistry::ProviderEntry* nsChromeRegistry::nsProviderArray::GetProvider(const nsACString& aPreferred, MatchType aType) { PRInt32 i = mArray.Count(); if (!i) return nsnull; ProviderEntry* found = nsnull; // Only set if we find a partial-match locale ProviderEntry* entry; while (i--) { entry = reinterpret_cast(mArray[i]); if (aPreferred.Equals(entry->provider)) return entry; if (aType != LOCALE) continue; if (LanguagesMatch(aPreferred, entry->provider)) { found = entry; continue; } if (!found && entry->provider.EqualsLiteral("en-US")) found = entry; } if (!found && aType != EXACT) return entry; return found; } nsIURI* nsChromeRegistry::nsProviderArray::GetBase(const nsACString& aPreferred, MatchType aType) { ProviderEntry* provider = GetProvider(aPreferred, aType); if (!provider) return nsnull; return provider->baseURI; } const nsACString& nsChromeRegistry::nsProviderArray::GetSelected(const nsACString& aPreferred, MatchType aType) { ProviderEntry* entry = GetProvider(aPreferred, aType); if (entry) return entry->provider; return EmptyCString(); } void nsChromeRegistry::nsProviderArray::SetBase(const nsACString& aProvider, nsIURI* aBaseURL) { ProviderEntry* provider = GetProvider(aProvider, EXACT); if (provider) { provider->baseURI = aBaseURL; return; } // no existing entries, add a new one provider = new ProviderEntry(aProvider, aBaseURL); if (!provider) return; // It's safe to silently fail on OOM mArray.AppendElement(provider); } void nsChromeRegistry::nsProviderArray::EnumerateToArray(nsTArray *a) { PRInt32 i = mArray.Count(); while (i--) { ProviderEntry *entry = reinterpret_cast(mArray[i]); a->AppendElement(entry->provider); } } void nsChromeRegistry::nsProviderArray::Clear() { PRInt32 i = mArray.Count(); while (i--) { ProviderEntry* entry = reinterpret_cast(mArray[i]); delete entry; } mArray.Clear(); } nsChromeRegistry::PackageEntry::PackageEntry(const nsACString& aPackage) : package(aPackage), flags(0) { } PLHashNumber nsChromeRegistry::HashKey(PLDHashTable *table, const void *key) { const nsACString& str = *reinterpret_cast(key); return HashString(str); } PRBool nsChromeRegistry::MatchKey(PLDHashTable *table, const PLDHashEntryHdr *entry, const void *key) { const nsACString& str = *reinterpret_cast(key); const PackageEntry* pentry = static_cast(entry); return str.Equals(pentry->package); } void nsChromeRegistry::ClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) { PackageEntry* pentry = static_cast(entry); pentry->~PackageEntry(); } PRBool nsChromeRegistry::InitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, const void *key) { const nsACString& str = *reinterpret_cast(key); new (entry) PackageEntry(str); return PR_TRUE; } const PLDHashTableOps nsChromeRegistry::kTableOps = { PL_DHashAllocTable, PL_DHashFreeTable, HashKey, MatchKey, PL_DHashMoveEntryStub, ClearEntry, PL_DHashFinalizeStub, InitEntry }; void nsChromeRegistry::OverlayListEntry::AddURI(nsIURI* aURI) { PRInt32 i = mArray.Count(); while (i--) { PRBool equals; if (NS_SUCCEEDED(aURI->Equals(mArray[i], &equals)) && equals) return; } mArray.AppendObject(aURI); } void nsChromeRegistry::OverlayListHash::Add(nsIURI* aBase, nsIURI* aOverlay) { OverlayListEntry* entry = mTable.PutEntry(aBase); if (entry) entry->AddURI(aOverlay); } const nsCOMArray* nsChromeRegistry::OverlayListHash::GetArray(nsIURI* aBase) { OverlayListEntry* entry = mTable.GetEntry(aBase); if (!entry) return nsnull; return &entry->mArray; } nsChromeRegistry::~nsChromeRegistry() { if (mPackagesHash.ops) PL_DHashTableFinish(&mPackagesHash); gChromeRegistry = nsnull; } NS_INTERFACE_MAP_BEGIN(nsChromeRegistry) NS_INTERFACE_MAP_ENTRY(nsIChromeRegistry) NS_INTERFACE_MAP_ENTRY(nsIXULChromeRegistry) NS_INTERFACE_MAP_ENTRY(nsIToolkitChromeRegistry) #ifdef MOZ_XUL NS_INTERFACE_MAP_ENTRY(nsIXULOverlayProvider) #endif NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChromeRegistry) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(nsChromeRegistry) NS_IMPL_RELEASE(nsChromeRegistry) //////////////////////////////////////////////////////////////////////////////// // nsIChromeRegistry methods: static nsresult getUILangCountry(nsACString& aUILang) { nsresult rv; nsCOMPtr localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsAutoString uiLang; rv = localeService->GetLocaleComponentForUserAgent(uiLang); NS_ENSURE_SUCCESS(rv, rv); CopyUTF16toUTF8(uiLang, aUILang); return NS_OK; } nsresult nsChromeRegistry::Init() { nsresult rv; // Check to see if necko and the JAR protocol handler are registered yet // if not, somebody is doing work during XPCOM registration that they // shouldn't be doing. See bug 292549, where JS components are trying // to call Components.utils.import("chrome:///") early in registration nsCOMPtr io (do_GetIOService()); if (!io) return NS_ERROR_FAILURE; nsCOMPtr ph; rv = io->GetProtocolHandler("jar", getter_AddRefs(ph)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr jph = do_QueryInterface(ph); if (!jph) return NS_ERROR_NOT_INITIALIZED; if (!PL_DHashTableInit(&mPackagesHash, &kTableOps, nsnull, sizeof(PackageEntry), 16)) return NS_ERROR_FAILURE; if (!mOverlayHash.Init() || !mStyleHash.Init() || !mOverrideTable.Init()) return NS_ERROR_FAILURE; mSelectedLocale = NS_LITERAL_CSTRING("en-US"); mSelectedSkin = NS_LITERAL_CSTRING("classic/1.0"); // This initialization process is fairly complicated and may cause reentrant // getservice calls to resolve chrome URIs (especially locale files). We // don't want that, so we inform the protocol handler about our existence // before we are actually fully initialized. gChromeRegistry = this; PRBool safeMode = PR_FALSE; nsCOMPtr xulrun (do_GetService(XULAPPINFO_SERVICE_CONTRACTID)); if (xulrun) xulrun->GetInSafeMode(&safeMode); nsCOMPtr prefserv (do_GetService(NS_PREFSERVICE_CONTRACTID)); nsCOMPtr prefs; if (safeMode) prefserv->GetDefaultBranch(nsnull, getter_AddRefs(prefs)); else prefs = do_QueryInterface(prefserv); if (!prefs) { NS_WARNING("Could not get pref service!"); } else { nsXPIDLCString provider; rv = prefs->GetCharPref(SELECTED_SKIN_PREF, getter_Copies(provider)); if (NS_SUCCEEDED(rv)) mSelectedSkin = provider; SelectLocaleFromPref(prefs); nsCOMPtr prefs2 (do_QueryInterface(prefs)); if (prefs2) { rv = prefs2->AddObserver(MATCH_OS_LOCALE_PREF, this, PR_TRUE); rv = prefs2->AddObserver(SELECTED_LOCALE_PREF, this, PR_TRUE); rv = prefs2->AddObserver(SELECTED_SKIN_PREF, this, PR_TRUE); } } nsCOMPtr obsService (do_GetService("@mozilla.org/observer-service;1")); if (obsService) { obsService->AddObserver(this, "command-line-startup", PR_TRUE); obsService->AddObserver(this, "profile-initial-state", PR_TRUE); } CheckForNewChrome(); mInitialized = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsChromeRegistry::CheckForOSAccessibility() { nsresult rv; nsCOMPtr lookAndFeel (do_GetService(kLookAndFeelCID)); if (lookAndFeel) { PRInt32 useAccessibilityTheme = 0; rv = lookAndFeel->GetMetric(nsILookAndFeel::eMetric_UseAccessibilityTheme, useAccessibilityTheme); if (NS_SUCCEEDED(rv) && useAccessibilityTheme) { /* Set the skin to classic and remove pref observers */ if (!mSelectedSkin.EqualsLiteral("classic/1.0")) { mSelectedSkin.AssignLiteral("classic/1.0"); RefreshSkins(); } nsCOMPtr prefs (do_GetService(NS_PREFSERVICE_CONTRACTID)); if (prefs) { prefs->RemoveObserver(SELECTED_SKIN_PREF, this); } } } return NS_OK; } nsresult nsChromeRegistry::GetProviderAndPath(nsIURL* aChromeURL, nsACString& aProvider, nsACString& aPath) { nsresult rv; #ifdef DEBUG PRBool isChrome; aChromeURL->SchemeIs("chrome", &isChrome); NS_ASSERTION(isChrome, "Non-chrome URI?"); #endif nsCAutoString path; rv = aChromeURL->GetPath(path); NS_ENSURE_SUCCESS(rv, rv); if (path.Length() < 3) { LogMessage("Invalid chrome URI: %s", path.get()); return NS_ERROR_FAILURE; } path.SetLength(nsUnescapeCount(path.BeginWriting())); NS_ASSERTION(path.First() == '/', "Path should always begin with a slash!"); PRInt32 slash = path.FindChar('/', 1); if (slash == 1) { LogMessage("Invalid chrome URI: %s", path.get()); return NS_ERROR_FAILURE; } if (slash == -1) { aPath.Truncate(); } else { if (slash == (PRInt32) path.Length() - 1) aPath.Truncate(); else aPath.Assign(path.get() + slash + 1, path.Length() - slash - 1); --slash; } aProvider.Assign(path.get() + 1, slash); return NS_OK; } nsresult nsChromeRegistry::Canonify(nsIURL* aChromeURL) { NS_NAMED_LITERAL_CSTRING(kSlash, "/"); nsresult rv; nsCAutoString provider, path; rv = GetProviderAndPath(aChromeURL, provider, path); NS_ENSURE_SUCCESS(rv, rv); if (path.IsEmpty()) { nsCAutoString package; rv = aChromeURL->GetHost(package); NS_ENSURE_SUCCESS(rv, rv); // we re-use the "path" local string to build a new URL path path.Assign(kSlash + provider + kSlash + package); if (provider.EqualsLiteral("content")) { path.AppendLiteral(".xul"); } else if (provider.EqualsLiteral("locale")) { path.AppendLiteral(".dtd"); } else if (provider.EqualsLiteral("skin")) { path.AppendLiteral(".css"); } else { return NS_ERROR_INVALID_ARG; } aChromeURL->SetPath(path); } else { // prevent directory traversals ("..") // path is already unescaped once, but uris can get unescaped twice const char* pos = path.BeginReading(); const char* end = path.EndReading(); while (pos < end) { switch (*pos) { case ':': return NS_ERROR_DOM_BAD_URI; case '.': if (pos[1] == '.') return NS_ERROR_DOM_BAD_URI; break; case '%': // chrome: URIs with double-escapes are trying to trick us. // watch for %2e, and %25 in case someone triple unescapes if (pos[1] == '2' && ( pos[2] == 'e' || pos[2] == 'E' || pos[2] == '5' )) return NS_ERROR_DOM_BAD_URI; break; case '?': case '#': pos = end; continue; } ++pos; } } return NS_OK; } NS_IMETHODIMP nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURI, nsIURI* *aResult) { nsresult rv; NS_ASSERTION(aChromeURI, "null url!"); if (mOverrideTable.Get(aChromeURI, aResult)) return NS_OK; nsCOMPtr chromeURL (do_QueryInterface(aChromeURI)); NS_ENSURE_TRUE(chromeURL, NS_NOINTERFACE); nsCAutoString package, provider, path; rv = chromeURL->GetHostPort(package); NS_ENSURE_SUCCESS(rv, rv); rv = GetProviderAndPath(chromeURL, provider, path); NS_ENSURE_SUCCESS(rv, rv); PackageEntry* entry = static_cast(PL_DHashTableOperate(&mPackagesHash, & (nsACString&) package, PL_DHASH_LOOKUP)); if (PL_DHASH_ENTRY_IS_FREE(entry)) { if (!mInitialized) return NS_ERROR_NOT_INITIALIZED; LogMessage("No chrome package registered for chrome://%s/%s/%s", package.get(), provider.get(), path.get()); return NS_ERROR_FAILURE; } if (entry->flags & PackageEntry::PLATFORM_PACKAGE) { #if defined(XP_WIN) || defined(XP_OS2) path.Insert("win/", 0); #elif defined(XP_MACOSX) path.Insert("mac/", 0); #else path.Insert("unix/", 0); #endif } nsIURI* baseURI = nsnull; if (provider.EqualsLiteral("locale")) { baseURI = entry->locales.GetBase(mSelectedLocale, nsProviderArray::LOCALE); } else if (provider.EqualsLiteral("skin")) { baseURI = entry->skins.GetBase(mSelectedSkin, nsProviderArray::ANY); } else if (provider.EqualsLiteral("content")) { baseURI = entry->baseURI; } if (!baseURI) { LogMessage("No chrome package registered for chrome://%s/%s/%s", package.get(), provider.get(), path.get()); return NS_ERROR_FAILURE; } return NS_NewURI(aResult, path, nsnull, baseURI); } nsresult nsChromeRegistry::GetSelectedLocale(const nsACString& aPackage, nsACString& aLocale) { PackageEntry* entry = static_cast(PL_DHashTableOperate(&mPackagesHash, & aPackage, PL_DHASH_LOOKUP)); if (PL_DHASH_ENTRY_IS_FREE(entry)) return NS_ERROR_FAILURE; aLocale = entry->locales.GetSelected(mSelectedLocale, nsProviderArray::LOCALE); if (aLocale.IsEmpty()) return NS_ERROR_FAILURE; return NS_OK; } NS_IMETHODIMP nsChromeRegistry::IsLocaleRTL(const nsACString& package, PRBool *aResult) { *aResult = PR_FALSE; nsCAutoString locale; GetSelectedLocale(package, locale); if (locale.Length() < 2) return NS_OK; // first check the intl.uidirection. preference, and if that is not // set, check the same preference but with just the first two characters of // the locale. If that isn't set, default to left-to-right. nsCAutoString prefString = NS_LITERAL_CSTRING("intl.uidirection.") + locale; nsCOMPtr prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID)); if (!prefBranch) return NS_OK; nsXPIDLCString dir; prefBranch->GetCharPref(prefString.get(), getter_Copies(dir)); if (dir.IsEmpty()) { PRInt32 hyphen = prefString.FindChar('-'); if (hyphen >= 1) { nsCAutoString shortPref(Substring(prefString, 0, hyphen)); prefBranch->GetCharPref(shortPref.get(), getter_Copies(dir)); } } *aResult = dir.EqualsLiteral("rtl"); return NS_OK; } NS_IMETHODIMP nsChromeRegistry::GetLocalesForPackage(const nsACString& aPackage, nsIUTF8StringEnumerator* *aResult) { nsTArray *a = new nsTArray; if (!a) return NS_ERROR_OUT_OF_MEMORY; PackageEntry* entry = static_cast(PL_DHashTableOperate(&mPackagesHash, & aPackage, PL_DHASH_LOOKUP)); if (PL_DHASH_ENTRY_IS_BUSY(entry)) { entry->locales.EnumerateToArray(a); } nsresult rv = NS_NewAdoptingUTF8StringEnumerator(aResult, a); if (NS_FAILED(rv)) delete a; return rv; } #ifdef MOZ_XUL NS_IMETHODIMP nsChromeRegistry::GetStyleOverlays(nsIURI *aChromeURL, nsISimpleEnumerator **aResult) { const nsCOMArray* parray = mStyleHash.GetArray(aChromeURL); if (!parray) return NS_NewEmptyEnumerator(aResult); return NS_NewArrayEnumerator(aResult, *parray); } NS_IMETHODIMP nsChromeRegistry::GetXULOverlays(nsIURI *aChromeURL, nsISimpleEnumerator **aResult) { const nsCOMArray* parray = mOverlayHash.GetArray(aChromeURL); if (!parray) return NS_NewEmptyEnumerator(aResult); return NS_NewArrayEnumerator(aResult, *parray); } #endif // MOZ_XUL //////////////////////////////////////////////////////////////////////// // theme stuff static void FlushSkinBindingsForWindow(nsIDOMWindowInternal* aWindow) { // Get the DOM document. nsCOMPtr domDocument; aWindow->GetDocument(getter_AddRefs(domDocument)); if (!domDocument) return; nsCOMPtr document = do_QueryInterface(domDocument); if (!document) return; // Annihilate all XBL bindings. document->FlushSkinBindings(); } // XXXbsmedberg: move this to nsIWindowMediator NS_IMETHODIMP nsChromeRegistry::RefreshSkins() { nsCOMPtr cssLoader(do_CreateInstance(kCSSLoaderCID)); if (!cssLoader) return NS_OK; nsCOMPtr windowMediator (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); if (!windowMediator) return NS_OK; nsCOMPtr windowEnumerator; windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator)); PRBool more; windowEnumerator->HasMoreElements(&more); while (more) { nsCOMPtr protoWindow; windowEnumerator->GetNext(getter_AddRefs(protoWindow)); if (protoWindow) { nsCOMPtr domWindow = do_QueryInterface(protoWindow); if (domWindow) FlushSkinBindingsForWindow(domWindow); } windowEnumerator->HasMoreElements(&more); } FlushSkinCaches(); windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator)); windowEnumerator->HasMoreElements(&more); while (more) { nsCOMPtr protoWindow; windowEnumerator->GetNext(getter_AddRefs(protoWindow)); if (protoWindow) { nsCOMPtr domWindow = do_QueryInterface(protoWindow); if (domWindow) RefreshWindow(domWindow, cssLoader); } windowEnumerator->HasMoreElements(&more); } return NS_OK; } void nsChromeRegistry::FlushSkinCaches() { nsCOMPtr obsSvc = do_GetService("@mozilla.org/observer-service;1"); NS_ASSERTION(obsSvc, "Couldn't get observer service."); obsSvc->NotifyObservers(static_cast(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, nsICSSLoader* aCSSLoader) { // Deal with our subframes first. nsCOMPtr frames; aWindow->GetFrames(getter_AddRefs(frames)); PRUint32 length; frames->GetLength(&length); PRUint32 j; for (j = 0; j < length; j++) { nsCOMPtr childWin; frames->Item(j, getter_AddRefs(childWin)); nsCOMPtr childInt(do_QueryInterface(childWin)); RefreshWindow(childInt, aCSSLoader); } nsresult rv; // Get the DOM document. nsCOMPtr domDocument; aWindow->GetDocument(getter_AddRefs(domDocument)); if (!domDocument) return NS_OK; nsCOMPtr document = do_QueryInterface(domDocument); if (!document) return NS_OK; // Deal with the agent sheets first. Have to do all the style sets by hand. nsCOMPtr shell = document->GetPrimaryShell(); if (shell) { // Reload only the chrome URL agent style sheets. nsCOMArray agentSheets; rv = shell->GetAgentStyleSheets(agentSheets); NS_ENSURE_SUCCESS(rv, rv); nsCOMArray newAgentSheets; for (PRInt32 l = 0; l < agentSheets.Count(); ++l) { nsIStyleSheet *sheet = agentSheets[l]; nsCOMPtr uri; rv = sheet->GetSheetURI(getter_AddRefs(uri)); if (NS_FAILED(rv)) return rv; if (IsChromeURI(uri)) { // Reload the sheet. nsCOMPtr newSheet; rv = aCSSLoader->LoadSheetSync(uri, PR_TRUE, 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 oldSheets; nsCOMArray 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 sheet = do_QueryInterface(oldSheets[i]); nsIURI* uri = sheet ? sheet->GetOriginalURI() : nsnull; if (uri && IsChromeURI(uri)) { // Reload the sheet. nsCOMPtr 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! aCSSLoader->LoadSheetSync(uri, 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; } void nsChromeRegistry::FlushAllCaches() { nsCOMPtr 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 windowMediator (do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); if (windowMediator) { nsCOMPtr 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 protoWindow; rv = windowEnumerator->GetNext(getter_AddRefs(protoWindow)); if (NS_SUCCEEDED(rv)) { nsCOMPtr domWindow = do_QueryInterface(protoWindow); if (domWindow) { nsCOMPtr 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; } NS_IMETHODIMP nsChromeRegistry::AllowScriptsForPackage(nsIURI* aChromeURI, PRBool *aResult) { nsresult rv; *aResult = PR_FALSE; #ifdef DEBUG PRBool isChrome; aChromeURI->SchemeIs("chrome", &isChrome); NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowScriptsForPackage!"); #endif nsCOMPtr url (do_QueryInterface(aChromeURI)); NS_ENSURE_TRUE(url, NS_NOINTERFACE); nsCAutoString provider, file; rv = GetProviderAndPath(url, provider, file); NS_ENSURE_SUCCESS(rv, rv); if (!provider.EqualsLiteral("skin")) *aResult = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsChromeRegistry::AllowContentToAccess(nsIURI *aURI, PRBool *aResult) { nsresult rv; *aResult = PR_FALSE; #ifdef DEBUG PRBool isChrome; aURI->SchemeIs("chrome", &isChrome); NS_ASSERTION(isChrome, "Non-chrome URI passed to AllowContentToAccess!"); #endif nsCOMPtr url = do_QueryInterface(aURI); if (!url) { NS_ERROR("Chrome URL doesn't implement nsIURL."); return NS_ERROR_UNEXPECTED; } nsCAutoString package; rv = url->GetHostPort(package); NS_ENSURE_SUCCESS(rv, rv); PackageEntry *entry = static_cast(PL_DHashTableOperate(&mPackagesHash, & (nsACString&) package, PL_DHASH_LOOKUP)); if (PL_DHASH_ENTRY_IS_BUSY(entry)) { *aResult = !!(entry->flags & PackageEntry::CONTENT_ACCESSIBLE); } return NS_OK; } static PLDHashOperator RemoveAll(PLDHashTable *table, PLDHashEntryHdr *entry, PRUint32 number, void *arg) { return (PLDHashOperator) (PL_DHASH_NEXT | PL_DHASH_REMOVE); } NS_IMETHODIMP nsChromeRegistry::CheckForNewChrome() { nsresult rv; PL_DHashTableEnumerate(&mPackagesHash, RemoveAll, nsnull); mOverlayHash.Clear(); mStyleHash.Clear(); mOverrideTable.Clear(); nsCOMPtr manifestURI; rv = NS_NewURI(getter_AddRefs(manifestURI), NS_LITERAL_CSTRING("resource:///chrome/app-chrome.manifest")); nsCOMPtr manifestFileURL (do_QueryInterface(manifestURI)); NS_ASSERTION(manifestFileURL, "Not a nsIFileURL!"); NS_ENSURE_TRUE(manifestFileURL, NS_ERROR_UNEXPECTED); nsCOMPtr manifest; manifestFileURL->GetFile(getter_AddRefs(manifest)); NS_ENSURE_TRUE(manifest, NS_ERROR_FAILURE); PRBool exists; rv = manifest->Exists(&exists); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr dirSvc (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); NS_ENSURE_TRUE(dirSvc, NS_ERROR_FAILURE); // check the extra chrome directories nsCOMPtr chromeML; rv = dirSvc->Get(NS_CHROME_MANIFESTS_FILE_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(chromeML)); if (NS_FAILED(rv)) { // ok, then simply load all .manifest files in the app chrome dir. nsCOMPtr chromeDir; rv = dirSvc->Get(NS_APP_CHROME_DIR, NS_GET_IID(nsIFile), getter_AddRefs(chromeDir)); if (NS_FAILED(rv)) return rv; rv = NS_NewSingletonEnumerator(getter_AddRefs(chromeML), chromeDir); if (NS_FAILED(rv)) return rv; } nsCOMPtr next; while (NS_SUCCEEDED(chromeML->HasMoreElements(&exists)) && exists) { chromeML->GetNext(getter_AddRefs(next)); nsCOMPtr lmanifest = do_QueryInterface(next); if (!lmanifest) { NS_ERROR("Directory enumerator returned a non-nsILocalFile"); continue; } PRBool isDir; if (NS_SUCCEEDED(lmanifest->IsDirectory(&isDir)) && isDir) { nsCOMPtr entries; rv = lmanifest->GetDirectoryEntries(getter_AddRefs(entries)); if (NS_FAILED(rv)) continue; while (NS_SUCCEEDED(entries->HasMoreElements(&exists)) && exists) { entries->GetNext(getter_AddRefs(next)); lmanifest = do_QueryInterface(next); if (lmanifest) { nsCAutoString leafName; lmanifest->GetNativeLeafName(leafName); if (StringEndsWith(leafName, NS_LITERAL_CSTRING(".manifest"))) { rv = ProcessManifest(lmanifest, PR_FALSE); if (NS_FAILED(rv)) { nsCAutoString path; lmanifest->GetNativePath(path); LogMessage("Failed to process chrome manifest '%s'.", path.get()); } } } } } else { rv = ProcessManifest(lmanifest, PR_FALSE); if (NS_FAILED(rv)) { nsCAutoString path; lmanifest->GetNativePath(path); LogMessage("Failed to process chrome manifest: '%s'.", path.get()); } } } rv = dirSvc->Get(NS_SKIN_MANIFESTS_FILE_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(chromeML)); if (NS_FAILED(rv)) return NS_OK; while (NS_SUCCEEDED(chromeML->HasMoreElements(&exists)) && exists) { chromeML->GetNext(getter_AddRefs(next)); nsCOMPtr lmanifest = do_QueryInterface(next); if (!lmanifest) { NS_ERROR("Directory enumerator returned a non-nsILocalFile"); continue; } rv = ProcessManifest(lmanifest, PR_TRUE); if (NS_FAILED(rv)) { nsCAutoString path; lmanifest->GetNativePath(path); LogMessage("Failed to process chrome manifest: '%s'.", path.get()); } } return NS_OK; } NS_IMETHODIMP_(PRBool) nsChromeRegistry::WrappersEnabled(nsIURI *aURI) { nsCOMPtr chromeURL (do_QueryInterface(aURI)); if (!chromeURL) return PR_FALSE; PRBool isChrome = PR_FALSE; nsresult rv = chromeURL->SchemeIs("chrome", &isChrome); if (NS_FAILED(rv) || !isChrome) return PR_FALSE; nsCAutoString package; rv = chromeURL->GetHostPort(package); if (NS_FAILED(rv)) return PR_FALSE; PackageEntry* entry = static_cast(PL_DHashTableOperate(&mPackagesHash, & (nsACString&) package, PL_DHASH_LOOKUP)); return PL_DHASH_ENTRY_IS_LIVE(entry) && entry->flags & PackageEntry::XPCNATIVEWRAPPERS; } nsresult nsChromeRegistry::SelectLocaleFromPref(nsIPrefBranch* prefs) { nsresult rv; PRBool matchOSLocale = PR_FALSE; rv = prefs->GetBoolPref(MATCH_OS_LOCALE_PREF, &matchOSLocale); if (NS_SUCCEEDED(rv) && matchOSLocale) { // compute lang and region code only when needed! nsCAutoString uiLocale; rv = getUILangCountry(uiLocale); if (NS_SUCCEEDED(rv)) mSelectedLocale = uiLocale; } else { nsXPIDLCString provider; rv = prefs->GetCharPref(SELECTED_LOCALE_PREF, getter_Copies(provider)); if (NS_SUCCEEDED(rv)) { mSelectedLocale = provider; } } return rv; } NS_IMETHODIMP nsChromeRegistry::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData) { nsresult rv = NS_OK; if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) { nsCOMPtr prefs (do_QueryInterface(aSubject)); NS_ASSERTION(prefs, "Bad observer call!"); NS_ConvertUTF16toUTF8 pref(someData); if (pref.EqualsLiteral(MATCH_OS_LOCALE_PREF) || pref.EqualsLiteral(SELECTED_LOCALE_PREF)) { rv = SelectLocaleFromPref(prefs); if (NS_SUCCEEDED(rv) && mProfileLoaded) FlushAllCaches(); } else if (pref.EqualsLiteral(SELECTED_SKIN_PREF)) { nsXPIDLCString provider; rv = prefs->GetCharPref(pref.get(), getter_Copies(provider)); if (NS_FAILED(rv)) { NS_ERROR("Couldn't get new locale pref!"); return rv; } mSelectedSkin = provider; RefreshSkins(); } else { NS_ERROR("Unexpected pref!"); } } else if (!strcmp("command-line-startup", aTopic)) { nsCOMPtr cmdLine (do_QueryInterface(aSubject)); if (cmdLine) { nsAutoString uiLocale; rv = cmdLine->HandleFlagWithParam(NS_LITERAL_STRING(UILOCALE_CMD_LINE_ARG), PR_FALSE, uiLocale); if (NS_SUCCEEDED(rv) && !uiLocale.IsEmpty()) { CopyUTF16toUTF8(uiLocale, mSelectedLocale); nsCOMPtr prefs (do_GetService(NS_PREFSERVICE_CONTRACTID)); if (prefs) { prefs->RemoveObserver(SELECTED_LOCALE_PREF, this); } } } } else if (!strcmp("profile-initial-state", aTopic)) { mProfileLoaded = PR_TRUE; } else { NS_ERROR("Unexpected observer topic!"); } return rv; } nsresult nsChromeRegistry::ProcessManifest(nsILocalFile* aManifest, PRBool aSkinOnly) { nsresult rv; PRFileDesc* fd; rv = aManifest->OpenNSPRFileDesc(PR_RDONLY, 0, &fd); NS_ENSURE_SUCCESS(rv, rv); PRInt32 n, size; char *buf; size = PR_Available(fd); if (size == -1) { rv = NS_ERROR_UNEXPECTED; goto mend; } buf = (char *) malloc(size + 1); if (!buf) { rv = NS_ERROR_OUT_OF_MEMORY; goto mend; } n = PR_Read(fd, buf, size); if (n > 0) { buf[size] = '\0'; rv = ProcessManifestBuffer(buf, size, aManifest, aSkinOnly); } free(buf); mend: PR_Close(fd); return rv; } static const char kWhitespace[] = "\t "; static const char kNewlines[] = "\r\n"; /** * Check for a modifier flag of the following forms: * "flag" (same as "true") * "flag=yes|true|1" * "flag="no|false|0" * @param aFlag The flag to compare. * @param aData The tokenized data to check; this is lowercased * before being passed in. * @param aResult If the flag is found, the value is assigned here. * @return Whether the flag was handled. */ static PRBool CheckFlag(const nsSubstring& aFlag, const nsSubstring& aData, PRBool& aResult) { if (!StringBeginsWith(aData, aFlag)) return PR_FALSE; if (aFlag.Length() == aData.Length()) { // the data is simply "flag", which is the same as "flag=yes" aResult = PR_TRUE; return PR_TRUE; } if (aData.CharAt(aFlag.Length()) != '=') { // the data is "flag2=", which is not anything we care about return PR_FALSE; } if (aData.Length() == aFlag.Length() + 1) { aResult = PR_FALSE; return PR_TRUE; } switch (aData.CharAt(aFlag.Length() + 1)) { case '1': case 't': //true case 'y': //yes aResult = PR_TRUE; return PR_TRUE; case '0': case 'f': //false case 'n': //no aResult = PR_FALSE; return PR_TRUE; } return PR_FALSE; } enum TriState { eUnspecified, eBad, eOK }; /** * Check for a modifier flag of the following form: * "flag=string" * "flag!=string" * @param aFlag The flag to compare. * @param aData The tokenized data to check; this is lowercased * before being passed in. * @param aValue The value that is expected. * @param aResult If this is "ok" when passed in, this is left alone. * Otherwise if the flag is found it is set to eBad or eOK. * @return Whether the flag was handled. */ static PRBool CheckStringFlag(const nsSubstring& aFlag, const nsSubstring& aData, const nsSubstring& aValue, TriState& aResult) { if (aData.Length() < aFlag.Length() + 1) return PR_FALSE; if (!StringBeginsWith(aData, aFlag)) return PR_FALSE; PRBool comparison = PR_TRUE; if (aData[aFlag.Length()] != '=') { if (aData[aFlag.Length()] == '!' && aData.Length() >= aFlag.Length() + 2 && aData[aFlag.Length() + 1] == '=') comparison = PR_FALSE; else return PR_FALSE; } if (aResult != eOK) { nsDependentSubstring testdata = Substring(aData, aFlag.Length() + (comparison ? 1 : 2)); if (testdata.Equals(aValue)) aResult = comparison ? eOK : eBad; else aResult = comparison ? eBad : eOK; } return PR_TRUE; } /** * Check for a modifier flag of the following form: * "flag=version" * "flag<=version" * "flag=version" * "flag>version" * @param aFlag The flag to compare. * @param aData The tokenized data to check; this is lowercased * before being passed in. * @param aValue The value that is expected. If this is empty then no * comparison will match. * @param aChecker the version checker to use. If null, aResult will always * be eBad. * @param aResult If this is eOK when passed in, this is left alone. * Otherwise if the flag is found it is set to eBad or eOK. * @return Whether the flag was handled. */ #define COMPARE_EQ 1 << 0 #define COMPARE_LT 1 << 1 #define COMPARE_GT 1 << 2 static PRBool CheckVersionFlag(const nsSubstring& aFlag, const nsSubstring& aData, const nsSubstring& aValue, nsIVersionComparator* aChecker, TriState& aResult) { if (aData.Length() < aFlag.Length() + 2) return PR_FALSE; if (!StringBeginsWith(aData, aFlag)) return PR_FALSE; if (aValue.Length() == 0) { if (aResult != eOK) aResult = eBad; return PR_TRUE; } PRUint32 comparison; nsAutoString testdata; switch (aData[aFlag.Length()]) { case '=': comparison = COMPARE_EQ; testdata = Substring(aData, aFlag.Length() + 1); break; case '<': if (aData[aFlag.Length() + 1] == '=') { comparison = COMPARE_EQ | COMPARE_LT; testdata = Substring(aData, aFlag.Length() + 2); } else { comparison = COMPARE_LT; testdata = Substring(aData, aFlag.Length() + 1); } break; case '>': if (aData[aFlag.Length() + 1] == '=') { comparison = COMPARE_EQ | COMPARE_GT; testdata = Substring(aData, aFlag.Length() + 2); } else { comparison = COMPARE_GT; testdata = Substring(aData, aFlag.Length() + 1); } break; default: return PR_FALSE; } if (testdata.Length() == 0) return PR_FALSE; if (aResult != eOK) { if (!aChecker) { aResult = eBad; } else { PRInt32 c; nsresult rv = aChecker->Compare(NS_ConvertUTF16toUTF8(aValue), NS_ConvertUTF16toUTF8(testdata), &c); if (NS_FAILED(rv)) { aResult = eBad; } else { if ((c == 0 && comparison & COMPARE_EQ) || (c < 0 && comparison & COMPARE_LT) || (c > 0 && comparison & COMPARE_GT)) aResult = eOK; else aResult = eBad; } } } return PR_TRUE; } static void EnsureLowerCase(char *aBuf) { for (; *aBuf; ++aBuf) { char ch = *aBuf; if (ch >= 'A' && ch <= 'Z') *aBuf = ch + 'a' - 'A'; } } nsresult nsChromeRegistry::ProcessManifestBuffer(char *buf, PRInt32 length, nsILocalFile* aManifest, PRBool aSkinOnly) { nsresult rv; NS_NAMED_LITERAL_STRING(kPlatform, "platform"); NS_NAMED_LITERAL_STRING(kXPCNativeWrappers, "xpcnativewrappers"); NS_NAMED_LITERAL_STRING(kContentAccessible, "contentaccessible"); NS_NAMED_LITERAL_STRING(kApplication, "application"); NS_NAMED_LITERAL_STRING(kAppVersion, "appversion"); NS_NAMED_LITERAL_STRING(kOs, "os"); NS_NAMED_LITERAL_STRING(kOsVersion, "osversion"); nsCOMPtr io (do_GetIOService()); if (!io) return NS_ERROR_FAILURE; nsCOMPtr ph; rv = io->GetProtocolHandler("resource", getter_AddRefs(ph)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr rph (do_QueryInterface(ph)); if (!rph) return NS_ERROR_FAILURE; nsCOMPtr manifestURI; rv = io->NewFileURI(aManifest, getter_AddRefs(manifestURI)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr xpc (do_GetService("@mozilla.org/js/xpc/XPConnect;1")); nsCOMPtr vc (do_GetService("@mozilla.org/xpcom/version-comparator;1")); nsAutoString appID; nsAutoString appVersion; nsAutoString osTarget; nsCOMPtr xapp (do_GetService(XULAPPINFO_SERVICE_CONTRACTID)); if (xapp) { nsCAutoString s; rv = xapp->GetID(s); if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(s, appID); rv = xapp->GetVersion(s); if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(s, appVersion); nsCOMPtr xruntime (do_QueryInterface(xapp)); if (xruntime) { rv = xruntime->GetOS(s); if (NS_SUCCEEDED(rv)) { CopyUTF8toUTF16(s, osTarget); ToLowerCase(osTarget); } } } nsAutoString osVersion; #if defined(XP_WIN) OSVERSIONINFO info = { sizeof(OSVERSIONINFO) }; if (GetVersionEx(&info)) { nsTextFormatter::ssprintf(osVersion, NS_LITERAL_STRING("%ld.%ld").get(), info.dwMajorVersion, info.dwMinorVersion); } #elif defined(XP_MACOSX) SInt32 majorVersion, minorVersion; if ((Gestalt(gestaltSystemVersionMajor, &majorVersion) == noErr) && (Gestalt(gestaltSystemVersionMinor, &minorVersion) == noErr)) { nsTextFormatter::ssprintf(osVersion, NS_LITERAL_STRING("%ld.%ld").get(), majorVersion, minorVersion); } #elif defined(MOZ_WIDGET_GTK2) nsTextFormatter::ssprintf(osVersion, NS_LITERAL_STRING("%ld.%ld").get(), gtk_major_version, gtk_minor_version); #endif char *token; char *newline = buf; PRUint32 line = 0; // outer loop tokenizes by newline while (nsnull != (token = nsCRT::strtok(newline, kNewlines, &newline))) { ++line; if (*token == '#') // ignore lines that begin with # as comments continue; char *whitespace = token; token = nsCRT::strtok(whitespace, kWhitespace, &whitespace); if (!token) continue; if (!strcmp(token, "content")) { if (aSkinOnly) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Ignoring content registration in skin-only manifest."); continue; } char *package = nsCRT::strtok(whitespace, kWhitespace, &whitespace); char *uri = nsCRT::strtok(whitespace, kWhitespace, &whitespace); if (!package || !uri) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Malformed content registration."); continue; } EnsureLowerCase(package); // NOTE: We check for platform and xpcnativewrappers modifiers on // content packages, but they are *applied* to content|skin|locale. PRBool platform = PR_FALSE; PRBool xpcNativeWrappers = PR_TRUE; PRBool contentAccessible = PR_FALSE; TriState stAppVersion = eUnspecified; TriState stApp = eUnspecified; TriState stOsVersion = eUnspecified; TriState stOs = eUnspecified; PRBool badFlag = PR_FALSE; while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && !badFlag) { NS_ConvertASCIItoUTF16 wtoken(token); ToLowerCase(wtoken); if (CheckFlag(kPlatform, wtoken, platform) || CheckFlag(kXPCNativeWrappers, wtoken, xpcNativeWrappers) || CheckFlag(kContentAccessible, wtoken, contentAccessible) || CheckStringFlag(kApplication, wtoken, appID, stApp) || CheckStringFlag(kOs, wtoken, osTarget, stOs) || CheckVersionFlag(kOsVersion, wtoken, osVersion, vc, stOsVersion) || CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion)) continue; LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Unrecognized chrome registration modifier '%s'.", token); badFlag = PR_TRUE; } if (badFlag || stApp == eBad || stAppVersion == eBad || stOs == eBad || stOsVersion == eBad) continue; nsCOMPtr resolved; rv = io->NewURI(nsDependentCString(uri), nsnull, manifestURI, getter_AddRefs(resolved)); if (NS_FAILED(rv)) continue; if (!CanLoadResource(resolved)) { LogMessageWithContext(resolved, line, nsIScriptError::warningFlag, "Warning: cannot register non-local URI '%s' as content.", uri); continue; } PackageEntry* entry = static_cast(PL_DHashTableOperate(&mPackagesHash, & (const nsACString&) nsDependentCString(package), PL_DHASH_ADD)); if (!entry) return NS_ERROR_OUT_OF_MEMORY; entry->baseURI = resolved; if (platform) entry->flags |= PackageEntry::PLATFORM_PACKAGE; if (xpcNativeWrappers) entry->flags |= PackageEntry::XPCNATIVEWRAPPERS; if (contentAccessible) entry->flags |= PackageEntry::CONTENT_ACCESSIBLE; if (xpc) { nsCAutoString urlp("chrome://"); urlp.Append(package); urlp.Append('/'); rv = xpc->FlagSystemFilenamePrefix(urlp.get(), xpcNativeWrappers); NS_ENSURE_SUCCESS(rv, rv); } } else if (!strcmp(token, "locale")) { if (aSkinOnly) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Ignoring locale registration in skin-only manifest."); continue; } char *package = nsCRT::strtok(whitespace, kWhitespace, &whitespace); char *provider = nsCRT::strtok(whitespace, kWhitespace, &whitespace); char *uri = nsCRT::strtok(whitespace, kWhitespace, &whitespace); if (!package || !provider || !uri) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Malformed locale registration."); continue; } EnsureLowerCase(package); TriState stAppVersion = eUnspecified; TriState stApp = eUnspecified; TriState stOs = eUnspecified; TriState stOsVersion = eUnspecified; PRBool badFlag = PR_FALSE; while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && !badFlag) { NS_ConvertASCIItoUTF16 wtoken(token); ToLowerCase(wtoken); if (CheckStringFlag(kApplication, wtoken, appID, stApp) || CheckStringFlag(kOs, wtoken, osTarget, stOs) || CheckVersionFlag(kOsVersion, wtoken, osVersion, vc, stOsVersion) || CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion)) continue; LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Unrecognized chrome registration modifier '%s'.", token); badFlag = PR_TRUE; } if (badFlag || stApp == eBad || stAppVersion == eBad || stOs == eBad || stOsVersion == eBad) continue; nsCOMPtr resolved; rv = io->NewURI(nsDependentCString(uri), nsnull, manifestURI, getter_AddRefs(resolved)); if (NS_FAILED(rv)) continue; if (!CanLoadResource(resolved)) { LogMessageWithContext(resolved, line, nsIScriptError::warningFlag, "Warning: cannot register non-local URI '%s' as a locale.", uri); continue; } PackageEntry* entry = static_cast(PL_DHashTableOperate(&mPackagesHash, & (const nsACString&) nsDependentCString(package), PL_DHASH_ADD)); if (!entry) return NS_ERROR_OUT_OF_MEMORY; entry->locales.SetBase(nsDependentCString(provider), resolved); } else if (!strcmp(token, "skin")) { char *package = nsCRT::strtok(whitespace, kWhitespace, &whitespace); char *provider = nsCRT::strtok(whitespace, kWhitespace, &whitespace); char *uri = nsCRT::strtok(whitespace, kWhitespace, &whitespace); if (!package || !provider || !uri) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Malformed skin registration."); continue; } EnsureLowerCase(package); TriState stAppVersion = eUnspecified; TriState stApp = eUnspecified; TriState stOs = eUnspecified; TriState stOsVersion = eUnspecified; PRBool badFlag = PR_FALSE; while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && !badFlag) { NS_ConvertASCIItoUTF16 wtoken(token); ToLowerCase(wtoken); if (CheckStringFlag(kApplication, wtoken, appID, stApp) || CheckStringFlag(kOs, wtoken, osTarget, stOs) || CheckVersionFlag(kOsVersion, wtoken, osVersion, vc, stOsVersion) || CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion)) continue; LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Unrecognized chrome registration modifier '%s'.", token); badFlag = PR_TRUE; } if (badFlag || stApp == eBad || stAppVersion == eBad || stOs == eBad || stOsVersion == eBad) continue; nsCOMPtr resolved; rv = io->NewURI(nsDependentCString(uri), nsnull, manifestURI, getter_AddRefs(resolved)); if (NS_FAILED(rv)) continue; if (!CanLoadResource(resolved)) { LogMessageWithContext(resolved, line, nsIScriptError::warningFlag, "Warning: cannot register non-local URI '%s' as a skin.", uri); continue; } PackageEntry* entry = static_cast(PL_DHashTableOperate(&mPackagesHash, & (const nsACString&) nsDependentCString(package), PL_DHASH_ADD)); if (!entry) return NS_ERROR_OUT_OF_MEMORY; entry->skins.SetBase(nsDependentCString(provider), resolved); } else if (!strcmp(token, "overlay")) { if (aSkinOnly) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Ignoring overlay registration in skin-only manifest."); continue; } char *base = nsCRT::strtok(whitespace, kWhitespace, &whitespace); char *overlay = nsCRT::strtok(whitespace, kWhitespace, &whitespace); if (!base || !overlay) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: malformed chrome overlay instruction."); continue; } TriState stAppVersion = eUnspecified; TriState stApp = eUnspecified; TriState stOs = eUnspecified; TriState stOsVersion = eUnspecified; PRBool badFlag = PR_FALSE; while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && !badFlag) { NS_ConvertASCIItoUTF16 wtoken(token); ToLowerCase(wtoken); if (CheckStringFlag(kApplication, wtoken, appID, stApp) || CheckStringFlag(kOs, wtoken, osTarget, stOs) || CheckVersionFlag(kOsVersion, wtoken, osVersion, vc, stOsVersion) || CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion)) continue; LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Unrecognized chrome registration modifier '%s'.", token); badFlag = PR_TRUE; } if (badFlag || stApp == eBad || stAppVersion == eBad || stOs == eBad || stOsVersion == eBad) continue; nsCOMPtr baseuri, overlayuri; rv = io->NewURI(nsDependentCString(base), nsnull, nsnull, getter_AddRefs(baseuri)); rv |= io->NewURI(nsDependentCString(overlay), nsnull, nsnull, getter_AddRefs(overlayuri)); if (NS_FAILED(rv)) { NS_WARNING("Could not make URIs for overlay directive. Ignoring."); continue; } if (!CanLoadResource(overlayuri)) { LogMessageWithContext(overlayuri, line, nsIScriptError::warningFlag, "Warning: cannot register non-local URI '%s' as an overlay.", overlay); continue; } mOverlayHash.Add(baseuri, overlayuri); } else if (!strcmp(token, "style")) { char *base = nsCRT::strtok(whitespace, kWhitespace, &whitespace); char *overlay = nsCRT::strtok(whitespace, kWhitespace, &whitespace); if (!base || !overlay) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: malformed chrome style instruction."); continue; } TriState stAppVersion = eUnspecified; TriState stApp = eUnspecified; TriState stOs = eUnspecified; TriState stOsVersion = eUnspecified; PRBool badFlag = PR_FALSE; while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && !badFlag) { NS_ConvertASCIItoUTF16 wtoken(token); ToLowerCase(wtoken); if (CheckStringFlag(kApplication, wtoken, appID, stApp) || CheckStringFlag(kOs, wtoken, osTarget, stOs) || CheckVersionFlag(kOsVersion, wtoken, osVersion, vc, stOsVersion) || CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion)) continue; LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Unrecognized chrome registration modifier '%s'.", token); badFlag = PR_TRUE; } if (badFlag || stApp == eBad || stAppVersion == eBad || stOs == eBad || stOsVersion == eBad) continue; nsCOMPtr baseuri, overlayuri; rv = io->NewURI(nsDependentCString(base), nsnull, nsnull, getter_AddRefs(baseuri)); rv |= io->NewURI(nsDependentCString(overlay), nsnull, nsnull, getter_AddRefs(overlayuri)); if (NS_FAILED(rv)) continue; if (!CanLoadResource(overlayuri)) { LogMessageWithContext(overlayuri, line, nsIScriptError::warningFlag, "Warning: cannot register non-local URI '%s' as a style overlay.", overlay); continue; } mStyleHash.Add(baseuri, overlayuri); } else if (!strcmp(token, "override")) { if (aSkinOnly) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Ignoring override registration in skin-only manifest."); continue; } char *chrome = nsCRT::strtok(whitespace, kWhitespace, &whitespace); char *resolved = nsCRT::strtok(whitespace, kWhitespace, &whitespace); if (!chrome || !resolved) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: malformed chrome override instruction."); continue; } TriState stAppVersion = eUnspecified; TriState stApp = eUnspecified; TriState stOs = eUnspecified; TriState stOsVersion = eUnspecified; PRBool badFlag = PR_FALSE; while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && !badFlag) { NS_ConvertASCIItoUTF16 wtoken(token); ToLowerCase(wtoken); if (CheckStringFlag(kApplication, wtoken, appID, stApp) || CheckStringFlag(kOs, wtoken, osTarget, stOs) || CheckVersionFlag(kOsVersion, wtoken, osVersion, vc, stOsVersion) || CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion)) continue; LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Unrecognized chrome registration modifier '%s'.", token); badFlag = PR_TRUE; } if (badFlag || stApp == eBad || stAppVersion == eBad || stOs == eBad || stOsVersion == eBad) continue; nsCOMPtr chromeuri, resolveduri; rv = io->NewURI(nsDependentCString(chrome), nsnull, nsnull, getter_AddRefs(chromeuri)); rv |= io->NewURI(nsDependentCString(resolved), nsnull, manifestURI, getter_AddRefs(resolveduri)); if (NS_FAILED(rv)) continue; if (!CanLoadResource(resolveduri)) { LogMessageWithContext(resolveduri, line, nsIScriptError::warningFlag, "Warning: cannot register non-local URI '%s' as an override.", resolved); continue; } mOverrideTable.Put(chromeuri, resolveduri); } else if (!strcmp(token, "resource")) { if (aSkinOnly) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Ignoring resource registration in skin-only manifest."); continue; } char *package = nsCRT::strtok(whitespace, kWhitespace, &whitespace); char *uri = nsCRT::strtok(whitespace, kWhitespace, &whitespace); if (!package || !uri) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Malformed resource registration."); continue; } EnsureLowerCase(package); TriState stAppVersion = eUnspecified; TriState stApp = eUnspecified; TriState stOsVersion = eUnspecified; TriState stOs = eUnspecified; PRBool badFlag = PR_FALSE; while (nsnull != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && !badFlag) { NS_ConvertASCIItoUTF16 wtoken(token); ToLowerCase(wtoken); if (CheckStringFlag(kApplication, wtoken, appID, stApp) || CheckStringFlag(kOs, wtoken, osTarget, stOs) || CheckVersionFlag(kOsVersion, wtoken, osVersion, vc, stOsVersion) || CheckVersionFlag(kAppVersion, wtoken, appVersion, vc, stAppVersion)) continue; LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Unrecognized chrome registration modifier '%s'.", token); badFlag = PR_TRUE; } if (badFlag || stApp == eBad || stAppVersion == eBad || stOs == eBad || stOsVersion == eBad) continue; nsDependentCString host(package); PRBool exists; rv = rph->HasSubstitution(host, &exists); NS_ENSURE_SUCCESS(rv, rv); if (exists) { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Duplicate resource declaration for '%s' ignored.", package); continue; } nsCOMPtr resolved; rv = io->NewURI(nsDependentCString(uri), nsnull, manifestURI, getter_AddRefs(resolved)); if (NS_FAILED(rv)) continue; if (!CanLoadResource(resolved)) { LogMessageWithContext(resolved, line, nsIScriptError::warningFlag, "Warning: cannot register non-local URI '%s' as a resource.", uri); continue; } rv = rph->SetSubstitution(host, resolved); NS_ENSURE_SUCCESS(rv, rv); } else { LogMessageWithContext(manifestURI, line, nsIScriptError::warningFlag, "Warning: Ignoring unrecognized chrome manifest instruction."); } } return NS_OK; }