Back to index

lightning-sunbird  0.9+nobinonly
nsEmbedChromeRegistry.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsEmbedChromeRegistry.h"
00039 #include "nsString.h"
00040 #include "nsReadableUtils.h"
00041 #include "plstr.h"
00042 
00043 #include "nsIDirectoryService.h"
00044 #include "nsAppDirectoryServiceDefs.h"
00045 #include "nsIProperties.h"
00046 #include "nsILocalFile.h"
00047 #include "nsIURI.h"
00048 
00049 #define CHROME_TYPE_CONTENT 0
00050 #define CHROME_TYPE_LOCALE 1
00051 #define CHROME_TYPE_SKIN 2
00052 
00053 const char kChromePrefix[] = "chrome://";
00054 
00055 static nsresult
00056 SplitURL(nsIURI *aChromeURI, nsCString& aPackage, nsCString& aProvider, nsCString& aFile,
00057          PRBool *aModified = nsnull)
00058 {
00059   // Splits a "chrome:" URL into its package, provider, and file parts.
00060   // Here are the current portions of a
00061   // chrome: url that make up the chrome-
00062   //
00063   //     chrome://global/skin/foo?bar
00064   //     \------/ \----/\---/ \-----/
00065   //         |       |     |     |
00066   //         |       |     |     `-- RemainingPortion
00067   //         |       |     |
00068   //         |       |     `-- Provider
00069   //         |       |
00070   //         |       `-- Package
00071   //         |
00072   //         `-- Always "chrome://"
00073   //
00074   //
00075 
00076   nsresult rv;
00077 
00078   nsCAutoString str;
00079   rv = aChromeURI->GetSpec(str);
00080   if (NS_FAILED(rv)) return rv;
00081 
00082   // We only want to deal with "chrome:" URLs here. We could return
00083   // an error code if the URL isn't properly prefixed here...
00084   if (PL_strncmp(str.get(), kChromePrefix, sizeof(kChromePrefix) - 1) != 0)
00085     return NS_ERROR_INVALID_ARG;
00086 
00087   // Cull out the "package" string; e.g., "navigator"
00088   aPackage = str.get() + sizeof(kChromePrefix) - 1;
00089 
00090   PRInt32 idx;
00091   idx = aPackage.FindChar('/');
00092   if (idx < 0)
00093     return NS_OK;
00094 
00095   // Cull out the "provider" string; e.g., "content"
00096   aPackage.Right(aProvider, aPackage.Length() - (idx + 1));
00097   aPackage.Truncate(idx);
00098 
00099   idx = aProvider.FindChar('/');
00100   if (idx < 0) {
00101     // Force the provider to end with a '/'
00102     idx = aProvider.Length();
00103     aProvider.Append('/');
00104   }
00105 
00106   // Cull out the "file"; e.g., "navigator.xul"
00107   aProvider.Right(aFile, aProvider.Length() - (idx + 1));
00108   aProvider.Truncate(idx);
00109 
00110   PRBool nofile = aFile.IsEmpty();
00111   if (nofile) {
00112     // If there is no file, then construct the default file
00113     aFile = aPackage;
00114 
00115     if (aProvider.Equals("content")) {
00116       aFile += ".xul";
00117     }
00118     else if (aProvider.Equals("skin")) {
00119       aFile += ".css";
00120     }
00121     else if (aProvider.Equals("locale")) {
00122       aFile += ".dtd";
00123     }
00124     else {
00125       NS_ERROR("unknown provider");
00126       return NS_ERROR_FAILURE;
00127     }
00128   } else {
00129     // Protect against URIs containing .. that reach up out of the
00130     // chrome directory to grant chrome privileges to non-chrome files.
00131     int depth = 0;
00132     PRBool sawSlash = PR_TRUE;  // .. at the beginning is suspect as well as /..
00133     for (const char* p=aFile.get(); *p; p++) {
00134       if (sawSlash) {
00135         if (p[0] == '.' && p[1] == '.'){
00136           depth--;    // we have /.., decrement depth.
00137         } else {
00138           static const char escape[] = "%2E%2E";
00139           if (PL_strncasecmp(p, escape, sizeof(escape)-1) == 0)
00140             depth--;   // we have the HTML-escaped form of /.., decrement depth.
00141         }
00142       } else if (p[0] != '/') {
00143         depth++;        // we have /x for some x that is not /
00144       }
00145       sawSlash = (p[0] == '/');
00146 
00147       if (depth < 0) {
00148         return NS_ERROR_FAILURE;
00149       }
00150     }
00151   }
00152   if (aModified)
00153     *aModified = nofile;
00154   return NS_OK;
00155 }
00156 
00157 NS_IMPL_ISUPPORTS1(nsEmbedChromeRegistry, nsIChromeRegistry)
00158 
00159 nsEmbedChromeRegistry::nsEmbedChromeRegistry()
00160 {
00161 }
00162 
00163 nsresult
00164 nsEmbedChromeRegistry::Init()
00165 {
00166     NS_ASSERTION(0, "Creating embedding chrome registry\n");
00167     nsresult rv;
00168     
00169     rv = NS_NewISupportsArray(getter_AddRefs(mEmptyArray));
00170     if (NS_FAILED(rv)) return rv;
00171 
00172     rv = ReadChromeRegistry();
00173     if (NS_FAILED(rv)) return rv;
00174     
00175     return NS_OK;
00176 }
00177 
00178 nsresult
00179 nsEmbedChromeRegistry::ReadChromeRegistry()
00180 {
00181     nsresult rv;
00182     nsCOMPtr<nsIProperties> directoryService =
00183         do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
00184     if (NS_FAILED(rv)) return rv;
00185 
00186     nsCOMPtr<nsILocalFile> listFile;
00187     rv = directoryService->Get(NS_APP_CHROME_DIR, NS_GET_IID(nsILocalFile),
00188                                getter_AddRefs(listFile));
00189     if (NS_FAILED(rv)) return rv;
00190 
00191     rv = listFile->AppendRelativeNativePath(NS_LITERAL_CSTRING("installed-chrome.txt"));
00192     if (NS_FAILED(rv)) return rv;
00193 
00194     PRFileDesc *file;
00195     rv = listFile->OpenNSPRFileDesc(PR_RDONLY, 0, &file);
00196     if (NS_FAILED(rv)) return rv;
00197 
00198     PRFileInfo finfo;
00199 
00200     if (PR_GetOpenFileInfo(file, &finfo) == PR_SUCCESS) {
00201         char *dataBuffer = new char[finfo.size+1];
00202         if (dataBuffer) {
00203             PRInt32 bufferSize = PR_Read(file, dataBuffer, finfo.size);
00204             if (bufferSize > 0) {
00205                 dataBuffer[bufferSize] = '\r';
00206                 rv = ProcessNewChromeBuffer(dataBuffer, bufferSize);
00207             }
00208             delete [] dataBuffer;
00209         }
00210     }
00211     PR_Close(file);
00212 
00213     return rv;
00214 }
00215 
00216 nsresult
00217 nsEmbedChromeRegistry::ProcessNewChromeBuffer(char* aBuffer, PRInt32 aLength)
00218 {
00219     while (aLength > 0) {
00220         PRInt32 processedBytes = ProcessChromeLine(aBuffer, aLength);
00221         aBuffer += processedBytes;
00222         aLength -= processedBytes;
00223     }
00224     return NS_OK;
00225 }
00226 
00227 #define MAX_TOKENS 5
00228 struct chromeToken {
00229     const char *tokenStart;
00230     const char *tokenEnd;
00231 };
00232 
00233 PRInt32
00234 nsEmbedChromeRegistry::ProcessChromeLine(const char* aBuffer, PRInt32 aLength)
00235 {
00236     PRInt32 bytesProcessed = 0;
00237     chromeToken tokens[MAX_TOKENS];
00238     PRInt32 tokenCount = 0;
00239     PRBool expectingToken = PR_TRUE;
00240     
00241     while (bytesProcessed <= aLength &&
00242            *aBuffer != '\n' && *aBuffer != '\r' &&
00243            tokenCount < MAX_TOKENS) {
00244 
00245         if (*aBuffer == ',') {
00246             tokenCount++;
00247             expectingToken = PR_TRUE;
00248         }
00249         else if (expectingToken)
00250             tokens[tokenCount].tokenStart = aBuffer;
00251         else
00252             tokens[tokenCount].tokenEnd = aBuffer;
00253 
00254 
00255         aBuffer++;
00256         bytesProcessed++;
00257     }
00258     NS_ASSERTION(tokenCount == 4, "Unexpected tokens in line");
00259 
00260     nsDependentCSubstring
00261         chromeType(tokens[0].tokenStart, tokens[0].tokenEnd);
00262     nsDependentCSubstring
00263         chromeProfile(tokens[1].tokenStart, tokens[1].tokenEnd);
00264     nsDependentCSubstring
00265         chromeLocType(tokens[2].tokenStart, tokens[2].tokenEnd);
00266     nsDependentCSubstring
00267         chromeLocation(tokens[3].tokenStart, tokens[3].tokenEnd);
00268     
00269     RegisterChrome(chromeType, chromeProfile, chromeLocType, chromeLocation);
00270     return bytesProcessed;
00271 }
00272 
00273 nsresult
00274 nsEmbedChromeRegistry::RegisterChrome(const nsACString& aChromeType,
00275                                       const nsACString& aChromeProfile,
00276                                       const nsACString& aChromeLocType,
00277                                       const nsACString& aChromeLocation)
00278 {
00279     PRInt32 chromeType;
00280     if (aChromeType.EqualsLiteral("skin"))
00281         chromeType = CHROME_TYPE_SKIN;
00282     else if (aChromeType.EqualsLiteral("locale"))
00283         chromeType = CHROME_TYPE_LOCALE;
00284     else
00285         chromeType = CHROME_TYPE_CONTENT;
00286 
00287     PRBool chromeIsProfile =
00288         aChromeProfile.EqualsLiteral("profile");
00289 
00290     PRBool chromeIsURL =
00291         aChromeProfile.EqualsLiteral("url");
00292 
00293     return RegisterChrome(chromeType, chromeIsProfile, chromeIsURL,
00294                           aChromeLocation);
00295 }
00296 
00297 nsresult
00298 nsEmbedChromeRegistry::RegisterChrome(PRInt32 aChromeType, // CHROME_TYPE_CONTENT, etc
00299                                       PRBool aChromeIsProfile, // per-profile?
00300                                       PRBool aChromeIsURL, // is it a url? (else path)
00301                                       const nsACString& aChromeLocation)
00302 {
00303 
00304 
00305     return NS_OK;
00306 }
00307 
00308 NS_IMETHODIMP
00309 nsEmbedChromeRegistry::CheckForNewChrome()
00310 {
00311     return NS_OK;
00312 }
00313 
00314 NS_IMETHODIMP
00315 nsEmbedChromeRegistry::Canonify(nsIURI* aChromeURI)
00316 {
00317 #if 1
00318   // Canonicalize 'chrome:' URLs. We'll take any 'chrome:' URL
00319   // without a filename, and change it to a URL -with- a filename;
00320   // e.g., "chrome://navigator/content" to
00321   // "chrome://navigator/content/navigator.xul".
00322   if (! aChromeURI)
00323       return NS_ERROR_NULL_POINTER;
00324 
00325   PRBool modified = PR_TRUE; // default is we do canonification
00326   nsCAutoString package, provider, file;
00327   nsresult rv;
00328   rv = SplitURL(aChromeURI, package, provider, file, &modified);
00329   if (NS_FAILED(rv))
00330     return rv;
00331 
00332   if (!modified)
00333     return NS_OK;
00334 
00335   nsCAutoString canonical( kChromePrefix );
00336   canonical += package;
00337   canonical += "/";
00338   canonical += provider;
00339   canonical += "/";
00340   canonical += file;
00341 
00342   return aChromeURI->SetSpec(canonical);
00343 #else
00344   return NS_OK;
00345 #endif
00346 }
00347 
00348 NS_IMETHODIMP
00349 nsEmbedChromeRegistry::ConvertChromeURL(nsIURI* aChromeURL, nsACString& aResult)
00350 {
00351     nsresult rv;
00352     
00353     rv = aChromeURL->GetSpec(aResult);
00354     if (NS_FAILED(rv)) return rv;
00355     
00356     return NS_OK;
00357 }