/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape 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/NPL/ * * 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 Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Original Author: David W. Hyatt (hyatt@netscape.com) * * Contributor(s): */ #include "nsCOMPtr.h" #include "nsIFileSpec.h" #include "nsSpecialSystemDirectory.h" #include "nsIChromeRegistry.h" #include "nsChromeRegistry.h" #include "nsChromeUIDataSource.h" #include "nsIRDFDataSource.h" #include "nsIRDFObserver.h" #include "nsIRDFRemoteDataSource.h" #include "nsCRT.h" #include "rdf.h" #include "nsIServiceManager.h" #include "nsIRDFService.h" #include "nsRDFCID.h" #include "nsIRDFResource.h" #include "nsIRDFDataSource.h" #include "nsIRDFContainer.h" #include "nsIRDFContainerUtils.h" #include "nsHashtable.h" #include "nsString.h" #include "nsXPIDLString.h" #include "nsISimpleEnumerator.h" #include "nsNetUtil.h" #include "nsFileLocations.h" #include "nsIFileLocator.h" #include "nsPIDOMWindow.h" #include "nsIDOMWindow.h" #include "nsIDOMWindowCollection.h" #include "nsIDOMLocation.h" #include "nsIWindowMediator.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" #include "nsIXULPrototypeCache.h" #include "nsIStyleSheet.h" #include "nsIHTMLCSSStyleSheet.h" #include "nsIHTMLStyleSheet.h" #include "nsIHTMLContentContainer.h" #include "nsIPresShell.h" #include "nsIStyleSet.h" #include "nsISupportsArray.h" #include "nsICSSLoader.h" #include "nsIDocumentObserver.h" #include "nsIXULDocument.h" #include "nsINameSpaceManager.h" #include "nsIIOService.h" #include "nsIResProtocolHandler.h" static char kChromePrefix[] = "chrome://"; static NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID); static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); static NS_DEFINE_CID(kRDFXMLDataSourceCID, NS_RDFXMLDATASOURCE_CID); static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID); class nsChromeRegistry; #define CHROME_URI "http://www.mozilla.org/rdf/chrome#" DEFINE_RDF_VOCAB(CHROME_URI, CHROME, selectedSkin); DEFINE_RDF_VOCAB(CHROME_URI, CHROME, selectedLocale); DEFINE_RDF_VOCAB(CHROME_URI, CHROME, baseURL); DEFINE_RDF_VOCAB(CHROME_URI, CHROME, packages); DEFINE_RDF_VOCAB(CHROME_URI, CHROME, package); //////////////////////////////////////////////////////////////////////////////// // XXX LOCAL COMPONENT PROBLEM! overlayEnumerator must take two sets of // arcs rather than one, and must be able to move to the second set after // finishing the first class nsOverlayEnumerator : public nsISimpleEnumerator { public: NS_DECL_ISUPPORTS NS_DECL_NSISIMPLEENUMERATOR nsOverlayEnumerator(nsISimpleEnumerator *aArcs); virtual ~nsOverlayEnumerator(); private: nsCOMPtr mArcs; }; NS_IMPL_ISUPPORTS1(nsOverlayEnumerator, nsISimpleEnumerator) nsOverlayEnumerator::nsOverlayEnumerator(nsISimpleEnumerator *aArcs) { NS_INIT_REFCNT(); mArcs = aArcs; } nsOverlayEnumerator::~nsOverlayEnumerator() { } NS_IMETHODIMP nsOverlayEnumerator::HasMoreElements(PRBool *aIsTrue) { return mArcs->HasMoreElements(aIsTrue); } NS_IMETHODIMP nsOverlayEnumerator::GetNext(nsISupports **aResult) { nsresult rv; *aResult = nsnull; if (!mArcs) return NS_ERROR_FAILURE; nsCOMPtr supports; mArcs->GetNext(getter_AddRefs(supports)); nsCOMPtr value = do_QueryInterface(supports, &rv); if (NS_FAILED(rv)) return NS_OK; const PRUnichar* valueStr; rv = value->GetValueConst(&valueStr); if (NS_FAILED(rv)) return rv; nsCOMPtr url; rv = nsComponentManager::CreateInstance("component://netscape/network/standard-url", nsnull, NS_GET_IID(nsIURL), getter_AddRefs(url)); if (NS_FAILED(rv)) return NS_OK; nsCAutoString str; str.AssignWithConversion(valueStr); url->SetSpec(str); nsCOMPtr sup; sup = do_QueryInterface(url, &rv); if (NS_FAILED(rv)) return NS_OK; *aResult = sup; NS_ADDREF(*aResult); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// nsChromeRegistry::nsChromeRegistry() { NS_INIT_REFCNT(); mInstallInitialized = PR_FALSE; mProfileInitialized = PR_FALSE; mDataSourceTable = nsnull; nsresult rv; rv = nsServiceManager::GetService(kRDFServiceCID, NS_GET_IID(nsIRDFService), (nsISupports**)&mRDFService); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service"); rv = nsServiceManager::GetService(kRDFContainerUtilsCID, NS_GET_IID(nsIRDFContainerUtils), (nsISupports**)&mRDFContainerUtils); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF container utils"); if (mRDFService) { rv = mRDFService->GetResource(kURICHROME_selectedSkin, getter_AddRefs(mSelectedSkin)); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource"); rv = mRDFService->GetResource(kURICHROME_selectedLocale, getter_AddRefs(mSelectedLocale)); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource"); rv = mRDFService->GetResource(kURICHROME_baseURL, getter_AddRefs(mBaseURL)); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource"); rv = mRDFService->GetResource(kURICHROME_packages, getter_AddRefs(mPackages)); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource"); rv = mRDFService->GetResource(kURICHROME_package, getter_AddRefs(mPackage)); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF resource"); } } nsChromeRegistry::~nsChromeRegistry() { delete mDataSourceTable; if (mRDFService) { nsServiceManager::ReleaseService(kRDFServiceCID, mRDFService); mRDFService = nsnull; } if (mRDFContainerUtils) { nsServiceManager::ReleaseService(kRDFContainerUtilsCID, mRDFContainerUtils); mRDFContainerUtils = nsnull; } } NS_IMPL_ISUPPORTS1(nsChromeRegistry, nsIChromeRegistry); //////////////////////////////////////////////////////////////////////////////// // nsIChromeRegistry methods: static nsresult SplitURL(nsIURI* aChromeURI, nsCString& aPackage, nsCString& aProvider, nsCString& aFile) { // Splits a "chrome:" URL into its package, provider, and file parts. // Here are the current portions of a // chrome: url that make up the chrome- // // chrome://global/skin/foo?bar // \------/ \----/\---/ \-----/ // | | | | // | | | `-- RemainingPortion // | | | // | | `-- Provider // | | // | `-- Package // | // `-- Always "chrome://" // // nsresult rv; char* str; rv = aChromeURI->GetSpec(&str); if (NS_FAILED(rv)) return rv; if (! str) return NS_ERROR_INVALID_ARG; PRInt32 len = PL_strlen(str); nsCAutoString spec = CBufDescriptor(str, PR_FALSE, len + 1, len); // We only want to deal with "chrome:" URLs here. We could return // an error code if the URL isn't properly prefixed here... if (PL_strncmp(spec, kChromePrefix, sizeof(kChromePrefix) - 1) != 0) return NS_ERROR_INVALID_ARG; // Cull out the "package" string; e.g., "navigator" spec.Right(aPackage, spec.Length() - (sizeof(kChromePrefix) - 1)); PRInt32 idx; idx = aPackage.FindChar('/'); if (idx < 0) return NS_OK; // Cull out the "provider" string; e.g., "content" aPackage.Right(aProvider, aPackage.Length() - (idx + 1)); aPackage.Truncate(idx); idx = aProvider.FindChar('/'); if (idx < 0) { // Force the provider to end with a '/' idx = aProvider.Length(); aProvider.Append('/'); } // Cull out the "file"; e.g., "navigator.xul" aProvider.Right(aFile, aProvider.Length() - (idx + 1)); aProvider.Truncate(idx); if (aFile.Length() == 0) { // If there is no file, then construct the default file aFile = aPackage; if (aProvider.Equals("content")) { aFile += ".xul"; } else if (aProvider.Equals("skin")) { aFile += ".css"; } else if (aProvider.Equals("locale")) { aFile += ".dtd"; } else { NS_ERROR("unknown provider"); return NS_ERROR_FAILURE; } } else { // Protect against URIs containing .. that reach up out of the // chrome directory to grant chrome privileges to non-chrome files. int depth = 0; PRBool sawSlash = PR_TRUE; // .. at the beginning is suspect as well as /.. for (const char* p=aFile; *p; p++) { if (sawSlash) { if (p[0] == '.') { if (p[1] == '.') { depth--; // we have /.., decrement depth. } else if (p[1] == '/') { // we have /./, leave depth alone } } else if (p[0] != '/') { depth++; // we have /x for some x that is not / } } sawSlash = (p[0] == '/'); if (depth < 0) return NS_ERROR_FAILURE; } } return NS_OK; } NS_IMETHODIMP nsChromeRegistry::Canonify(nsIURI* aChromeURI) { // Canonicalize 'chrome:' URLs. We'll take any 'chrome:' URL // without a filename, and change it to a URL -with- a filename; // e.g., "chrome://navigator/content" to // "chrome://navigator/content/navigator.xul". if (! aChromeURI) return NS_ERROR_NULL_POINTER; nsCAutoString package, provider, file; nsresult rv; rv = SplitURL(aChromeURI, package, provider, file); if (NS_FAILED(rv)) return rv; nsCAutoString canonical = kChromePrefix; canonical += package; canonical += "/"; canonical += provider; canonical += "/"; canonical += file; return aChromeURI->SetSpec(canonical); } NS_IMETHODIMP nsChromeRegistry::ConvertChromeURL(nsIURI* aChromeURL) { nsresult rv = NS_OK; NS_ASSERTION(aChromeURL, "null url!"); if (!aChromeURL) return NS_ERROR_NULL_POINTER; // First canonify the beast Canonify(aChromeURL); // Obtain the package, provider and remaining from the URL nsCAutoString package, provider, remaining; rv = SplitURL(aChromeURL, package, provider, remaining); if (NS_FAILED(rv)) return rv; if (!mInstallInitialized) { // Load the installed search path for skins, content, and locales // Prepend them to our list of substitutions nsresult rv = GetInstallRoot(mInstallRoot); if (NS_SUCCEEDED(rv)) { mInstallInitialized = PR_TRUE; AddToCompositeDataSource(PR_FALSE); } } if (!mProfileInitialized) { // Just setSpec nsresult rv = GetProfileRoot(mProfileRoot); if (NS_SUCCEEDED(rv)) { // Load the profile search path for skins, content, and locales // Prepend them to our list of substitutions. mProfileInitialized = PR_TRUE; mChromeDataSource = nsnull; AddToCompositeDataSource(PR_TRUE); } } nsCAutoString finalURL; GetBaseURL(package, provider, finalURL); if (finalURL.IsEmpty()) { finalURL = "resource:/chrome/"; finalURL += package; finalURL += "/"; finalURL += provider; finalURL += "/"; // XXX Remove hack when our directory structure gets fixed. if (provider.Equals(nsCAutoString("locale"))) finalURL += "en-US"; else finalURL += "default"; finalURL += "/"; finalURL += remaining; } else { finalURL += package; finalURL += "/"; finalURL += provider; finalURL += "/"; finalURL += remaining; } aChromeURL->SetSpec(finalURL); return NS_OK; } NS_IMETHODIMP nsChromeRegistry::GetBaseURL(const nsCAutoString& aPackage, const nsCAutoString& aProvider, nsCAutoString& aBaseURL) { nsCAutoString resourceStr("urn:mozilla:package:"); resourceStr += aPackage; // Obtain the resource. nsresult rv = NS_OK; nsCOMPtr resource; rv = GetResource(resourceStr, getter_AddRefs(resource)); if (NS_FAILED(rv)) { NS_ERROR("Unable to obtain the package resource."); return rv; } // Follow the "selectedSkin" or "selectedLocale" arc. nsCOMPtr arc; if (aProvider.Equals(nsCAutoString("skin"))) { arc = mSelectedSkin; } else if (aProvider.Equals(nsCAutoString("locale"))) { arc = mSelectedLocale; } if (!arc) return NS_ERROR_FAILURE; nsCOMPtr selectedProvider; if (NS_FAILED(rv = mChromeDataSource->GetTarget(resource, arc, PR_TRUE, getter_AddRefs(selectedProvider)))) { NS_ERROR("Unable to obtain the provider."); return rv; } if (!selectedProvider) return NS_ERROR_FAILURE; nsCOMPtr selectedResource(do_QueryInterface(selectedProvider)); if (!selectedResource) return NS_ERROR_FAILURE; // From this resource, follow the "baseURL" arc. nsChromeRegistry::FollowArc(mChromeDataSource, aBaseURL, selectedResource, mBaseURL); return NS_OK; } NS_IMETHODIMP nsChromeRegistry::GetOverlayDataSource(nsIURI *aChromeURL, nsIRDFDataSource **aResult) { *aResult = nsnull; nsresult rv; if (!mDataSourceTable) return NS_OK; // Obtain the package, provider and remaining from the URL nsCAutoString package, provider, remaining; rv = SplitURL(aChromeURL, package, provider, remaining); if (NS_FAILED(rv)) return rv; // Retrieve the mInner data source. nsCAutoString overlayFile = package; overlayFile += "/"; overlayFile += provider; overlayFile += "/"; overlayFile += "overlays.rdf"; // XXX For now, only support install-based overlays (not profile-based overlays) return LoadDataSource(overlayFile, aResult, PR_FALSE); } NS_IMETHODIMP nsChromeRegistry::GetOverlays(nsIURI *aChromeURL, nsISimpleEnumerator **aResult) { *aResult = nsnull; nsresult rv; if (!mDataSourceTable) return NS_OK; nsCOMPtr dataSource; GetOverlayDataSource(aChromeURL, getter_AddRefs(dataSource)); if (dataSource) { nsCOMPtr container; rv = nsComponentManager::CreateInstance("component://netscape/rdf/container", nsnull, NS_GET_IID(nsIRDFContainer), getter_AddRefs(container)); if (NS_FAILED(rv)) return NS_OK; char *lookup; aChromeURL->GetSpec(&lookup); // Get the chromeResource from this lookup string nsCOMPtr chromeResource; if (NS_FAILED(rv = GetResource(lookup, getter_AddRefs(chromeResource)))) { NS_ERROR("Unable to retrieve the resource corresponding to the chrome skin or content."); return rv; } nsAllocator::Free(lookup); if (NS_FAILED(container->Init(dataSource, chromeResource))) return NS_OK; nsCOMPtr arcs; if (NS_FAILED(container->GetElements(getter_AddRefs(arcs)))) return NS_OK; *aResult = new nsOverlayEnumerator(arcs); NS_ADDREF(*aResult); } return NS_OK; } NS_IMETHODIMP nsChromeRegistry::LoadDataSource(const nsCAutoString &aFileName, nsIRDFDataSource **aResult, PRBool aUseProfileDir) { // Init the data source to null. *aResult = nsnull; nsCAutoString key; // Try the profile root first. if (aUseProfileDir) { key = mProfileRoot; key += aFileName; } else { key = mInstallRoot; key += aFileName; } if (mDataSourceTable) { nsStringKey skey(key); nsCOMPtr supports = getter_AddRefs(NS_STATIC_CAST(nsISupports*, mDataSourceTable->Get(&skey))); if (supports) { nsCOMPtr dataSource = do_QueryInterface(supports); if (dataSource) { *aResult = dataSource; NS_ADDREF(*aResult); return NS_OK; } return NS_ERROR_FAILURE; } } nsresult rv = nsComponentManager::CreateInstance(kRDFXMLDataSourceCID, nsnull, NS_GET_IID(nsIRDFDataSource), (void**) aResult); if (NS_FAILED(rv)) return rv; nsCOMPtr remote = do_QueryInterface(*aResult); if (! remote) return NS_ERROR_UNEXPECTED; if (!mDataSourceTable) mDataSourceTable = new nsSupportsHashtable; // We need to read this synchronously. rv = remote->Init(key); rv = remote->Refresh(PR_TRUE); nsCOMPtr supports = do_QueryInterface(remote); nsStringKey skey(key); mDataSourceTable->Put(&skey, (void*)supports.get()); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// nsresult nsChromeRegistry::GetResource(const nsCAutoString& aURL, nsIRDFResource** aResult) { nsresult rv = NS_OK; if (NS_FAILED(rv = mRDFService->GetResource(aURL, aResult))) { NS_ERROR("Unable to retrieve a resource for this URL."); *aResult = nsnull; return rv; } return NS_OK; } nsresult nsChromeRegistry::FollowArc(nsIRDFDataSource *aDataSource, nsCString& aResult, nsIRDFResource* aChromeResource, nsIRDFResource* aProperty) { if (!aDataSource) return NS_ERROR_FAILURE; nsresult rv; nsCOMPtr chromeBase; if (NS_FAILED(rv = aDataSource->GetTarget(aChromeResource, aProperty, PR_TRUE, getter_AddRefs(chromeBase)))) { NS_ERROR("Unable to obtain a base resource."); return rv; } if (chromeBase == nsnull) return NS_ERROR_FAILURE; nsCOMPtr resource(do_QueryInterface(chromeBase)); if (resource) { nsXPIDLCString uri; resource->GetValue( getter_Copies(uri) ); aResult.Assign(uri); return NS_OK; } nsCOMPtr literal(do_QueryInterface(chromeBase)); if (literal) { nsXPIDLString s; literal->GetValue( getter_Copies(s) ); aResult.AssignWithConversion(s); } else { // This should _never_ happen. NS_ERROR("uh, this isn't a resource or a literal!"); return NS_ERROR_UNEXPECTED; } return NS_OK; } //////////////////////////////////////////////////////////////////////// // theme stuff NS_IMETHODIMP nsChromeRegistry::RefreshSkins() { nsresult rv; // Flush the style sheet cache completely. // XXX For now flush everything. need a better call that only flushes style sheets. NS_WITH_SERVICE(nsIXULPrototypeCache, xulCache, "component://netscape/rdf/xul-prototype-cache", &rv); if (NS_SUCCEEDED(rv) && xulCache) { xulCache->Flush(); } // Get the window mediator NS_WITH_SERVICE(nsIWindowMediator, windowMediator, kWindowMediatorCID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr windowEnumerator; if (NS_SUCCEEDED(windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator)))) { // Get each dom window PRBool more; windowEnumerator->HasMoreElements(&more); while (more) { nsCOMPtr protoWindow; rv = windowEnumerator->GetNext(getter_AddRefs(protoWindow)); if (NS_SUCCEEDED(rv) && protoWindow) { nsCOMPtr domWindow = do_QueryInterface(protoWindow); if (domWindow) RefreshWindow(domWindow); } windowEnumerator->HasMoreElements(&more); } } } return NS_OK; } NS_IMETHODIMP nsChromeRegistry::RefreshWindow(nsIDOMWindow* aWindow) { // 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; nsCOMPtr xulDoc = do_QueryInterface(domDocument); if (xulDoc) { nsCOMPtr container = do_QueryInterface(document); nsCOMPtr cssLoader; container->GetCSSLoader(*getter_AddRefs(cssLoader)); // Build an array of nsIURIs of style sheets we need to load. nsCOMPtr urls; NS_NewISupportsArray(getter_AddRefs(urls)); PRInt32 count = document->GetNumberOfStyleSheets(); // Iterate over the style sheets. for (PRInt32 i = 0; i < count; i++) { // Get the style sheet nsCOMPtr styleSheet = getter_AddRefs(document->GetStyleSheetAt(i)); // Make sure we aren't the special style sheets that never change. We // want to skip those. nsCOMPtr attrSheet; container->GetAttributeStyleSheet(getter_AddRefs(attrSheet)); nsCOMPtr inlineSheet; container->GetInlineStyleSheet(getter_AddRefs(inlineSheet)); nsCOMPtr attr = do_QueryInterface(attrSheet); nsCOMPtr inl = do_QueryInterface(inlineSheet); if ((attr.get() != styleSheet.get()) && (inl.get() != styleSheet.get())) { // Get the URI and add it to our array. nsCOMPtr uri; styleSheet->GetURL(*getter_AddRefs(uri)); urls->AppendElement(uri); // Remove the sheet. count--; i--; document->RemoveStyleSheet(styleSheet); } } // Iterate over the URL array and kick off an asynchronous load of the // sheets for our doc. PRUint32 urlCount; urls->Count(&urlCount); for (PRUint32 j = 0; j < urlCount; j++) { nsCOMPtr supports = getter_AddRefs(urls->ElementAt(j)); nsCOMPtr url = do_QueryInterface(supports); ProcessStyleSheet(url, cssLoader, document); } } return NS_OK; } NS_IMETHODIMP nsChromeRegistry::ProcessStyleSheet(nsIURL* aURL, nsICSSLoader* aLoader, nsIDocument* aDocument) { PRBool doneLoading; nsresult rv = aLoader->LoadStyleLink(nsnull, // anElement aURL, nsAutoString(), // aTitle nsAutoString(), // aMedia kNameSpaceID_Unknown, aDocument->GetNumberOfStyleSheets(), nsnull, doneLoading, // Ignore doneLoading. Don't care. nsnull); return rv; } NS_IMETHODIMP nsChromeRegistry::ReallyRemoveOverlayFromDataSource(const PRUnichar *aDocURI, char *aOverlayURI) { nsresult rv; nsCOMPtr url; rv = nsComponentManager::CreateInstance("component://netscape/network/standard-url", nsnull, NS_GET_IID(nsIURL), getter_AddRefs(url)); if (NS_FAILED(rv)) return NS_OK; nsCAutoString str; str.AssignWithConversion(aDocURI); url->SetSpec(str); nsCOMPtr dataSource; GetOverlayDataSource(url, getter_AddRefs(dataSource)); if (!dataSource) return NS_OK; nsCOMPtr resource; nsCAutoString aDocURIString; aDocURIString.AssignWithConversion(aDocURI); rv = GetResource(aDocURIString, getter_AddRefs(resource)); if (NS_FAILED(rv)) return NS_OK; nsCOMPtr container; rv = nsComponentManager::CreateInstance("component://netscape/rdf/container", nsnull, NS_GET_IID(nsIRDFContainer), getter_AddRefs(container)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; if (NS_FAILED(container->Init(dataSource, resource))) return NS_ERROR_FAILURE; nsAutoString unistr; unistr.AssignWithConversion(aOverlayURI); nsCOMPtr literal; mRDFService->GetLiteral(unistr.GetUnicode(), getter_AddRefs(literal)); container->RemoveElement(literal, PR_TRUE); nsCOMPtr remote = do_QueryInterface(dataSource, &rv); if (NS_FAILED(rv)) return NS_OK; remote->Flush(); return NS_OK; } NS_IMETHODIMP nsChromeRegistry::RemoveOverlay(nsIRDFDataSource *aDataSource, nsIRDFResource *aResource) { nsCOMPtr container; nsresult rv; rv = nsComponentManager::CreateInstance("component://netscape/rdf/container", nsnull, NS_GET_IID(nsIRDFContainer), getter_AddRefs(container)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; if (NS_FAILED(container->Init(aDataSource, aResource))) return NS_ERROR_FAILURE; nsCOMPtr arcs; if (NS_FAILED(container->GetElements(getter_AddRefs(arcs)))) return NS_ERROR_FAILURE; PRBool moreElements; arcs->HasMoreElements(&moreElements); char *value; aResource->GetValue(&value); while (moreElements) { nsCOMPtr supports; arcs->GetNext(getter_AddRefs(supports)); nsCOMPtr literal = do_QueryInterface(supports, &rv); if (NS_SUCCEEDED(rv)) { const PRUnichar* valueStr; rv = literal->GetValueConst(&valueStr); if (NS_FAILED(rv)) return rv; ReallyRemoveOverlayFromDataSource(valueStr, value); } arcs->HasMoreElements(&moreElements); } nsAllocator::Free(value); return NS_OK; } NS_IMETHODIMP nsChromeRegistry::RemoveOverlays(nsAutoString aPackage, nsAutoString aProvider, nsIRDFContainer *aContainer, nsIRDFDataSource *aDataSource) { nsresult rv; nsCOMPtr arcs; if (NS_FAILED(aContainer->GetElements(getter_AddRefs(arcs)))) return NS_OK; PRBool moreElements; arcs->HasMoreElements(&moreElements); while (moreElements) { nsCOMPtr supports; arcs->GetNext(getter_AddRefs(supports)); nsCOMPtr resource = do_QueryInterface(supports, &rv); if (NS_SUCCEEDED(rv)) { RemoveOverlay(aDataSource, resource); } arcs->HasMoreElements(&moreElements); } return NS_OK; } NS_IMETHODIMP nsChromeRegistry::SelectSkin(const PRUnichar* aSkin, PRBool aUseProfile) { return SetProvider("skin", mSelectedSkin, aSkin, aUseProfile, PR_TRUE); } NS_IMETHODIMP nsChromeRegistry::SelectLocale(const PRUnichar* aLocale, PRBool aUseProfile) { return SetProvider("skin", mSelectedSkin, aLocale, aUseProfile, PR_TRUE); } NS_IMETHODIMP nsChromeRegistry::DeselectSkin(const PRUnichar* aSkin, PRBool aUseProfile) { return SetProvider("skin", mSelectedSkin, aSkin, aUseProfile, PR_FALSE); } NS_IMETHODIMP nsChromeRegistry::DeselectLocale(const PRUnichar* aLocale, PRBool aUseProfile) { return SetProvider("skin", mSelectedSkin, aLocale, aUseProfile, PR_FALSE); } NS_IMETHODIMP nsChromeRegistry::SetProvider(const nsCAutoString& aProvider, nsIRDFResource* aSelectionArc, const PRUnichar* aProviderName, PRBool aUseProfile, PRBool aIsAdding) { // Build the provider resource str. // e.g., urn:mozilla:skin:aqua/1.0 nsCAutoString resourceStr = "urn:mozilla:"; resourceStr += aProvider; resourceStr += ":"; resourceStr.AppendWithConversion(aProviderName); // Obtain the provider resource. nsresult rv = NS_OK; nsCOMPtr resource; rv = GetResource(resourceStr, getter_AddRefs(resource)); if (NS_FAILED(rv)) { NS_ERROR("Unable to obtain the package resource."); return rv; } if (!resource) return NS_ERROR_FAILURE; // Follow the packages arc to the package resources. nsCOMPtr packageList; if (NS_FAILED(rv = mChromeDataSource->GetTarget(resource, mPackages, PR_TRUE, getter_AddRefs(packageList)))) { NS_ERROR("Unable to obtain the SEQ for the package list."); return rv; } if (!packageList) return NS_ERROR_FAILURE; nsCOMPtr packageSeq(do_QueryInterface(packageList)); if (!packageSeq) return NS_ERROR_FAILURE; // Build an RDF container to wrap the SEQ nsCOMPtr container; rv = nsComponentManager::CreateInstance("component://netscape/rdf/container", nsnull, NS_GET_IID(nsIRDFContainer), getter_AddRefs(container)); if (NS_FAILED(rv)) return NS_OK; if (NS_FAILED(container->Init(mChromeDataSource, packageSeq))) return NS_OK; nsCOMPtr arcs; if (NS_FAILED(container->GetElements(getter_AddRefs(arcs)))) return NS_OK; // For each skin/package entry, follow the arcs to the real package // resource. PRBool more; arcs->HasMoreElements(&more); while (more) { nsCOMPtr packageSkinEntry; rv = arcs->GetNext(getter_AddRefs(packageSkinEntry)); if (NS_SUCCEEDED(rv) && packageSkinEntry) { nsCOMPtr entry = do_QueryInterface(packageSkinEntry); if (entry) { // Obtain the real package resource. nsCOMPtr packageNode; if (NS_FAILED(rv = mChromeDataSource->GetTarget(entry, mPackage, PR_TRUE, getter_AddRefs(packageNode)))) { NS_ERROR("Unable to obtain the package resource."); return rv; } // Select the skin for this package resource. nsCOMPtr packageResource(do_QueryInterface(packageNode)); if (packageResource) { SetProviderForPackage(aProvider, packageResource, entry, aSelectionArc, aUseProfile, aIsAdding); } } } arcs->HasMoreElements(&more); } return NS_OK; } NS_IMETHODIMP nsChromeRegistry::SetProviderForPackage(const nsCAutoString& aProvider, nsIRDFResource* aPackageResource, nsIRDFResource* aProviderPackageResource, nsIRDFResource* aSelectionArc, PRBool aUseProfile, PRBool aIsAdding) { // Figure out which file we're needing to modify, e.g., is it the install // dir or the profile dir, and get the right datasource. nsCAutoString dataSourceStr = "user-"; dataSourceStr += aProvider; dataSourceStr += "s.rdf"; nsCOMPtr dataSource; LoadDataSource(dataSourceStr, getter_AddRefs(dataSource), aUseProfile); if (!dataSource) return NS_ERROR_FAILURE; // Get the old targets nsCOMPtr retVal; dataSource->GetTarget(aPackageResource, aSelectionArc, PR_TRUE, getter_AddRefs(retVal)); if (retVal) { if (aIsAdding) { // Perform a CHANGE operation. dataSource->Change(aPackageResource, aSelectionArc, retVal, aProviderPackageResource); } else { // Only do an unassert if we are the current selected provider. nsCOMPtr res(do_QueryInterface(retVal)); if (res.get() == aProviderPackageResource) dataSource->Unassert(aPackageResource, aSelectionArc, aProviderPackageResource); } } else if (aIsAdding) { // Do an ASSERT instead. dataSource->Assert(aPackageResource, aSelectionArc, aProviderPackageResource, PR_TRUE); } nsCOMPtr remote = do_QueryInterface(dataSource); if (!remote) return NS_ERROR_UNEXPECTED; remote->Flush(); return NS_OK; } NS_IMETHODIMP nsChromeRegistry::SelectSkinForPackage(const PRUnichar *aSkin, const PRUnichar *aPackageName, PRBool aUseProfile) { nsCAutoString provider("skin"); return SelectProviderForPackage(provider, aSkin, aPackageName, mSelectedSkin, aUseProfile, PR_TRUE); } NS_IMETHODIMP nsChromeRegistry::SelectLocaleForPackage(const PRUnichar *aLocale, const PRUnichar *aPackageName, PRBool aUseProfile) { nsCAutoString provider("locale"); return SelectProviderForPackage(provider, aLocale, aPackageName, mSelectedLocale, aUseProfile, PR_TRUE); } NS_IMETHODIMP nsChromeRegistry::DeselectSkinForPackage(const PRUnichar *aSkin, const PRUnichar *aPackageName, PRBool aUseProfile) { nsCAutoString provider("skin"); return SelectProviderForPackage(provider, aSkin, aPackageName, mSelectedSkin, aUseProfile, PR_FALSE); } NS_IMETHODIMP nsChromeRegistry::DeselectLocaleForPackage(const PRUnichar *aLocale, const PRUnichar *aPackageName, PRBool aUseProfile) { nsCAutoString provider("skin"); return SelectProviderForPackage(provider, aLocale, aPackageName, mSelectedLocale, aUseProfile, PR_FALSE); } NS_IMETHODIMP nsChromeRegistry::SelectProviderForPackage(const nsCAutoString& aProviderType, const PRUnichar *aProviderName, const PRUnichar *aPackageName, nsIRDFResource* aSelectionArc, PRBool aUseProfile, PRBool aIsAdding) { nsCAutoString package = "urn:mozilla:package:"; package.AppendWithConversion(aPackageName); nsCAutoString provider = "urn:mozilla:"; provider += aProviderType; provider += ":"; provider.AppendWithConversion(aProviderName); provider += ":"; provider.AppendWithConversion(aPackageName); // Obtain the package resource. nsresult rv = NS_OK; nsCOMPtr packageResource; rv = GetResource(package, getter_AddRefs(packageResource)); if (NS_FAILED(rv)) { NS_ERROR("Unable to obtain the package resource."); return rv; } if (!packageResource) return NS_ERROR_FAILURE; // Obtain the provider resource. nsCOMPtr providerResource; rv = GetResource(provider, getter_AddRefs(providerResource)); if (NS_FAILED(rv)) { NS_ERROR("Unable to obtain the provider resource."); return rv; } if (!providerResource) return NS_ERROR_FAILURE; return SetProviderForPackage(aProviderType, packageResource, providerResource, aSelectionArc, aUseProfile, aIsAdding);; } NS_IMETHODIMP nsChromeRegistry::InstallProvider(const nsCAutoString& aProviderType, const nsCAutoString& aBaseURL, PRBool aUseProfile) { // Load the data source found at the base URL. nsCOMPtr dataSource; nsresult rv = nsComponentManager::CreateInstance(kRDFXMLDataSourceCID, nsnull, NS_GET_IID(nsIRDFDataSource), (void**) getter_AddRefs(dataSource)); if (NS_FAILED(rv)) return rv; nsCOMPtr remote = do_QueryInterface(dataSource); if (!remote) return NS_ERROR_UNEXPECTED; // We need to read this synchronously. nsCAutoString key = aBaseURL; key += "manifest.rdf"; rv = remote->Init(key); rv = remote->Refresh(PR_TRUE); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; // Load the data source that we wish to manipulate. nsCOMPtr installSource; nsCAutoString installStr = "all-"; installStr += aProviderType; installStr += "s.rdf"; LoadDataSource(installStr, getter_AddRefs(installSource), aUseProfile); if (!installSource) return NS_ERROR_FAILURE; // Build the prefix string. Only resources with this prefix string will have their // assertions copied. nsCAutoString prefix = "urn:mozilla:"; prefix += aProviderType; prefix += ":"; // Get all the resources nsCOMPtr resources; dataSource->GetAllResources(getter_AddRefs(resources)); // For each resource PRBool moreElements; resources->HasMoreElements(&moreElements); while (moreElements) { nsCOMPtr supports; resources->GetNext(getter_AddRefs(supports)); nsCOMPtr resource = do_QueryInterface(supports); // Check against the prefix string const char* value; resource->GetValueConst(&value); nsCAutoString val(value); if (val.Find(prefix) == 0) { // It's valid. nsCOMPtr container; rv = nsComponentManager::CreateInstance("component://netscape/rdf/container", nsnull, NS_GET_IID(nsIRDFContainer), getter_AddRefs(container)); if (NS_SUCCEEDED(container->Init(dataSource, resource))) { // XXX Deal with BAGS and ALTs? Aww, to hell with it. Who cares? I certainly don't. // We're a SEQ. Different rules apply. Do an AppendElement instead. // First do the decoration in the install data source. nsCOMPtr installContainer; mRDFContainerUtils->MakeSeq(installSource, resource, getter_AddRefs(installContainer)); if (!installContainer) { // Already exists. Create a container instead. rv = nsComponentManager::CreateInstance("component://netscape/rdf/container", nsnull, NS_GET_IID(nsIRDFContainer), getter_AddRefs(installContainer)); installContainer->Init(installSource, resource); } { // Restrict variable scope // Put all our elements into the install container. nsCOMPtr seqKids; container->GetElements(getter_AddRefs(seqKids)); PRBool moreKids; seqKids->HasMoreElements(&moreKids); while (moreKids) { nsCOMPtr supp; seqKids->GetNext(getter_AddRefs(supp)); nsCOMPtr kid = do_QueryInterface(supp); installContainer->AppendElement(kid); seqKids->HasMoreElements(&moreKids); } } // See if we're a packages seq. If so, we need to set up the baseURL and // the package arcs. if (val.Find(":packages") != -1 && !aProviderType.Equals(nsCAutoString("package"))) { // Get the literal for our base URL. nsAutoString unistr(aBaseURL); nsCOMPtr literal; mRDFService->GetLiteral(unistr.GetUnicode(), getter_AddRefs(literal)); // Iterate over our kids a second time. nsCOMPtr seqKids; installContainer->GetElements(getter_AddRefs(seqKids)); PRBool moreKids; seqKids->HasMoreElements(&moreKids); while (moreKids) { nsCOMPtr supp; seqKids->GetNext(getter_AddRefs(supp)); nsCOMPtr entry(do_QueryInterface(supp)); if (entry) { nsCOMPtr retVal; installSource->GetTarget(entry, mBaseURL, PR_TRUE, getter_AddRefs(retVal)); if (retVal) installSource->Change(entry, mBaseURL, retVal, literal); else installSource->Assert(entry, mBaseURL, literal, PR_TRUE); // Now set up the package arc. const char* val; entry->GetValueConst(&val); nsCAutoString value(val); PRInt32 index = value.RFind(":"); if (index != -1) { // Peel off the package name. nsCAutoString packageName; value.Right(packageName, value.Length() - index - 1); nsCAutoString resourceName = "urn:mozilla:package:"; resourceName += packageName; nsCOMPtr packageResource; GetResource(resourceName, getter_AddRefs(packageResource)); if (packageResource) { retVal = nsnull; installSource->GetTarget(entry, mPackage, PR_TRUE, getter_AddRefs(retVal)); if (retVal) installSource->Change(entry, mPackage, retVal, packageResource); else installSource->Assert(entry, mPackage, packageResource, PR_TRUE); } } } seqKids->HasMoreElements(&moreKids); } } } else { // We're not a seq. Get all of the arcs that go out. nsCOMPtr arcs; dataSource->ArcLabelsOut(resource, getter_AddRefs(arcs)); PRBool moreArcs; arcs->HasMoreElements(&moreArcs); while (moreArcs) { nsCOMPtr supp; arcs->GetNext(getter_AddRefs(supp)); nsCOMPtr arc = do_QueryInterface(supp); nsCOMPtr retVal; installSource->GetTarget(resource, arc, PR_TRUE, getter_AddRefs(retVal)); nsCOMPtr newTarget; dataSource->GetTarget(resource, arc, PR_TRUE, getter_AddRefs(newTarget)); if (retVal) installSource->Change(resource, arc, retVal, newTarget); else { // Do an ASSERT instead. installSource->Assert(resource, arc, newTarget, PR_TRUE); } arcs->HasMoreElements(&moreArcs); } } } resources->HasMoreElements(&moreElements); } // Flush the install source nsCOMPtr remoteInstall = do_QueryInterface(installSource, &rv); if (NS_FAILED(rv)) return NS_OK; remoteInstall->Flush(); // XXX Handle the installation of overlays. return NS_OK; } NS_IMETHODIMP nsChromeRegistry::InstallSkin(const char* aBaseURL, PRBool aUseProfile) { nsCAutoString provider("skin"); return InstallProvider(provider, aBaseURL, aUseProfile); } NS_IMETHODIMP nsChromeRegistry::InstallLocale(const char* aBaseURL, PRBool aUseProfile) { nsCAutoString provider("locale"); return InstallProvider(provider, aBaseURL, aUseProfile); } NS_IMETHODIMP nsChromeRegistry::InstallPackage(const char* aBaseURL, PRBool aUseProfile) { nsCAutoString provider("package"); return InstallProvider(provider, aBaseURL, aUseProfile); } NS_IMETHODIMP nsChromeRegistry::UninstallSkin(const PRUnichar* aSkinName, PRBool aUseProfile) { NS_ERROR("Write me!\n"); return NS_OK; } NS_IMETHODIMP nsChromeRegistry::UninstallLocale(const PRUnichar* aLocaleName, PRBool aUseProfile) { NS_ERROR("Write me!\n"); return NS_OK; } NS_IMETHODIMP nsChromeRegistry::UninstallPackage(const PRUnichar* aPackageName, PRBool aUseProfile) { NS_ERROR("Write me!\n"); return NS_OK; } NS_IMETHODIMP nsChromeRegistry::GetProfileRoot(nsCAutoString& aFileURL) { nsCOMPtr fl; nsresult rv = nsComponentManager::CreateInstance("component://netscape/filelocator", nsnull, NS_GET_IID(nsIFileLocator), getter_AddRefs(fl)); if (NS_FAILED(rv)) return NS_OK; // Build a fileSpec that points to the destination // (profile dir + chrome + package + provider + chrome.rdf) nsCOMPtr chromeFileInterface; fl->GetFileLocation(nsSpecialFileSpec::App_UserProfileDirectory50, getter_AddRefs(chromeFileInterface)); if (chromeFileInterface) { nsFileSpec chromeFile; chromeFileInterface->GetFileSpec(&chromeFile); nsFileURL fileURL(chromeFile); const char* fileStr = fileURL.GetURLString(); aFileURL = fileStr; aFileURL += "chrome/"; } else return NS_ERROR_FAILURE; return NS_OK; } NS_IMETHODIMP nsChromeRegistry::GetInstallRoot(nsCAutoString& aFileURL) { nsCOMPtr fl; nsresult rv = nsComponentManager::CreateInstance("component://netscape/filelocator", nsnull, NS_GET_IID(nsIFileLocator), getter_AddRefs(fl)); if (NS_FAILED(rv)) return NS_OK; // Build a fileSpec that points to the destination // (profile dir + chrome + package + provider + chrome.rdf) nsCOMPtr chromeFileInterface; fl->GetFileLocation(nsSpecialFileSpec::App_ChromeDirectory, getter_AddRefs(chromeFileInterface)); if (chromeFileInterface) { nsFileSpec chromeFile; chromeFileInterface->GetFileSpec(&chromeFile); nsFileURL fileURL(chromeFile); const char* fileStr = fileURL.GetURLString(); aFileURL = fileStr; } else return NS_ERROR_FAILURE; return NS_OK; } NS_IMETHODIMP nsChromeRegistry::ReloadChrome() { // Do a reload of all top level windows. nsresult rv; // Flush the cache completely. NS_WITH_SERVICE(nsIXULPrototypeCache, xulCache, "component://netscape/rdf/xul-prototype-cache", &rv); if (NS_SUCCEEDED(rv) && xulCache) { xulCache->Flush(); } // Get the window mediator NS_WITH_SERVICE(nsIWindowMediator, windowMediator, kWindowMediatorCID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr windowEnumerator; if (NS_SUCCEEDED(windowMediator->GetEnumerator(nsnull, getter_AddRefs(windowEnumerator)))) { // Get each dom window PRBool more; windowEnumerator->HasMoreElements(&more); while (more) { nsCOMPtr protoWindow; rv = windowEnumerator->GetNext(getter_AddRefs(protoWindow)); if (NS_SUCCEEDED(rv) && protoWindow) { nsCOMPtr domWindow = do_QueryInterface(protoWindow); if (domWindow) { nsCOMPtr location; domWindow->GetLocation(getter_AddRefs(location)); if (location) location->Reload(PR_FALSE); } } windowEnumerator->HasMoreElements(&more); } } } return NS_OK; } NS_IMETHODIMP nsChromeRegistry::GetArcs(nsIRDFDataSource* aDataSource, const nsCAutoString& aType, nsISimpleEnumerator** aResult) { nsCOMPtr container; nsresult rv = nsComponentManager::CreateInstance("component://netscape/rdf/container", nsnull, NS_GET_IID(nsIRDFContainer), getter_AddRefs(container)); if (NS_FAILED(rv)) return NS_OK; nsCAutoString lookup("chrome:"); lookup += aType; // Get the chromeResource from this lookup string nsCOMPtr chromeResource; if (NS_FAILED(rv = GetResource(lookup, getter_AddRefs(chromeResource)))) { NS_ERROR("Unable to retrieve the resource corresponding to the chrome skin or content."); return rv; } if (NS_FAILED(container->Init(aDataSource, chromeResource))) return NS_OK; nsCOMPtr arcs; if (NS_FAILED(container->GetElements(getter_AddRefs(arcs)))) return NS_OK; *aResult = arcs; NS_IF_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsChromeRegistry::AddToCompositeDataSource(PRBool aUseProfile) { nsresult rv = NS_OK; if (!mChromeDataSource) { rv = nsComponentManager::CreateInstance("component://netscape/rdf/datasource?name=composite-datasource", nsnull, NS_GET_IID(nsIRDFCompositeDataSource), getter_AddRefs(mChromeDataSource)); if (NS_FAILED(rv)) return rv; // Also create and hold on to our UI data source. NS_NewChromeUIDataSource(mChromeDataSource, &mUIDataSource); mRDFService->RegisterDataSource(mUIDataSource, PR_FALSE); } if (aUseProfile) { // Profiles take precedence. Load them first. nsCOMPtr dataSource; nsCAutoString name("user-skins.rdf"); LoadDataSource(name, getter_AddRefs(dataSource), PR_TRUE); mChromeDataSource->AddDataSource(dataSource); name = "all-skins.rdf"; LoadDataSource(name, getter_AddRefs(dataSource), PR_TRUE); mChromeDataSource->AddDataSource(dataSource); name = "user-locales.rdf"; LoadDataSource(name, getter_AddRefs(dataSource), PR_TRUE); mChromeDataSource->AddDataSource(dataSource); name = "all-locales.rdf"; LoadDataSource(name, getter_AddRefs(dataSource), PR_TRUE); mChromeDataSource->AddDataSource(dataSource); name = "all-packages.rdf"; LoadDataSource(name, getter_AddRefs(dataSource), PR_TRUE); mChromeDataSource->AddDataSource(dataSource); } // Always load the install dir datasources nsCOMPtr dataSource; nsCAutoString name = "user-skins.rdf"; LoadDataSource(name, getter_AddRefs(dataSource), PR_FALSE); mChromeDataSource->AddDataSource(dataSource); name = "all-skins.rdf"; LoadDataSource(name, getter_AddRefs(dataSource), PR_FALSE); mChromeDataSource->AddDataSource(dataSource); name = "user-locales.rdf"; LoadDataSource(name, getter_AddRefs(dataSource), PR_FALSE); mChromeDataSource->AddDataSource(dataSource); name = "all-locales.rdf"; LoadDataSource(name, getter_AddRefs(dataSource), PR_FALSE); mChromeDataSource->AddDataSource(dataSource); name = "all-packages.rdf"; LoadDataSource(name, getter_AddRefs(dataSource), PR_FALSE); mChromeDataSource->AddDataSource(dataSource); return NS_OK; } ////////////////////////////////////////////////////////////////////// nsresult NS_NewChromeRegistry(nsIChromeRegistry** aResult) { NS_PRECONDITION(aResult != nsnull, "null ptr"); if (! aResult) return NS_ERROR_NULL_POINTER; nsChromeRegistry* chromeRegistry = new nsChromeRegistry(); if (chromeRegistry == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(chromeRegistry); *aResult = chromeRegistry; return NS_OK; }